diff --git a/lib/env/shared_env_lib.py b/lib/env/shared_env_lib.py
index ceef445..af2634a 100644
--- a/lib/env/shared_env_lib.py
+++ b/lib/env/shared_env_lib.py
@@ -195,22 +195,27 @@ def apply_ai_env_to_all(updates: dict[str, str]) -> dict[str, Any]:
}
-def restart_hub_and_instances_pm2() -> dict[str, Any]:
+def restart_instances_then_hub_pm2() -> dict[str, Any]:
+ """先重启三实例,最后重启中控(避免当前请求被中断)。"""
if not sys.platform.startswith("linux"):
return {"ok": False, "msg": "仅 Linux 服务器支持 PM2 重启", "results": []}
from lib.instance.instance_pm2_lib import restart_instance_pm2
- apps = ["manual-trading-hub"]
results: list[dict[str, Any]] = []
- hub_result = _restart_pm2_app("manual-trading-hub")
- results.append({"app": "manual-trading-hub", **hub_result})
for ex in ("okx", "binance", "gate"):
r = restart_instance_pm2(ex)
results.append({"exchange": ex, **r})
+ hub_result = _restart_pm2_app("manual-trading-hub")
+ results.append({"app": "manual-trading-hub", **hub_result})
ok = all(r.get("ok") for r in results)
return {"ok": ok, "results": results}
+def restart_hub_and_instances_pm2() -> dict[str, Any]:
+ """兼容旧调用:与 restart_instances_then_hub_pm2 相同顺序。"""
+ return restart_instances_then_hub_pm2()
+
+
def _restart_pm2_app(app_name: str) -> dict[str, Any]:
try:
proc = subprocess.run(
diff --git a/manual_trading_hub/hub.py b/manual_trading_hub/hub.py
index 9dc6ee6..a7c2da7 100644
--- a/manual_trading_hub/hub.py
+++ b/manual_trading_hub/hub.py
@@ -110,7 +110,7 @@ from env_load import load_hub_dotenv
load_hub_dotenv()
import httpx
-from fastapi import Body, FastAPI, File, Form, HTTPException, Request, UploadFile
+from fastapi import BackgroundTasks, Body, FastAPI, File, Form, HTTPException, Request, UploadFile
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field
@@ -1706,27 +1706,21 @@ def api_get_ai_env(request: Request):
@app.post("/api/settings/ai-env")
-def api_save_ai_env(request: Request, body: HubAiEnvBody):
+def api_save_ai_env(request: Request, body: HubAiEnvBody, background_tasks: BackgroundTasks):
_require_hub_logged_in(request)
from hub_env_lib import get_hub_ai_env_payload, restart_all_pm2, save_hub_ai_env
result = save_hub_ai_env(body.values or {})
if not result.get("ok"):
raise HTTPException(status_code=400, detail="; ".join(result.get("errors") or ["保存失败"]))
- restart_result = None
- if body.restart and result.get("restart_required"):
- restart_result = restart_all_pm2()
- if not restart_result.get("ok"):
- raise HTTPException(
- status_code=500,
- detail="保存成功但 PM2 重启失败,请手动 restart",
- )
payload = get_hub_ai_env_payload()
+ restart_required = bool(body.restart and result.get("restart_required"))
+ if restart_required:
+ background_tasks.add_task(restart_all_pm2)
return {
"ok": True,
"changed": result.get("changed") or {},
- "restart_required": bool(result.get("restart_required")),
- "restart": restart_result,
+ "restart_required": restart_required,
"sync_status": payload.get("sync_status"),
}
diff --git a/manual_trading_hub/hub_env_lib.py b/manual_trading_hub/hub_env_lib.py
index ffc9ece..94ee7f8 100644
--- a/manual_trading_hub/hub_env_lib.py
+++ b/manual_trading_hub/hub_env_lib.py
@@ -41,7 +41,7 @@ def save_hub_ai_env(updates: dict[str, str]) -> dict[str, Any]:
def restart_all_pm2() -> dict[str, Any]:
- return restart_hub_and_instances_pm2()
+ return restart_instances_then_hub_pm2()
def restart_hub_pm2() -> dict[str, Any]:
diff --git a/manual_trading_hub/static/app.js b/manual_trading_hub/static/app.js
index b30b4d5..65224ad 100644
--- a/manual_trading_hub/static/app.js
+++ b/manual_trading_hub/static/app.js
@@ -4409,6 +4409,16 @@
return values;
}
+ async function parseApiJson(r) {
+ const ct = (r.headers.get("content-type") || "").toLowerCase();
+ if (!ct.includes("application/json")) {
+ const text = await r.text();
+ const snippet = (text || "").replace(/\s+/g, " ").trim().slice(0, 120);
+ throw new Error(snippet ? `服务返回非 JSON:${snippet}` : `HTTP ${r.status}`);
+ }
+ return r.json();
+ }
+
async function saveHubAiEnv() {
const status = document.getElementById("hub-ai-env-save-status");
const setStatus = (msg, err) => {
@@ -4423,15 +4433,17 @@
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ values: collectHubAiEnvValues(), restart: true }),
});
- const j = await r.json();
+ const j = await parseApiJson(r);
if (!r.ok) throw new Error(j.detail || j.msg || "保存失败");
if (!j.changed || !Object.keys(j.changed).length) {
setStatus("未修改(与当前配置相同)");
showToast("未修改");
return;
}
- setStatus("已同步三所,服务重启中…");
- await waitHubHealth();
+ if (j.restart_required) {
+ setStatus("已同步三所,服务重启中…");
+ await waitHubHealth();
+ }
setStatus("AI 配置已保存并同步至三所实例");
showToast("AI 配置已保存并同步");
await loadHubAiEnvSettings();
diff --git a/manual_trading_hub/static/index.html b/manual_trading_hub/static/index.html
index 62a812c..8b42f08 100644
--- a/manual_trading_hub/static/index.html
+++ b/manual_trading_hub/static/index.html
@@ -1192,6 +1192,6 @@
-
+