feat: 设置页 MCP 管理卡片(方案B)— DB 真开关 + 即时生效 + env 仅首次种子

- app_settings 表(key/value):MCP 开关与令牌存 DB,不属于任何用户、不参与同步协议
- 配置读取/写入收敛到 core/mcp.js(readMcpConfig/saveMcpToken/setMcpEnabled)
- 状态语义:未配置→404(端点视为不存在);已配置关闭→503(可重新开启);令牌错→401
- env MCP_AUTH_TOKEN 仅首次种子(库里从没有过令牌时迁入 DB),不设也行;后台改动即时生效无需重启
- 管理 API(登录态):GET /api/mcp/status(掩码)、POST /api/mcp/token(生成/重置,明文仅一次)、GET /api/mcp/token(复制用)、POST /api/mcp/enabled
- 设置页新增「AI 接入 (MCP)」卡片:开关 toggle、掩码展示、一键生成并复制(writeClipboard 兜底)、重置令牌;未登录徽章「本设备请先登录」
- 入口接线:node-server.js 与 worker.js 均挂 /mcp(seedToken 透传);sw.js 放行裸 /mcp(v2→v3)
- 两个适配器补 getSetting/setSetting(node:sqlite 与 D1 同语义)
- 测试:server/test/mcp.test.js 39 项(未配置关闭/鉴权/JSON-RPC 全链路/开关热切换/令牌轮换);npm test 全绿(107+72+39)
- 线上 E2E:生产令牌 initialize/tools 7 个/2026-09 大盘(出勤11天/82h/挣得1599);公网 HTTPS 链路通;UI puppeteer 17/17(含关→503→开→200 热切换,全程无重启)
- 文档:README + server/README(环境变量表/接口表/数据模型/MCP 章节)
This commit is contained in:
OpenClaw Agent
2026-09-13 10:24:33 +08:00
parent 9702d1949a
commit 26306eee52
14 changed files with 1006 additions and 12 deletions
+22 -3
View File
@@ -14,15 +14,16 @@ work-hours-tracker/
├── core/ ← 平台无关,VPS 与 CF Workers 共用
│ ├── api.js HTTP 接口逻辑(收发标准 Request/Response)
│ ├── auth.js PBKDF2 密码 + SHA-256 令牌(纯 Web Crypto)
│ ├── mcp.js 只读 MCP 端点(JSON-RPC 2.0)+ 配置管理
│ ├── store.js 存储契约 + 输入校验
│ └── schema.sql 建表语句(SQLite / D1 通用)
├── adapters/
│ ├── sqlite-node.js VPS:node:sqlite(Node 22 内置,零 npm 依赖)
│ └── d1.js Cloudflare D1
├── node-server.js VPS 入口:静态页 + /api/* 同一端口
├── node-server.js VPS 入口:静态页 + /api/* + /mcp 同一端口
├── worker.js CF Workers 入口
├── wrangler.toml CF 部署配置
└── test/api.test.js 服务端用例(72 项)
└── test/ api.test.js(72 项)+ mcp.test.js(39 项)
```
换平台只需换适配器,`core/` 一行都不用动。
@@ -43,6 +44,7 @@ npm start # 默认 9523,静态页与 API 同源
| `DB_PATH` | `server/data/work-hours.db` | SQLite 文件位置 |
| `STATIC_DIR` | 项目根 | 静态目录 |
| `ALLOW_REGISTER` | 未设 | 设 `1` 才开放注册 |
| `MCP_AUTH_TOKEN` | 未设 | MCP 端点接入令牌,**仅首次种子**:库里已有令牌后以 DB 为准(页面后台可改),可不设 |
首次访问 `http://<地址>:9523` → 设置页 → 云端同步 → 创建首个账号。
**建首个账号后注册自动关闭**,公网暴露也不会被陌生人开号。
@@ -50,10 +52,22 @@ npm start # 默认 9523,静态页与 API 同源
跑测试:
```bash
cd server && npm test # 72 项
cd server && npm test # 72 项 API + 39 项 MCP
cd .. && node verify_payroll.js # 220 项
```
## MCP Server(POST /mcp)
服务端内置只读 MCP 端点(Streamable HTTP,JSON-RPC 2.0),让对话式 AI 查询出勤与薪酬。
工具 7 个:`get_month_summary` / `get_shift_records` / `get_payroll_settings` / `get_penalty_details` / `simulate_leave` / `list_month_overview` / `get_business_rules`。
算钱一律复用 `shared/payroll.js` 引擎(与前端看板同源),不让 AI 口算。
- **配置存 DB**(`app_settings` 表),页面「设置 → AI 接入 (MCP)」可开关/生成/重置令牌,**即时生效无需重启**
- `MCP_AUTH_TOKEN` 环境变量只做首启种子(库里从没有过令牌时迁入 DB),不设也行
- 状态语义:未配置 → `404`(端点视为不存在);已配置但关闭 → `503`;令牌错误 → `401`
- 管理 API(登录态):`GET /api/mcp/status`(掩码)、`POST /api/mcp/token`(生成/重置,明文仅回一次)、`GET /api/mcp/token`(复制用取回明文)、`POST /api/mcp/enabled`
- 只读约束:任何工具都不写库,AI 最多能看,不能改
### systemd
现有 `work-hours-tracker.service` 是 `python3 -m http.server 9522`,只发静态文件,
@@ -108,6 +122,10 @@ PBKDF2 十万次迭代实测约 17ms,远低于 Workers 的 CPU 时间预算。
| DELETE | `/api/record/YYYY-MM-DD` | 删单日 |
| DELETE | `/api/adjust/YYYY-MM` | 删单月奖惩 |
| GET | `/api/export` | 导出 JSON,与前端「导出备份」同构,可互相导入 |
| GET | `/api/mcp/status` | MCP 接入状态(configured/enabled/令牌掩码) |
| POST | `/api/mcp/token` | 生成/重置 MCP 令牌(明文仅此一次,自动启用) |
| GET | `/api/mcp/token` | 取回 MCP 令牌明文(复制用) |
| POST | `/api/mcp/enabled` | 开/关 MCP 端点(即时生效,无需重启) |
## 同步机制
@@ -138,6 +156,7 @@ PBKDF2 十万次迭代实测约 17ms,远低于 Workers 的 CPU 时间预算。
| `records` | (user_id, day) | day = `2026-09-08` |
| `month_adjust` | (user_id, month) | month = `2026-09`,`status` = draft/final |
| `settings` | user_id | 整份 JSON,字段会迭代所以不拆列 |
| `app_settings` | key | App 级运行时配置(MCP 开关/令牌),不属于任何用户,不参与同步 |
## 安全