diff --git a/src/main.electron.ts b/src/main.electron.ts index 87cb1a78..59d191f0 100644 --- a/src/main.electron.ts +++ b/src/main.electron.ts @@ -126,9 +126,9 @@ const sqliteReady = new Promise((resolve, reject) => { // Now execute the test query const testQuery = await ipcRenderer.invoke('sqlite-query', { database: 'timesafari', - statement: 'SELECT sqlite_version() as version;' + statement: 'SELECT * FROM secret;' }); - logger.info("[Main Electron] SQLite test query successful:", testQuery); + logger.info("[Main Electron] SQLite secret query successful:", testQuery); // Close the database await ipcRenderer.invoke('sqlite-close', { diff --git a/src/services/platforms/ElectronPlatformService.ts b/src/services/platforms/ElectronPlatformService.ts index b76307fd..9c392004 100644 --- a/src/services/platforms/ElectronPlatformService.ts +++ b/src/services/platforms/ElectronPlatformService.ts @@ -39,6 +39,11 @@ interface Migration { sql: string; } +interface SQLiteQueryResult { + values?: Record[]; + changes?: { changes: number; lastId?: number }; +} + /** * Platform service implementation for Electron (desktop) platform. * Provides native desktop functionality through Electron and Capacitor plugins for: @@ -349,21 +354,90 @@ export class ElectronPlatformService implements PlatformService { } /** - * @see PlatformService.dbQuery + * Executes a database query with proper connection lifecycle management. + * Opens connection, executes query, and ensures proper cleanup. + * + * @param sql - SQL query to execute + * @param params - Optional parameters for the query + * @returns Promise resolving to query results + * @throws Error if database operations fail */ async dbQuery(sql: string, params: unknown[] = []): Promise> { - await this.initializeDatabase(); - if (this.dbFatalError) throw new Error("Database is in a fatal error state. Please restart the app."); - const result = await this.sqlite.query({ - database: this.dbName, - statement: sql, - values: params - }); - const columns = result.values?.[0] ? Object.keys(result.values[0]) : []; - return { - columns, - values: (result.values || []).map((row: Record) => row as T) - }; + if (this.dbFatalError) { + throw new Error("Database is in a fatal error state. Please restart the app."); + } + + if (!window.electron?.ipcRenderer) { + throw new Error("IPC renderer not available"); + } + + try { + // Check SQLite availability first + const isAvailable = await window.electron.ipcRenderer.invoke('sqlite-is-available'); + if (!isAvailable) { + throw new Error('[ElectronPlatformService] [dbQuery] SQLite is not available'); + } + logger.debug("[ElectronPlatformService] [dbQuery] SQLite is available"); + + // Create database connection + await window.electron.ipcRenderer.invoke('sqlite-create-connection', { + database: this.dbName, + version: 1 + }); + logger.debug("[ElectronPlatformService] [dbQuery] Database connection created"); + + // Open database + await window.electron.ipcRenderer.invoke('sqlite-open', { + database: this.dbName + }); + logger.debug("[ElectronPlatformService] [dbQuery] Database opened"); + + // Verify database is open + const isOpen = await window.electron.ipcRenderer.invoke('sqlite-is-db-open', { + database: this.dbName + }); + if (!isOpen) { + throw new Error('[ElectronPlatformService] [dbQuery] Database failed to open'); + } + + // Execute query + const result = await window.electron.ipcRenderer.invoke('sqlite-query', { + database: this.dbName, + statement: sql, + values: params + }) as SQLiteQueryResult; + logger.debug("[ElectronPlatformService] [dbQuery] Query executed successfully"); + + // Process results + const columns = result.values?.[0] ? Object.keys(result.values[0]) : []; + const processedResult = { + columns, + values: (result.values || []).map((row: Record) => row as T) + }; + + return processedResult; + } catch (error) { + logger.error("[ElectronPlatformService] [dbQuery] Query failed:", error); + throw error; + } finally { + // Ensure proper cleanup + try { + // Close database + await window.electron.ipcRenderer.invoke('sqlite-close', { + database: this.dbName + }); + logger.debug("[ElectronPlatformService] [dbQuery] Database closed"); + + // Close connection + await window.electron.ipcRenderer.invoke('sqlite-close-connection', { + database: this.dbName + }); + logger.debug("[ElectronPlatformService] [dbQuery] Database connection closed"); + } catch (closeError) { + logger.error("[ElectronPlatformService] [dbQuery] Failed to cleanup database:", closeError); + // Don't throw here - we want to preserve the original error if any + } + } } /**