Rewritten to use drizzle instead of prisma
commit c0894548dac5133bd89da5b68684443748fa2559 Author: fuomag9 <1580624+fuomag9@users.noreply.github.com> Date: Fri Nov 7 18:38:30 2025 +0100 Update config.ts commit 5a4f1159d2123ada0f698a10011c24720bf6ea6f Author: fuomag9 <1580624+fuomag9@users.noreply.github.com> Date: Fri Nov 7 15:58:13 2025 +0100 first drizzle rewrite
This commit is contained in:
+119
-11
@@ -1,20 +1,128 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import Database from "better-sqlite3";
|
||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { dirname, isAbsolute, resolve as resolvePath } from "node:path";
|
||||
import * as schema from "./db/schema";
|
||||
|
||||
// Prevent multiple instances of Prisma Client in development
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined;
|
||||
const DEFAULT_SQLITE_URL = "file:./data/caddy-proxy-manager.db";
|
||||
|
||||
type GlobalForDrizzle = typeof globalThis & {
|
||||
__DRIZZLE_DB__?: ReturnType<typeof drizzle<typeof schema>>;
|
||||
__SQLITE_CLIENT__?: Database.Database;
|
||||
__MIGRATIONS_RAN__?: boolean;
|
||||
};
|
||||
|
||||
export const prisma =
|
||||
globalForPrisma.prisma ??
|
||||
new PrismaClient({
|
||||
log: process.env.NODE_ENV === "development" ? ["error", "warn"] : ["error"],
|
||||
});
|
||||
function resolveSqlitePath(rawUrl: string): string {
|
||||
if (!rawUrl) {
|
||||
return ":memory:";
|
||||
}
|
||||
if (rawUrl === ":memory:" || rawUrl === "file::memory:") {
|
||||
return ":memory:";
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
|
||||
if (rawUrl.startsWith("file:./") || rawUrl.startsWith("file:../")) {
|
||||
const relative = rawUrl.slice("file:".length);
|
||||
return resolvePath(process.cwd(), relative);
|
||||
}
|
||||
|
||||
export default prisma;
|
||||
if (rawUrl.startsWith("file:")) {
|
||||
try {
|
||||
const fileUrl = new URL(rawUrl);
|
||||
if (fileUrl.host && fileUrl.host !== "localhost") {
|
||||
throw new Error("Remote SQLite hosts are not supported.");
|
||||
}
|
||||
return decodeURIComponent(fileUrl.pathname);
|
||||
} catch {
|
||||
const remainder = rawUrl.slice("file:".length);
|
||||
if (!remainder) {
|
||||
return ":memory:";
|
||||
}
|
||||
return isAbsolute(remainder) ? remainder : resolvePath(process.cwd(), remainder);
|
||||
}
|
||||
}
|
||||
|
||||
return isAbsolute(rawUrl) ? rawUrl : resolvePath(process.cwd(), rawUrl);
|
||||
}
|
||||
|
||||
const databaseUrl = process.env.DATABASE_URL ?? DEFAULT_SQLITE_URL;
|
||||
const sqlitePath = resolveSqlitePath(databaseUrl);
|
||||
|
||||
function ensureDirectoryFor(pathname: string) {
|
||||
if (pathname === ":memory:") {
|
||||
return;
|
||||
}
|
||||
const dir = dirname(pathname);
|
||||
mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
const globalForDrizzle = globalThis as GlobalForDrizzle;
|
||||
|
||||
const sqlite =
|
||||
globalForDrizzle.__SQLITE_CLIENT__ ??
|
||||
(() => {
|
||||
ensureDirectoryFor(sqlitePath);
|
||||
return new Database(sqlitePath);
|
||||
})();
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
globalForDrizzle.__SQLITE_CLIENT__ = sqlite;
|
||||
}
|
||||
|
||||
export const db =
|
||||
globalForDrizzle.__DRIZZLE_DB__ ?? drizzle(sqlite, { schema, casing: "snake_case" });
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
globalForDrizzle.__DRIZZLE_DB__ = db;
|
||||
}
|
||||
|
||||
const migrationsFolder = resolvePath(process.cwd(), "drizzle");
|
||||
|
||||
function runMigrations() {
|
||||
if (sqlitePath === ":memory:") {
|
||||
return;
|
||||
}
|
||||
if (globalForDrizzle.__MIGRATIONS_RAN__) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
migrate(db, { migrationsFolder });
|
||||
globalForDrizzle.__MIGRATIONS_RAN__ = true;
|
||||
} catch (error: any) {
|
||||
// During build, pages may be pre-rendered in parallel, causing race conditions
|
||||
// with migrations. If tables already exist, just continue.
|
||||
if (error?.code === 'SQLITE_ERROR' && error?.message?.includes('already exists')) {
|
||||
console.log('Database tables already exist, skipping migrations');
|
||||
globalForDrizzle.__MIGRATIONS_RAN__ = true;
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
runMigrations();
|
||||
} catch (error) {
|
||||
console.error("Failed to run database migrations:", error);
|
||||
// In build mode, allow the build to continue even if migrations fail
|
||||
// The runtime initialization will handle migrations properly
|
||||
if (process.env.NODE_ENV !== 'production' || process.env.NEXT_PHASE === 'phase-production-build') {
|
||||
console.warn('Continuing despite migration error during build phase');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export { schema };
|
||||
export default db;
|
||||
|
||||
export function nowIso(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
export function toIso(value: string | Date | null | undefined): string | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user