diff --git a/electron/src/preload.ts b/electron/src/preload.ts index fb058374..225c9d8d 100644 --- a/electron/src/preload.ts +++ b/electron/src/preload.ts @@ -48,6 +48,9 @@ const VALID_CHANNELS = { 'sqlite-query', 'sqlite-run', 'sqlite-close-connection', + 'sqlite-open', + 'sqlite-close', + 'sqlite-is-db-open', 'get-path', 'get-base-path' ] as const diff --git a/electron/src/rt/sqlite-init.ts b/electron/src/rt/sqlite-init.ts index f06c2c7c..bf382082 100644 --- a/electron/src/rt/sqlite-init.ts +++ b/electron/src/rt/sqlite-init.ts @@ -681,7 +681,7 @@ export function setupSQLiteHandlers(): void { endDatabaseOperation(); } }); - + // Handler for creating database connection registerHandler('sqlite-create-connection', async (_event, options: SQLiteConnectionOptions) => { logger.debug('Creating SQLite connection:', options); @@ -701,7 +701,7 @@ export function setupSQLiteHandlers(): void { endDatabaseOperation(); } }); - + // Handler for executing SQL statements registerHandler('sqlite-execute', async (_event, options: SQLiteExecuteOptions) => { logger.debug('Executing SQL statements:', options); @@ -720,7 +720,7 @@ export function setupSQLiteHandlers(): void { endDatabaseOperation(); } }); - + // Handler for querying data registerHandler('sqlite-query', async (_event, options: SQLiteQueryOptions) => { logger.debug('Querying SQLite:', options); @@ -778,6 +778,63 @@ export function setupSQLiteHandlers(): void { } }); + // Handler for opening database + registerHandler('sqlite-open', async (_event, options: SQLiteConnectionOptions) => { + logger.debug('Opening SQLite database:', options); + try { + startDatabaseOperation(); + if (!pluginState.instance) { + throw new SQLiteError('Plugin not initialized', 'sqlite-open'); + } + await pluginState.instance.open(options); + logger.debug('SQLite database opened successfully'); + return true; + } catch (error) { + logger.error('SQLite database open failed:', error); + throw error; + } finally { + endDatabaseOperation(); + } + }); + + // Handler for closing database + registerHandler('sqlite-close', async (_event, options: { database: string }) => { + logger.debug('Closing SQLite database:', options); + try { + startDatabaseOperation(); + if (!pluginState.instance) { + throw new SQLiteError('Plugin not initialized', 'sqlite-close'); + } + await pluginState.instance.close(options); + logger.debug('SQLite database closed successfully'); + return true; + } catch (error) { + logger.error('SQLite database close failed:', error); + throw error; + } finally { + endDatabaseOperation(); + } + }); + + // Handler for checking if database is open + registerHandler('sqlite-is-db-open', async (_event, options: { database: string }) => { + logger.debug('Checking if SQLite database is open:', options); + try { + startDatabaseOperation(); + if (!pluginState.instance) { + throw new SQLiteError('Plugin not initialized', 'sqlite-is-db-open'); + } + const isOpen = await pluginState.instance.isDBOpen(options); + logger.debug('SQLite database open check:', { isOpen }); + return isOpen; + } catch (error) { + logger.error('SQLite database open check failed:', error); + throw error; + } finally { + endDatabaseOperation(); + } + }); + // Handler for getting database path registerHandler('get-path', async () => { logger.debug('Getting database path'); @@ -794,7 +851,7 @@ export function setupSQLiteHandlers(): void { endDatabaseOperation(); } }); - + // Handler for getting base path registerHandler('get-base-path', async () => { logger.debug('Getting base path'); diff --git a/electron/src/setup.ts b/electron/src/setup.ts index ac0e7ca9..60f491f5 100644 --- a/electron/src/setup.ts +++ b/electron/src/setup.ts @@ -166,7 +166,7 @@ export function setupReloadWatcher(electronCapacitorApp: ElectronCapacitorApp): } // Set up new debouncer - reloadWatcher.debouncer = setTimeout(async () => { + reloadWatcher.debouncer = setTimeout(async () => { if (!canReload()) { return; } diff --git a/src/db/databaseUtil.ts b/src/db/databaseUtil.ts index d3ac8109..40d689f9 100644 --- a/src/db/databaseUtil.ts +++ b/src/db/databaseUtil.ts @@ -131,9 +131,9 @@ let lastCleanupDate: string | null = null; * @author Matthew Raymer */ export async function logToDb(message: string): Promise { - const platform = PlatformServiceFactory.getInstance(); + //const platform = PlatformServiceFactory.getInstance(); const todayKey = new Date().toDateString(); - const nowKey = new Date().toISOString(); + //const nowKey = new Date().toISOString(); try { // Try to insert first, if it fails due to UNIQUE constraint, update instead @@ -145,9 +145,9 @@ export async function logToDb(message: string): Promise { // Clean up old logs (keep only last 7 days) - do this less frequently // Only clean up if the date is different from the last cleanup if (!lastCleanupDate || lastCleanupDate !== todayKey) { - const sevenDaysAgo = new Date( - new Date().getTime() - 7 * 24 * 60 * 60 * 1000, - ); + // const sevenDaysAgo = new Date( + // new Date().getTime() - 7 * 24 * 60 * 60 * 1000, + // ); // await platform.dbExec("DELETE FROM logs WHERE date < ?", [ // sevenDaysAgo.toDateString(), // ]); diff --git a/src/main.electron.ts b/src/main.electron.ts index 18b37dc0..87cb1a78 100644 --- a/src/main.electron.ts +++ b/src/main.electron.ts @@ -63,44 +63,106 @@ const sqliteReady = new Promise((resolve, reject) => { // Wait for electron bridge to be available const checkElectronBridge = () => { - if (window.electron?.ipcRenderer) { - logger.info("[Main Electron] IPC renderer bridge available"); - - // Listen for SQLite ready signal - window.electron.ipcRenderer.once('sqlite-ready', () => { - clearTimeout(initializationTimeout); - logger.info("[Main Electron] Received SQLite ready signal"); - resolve(); - }); - - // Also listen for database errors - window.electron.ipcRenderer.once('database-status', (...args: unknown[]) => { - clearTimeout(initializationTimeout); - const status = args[0] as { status: string; error?: string }; - if (status.status === 'error') { - logger.error("[Main Electron] Database error:", status.error); - reject(new Error(status.error || 'Database initialization failed')); - } - }); - - // Check if SQLite is already available - window.electron.ipcRenderer.invoke('sqlite-is-available') - .then((result: unknown) => { - const isAvailable = Boolean(result); - if (isAvailable) { - logger.info("[Main Electron] SQLite is already available"); - // Don't resolve here - wait for the ready signal - // This prevents race conditions where the ready signal arrives after this check - } - }) - .catch((error: Error) => { - logger.error("[Main Electron] Failed to check SQLite availability:", error); - // Don't reject here - wait for either ready signal or timeout - }); - } else { + if (!window.electron?.ipcRenderer) { // Check again in 100ms if bridge isn't ready setTimeout(checkElectronBridge, 100); + return; } + + // At this point we know ipcRenderer exists + const ipcRenderer = window.electron.ipcRenderer; + + logger.info("[Main Electron] IPC renderer bridge available"); + + // Listen for SQLite ready signal + ipcRenderer.once('sqlite-ready', () => { + clearTimeout(initializationTimeout); + logger.info("[Main Electron] Received SQLite ready signal"); + resolve(); + }); + + // Also listen for database errors + ipcRenderer.once('database-status', (...args: unknown[]) => { + clearTimeout(initializationTimeout); + const status = args[0] as { status: string; error?: string }; + if (status.status === 'error') { + logger.error("[Main Electron] Database error:", status.error); + reject(new Error(status.error || 'Database initialization failed')); + } + }); + + // Check if SQLite is already available + ipcRenderer.invoke('sqlite-is-available') + .then(async (result: unknown) => { + const isAvailable = Boolean(result); + if (isAvailable) { + logger.info("[Main Electron] SQLite is already available"); + + try { + // First create a database connection + const dbPath = await ipcRenderer.invoke('get-path'); + logger.info("[Main Electron] Creating database connection:", { dbPath }); + + // Create the database connection + await ipcRenderer.invoke('sqlite-create-connection', { + database: 'timesafari', + version: 1 + }); + + // Explicitly open the database + await ipcRenderer.invoke('sqlite-open', { + database: 'timesafari' + }); + logger.info("[Main Electron] Database opened successfully"); + + // Verify the database is open + const isOpen = await ipcRenderer.invoke('sqlite-is-db-open', { + database: 'timesafari' + }); + if (!isOpen) { + throw new Error('Database failed to open'); + } + + // Now execute the test query + const testQuery = await ipcRenderer.invoke('sqlite-query', { + database: 'timesafari', + statement: 'SELECT sqlite_version() as version;' + }); + logger.info("[Main Electron] SQLite test query successful:", testQuery); + + // Close the database + await ipcRenderer.invoke('sqlite-close', { + database: 'timesafari' + }); + logger.info("[Main Electron] Database closed successfully"); + + // Close the connection + await ipcRenderer.invoke('sqlite-close-connection', { + database: 'timesafari' + }); + logger.info("[Main Electron] Database connection closed successfully"); + } catch (error) { + logger.error("[Main Electron] SQLite test operation failed:", error); + // Try to close everything if anything was opened + try { + await ipcRenderer.invoke('sqlite-close', { + database: 'timesafari' + }).catch(() => {}); + await ipcRenderer.invoke('sqlite-close-connection', { + database: 'timesafari' + }).catch(() => {}); + logger.info("[Main Electron] Database cleanup completed after error"); + } catch (closeError) { + logger.error("[Main Electron] Failed to cleanup database:", closeError); + } + // Don't reject here - we still want to wait for the ready signal + } + } + }) + .catch((error: Error) => { + logger.error("[Main Electron] Failed to check SQLite availability:", error); + // Don't reject here - wait for either ready signal or timeout + }); }; // Start checking for bridge