Add email verification, personal calendar feed, and full SEO implementation

- Customer accounts now require email verification (hashed, single-use,
  time-limited tokens) before they can request/confirm bookings, with
  resend flows on login/account/booking widget and rate limiting.
- Admins get a private, rotatable iCalendar (ICS) subscription feed of
  their confirmed bookings and public events, timezone-correct for
  Europe/Berlin including DST, never exposing meeting passwords.
- Adds a full SEO layer: per-page canonical/OG/Twitter metadata with
  CMS-editable overrides and content-derived fallbacks, a dynamic
  sitemap.xml and robots.txt driven by real published content, JSON-LD
  (Organization/LocalBusiness, WebSite, WebPage, BreadcrumbList, Service,
  Event, BlogPosting) that never fabricates data, and a CMS-managed
  redirect table for changed slugs.
- Global ANOUMA-naming audit: the brand name is never used to label
  personal account/calendar areas anywhere in the app, CMS, or emails.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 22:57:31 +02:00
co-authored by Claude Sonnet 5
parent 50c39a70e0
commit 1cd15aff25
72 changed files with 2835 additions and 257 deletions
@@ -0,0 +1,54 @@
import type { Metadata } from "next";
import Link from "next/link";
import { PageHeader } from "@/components/PageHeader";
import { Section } from "@/components/Section";
import { ResendVerificationButton } from "@/components/auth/ResendVerificationButton";
import { verifyEmailToken } from "@/lib/auth/verifyEmailToken";
export const dynamic = "force-dynamic";
export const metadata: Metadata = { title: "E-Mail bestätigen", robots: { index: false, follow: false } };
type Args = { params: Promise<{ token: string }> };
export default async function VerifyEmailPage({ params }: Args) {
const { token } = await params;
const verified = await verifyEmailToken(token);
return (
<>
<PageHeader
eyebrow="Konto"
title={verified ? "E-Mail-Adresse bestätigt" : "Bestätigung fehlgeschlagen"}
crumbs={[{ title: "E-Mail bestätigen" }]}
/>
<Section tone="plain">
<div className="mx-auto max-w-md text-center">
{verified ? (
<>
<p className="text-base leading-relaxed text-anouma-plum">Dein Konto ist jetzt aktiviert.</p>
<Link
href="/konto"
className="mt-6 inline-flex rounded-full bg-anouma-mauve-dark px-7 py-3.5 text-sm font-medium text-white transition-colors hover:bg-anouma-plum"
>
Zum Konto
</Link>
</>
) : (
<>
<p className="text-base leading-relaxed text-anouma-plum">
Dieser Verifizierungslink ist ungültig oder abgelaufen.
</p>
<div className="mt-6 flex flex-col items-center gap-4">
<ResendVerificationButton variant="email-prompt" triggerLabel="Neuen Link anfordern" />
<Link href="/login" className="text-sm font-medium text-anouma-mauve-dark underline underline-offset-4">
Zur Anmeldung
</Link>
</div>
</>
)}
</div>
</Section>
</>
);
}