refactor: 仓库结构扁平化 + PHP前端合并到Nexus仓库

- 将 Nexus/Nexus/* 移到仓库根目录(消除双层嵌套)
- 删除旧的多层空壳目录 (server/, web/, agent/, deploy/, docs/)
- 将PHP前端 (web/) 合并进Nexus仓库 — 统一管理
- 部署时 git clone Nexus 仓库即可,不再需要两个仓库

目录结构:
  server/     ← Python FastAPI后端
  web/        ← PHP前端 (install.php, config.php, etc)
  deploy/     ← Supervisor + Shell健康检查
  docs/       ← 部署文档
  tests/      ← 测试
  .env        ← 不在git中 (install.php生成)
  .gitignore  ← 排除 .env, SECRETS.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-05-20 17:24:21 +08:00
parent 05600232b7
commit f9045a8404
92 changed files with 15610 additions and 180 deletions
@@ -0,0 +1,95 @@
"""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, SyncLog
from server.domain.repositories import ServerRepository, SyncLogRepository
from server.infrastructure.ssh.pool import SSHConfig, exec_command
from server.infrastructure.redis.client import get_redis
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 list_servers(self, category: Optional[str] = None) -> List[Server]:
"""List all servers, optionally filtered by category"""
if category:
return await self.server_repo.get_by_category(category)
return await self.server_repo.get_all()
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 Agent API (no SSH)"""
results = {}
# TODO: Use httpx to call each Agent's /health endpoint
for id in server_ids:
server = await self.server_repo.get_by_id(id)
if server and server.is_online:
results[id] = {"status": "online", "system_info": json.loads(server.system_info or "{}")}
elif server:
results[id] = {"status": "offline"}
else:
results[id] = {"status": "not_found"}
return results
async def push_to_servers(
self,
server_ids: List[int],
source_path: str,
sync_mode: str = "incremental",
operator: str = "admin",
) -> Dict[int, SyncLog]:
"""Push files to multiple servers in batches"""
results = {}
servers = []
for id in server_ids:
server = await self.server_repo.get_by_id(id)
if server:
servers.append(server)
# TODO: Implement batch push with asyncio.Semaphore(10)
# batch_size=50, interval=30s
for server in servers:
target_path = server.target_path or f"/www/wwwroot/"
log = SyncLog(
server_id=server.id,
source_path=source_path,
target_path=target_path,
trigger_type="batch",
operator=operator,
status="pending",
sync_mode=sync_mode,
)
log = await self.sync_log_repo.create(log)
results[server.id] = log
return results