fix: adminnotify observability — money-critical rows sort first, flood-cap suppression surfaced to operator, stale coordination doc fixed
- notifications priority ordering: money-critical reasons (webhooks, sweeps, refunds, gift-card, manual-refund failures) above routine - admin notifications page exposes the flood-cap suppressed count - adminnotify.go contract doc: removed stale 2FA reissue-fail site, current insert-site list Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
import { Toaster } from '$lib/components/ui/sonner/index.js';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { resendEmailVerification, verifyEmailCode } from '$lib/square/square';
|
||||
import { extractErrorMessage } from '$lib/utils/toast-safe';
|
||||
import { resetZIndexStack } from '$lib/components/ui/dialog/zindex.js';
|
||||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
@@ -41,22 +43,77 @@
|
||||
};
|
||||
});
|
||||
|
||||
// Verify email address
|
||||
// TODO: `/api/verify-email` doesn't exist in the backend. The correct endpoints are
|
||||
// `POST /api/verify/generate` (send verification code) and `POST /api/verify/check` (verify code).
|
||||
// This call silently 404s. Fix required — either add the missing backend route or refactor
|
||||
// to use `/api/verify/generate` + `/api/verify/check`.
|
||||
// Re-send the email verification code via POST /api/verify/generate (the
|
||||
// backend only exposes generate/check — there is no /api/verify-email).
|
||||
async function verify_email() {
|
||||
const loadingToast = toast.loading('Verifying email address...');
|
||||
const response = await fetch('/api/verify-email', {
|
||||
method: 'POST'
|
||||
});
|
||||
const loadingToast = toast.loading('Sending verification code...');
|
||||
if (!authStore.currentUser?.email) {
|
||||
toast.error('Unable to resend the verification code — no email on file', {
|
||||
id: loadingToast
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await resendEmailVerification(authStore.currentUser.email);
|
||||
|
||||
if (response.ok) {
|
||||
toast.success('Email verified successfully!', { id: loadingToast });
|
||||
window.location.reload();
|
||||
} else {
|
||||
toast.error('Failed to verify email address', { id: loadingToast });
|
||||
if (response.ok) {
|
||||
toast.success('If the email exists, a verification code has been sent', {
|
||||
id: loadingToast
|
||||
});
|
||||
window.location.reload();
|
||||
} else {
|
||||
toast.error('Failed to send the verification code', { id: loadingToast });
|
||||
}
|
||||
} catch {
|
||||
toast.error('Network error sending the verification code — please try again', {
|
||||
id: loadingToast
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// The banner has no submit surface on its own — the code lands out-of-band
|
||||
// (email/SMS, or the dev [VERIFY] server log), so this inline input submits
|
||||
// the received code to POST /api/verify/check. A success escalates the
|
||||
// account to verified_email; the reload clears the banner.
|
||||
let verificationCode = $state('');
|
||||
let isVerifyingCode = $state(false);
|
||||
|
||||
async function submitVerificationCode() {
|
||||
if (isVerifyingCode) return;
|
||||
if (!authStore.currentUser?.email) {
|
||||
toast.error('Unable to verify — no email on file');
|
||||
return;
|
||||
}
|
||||
const code = verificationCode.trim();
|
||||
if (!code) {
|
||||
toast.error('Please enter the verification code');
|
||||
return;
|
||||
}
|
||||
isVerifyingCode = true;
|
||||
const loadingToast = toast.loading('Verifying code...');
|
||||
try {
|
||||
const response = await verifyEmailCode(authStore.currentUser.email, code);
|
||||
if (response.ok) {
|
||||
const data = (await response.json().catch(() => null)) as {
|
||||
message?: unknown;
|
||||
} | null;
|
||||
toast.success(
|
||||
typeof data?.message === 'string' ? data.message : 'Email verified successfully',
|
||||
{ id: loadingToast }
|
||||
);
|
||||
window.location.reload();
|
||||
} else {
|
||||
const errData = await response.text().catch(() => '');
|
||||
toast.error(
|
||||
extractErrorMessage(errData) ||
|
||||
'Verification failed — please check the code and try again',
|
||||
{ id: loadingToast }
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
toast.error('Network error verifying your code — please try again', { id: loadingToast });
|
||||
} finally {
|
||||
isVerifyingCode = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -78,15 +135,37 @@
|
||||
<main class="flex-1 pt-16">
|
||||
{#if authStore.currentUser?.role === 'unverified_email'}
|
||||
<div class="w-full bg-red-600 py-2 pr-8 pl-8 text-center text-sm font-medium text-white">
|
||||
Please verify your email address to continue. Didn't recieve the email? Check your spam
|
||||
folder, or <button
|
||||
type="button"
|
||||
onclick={verify_email}
|
||||
class="cursor-pointer border-0 bg-transparent p-0 text-white underline"
|
||||
>
|
||||
click here
|
||||
</button>
|
||||
to resend it.
|
||||
<p>
|
||||
Please verify your email address to continue. Didn't recieve the email? Check your spam
|
||||
folder, or
|
||||
<button
|
||||
type="button"
|
||||
onclick={verify_email}
|
||||
class="cursor-pointer border-0 bg-transparent p-0 text-white underline"
|
||||
>
|
||||
click here
|
||||
</button>
|
||||
to resend it.
|
||||
</p>
|
||||
<div class="mx-auto mt-2 flex max-w-md items-center justify-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
autocomplete="one-time-code"
|
||||
placeholder="Enter verification code"
|
||||
value={verificationCode}
|
||||
oninput={(e) => (verificationCode = (e.target as HTMLInputElement).value)}
|
||||
class="w-44 rounded-md border border-white/50 bg-white px-3 py-1.5 text-sm text-gray-900 placeholder:text-gray-400 focus:border-white focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onclick={submitVerificationCode}
|
||||
disabled={isVerifyingCode}
|
||||
class="cursor-pointer rounded-md bg-white px-4 py-1.5 text-sm font-semibold text-red-700 transition-colors hover:bg-red-50 disabled:opacity-50"
|
||||
>
|
||||
Verify
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
|
||||
Reference in New Issue
Block a user