import { createMiddleware, createServerFn, createServerOnlyFn, } from "@tanstack/react-start"; import { getRequest } from "@tanstack/react-start/server"; import { Logger } from "@/lib/logger"; import { isAdminRole } from "@/features/core/utils/roles"; import z from "zod"; const logger = new Logger("Middleware"); export const PUBLIC_ROUTES = ["/login", "/logout", "/refresh-session"]; export const getSessionContext = createServerOnlyFn( async (request: Request, options?: { isServerFunction?: boolean }) => { const { getSessionContextCore } = await import("./supertokens-core.server"); return getSessionContextCore(request, options); } ); export const superTokensRequestMiddleware = createMiddleware({ type: "request", }).server(async ({ next, request }) => { const context = await getSessionContext(request); return next({ context }); }); export const superTokensOptionalRequestMiddleware = createMiddleware({ type: "request", }).server(async ({ next, request }) => { type SessionContext = Awaited>; let context: SessionContext; try { context = await getSessionContext(request); } catch { context = { userAuthId: null, roles: [], metadata: undefined, phone: undefined, player: null, } as unknown as SessionContext; } return next({ context }); }); export const superTokensFunctionMiddleware = createMiddleware({ type: "function", }).server(async ({ next }) => { const request = getRequest(); try { const context = await getSessionContext(request, { isServerFunction: true }); return next({ context }); } catch (error: any) { if (error.message === "SESSION_REFRESH_REQUIRED") { throw new Response( JSON.stringify({ error: "SESSION_REFRESH_REQUIRED", message: "Session needs to be refreshed" }), { status: 401, headers: { "Content-Type": "application/json" } } ); } throw error; } }); export const superTokensAdminFunctionMiddleware = createMiddleware({ type: "function", }).server(async ({ next, serverFnMeta }) => { const request = getRequest(); try { const context = await getSessionContext(request, { isServerFunction: true }); if (isAdminRole(context.roles)) { return next({ context }); } logger.error("Unauthorized user in admin function.", { userAuthId: context.userAuthId, roles: context.roles, }); const { recordDeniedServerFn } = await import("./activities"); recordDeniedServerFn(request, context.metadata, serverFnMeta); throw new Error("Unauthorized"); } catch (error: any) { if (error.message === "SESSION_REFRESH_REQUIRED") { throw new Response( JSON.stringify({ error: "SESSION_REFRESH_REQUIRED", message: "Session needs to be refreshed" }), { status: 401, headers: { "Content-Type": "application/json" } } ); } throw error; } }); export const setUserMetadata = createServerFn({ method: "POST" }) .validator( z .object({ first_name: z .string() .min(2) .max(20) .regex( /^[a-zA-Z0-9\s]+$/, "First name must be 2-20 characters long and contain only letters and spaces" ), last_name: z .string() .min(2) .max(20) .regex( /^[a-zA-Z0-9\s]+$/, "Last name must be 2-20 characters long and contain only letters and spaces" ), player_id: z.string(), }) .partial() ) .middleware([superTokensFunctionMiddleware]) .handler(async ({ context, data }) => { const { userAuthId, metadata } = context; if (!userAuthId) return; const { updateUserMetadataFields } = await import( "./supertokens-core.server" ); await updateUserMetadataFields(userAuthId, { first_name: data.first_name, last_name: data.last_name, player_id: data.player_id, }); return { metadata: { ...metadata, ...data, }, }; });