系统设置增加备份恢复:中控本机备份,并可下载各客户端备份。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-04 06:50:44 +08:00
parent 9f3a1f5b66
commit a983fff6e0
7 changed files with 720 additions and 2 deletions
+63
View File
@@ -269,6 +269,69 @@ def pull_all_stats(window: str = "7d") -> list[dict[str, Any]]:
return results
def fetch_backup_list(inst: dict[str, Any]) -> dict[str, Any]:
base = (inst.get("base_url") or "").rstrip("/")
token = (inst.get("bridge_token") or "").strip()
if not base or not token:
return {"ok": False, "msg": "缺少 URL 或 Token", "items": []}
url = f"{base}/api/hub/backup/list"
try:
r = requests.get(
url,
headers={"X-Hub-Token": token, "Accept": "application/json"},
timeout=_timeout(),
)
data = r.json() if r.content else {}
if isinstance(data, dict):
data.setdefault("instance_id", inst.get("id"))
data.setdefault("instance_name", inst.get("name"))
return data
return {"ok": False, "msg": f"HTTP {r.status_code}", "items": []}
except Exception as e:
return {
"ok": False,
"msg": str(e) or "拉取失败",
"items": [],
"instance_id": inst.get("id"),
"instance_name": inst.get("name"),
}
def fetch_backup_file(inst: dict[str, Any], name: str) -> tuple[bool, bytes | None, str]:
"""返回 (ok, content, msg_or_filename)."""
base = (inst.get("base_url") or "").rstrip("/")
token = (inst.get("bridge_token") or "").strip()
safe = (name or "").strip()
if not base or not token or not safe:
return False, None, "参数不完整"
url = f"{base}/api/hub/backup/download/{safe}"
try:
r = requests.get(
url,
headers={"X-Hub-Token": token, "Accept": "application/gzip, application/json"},
timeout=max(_timeout(), 60.0),
stream=True,
)
ctype = (r.headers.get("content-type") or "").lower()
if r.status_code != 200:
try:
data = r.json()
return False, None, (data.get("msg") if isinstance(data, dict) else None) or f"HTTP {r.status_code}"
except Exception:
return False, None, f"HTTP {r.status_code}"
if "json" in ctype:
try:
data = r.json()
return False, None, (data.get("msg") if isinstance(data, dict) else None) or "下载失败"
except Exception:
return False, None, "下载失败"
return True, r.content, os.path.basename(safe)
except requests.Timeout:
return False, None, "下载超时"
except Exception as e:
return False, None, str(e) or "下载失败"
def dispatch_cond_order_to_ids(instance_ids: list[int], payload: dict[str, Any]) -> list[dict[str, Any]]:
results = []
for iid in instance_ids: