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:
+10
-1
@@ -9,6 +9,7 @@
|
||||
// DB_PATH SQLite 文件(默认 <server>/data/work-hours.db)
|
||||
// STATIC_DIR 静态目录(默认项目根,即 index.html 所在处)
|
||||
// ALLOW_REGISTER=1 开放注册(默认只允许「库里还没有用户」时注册首个账号)
|
||||
// MCP_AUTH_TOKEN MCP 端点(POST /mcp)访问令牌。只做首次种子:库里已有令牌后以 DB 为准(后台可改)
|
||||
// ACCESS_LOG 访问日志文件(默认 <server>/data/access.log;设 off 关闭)
|
||||
|
||||
import { createServer } from 'node:http';
|
||||
@@ -21,6 +22,7 @@ import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join, resolve, normalize, extname } from 'node:path';
|
||||
import { SqliteStore } from './adapters/sqlite-node.js';
|
||||
import { handleApi } from './core/api.js';
|
||||
import { handleMcp } from './core/mcp.js';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@@ -29,6 +31,7 @@ const HOST = process.env.HOST || '0.0.0.0';
|
||||
const DB_PATH = process.env.DB_PATH || join(__dirname, 'data', 'work-hours.db');
|
||||
const STATIC_DIR = resolve(process.env.STATIC_DIR || join(__dirname, '..'));
|
||||
const ALLOW_REGISTER = process.env.ALLOW_REGISTER === '1';
|
||||
const MCP_SEED_TOKEN = process.env.MCP_AUTH_TOKEN || '';
|
||||
const ACCESS_LOG = process.env.ACCESS_LOG === 'off'
|
||||
? null
|
||||
: resolve(process.env.ACCESS_LOG || join(__dirname, 'data', 'access.log'));
|
||||
@@ -350,8 +353,14 @@ const server = createServer(async (req, res) => {
|
||||
res.on('close', () => writeAccess(req, res, startedAt, sock, baseline));
|
||||
try {
|
||||
const u = new URL(req.url, 'http://x');
|
||||
if (u.pathname === '/mcp' || u.pathname.startsWith('/mcp/')) {
|
||||
const response = await handleMcp(await toRequest(req), store, { seedToken: MCP_SEED_TOKEN });
|
||||
return await sendResponse(res, response);
|
||||
}
|
||||
if (u.pathname.startsWith('/api/')) {
|
||||
const response = await handleApi(await toRequest(req), store, { allowRegister: ALLOW_REGISTER });
|
||||
const response = await handleApi(await toRequest(req), store, {
|
||||
allowRegister: ALLOW_REGISTER, mcpSeedToken: MCP_SEED_TOKEN
|
||||
});
|
||||
return await sendResponse(res, response);
|
||||
}
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
||||
|
||||
Reference in New Issue
Block a user