|
|
@ -16,16 +16,49 @@ interface SQLDatabase { |
|
|
|
} |
|
|
|
|
|
|
|
class DatabaseService { |
|
|
|
private static instance: DatabaseService | null = null; |
|
|
|
private db: SQLDatabase | null; |
|
|
|
private initialized: boolean; |
|
|
|
private initializationPromise: Promise<void> | null = null; |
|
|
|
|
|
|
|
constructor() { |
|
|
|
private constructor() { |
|
|
|
this.db = null; |
|
|
|
this.initialized = false; |
|
|
|
} |
|
|
|
|
|
|
|
static getInstance(): DatabaseService { |
|
|
|
if (!DatabaseService.instance) { |
|
|
|
DatabaseService.instance = new DatabaseService(); |
|
|
|
} |
|
|
|
return DatabaseService.instance; |
|
|
|
} |
|
|
|
|
|
|
|
async initialize(): Promise<void> { |
|
|
|
if (this.initialized) return; |
|
|
|
// If already initialized, return immediately
|
|
|
|
if (this.initialized) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// If initialization is in progress, wait for it
|
|
|
|
if (this.initializationPromise) { |
|
|
|
return this.initializationPromise; |
|
|
|
} |
|
|
|
|
|
|
|
// Start initialization
|
|
|
|
this.initializationPromise = this._initialize(); |
|
|
|
try { |
|
|
|
await this.initializationPromise; |
|
|
|
} catch (error) { |
|
|
|
console.error(`DatabaseService initialize method failed:`, error); |
|
|
|
this.initializationPromise = null; // Reset on failure
|
|
|
|
throw error; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async _initialize(): Promise<void> { |
|
|
|
if (this.initialized) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const SQL = await initSqlJs({ |
|
|
|
locateFile: (file: string) => { |
|
|
@ -48,12 +81,10 @@ class DatabaseService { |
|
|
|
|
|
|
|
this.db = new SQL.Database(path, { filename: true }); |
|
|
|
if (!this.db) { |
|
|
|
throw new Error('Failed to initialize database'); |
|
|
|
throw new Error('The database initialization failed. We recommend you restart or reinstall.'); |
|
|
|
} |
|
|
|
|
|
|
|
await this.db.exec(` |
|
|
|
PRAGMA journal_mode=MEMORY; |
|
|
|
`);
|
|
|
|
await this.db.exec(`PRAGMA journal_mode=MEMORY;`); |
|
|
|
const sqlExec = this.db.exec.bind(this.db); |
|
|
|
|
|
|
|
// Run migrations
|
|
|
@ -62,38 +93,52 @@ class DatabaseService { |
|
|
|
this.initialized = true; |
|
|
|
} |
|
|
|
|
|
|
|
private ensureInitialized(): void { |
|
|
|
if (!this.initialized || !this.db) { |
|
|
|
throw new Error('Database not initialized'); |
|
|
|
private async waitForInitialization(): Promise<void> { |
|
|
|
// If we have an initialization promise, wait for it
|
|
|
|
if (this.initializationPromise) { |
|
|
|
await this.initializationPromise; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// If not initialized and no promise, start initialization
|
|
|
|
if (!this.initialized) { |
|
|
|
await this.initialize(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// If initialized but no db, something went wrong
|
|
|
|
if (!this.db) { |
|
|
|
console.error(`Database not properly initialized after await waitForInitialization() - initialized flag is true but db is null`); |
|
|
|
throw new Error(`The database could not be initialized. We recommend you restart or reinstall.`); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Used for inserts, updates, and deletes
|
|
|
|
async run(sql: string, params: any[] = []): Promise<{ changes: number; lastId?: number }> { |
|
|
|
this.ensureInitialized(); |
|
|
|
await this.waitForInitialization(); |
|
|
|
return this.db!.run(sql, params); |
|
|
|
} |
|
|
|
|
|
|
|
// Note that the resulting array may be empty if there are no results from the query
|
|
|
|
async query(sql: string, params: any[] = []): Promise<QueryExecResult[]> { |
|
|
|
this.ensureInitialized(); |
|
|
|
await this.waitForInitialization(); |
|
|
|
return this.db!.exec(sql, params); |
|
|
|
} |
|
|
|
|
|
|
|
async getOneRow(sql: string, params: any[] = []): Promise<any[] | undefined> { |
|
|
|
this.ensureInitialized(); |
|
|
|
await this.waitForInitialization(); |
|
|
|
const result = await this.db!.exec(sql, params); |
|
|
|
return result[0]?.values[0]; |
|
|
|
} |
|
|
|
|
|
|
|
async all(sql: string, params: any[] = []): Promise<any[][]> { |
|
|
|
this.ensureInitialized(); |
|
|
|
await this.waitForInitialization(); |
|
|
|
const result = await this.db!.exec(sql, params); |
|
|
|
return result[0]?.values || []; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Create a singleton instance
|
|
|
|
const databaseService = new DatabaseService(); |
|
|
|
const databaseService = DatabaseService.getInstance(); |
|
|
|
|
|
|
|
export default databaseService; |