c8fa2e8b52
Deploy to route.crispygoat.com / deploy (push) Successful in 4m33s
- Remove Google Fonts @import from 4 components (SiteHeader, LandingPageWrapper, TestimonialsAndCTA, FeaturesAndStats) — eliminate render-blocking external request and font flash - Replace all unloaded Cormorant Garamond / Playfair Display / DM Sans / Plus Jakarta Sans references with the existing next/font variables (Fraunces, Manrope, Fragment_Mono) — visual coherence with the design system - Update 9 pages (blog, brands, changelog, maintenance, protected-example, roadmap, security, waitlist, WaitlistForm) to use the loaded Fraunces - Fix dead --font-geist / --font-jetbrains-mono references in admin-design-system.css (now point to --font-manrope / --font-fragment-mono which are actually loaded) - Add atelier-pill, atelier-numerals, atelier-fineprint, atelier-canvas-soft utility classes; .atelier-input:focus-visible refined ring - Add .ha-field-textarea, .ha-scroll, .ha-skeleton to admin design system - Extend prefers-reduced-motion guard to atelier-enter/stagger/shimmer - Apply atelier-fineprint to login/not-found/error pages for consistency - No structural changes; build passes, 0 type errors
122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
import { getSession, signOut } from "@/lib/auth";
|
|
import { redirect } from "next/navigation";
|
|
|
|
// `getSession()` reads cookies(), which forces dynamic rendering.
|
|
// Without this, Next.js tries to prerender this page at build time
|
|
// and fails with "Dynamic server usage".
|
|
export const dynamic = "force-dynamic";
|
|
|
|
/**
|
|
* /protected-example
|
|
*
|
|
* Smoke-test page that demonstrates the Neon Auth pattern. Calling
|
|
* `getSession()` server-side returns the current session (null if not signed
|
|
* in). The middleware in `../middleware.ts` already redirects
|
|
* unauthenticated visitors to `/login`, so by the time this page renders
|
|
* we always have a session.
|
|
*
|
|
* The page shows:
|
|
* • The user's name, email, and provider
|
|
* • A "Sign out" form action that calls `signOut()` from Neon Auth
|
|
*/
|
|
export default async function ProtectedExamplePage() {
|
|
const { data: session } = await getSession();
|
|
|
|
// Defensive: middleware should have already redirected. Render a
|
|
// friendly hint if we ever reach here unauthenticated.
|
|
if (!session?.user) {
|
|
return (
|
|
<main className="min-h-screen flex items-center justify-center bg-stone-50 px-6">
|
|
<div className="max-w-md rounded-2xl bg-white p-8 shadow ring-1 ring-stone-200">
|
|
<h1 className="text-xl font-semibold text-stone-900">
|
|
Not signed in
|
|
</h1>
|
|
<p className="mt-2 text-sm text-stone-600">
|
|
You should have been redirected to{" "}
|
|
<a className="text-emerald-700 underline" href="/login">
|
|
/login
|
|
</a>
|
|
. If you can see this, the middleware matcher needs adjusting.
|
|
</p>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
const user = session.user;
|
|
|
|
return (
|
|
<main className="min-h-screen flex items-center justify-center bg-stone-50 px-6 py-12">
|
|
<div className="w-full max-w-xl space-y-6">
|
|
<header>
|
|
<h1
|
|
className="text-3xl font-semibold tracking-tight text-stone-900"
|
|
style={{ fontFamily: "var(--font-fraunces)" }}
|
|
>
|
|
Protected example
|
|
</h1>
|
|
<p className="mt-1 text-sm text-stone-500">
|
|
You are signed in. This page is guarded by the Neon Auth
|
|
middleware in <code className="text-xs">middleware.ts</code>.
|
|
</p>
|
|
</header>
|
|
|
|
<section className="rounded-2xl bg-white p-6 shadow ring-1 ring-stone-200">
|
|
<h2 className="text-sm font-semibold uppercase tracking-wider text-stone-500">
|
|
Session
|
|
</h2>
|
|
<dl className="mt-4 grid grid-cols-1 gap-4 text-sm sm:grid-cols-2">
|
|
<div>
|
|
<dt className="text-stone-500">Name</dt>
|
|
<dd className="mt-1 font-medium text-stone-900">
|
|
{user.name ?? "(none)"}
|
|
</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-stone-500">Email</dt>
|
|
<dd className="mt-1 font-medium text-stone-900 break-all">
|
|
{user.email ?? "(none)"}
|
|
</dd>
|
|
</div>
|
|
<div>
|
|
<dt className="text-stone-500">User id</dt>
|
|
<dd className="mt-1 font-mono text-xs text-stone-700 break-all">
|
|
{user.id ?? "(none)"}
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</section>
|
|
|
|
<section className="rounded-2xl bg-white p-6 shadow ring-1 ring-stone-200">
|
|
<h2 className="text-sm font-semibold uppercase tracking-wider text-stone-500">
|
|
Try it
|
|
</h2>
|
|
<p className="mt-2 text-sm text-stone-600">
|
|
Use the form below to sign out, or navigate to{" "}
|
|
<a className="text-emerald-700 underline" href="/admin">
|
|
/admin
|
|
</a>{" "}
|
|
(the same session is shared).
|
|
</p>
|
|
|
|
<form
|
|
action={async () => {
|
|
"use server";
|
|
await signOut();
|
|
redirect("/login");
|
|
}}
|
|
className="mt-4"
|
|
>
|
|
<button
|
|
type="submit"
|
|
className="rounded-xl bg-stone-900 px-5 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-stone-700"
|
|
>
|
|
Sign out
|
|
</button>
|
|
</form>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|