Redesign marketing pages and isolate public routes from app auth.

Give the landing, legal, and sign-in flows a consistent product shell while keeping marketing pages free of tRPC/session calls, fixing dev auth URL handling, and refreshing env and deploy docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 02:00:06 -04:00
co-authored by Cursor
parent 82977b6dd8
commit 6ec26a4a0d
36 changed files with 1743 additions and 819 deletions
+11 -4
View File
@@ -3,10 +3,17 @@
import { createAuthClient } from "better-auth/react";
import { genericOAuthClient } from "better-auth/client/plugins";
/**
* Auth client configuration
*/
function resolveAuthBaseUrl(): string | undefined {
// Always use the current origin in the browser so dev works on any port
// (e.g. 3002 when 3000 is taken), without rebuilding for NEXT_PUBLIC_APP_URL.
if (typeof window !== "undefined") {
return window.location.origin;
}
return process.env.NEXT_PUBLIC_APP_URL;
}
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL,
baseURL: resolveAuthBaseUrl(),
plugins: [genericOAuthClient()],
});
+27
View File
@@ -0,0 +1,27 @@
import { headers as nextHeaders } from "next/headers";
import { auth } from "~/lib/auth";
export function hasSessionCookie(headers: Headers): boolean {
const cookie = headers.get("cookie") ?? "";
return (
cookie.includes("better-auth.session_token=") ||
cookie.includes("__Secure-better-auth.session_token=")
);
}
export async function getOptionalServerSession(headers: Headers) {
if (!hasSessionCookie(headers)) {
return null;
}
try {
return await auth.api.getSession({ headers });
} catch (error) {
console.error("[auth] Failed to resolve session:", error);
return null;
}
}
export async function getOptionalServerSessionFromHeaders() {
return getOptionalServerSession(await nextHeaders());
}
+21
View File
@@ -0,0 +1,21 @@
/** Routes that do not require authentication or server session lookups. */
export const PUBLIC_ROUTES = [
"/",
"/auth/signin",
"/auth/register",
"/auth/forgot-password",
"/auth/reset-password",
"/privacy",
"/terms",
] as const;
/** Path prefixes treated as public (e.g. shareable invoice links). */
export const PUBLIC_ROUTE_PREFIXES = ["/i/"] as const;
export function isPublicRoute(pathname: string): boolean {
if ((PUBLIC_ROUTES as readonly string[]).includes(pathname)) {
return true;
}
return PUBLIC_ROUTE_PREFIXES.some((prefix) => pathname.startsWith(prefix));
}