import { logger } from "../../logger"; import { ErrorType, ServerError, ServerResult } from "../types"; import { pbAdmin } from "../../pocketbase/client"; import { getRequest } from "@tanstack/react-start/server"; export const createServerError = ( type: ErrorType, message: string, userMessage: string, statusCode?: number, context?: Record ): ServerError => ({ code: type, message, userMessage, statusCode, context, }); export const toServerResult = async ( serverFn: () => Promise ): Promise> => { const startTime = Date.now(); try { const data = await serverFn(); return { success: true, data }; } catch (error) { if (error && typeof error === 'object' && 'options' in error) { const redirectError = error as any; if (redirectError.options?.to && redirectError.options?.statusCode) { logger.info('toServerResult: Re-throwing TanStack Router redirect', redirectError.options); throw error; } } const duration = Date.now() - startTime; logger.error('Server Fn Error', error); const mappedError = mapKnownError(error); let fnName = 'unknown'; try { const request = getRequest(); const url = new URL(request.url); const functionId = url.searchParams.get('_serverFnId') || url.pathname; if (functionId.includes('--')) { const match = functionId.match(/--([^_]+)_/); fnName = match?.[1] || functionId.split('--')[1]?.split('_')[0] || 'unknown'; } else { fnName = serverFn.name || 'unknown'; } } catch { fnName = serverFn.name || 'unknown'; } try { await pbAdmin.authPromise; await pbAdmin.createActivity({ name: fnName, duration, success: false, error: mappedError.message, arguments: { errorType: mappedError.code, statusCode: mappedError.statusCode, userMessage: mappedError.userMessage, }, }); } catch (activityError) { } return { success: false, error: mappedError }; } }; const mapKnownError = (error: unknown): ServerError => { if (error && typeof error === "object" && "status" in error) { const pbError = error as { status: number; message: string; data?: unknown; }; switch (pbError.status) { case 400: return createServerError( ErrorType.VALIDATION, pbError.message, "Invalid request", 400, { originalError: pbError.data } ); case 401: return createServerError( ErrorType.UNAUTHORIZED, pbError.message, "You are not authorized to perform this action", 401 ); case 403: return createServerError( ErrorType.FORBIDDEN, pbError.message, "Access denied", 403 ); case 404: return createServerError( ErrorType.NOT_FOUND, pbError.message, "The requested resource was not found", 404 ); default: return createServerError( ErrorType.POCKETBASE, pbError.message, "Something went wrong. Please try again.", pbError.status ); } } if (error instanceof TypeError && error.message.includes("fetch")) { return createServerError( ErrorType.NETWORK, error.message, "Network error. Please check your connection and try again." ); } if (error instanceof Error) { return createServerError( ErrorType.UNKNOWN, error.message, "An unexpected error occurred. Please try again.", undefined, { stack: error.stack } ); } return createServerError( ErrorType.UNKNOWN, String(error), "An unexpected error occurred. Please try again.", undefined, { originalError: error } ); };