修复bug
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
"""加载 manual_trading_hub/.env(Windows 直接 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
|
||||||
@@ -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 ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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)");
|
||||||
|
if (j.settings) {
|
||||||
|
settingsCache = j.settings;
|
||||||
|
renderSettingsList(j.settings);
|
||||||
|
loadSettingsMetaLine();
|
||||||
|
} else {
|
||||||
await loadSettingsUI();
|
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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user