Openstatus www.openstatus.dev
at main 76 lines 2.3 kB view raw
1import { Code, ConnectError, type Interceptor } from "@connectrpc/connect"; 2import { getLogger } from "@logtape/logtape"; 3import type { ErrorCode } from "@openstatus/error"; 4 5import { OpenStatusApiError } from "@/libs/errors"; 6import { RPC_CONTEXT_KEY } from "./auth"; 7 8const logger = getLogger("api-server"); 9 10/** 11 * Mapping from OpenStatus error codes to ConnectRPC codes. 12 */ 13const ERROR_CODE_MAP: Record<ErrorCode, Code> = { 14 BAD_REQUEST: Code.InvalidArgument, 15 UNAUTHORIZED: Code.Unauthenticated, 16 PAYMENT_REQUIRED: Code.ResourceExhausted, 17 FORBIDDEN: Code.PermissionDenied, 18 NOT_FOUND: Code.NotFound, 19 METHOD_NOT_ALLOWED: Code.Unimplemented, 20 CONFLICT: Code.AlreadyExists, 21 UNPROCESSABLE_ENTITY: Code.InvalidArgument, 22 INTERNAL_SERVER_ERROR: Code.Internal, 23}; 24 25/** 26 * Error mapping interceptor for ConnectRPC. 27 * Converts OpenStatusApiError to ConnectError with appropriate codes. 28 * Logs server errors and passes through client errors. 29 */ 30export function errorInterceptor(): Interceptor { 31 return (next) => async (req) => { 32 try { 33 return await next(req); 34 } catch (error) { 35 const rpcCtx = req.contextValues.get(RPC_CONTEXT_KEY); 36 37 // Already a ConnectError, pass through 38 if (error instanceof ConnectError) { 39 throw error; 40 } 41 42 // Map OpenStatusApiError to ConnectError 43 if (error instanceof OpenStatusApiError) { 44 const code = ERROR_CODE_MAP[error.code] ?? Code.Internal; 45 46 // Log server errors (5xx equivalent) 47 if (error.status >= 500) { 48 logger.error("RPC server error", { 49 error: { 50 code: error.code, 51 message: error.message, 52 }, 53 requestId: rpcCtx?.requestId, 54 }); 55 } 56 57 throw new ConnectError(error.message, code); 58 } 59 60 // Unknown error - log and wrap as Internal 61 logger.error("RPC unexpected error", { 62 error: { 63 name: error instanceof Error ? error.name : "Unknown", 64 message: error instanceof Error ? error.message : String(error), 65 stack: error instanceof Error ? error.stack : undefined, 66 }, 67 requestId: rpcCtx?.requestId, 68 }); 69 70 throw new ConnectError( 71 error instanceof Error ? error.message : "Internal server error", 72 Code.Internal, 73 ); 74 } 75 }; 76}