- Scope @typescript-eslint/no-explicit-any off for *.spec.ts test doubles - Type enhance() submit callbacks with SubmitFunction in api-keys page - Drop as-any casts in proclamations upsert by using typed form data - Fix useless regex escapes in profiles phone validation - Remove unused locals param from api-keys load - Use resolve() for admin nav/error links; store literal hrefs in adminModels - Update README admin section and ADMIN_OVERHAUL lint caveat
74 lines
1.9 KiB
TypeScript
74 lines
1.9 KiB
TypeScript
import type { PageServerLoad } from './$types';
|
|
import { error } from '@sveltejs/kit';
|
|
import { prisma } from '$lib/db';
|
|
import { rotateApiKey } from '$lib/server/api-key';
|
|
|
|
const SUPER = 1;
|
|
|
|
export const load: PageServerLoad = async ({ parent }) => {
|
|
const { adminUser } = await parent();
|
|
if (adminUser.roleId > SUPER) {
|
|
throw error(403, 'Only Super admins can manage API keys');
|
|
}
|
|
|
|
const now = new Date();
|
|
const activeKey = await prisma.apiKey.findFirst({
|
|
where: {
|
|
revokedAt: null,
|
|
OR: [{ expiresAt: null }, { expiresAt: { gt: now } }]
|
|
},
|
|
select: { keyPrefix: true, createdAt: true, expiresAt: true },
|
|
orderBy: { createdAt: 'desc' }
|
|
});
|
|
|
|
const expiredCount = await prisma.apiKey.count({
|
|
where: {
|
|
OR: [{ revokedAt: { not: null, lte: now } }, { expiresAt: { not: null, lte: now } }]
|
|
}
|
|
});
|
|
|
|
return {
|
|
activeKey: activeKey
|
|
? {
|
|
prefix: activeKey.keyPrefix,
|
|
createdAt: activeKey.createdAt.toISOString(),
|
|
expiresAt: activeKey.expiresAt?.toISOString() ?? null
|
|
}
|
|
: null,
|
|
totalRotated: expiredCount
|
|
};
|
|
};
|
|
|
|
async function viewerRoleId(locals: App.Locals): Promise<number> {
|
|
if (!locals.user?.id) return 4;
|
|
const user = await prisma.user.findUnique({
|
|
where: { id: locals.user.id },
|
|
select: { roleId: true }
|
|
});
|
|
return user?.roleId ?? 4;
|
|
}
|
|
|
|
export const actions = {
|
|
rotate: async ({ locals }) => {
|
|
if ((await viewerRoleId(locals)) > SUPER) {
|
|
return { success: false, error: 'Not authorized' };
|
|
}
|
|
|
|
const result = await rotateApiKey(false);
|
|
return {
|
|
success: true,
|
|
newKey: result.plaintext,
|
|
mode: 'normal' as const,
|
|
expiresAt: result.previousExpiresAt?.toISOString() ?? null
|
|
};
|
|
},
|
|
rotateEmergency: async ({ locals }) => {
|
|
if ((await viewerRoleId(locals)) > SUPER) {
|
|
return { success: false, error: 'Not authorized' };
|
|
}
|
|
|
|
const result = await rotateApiKey(true);
|
|
return { success: true, newKey: result.plaintext, mode: 'emergency' as const };
|
|
}
|
|
};
|