import { asc, eq } from "drizzle-orm"; import { CheckCircle2, Clock, MailX } from "lucide-react"; import type { Metadata } from "next"; import { getTranslations } from "next-intl/server"; import { ResendVerificationForm } from "@/components/auth/resend-verification-form"; import Link from "@/components/link"; import { SurfaceCard } from "@/components/surface-card"; import { isValidVerificationToken } from "@/lib/auth/email-verification"; import { db, User } from "@/lib/db"; export async function generateMetadata(): Promise { const t = await getTranslations("pages.verify"); return { title: t("verifiedTitle"), description: t("invalidSubtitle"), // Token links are single-use; the page itself has nothing to index. robots: { index: false, follow: false }, }; } type Status = "verified" | "already" | "invalid" | "unavailable"; function StatusCard({ icon, title, subtitle, children, color, }: { icon: React.ReactNode; title: string; subtitle: string; children: React.ReactNode; color: string; }) { const tintMap: Record = { green: "#16a34a", yellow: "#d97706", red: "#dc2626", blue: "#2563eb", }; const tint = tintMap[color] ?? tintMap.blue; return (
{icon}

{title}

{icon}

{subtitle}

{children}
); } export default async function VerifyPage({ searchParams, }: { searchParams: Promise<{ token?: string; email?: string; }>; }) { const t = await getTranslations("pages.verify"); const { token = "", email = "" } = await searchParams; const normalisedEmail = email.trim().toLowerCase(); let status: Status = "invalid"; if (normalisedEmail && token) { const ok = await isValidVerificationToken(normalisedEmail, token); if (ok) { try { // Legacy databases allow duplicate addresses; always resolve the // oldest account so the link cannot verify a different one. const matches = await db .select({ id: User.id, mailVerified: User.mailVerified }) .from(User) .where(eq(User.mail, normalisedEmail)) .orderBy(asc(User.id)); const user = matches[0]; if (!user) { status = "invalid"; } else if (user.mailVerified === "1") { status = "already"; } else { await db .update(User) .set({ mailVerified: "1" }) .where(eq(User.id, user.id)); status = "verified"; } } catch { status = "unavailable"; } } } const linkClass = "btn-shine inline-flex items-center gap-2 font-extrabold text-sm px-6 py-3 rounded-xl transition-all duration-200 hover:scale-[1.02] active:scale-95 shadow-lg"; const linkStyle = { background: "var(--color-primary)", color: "var(--color-primary-foreground)", boxShadow: "0 4px 20px color-mix(in srgb, var(--color-primary) 30%, transparent)", }; return ( <> {status === "verified" && ( } title={t("verifiedTitle")} subtitle={t("verifiedSubtitle")} color="green" >

{t("verifiedBody")}

{t("goToLogin")}
)} {status === "already" && ( } title={t("alreadyTitle")} subtitle={t("alreadySubtitle")} color="yellow" >

{t("alreadyBody")}

{t("goToLogin")}
)} {status === "unavailable" && ( } title={t("unavailableTitle")} subtitle={t("unavailableSubtitle")} color="blue" >

{t("unavailableBody")}

{/* Transient failure: offer both the retry path and the way out instead of leaving the visitor stranded on this card. */} {t("backToLogin")}
)} {status === "invalid" && ( } title={t("invalidTitle")} subtitle={t("invalidSubtitle")} color="red" >

{t("invalidBody")}

{t("backToLogin")}
)} ); }