// Sign in / sign up — URL `/login`. A REAL auth form: it POSTs to the api's // `/auth/*` routes (contributed by @voltro/plugin-auth on api-cms), which set // the HttpOnly session cookie. State-changing auth routes are CSRF-protected, so // we fetch a token first. On success we do a FULL navigation so the editor's SSR // gate re-runs with the freshly-set cookie. import type { ReactNode } from 'react' import { useState, type FormEvent } from 'react' import type { PageMeta } from '@voltro/web' import { T, useT } from '@voltro/i18n' import { getCatalog } from '../../locales' export const renderMode = 'static' as const export const meta = ({ locale }: { readonly locale: string }): PageMeta => ({ title: getCatalog(locale)['meta.login.title'], }) type Mode = 'signIn' | 'signUp' async function submitAuth(mode: Mode, email: string, password: string): Promise { const csrf = (await fetch('/auth/csrf').then((r) => r.json())) as { csrfToken: string } const path = mode === 'signUp' ? '/auth/sign-up' : '/auth/sign-in' const res = await fetch(path, { method: 'POST', headers: { 'content-type': 'application/json', 'x-csrf-token': csrf.csrfToken }, body: JSON.stringify({ email, password }), }) return res.ok } export default function Login(): ReactNode { const [mode, setMode] = useState('signIn') const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [pending, setPending] = useState(false) const [failed, setFailed] = useState(false) const emailLabel = useT('auth.email') const passwordLabel = useT('auth.password') const onSubmit = (e: FormEvent): void => { e.preventDefault() setPending(true) setFailed(false) void submitAuth(mode, email, password) .then((ok) => { if (ok) { const from = new URLSearchParams(window.location.search).get('from') ?? '/' window.location.assign(from) } else { setFailed(true) setPending(false) } }) .catch(() => { setFailed(true) setPending(false) }) } return (

{failed ?

: null}

) }