Files
Epicnabbo-Catalogus-Updated…/src/app/forgot/page.tsx
T
Simo e668fa85ec Add 2FA, email + password reset, and batch-7 pages
Auth (hand-built on the auth core):
- 2FA: User model gains two_factor_secret/recovery_codes/confirmed_at (+ idempotent
  MariaDB migration). authorize() requires a valid TOTP code when 2FA is confirmed
  (secret decrypted via Laravel APP_KEY, fail-closed). Two-step login (precheckLogin
  reveals the code field). /settings/2fa enable/confirm/disable flow.
- Password reset: nodemailer email service; PasswordReset model + migration;
  /forgot (request, generic response) + /reset (token sha256 + 1h TTL, sets argon2id
  hash). Login links to forgot.

Batch 7 (parallel agents): /admin/commandocentrum (RCON controls + emulator_errors),
social write actions (friend request + guild forum new thread), /help/[category],
/badges (public). env: APP_KEY, APP_URL, SMTP_*. Nav extended.

Verified: tsc exit 0, vitest 48/48, next build exit 0 (64 page routes).
2026-06-28 14:25:19 +02:00

34 lines
1.2 KiB
TypeScript

import Link from "next/link";
import { requestReset } from "@/actions/password-reset";
export default async function ForgotPage({
searchParams,
}: {
searchParams: Promise<{ sent?: string }>;
}) {
const { sent } = await searchParams;
return (
<main style={{ maxWidth: 400, margin: "2rem auto" }}>
<div className="card" style={{ padding: "1.75rem" }}>
<h1 style={{ marginTop: 0, textAlign: "center" }}>Reset password</h1>
{sent ? (
<p className="muted" style={{ textAlign: "center" }}>
If that email is registered, a reset link has been sent. Check your inbox.
</p>
) : (
<form action={requestReset} style={{ display: "grid", gap: "0.7rem", marginTop: "1rem" }}>
<input name="email" type="email" placeholder="Your email" autoComplete="email" required />
<button type="submit" className="btn btn-primary">
Send reset link
</button>
</form>
)}
<p className="muted" style={{ textAlign: "center", marginBottom: 0, marginTop: "1rem" }}>
<Link href="/login">Back to login</Link>
</p>
</div>
</main>
);
}