feat(api): read/write adminGroups in bot settings endpoints

This commit is contained in:
saopig1 committed 2026-06-26 20:44:29 +08:00
1 parent 72ffd44f68
commit 3346286ffd
2 files changed
+48 -1

No files matched your search

+38
View File
@@ -158,6 +158,44 @@ describe("bot router /settings", () => {
expect(bot.autoPauseCalls).toEqual([]); expect(bot.autoPauseCalls).toEqual([]);
} }
}); });
it("GET /settings includes adminGroups reflecting config", async () => {
config.adminGroups = [6, 8];
const res = await request(app).get("/api/bot/settings").set("Cookie", cookie);
expect(res.status).toBe(200);
expect(res.body.adminGroups).toEqual([6, 8]);
});
it("POST /settings persists a validated adminGroups and GET returns it", async () => {
const res = await request(app)
.post("/api/bot/settings")
.set("Cookie", cookie)
.send({ adminGroups: [6, 8] });
expect(res.status).toBe(200);
expect(res.body.adminGroups).toEqual([6, 8]);
expect(config.adminGroups).toEqual([6, 8]);
const followUp = await request(app).get("/api/bot/settings").set("Cookie", cookie);
expect(followUp.body.adminGroups).toEqual([6, 8]);
});
it("POST /settings filters invalid adminGroups entries (negative, non-integer, non-number)", async () => {
const res = await request(app)
.post("/api/bot/settings")
.set("Cookie", cookie)
.send({ adminGroups: [6, -1, 2.5, "x", 8] });
expect(res.status).toBe(200);
expect(config.adminGroups).toEqual([6, 8]);
});
it("POST /settings ignores a non-array adminGroups (leaves config unchanged)", async () => {
config.adminGroups = [6];
const res = await request(app)
.post("/api/bot/settings")
.set("Cookie", cookie)
.send({ adminGroups: "6" });
expect(res.status).toBe(200);
expect(config.adminGroups).toEqual([6]);
});
}); });
describe("bot router /settings guest-mode gating + persistence", () => { describe("bot router /settings guest-mode gating + persistence", () => {
+10 -1
View File
@@ -36,6 +36,7 @@ export function createBotRouter(
res.json({ res.json({
idleTimeoutMinutes: config.idleTimeoutMinutes ?? 0, idleTimeoutMinutes: config.idleTimeoutMinutes ?? 0,
autoPauseOnEmpty: config.autoPauseOnEmpty, autoPauseOnEmpty: config.autoPauseOnEmpty,
adminGroups: config.adminGroups ?? [],
guestMode: config.guestMode, guestMode: config.guestMode,
}); });
}); });
@@ -43,7 +44,7 @@ export function createBotRouter(
// POST /api/bot/settings — 保存全局 bot 行为设置 (gated: changing global bot // POST /api/bot/settings — 保存全局 bot 行为设置 (gated: changing global bot
// behavior is a bot.manage operation, consistent with PR #80's permission model) // behavior is a bot.manage operation, consistent with PR #80's permission model)
router.post("/settings", requirePermission("bot.manage"), (req, res) => { router.post("/settings", requirePermission("bot.manage"), (req, res) => {
const { idleTimeoutMinutes, autoPauseOnEmpty, guestMode } = req.body; const { idleTimeoutMinutes, autoPauseOnEmpty, guestMode, adminGroups } = req.body;
const hasIdle = idleTimeoutMinutes !== undefined; const hasIdle = idleTimeoutMinutes !== undefined;
if (hasIdle && (typeof idleTimeoutMinutes !== "number" || idleTimeoutMinutes < 0)) { if (hasIdle && (typeof idleTimeoutMinutes !== "number" || idleTimeoutMinutes < 0)) {
@@ -74,6 +75,13 @@ export function createBotRouter(
} }
} }
if (Array.isArray(adminGroups)) {
config.adminGroups = adminGroups.filter(
(g: unknown): g is number =>
typeof g === "number" && Number.isInteger(g) && g >= 0,
);
}
saveConfig(configPath, config); saveConfig(configPath, config);
// Guest-mode changed: tear down / re-scope in-flight guest WS sockets so a // Guest-mode changed: tear down / re-scope in-flight guest WS sockets so a
@@ -92,6 +100,7 @@ export function createBotRouter(
res.json({ res.json({
idleTimeoutMinutes: config.idleTimeoutMinutes ?? 0, idleTimeoutMinutes: config.idleTimeoutMinutes ?? 0,
autoPauseOnEmpty: config.autoPauseOnEmpty, autoPauseOnEmpty: config.autoPauseOnEmpty,
adminGroups: config.adminGroups ?? [],
guestMode: config.guestMode, guestMode: config.guestMode,
}); });
}); });