# servers.php 添加服务器优化 — 实现计划 > ⚠️ **已归档** — 2026-05-22 > 本文档引用的文件结构已过时:`web/servers.php` 已迁移到 `web/app/servers.html` (Tailwind CSS v4 + Alpine.js);`server/services/ssh_direct.py` 已被 asyncssh 连接池替代;`server/database.py` 已拆分为 `server/domain/models/`。功能已实现,保留本文档供历史参考。 **基于设计**: docs/superpowers/specs/2026-05-17-server-add-optimize-design.md **日期**: 2026-05-17 ## 任务 1 — 后端:create_server 自动生成密钥 + 目标路径入表单 **文件**: `server/api/servers.py` ```python # ServerCreate 模型:加 target_path,agent_api_key 改为 Optional(不从前端收) class ServerCreate(BaseModel): ... target_path: str # 新增必填 agent_api_key: Optional[str] = None # 自动生成 # create_server 路由:自动生成 agent_api_key def create_server(server_data): import secrets data = server_data.model_dump() data["agent_api_key"] = data.get("agent_api_key") or secrets.token_hex(16) ... ``` **验证**: `curl -X POST /api/servers/ -d '{"name":"t","domain":"1.1.1.1","target_path":"/tmp"}'` 返回含 agent_api_key --- ## 任务 2 — 后端:异步 SSH 安装 Agent 服务 **文件**: 新建 `server/services/agent_installer.py` ```python async def install_agent_on_server(server_id: int): """SSH 到子服务器执行 Agent 安装脚本,通过 Redis pub/sub 推送进度""" # 1. 读 DB 获取 server # 2. 构造安装命令:curl install.sh | bash --url ... --key ... --dirs ... # 3. SSH exec_command 执行 # 4. 逐行读取 stdout → Redis PUBLISH install:{id}:log # 5. 完成/失败 → 更新 server.is_online + Redis state ``` **Redis 消息格式**: ``` PUBLISH install:3:log {"percent":45,"line":"[3/5] 下载 Agent..."} PUBLISH install:3:log {"percent":100,"line":"✅ 安装完成","status":"done"} ``` **安装命令**: ```bash curl -fsSL https://${NEXUS_DOMAIN}/agent/install.sh | bash -s -- \ --url http://${NEXUS_DOMAIN}:8600 \ --key \ --dirs ``` **验证**: `python -m py_compile server/services/agent_installer.py` 通过 --- ## 任务 3 — 后端:create_server 触发异步安装 **文件**: `server/api/servers.py` ```python def create_server(): ... db.commit(); db.refresh(server) # 触发后台安装 import asyncio from server.services.agent_installer import install_agent_on_server asyncio.create_task(install_agent_on_server(server.id)) return server ``` **验证**: 创建服务器后,查看 Redis `install:{id}:state` 是否有进度 --- ## 任务 4 — 后端:WebSocket 转发安装进度 **文件**: `server/main.py` ```python # 已有 broadcast_push_progress,新增 install 进度广播 async def broadcast_install_progress(server_id: int, data: dict): """将 Redis 安装进度转发到 WebSocket""" for ws in connected_ws: await ws.send_json({"type":"install_progress","server_id":server_id,**data}) ``` **验证**: WebSocket 连接后能收到 `{"type":"install_progress",...}` --- ## 任务 5 — 前端:表单改造 **文件**: `web/servers.php` **变更**: 1. 移除 `agent_api_key` 输入框 2. 隐藏 `agent_port` → 固定 8601 3. 新增 `target_path` 必填输入框 4. SSH 密码标记必填 5. 提交按钮改为「保存并安装 Agent」 ```html
``` **PHP POST 处理**: 加 `target_path` 到 `$formData` **验证**: 表单提交后服务器创建成功,密码可空→必填校验生效 --- ## 任务 6 — 前端:安装进度显示 + 日志弹窗 **文件**: `web/servers.php` **变更**: 1. 服务器列表:离线且 `install_state != done` → 显示进度条 2. WebSocket 监听 `install_progress` → 更新行内进度和日志弹窗 3. 新增日志弹窗 DOM ```js // 日志弹窗

安装日志:

``` **ws.onmessage 处理**: ```js if (msg.type === 'install_progress') { updateInstallProgress(msg.server_id, msg.percent, msg.line); if (msg.status === 'done') location.reload(); } ``` **验证**: 添加服务器后列表出现进度,点击弹出日志 --- ## 任务 7 — 前端:目标路径快速编辑 **文件**: `web/servers.php` **变更**: 1. 列表每行 `target_path` 后加 ✏️ 按钮 2. 点击 → `td` 内出现 `` + 确认按钮 3. 回车 → `PUT /api/servers/{id}` 更新 target_path ```html ``` ```js function editPath(id) { const span = document.getElementById('path_' + id); const old = span.textContent; span.innerHTML = '' + ''; } function savePath(id) { const v = document.getElementById('path_input_' + id).value; fetch(API_BASE + '/api/servers/' + id, {method:'PUT',headers:{'Content-Type':'application/json','X-API-Key':API_KEY}, body: JSON.stringify({target_path: v})}) .then(() => location.reload()); } ``` **验证**: 点击 ✏️ → 修改路径 → 回车 → 刷新后路径更新 --- ## 任务 8 — 端到端验证 1. 访问 `https://${NEXUS_DOMAIN}/servers.html` (添加服务器页面) 2. 填写表单、提交 3. 列表出现安装进度 4. 安装完成 → 绿点在线 5. ✏️ 编辑目标路径生效 **验证**: `mcp__multisync__run_test()` 全部通过