feat(permissions): guest permission types + resolve guest branch

This commit is contained in:
saopig1 committed 2026-06-25 11:08:17 +08:00
1 parent 3433ccb661
commit 1fd5dbaba3
2 files changed
+76 -2

No files matched your search

+43
View File
@@ -88,3 +88,46 @@ describe("PermissionStore", () => {
}); });
}); });
}); });
import { GUEST_PERMISSION_FLAGS } from "./permissions.js";
describe("resolvePermissionContext guest branch", () => {
const noStore = {
getCapabilities: () => [],
getBotAccess: () => [] as string[],
setPermissions: () => {},
pruneBot: () => {},
};
it("guest has no member capabilities and exposes the guest permissions + bots", () => {
const ctx = resolvePermissionContext("guest", "__guest__", noStore, {
bots: ["bot1"],
permissions: {
addToQueue: true, playNext: false, playNow: false,
skip: true, transport: false, removeClear: false, playMode: false,
},
});
expect([...ctx.capabilities]).toEqual([]);
expect(ctx.bots).toBeInstanceOf(Set);
expect((ctx.bots as Set<string>).has("bot1")).toBe(true);
expect(ctx.guest?.addToQueue).toBe(true);
expect(ctx.guest?.skip).toBe(true);
});
it("guest with bots:'all' resolves to 'all'", () => {
const ctx = resolvePermissionContext("guest", "__guest__", noStore, {
bots: "all",
permissions: {
addToQueue: true, playNext: false, playNow: false,
skip: false, transport: false, removeClear: false, playMode: false,
},
});
expect(ctx.bots).toBe("all");
});
it("exposes the 7 canonical flags", () => {
expect([...GUEST_PERMISSION_FLAGS].sort()).toEqual(
["addToQueue", "playMode", "playNext", "playNow", "removeClear", "skip", "transport"].sort()
);
});
});
+33 -2
View File
@@ -21,6 +21,27 @@ export function isCapability(x: string): x is Capability {
export type BotAccess = "all" | string[]; export type BotAccess = "all" | string[];
export interface GuestPermissions {
addToQueue: boolean;
playNext: boolean;
playNow: boolean;
skip: boolean;
transport: boolean;
removeClear: boolean;
playMode: boolean;
}
export const GUEST_PERMISSION_FLAGS = [
"addToQueue",
"playNext",
"playNow",
"skip",
"transport",
"removeClear",
"playMode",
] as const;
export type GuestFlag = (typeof GUEST_PERMISSION_FLAGS)[number];
export interface PermissionStore { export interface PermissionStore {
getCapabilities(userId: string): Capability[]; getCapabilities(userId: string): Capability[];
getBotAccess(userId: string): BotAccess; getBotAccess(userId: string): BotAccess;
@@ -71,16 +92,26 @@ export function createPermissionStore(db: Database.Database): PermissionStore {
export interface PermissionContext { export interface PermissionContext {
capabilities: Set<string>; capabilities: Set<string>;
bots: "all" | Set<string>; bots: "all" | Set<string>;
guest?: GuestPermissions;
} }
export function resolvePermissionContext( export function resolvePermissionContext(
role: "admin" | "member", role: "admin" | "member" | "guest",
userId: string, userId: string,
store: PermissionStore store: PermissionStore,
guest?: { bots: BotAccess; permissions: GuestPermissions }
): PermissionContext { ): PermissionContext {
if (role === "admin") { if (role === "admin") {
return { capabilities: new Set(CAPABILITIES), bots: "all" }; return { capabilities: new Set(CAPABILITIES), bots: "all" };
} }
if (role === "guest") {
const bots = guest?.bots ?? [];
return {
capabilities: new Set<string>(),
bots: bots === "all" ? "all" : new Set(bots),
guest: guest?.permissions,
};
}
const access = store.getBotAccess(userId); const access = store.getBotAccess(userId);
return { return {
capabilities: new Set(store.getCapabilities(userId)), capabilities: new Set(store.getCapabilities(userId)),