修复bug

This commit is contained in:
dekun
2026-05-23 16:39:35 +08:00
parent c66c9f01c5
commit 952d57ab6d
7 changed files with 77 additions and 13 deletions
+4
View File
@@ -6394,6 +6394,10 @@ def strategy_roll_page():
return redirect("/strategy") return redirect("/strategy")
# 根目录 strategy_* 与币安/Gate 共用同一套属性名(OKX 内部仍用 normalize_okx_symbol / ensure_okx_live_ready
normalize_exchange_symbol = normalize_okx_symbol
ensure_exchange_live_ready = ensure_okx_live_ready
from strategy_register import install_strategy_trading from strategy_register import install_strategy_trading
from strategy_trend_register import install_strategy_trend from strategy_trend_register import install_strategy_trend
+2 -2
View File
@@ -14,8 +14,8 @@ HUB_PORT=5100
# HUB_BRIDGE_TOKEN=your-long-random-token # HUB_BRIDGE_TOKEN=your-long-random-token
# 逗号分隔的账户 id,强制关闭(不参与监控/全局全平;设置页对应行勾选框灰掉) # 逗号分隔的账户 id,强制关闭(不参与监控/全局全平;设置页对应行勾选框灰掉)
# 默认 1 = OKX;不用 OKX 可保持;要用 OKX 请删掉本行或改为空 # 留空 = 不强制关闭;仅不想用 OKX 时可设 HUB_DISABLED_IDS=1
HUB_DISABLED_IDS=1 HUB_DISABLED_IDS=
# true=允许 RFC1918 私网访问中控页面;false=仅 127.0.0.1 # true=允许 RFC1918 私网访问中控页面;false=仅 127.0.0.1
HUB_TRUST_LAN=true HUB_TRUST_LAN=true
+34
View File
@@ -0,0 +1,34 @@
"""加载 manual_trading_hub/.envWindows 直接 python hub.py 时也需要)。"""
from __future__ import annotations
import os
from pathlib import Path
HUB_DIR = Path(__file__).resolve().parent
def load_hub_dotenv() -> None:
path = HUB_DIR / ".env"
if not path.is_file():
return
raw_bytes = path.read_bytes()
text = ""
for enc in ("utf-8-sig", "utf-16", "utf-16-le", "utf-16-be"):
try:
text = raw_bytes.decode(enc)
break
except Exception:
continue
if not text:
text = raw_bytes.decode("utf-8", errors="ignore")
text = text.replace("\x00", "")
for line in text.splitlines():
raw = line.strip()
if not raw or raw.startswith("#") or "=" not in raw:
continue
key, value = raw.split("=", 1)
clean_key = key.strip().lstrip("\ufeff")
if not clean_key.replace("_", "").isalnum():
continue
clean_value = value.strip().strip('"').strip("'")
os.environ[clean_key] = clean_value
+18 -4
View File
@@ -8,6 +8,10 @@ import asyncio
import os import os
from pathlib import Path from pathlib import Path
from env_load import load_hub_dotenv
load_hub_dotenv()
import httpx import httpx
from fastapi import Body, FastAPI, HTTPException, Request from fastapi import Body, FastAPI, HTTPException, Request
from fastapi.responses import FileResponse, JSONResponse from fastapi.responses import FileResponse, JSONResponse
@@ -39,7 +43,7 @@ HUB_BRIDGE_TOKEN = (os.getenv("HUB_BRIDGE_TOKEN") or os.getenv("CONTROL_TOKEN")
_trust_raw = (os.getenv("HUB_TRUST_LAN", "true") or "").strip().lower() _trust_raw = (os.getenv("HUB_TRUST_LAN", "true") or "").strip().lower()
HUB_TRUST_LAN = _trust_raw not in ("0", "false", "no", "off") HUB_TRUST_LAN = _trust_raw not in ("0", "false", "no", "off")
DIR = Path(__file__).resolve().parent DIR = Path(__file__).resolve().parent
HUB_BUILD = "20260521-no-trade-ui" HUB_BUILD = "20260522-settings-fix"
def _is_local(host: str | None) -> bool: def _is_local(host: str | None) -> bool:
@@ -224,9 +228,17 @@ class SettingsBody(BaseModel):
@app.post("/api/settings") @app.post("/api/settings")
def api_save_settings(body: SettingsBody): def api_save_settings(body: SettingsBody):
data = {"version": 1, "exchanges": body.exchanges} force_off = env_force_disabled_ids()
save_settings(data) to_save = []
return {"ok": True} for ex in body.exchanges:
row = dict(ex)
eid = str(row.get("id", "")).strip()
if eid in force_off:
row["enabled"] = False
row.pop("env_disabled", None)
to_save.append(row)
save_settings({"version": 1, "exchanges": to_save})
return {"ok": True, "settings": load_settings()}
@app.get("/api/settings/meta") @app.get("/api/settings/meta")
@@ -438,6 +450,8 @@ def api_ping():
"trade_ui": False, "trade_ui": False,
"features": ["monitor", "settings", "auth"], "features": ["monitor", "settings", "auth"],
"password_required": password_required(), "password_required": password_required(),
"env_disabled_ids": sorted(env_force_disabled_ids()),
"hub_disabled_ids_raw": (os.getenv("HUB_DISABLED_IDS") or ""),
} }
+3 -2
View File
@@ -27,7 +27,7 @@ DEFAULT_EXCHANGES = [
"agent_url": "http://127.0.0.1:15201", "agent_url": "http://127.0.0.1:15201",
"flask_url": "http://127.0.0.1:5004", "flask_url": "http://127.0.0.1:5004",
"review_url": "http://127.0.0.1:5004/records", "review_url": "http://127.0.0.1:5004/records",
"enabled": False, "enabled": True,
"capabilities": ["key", "trend"], "capabilities": ["key", "trend"],
}, },
{ {
@@ -60,7 +60,8 @@ def _ids_from_csv(raw: str | None) -> set[str]:
def env_force_disabled_ids() -> set[str]: def env_force_disabled_ids() -> set[str]:
raw = os.getenv("HUB_DISABLED_IDS", "1").strip() # 未设置时默认不强制关闭任何账户;要用旧行为可设 HUB_DISABLED_IDS=1
raw = (os.getenv("HUB_DISABLED_IDS") or "").strip()
return _ids_from_csv(raw) return _ids_from_csv(raw)
+14 -3
View File
@@ -270,8 +270,11 @@
else parts.push("中控未设 HUB_BRIDGE_TOKEN(实例需 APP_AUTH_DISABLED 或同令牌)"); else parts.push("中控未设 HUB_BRIDGE_TOKEN(实例需 APP_AUTH_DISABLED 或同令牌)");
if (m.public_origin) parts.push("浏览器外链基址: " + m.public_origin); if (m.public_origin) parts.push("浏览器外链基址: " + m.public_origin);
else parts.push("未设 HUB_PUBLIC_ORIGIN(复盘链接仅本机可开)"); else parts.push("未设 HUB_PUBLIC_ORIGIN(复盘链接仅本机可开)");
if ((m.env_disabled_ids || []).length) if ((m.env_disabled_ids || []).length) {
parts.push("环境强制关闭 id: " + m.env_disabled_ids.join(", ")); parts.push("环境强制关闭 id: " + m.env_disabled_ids.join(", ") + "(改 .env 后须重启 hub");
} else {
parts.push("HUB_DISABLED_IDS 未强制关闭任何账户");
}
el.textContent = parts.join(" · "); el.textContent = parts.join(" · ");
} catch (_) {} } catch (_) {}
} }
@@ -361,7 +364,13 @@
const j = await r.json(); const j = await r.json();
if (j.ok) { if (j.ok) {
showToast("设置已保存(已写入 hub_settings.json"); showToast("设置已保存(已写入 hub_settings.json");
await loadSettingsUI(); if (j.settings) {
settingsCache = j.settings;
renderSettingsList(j.settings);
loadSettingsMetaLine();
} else {
await loadSettingsUI();
}
} else showToast("保存失败", true); } else showToast("保存失败", true);
} catch (e) { } catch (e) {
showToast(String(e), true); showToast(String(e), true);
@@ -395,10 +404,12 @@
}); });
settingsCache = data; settingsCache = data;
renderSettingsList(data); renderSettingsList(data);
showToast("已添加一行,请填写 URL 后点「保存设置」");
}; };
initAuth().then((ok) => { initAuth().then((ok) => {
if (!ok) return; if (!ok) return;
loadSettings().catch(() => {});
setActiveNav(); setActiveNav();
window.addEventListener("popstate", setActiveNav); window.addEventListener("popstate", setActiveNav);
}); });
+2 -2
View File
@@ -7,7 +7,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="/assets/app.css" /> <link rel="stylesheet" href="/assets/app.css?v=20260522-settings-fix" />
</head> </head>
<body> <body>
<div class="app-bg" aria-hidden="true"></div> <div class="app-bg" aria-hidden="true"></div>
@@ -80,6 +80,6 @@
</div> </div>
<div id="toast"></div> <div id="toast"></div>
<script src="/assets/app.js"></script> <script src="/assets/app.js?v=20260522-settings-fix"></script>
</body> </body>
</html> </html>