import { createServerFn, createServerOnlyFn } from '@tanstack/react-start' import { getRequest } from '@tanstack/react-start/server' import { auth } from '#/lib/auth.ts' // Wrapped in createServerOnlyFn: this file is exported for reuse by several // server/*.ts modules, so (unlike a private, unexported helper) the // framework's client/server code-split can't tree-shake it away on its own // — without this it drags getRequest (and the rest of this file) into any // client bundle that transitively imports one of this module's exports, // which @tanstack/react-start's import-protection then refuses to load. export const getSessionUser = createServerOnlyFn(async () => { const session = await auth.api.getSession({ headers: getRequest().headers }) return session?.user }) export const getSessionCustomerId = createServerOnlyFn( async () => (await getSessionUser())?.id, ) // Defense in depth: an admin-only server function must check this itself // rather than trust the caller's route already gated on role, since a // server function is reachable directly over its RPC endpoint. export const requireAdmin = createServerOnlyFn(async () => { const user = await getSessionUser() if (user?.role !== 'admin') throw new Error('Admin access required') }) // A minimal round trip the admin layout route's loader can call to gate // every /admin/* page behind one check, without fetching any page's data. export const requireAdminSession = createServerFn({ method: 'GET' }).handler( async () => { await requireAdmin() }, ) // The account settings route's loader calls this to both fetch the // signed-in user and gate the page — thrown message is matched against by // the route the same way withAdminGuard matches "Admin access required". export const getCurrentUser = createServerFn({ method: 'GET' }).handler( async () => { const user = await getSessionUser() if (!user) throw new Error('Not signed in') return user }, )