diff --git a/README.md b/README.md index be75cca..43da4d6 100644 --- a/README.md +++ b/README.md @@ -50,4 +50,3 @@ CRUD layer has been removed; see `docs/ADMIN_OVERHAUL.md` for the migration note | `/admin/proclamation-categories` | Draggable reorder | | `/admin/proclamation-groups` | Draggable reorder | | `/admin/license-keys` | List-only + rotate | -| `/admin/api-keys` | Super-only key rotation | diff --git a/src/routes/(protected)/admin/api-keys/+page.server.ts b/src/routes/(protected)/admin/api-keys/+page.server.ts deleted file mode 100644 index 49a5628..0000000 --- a/src/routes/(protected)/admin/api-keys/+page.server.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { PageServerLoad } from './$types'; -import { error } from '@sveltejs/kit'; -import { prisma } from '$lib/db'; -import { rotateApiKey } from '$lib/server/api-key'; -import { isSuper } from '$lib/server/roles'; - -export const load: PageServerLoad = async ({ locals }) => { - if (!isSuper(locals)) { - 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 - }; -}; - -export const actions = { - rotate: async ({ locals }) => { - if (!isSuper(locals)) { - 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 (!isSuper(locals)) { - return { success: false, error: 'Not authorized' }; - } - - const result = await rotateApiKey(true); - return { success: true, newKey: result.plaintext, mode: 'emergency' as const }; - } -}; diff --git a/src/routes/(protected)/admin/api-keys/+page.svelte b/src/routes/(protected)/admin/api-keys/+page.svelte deleted file mode 100644 index 40e23e0..0000000 --- a/src/routes/(protected)/admin/api-keys/+page.svelte +++ /dev/null @@ -1,143 +0,0 @@ - - -
-
-

API Key Management

- {#if data.activeKey} - - Active - - {/if} -
- - {#if newKey} -
-

- {mode === 'emergency' ? 'Emergency Rotation Complete' : 'New API Key Generated'} -

-

This key will not be shown again. Copy it now.

-
- {newKey} -
-
- - -
- {#if mode === 'normal' && expiryDate} -

- The previous key will remain valid until {new Date(expiryDate).toLocaleString()} - to give in-field devices time to update. -

- {/if} -
- {/if} - - {#if toastMessage} -
- {toastMessage} -
- {/if} - -
-

Current Key Status

- {#if data.activeKey} -
-
- Prefix: {data.activeKey.prefix} -
-
Created: {new Date(data.activeKey.createdAt).toLocaleString()}
- {#if data.activeKey.expiresAt} -
Expires: {new Date(data.activeKey.expiresAt).toLocaleString()}
- {/if} -
- {:else} -

No active API key. Generate one below.

- {/if} - {#if data.totalRotated > 0} -

- {data.totalRotated} previous key(s) have been rotated. -

- {/if} -
- -
-
- -
-
- -
-
-
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 862663f..07f1b60 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -165,17 +165,6 @@ {model.displayName} {/each} - (adminOpen = false)} - class={`block rounded-md px-3 py-2 text-sm transition ${ - isActive('/admin/api-keys') - ? 'bg-indigo-50 text-indigo-700' - : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900' - }`} - > - API Keys - {/if} @@ -317,13 +306,6 @@ {model.displayName} {/each} - - API Keys - {/if}