Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
3.0 kB ยท 93 lines
TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394import { Code, ConnectError, type Interceptor } from "@connectrpc/connect";import { getLogger } from "@logtape/logtape";import type { ErrorCode } from "@openstatus/error";
import { OpenStatusApiError } from "@/libs/errors";
import { RPC_CONTEXT_KEY } from "./auth";
const logger = getLogger("api-server");
/** * Mapping from OpenStatus error codes to ConnectRPC codes. */const ERROR_CODE_MAP: Record<ErrorCode, Code> = { BAD_REQUEST: Code.InvalidArgument, UNAUTHORIZED: Code.Unauthenticated, PAYMENT_REQUIRED: Code.ResourceExhausted, FORBIDDEN: Code.PermissionDenied, NOT_FOUND: Code.NotFound, METHOD_NOT_ALLOWED: Code.Unimplemented, CONFLICT: Code.AlreadyExists, UNPROCESSABLE_ENTITY: Code.InvalidArgument, TOO_MANY_REQUESTS: Code.ResourceExhausted, INTERNAL_SERVER_ERROR: Code.Internal, SERVICE_UNAVAILABLE: Code.Unavailable,};
/** * Opaque `Internal` error for anything we didn't classify. The request id is * the only detail that crosses the wire โ it's the handle support needs to * find the real cause in the logs. */export function internalError(requestId?: string): ConnectError { return new ConnectError( requestId ? `Internal server error (request id: ${requestId})` : "Internal server error", Code.Internal, );}
/** * Error mapping interceptor for ConnectRPC. * Converts OpenStatusApiError to ConnectError with appropriate codes. * Logs server errors and passes through client errors. */export function errorInterceptor(): Interceptor { return (next) => async (req) => { try { return await next(req); } catch (error) { const rpcCtx = req.contextValues.get(RPC_CONTEXT_KEY);
// Already a ConnectError, pass through if (error instanceof ConnectError) { throw error; }
// Map OpenStatusApiError to ConnectError if (error instanceof OpenStatusApiError) { const code = ERROR_CODE_MAP[error.code] ?? Code.Internal;
// Log server errors (5xx equivalent) if (error.status >= 500) { logger.error("RPC server error", { error: { code: error.code, message: error.message, }, requestId: rpcCtx?.requestId, }); }
throw new ConnectError(error.message, code); }
// Unknown error - log and wrap as Internal logger.error("RPC unexpected error", { error: { name: error instanceof Error ? error.name : "Unknown", message: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, }, requestId: rpcCtx?.requestId, });
// Never forward the raw message: drizzle's `DrizzleQueryError` embeds // the full SQL and bound params, which would leak schema and other // rows' ids to the API client. throw internalError(rpcCtx?.requestId); } };}