import { NextRequest, NextResponse } from "next/server"; import { Prisma } from "@prisma/client"; import { prisma } from "@/lib/prisma"; export class ApiError extends Error { constructor( message: string, public status: number = 400, ) { super(message); this.name = "ApiError"; } } /** * Validates that value is a non-empty string; throws 401 otherwise. * Used for room routes where anonymous users pass userId in body. * For registered-user routes, prefer getAuthUserId() from lib/auth. */ export function requireUserId(value: unknown): string { if (!value || typeof value !== "string") { throw new ApiError("请先登录", 401); } return value; } /** Finds user by ID; throws 404 if not found. */ export async function requireUser(userId: string) { const user = await prisma.user.findUnique({ where: { id: userId } }); if (!user) throw new ApiError("用户不存在", 404); return user; } type RouteContext = { params: Promise> }; type RouteHandler = ( req: NextRequest, ctx: RouteContext, ) => Promise; /** * Wraps a Next.js route handler with unified error handling. * - ApiError instances are converted to JSON responses with matching status codes * - Unknown errors are logged and returned as 500 */ export function apiHandler(handler: RouteHandler): RouteHandler { return async (req, ctx) => { try { return await handler(req, ctx); } catch (e) { if (e instanceof ApiError) { return NextResponse.json({ error: e.message }, { status: e.status }); } if ( e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2002" ) { return NextResponse.json( { error: "该记录已存在或值已被占用" }, { status: 409 }, ); } console.error(`[API ${req.method} ${req.nextUrl.pathname}]`, e); return NextResponse.json( { error: "操作失败,请稍后重试" }, { status: 500 }, ); } }; }