Files
portal/src/routes/(protected)/admin/api-keys/+page.svelte
T
NeilClack 1d11de8cec Refactor admin to shared generic CRUD; restructure proclamation categories/groups and APIs
- Replace per-model admin forms with the shared generic AdminTable/AdminModal CRUD
- Add ProclamationGroup model and admin + API v2 routes (replaces subscription levels)
- Drop subscription levels and flatten proclamation categories
- Update v2 serializers and seed data accordingly
2026-08-27 12:03:40 -05:00

112 lines
2.7 KiB
Svelte

<script lang="ts">
import type { PageProps } from './$types';
import { enhance } from '$app/forms';
let { data }: PageProps = $props();
let newKey = $state('');
let mode = $state<'normal' | 'emergency' | null>(null);
let expiryDate = $state<string | null>(null);
let toastMessage = $state('');
function handleRotate(emergency: boolean) {
return ({ cancel }: any) => {
const msg = emergency
? 'EMERGENCY ROTATION: The current key will be revoked IMMEDIATELY. Continue?'
: 'Rotate the API key? The current key will remain valid for 48 hours.';
if (!confirm(msg)) {
cancel();
return;
}
return async ({ result }: any) => {
if (result.type === 'success' && result.status === 200) {
if (result.data?.newKey) {
newKey = result.data.newKey;
mode = result.data.mode ?? null;
expiryDate = result.data.expiresAt ?? null;
}
} else {
toastMessage = 'Rotation failed';
}
};
};
}
function copyKey() {
navigator.clipboard.writeText(newKey);
toastMessage = 'Copied to clipboard';
}
function dismissKey() {
newKey = '';
mode = null;
expiryDate = null;
}
</script>
<div>
<h1>API Key Management</h1>
{#if newKey}
<div>
<p>
{mode === 'emergency' ? 'Emergency Rotation Complete' : 'New API Key Generated'}
</p>
<p>This key will not be shown again. Copy it now.</p>
<div>
<code>
{newKey}
</code>
</div>
<div>
<button type="button" onclick={copyKey}> Copy to Clipboard </button>
<button type="button" onclick={dismissKey}> Dismiss </button>
</div>
{#if mode === 'normal' && expiryDate}
<p>
The previous key will remain valid until {new Date(expiryDate).toLocaleString()}
to give in-field devices time to update.
</p>
{/if}
</div>
{/if}
{#if toastMessage}
<div>
{toastMessage}
</div>
{/if}
<div>
<h2>Current Key Status</h2>
{#if data.activeKey}
<div>
<div>
Prefix: <code>{data.activeKey.prefix}</code>
</div>
<div>Created: {new Date(data.activeKey.createdAt).toLocaleString()}</div>
{#if data.activeKey.expiresAt}
<div>Expires: {new Date(data.activeKey.expiresAt).toLocaleString()}</div>
{/if}
</div>
{:else}
<p>No active API key. Generate one below.</p>
{/if}
{#if data.totalRotated > 0}
<p>
{data.totalRotated} previous key(s) have been rotated.
</p>
{/if}
</div>
<div>
<form method="POST" action="?/rotate" use:enhance={handleRotate(false)}>
<button type="submit"> Generate New API Key </button>
</form>
<form method="POST" action="?/rotateEmergency" use:enhance={handleRotate(true)}>
<button type="submit"> Emergency Rotation </button>
</form>
</div>
</div>