From d8b3a18d658b65908ed9b0411fa98846e39c2fe5 Mon Sep 17 00:00:00 2001 From: RainbowBird Date: Tue, 3 Mar 2026 18:23:41 +0800 Subject: [PATCH] refactor(server): add more error utils --- apps/server/package.json | 2 +- apps/server/src/routes/stripe.ts | 10 +++++----- apps/server/src/routes/v1completions.ts | 3 ++- apps/server/src/services/flux.ts | 4 +++- apps/server/src/utils/error.ts | 14 ++++++++++++++ 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index ce0176367..aed056e7a 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -40,7 +40,7 @@ "injeca": "catalog:", "pg": "^8.20.0", "postgres": "^3.4.8", - "stripe": "catalog:", + "stripe": "^20.3.0", "tsx": "^4.21.0", "valibot": "catalog:" }, diff --git a/apps/server/src/routes/stripe.ts b/apps/server/src/routes/stripe.ts index 92b9ac1a3..ec01c230e 100644 --- a/apps/server/src/routes/stripe.ts +++ b/apps/server/src/routes/stripe.ts @@ -8,7 +8,7 @@ import { Hono } from 'hono' import { integer, minValue, number, object, pipe, safeParse } from 'valibot' import { authGuard } from '../middlewares/auth' -import { ApiError, createBadRequestError } from '../utils/error' +import { createBadRequestError, createServiceUnavailableError } from '../utils/error' const CheckoutBodySchema = object({ amount: pipe(number(), integer(), minValue(1)), @@ -20,7 +20,7 @@ export function createStripeRoutes(fluxService: FluxService, env: Env) { return new Hono() .post('/checkout', authGuard, async (c) => { if (!stripe) - throw new ApiError(503, 'STRIPE_NOT_CONFIGURED', 'Stripe is not configured') + throw createServiceUnavailableError('Stripe is not configured', 'STRIPE_NOT_CONFIGURED') const user = c.get('user')! const body = await c.req.json() @@ -58,11 +58,11 @@ export function createStripeRoutes(fluxService: FluxService, env: Env) { }) .post('/webhook', async (c) => { if (!stripe || !env.STRIPE_WEBHOOK_SECRET) - throw new ApiError(503, 'STRIPE_NOT_CONFIGURED', 'Stripe is not configured') + throw createServiceUnavailableError('Stripe is not configured', 'STRIPE_NOT_CONFIGURED') const sig = c.req.header('stripe-signature') if (!sig) - return c.json({ error: 'No signature' }, 400) + throw createBadRequestError('No signature', 'MISSING_SIGNATURE') let event: Stripe.Event try { @@ -71,7 +71,7 @@ export function createStripeRoutes(fluxService: FluxService, env: Env) { } catch (err: unknown) { const message = err instanceof Error ? err.message : 'Unknown error' - return c.json({ error: `Webhook Error: ${message}` }, 400) + throw createBadRequestError(`Webhook Error: ${message}`, 'WEBHOOK_ERROR') } if (event.type === 'checkout.session.completed') { diff --git a/apps/server/src/routes/v1completions.ts b/apps/server/src/routes/v1completions.ts index 1cfec9c5a..cfdb7af85 100644 --- a/apps/server/src/routes/v1completions.ts +++ b/apps/server/src/routes/v1completions.ts @@ -7,6 +7,7 @@ import type { HonoEnv } from '../types/hono' import { Hono } from 'hono' import { authGuard } from '../middlewares/auth' +import { createPaymentRequiredError } from '../utils/error' // Only forward these headers from the upstream LLM response const SAFE_RESPONSE_HEADERS = new Set([ @@ -21,7 +22,7 @@ export function createV1CompletionsRoutes(fluxService: FluxService, env: Env) { const user = c.get('user')! const flux = await fluxService.getFlux(user.id) if (flux.flux <= 0) { - return c.json({ error: 'Insufficient flux' }, 402) + throw createPaymentRequiredError('Insufficient flux') } const body = await c.req.json() diff --git a/apps/server/src/services/flux.ts b/apps/server/src/services/flux.ts index dff952a3c..7401ed6c6 100644 --- a/apps/server/src/services/flux.ts +++ b/apps/server/src/services/flux.ts @@ -3,6 +3,8 @@ import type { Database } from './db' import { and, eq, gte, sql } from 'drizzle-orm' +import { createPaymentRequiredError } from '../utils/error' + import * as schema from '../schemas/flux' export function createFluxService(db: Database) { @@ -39,7 +41,7 @@ export function createFluxService(db: Database) { .returning() if (result.length === 0) { - throw new Error('Insufficient flux') + throw createPaymentRequiredError('Insufficient flux') } return result[0] diff --git a/apps/server/src/utils/error.ts b/apps/server/src/utils/error.ts index 92f168440..de4da38dd 100644 --- a/apps/server/src/utils/error.ts +++ b/apps/server/src/utils/error.ts @@ -47,9 +47,23 @@ export function createNotFoundError(message = 'Not Found', details?: unknown) { return new ApiError(404, 'NOT_FOUND', message, details) } +/** + * Creates a payment required error (402) + */ +export function createPaymentRequiredError(message: string, details?: unknown) { + return new ApiError(402, 'PAYMENT_REQUIRED', message, details) +} + /** * Creates a conflict error (409) */ export function createConflictError(message: string, details?: unknown) { return new ApiError(409, 'CONFLICT', message, details) } + +/** + * Creates a service unavailable error (503) + */ +export function createServiceUnavailableError(message = 'Service Unavailable', errorCode = 'SERVICE_UNAVAILABLE', details?: unknown) { + return new ApiError(503, errorCode, message, details) +}