fix: route web controls through proxy auth
This commit is contained in:
@@ -260,7 +260,7 @@ Live-исполнение ведет журнал order intent до отправ
|
|||||||
|
|
||||||
- `GET /api/health` — healthcheck.
|
- `GET /api/health` — healthcheck.
|
||||||
- `GET /api/status` — статус бота, account snapshot, позиции.
|
- `GET /api/status` — статус бота, account snapshot, позиции.
|
||||||
- `GET /api/dashboard/snapshot` — компактный защищённый snapshot для веб-панели.
|
- `GET /api/dashboard/snapshot` — компактный защищённый snapshot для веб-панели; внешний UI использует эквивалентный `/web-api/dashboard/snapshot`, чтобы не смешивать Basic-auth браузера с токеном мобильного API.
|
||||||
- `GET /api/markets` — пары, ticker, свечи, инструменты.
|
- `GET /api/markets` — пары, ticker, свечи, инструменты.
|
||||||
- `GET /api/training/market-observations?symbol=BTCUSDT&after_id=0&limit=5000` — защищённая training-token выгрузка L1-наблюдений.
|
- `GET /api/training/market-observations?symbol=BTCUSDT&after_id=0&limit=5000` — защищённая training-token выгрузка L1-наблюдений.
|
||||||
- `GET /api/training/market-observations/manifest` — training-token manifest для инкрементальной синхронизации forward L1-данных.
|
- `GET /api/training/market-observations/manifest` — training-token manifest для инкрементальной синхронизации forward L1-данных.
|
||||||
|
|||||||
@@ -339,6 +339,7 @@ def create_app(settings: Settings | None = None) -> FastAPI:
|
|||||||
"backtest": _runtime_json(settings, "torch_threshold_calibration.json"),
|
"backtest": _runtime_json(settings, "torch_threshold_calibration.json"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@app.get("/web-api/dashboard/snapshot", include_in_schema=False)
|
||||||
@app.get("/api/dashboard/snapshot")
|
@app.get("/api/dashboard/snapshot")
|
||||||
async def dashboard_snapshot(_: None = Depends(authorizer.require)) -> dict[str, Any]:
|
async def dashboard_snapshot(_: None = Depends(authorizer.require)) -> dict[str, Any]:
|
||||||
market_data = market.snapshot()
|
market_data = market.snapshot()
|
||||||
@@ -376,6 +377,7 @@ def create_app(settings: Settings | None = None) -> FastAPI:
|
|||||||
"config": _safe_config(settings),
|
"config": _safe_config(settings),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@app.post("/web-api/config/fast-trading", include_in_schema=False)
|
||||||
@app.post("/api/config/fast-trading")
|
@app.post("/api/config/fast-trading")
|
||||||
async def set_fast_trading(
|
async def set_fast_trading(
|
||||||
payload: dict[str, Any],
|
payload: dict[str, Any],
|
||||||
@@ -387,11 +389,13 @@ def create_app(settings: Settings | None = None) -> FastAPI:
|
|||||||
response["env_persisted"] = env_persisted
|
response["env_persisted"] = env_persisted
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@app.post("/web-api/control/start", include_in_schema=False)
|
||||||
@app.post("/api/control/start")
|
@app.post("/api/control/start")
|
||||||
async def start(_: None = Depends(authorizer.require)) -> dict[str, Any]:
|
async def start(_: None = Depends(authorizer.require)) -> dict[str, Any]:
|
||||||
await bot.start()
|
await bot.start()
|
||||||
return bot.status().as_dict()
|
return bot.status().as_dict()
|
||||||
|
|
||||||
|
@app.post("/web-api/control/stop", include_in_schema=False)
|
||||||
@app.post("/api/control/stop")
|
@app.post("/api/control/stop")
|
||||||
async def stop(_: None = Depends(authorizer.require)) -> dict[str, Any]:
|
async def stop(_: None = Depends(authorizer.require)) -> dict[str, Any]:
|
||||||
await bot.stop()
|
await bot.stop()
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ async function loadSnapshot(manual = false, fromAuth = false) {
|
|||||||
$("#refreshButton").classList.add("is-spinning");
|
$("#refreshButton").classList.add("is-spinning");
|
||||||
if (manual) setText("syncLabel", "Обновление…");
|
if (manual) setText("syncLabel", "Обновление…");
|
||||||
try {
|
try {
|
||||||
const snapshot = await api("/api/dashboard/snapshot");
|
const snapshot = await api("/web-api/dashboard/snapshot");
|
||||||
state.snapshot = snapshot;
|
state.snapshot = snapshot;
|
||||||
render(snapshot);
|
render(snapshot);
|
||||||
setOffline(false);
|
setOffline(false);
|
||||||
@@ -411,7 +411,7 @@ async function requestControl(action) {
|
|||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
setControlsBusy(true);
|
setControlsBusy(true);
|
||||||
try {
|
try {
|
||||||
await api(`/api/control/${action}`, { method: "POST" });
|
await api(`/web-api/control/${action}`, { method: "POST" });
|
||||||
toast(start ? "Торговый цикл запущен." : "Торговый цикл остановлен.");
|
toast(start ? "Торговый цикл запущен." : "Торговый цикл остановлен.");
|
||||||
await loadSnapshot(true);
|
await loadSnapshot(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -436,7 +436,7 @@ async function onFastTradingChange(event) {
|
|||||||
if (!confirmed) { toggle.checked = previous; return; }
|
if (!confirmed) { toggle.checked = previous; return; }
|
||||||
toggle.disabled = true;
|
toggle.disabled = true;
|
||||||
try {
|
try {
|
||||||
const result = await api("/api/config/fast-trading", { method: "POST", body: JSON.stringify({ enabled }) });
|
const result = await api("/web-api/config/fast-trading", { method: "POST", body: JSON.stringify({ enabled }) });
|
||||||
toast(`Быстрая торговля ${enabled ? "включена" : "выключена"}${result.env_persisted === false ? " только в runtime" : ""}.`);
|
toast(`Быстрая торговля ${enabled ? "включена" : "выключена"}${result.env_persisted === false ? " только в runtime" : ""}.`);
|
||||||
await loadSnapshot(true);
|
await loadSnapshot(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -19,23 +19,23 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<nav class="nav-list">
|
<nav class="nav-list">
|
||||||
<button class="nav-item is-active" type="button" data-page="overview" aria-current="page">
|
<button class="nav-item is-active" type="button" data-page="overview" aria-label="Обзор" aria-current="page">
|
||||||
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M4 13h6V4H4v9Zm0 7h6v-5H4v5Zm10 0h6v-9h-6v9Zm0-16v5h6V4h-6Z"/></svg>
|
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M4 13h6V4H4v9Zm0 7h6v-5H4v5Zm10 0h6v-9h-6v9Zm0-16v5h6V4h-6Z"/></svg>
|
||||||
<span>Обзор</span>
|
<span>Обзор</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="nav-item" type="button" data-page="markets">
|
<button class="nav-item" type="button" data-page="markets" aria-label="Рынки">
|
||||||
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="m4 17 5-5 4 3 7-8v3l-7 8-4-3-5 5v-3Z"/></svg>
|
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="m4 17 5-5 4 3 7-8v3l-7 8-4-3-5 5v-3Z"/></svg>
|
||||||
<span>Рынки</span>
|
<span>Рынки</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="nav-item" type="button" data-page="positions">
|
<button class="nav-item" type="button" data-page="positions" aria-label="Позиции">
|
||||||
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M4 6h16v12H4V6Zm2 3v6h12V9H6Zm2 1h4v4H8v-4Z"/></svg>
|
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M4 6h16v12H4V6Zm2 3v6h12V9H6Zm2 1h4v4H8v-4Z"/></svg>
|
||||||
<span>Позиции</span>
|
<span>Позиции</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="nav-item" type="button" data-page="activity">
|
<button class="nav-item" type="button" data-page="activity" aria-label="Активность">
|
||||||
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M5 4h14v3H5V4Zm0 6h14v3H5v-3Zm0 6h14v3H5v-3Z"/></svg>
|
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M5 4h14v3H5V4Zm0 6h14v3H5v-3Zm0 6h14v3H5v-3Z"/></svg>
|
||||||
<span>Активность</span>
|
<span>Активность</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="nav-item" type="button" data-page="system">
|
<button class="nav-item" type="button" data-page="system" aria-label="Система">
|
||||||
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M12 8a4 4 0 1 1 0 8 4 4 0 0 1 0-8Zm9 4-2.1-1.2.1-2.4-2.2-2.2-2.4.1L13.2 4h-2.4L9.6 6.3l-2.4-.1L5 8.4l.1 2.4L3 12l2.1 1.2-.1 2.4 2.2 2.2 2.4-.1 1.2 2.3h2.4l1.2-2.3 2.4.1 2.2-2.2-.1-2.4L21 12Z"/></svg>
|
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M12 8a4 4 0 1 1 0 8 4 4 0 0 1 0-8Zm9 4-2.1-1.2.1-2.4-2.2-2.2-2.4.1L13.2 4h-2.4L9.6 6.3l-2.4-.1L5 8.4l.1 2.4L3 12l2.1 1.2-.1 2.4 2.2 2.2 2.4-.1 1.2 2.3h2.4l1.2-2.3 2.4.1 2.2-2.2-.1-2.4L21 12Z"/></svg>
|
||||||
<span>Система</span>
|
<span>Система</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ def test_web_ui_assets_are_available() -> None:
|
|||||||
assert "TradeBot — панель управления" in html
|
assert "TradeBot — панель управления" in html
|
||||||
assert "/assets/dashboard.css" in html
|
assert "/assets/dashboard.css" in html
|
||||||
assert "/assets/dashboard.js" in html
|
assert "/assets/dashboard.js" in html
|
||||||
|
assert "/web-api/dashboard/snapshot" in WEB_INDEX.with_name("dashboard.js").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_compact_markets_keeps_dashboard_fields_and_limits_candles() -> None:
|
def test_compact_markets_keeps_dashboard_fields_and_limits_candles() -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user