// server/utils/db.ts - Prisma Client 单例,当前答题 API 不依赖数据库,保留给后续业务使用 import { PrismaMariaDb } from "@prisma/adapter-mariadb"; import { PrismaClient } from "~~/prisma/generated/client"; /** * 将 `DATABASE_URL` 解析为 MariaDB driver 的连接池配置 * * Prisma 7 使用 driver adapter 后,需要显式传入 adapter; * 这里不把连接串原样传给前端或日志,只在服务端初始化连接池 */ const createMariaDbConfig = () => { const databaseUrl = process.env.DATABASE_URL; if (!databaseUrl) { throw new Error("DATABASE_URL is not set."); } const url = new URL(databaseUrl); return { host: url.hostname, port: Number(url.port || 3306), user: decodeURIComponent(url.username), password: decodeURIComponent(url.password), database: decodeURIComponent(url.pathname.slice(1)), connectionLimit: 5, connectTimeout: 15_000, // 正常情况下连接应立即可用;5s 内拿不到连接说明池已耗尽,快速报错优于无限挂起 acquireTimeout: 5_000 }; }; type MariaDbConfig = ReturnType; /** * 创建 Prisma Client * * 连接池超时设置比默认值更宽松,是因为当前数据库是远程 MySQL, * 默认超时时间过短时 Node driver 可能还没建好 socket 就失败 */ const prismaClientSingleton = (config: MariaDbConfig) => { const adapter = new PrismaMariaDb(config); return new PrismaClient({ adapter }); }; type PrismaClientSingleton = ReturnType; /** * 开发环境热更新会反复加载模块 * * 把 Prisma Client 挂到 globalThis 上,可以避免每次 HMR 都新建连接池 */ const globalForPrisma = globalThis as unknown as { prisma: PrismaClientSingleton | undefined; prismaConfigSignature: string | undefined; }; const mariaDbConfig = createMariaDbConfig(); const prismaConfigSignature = JSON.stringify(mariaDbConfig); /** 全项目唯一 Prisma Client 实例 */ export const prisma = globalForPrisma.prisma && globalForPrisma.prismaConfigSignature === prismaConfigSignature ? globalForPrisma.prisma : prismaClientSingleton(mariaDbConfig); // 生产环境由进程生命周期管理;开发环境缓存到全局,减少 HMR 连接泄漏 if (process.env.NODE_ENV !== "production") { globalForPrisma.prisma = prisma; globalForPrisma.prismaConfigSignature = prismaConfigSignature; }