代码修复: - web/agent/agent.py: datetime.utcnow() → datetime.now(timezone.utc) - requirements.txt: 移除 paramiko==3.5.0 (已无活跃引用) - 删除 server/infrastructure/ssh/pool.py (DEPRECATED, 无引用) 连接池三层对齐 (config.py/.env.example/session.py): - DB_POOL_SIZE 100→160, DB_MAX_OVERFLOW 100→120 - 基于 MySQL max_connections=400, install.php 公式 文档修复 (21项): - P0: 硬编码域名/IP替换为配置变量占位符 - P1: 10个过时设计文档加归档标注 (引用旧文件结构) - P2: step-3-webssh报告 paramiko引用修正 - 6份审查报告连接池参数勘误 100/100→160/120 - ECC安全报告 EC5 datetime.utcnow 标记已修复 - docs/README.md 文档索引重写 - docs/memory/mem_nexus_overview.md 移除硬编码凭证 - docs/project/tech-stack-inventory.md paramiko标记已移除 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
28 KiB
简化推送模型 实现计划
⚠️ 已归档 — 2026-05-22 本文档引用的文件结构已过时:
server/database.py已拆分为server/domain/models/;server/api/tasks.py已删除;PHP 前端 (servers.php/tasks.php/push.php) 已迁移到 Tailwind CSS v4 + Alpine.js;sync_engine 已改为 asyncssh。功能已实现,保留本文档供历史参考。
面向 AI 代理的工作者: 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(
- [ ])语法来跟踪进度。
目标: 将复杂 SyncTask 模型简化为 Server.target_path + 批量 push API + 分类选择推送界面
架构: Server 模型新增 target_path/category 字段;删除 SyncTask 表;新增 POST /api/servers/push 批量推送端点;sync_engine 简化为纯单向 push;前端 servers.php 加入分类推送对话框
技术栈: Python FastAPI + SQLAlchemy + MySQL + PHP 7.4 + rsync
任务 1:数据库模型变更
文件:
-
修改:
server/database.py(全文件) -
修改:
server/config.py:27(删 SYNC_INTERVAL_SECONDS) -
修改:
.env.example:26-30(删 trigger/sync 配置) -
步骤 1:Server 模型加 target_path + category,删 SyncTask,调整 SyncLog
修改 server/database.py,在 Server 模型的 description 字段后添加:
target_path = Column(String(500), nullable=True, comment="推送目标路径")
category = Column(String(100), nullable=True, comment="服务器分类(如 Web服务器/API服务器)")
删除整个 SyncTask 类(第 112-141 行)。
修改 SyncLog 类:删除 task_id 列,添加 source_path、target_path 列:
class SyncLog(Base):
"""Push operation log"""
__tablename__ = "sync_logs"
id = Column(Integer, primary_key=True, autoincrement=True)
server_id = Column(Integer, ForeignKey("servers.id"), nullable=False)
source_path = Column(String(500), nullable=False, comment="源目录")
target_path = Column(String(500), nullable=False, comment="目标目录")
trigger_type = Column(String(20), comment="触发类型: manual")
status = Column(String(20), comment="pending/running/success/failed")
files_total = Column(Integer, default=0)
files_transferred = Column(Integer, default=0)
files_skipped = Column(Integer, default=0)
bytes_transferred = Column(Integer, default=0)
duration_seconds = Column(Integer, default=0)
diff_summary = Column(Text, nullable=True, comment="rsync输出摘要")
error_message = Column(Text, nullable=True)
started_at = Column(DateTime, default=datetime.datetime.utcnow)
finished_at = Column(DateTime, nullable=True)
server = relationship("Server", back_populates="sync_logs")
更新 Server 模型的 relationship(删 sync_tasks):
sync_logs = relationship("SyncLog", back_populates="server", cascade="all, delete-orphan")
- 步骤 2:删 config.py 中 SYNC_INTERVAL_SECONDS
# 删除以下行:
# SYNC_INTERVAL_SECONDS: int = 300 # 5 minutes
- 步骤 3:更新 .env.example
删 NEXUS_SYNC_INTERVAL_SECONDS 和 NEXUS_TRIGGER_FILENAME 行。
- 步骤 4:验证数据库初始化
python -m py_compile server/database.py && python -m py_compile server/config.py && echo "OK"
- 步骤 5:Commit
git add server/database.py server/config.py .env.example
git commit -m "refactor(db): 简化数据模型 — Server 加 target_path/category,删 SyncTask,调整 SyncLog"
任务 2:Python API 变更 — 删除 tasks 路由 + 新增 push 端点 + 清理 agent
文件:
-
删除:
server/api/tasks.py -
修改:
server/api/servers.py(添加 push 端点 + ServerCreate/Update/Response 更新) -
修改:
server/api/agent.py(删除 /trigger 端点) -
修改:
server/main.py(删除 tasks 路由注册,更新 imports) -
步骤 1:删除 tasks.py + 清理 main.py 中 tasks 路由
rm server/api/tasks.py
修改 server/main.py,删除 from server.api import servers, tasks, agent, auth 中的 tasks → 改为 from server.api import servers, agent, auth。删除 tasks.router 的 app.include_router 调用(含 dependencies 行)。
- 步骤 2:更新 servers.py — Pydantic 模型加 target_path/category
修改 server/api/servers.py 的 ServerCreate、ServerUpdate、ServerResponse:
class ServerCreate(BaseModel):
name: str
domain: str
port: int = 22
username: str = "root"
auth_method: str = "key"
password: Optional[str] = None
ssh_key_path: Optional[str] = None
agent_port: int = 8601
agent_api_key: Optional[str] = None
description: Optional[str] = None
target_path: Optional[str] = None # 新增
category: Optional[str] = None # 新增
class ServerUpdate(BaseModel):
# ... 所有已有字段 ...
target_path: Optional[str] = None # 新增
category: Optional[str] = None # 新增
class ServerResponse(BaseModel):
id: int
name: str
domain: str
port: int
username: str
auth_method: str
agent_port: int
description: Optional[str]
target_path: Optional[str] # 新增
category: Optional[str] # 新增
is_online: bool
last_heartbeat: Optional[datetime]
system_info: Optional[str]
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
- 步骤 3:servers.py 添加 POST /push 批量推送端点
在 server/api/servers.py 末尾添加:
from pydantic import BaseModel as PydanticBase
class PushRequest(PydanticBase):
source_path: str
server_ids: List[int]
@router.post("/push")
async def push_to_servers(req: PushRequest, db: Session = Depends(get_db)):
"""批量推送到多台服务器"""
if not req.server_ids:
raise HTTPException(status_code=400, detail="No servers selected")
servers_list = db.query(Server).filter(Server.id.in_(req.server_ids)).all()
if not servers_list:
raise HTTPException(status_code=404, detail="No servers found")
from server.services.sync_engine import push_to_server
import asyncio
tasks = []
for server in servers_list:
target = server.target_path or "/"
tasks.append(push_to_server(server, req.source_path, target))
results = await asyncio.gather(*tasks, return_exceptions=True)
successes = sum(1 for r in results if isinstance(r, dict) and r.get("status") == "success")
return {
"total": len(servers_list),
"success": successes,
"failed": len(servers_list) - successes,
"results": [
{"server_id": s.id, "server_name": s.name, **r}
if isinstance(r, dict) else
{"server_id": s.id, "server_name": s.name, "status": "error", "message": str(r)}
for s, r in zip(servers_list, results)
],
}
- 步骤 4:删除 agent.py 中 /trigger 端点
修改 server/api/agent.py,删除 TriggerPayload 类和 @router.post("/trigger") 整个函数。
- 步骤 5:验证编译
python -m py_compile server/api/servers.py server/api/agent.py server/main.py && echo "OK"
- 步骤 6:Commit
git add server/api/ server/main.py
git rm server/api/tasks.py 2>/dev/null
git commit -m "refactor(api): 删除 tasks 路由,新增 POST /api/servers/push,清理 agent trigger"
任务 3:服务层简化 — sync_engine 纯 push + 清理 ssh_direct + 删除 trigger_poller
文件:
-
修改:
server/services/sync_engine.py -
修改:
server/services/ssh_direct.py -
删除:
server/services/trigger_poller.py -
修改:
server/main.py(启动逻辑中删除 trigger_poller 引用) -
步骤 1:重写 sync_engine.py 为纯 push
"""Sync engine — push source directory to target server via rsync"""
import asyncio
from datetime import datetime
from typing import Dict, Any
from server.database import SessionLocal, SyncLog, Server
from server.config import settings
async def push_to_server(server: Server, source_path: str, target_path: str) -> Dict[str, Any]:
"""Push source_path to server:target_path via rsync over SSH"""
db = SessionLocal()
try:
log = SyncLog(
server_id=server.id,
source_path=source_path,
target_path=target_path,
trigger_type="manual",
status="running",
started_at=datetime.utcnow(),
)
db.add(log)
db.commit()
db.refresh(log)
ssh_opts = f"-p {server.port} -o ConnectTimeout={settings.HEALTH_CHECK_TIMEOUT}"
if not settings.SSH_STRICT_HOST_CHECKING:
ssh_opts += " -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
if server.auth_method == "key" and server.ssh_key_path:
ssh_opts += f" -i {server.ssh_key_path}"
cmd = [
"rsync", "-avz", "--stats",
"-e", f"ssh {ssh_opts}",
f"{source_path.rstrip('/')}/",
f"{server.username}@{server.domain}:{target_path.rstrip('/')}/",
]
start = datetime.utcnow()
proc = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await proc.communicate()
end = datetime.utcnow()
output = stdout.decode()
err = stderr.decode()
duration = int((end - start).total_seconds())
# 解析统计
import re
files_total = files_transferred = bytes_transferred = 0
for line in output.split("\n"):
if m := re.search(r'Number of files(?:\s+transferred)?:\s*([\d,]+)', line):
if "transferred" in line:
files_transferred = int(m.group(1).replace(",", ""))
else:
files_total = int(m.group(1).replace(",", ""))
elif m := re.search(r'Total (?:transferred )?file size:\s*([\d,.]+)\s*bytes', line):
bytes_transferred = int(m.group(1).replace(",", "").replace(".", ""))
success = proc.returncode == 0
status = "success" if success else "failed"
log.status = status
log.files_total = files_total
log.files_transferred = files_transferred
log.files_skipped = files_total - files_transferred
log.bytes_transferred = bytes_transferred
log.duration_seconds = duration
log.diff_summary = output[-2000:] if len(output) > 2000 else output
log.error_message = err if err else None
log.finished_at = end
db.commit()
summary = f"Transferred {files_transferred} files ({bytes_transferred} bytes) in {duration}s"
return {
"status": status,
"message": summary if success else f"Failed: {err[:200]}",
"log_id": log.id,
"duration": duration,
"files_transferred": files_transferred,
"bytes_transferred": bytes_transferred,
}
except Exception as e:
if 'log' in locals():
try:
log.status = "failed"
log.error_message = str(e)
log.finished_at = datetime.utcnow()
db.commit()
except Exception:
pass
return {"status": "failed", "message": str(e)}
finally:
db.close()
这完全替换原有的 compute_diff、execute_sync、trigger_sync_task 函数。
- 步骤 2:清理 ssh_direct.py — 删除触发文件轮询
删除 ssh_trigger_poll_loop 函数、check_trigger_file、remove_trigger_file、check_triggers_on_server、list_remote_dir 函数,以及 _trigger_locks、_trigger_lock_key 变量和 import Dict。
保留:ssh_health_check_loop、check_server_health_ssh、SSHConfig、get_ssh_config、create_ssh_client、ssh_connection、exec_command。
- 步骤 3:删除 trigger_poller.py
rm server/services/trigger_poller.py
- 步骤 4:更新 main.py 启动逻辑
修改 server/main.py 第 79-103 行,三种模式都删除 trigger_poll_loop 相关:
if SYNC_MODE == "ssh_direct":
from server.services.ssh_direct import ssh_health_check_loop
background_tasks.append(asyncio.create_task(ssh_health_check_loop()))
elif SYNC_MODE == "agent":
from server.services.health_checker import health_check_loop
background_tasks.append(asyncio.create_task(health_check_loop()))
else:
from server.services.ssh_direct import ssh_health_check_loop
from server.services.health_checker import health_check_loop
background_tasks.append(asyncio.create_task(ssh_health_check_loop()))
background_tasks.append(asyncio.create_task(health_check_loop()))
- 步骤 5:验证编译
python -m py_compile server/services/sync_engine.py server/services/ssh_direct.py server/main.py && echo "OK"
- 步骤 6:Commit
git add server/services/sync_engine.py server/services/ssh_direct.py server/main.py
git rm server/services/trigger_poller.py
git commit -m "refactor(services): sync_engine 简化为纯 push,删除 trigger_poller,清理 ssh_direct"
任务 4:PHP API 客户端更新
文件:
-
修改:
web/api_client.php -
修改:
web/api_proxy.php -
步骤 1:api_client.php 删 task 方法,加 pushServers
删除:getTasks、getTask、createTask、updateTask、deleteTask、triggerSync、getTaskDiff、getTaskLogs 方法。
删除 getDashboardStats 中的 tasks 部分,简化为只统计 servers。
新增方法:
public function pushServers($sourcePath, $serverIds) {
return $this->request('POST', '/api/servers/push', [
'source_path' => $sourcePath,
'server_ids' => $serverIds,
]);
}
- 步骤 2:api_proxy.php 删 task action,加 push
删除 case:get_tasks、get_task、trigger_sync、sync_all、get_diff、get_task_logs、get_logs(logs 数据通过 sync_logs API 读取)。
新增 case:
case 'push_servers':
$input = json_decode(file_get_contents('php://input'), true);
$result = api()->pushServers($input['source_path'] ?? '', $input['server_ids'] ?? []);
break;
- 步骤 3:Commit
git add web/api_client.php web/api_proxy.php
git commit -m "refactor(php): API 客户端删除 task 方法,新增 pushServers"
任务 5:前端 — servers.php 加入分类 + 目标路径 + 推送对话框
文件:
-
修改:
web/servers.php -
步骤 1:列表表头加分类和目标路径列
在 servers.php 表格表头 <th>Agent端口</th> 后添加:
<th>分类</th>
<th>目标路径</th>
在循环中对应位置添加:
<td><?= htmlspecialchars($s['category'] ?? '-') ?></td>
<td class="path-cell"><?= htmlspecialchars($s['target_path'] ?? '-') ?></td>
- 步骤 2:编辑表单加分类下拉和目标路径输入框
在 servers.php 的 <form> 中,description 字段前添加:
<div class="form-row">
<div class="form-group">
<label>服务器分类</label>
<input type="text" name="category" class="form-control"
value="<?= htmlspecialchars($server['category'] ?? '') ?>"
placeholder="如: Web服务器, API服务器"
list="categoryList">
<datalist id="categoryList">
<option value="Web服务器">
<option value="API服务器">
<option value="数据库服务器">
<option value="缓存服务器">
</datalist>
</div>
<div class="form-group">
<label>推送目标路径</label>
<input type="text" name="target_path" class="form-control"
value="<?= htmlspecialchars($server['target_path'] ?? '') ?>"
placeholder="/var/www/html">
</div>
</div>
- 步骤 3:添加 "推送" 按钮 + 推送对话框 CSS/HTML/JS
在 servers.php 的顶部操作区添加:
<button class="btn btn-success" onclick="openPushDialog()">
<i class="fas fa-paper-plane"></i> 推送文件
</button>
添加推送对话框样式(参考 tasks.php 的 .fm-overlay 风格,更紧凑):
.push-overlay { display:none; position:fixed; top:0;left:0;right:0;bottom:0; background:rgba(0,0,0,0.4); z-index:9999; align-items:center;justify-content:center; }
.push-overlay.active { display:flex; }
.push-dialog { background:#fff; border-radius:8px; width:90%; max-width:700px; max-height:85vh; overflow-y:auto; box-shadow:0 20px 50px rgba(0,0,0,0.3); }
.push-dialog h3 { padding:16px 20px; margin:0; border-bottom:1px solid #e5e7eb; font-size:16px; }
.push-body { padding:20px; }
.push-body .src-row { display:flex; gap:8px; align-items:center; margin-bottom:16px; }
.push-body .src-row input { flex:1; }
.push-category { margin-bottom:12px; }
.push-cat-title { font-weight:600; font-size:14px; padding:8px 0; cursor:pointer; display:flex; align-items:center; gap:6px; }
.push-cat-title .count { font-weight:400; color:#9ca3af; font-size:12px; }
.push-cat-title .cat-check { margin-left:auto; font-weight:400; font-size:12px; color:#3b82f6; cursor:pointer; }
.push-server { display:flex; align-items:center; gap:8px; padding:6px 12px; margin-left:20px; font-size:13px; }
.push-server label { flex:1; cursor:pointer; }
.push-server .path { color:#6b7280; font-size:12px; }
.push-footer { padding:12px 20px; border-top:1px solid #e5e7eb; display:flex; align-items:center; gap:12px; }
.push-footer .btn-push { background:#22c55e; color:#fff; border:none; padding:10px 24px; border-radius:6px; font-weight:600; cursor:pointer; }
.push-footer .btn-push:hover { background:#16a34a; }
.push-footer .btn-push:disabled { background:#9ca3af; cursor:not-allowed; }
.push-footer .summary { flex:1; font-size:13px; color:#6b7280; }
推送对话框 HTML:
<div class="push-overlay" id="pushOverlay" onclick="if(event.target===this)closePushDialog()">
<div class="push-dialog">
<h3>推送文件</h3>
<div class="push-body">
<div class="src-row">
<input type="text" id="pushSourcePath" class="form-control"
placeholder="/www/wwwroot/你的项目" value="/www/wwwroot/">
<button type="button" class="btn-browse" onclick="FM.open('source','pushSourcePath')"><i class="fas fa-folder-open"></i></button>
</div>
<div class="push-category" id="allCheckRow">
<div class="push-cat-title">
<input type="checkbox" id="pushSelectAll" onchange="toggleAllServers()">
<label for="pushSelectAll">全选 (<span id="pushTotalCount">0</span> 台)</label>
</div>
</div>
<div id="pushCategoryList"></div>
</div>
<div class="push-footer">
<span class="summary" id="pushSummary">已选 0 台</span>
<button class="btn btn-secondary" onclick="closePushDialog()">取消</button>
<button class="btn-push" id="btnDoPush" onclick="doPush()">推送到 <span id="pushBtnCount">0</span> 台服务器</button>
</div>
</div>
</div>
推送 JS:
let pushServersData = [];
function loadPushServers() {
const API_BASE = <?= json_encode(defined('API_BASE_URL') ? API_BASE_URL : 'http://localhost:8600') ?>;
const API_KEY = <?= json_encode(defined('API_KEY') ? API_KEY : '') ?>;
fetch(API_BASE + '/api/servers/', { headers: API_KEY ? {'X-API-Key': API_KEY} : {} })
.then(r => r.json()).then(data => {
pushServersData = data.data || data;
renderPushDialog();
});
}
function renderPushDialog() {
const grouped = {};
pushServersData.forEach(s => {
const cat = s.category || '未分类';
if (!grouped[cat]) grouped[cat] = [];
grouped[cat].push(s);
});
document.getElementById('pushTotalCount').textContent = pushServersData.length;
let html = '';
for (const [cat, servers] of Object.entries(grouped)) {
html += '<div class="push-category"><div class="push-cat-title">' +
'<input type="checkbox" class="cat-check-all" onchange="toggleCategory(this)" data-cat="' + esc(cat) + '">' +
esc(cat) + ' <span class="count">(' + servers.length + ' 台)</span>' +
'<span class="cat-check" onclick="checkCategory(\'' + esc(cat) + '\')">全选此分类</span>' +
'</div>';
servers.forEach(s => {
html += '<div class="push-server">' +
'<input type="checkbox" class="server-check" data-id="' + s.id + '" data-cat="' + esc(cat) + '" onchange="updatePushCount()">' +
'<label>' + esc(s.name) + ' (' + esc(s.domain) + ')</label>' +
'<span class="path">→ ' + esc(s.target_path || '/') + '</span>' +
'</div>';
});
html += '</div>';
}
document.getElementById('pushCategoryList').innerHTML = html;
}
function openPushDialog() {
document.getElementById('pushOverlay').classList.add('active');
loadPushServers();
}
function closePushDialog() { document.getElementById('pushOverlay').classList.remove('active'); }
function toggleAllServers() {
const all = document.getElementById('pushSelectAll').checked;
document.querySelectorAll('.server-check').forEach(cb => cb.checked = all);
document.querySelectorAll('.cat-check-all').forEach(cb => cb.checked = all);
updatePushCount();
}
function toggleCategory(el) {
const cat = el.dataset.cat;
const checked = el.checked;
document.querySelectorAll('.server-check[data-cat="' + esc(cat) + '"]').forEach(cb => cb.checked = checked);
updatePushCount();
}
function checkCategory(cat) {
document.querySelectorAll('.server-check[data-cat="' + esc(cat) + '"]').forEach(cb => cb.checked = true);
document.querySelector('.cat-check-all[data-cat="' + esc(cat) + '"]').checked = true;
updatePushCount();
}
function updatePushCount() {
const checked = document.querySelectorAll('.server-check:checked').length;
document.getElementById('pushSummary').textContent = '已选 ' + checked + ' 台';
document.getElementById('pushBtnCount').textContent = checked;
document.getElementById('pushSelectAll').checked = checked === pushServersData.length;
document.getElementById('btnDoPush').disabled = checked === 0;
}
function doPush() {
const ids = Array.from(document.querySelectorAll('.server-check:checked')).map(cb => parseInt(cb.dataset.id));
if (!ids.length) return;
const sp = document.getElementById('pushSourcePath').value.trim();
if (!sp) { alert('请输入源目录'); return; }
document.getElementById('btnDoPush').disabled = true;
document.getElementById('btnDoPush').textContent = '推送中...';
fetch('api_proxy.php?action=push_servers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ source_path: sp, server_ids: ids })
}).then(r => r.json()).then(data => {
const d = data.data || data;
alert('推送完成: 成功 ' + (d.success || 0) + ' / 失败 ' + (d.failed || 0));
closePushDialog();
location.reload();
}).catch(e => {
alert('推送失败: ' + e.message);
document.getElementById('btnDoPush').disabled = false;
document.getElementById('btnDoPush').innerHTML = '推送到 <span id="pushBtnCount">' + ids.length + '</span> 台服务器';
});
}
function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
- 步骤 4:sidebar 导航删除 "同步任务" 链接,添加 "推送日志"
<li><a href="logs.php"><i class="fas fa-history"></i> <span>推送日志</span></a></li>
删掉 <li><a href="tasks.php">... 那一行。
- 步骤 5:Commit
git add web/servers.php
git commit -m "feat(web): servers.php 加入分类/目标路径编辑 + 分类批量推送对话框"
任务 6:tasks.php → push.php + index.php + logs.php 更新
文件:
-
重命名:
web/tasks.php→web/push.php -
修改:
web/index.php -
修改:
web/logs.php -
修改:
web/servers.php(sidebar 链接更新) -
步骤 1:tasks.php 改为推送历史页
mv web/tasks.php web/push.php
修改 push.php:删掉所有任务表单/编辑/添加/删除相关代码。保留表格结构,改为显示 SyncLog(推送历史):
$logs = [];
$resp = api()->getAllLogs(100);
$logs = $resp['data'] ?? [];
表格列改为:ID、服务器、源目录、目标目录、状态、文件数、传输大小、耗时、时间。
- 步骤 2:index.php 仪表盘更新
删除 "活跃同步任务" 卡片。新增 "推送目标服务器" 卡片(统计有 target_path 的服务器数)。
dashboard stats 简化为:服务器总数、在线服务器、离线服务器、已配置推送目标。
- 步骤 3:logs.php 简化
删除 task_id 筛选,保留 server_id 和 status 筛选。
- 步骤 4:所有页面 sidebar 链接更新
将 <a href="tasks.php"> 改为 <a href="push.php">推送日志</a>。
在 servers.php、index.php、push.php、logs.php、settings.php、deploy.php 的 sidebar 中统一修改。
- 步骤 5:Commit
git add web/
git commit -m "refactor(web): tasks.php→push.php 推送历史页,仪表盘/日志简化,sidebar 统一"
任务 7:脚本和配置文件清理
文件:
-
修改:
install.sh -
修改:
deploy/install_central.sh -
修改:
deploy/install_agent.sh -
修改:
agent/install.sh -
修改:
agent/agent.sh -
修改:
agent/config.example.yaml -
修改:
web/agent/install.sh -
修改:
web/agent/agent.sh -
修改:
web/deploy.php -
步骤 1:agent 脚本删除 trigger 相关
agent/agent.sh 删除 notify_central 函数和 watch_directory 中对 TRIGGER_FILE 的检测逻辑。保留 heartbeat。
agent/install.sh 和 web/agent/install.sh 删除 NEXUS_TRIGGER_FILE 环境变量行。
agent/config.example.yaml 删除 trigger_file 字段。
- 步骤 2:install.sh 清理
删除 --db-path 参数和 DB_PATH 变量。更新 help 文本删除 trigger/cron 相关说明。
- 步骤 3:deploy/install_central.sh 清理
删除 trigger/sync 相关 echo 提示。
- 步骤 4:deploy/install_agent.sh 清理
删除 trigger_file 配置项。
- 步骤 5:web/deploy.php 清理
删除 .sync-trigger 命令示例文本。
- 步骤 6:Commit
git add agent/ deploy/ install.sh web/agent/ web/deploy.php
git commit -m "chore: 清理 scripts/config 中的 trigger/task 引用"
任务 8:最终验证和文档更新
文件:
-
修改:
docs/CHANGELOG.md -
修改:
README.md(部分) -
修改:
宝塔部署指南.md(部分) -
步骤 1:全量 Python 编译验证
find server -name "*.py" | xargs python -m py_compile && echo "All Python OK"
- 步骤 2:更新 CHANGELOG.md
追加迭代 2 记录。
- 步骤 3:更新 README.md 架构图和特性列表
删除 sync_tasks / trigger_file / cron 相关描述。更新系统架构图。
- 步骤 4:Commit
git add docs/ README.md 宝塔部署指南.md
git commit -m "docs: 更新 CHANGELOG/README/宝塔指南 适配简化推送模型"
自检
- 规格全覆盖:数据模型 ✓ API ✓ 服务层 ✓ 前端 ✓ 脚本 ✓ 文档 ✓
- 无占位符/TODO
- 类型一致性:
push_to_server(server, source_path, target_path)签名在 sync_engine 和 servers.py 一致 - 每个任务都有精确文件路径和完整代码