56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
import { Inject, Injectable, Logger, OnModuleDestroy, OnModuleInit, Optional } from '@nestjs/common'
|
|
import { ConfigService } from '@nestjs/config'
|
|
import Database from 'better-sqlite3'
|
|
import { mkdirSync } from 'node:fs'
|
|
import { dirname, join } from 'node:path'
|
|
import { EXPECTED_TABLES, listUserTables, runSchemaMigrations } from './migration.service'
|
|
|
|
@Injectable()
|
|
export class DatabaseService implements OnModuleInit, OnModuleDestroy {
|
|
private readonly logger = new Logger(DatabaseService.name)
|
|
private db: Database.Database | null = null
|
|
private readonly dbPath: string
|
|
|
|
constructor(@Optional() @Inject(ConfigService) private readonly config?: ConfigService) {
|
|
const configured = this.config?.get<string>('DB_PATH') ?? process.env.DB_PATH
|
|
this.dbPath = configured ?? join(process.cwd(), 'data', 'app.db')
|
|
}
|
|
|
|
onModuleInit(): void {
|
|
mkdirSync(dirname(this.dbPath), { recursive: true })
|
|
this.db = new Database(this.dbPath)
|
|
this.db.pragma('journal_mode = WAL')
|
|
runSchemaMigrations(this.db)
|
|
this.logger.log(`SQLite ready at ${this.dbPath}`)
|
|
}
|
|
|
|
onModuleDestroy(): void {
|
|
if (this.db) {
|
|
this.db.close()
|
|
this.db = null
|
|
this.logger.log('SQLite connection closed')
|
|
}
|
|
}
|
|
|
|
getDbPath(): string {
|
|
return this.dbPath
|
|
}
|
|
|
|
getDb(): Database.Database {
|
|
if (!this.db) {
|
|
throw new Error('Database is not initialized')
|
|
}
|
|
return this.db
|
|
}
|
|
|
|
transaction<T>(fn: () => T): T {
|
|
return this.getDb().transaction(fn)()
|
|
}
|
|
|
|
verifyMigrationsApplied(): { ok: boolean; tables: string[]; missing: string[] } {
|
|
const tables = listUserTables(this.getDb())
|
|
const missing = EXPECTED_TABLES.filter((name) => !tables.includes(name))
|
|
return { ok: missing.length === 0, tables, missing }
|
|
}
|
|
}
|