2026-03-25 09:24:37 +08:00
|
|
|
import { mkdirSync } from "node:fs";
|
|
|
|
|
import { dirname } from "node:path";
|
2026-03-26 01:10:13 +08:00
|
|
|
import Database, { type Database as DatabaseType } from "better-sqlite3";
|
2026-03-22 02:51:57 +08:00
|
|
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
|
|
|
import { env } from "../config.js";
|
|
|
|
|
import * as schema from "./schema.js";
|
|
|
|
|
|
2026-06-06 16:05:59 +08:00
|
|
|
try {
|
|
|
|
|
mkdirSync(dirname(env.DB_PATH), { recursive: true });
|
|
|
|
|
} catch (err: unknown) {
|
|
|
|
|
const code = (err as NodeJS.ErrnoException).code;
|
|
|
|
|
if (code === "EACCES") {
|
|
|
|
|
console.error(
|
|
|
|
|
`FATAL: Cannot write to data directory "${dirname(env.DB_PATH)}". Check volume permissions (PUID/PGID).`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
2026-03-22 02:51:57 +08:00
|
|
|
|
2026-03-26 01:10:13 +08:00
|
|
|
const sqlite: DatabaseType = new Database(env.DB_PATH);
|
2026-03-22 02:51:57 +08:00
|
|
|
|
2026-04-06 22:00:48 +08:00
|
|
|
// Critical SQLite pragmas for reliability.
|
|
|
|
|
// busy_timeout must be set first so journal_mode = WAL can retry
|
|
|
|
|
// if another connection holds the lock (e.g. parallel test files).
|
2026-04-20 21:46:07 +08:00
|
|
|
sqlite.pragma("busy_timeout = 10000");
|
2026-04-06 22:00:48 +08:00
|
|
|
sqlite.pragma("journal_mode = WAL");
|
2026-03-22 02:51:57 +08:00
|
|
|
sqlite.pragma("synchronous = NORMAL");
|
|
|
|
|
sqlite.pragma("foreign_keys = ON");
|
2026-06-06 16:05:59 +08:00
|
|
|
sqlite.pragma("wal_autocheckpoint = 1000");
|
|
|
|
|
sqlite.pragma("journal_size_limit = 67108864");
|
2026-03-22 02:51:57 +08:00
|
|
|
|
|
|
|
|
export const db = drizzle(sqlite, { schema });
|
2026-03-25 09:24:37 +08:00
|
|
|
export { schema, sqlite };
|