chore: sync in-progress work across marketing, admin, API and tests

Snapshot of uncommitted work that had accumulated in the tree alongside
the Turnstile changes:

- marketing pages, SEO helpers (lib/seo.ts, lib/marketing/) and
  structured data
- admin billing actions and a per-user portfolio view, plus an admin
  error boundary
- rate limiting (lib/rate-limit.ts) applied across the /api/v1 surface
- CSP and proxy adjustments, accounting/webhook lib updates
- Playwright config and an e2e/unit test suite
- next bumped to ^16.3.4 with the lockfile regenerated
- generated AGENTS.md / CLAUDE.md

Authored by other sessions working in this tree; committed here so the
Turnstile work could be pushed without leaving the tree dirty.
Typecheck passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leon Serfaty
2026-09-05 16:27:16 -04:00
co-authored by Claude Opus 5
parent 8f90347659
commit 1d02598786
71 changed files with 3641 additions and 819 deletions
+13 -4
View File
@@ -35,12 +35,21 @@ const ADMIN_EMAILS = (process.env.ADMIN_EMAILS ?? "")
.filter(Boolean)
export function isAdminUser(
u: { id?: string; email?: string; role?: string | null } | null | undefined
u:
| { id?: string; email?: string; emailVerified?: boolean; role?: string | null }
| null
| undefined
): boolean {
if (!u) return false
if (u.role === "admin") return true
// ADMIN_USER_IDS is the unconditional bootstrap path: an id can only come from
// a row we created, so it is not attacker-selectable.
if (u.id && ADMIN_USER_IDS.includes(u.id)) return true
if (u.email && ADMIN_EMAILS.includes(u.email.toLowerCase())) return true
// ADMIN_EMAILS is matched on a user-supplied string, so it additionally
// requires a VERIFIED address. Otherwise, in any environment where
// REQUIRE_EMAIL_VERIFICATION is off, simply signing up with a listed address
// would grant admin without ever controlling the mailbox.
if (u.email && u.emailVerified && ADMIN_EMAILS.includes(u.email.toLowerCase())) return true
return false
}
@@ -52,7 +61,7 @@ export function isAdminUser(
export async function getAdminSession() {
const session = await auth.api.getSession({ headers: await headers() })
const user = session?.user ?? null
if (!isAdminUser(user as { role?: string | null })) return null
if (!isAdminUser(user)) return null
const profile = await db.query.profiles.findFirst({ where: eq(profiles.id, user!.id) })
return { user: user!, profile: profile ?? null }
}
@@ -65,7 +74,7 @@ export async function requireAdmin() {
const session = await auth.api.getSession({ headers: await headers() })
const user = session?.user ?? null
if (!user) redirect("/login")
if (!isAdminUser(user as { role?: string | null })) redirect("/dashboard")
if (!isAdminUser(user)) redirect("/dashboard")
const profile = await db.query.profiles.findFirst({ where: eq(profiles.id, user.id) })
return { user, profile: profile ?? null }
}