From 9e17814e370827b651122e4da48ccc5fd0508068 Mon Sep 17 00:00:00 2001 From: dekun Date: Tue, 14 Jul 2026 11:22:28 +0800 Subject: [PATCH] Prevent env autofill from overwriting OKX API keys when saving hedge settings. Co-authored-by: Cursor --- crypto_monitor_okx/app.py | 22 ++++++++++++------- lib/common/static/instance_settings_prefs.js | 20 ++++++++++++++++- lib/env/env_schema.py | 4 ++++ lib/instance/instance_embed_context_lib.py | 23 +++++++++++--------- lib/instance/templates/embed_shell.html | 2 +- lib/instance/templates/index.html | 2 +- 6 files changed, 52 insertions(+), 21 deletions(-) diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py index 1a484e6..072fdfd 100644 --- a/crypto_monitor_okx/app.py +++ b/crypto_monitor_okx/app.py @@ -2735,9 +2735,12 @@ def resolve_order_entry_price(order_resp, exchange_symbol, fallback_price): def get_contract_size(exchange_symbol): - ensure_markets_loaded() - market = exchange.market(exchange_symbol) - return float(market.get("contractSize") or 1) + try: + ensure_markets_loaded() + market = exchange.market(exchange_symbol) + return float(market.get("contractSize") or 1) + except Exception: + return 1.0 def parse_positive_float(value): @@ -6945,11 +6948,14 @@ def api_account_snapshot(): except Exception: return exchange.fetch_positions() or [] - unrealized_pnl = resolve_instance_unrealized_pnl( - _okx_positions, - active_pnl_rows, - get_live_position_exchange_metrics, - ) + try: + unrealized_pnl = resolve_instance_unrealized_pnl( + _okx_positions, + active_pnl_rows, + get_live_position_exchange_metrics, + ) + except Exception: + unrealized_pnl = None options_unrealized_pnl = None if OKX_OPTIONS_ENABLED and exchange_options.apiKey: try: diff --git a/lib/common/static/instance_settings_prefs.js b/lib/common/static/instance_settings_prefs.js index f8d9d23..c5a7544 100644 --- a/lib/common/static/instance_settings_prefs.js +++ b/lib/common/static/instance_settings_prefs.js @@ -202,8 +202,21 @@ input = document.createElement("input"); input.id = "env-f-" + field.key; input.type = "password"; - input.autocomplete = "off"; + // 防止浏览器把登录密码自动填进 API Key/Secret(保存对冲开关时曾误写入密钥) + input.autocomplete = "new-password"; + input.setAttribute("data-lpignore", "true"); + input.setAttribute("data-1p-ignore", "true"); + input.setAttribute("data-form-type", "other"); + input.readOnly = true; + input.addEventListener("focus", function () { + input.readOnly = false; + }); if (field.sensitive) { + input.dataset.envSensitive = "1"; + input.dataset.envDirty = "0"; + input.addEventListener("input", function () { + input.dataset.envDirty = "1"; + }); if (field.has_value) { const cur = document.createElement("div"); cur.className = "env-sensitive-current muted"; @@ -219,6 +232,7 @@ input.placeholder = field.has_value ? "修改时填写新值,留空不修改" : "请输入"; } else { input.type = "text"; + input.autocomplete = "off"; input.value = field.current || field.default || ""; } } @@ -309,6 +323,10 @@ const values = {}; const scope = root || document; scope.querySelectorAll(".env-field-input[data-env-key]").forEach((el) => { + if (el.dataset.envSensitive === "1" && el.dataset.envDirty !== "1") { + // 未改动过的敏感项不提交,避免浏览器自动填充覆盖已有密钥 + return; + } values[el.dataset.envKey] = el.value; }); return values; diff --git a/lib/env/env_schema.py b/lib/env/env_schema.py index 596067c..45d2625 100644 --- a/lib/env/env_schema.py +++ b/lib/env/env_schema.py @@ -275,6 +275,10 @@ def validate_env_updates(groups: list[dict], updates: dict[str, str]) -> tuple[d val = str(value).strip() if allowed[key].get("sensitive") and (val == "" or (val.startswith("****") and len(val) <= 8)): continue + # API Key 被密码管理器/自动填充成登录密码时通常很短;OKX Key 一般为 36 位 + if key.endswith("_API_KEY") and 0 < len(val) < 16: + errors.append(f"{key} 长度异常,疑似自动填充;留空则不修改已有密钥") + continue ftype = allowed[key].get("type") if ftype == "bool": low = val.lower() diff --git a/lib/instance/instance_embed_context_lib.py b/lib/instance/instance_embed_context_lib.py index ba52a80..563467f 100644 --- a/lib/instance/instance_embed_context_lib.py +++ b/lib/instance/instance_embed_context_lib.py @@ -105,18 +105,21 @@ def total_funds_usdt( options_funding_usdt: float | None = None, options_trading_usdt: float | None = None, ) -> float | None: - if funding_usdt is None: + parts = [ + funding_usdt, + trading_usdt, + options_funding_usdc, + options_funding_usdt, + options_trading_usdc, + options_trading_usdt, + ] + if all(v is None for v in parts): return None try: - total = float(funding_usdt) + float(trading_usdt or 0) - if options_funding_usdc is not None: - total += float(options_funding_usdc) - if options_funding_usdt is not None: - total += float(options_funding_usdt) - if options_trading_usdc is not None: - total += float(options_trading_usdc) - if options_trading_usdt is not None: - total += float(options_trading_usdt) + total = 0.0 + for v in parts: + if v is not None: + total += float(v) return round(total, 2) except (TypeError, ValueError): return None diff --git a/lib/instance/templates/embed_shell.html b/lib/instance/templates/embed_shell.html index bade13c..4f0dc3c 100644 --- a/lib/instance/templates/embed_shell.html +++ b/lib/instance/templates/embed_shell.html @@ -108,7 +108,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj - + diff --git a/lib/instance/templates/index.html b/lib/instance/templates/index.html index d8355b1..002d9bf 100644 --- a/lib/instance/templates/index.html +++ b/lib/instance/templates/index.html @@ -2160,6 +2160,6 @@ setInterval(refreshPriceSnapshotConditional, {{ price_refresh_seconds * 1000 }}) - + \ No newline at end of file