diff --git a/src/routes/(protected)/admin/proclamations/+page.server.ts b/src/routes/(protected)/admin/proclamations/+page.server.ts index 6154c98..fb82252 100644 --- a/src/routes/(protected)/admin/proclamations/+page.server.ts +++ b/src/routes/(protected)/admin/proclamations/+page.server.ts @@ -13,8 +13,16 @@ const ProclamationSchema = z.object({ content: z.string().optional().default('') }); -function isSuper(locals: App.Locals): boolean { - return ((locals.user as { roleId?: number } | null)?.roleId ?? 4) <= 1; +async function isSuper(locals: App.Locals): Promise { + if (!locals.user?.id) return false; + + // The Better Auth session user does not include `roleId`, so read the + // current role from the database. + const user = await prisma.user.findUnique({ + where: { id: locals.user.id }, + select: { roleId: true } + }); + return (user?.roleId ?? 4) <= 1; } export const load: PageServerLoad = async ({ locals }) => { @@ -40,7 +48,7 @@ export const load: PageServerLoad = async ({ locals }) => { const records = proclamations as unknown as Record[]; // Strip content from non-Super users - if (!isSuper(locals)) { + if (!(await isSuper(locals))) { for (const record of records) { delete record.content; } @@ -55,7 +63,7 @@ export const actions = { return { success: false, action: 'upsert', error: 'Unauthorized' }; } - if (!isSuper(locals)) { + if (!(await isSuper(locals))) { return { success: false, action: 'upsert', @@ -98,7 +106,7 @@ export const actions = { return { success: true, action: 'upsert' }; }, delete: async ({ request, locals }) => { - if (!isSuper(locals)) { + if (!(await isSuper(locals))) { return { success: false, action: 'delete', diff --git a/src/routes/(protected)/admin/proclamations/page.server.spec.ts b/src/routes/(protected)/admin/proclamations/page.server.spec.ts index 07efe57..c3f45fc 100644 --- a/src/routes/(protected)/admin/proclamations/page.server.spec.ts +++ b/src/routes/(protected)/admin/proclamations/page.server.spec.ts @@ -18,6 +18,9 @@ vi.mock('$lib/db', () => ({ }, proclamationGroup: { findMany: vi.fn() + }, + user: { + findUnique: vi.fn() } } })); @@ -33,6 +36,7 @@ const mockProclamationDelete = prisma.proclamation.delete as ReturnType; const mockCatFindMany = prisma.proclamationCategory.findMany as ReturnType; const mockGroupFindMany = prisma.proclamationGroup.findMany as ReturnType; +const mockUserFindUnique = prisma.user.findUnique as ReturnType; const frequencies = [{ id: 1, name: 'UT', value: 396 }]; const categories = [{ id: 1, name: 'Healing' }]; @@ -52,6 +56,8 @@ function validRequest(overrides: Record = {}) { describe('proclamations admin page server', () => { beforeEach(() => { vi.clearAllMocks(); + // Default the caller to SUPER (1); override per test as needed. + mockUserFindUnique.mockResolvedValue({ roleId: 1 }); }); describe('load', () => { @@ -68,6 +74,7 @@ describe('proclamations admin page server', () => { }); it('strips content for non-Super users', async () => { + mockUserFindUnique.mockResolvedValue({ roleId: 4 }); const locals = createLocals(mockUser({ roleId: 4 }) as any); const result = await load({ locals } as any); @@ -109,6 +116,7 @@ describe('proclamations admin page server', () => { }); it('rejects non-Super users', async () => { + mockUserFindUnique.mockResolvedValue({ roleId: 4 }); const locals = createLocals(mockUser({ roleId: 4 }) as any); const req = createFormRequest({ name: 'test' }); @@ -149,6 +157,21 @@ describe('proclamations admin page server', () => { }); }); + it('allows a Super user whose session user has no roleId', async () => { + mockProclamationCreate.mockResolvedValue({ id: 'p1' }); + + // The real Better Auth session user does not carry `roleId`; the + // action must look the role up in the database instead. + const locals = createLocals(mockUser({ roleId: null }) as any); + const result = await actions.upsert({ request: validRequest(), locals } as any); + + expect(result).toEqual({ success: true, action: 'upsert' }); + expect(mockUserFindUnique).toHaveBeenCalledWith({ + where: { id: 'user-1' }, + select: { roleId: true } + }); + }); + it('updates an existing proclamation for a Super user', async () => { mockProclamationFindUnique.mockResolvedValue(mockProclamation()); mockProclamationUpdate.mockResolvedValue({}); @@ -200,6 +223,7 @@ describe('proclamations admin page server', () => { describe('delete action', () => { it('rejects non-Super users', async () => { + mockUserFindUnique.mockResolvedValue({ roleId: 4 }); const locals = createLocals(mockUser({ roleId: 4 }) as any); const req = createFormRequest({ id: '1' });