feat: MCP 端点鉴权改用后台 Token 管理,废弃 mcp_kv 配置
build-and-deploy / build-and-deploy (push) Successful in 42s
build-and-deploy / build-and-deploy (push) Successful in 42s
- tokenService 支持 mcpEnabled 标记,新增 getMcpTokenValues 与迁移逻辑 - /mcp 鉴权改为校验 Token 表中标记为 MCP 访问的生效令牌 - 启动时自动把旧 mcp_token 配置迁成一条 MCP Token,保证旧客户端不中断 - admin 页面 Token 表新增 MCP 访问列与生成/编辑开关 - 新增 tokenService 测试(58 项全部通过)
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Token 服务单元测试
|
||||
* 覆盖:mcpEnabled 标记、MCP token 列表过滤、历史 MCP 配置迁移
|
||||
* 运行:npm test
|
||||
*/
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
const path = require("node:path");
|
||||
|
||||
// 使用临时数据目录,避免污染真实 data/
|
||||
const tmpDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "registry-token-test-"));
|
||||
process.env.REGISTRY_DATA_DIR = tmpDataDir;
|
||||
|
||||
const { init } = require("../src/persistence/duckdbAdapter");
|
||||
const tokenService = require("../src/services/tokenService");
|
||||
|
||||
test.before(async () => {
|
||||
await init();
|
||||
});
|
||||
|
||||
test("createToken 保存 mcpEnabled 标记", async () => {
|
||||
const mcpToken = await tokenService.createToken({
|
||||
name: "mcp-key",
|
||||
projectId: "all",
|
||||
permission: "readwrite",
|
||||
mcpEnabled: true,
|
||||
expiresInDays: 30,
|
||||
});
|
||||
assert.strictEqual(mcpToken.mcpEnabled, true);
|
||||
const plainToken = await tokenService.createToken({
|
||||
name: "plain",
|
||||
mcpEnabled: false,
|
||||
});
|
||||
assert.strictEqual(plainToken.mcpEnabled, false);
|
||||
});
|
||||
|
||||
test("getMcpTokenValues 仅返回生效的 MCP token 明文", async () => {
|
||||
const values = await tokenService.getMcpTokenValues();
|
||||
assert.ok(values.length >= 1);
|
||||
// 明文值以 reg_ 开头
|
||||
assert.ok(values.every((v) => typeof v === "string" && v.startsWith("reg_")));
|
||||
});
|
||||
|
||||
test("updateToken 可切换 mcpEnabled", async () => {
|
||||
const plain = await tokenService.createToken({ name: "toggle", mcpEnabled: false });
|
||||
const before = await tokenService.getMcpTokenValues();
|
||||
assert.ok(!before.includes(plain.value));
|
||||
await tokenService.updateToken(plain.id, { mcpEnabled: true });
|
||||
const after = await tokenService.getMcpTokenValues();
|
||||
assert.ok(after.includes(plain.value));
|
||||
});
|
||||
|
||||
test("吊销或过期后 MCP token 不再生效", async () => {
|
||||
const t = await tokenService.createToken({ name: "will-revoke", mcpEnabled: true, expiresInDays: 30 });
|
||||
const ok = await tokenService.getMcpTokenValues();
|
||||
assert.ok(ok.includes(t.value));
|
||||
await tokenService.revokeToken(t.id);
|
||||
const revoked = await tokenService.getMcpTokenValues();
|
||||
assert.ok(!revoked.includes(t.value));
|
||||
});
|
||||
|
||||
test("migrateLegacyMcpToken 把历史 mcp_kv 配置迁移为 Token 记录", async () => {
|
||||
// 清理当前 token 表,模拟空 MCP 标记状态
|
||||
const { loadApiTokens, saveApiTokens, saveMcpConfig, loadMcpConfig } = require("../src/persistence/duckdbAdapter");
|
||||
await saveApiTokens([]);
|
||||
await saveMcpConfig({ enabled: true, token: "legacy-mcp-token-abc" });
|
||||
await tokenService.migrateLegacyMcpToken();
|
||||
|
||||
const tokens = await loadApiTokens();
|
||||
const migrated = tokens.find((t) => t.mcpEnabled);
|
||||
assert.ok(migrated, "应生成一条 mcpEnabled 的 Token");
|
||||
assert.strictEqual(migrated.value, "legacy-mcp-token-abc");
|
||||
const values = await tokenService.getMcpTokenValues();
|
||||
assert.ok(values.includes("legacy-mcp-token-abc"));
|
||||
// 旧配置已清空
|
||||
const mcp = await loadMcpConfig();
|
||||
assert.strictEqual(mcp.token, "");
|
||||
});
|
||||
Reference in New Issue
Block a user