diff --git a/electron/src/setup.ts b/electron/src/setup.ts index 02559500..bb940ef8 100644 --- a/electron/src/setup.ts +++ b/electron/src/setup.ts @@ -232,19 +232,35 @@ export function setupContentSecurityPolicy(customScheme: string): void { ...details.responseHeaders, 'Content-Security-Policy': [ // Base CSP for both dev and prod - `default-src ${customScheme}://* 'unsafe-inline' data:;`, - // Allow Google Fonts - `style-src ${customScheme}://* 'unsafe-inline' https://fonts.googleapis.com;`, - `font-src ${customScheme}://* https://fonts.gstatic.com;`, - // Allow images and media - `img-src ${customScheme}://* data: https:;`, - // Allow connections to HTTPS resources - `connect-src ${customScheme}://* https:;`, - // Add dev-specific policies - ...(electronIsDev ? [ - `script-src ${customScheme}://* 'unsafe-inline' 'unsafe-eval' devtools://*;`, - `default-src ${customScheme}://* 'unsafe-inline' devtools://* 'unsafe-eval' data:;` - ] : []) + `default-src ${customScheme}://*;`, + // Script sources + `script-src ${customScheme}://* 'self' 'unsafe-inline'${electronIsDev ? " 'unsafe-eval'" : ''};`, + // Style sources + `style-src ${customScheme}://* 'self' 'unsafe-inline' https://fonts.googleapis.com;`, + // Font sources + `font-src ${customScheme}://* 'self' https://fonts.gstatic.com;`, + // Image sources + `img-src ${customScheme}://* 'self' data: https:;`, + // Connect sources (for API calls) + `connect-src ${customScheme}://* 'self' https:;`, + // Worker sources + `worker-src ${customScheme}://* 'self' blob:;`, + // Frame sources + `frame-src ${customScheme}://* 'self';`, + // Media sources + `media-src ${customScheme}://* 'self' data:;`, + // Object sources + `object-src 'none';`, + // Base URI + `base-uri 'self';`, + // Form action + `form-action ${customScheme}://* 'self';`, + // Frame ancestors + `frame-ancestors 'none';`, + // Upgrade insecure requests + 'upgrade-insecure-requests;', + // Block mixed content + 'block-all-mixed-content;' ].join(' ') }, }); diff --git a/src/services/platforms/ElectronPlatformService.ts b/src/services/platforms/ElectronPlatformService.ts index 5caafa8a..b76307fd 100644 --- a/src/services/platforms/ElectronPlatformService.ts +++ b/src/services/platforms/ElectronPlatformService.ts @@ -6,7 +6,8 @@ import { } from "../PlatformService"; import { logger } from "../../utils/logger"; import { DEFAULT_ENDORSER_API_SERVER } from "@/constants/app"; -import { DatabaseConnectionPool } from "../database/ConnectionPool"; + +import { verifyElectronAPI, testSQLiteOperations } from "../../utils/debug-electron"; // Type for the electron window object declare global { @@ -54,29 +55,51 @@ export class ElectronPlatformService implements PlatformService { private sqliteReadyPromise: Promise | null = null; constructor() { - this.sqliteReadyPromise = new Promise((resolve, reject) => { - if (!window.electron?.ipcRenderer) { - logger.warn('[ElectronPlatformService] IPC renderer not available'); - reject(new Error('IPC renderer not available')); - return; - } - const timeout = setTimeout(() => { - reject(new Error('SQLite initialization timeout')); - }, 30000); - window.electron.ipcRenderer.once('sqlite-ready', () => { - clearTimeout(timeout); - logger.info('[ElectronPlatformService] Received SQLite ready signal'); - this.isInitialized = true; - resolve(); - }); - window.electron.ipcRenderer.once('database-status', (...args: unknown[]) => { - clearTimeout(timeout); - const status = args[0] as { status: string; error?: string }; - if (status.status === 'error') { - this.dbFatalError = true; - reject(new Error(status.error || 'Database initialization failed')); + this.sqliteReadyPromise = new Promise(async (resolve, reject) => { + try { + // Verify Electron API exposure first + await verifyElectronAPI(); + logger.info('[ElectronPlatformService] Electron API verification successful'); + + if (!window.electron?.ipcRenderer) { + logger.warn('[ElectronPlatformService] IPC renderer not available'); + reject(new Error('IPC renderer not available')); + return; } - }); + + const timeout = setTimeout(() => { + reject(new Error('SQLite initialization timeout')); + }, 30000); + + window.electron.ipcRenderer.once('sqlite-ready', async () => { + clearTimeout(timeout); + logger.info('[ElectronPlatformService] Received SQLite ready signal'); + + try { + // Test SQLite operations after receiving ready signal + await testSQLiteOperations(); + logger.info('[ElectronPlatformService] SQLite operations test successful'); + + this.isInitialized = true; + resolve(); + } catch (error) { + logger.error('[ElectronPlatformService] SQLite operations test failed:', error); + reject(error); + } + }); + + window.electron.ipcRenderer.once('database-status', (...args: unknown[]) => { + clearTimeout(timeout); + const status = args[0] as { status: string; error?: string }; + if (status.status === 'error') { + this.dbFatalError = true; + reject(new Error(status.error || 'Database initialization failed')); + } + }); + } catch (error) { + logger.error('[ElectronPlatformService] Initialization failed:', error); + reject(error); + } }); } diff --git a/src/utils/debug-electron.ts b/src/utils/debug-electron.ts new file mode 100644 index 00000000..2d46b04c --- /dev/null +++ b/src/utils/debug-electron.ts @@ -0,0 +1,123 @@ +/** + * Debug utilities for Electron integration + * Helps verify the context bridge and SQLite functionality + */ + +const debugLogger = { + log: (...args: unknown[]) => console.log('[Debug]', ...args), + error: (...args: unknown[]) => console.error('[Debug]', ...args), + info: (...args: unknown[]) => console.info('[Debug]', ...args), + warn: (...args: unknown[]) => console.warn('[Debug]', ...args), + debug: (...args: unknown[]) => console.debug('[Debug]', ...args) +}; + +export async function verifyElectronAPI(): Promise { + debugLogger.info('Verifying Electron API exposure...'); + + // Check if window.electron exists + if (!window.electron) { + throw new Error('window.electron is not defined'); + } + debugLogger.info('window.electron is available'); + + // Verify IPC renderer + if (!window.electron.ipcRenderer) { + throw new Error('IPC renderer is not available'); + } + debugLogger.info('IPC renderer is available with methods:', { + hasOn: typeof window.electron.ipcRenderer.on === 'function', + hasOnce: typeof window.electron.ipcRenderer.once === 'function', + hasSend: typeof window.electron.ipcRenderer.send === 'function', + hasInvoke: typeof window.electron.ipcRenderer.invoke === 'function' + }); + + // Verify SQLite API + if (!window.electron.sqlite) { + throw new Error('SQLite API is not available'); + } + debugLogger.info('SQLite API is available with methods:', { + hasIsAvailable: typeof window.electron.sqlite.isAvailable === 'function', + hasEcho: typeof window.electron.sqlite.echo === 'function', + hasCreateConnection: typeof window.electron.sqlite.createConnection === 'function', + hasCloseConnection: typeof window.electron.sqlite.closeConnection === 'function', + hasQuery: typeof window.electron.sqlite.query === 'function', + hasRun: typeof window.electron.sqlite.run === 'function', + hasExecute: typeof window.electron.sqlite.execute === 'function', + hasGetPlatform: typeof window.electron.sqlite.getPlatform === 'function' + }); + + // Test SQLite availability + try { + const isAvailable = await window.electron.sqlite.isAvailable(); + debugLogger.info('SQLite availability check:', { isAvailable }); + } catch (error) { + debugLogger.error('SQLite availability check failed:', error); + } + + // Test echo functionality + try { + const echoResult = await window.electron.sqlite.echo('test'); + debugLogger.info('SQLite echo test:', echoResult); + } catch (error) { + debugLogger.error('SQLite echo test failed:', error); + } + + // Verify environment + debugLogger.info('Environment:', { + platform: window.electron.env.platform, + isDev: window.electron.env.isDev + }); + + debugLogger.info('Electron API verification complete'); +} + +// Export a function to test SQLite operations +export async function testSQLiteOperations(): Promise { + debugLogger.info('Testing SQLite operations...'); + + try { + // Test connection creation + debugLogger.info('Creating test connection...'); + await window.electron.sqlite.createConnection({ + database: 'test', + version: 1, + readOnly: false + }); + debugLogger.info('Test connection created successfully'); + + // Test query + debugLogger.info('Testing query operation...'); + const queryResult = await window.electron.sqlite.query({ + statement: 'SELECT 1 as test' + }); + debugLogger.info('Query test result:', queryResult); + + // Test run + debugLogger.info('Testing run operation...'); + const runResult = await window.electron.sqlite.run({ + statement: 'CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY)' + }); + debugLogger.info('Run test result:', runResult); + + // Test execute + debugLogger.info('Testing execute operation...'); + const executeResult = await window.electron.sqlite.execute({ + statements: [ + { statement: 'INSERT INTO test_table (id) VALUES (1)' }, + { statement: 'SELECT * FROM test_table' } + ] + }); + debugLogger.info('Execute test result:', executeResult); + + // Clean up + debugLogger.info('Closing test connection...'); + await window.electron.sqlite.closeConnection({ database: 'test' }); + debugLogger.info('Test connection closed'); + + } catch (error) { + debugLogger.error('SQLite operation test failed:', error); + throw error; + } + + debugLogger.info('SQLite operations test complete'); +} \ No newline at end of file