Files
Nexus/server/application/services/server_service.py
T
r 6720ca3474 feat(servers): 主列表宝塔登录 + 健康检查写心跳
主服务器列表操作列一键登录宝塔;SSH 健康检查写入 Redis 心跳并刷新在线状态。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-21 11:05:43 +08:00

80 lines
3.0 KiB
Python

"""Nexus — Server Service (Business logic for server management)
Application layer — orchestrates Repository + SSH pool + Redis cache.
"""
import asyncio
import json
import logging
from typing import Optional, List, Dict
from server.domain.models import Server
from server.domain.repositories import ServerRepository, SyncLogRepository
logger = logging.getLogger("nexus.server_service")
class ServerService:
"""Business logic for server CRUD, health check, and push operations"""
def __init__(self, server_repo: ServerRepository, sync_log_repo: SyncLogRepository):
self.server_repo = server_repo
self.sync_log_repo = sync_log_repo
async def get_server(self, id: int) -> Optional[Server]:
return await self.server_repo.get_by_id(id)
async def create_server(self, server: Server) -> Server:
return await self.server_repo.create(server)
async def update_server(self, server: Server) -> Server:
return await self.server_repo.update(server)
async def delete_server(self, id: int) -> bool:
return await self.server_repo.delete(id)
async def update_heartbeat(self, id: int, data: dict) -> None:
"""Process Agent heartbeat data"""
is_online = data.get("is_online", True)
system_info = json.dumps(data.get("system_info", {}))
agent_version = data.get("agent_version", "")
await self.server_repo.update_heartbeat(id, is_online, system_info, agent_version)
async def check_all_servers(self, server_ids: List[int]) -> Dict[int, dict]:
"""Check health of multiple servers via SSH (no Agent port 8601 required)."""
from server.application.server_connectivity import write_ssh_health_heartbeat
from server.infrastructure.redis.client import get_redis
from server.infrastructure.ssh.remote_probe import ssh_health_probe
results = {}
redis = get_redis()
async def _check_one(server: Server) -> tuple:
try:
probe = await ssh_health_probe(server, timeout=15)
try:
last_hb = await write_ssh_health_heartbeat(redis, server, probe)
probe = {**probe, "last_heartbeat": last_hb}
except Exception as redis_err:
logger.warning(
"health check heartbeat write failed server=%s: %s",
server.id,
redis_err,
)
return server.id, probe
except Exception as e:
return server.id, {"status": "offline", "error": str(e)[:200], "channel": "ssh"}
servers = []
for sid in server_ids:
server = await self.server_repo.get_by_id(sid)
if server:
servers.append(server)
else:
results[sid] = {"status": "not_found"}
if servers:
check_results = await asyncio.gather(*[_check_one(s) for s in servers])
for sid, result in check_results:
results[sid] = result
return results