Files
vps-tracker/server/db/index.js
T
2026-03-17 23:19:28 +07:00

110 lines
2.5 KiB
JavaScript

/**
* Database initialization and access
*/
import initSqlJs from 'sql.js'
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'
import { SCHEMA } from './schema.js'
import { MIGRATIONS } from './migrations.js'
import { seed, isDbEmpty } from './seed.js'
const __dirname = dirname(fileURLToPath(import.meta.url))
// db/ is in server/, so .. = server, .. again = project root
const DB_PATH = join(__dirname, '..', '..', 'data', 'vps-tracker.db')
const SEED_DIR = join(__dirname, '..', '..', 'public', 'data')
let dbInstance = null
export async function initDb() {
const dataDir = join(__dirname, '..', '..', 'data')
if (!existsSync(dataDir)) {
mkdirSync(dataDir, { recursive: true })
}
const SQL = await initSqlJs()
let db
if (existsSync(DB_PATH)) {
const fileBuffer = readFileSync(DB_PATH)
db = new SQL.Database(fileBuffer)
} else {
db = new SQL.Database()
}
db.exec(SCHEMA)
for (const m of MIGRATIONS) {
try {
m.run(db)
} catch (err) {
console.warn(`Migration ${m.name} failed:`, err.message)
}
}
if (isDbEmpty(db)) {
seed(db, SEED_DIR)
const data = db.export()
writeFileSync(DB_PATH, Buffer.from(data))
}
dbInstance = db
return db
}
export function getDb() {
if (!dbInstance) throw new Error('Database not initialized')
return createDbWrapper(dbInstance)
}
function createDbWrapper(db) {
return {
/** One-off run: prepare, bind, step, free. Use for statements that run once. */
run(sql, ...params) {
const stmt = db.prepare(sql)
stmt.bind(params)
stmt.step()
const changes = db.getRowsModified()
stmt.free()
saveDb()
return { changes }
},
prepare(sql) {
const stmt = db.prepare(sql)
return {
all(...params) {
stmt.bind(params)
const rows = []
while (stmt.step()) {
rows.push(stmt.getAsObject())
}
stmt.free()
return rows
},
get(...params) {
stmt.bind(params)
const row = stmt.step() ? stmt.getAsObject() : null
stmt.free()
return row
},
run(...params) {
stmt.bind(params)
stmt.step()
const changes = db.getRowsModified()
stmt.free()
saveDb()
return { changes }
},
}
},
}
}
export function saveDb() {
if (!dbInstance) return
const data = dbInstance.export()
writeFileSync(DB_PATH, Buffer.from(data))
}