sistema-de-chamados/src/lib/prisma.ts
2025-11-19 15:48:32 -03:00

102 lines
2.7 KiB
TypeScript

import path from "node:path"
import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3"
import { PrismaClient } from "@/generated/prisma/client"
type PrismaClientInstance = InstanceType<typeof PrismaClient>
type TransactionCallback = Extract<
Parameters<PrismaClientInstance["$transaction"]>[0],
(tx: PrismaClientInstance) => Promise<unknown>
>
export type PrismaTransactionClient = TransactionCallback extends (tx: infer Client) => Promise<unknown>
? Client
: never
export type PrismaDelegateClient = PrismaClientInstance
declare global {
var prisma: PrismaClientInstance | undefined
}
// Resolve a robust DATABASE_URL for all runtimes (prod/dev)
const PROJECT_ROOT = process.cwd()
const PRISMA_DIR = path.join(PROJECT_ROOT, "prisma")
function resolveFileUrl(url: string) {
if (!url.startsWith("file:")) {
return url
}
const filePath = url.slice("file:".length)
if (filePath.startsWith("//")) {
return url
}
if (path.isAbsolute(filePath)) {
return `file:${path.normalize(filePath)}`
}
const normalized = path.normalize(filePath)
const prismaPrefix = `prisma${path.sep}`
const relativeToPrisma = normalized.startsWith(prismaPrefix)
? normalized.slice(prismaPrefix.length)
: normalized
const absolutePath = path.resolve(PRISMA_DIR, relativeToPrisma)
if (!absolutePath.startsWith(PROJECT_ROOT)) {
throw new Error(`DATABASE_URL path escapes project directory: ${filePath}`)
}
return `file:${absolutePath}`
}
function normalizeDatasourceUrl(envUrl?: string | null) {
const trimmed = envUrl?.trim()
if (trimmed) {
return resolveFileUrl(trimmed)
}
if (process.env.NODE_ENV === "production") {
return "file:/app/data/db.sqlite"
}
return resolveFileUrl("file:./db.dev.sqlite")
}
const resolvedDatabaseUrl = normalizeDatasourceUrl(process.env.DATABASE_URL)
process.env.DATABASE_URL = resolvedDatabaseUrl
const sqliteAdapter = new PrismaBetterSqlite3({
url: resolvedDatabaseUrl,
})
export const prisma = global.prisma ?? new PrismaClient({ adapter: sqliteAdapter })
if (process.env.NODE_ENV !== "production") {
global.prisma = prisma
}
if (process.env.NODE_ENV !== "production") {
// Helps detect mismatched DB path during dev server bootstrap
console.log("[prisma] Using database:", resolvedDatabaseUrl)
}
export * from "@/generated/prisma/client"
type PrismaKnownErrorLike = {
code?: string
meta?: Record<string, unknown>
message?: string
}
export function asPrismaKnownError(error: unknown): PrismaKnownErrorLike | null {
if (!error || typeof error !== "object") {
return null
}
const candidate = error as PrismaKnownErrorLike
if (typeof candidate.code !== "string") {
return null
}
return candidate
}