patch: swap to stdlib sqlite3
This commit is contained in:
Generated
+2570
-3820
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,6 @@
|
|||||||
"nuxt": "^4.0.0",
|
"nuxt": "^4.0.0",
|
||||||
"openid-client": "^6.8.2",
|
"openid-client": "^6.8.2",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"sqlite3": "^5.1.7",
|
|
||||||
"vue": "^3.4.0",
|
"vue": "^3.4.0",
|
||||||
"vue-router": "^5.0.0",
|
"vue-router": "^5.0.0",
|
||||||
"ws": "^8.18.0"
|
"ws": "^8.18.0"
|
||||||
|
|||||||
+87
-25
@@ -1,14 +1,12 @@
|
|||||||
import { join, dirname } from 'node:path'
|
import { join, dirname } from 'node:path'
|
||||||
import { mkdirSync, existsSync } from 'node:fs'
|
import { mkdirSync, existsSync } from 'node:fs'
|
||||||
|
import { randomBytes, randomUUID } from 'node:crypto'
|
||||||
import { createRequire } from 'node:module'
|
import { createRequire } from 'node:module'
|
||||||
import { promisify } from 'node:util'
|
|
||||||
import { randomBytes } from 'node:crypto'
|
|
||||||
import { hashPassword } from './password.js'
|
import { hashPassword } from './password.js'
|
||||||
import { registerCleanup } from './shutdown.js'
|
import { registerCleanup } from './shutdown.js'
|
||||||
|
|
||||||
// Resolve from project root so bundled server (e.g. .output) finds node_modules/sqlite3
|
|
||||||
const requireFromRoot = createRequire(join(process.cwd(), 'package.json'))
|
const requireFromRoot = createRequire(join(process.cwd(), 'package.json'))
|
||||||
const sqlite3 = requireFromRoot('sqlite3')
|
const { DatabaseSync } = requireFromRoot('node:sqlite')
|
||||||
|
|
||||||
const SCHEMA_VERSION = 4
|
const SCHEMA_VERSION = 4
|
||||||
const DB_BUSY_TIMEOUT_MS = 5000
|
const DB_BUSY_TIMEOUT_MS = 5000
|
||||||
@@ -144,7 +142,6 @@ const initDb = async (db, run, all, get) => {
|
|||||||
catch {
|
catch {
|
||||||
// WAL not supported (e.g., network filesystem)
|
// WAL not supported (e.g., network filesystem)
|
||||||
}
|
}
|
||||||
db.configure('busyTimeout', DB_BUSY_TIMEOUT_MS)
|
|
||||||
|
|
||||||
await run(SCHEMA.schema_version)
|
await run(SCHEMA.schema_version)
|
||||||
await run(SCHEMA.users)
|
await run(SCHEMA.users)
|
||||||
@@ -168,7 +165,7 @@ const initDb = async (db, run, all, get) => {
|
|||||||
|
|
||||||
await run(
|
await run(
|
||||||
'INSERT INTO users (id, identifier, password_hash, role, created_at, auth_provider, oidc_issuer, oidc_sub) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
'INSERT INTO users (id, identifier, password_hash, role, created_at, auth_provider, oidc_issuer, oidc_sub) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||||
[crypto.randomUUID(), identifier, hashPassword(plainPassword), 'admin', new Date().toISOString(), 'local', null, null],
|
[randomUUID(), identifier, hashPassword(plainPassword), 'admin', new Date().toISOString(), 'local', null, null],
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!email || !password) {
|
if (!email || !password) {
|
||||||
@@ -181,37 +178,104 @@ const initDb = async (db, run, all, get) => {
|
|||||||
export async function getDb() {
|
export async function getDb() {
|
||||||
if (dbInstance) return dbInstance
|
if (dbInstance) return dbInstance
|
||||||
|
|
||||||
const db = new sqlite3.Database(getDbPath(), (err) => {
|
const sqliteDb = (() => {
|
||||||
if (err) {
|
try {
|
||||||
|
return new DatabaseSync(getDbPath(), { timeout: DB_BUSY_TIMEOUT_MS })
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
console.error('[db] Failed to open database:', err.message)
|
console.error('[db] Failed to open database:', err.message)
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
})
|
})()
|
||||||
|
|
||||||
const run = promisify(db.run.bind(db))
|
|
||||||
const all = promisify(db.all.bind(db))
|
|
||||||
const get = promisify(db.get.bind(db))
|
|
||||||
|
|
||||||
|
const run = (sql, params = []) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const stmt = sqliteDb.prepare(sql)
|
||||||
try {
|
try {
|
||||||
await initDb(db, run, all, get)
|
stmt.run(...params)
|
||||||
|
resolve()
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
db.close()
|
reject(error)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
stmt.finalize()
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// ignore finalize errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const all = (sql, params = []) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const stmt = sqliteDb.prepare(sql)
|
||||||
|
try {
|
||||||
|
const rows = stmt.all(...params)
|
||||||
|
resolve(rows)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
stmt.finalize()
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// ignore finalize errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const get = (sql, params = []) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const stmt = sqliteDb.prepare(sql)
|
||||||
|
try {
|
||||||
|
const row = stmt.get(...params)
|
||||||
|
resolve(row)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
stmt.finalize()
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// ignore finalize errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const wrappedDb = {
|
||||||
|
close(callback) {
|
||||||
|
try {
|
||||||
|
sqliteDb.close()
|
||||||
|
if (typeof callback === 'function') callback(null)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (typeof callback === 'function') callback(error)
|
||||||
|
else throw error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await initDb(wrappedDb, run, all, get)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
wrappedDb.close()
|
||||||
console.error('[db] Database initialization failed:', error.message)
|
console.error('[db] Database initialization failed:', error.message)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
dbInstance = { db, run, all, get }
|
dbInstance = { db: wrappedDb, run, all, get }
|
||||||
|
|
||||||
registerCleanup(async () => {
|
registerCleanup(async () => {
|
||||||
if (dbInstance) {
|
if (dbInstance) {
|
||||||
try {
|
try {
|
||||||
await new Promise((resolve, reject) => {
|
dbInstance.db.close()
|
||||||
dbInstance.db.close((err) => {
|
|
||||||
if (err) reject(err)
|
|
||||||
else resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('[db] Error closing database during shutdown:', error?.message)
|
console.error('[db] Error closing database during shutdown:', error?.message)
|
||||||
@@ -290,9 +354,7 @@ export async function withTransaction(db, callback) {
|
|||||||
export function closeDb() {
|
export function closeDb() {
|
||||||
if (!dbInstance) return
|
if (!dbInstance) return
|
||||||
try {
|
try {
|
||||||
dbInstance.db.close((err) => {
|
dbInstance.db.close()
|
||||||
if (err) console.error('[db] Error closing database:', err.message)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('[db] Error closing database:', error.message)
|
console.error('[db] Error closing database:', error.message)
|
||||||
|
|||||||
Reference in New Issue
Block a user