fix(guest): add playCollection permission so guests can Play All playlist/album (#103)

- New guest flag playCollection (default OFF), gates play-playlist/play-album
- Keeps playNow's non-destructive semantics intact (Play All clears the queue)
- Admin-toggleable in Settings → 游客模式; default-off, backward-compatible
- Frontend: gate the 播放全部 button on the flag + surface 403 as a toast
  instead of failing silently (the silent-failure half of the issue)
This commit is contained in:
saopig1 committed 2026-06-29 12:12:47 +08:00
1 parent e2fa288f48
commit 70c0273ae7
11 files changed
+61 -22

No files matched your search

+3 -1
View File
@@ -115,6 +115,7 @@ describe("guestMode config", () => {
expect(c.guestMode.permissions).toEqual({
addToQueue: true, playNext: false, playNow: false,
skip: false, transport: false, removeClear: false, playMode: false,
playCollection: false,
});
});
@@ -167,10 +168,11 @@ describe("guestMode config", () => {
});
it("a string permissions value yields defaults with no numeric index keys", () => {
const gm = loadGuestMode({ guestMode: { permissions: "hacked" } });
// 7 known flags present at their defaults
// all known flags present at their defaults
expect(gm.permissions).toEqual({
addToQueue: true, playNext: false, playNow: false,
skip: false, transport: false, removeClear: false, playMode: false,
playCollection: false,
});
// no garbage index keys leaked from spreading a string
expect((gm.permissions as unknown as Record<string, unknown>)["0"]).toBeUndefined();
+1
View File
@@ -63,6 +63,7 @@ export function getDefaultConfig(): BotConfig {
transport: false,
removeClear: false,
playMode: false,
playCollection: false,
},
},
};
+4 -2
View File
@@ -105,6 +105,7 @@ describe("resolvePermissionContext guest branch", () => {
permissions: {
addToQueue: true, playNext: false, playNow: false,
skip: true, transport: false, removeClear: false, playMode: false,
playCollection: false,
},
});
expect([...ctx.capabilities]).toEqual([]);
@@ -120,14 +121,15 @@ describe("resolvePermissionContext guest branch", () => {
permissions: {
addToQueue: true, playNext: false, playNow: false,
skip: false, transport: false, removeClear: false, playMode: false,
playCollection: false,
},
});
expect(ctx.bots).toBe("all");
});
it("exposes the 7 canonical flags", () => {
it("exposes the 8 canonical flags", () => {
expect([...GUEST_PERMISSION_FLAGS].sort()).toEqual(
["addToQueue", "playMode", "playNext", "playNow", "removeClear", "skip", "transport"].sort()
["addToQueue", "playCollection", "playMode", "playNext", "playNow", "removeClear", "skip", "transport"].sort()
);
});
});
+3
View File
@@ -29,6 +29,8 @@ export interface GuestPermissions {
transport: boolean;
removeClear: boolean;
playMode: boolean;
/** Load + play an entire playlist/album (clears the queue). Issue #103. */
playCollection: boolean;
}
export const GUEST_PERMISSION_FLAGS = [
@@ -39,6 +41,7 @@ export const GUEST_PERMISSION_FLAGS = [
"transport",
"removeClear",
"playMode",
"playCollection",
] as const;
export type GuestFlag = (typeof GUEST_PERMISSION_FLAGS)[number];