fix(web): csp font violation, /pricing 401, residual login emoji
Three bugs caught via devtools on live site:
**1. CSP 'font-src 'self' data:' violation × 3 per landing load.**
BaseLayout was loading Geist + Geist_Mono via next/font/google. In
prod builds Next.js self-hosts those under /_next/static, but the
generated CSS still references `--font-sans: "Geist", …` which some
browsers resolve by re-requesting fonts.gstatic.com. Since we ship
Anthropic Sans/Serif/Mono self-hosted already (/fonts/*.woff2 via
@font-face in globals.css), the Geist dependency was pure overhead.
Removed `next/font/google` imports entirely. Added a `.cm-root`
class on <html> that remaps the Tailwind `--font-sans/--font-mono`
tokens to our `--cm-font-sans/--cm-font-mono` vars — so every
Tailwind `font-sans` / `font-mono` utility now resolves to Anthropic
families. No Google Fonts fetch, no CSP violation.
**2. /pricing 401 on public visit.**
`<Plan>` calls `useCustomer()` → `GET /api/billing/customer` which
needs auth. Unauthed visitor on /pricing → 401 in devtools + wasted
round trip. Gated `useCustomer` on `authClient.useSession()` —
query `enabled: !!session?.user`. Public visitors now skip the fetch
entirely; signed-in users still get their customer record.
**3. Residual "Welcome back! 👋" on /auth/login (both locales).**
Emoji sweep (e91fc80) missed the i18n translation files. Removed 👋
from en/auth.json + es/auth.json login header titles.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -154,3 +154,13 @@
|
|||||||
:root {
|
:root {
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Override the Tailwind default --font-sans / --font-mono CSS vars
|
||||||
|
(which BaseLayout used to populate from next/font/google Geist).
|
||||||
|
We self-host Anthropic Sans/Serif/Mono now — no Google Fonts fetch,
|
||||||
|
no CSP font-src violation. */
|
||||||
|
.cm-root {
|
||||||
|
--font-sans: var(--cm-font-sans);
|
||||||
|
--font-mono: var(--cm-font-mono);
|
||||||
|
--font-serif: var(--cm-font-serif);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import { authClient } from "~/lib/auth/client";
|
||||||
import { billing } from "~/modules/billing/lib/api";
|
import { billing } from "~/modules/billing/lib/api";
|
||||||
|
|
||||||
export const useCustomer = () => useQuery(billing.queries.customer.get);
|
/**
|
||||||
|
* Fetches the current user's billing customer. Gated on session
|
||||||
|
* presence so unauthenticated public pages (landing, /pricing) don't
|
||||||
|
* fire a 401 just to render plan cards.
|
||||||
|
*/
|
||||||
|
export const useCustomer = () => {
|
||||||
|
const { data: session } = authClient.useSession();
|
||||||
|
return useQuery({
|
||||||
|
...billing.queries.customer.get,
|
||||||
|
enabled: !!session?.user,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,22 +1,7 @@
|
|||||||
import { Geist_Mono, Geist } from "next/font/google";
|
|
||||||
|
|
||||||
import { cn } from "@turbostarter/ui";
|
import { cn } from "@turbostarter/ui";
|
||||||
|
|
||||||
import { appConfig } from "~/config/app";
|
import { appConfig } from "~/config/app";
|
||||||
|
|
||||||
const sans = Geist({
|
|
||||||
subsets: ["latin"],
|
|
||||||
display: "swap",
|
|
||||||
variable: "--font-sans",
|
|
||||||
});
|
|
||||||
|
|
||||||
const mono = Geist_Mono({
|
|
||||||
subsets: ["latin"],
|
|
||||||
display: "swap",
|
|
||||||
variable: "--font-mono",
|
|
||||||
weight: ["300", "400", "500"],
|
|
||||||
});
|
|
||||||
|
|
||||||
interface BaseLayoutProps {
|
interface BaseLayoutProps {
|
||||||
readonly locale: string;
|
readonly locale: string;
|
||||||
readonly children: React.ReactNode;
|
readonly children: React.ReactNode;
|
||||||
@@ -24,7 +9,7 @@ interface BaseLayoutProps {
|
|||||||
|
|
||||||
export const BaseLayout = ({ children, locale }: BaseLayoutProps) => {
|
export const BaseLayout = ({ children, locale }: BaseLayoutProps) => {
|
||||||
return (
|
return (
|
||||||
<html lang={locale} className={cn(sans.variable, mono.variable)}>
|
<html lang={locale} className={cn("cm-root")}>
|
||||||
<body
|
<body
|
||||||
suppressHydrationWarning
|
suppressHydrationWarning
|
||||||
className="bg-background text-foreground flex min-h-screen flex-col items-center justify-center font-sans antialiased"
|
className="bg-background text-foreground flex min-h-screen flex-col items-center justify-center font-sans antialiased"
|
||||||
|
|||||||
@@ -224,7 +224,7 @@
|
|||||||
"login": {
|
"login": {
|
||||||
"title": "Login",
|
"title": "Login",
|
||||||
"header": {
|
"header": {
|
||||||
"title": "Welcome back! 👋",
|
"title": "Welcome back",
|
||||||
"description": "Enter your data below to login to your account"
|
"description": "Enter your data below to login to your account"
|
||||||
},
|
},
|
||||||
"magicLink": {
|
"magicLink": {
|
||||||
|
|||||||
@@ -218,7 +218,7 @@
|
|||||||
"login": {
|
"login": {
|
||||||
"title": "Iniciar sesión",
|
"title": "Iniciar sesión",
|
||||||
"header": {
|
"header": {
|
||||||
"title": "¡Bienvenido de nuevo! 👋",
|
"title": "Bienvenido de nuevo",
|
||||||
"description": "Ingresa tus datos abajo"
|
"description": "Ingresa tus datos abajo"
|
||||||
},
|
},
|
||||||
"magicLink": {
|
"magicLink": {
|
||||||
|
|||||||
Reference in New Issue
Block a user