Skip to content

Commit e989834

Browse files
committed
Add OAuth admin management for strategy switch console
1 parent 1553fe9 commit e989834

9 files changed

Lines changed: 639 additions & 143 deletions

docs/index.html

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,33 @@ <h2 data-i18n="summary">切换摘要</h2>
618618
{ key: "paper", label: "paper", target_name: "paper", account_selector: "PAPER" },
619619
],
620620
ibkr: [
621-
{ key: "u0000000", label: "u0000000", target_name: "u0000000", account_selector: "u0000000" },
621+
{
622+
key: "u15998061",
623+
label: "u15998061",
624+
target_name: "u15998061",
625+
account_selector: "U15998061",
626+
deployment_selector: "live-u1599-tqqq",
627+
account_scope: "live-u1599-tqqq",
628+
service_name: "interactive-brokers-live-u1599-tqqq-service",
629+
},
630+
{
631+
key: "u16608560",
632+
label: "u16608560",
633+
target_name: "u16608560",
634+
account_selector: "U16608560",
635+
deployment_selector: "live-u1660-soxl",
636+
account_scope: "live-u1660-soxl",
637+
service_name: "interactive-brokers-live-u1660-soxl-service",
638+
},
639+
{
640+
key: "u18336562",
641+
label: "u18336562",
642+
target_name: "u18336562",
643+
account_selector: "U18336562",
644+
deployment_selector: "live-u1833-smart-dca",
645+
account_scope: "live-u1833-smart-dca",
646+
service_name: "interactive-brokers-live-u1833-smart-dca-service",
647+
},
622648
],
623649
schwab: [
624650
{ key: "default", label: "default", target_name: "default" },
@@ -669,7 +695,7 @@ <h2 data-i18n="summary">切换摘要</h2>
669695
en: {
670696
appTitle: "Strategy Switch",
671697
appSubtitle: "Pick platform, target account, and strategy. One action switches everything.",
672-
loginManage: "Login",
698+
loginManage: "Login Management",
673699
activePlatform: "Active Platform",
674700
account: "Target account",
675701
strategy: "Strategy",
@@ -719,7 +745,7 @@ <h2 data-i18n="summary">切换摘要</h2>
719745
configSource: "default",
720746
forms: {
721747
longbridge: { accountKey: "hk", strategy: "tqqq_growth_income", executionMode: "live" },
722-
ibkr: { accountKey: "u0000000", strategy: "tqqq_growth_income", executionMode: "live" },
748+
ibkr: { accountKey: "u15998061", strategy: "tqqq_growth_income", executionMode: "live" },
723749
schwab: { accountKey: "default", strategy: "tqqq_growth_income", executionMode: "live" },
724750
firstrade: { accountKey: "default", strategy: "tqqq_growth_income", executionMode: "live" },
725751
},
Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,50 @@
11
# Strategy Switch Admin Backend
22

3-
Goal: keep the personal strategy switch console simple while avoiding code changes for every login or account dropdown update.
3+
Goal: keep the open-source switch page public and read-only by default, while allowing an authenticated admin to manage who can switch strategies and which account routes appear in the dropdown.
44

5-
## Current Mode
5+
## Current Implementation
66

7-
- GitHub OAuth signs users in.
8-
- `ALLOWED_GITHUB_LOGINS` controls who can dispatch a switch.
9-
- `STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON` controls signed-in account dropdowns.
10-
- The GitHub dispatch token stays in Worker secrets and is never sent to the browser.
7+
- Login method: GitHub OAuth 2.0.
8+
- Public access: unsigned visitors can view the page, but cannot dispatch the workflow.
9+
- Allowed switch users: `ALLOWED_GITHUB_LOGINS`, KV `auth_config.allowed_logins`, and all admins.
10+
- Admin users: `STRATEGY_SWITCH_ADMIN_LOGINS` plus KV `auth_config.admin_logins`.
11+
- Account dropdowns: KV `account_options` first, falling back to `STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON`.
12+
- Audit log: each admin save appends to KV `audit_log`, capped at 50 entries.
1113

12-
This is enough for the first deployment. Its main limitation is that user and account changes require updating Worker secrets.
14+
## Cloudflare KV
1315

14-
## Recommended Admin Mode
16+
Bind the namespace:
1517

16-
Keep GitHub OAuth and use an admin-only `/admin` page:
18+
```toml
19+
[[kv_namespaces]]
20+
binding = "STRATEGY_SWITCH_CONFIG"
21+
id = "..."
22+
```
1723

18-
- Bootstrap admins come from `STRATEGY_SWITCH_ADMIN_LOGINS`; keep your own GitHub login there.
19-
- Admin actions:
20-
- The current version verifies admin identity and shows configured account counts for the four platforms.
21-
- After KV is connected, add or remove allowed GitHub logins.
22-
- After KV is connected, edit account dropdowns for the four platforms.
23-
- After KV is connected, review recent permission and account-config changes.
24-
- Storage:
25-
- Cloudflare KV namespace: `STRATEGY_SWITCH_CONFIG`.
26-
- key `auth_config`: `allowed_logins` and `admin_logins`.
27-
- key `account_options`: platform account dropdowns.
28-
- key `audit_log`: recent admin changes.
24+
KV keys:
25+
26+
```text
27+
auth_config
28+
account_options
29+
audit_log
30+
```
31+
32+
Without the KV binding, `/admin` is read-only and the Worker falls back to secrets.
2933

3034
## Permission Rules
3135

3236
- Not signed in: public read-only page.
33-
- Signed in but not allowlisted: no switch, no admin page.
37+
- Signed in but not allowlisted: no switch and no admin page.
3438
- Allowlisted: can dispatch switches.
35-
- Admin-listed: can manage login permissions and account dropdowns.
36-
- `STRATEGY_SWITCH_ADMIN_LOGINS` remains the break-glass admin source so you cannot remove yourself through the UI.
39+
- Admin-listed: can open `/admin` and manage allowed logins, admin logins, and account dropdown JSON.
40+
- `STRATEGY_SWITCH_ADMIN_LOGINS` remains the break-glass admin source and is preserved on save.
3741

3842
## Security Boundary
3943

4044
- The admin backend stores GitHub logins and account routing metadata only.
4145
- Broker passwords, tokens, API keys, and cloud credentials stay out of this config.
42-
- Admin writes use POST and the existing Worker same-origin checks.
43-
- Sessions keep HttpOnly, Secure, SameSite=Lax, and HMAC-signed cookies.
44-
- Dispatch tokens remain separate from admin config and are never readable from frontend code.
45-
- Audit logs record time, admin login, and action type, but never secrets.
46-
47-
## Rollout
48-
49-
1. Ship the current secret-backed console.
50-
2. The read-only `/admin` verification page is already available for `STRATEGY_SWITCH_ADMIN_LOGINS`.
51-
3. Add Worker KV reads with secret fallback.
52-
4. Add `/api/admin/config` write operations for admins.
53-
5. Add audit logs and last-version rollback.
46+
- Admin writes use POST and same-origin checks.
47+
- Sessions use HttpOnly, Secure, SameSite=Lax, and HMAC-signed cookies.
48+
- The GitHub dispatch token stays in Worker secrets and is never returned to frontend or admin APIs.
5449

55-
This avoids a database, custom user system, or broad RBAC while still giving a practical backend for a personal open-source project.
50+
This keeps the personal system simple: no database, review flow, or custom RBAC, while preventing strangers from operating the public page.
Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,50 @@
1-
# 策略切换登录权限后台方案
1+
# 策略切换登录权限后台
22

3-
目标:保持个人量化系统足够简单,同时不用每次改权限或账号下拉都重新改代码
3+
目标:开源页面可以公开查看,但只有登录并通过权限校验的 GitHub 账号能一键切换策略;管理员可以自己维护登录名单和账号下拉,不需要每次改代码
44

5-
## 当前模式
5+
## 当前实现
66

7-
- GitHub OAuth 登录。
8-
- `ALLOWED_GITHUB_LOGINS` 控制谁能触发切换。
9-
- `STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON` 控制登录后可选账号。
10-
- GitHub dispatch token 只在 Worker secret 里,前端拿不到。
7+
- 登录方式:GitHub OAuth 2.0。
8+
- 公开访问:未登录用户只能看到只读切换页,不能触发 workflow。
9+
- 可切换用户:来自 `ALLOWED_GITHUB_LOGINS`、KV `auth_config.allowed_logins` 和管理员名单。
10+
- 管理员:来自 `STRATEGY_SWITCH_ADMIN_LOGINS` 和 KV `auth_config.admin_logins`
11+
- 账号下拉:优先读取 KV `account_options`,没有 KV 配置时回退 `STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON`
12+
- 审计:管理员保存配置后写入 KV `audit_log`,保留最近 50 条。
1113

12-
这个模式可以先上线。它的缺点是:新增登录用户或账号 target 时,需要改 Worker secret。
14+
## Cloudflare KV
1315

14-
## 推荐后台模式
16+
绑定 namespace:
1517

16-
保留 GitHub OAuth,使用一个只给管理员看的 `/admin` 页面:
18+
```toml
19+
[[kv_namespaces]]
20+
binding = "STRATEGY_SWITCH_CONFIG"
21+
id = "..."
22+
```
1723

18-
- 管理员:由 `STRATEGY_SWITCH_ADMIN_LOGINS` 启动配置,建议只放你自己的 GitHub login。
19-
- 可操作内容:
20-
- 当前已支持验证管理员身份,并展示四个平台已加载账号数量。
21-
- 后续接 KV 后,可添加或移除允许登录的 GitHub 用户名。
22-
- 后续接 KV 后,可编辑四个平台的账号下拉配置。
23-
- 后续接 KV 后,可查看最近的权限和账号配置修改记录。
24-
- 存储:
25-
- Cloudflare KV namespace:`STRATEGY_SWITCH_CONFIG`
26-
- key `auth_config`:保存 `allowed_logins``admin_logins`
27-
- key `account_options`:保存四个平台账号下拉。
28-
- key `audit_log`:保存最近 50 条管理操作。
24+
使用的 key:
25+
26+
```text
27+
auth_config
28+
account_options
29+
audit_log
30+
```
31+
32+
没有绑定 KV 时,`/admin` 只读;登录和切换仍可通过 Worker secrets 运行。
2933

3034
## 权限规则
3135

32-
- 未登录:只能看公开只读页
36+
- 未登录:只能查看公开页面
3337
- 登录但不在 allowlist:不能切换,也不能进入后台。
34-
- 登录且在 allowlist:可以一键切换。
35-
- 登录且在 admin list:可以进入后台管理权限和账号配置
36-
- `STRATEGY_SWITCH_ADMIN_LOGINS` 是兜底管理员,不通过后台删除,避免把自己锁在外面。
38+
- allowlist 用户:可以一键切换。
39+
- admin 用户:可以进入 `/admin`,维护 allowlist、admin list 和账号下拉 JSON
40+
- `STRATEGY_SWITCH_ADMIN_LOGINS` 是兜底管理员来源,后台保存时会自动保留,避免把自己锁在外面。
3741

3842
## 安全边界
3943

40-
- 后台只管理 GitHub login 和账号路由信息,不保存 broker 密码、token、API key。
41-
- 所有后台写操作使用 POST,并复用当前 Worker 的 Same-Origin 校验。
42-
- session cookie 继续使用 HttpOnly、Secure、SameSite=Lax 和 HMAC 签名。
43-
- dispatch token 与后台配置分离;管理员能改账号路由,但不能在前端读取 token。
44-
- 每次后台修改写 audit log,至少记录时间、管理员 login、操作类型,不记录密钥。
45-
46-
## 分阶段落地
47-
48-
1. 先上线当前 secret 模式:页面一键切换、账号配置由 `STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON` 提供。
49-
2. `/admin` 只读验证页已经具备:可验证 `STRATEGY_SWITCH_ADMIN_LOGINS` 管理权限,并展示已加载账号数量。
50-
3. 给 Worker 增加 KV 读取:优先读 KV,没有 KV 时回退到 secrets。
51-
4. 增加 `/api/admin/config` 写接口:管理员可更新 allowlist 和账号配置。
52-
5. 增加 audit log 与回滚:保留最近版本,改错后可以恢复。
44+
- 后台只保存 GitHub login 和账号路由信息。
45+
- 不保存 broker 密码、token、API key 或云密钥。
46+
- 后台写操作使用 POST,并校验 Same-Origin。
47+
- session cookie 使用 HttpOnly、Secure、SameSite=Lax 和 HMAC 签名。
48+
- GitHub dispatch token 只在 Worker secret 中,前端和后台配置接口都不会返回。
5349

54-
这个方案不引入独立数据库、用户系统或复杂 RBAC,适合个人开源项目。真正的权限根仍然是你的 GitHub 账号和 Worker secret
50+
这个方案保留个人项目的简单性:没有独立数据库、审批流或复杂 RBAC,但公开页面不能被陌生人直接操作

web/strategy-switch-console/README.md

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Optional variables:
2323
RUNTIME_SETTINGS_REPO=QuantStrategyLab/QuantRuntimeSettings
2424
RUNTIME_SETTINGS_WORKFLOW=manual-strategy-switch.yml
2525
RUNTIME_SETTINGS_REF=main
26-
STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON={"longbridge":[{"key":"hk","label":"hk","target_name":"hk","account_selector":"HK"},{"key":"sg","label":"sg","target_name":"sg","account_selector":"SG"},{"key":"paper","label":"paper","target_name":"paper","account_selector":"PAPER"}],"ibkr":[{"key":"u0000000","label":"u0000000","target_name":"u0000000","account_selector":"u0000000"}],"schwab":[{"key":"default","label":"default","target_name":"default"}],"firstrade":[{"key":"default","label":"default","target_name":"default"}]}
26+
STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON=<contents of account-options.example.json>
2727
```
2828

2929
`ALLOWED_GITHUB_LOGINS` and `STRATEGY_SWITCH_ADMIN_LOGINS` are comma-separated lists:
@@ -32,7 +32,7 @@ STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON={"longbridge":[{"key":"hk","label":"hk","ta
3232
your-github-login
3333
```
3434

35-
The login entrypoint is `/login` on the Worker domain. When the Worker is available, the page header shows the GitHub sign-in link. After sign-in, `/api/session` returns:
35+
The login entrypoint is `/login` on the Worker domain. The page header keeps a single Login Management entry. After sign-in, `/api/session` returns:
3636

3737
```json
3838
{
@@ -43,7 +43,21 @@ The login entrypoint is `/login` on the Worker domain. When the Worker is availa
4343
}
4444
```
4545

46-
`admin=true` means the login is listed in `STRATEGY_SWITCH_ADMIN_LOGINS`. You can also open `/admin` directly to verify admin permission; non-admin users receive 403.
46+
`admin=true` means the login is listed in `STRATEGY_SWITCH_ADMIN_LOGINS` or the KV-backed admin list. Open `/admin` to manage allowed GitHub logins and account dropdown routes; non-admin users receive 403.
47+
48+
## Admin Management
49+
50+
GitHub OAuth 2.0 is the only login method. Keep your own GitHub login in `STRATEGY_SWITCH_ADMIN_LOGINS`; that secret is the break-glass admin source and cannot be removed from the UI.
51+
52+
For editable admin settings, bind a Cloudflare KV namespace named `STRATEGY_SWITCH_CONFIG`. The Worker uses these KV keys:
53+
54+
```text
55+
auth_config
56+
account_options
57+
audit_log
58+
```
59+
60+
Without the KV binding, `/admin` is read-only and the Worker falls back to `ALLOWED_GITHUB_LOGINS`, `STRATEGY_SWITCH_ADMIN_LOGINS`, and `STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON`.
4761

4862
## Page Asset
4963

@@ -72,15 +86,19 @@ cd web/strategy-switch-console
7286
wrangler secret put STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON < /tmp/strategy-switch-accounts.json
7387
```
7488

89+
After `STRATEGY_SWITCH_CONFIG` is bound, admins can also edit and save the same account JSON from `/admin`. KV takes precedence over the secret; the secret remains a fallback.
90+
7591
Each account item supports:
7692

7793
```json
7894
{
79-
"key": "u0000000",
80-
"label": "u0000000",
81-
"target_name": "u0000000",
82-
"account_selector": "u0000000",
83-
"service_name": "interactive-brokers-u0000000-service"
95+
"key": "u15998061",
96+
"label": "u15998061",
97+
"target_name": "u15998061",
98+
"account_selector": "U15998061",
99+
"deployment_selector": "live-u1599-tqqq",
100+
"account_scope": "live-u1599-tqqq",
101+
"service_name": "interactive-brokers-live-u1599-tqqq-service"
84102
}
85103
```
86104

@@ -116,6 +134,14 @@ wrangler secret put STRATEGY_SWITCH_ADMIN_LOGINS
116134
wrangler secret put STRATEGY_SWITCH_ACCOUNT_OPTIONS_JSON < /tmp/strategy-switch-accounts.json
117135
```
118136

137+
Create and bind KV if you want `/admin` to save changes:
138+
139+
```bash
140+
wrangler kv namespace create STRATEGY_SWITCH_CONFIG
141+
```
142+
143+
Add the returned namespace id to `wrangler.toml`.
144+
119145
Deploy:
120146

121147
```bash

0 commit comments

Comments
 (0)