raqim / lib /db /src /index.ts
RAQIM Deploy
Deploy RAQIM 2026-05-02 23:08
3e9069b
import { drizzle } from "drizzle-orm/sqlite-proxy";
import { DatabaseSync } from "node:sqlite";
import path from "path";
import fs from "fs";
import * as schema from "./schema";
const dbPath = process.env.DB_PATH || path.join(process.cwd(), "raqim.db");
// __dirname is injected by esbuild (bundle lives at artifacts/api-server/dist/)
// ../../../lib/db/drizzle → workspace-root/lib/db/drizzle in both dev and Docker
const migrationsDir =
process.env.MIGRATIONS_DIR ||
path.join(__dirname, "../../../lib/db/drizzle");
// ── Open / create the SQLite database ────────────────────────────────────────
export const sqlite = new DatabaseSync(dbPath);
sqlite.exec("PRAGMA journal_mode = WAL");
sqlite.exec("PRAGMA foreign_keys = ON");
// ── Apply pending migrations on startup ───────────────────────────────────────
function applyMigrations() {
sqlite.exec(`CREATE TABLE IF NOT EXISTS __drizzle_migrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hash TEXT NOT NULL UNIQUE,
applied_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
)`);
if (!fs.existsSync(migrationsDir)) {
console.warn("[db] No migrations directory found at:", migrationsDir);
return;
}
const sqlFiles = fs
.readdirSync(migrationsDir)
.filter((f) => f.endsWith(".sql"))
.sort();
for (const file of sqlFiles) {
const hash = file.replace(".sql", "");
const already = sqlite
.prepare("SELECT 1 FROM __drizzle_migrations WHERE hash = ?")
.get(hash);
if (already) continue;
const sql = fs.readFileSync(path.join(migrationsDir, file), "utf-8");
const statements = sql
.split("--> statement-breakpoint")
.map((s) => s.trim())
.filter(Boolean);
for (const stmt of statements) {
sqlite.exec(stmt);
}
sqlite
.prepare("INSERT INTO __drizzle_migrations (hash) VALUES (?)")
.run(hash);
console.log(`[db] Applied migration: ${file}`);
}
}
applyMigrations();
// ── Drizzle ORM wrapping node:sqlite via sqlite-proxy ────────────────────────
export const db = drizzle(
async (sql, params, method) => {
try {
const stmt = sqlite.prepare(sql);
if (method === "run") {
stmt.run(...(params as Parameters<typeof stmt.run>));
return { rows: [] };
}
// "get" expects a flat value array for the single row, not an array of arrays
if (method === "get") {
const row = stmt.get(...(params as Parameters<typeof stmt.get>)) as
| Record<string, unknown>
| undefined;
return { rows: row ? Object.values(row) : [] };
}
const result = stmt.all(...(params as Parameters<typeof stmt.all>)) as Record<
string,
unknown
>[];
return { rows: result.map((row) => Object.values(row)) };
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
console.error(`[db] ${method} error: ${msg}\n sql: ${sql.slice(0, 200)}\n params: ${JSON.stringify(params)}`);
throw new Error(`DB error [${method}]: ${msg}`);
}
},
{ schema },
);
export * from "./schema";