Skip to content

Commit c34ea80

Browse files
committed
Hide live controls from public switch page
1 parent 52f3529 commit c34ea80

3 files changed

Lines changed: 67 additions & 52 deletions

File tree

tests/strategy_switch_worker_validation.mjs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,25 @@ assert.ok(indexHtml.includes(".switch-surface.summary-hidden"));
1717
assert.ok(indexHtml.includes('summaryPanel.hidden = !showSummary'));
1818
assert.ok(indexHtml.includes('switchSurface.classList.toggle("summary-hidden", !showSummary)'));
1919
assert.equal(indexHtml.includes("publicSummary"), false);
20+
assert.ok(indexHtml.includes("function hasPrivateConfig()"));
21+
assert.ok(indexHtml.includes('el("quick-form").hidden = !showPrivateControls'));
22+
assert.equal(indexHtml.includes("u15998061"), false);
23+
assert.equal(indexHtml.includes("longbridge-quant-sg-service"), false);
24+
assert.equal(indexHtml.includes('account_selector: "SG"'), false);
2025
assert.match(indexHtml, /body\.app-loading \.shell\s*\{\s*display: none;/);
2126

27+
const servedPageResponse = await worker.fetch(new Request("https://switch.example/"), {});
28+
const servedHtml = await servedPageResponse.text();
29+
assert.equal(servedPageResponse.status, 200);
30+
assert.ok(servedHtml.includes("function hasPrivateConfig()"));
31+
assert.equal(servedHtml.includes("u15998061"), false);
32+
assert.equal(servedHtml.includes("longbridge-quant-sg-service"), false);
33+
assert.equal(servedHtml.includes('account_selector: "SG"'), false);
34+
35+
const publicConfigResponse = await worker.fetch(new Request("https://switch.example/api/config"), {});
36+
assert.equal(publicConfigResponse.status, 200);
37+
assert.deepEqual(await publicConfigResponse.json(), { accountOptions: null });
38+
2239
const headers = __test.responseHeaders({ "Content-Type": "text/html; charset=utf-8" });
2340
assert.equal(headers.get("X-Frame-Options"), "DENY");
2441
assert.equal(headers.get("X-Content-Type-Options"), "nosniff");
@@ -63,7 +80,7 @@ assert.equal(unauthorizedSyncResponse.status, 401);
6380
assert.match((await unauthorizedSyncResponse.json()).error, /internal sync token is invalid/);
6481

6582
assert.equal(
66-
await __test.withTimeout(new Promise((resolve) => setTimeout(() => resolve("late"), 25)), 1, "fallback"),
83+
await __test.withTimeout(new Promise(() => {}), 1, "fallback"),
6784
"fallback",
6885
);
6986

web/strategy-switch-console/index.html

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,13 @@
469469
padding-top: 2px;
470470
}
471471

472+
.public-note {
473+
color: var(--muted);
474+
font-size: 14px;
475+
line-height: 1.55;
476+
max-width: 560px;
477+
}
478+
472479
.run-button {
473480
min-height: 56px;
474481
border: 0;
@@ -705,7 +712,9 @@ <h2 data-i18n="bootTitle">读取策略配置</h2>
705712
<h2 id="platform-title">LongBridge</h2>
706713
</div>
707714

708-
<div class="quick-form">
715+
<p class="public-note" id="public-note" hidden data-i18n="publicReadonly">登录后查看账号配置。</p>
716+
717+
<div class="quick-form" id="quick-form">
709718
<label class="control-block">
710719
<span data-i18n="account">目标账号</span>
711720
<select id="account-select"></select>
@@ -727,7 +736,7 @@ <h2 id="platform-title">LongBridge</h2>
727736
</div>
728737
</div>
729738

730-
<div class="run-area">
739+
<div class="run-area" id="run-area">
731740
<button class="run-button" id="dispatch-button" type="button" disabled data-i18n="loginToRun">登录后切换</button>
732741
<p class="action-note" id="action-note">登录后才可执行切换。</p>
733742
<p class="toast" id="toast" aria-live="polite"></p>
@@ -781,50 +790,16 @@ <h2 data-i18n="summary">切换摘要</h2>
781790

782791
const defaultAccountOptions = {
783792
longbridge: [
784-
{ key: "hk", label: "hk", target_name: "hk", account_selector: "HK", default_strategy_profile: "hk_low_vol_dividend_quality_snapshot", supported_domains: ["us_equity", "hk_equity"] },
785-
{ key: "sg", label: "sg", target_name: "sg", account_selector: "SG", default_strategy_profile: "soxl_soxx_trend_income", supported_domains: ["us_equity", "hk_equity"] },
786-
{ key: "paper", label: "paper", target_name: "paper", account_selector: "PAPER", default_strategy_profile: "mega_cap_leader_rotation_top50_balanced", supported_domains: ["us_equity", "hk_equity"] },
793+
{ key: "preview", label: "LongBridge", target_name: "preview", supported_domains: ["us_equity", "hk_equity"] },
787794
],
788795
ibkr: [
789-
{
790-
key: "u15998061",
791-
label: "u15998061",
792-
target_name: "u15998061",
793-
account_selector: "U15998061",
794-
deployment_selector: "live-u1599-tqqq",
795-
account_scope: "live-u1599-tqqq",
796-
service_name: "interactive-brokers-live-u1599-tqqq-service",
797-
default_strategy_profile: "tqqq_growth_income",
798-
supported_domains: ["us_equity", "hk_equity"],
799-
},
800-
{
801-
key: "u16608560",
802-
label: "u16608560",
803-
target_name: "u16608560",
804-
account_selector: "U16608560",
805-
deployment_selector: "live-u1660-soxl",
806-
account_scope: "live-u1660-soxl",
807-
service_name: "interactive-brokers-live-u1660-soxl-service",
808-
default_strategy_profile: "soxl_soxx_trend_income",
809-
supported_domains: ["us_equity", "hk_equity"],
810-
},
811-
{
812-
key: "u18336562",
813-
label: "u18336562",
814-
target_name: "u18336562",
815-
account_selector: "U18336562",
816-
deployment_selector: "live-u1833-smart-dca",
817-
account_scope: "live-u1833-smart-dca",
818-
service_name: "interactive-brokers-live-u1833-smart-dca-service",
819-
default_strategy_profile: "nasdaq_sp500_smart_dca",
820-
supported_domains: ["us_equity", "hk_equity"],
821-
},
796+
{ key: "preview", label: "IBKR", target_name: "preview", supported_domains: ["us_equity", "hk_equity"] },
822797
],
823798
schwab: [
824-
{ key: "default", label: "default", target_name: "default", default_strategy_profile: "soxl_soxx_trend_income", supported_domains: ["us_equity"] },
799+
{ key: "preview", label: "Schwab", target_name: "preview", supported_domains: ["us_equity"] },
825800
],
826801
firstrade: [
827-
{ key: "default", label: "default", target_name: "default", default_strategy_profile: "mega_cap_leader_rotation_top50_balanced", supported_domains: ["us_equity"] },
802+
{ key: "preview", label: "Firstrade", target_name: "preview", supported_domains: ["us_equity"] },
828803
],
829804
};
830805

@@ -855,6 +830,7 @@ <h2 data-i18n="summary">切换摘要</h2>
855830
configureAccounts: "配置账号后切换",
856831
runSwitch: "一键切换",
857832
readonlyNote: "登录后才可执行切换。",
833+
publicReadonly: "登录后查看账号配置。",
858834
loadingConfigNote: "正在读取账号配置和当前状态。",
859835
missingConfigNote: "账号配置未加载,暂时不能执行。",
860836
readyNote: "点击后会触发 workflow,并同步目标平台服务。",
@@ -902,6 +878,7 @@ <h2 data-i18n="summary">切换摘要</h2>
902878
configureAccounts: "Configure accounts",
903879
runSwitch: "Switch now",
904880
readonlyNote: "Sign in to switch.",
881+
publicReadonly: "Sign in to view account config.",
905882
loadingConfigNote: "Reading account config and current state.",
906883
missingConfigNote: "Account config is not loaded, so switching is disabled.",
907884
readyNote: "This dispatches the workflow and syncs the target platform service.",
@@ -941,10 +918,10 @@ <h2 data-i18n="summary">切换摘要</h2>
941918
currentStrategies: {},
942919
configSource: "default",
943920
forms: {
944-
longbridge: { accountKey: "hk", strategy: "hk_low_vol_dividend_quality_snapshot", executionMode: "live" },
945-
ibkr: { accountKey: "u15998061", strategy: "tqqq_growth_income", executionMode: "live" },
946-
schwab: { accountKey: "default", strategy: "soxl_soxx_trend_income", executionMode: "live" },
947-
firstrade: { accountKey: "default", strategy: "mega_cap_leader_rotation_top50_balanced", executionMode: "live" },
921+
longbridge: { accountKey: "preview", strategy: "", executionMode: "live" },
922+
ibkr: { accountKey: "preview", strategy: "", executionMode: "live" },
923+
schwab: { accountKey: "preview", strategy: "", executionMode: "live" },
924+
firstrade: { accountKey: "preview", strategy: "", executionMode: "live" },
948925
},
949926
};
950927

@@ -963,6 +940,10 @@ <h2 data-i18n="summary">切换摘要</h2>
963940
return options.find((option) => option.key === form.accountKey) || options[0];
964941
}
965942

943+
function hasPrivateConfig() {
944+
return Boolean(state.auth.allowed && state.configSource === "private");
945+
}
946+
966947
function cleanStrategyProfile(value) {
967948
const profile = String(value || "").trim();
968949
return /^[a-z0-9._=-]{1,120}$/.test(profile) ? profile : "";
@@ -1220,6 +1201,7 @@ <h2 data-i18n="summary">切换摘要</h2>
12201201
function renderPlatforms() {
12211202
const strip = el("platform-strip");
12221203
strip.replaceChildren();
1204+
const showPrivateConfig = hasPrivateConfig();
12231205
for (const platform of Object.keys(platformMeta)) {
12241206
ensureAccountSelection(platform);
12251207
const meta = platformMeta[platform];
@@ -1237,11 +1219,14 @@ <h2 data-i18n="summary">切换摘要</h2>
12371219
copyNode.className = "platform-copy";
12381220
const labelNode = document.createElement("strong");
12391221
labelNode.textContent = meta.label;
1240-
const accountNode = document.createElement("span");
1241-
accountNode.textContent = account.label;
1242-
const strategyNode = document.createElement("small");
1243-
strategyNode.textContent = strategyLabel(form.strategy);
1244-
copyNode.append(labelNode, accountNode, strategyNode);
1222+
copyNode.append(labelNode);
1223+
if (showPrivateConfig) {
1224+
const accountNode = document.createElement("span");
1225+
accountNode.textContent = account.label;
1226+
const strategyNode = document.createElement("small");
1227+
strategyNode.textContent = strategyLabel(form.strategy);
1228+
copyNode.append(accountNode, strategyNode);
1229+
}
12451230
button.append(mark, copyNode);
12461231
strip.appendChild(button);
12471232
}
@@ -1256,9 +1241,22 @@ <h2 data-i18n="summary">切换摘要</h2>
12561241
const choices = strategyChoicesForAccount(platform, account);
12571242
const accountSelect = el("account-select");
12581243
const strategySelect = el("strategy-select");
1244+
const showPrivateControls = hasPrivateConfig();
12591245

12601246
el("switch-panel").style.setProperty("--platform-color", meta.accent);
12611247
el("platform-title").textContent = meta.label;
1248+
el("quick-form").hidden = !showPrivateControls;
1249+
el("run-area").hidden = !showPrivateControls;
1250+
el("public-note").hidden = showPrivateControls;
1251+
el("public-note").textContent = state.auth.allowed ? t("missingConfigNote") : t("publicReadonly");
1252+
1253+
if (!showPrivateControls) {
1254+
accountSelect.replaceChildren();
1255+
strategySelect.replaceChildren();
1256+
el("account-meta").textContent = "";
1257+
el("strategy-meta").textContent = "";
1258+
return;
1259+
}
12621260

12631261
accountSelect.replaceChildren();
12641262
if (accounts.length) {
@@ -1292,7 +1290,7 @@ <h2 data-i18n="summary">切换摘要</h2>
12921290
}
12931291

12941292
function renderSummary() {
1295-
const showSummary = Boolean(state.auth.allowed);
1293+
const showSummary = hasPrivateConfig();
12961294
const summaryPanel = document.querySelector(".summary-panel");
12971295
const switchSurface = document.querySelector(".switch-surface");
12981296
summaryPanel.hidden = !showSummary;

web/strategy-switch-console/page_asset.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)