feat(isac): add avatar oversight queue

This commit is contained in:
JB
2026-07-25 14:26:38 -04:00
parent 8079a75c6e
commit e30d4e4598
11 changed files with 217 additions and 20 deletions
+5 -2
View File
@@ -8,6 +8,7 @@ import { AVATAR_PRESETS, AvatarValidationError, processAvatarUpload, saveUploade
import { createMagicLinkToken } from '$lib/server/auth/magic-link';
import { emailDeliveryConfigured, sendAuthenticationLink } from '$lib/server/auth/mail';
import { canRemoveAuthenticationMethod, getAuthenticationMethods } from '$lib/server/auth/methods';
import { recordIsacAudit } from '$lib/server/audit';
export const load: PageServerLoad = async ({ cookies, url }) => {
const user = await getSessionUser(cookies);
@@ -145,8 +146,10 @@ export const actions: Actions = {
if (!(file instanceof File)) return fail(400, { avatarError: 'Choose an image to upload.' });
try {
const output = await processAvatarUpload(file);
await saveUploadedAvatar(await getDatabase(), user.id, output);
return { avatarChanged: true };
const database = await getDatabase();
const uploadedAt = await saveUploadedAvatar(database, user.id, output);
await recordIsacAudit({ actorId: user.id, category: 'user_moderation', action: 'avatar.submitted', entityId: user.id, after: { status: 'pending', uploadedAt, byteSize: output.length } });
return { avatarChanged: true, avatarReviewQueued: true };
} catch (error) {
if (error instanceof AvatarValidationError) return fail(400, { avatarError: error.message });
throw error;
+2 -2
View File
@@ -22,7 +22,7 @@
return 'passkeys';
}
function initialAvatarPanel(): AvatarPanel {
return form?.avatarError ? 'upload' : 'presets';
return form?.avatarError || form?.avatarReviewQueued ? 'upload' : 'presets';
}
let accountPanel = $state<AccountPanel>(initialAccountPanel());
let accessPanel = $state<AccessPanel>(initialAccessPanel());
@@ -111,7 +111,7 @@
<ImageUploadInput name="avatar" accept="image/jpeg,image/png,image/webp,image/avif" required emptyLabel="NO AVATAR SELECTED" browseLabel="CHOOSE AVATAR"/>
<small class="identity-note">JPEG, PNG, WebP, or AVIF · 5 MiB maximum · cropped to a 256 px square.</small>
{#if form?.avatarError}<p class="error">{form.avatarError}</p>{/if}
{#if form?.avatarChanged}<p class="success">Avatar updated.</p>{/if}
{#if form?.avatarChanged}<p class="success">{form?.avatarReviewQueued ? 'Avatar active now · queued for Oversight review.' : 'Avatar updated.'}</p>{/if}
<button class="secondary console-action">UPLOAD CUSTOM AVATAR →</button>
</PendingForm>
{/if}