From 813ebf0e4eb611fedecb2ec4a8246638f041889b Mon Sep 17 00:00:00 2001 From: dekun Date: Wed, 24 Jun 2026 00:39:54 +0800 Subject: [PATCH] Skip exchange PnL sync on hub iframe soft nav to fix slow records tab. Remove hover prefetch and mark soft-nav fetches so Gate/OKX render pages from local DB without blocking on exchange history sync. Co-authored-by: Cursor --- crypto_monitor_binance/templates/index.html | 2 +- crypto_monitor_gate/app.py | 3 +- crypto_monitor_gate/templates/index.html | 2 +- crypto_monitor_gate_bot/app.py | 3 +- crypto_monitor_gate_bot/templates/index.html | 2 +- crypto_monitor_okx/app.py | 3 +- crypto_monitor_okx/templates/index.html | 2 +- instance_nav_lib.py | 19 +++++++ static/instance_theme.js | 58 ++++---------------- tests/test_instance_nav_lib.py | 21 +++++++ 10 files changed, 61 insertions(+), 54 deletions(-) create mode 100644 instance_nav_lib.py create mode 100644 tests/test_instance_nav_lib.py diff --git a/crypto_monitor_binance/templates/index.html b/crypto_monitor_binance/templates/index.html index 2bfa979..eadea96 100644 --- a/crypto_monitor_binance/templates/index.html +++ b/crypto_monitor_binance/templates/index.html @@ -3,7 +3,7 @@ - + diff --git a/crypto_monitor_gate/app.py b/crypto_monitor_gate/app.py index c962632..d16500f 100644 --- a/crypto_monitor_gate/app.py +++ b/crypto_monitor_gate/app.py @@ -178,6 +178,7 @@ from order_monitor_display_lib import ( ) from wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook from hub_auth import request_allowed as hub_request_allowed +from instance_nav_lib import request_is_hub_soft_nav from hub_volume_rank_lib import resolve_daily_volume_rank from history_window_lib import ( PRESET_CUSTOM, @@ -6725,7 +6726,7 @@ def render_main_page(page="trade"): for o in raw_order_list: order_list.append(enrich_order_item(row_to_dict(o), current_capital)) exchange_pnl_sync = {} - if exchange_private_api_configured(): + if exchange_private_api_configured() and not request_is_hub_soft_nav(): try: exchange_pnl_sync = sync_trade_records_from_exchange(conn) or {} except Exception as e: diff --git a/crypto_monitor_gate/templates/index.html b/crypto_monitor_gate/templates/index.html index 606563d..a10f789 100644 --- a/crypto_monitor_gate/templates/index.html +++ b/crypto_monitor_gate/templates/index.html @@ -3,7 +3,7 @@ - + diff --git a/crypto_monitor_gate_bot/app.py b/crypto_monitor_gate_bot/app.py index b698473..500bb15 100644 --- a/crypto_monitor_gate_bot/app.py +++ b/crypto_monitor_gate_bot/app.py @@ -178,6 +178,7 @@ from order_monitor_display_lib import ( ) from wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook from hub_auth import request_allowed as hub_request_allowed +from instance_nav_lib import request_is_hub_soft_nav from hub_volume_rank_lib import resolve_daily_volume_rank from history_window_lib import ( PRESET_CUSTOM, @@ -6725,7 +6726,7 @@ def render_main_page(page="trade"): for o in raw_order_list: order_list.append(enrich_order_item(row_to_dict(o), current_capital)) exchange_pnl_sync = {} - if exchange_private_api_configured(): + if exchange_private_api_configured() and not request_is_hub_soft_nav(): try: exchange_pnl_sync = sync_trade_records_from_exchange(conn) or {} except Exception as e: diff --git a/crypto_monitor_gate_bot/templates/index.html b/crypto_monitor_gate_bot/templates/index.html index 606563d..a10f789 100644 --- a/crypto_monitor_gate_bot/templates/index.html +++ b/crypto_monitor_gate_bot/templates/index.html @@ -3,7 +3,7 @@ - + diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py index 966fb62..9c7219c 100644 --- a/crypto_monitor_okx/app.py +++ b/crypto_monitor_okx/app.py @@ -177,6 +177,7 @@ from order_monitor_display_lib import ( ) from wechat_notify_lib import build_wechat_rs_level_message, send_wechat_webhook from hub_auth import request_allowed as hub_request_allowed +from instance_nav_lib import request_is_hub_soft_nav from hub_volume_rank_lib import resolve_daily_volume_rank from history_window_lib import ( PRESET_CUSTOM, @@ -6229,7 +6230,7 @@ def render_main_page(page="trade"): for o in raw_order_list: order_list.append(enrich_order_item(row_to_dict(o), current_capital)) exchange_pnl_sync = {} - if exchange_private_api_configured(): + if exchange_private_api_configured() and not request_is_hub_soft_nav(): try: exchange_pnl_sync = sync_trade_records_from_exchange(conn) or {} except Exception as e: diff --git a/crypto_monitor_okx/templates/index.html b/crypto_monitor_okx/templates/index.html index 30ce303..de222d1 100644 --- a/crypto_monitor_okx/templates/index.html +++ b/crypto_monitor_okx/templates/index.html @@ -3,7 +3,7 @@ - + diff --git a/instance_nav_lib.py b/instance_nav_lib.py new file mode 100644 index 0000000..244dcc5 --- /dev/null +++ b/instance_nav_lib.py @@ -0,0 +1,19 @@ +"""中控 iframe 内软导航:服务端跳过重型同步,避免切 tab 等待数秒。""" + +from __future__ import annotations + +from flask import Request + + +def request_is_hub_soft_nav(req: Request | None = None) -> bool: + """embed=1 且带 X-Instance-Soft-Nav 头:实例页内 fetch 换页,非整页刷新。""" + try: + from flask import request as flask_request + + r = req or flask_request + if str(r.args.get("embed") or "").strip() != "1": + return False + flag = (r.headers.get("X-Instance-Soft-Nav") or "").strip().lower() + return flag in ("1", "true", "yes") + except Exception: + return False diff --git a/static/instance_theme.js b/static/instance_theme.js index 2e51854..05e30d5 100644 --- a/static/instance_theme.js +++ b/static/instance_theme.js @@ -408,8 +408,6 @@ if (!isHubLinked()) return; let navToken = 0; - const prefetch = new Map(); - const PREFETCH_MAX = 8; function isSoftNavLink(a) { if (!a || !a.getAttribute) return false; @@ -417,25 +415,11 @@ return !!a.closest(".top-nav, .strategy-subnav"); } - function rememberPrefetch(href, html) { - if (!href || !html) return; - if (prefetch.has(href)) prefetch.delete(href); - prefetch.set(href, html); - while (prefetch.size > PREFETCH_MAX) { - const first = prefetch.keys().next().value; - prefetch.delete(first); - } - } - - function warmPrefetch(href) { - if (!href || prefetch.has(href)) return; - const token = navToken; - fetch(href, { credentials: "same-origin" }) - .then((r) => (r.ok ? r.text() : null)) - .then((html) => { - if (html && token === navToken) rememberPrefetch(href, html); - }) - .catch(() => {}); + function softNavFetch(href) { + return fetch(href, { + credentials: "same-origin", + headers: { "X-Instance-Soft-Nav": "1" }, + }); } async function navigateInFrame(href, opts) { @@ -443,17 +427,13 @@ notifyParentFrameNavStart(); ensureNavOverlay(); try { - let html = prefetch.get(href); - if (html) prefetch.delete(href); - if (!html) { - const r = await fetch(href, { credentials: "same-origin" }); - if (token !== navToken) return; - if (!r.ok) { - location.assign(href); - return; - } - html = await r.text(); + const r = await softNavFetch(href); + if (token !== navToken) return; + if (!r.ok) { + location.assign(href); + return; } + let html = await r.text(); if (token !== navToken) return; html = injectNavOverlayIntoHtml(html, get()); let path = href; @@ -494,22 +474,6 @@ true ); - document.addEventListener( - "pointerenter", - (ev) => { - const a = ev.target.closest("a[href]"); - if (!a || !isSoftNavLink(a)) return; - const rawHref = a.getAttribute("href"); - if (!rawHref || rawHref.startsWith("#") || rawHref.startsWith("javascript:")) return; - try { - const target = new URL(rawHref, location.href); - if (target.origin !== location.origin) return; - warmPrefetch(target.pathname + target.search + target.hash); - } catch (_) {} - }, - true - ); - window.addEventListener("popstate", () => { void navigateInFrame(location.pathname + location.search + location.hash, { replace: true }); }); diff --git a/tests/test_instance_nav_lib.py b/tests/test_instance_nav_lib.py new file mode 100644 index 0000000..52cfab8 --- /dev/null +++ b/tests/test_instance_nav_lib.py @@ -0,0 +1,21 @@ +from instance_nav_lib import request_is_hub_soft_nav + + +def test_request_is_hub_soft_nav(): + class Req: + args = {"embed": "1"} + headers = {"X-Instance-Soft-Nav": "1"} + + assert request_is_hub_soft_nav(Req()) is True + + class Req2: + args = {"embed": "1"} + headers = {} + + assert request_is_hub_soft_nav(Req2()) is False + + class Req3: + args = {} + headers = {"X-Instance-Soft-Nav": "1"} + + assert request_is_hub_soft_nav(Req3()) is False