diff --git a/src/libs/util.ts b/src/libs/util.ts index fc4a6b77..8282e01d 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -646,29 +646,29 @@ export async function saveNewIdentity( const secrets = await platformService.dbQuery( `SELECT secretBase64 FROM secret`, ); - + // If no secret exists, create one let secretBase64: string; if (!secrets?.values?.length || !secrets.values[0]?.length) { // Generate a new secret const randomBytes = crypto.getRandomValues(new Uint8Array(32)); secretBase64 = arrayBufferToBase64(randomBytes); - + // Store the new secret await platformService.dbExec( `INSERT INTO secret (id, secretBase64) VALUES (1, ?)`, - [secretBase64] + [secretBase64], ); } else { secretBase64 = secrets.values[0][0] as string; } - + const secret = base64ToArrayBuffer(secretBase64); const encryptedIdentity = await simpleEncrypt(identity, secret); const encryptedMnemonic = await simpleEncrypt(mnemonic, secret); const encryptedIdentityBase64 = arrayBufferToBase64(encryptedIdentity); const encryptedMnemonicBase64 = arrayBufferToBase64(encryptedMnemonic); - + await platformService.dbExec( `INSERT INTO accounts (dateCreated, derivationPath, did, identityEncrBase64, mnemonicEncrBase64, publicKeyHex) VALUES (?, ?, ?, ?, ?, ?)`, diff --git a/src/services/platforms/ElectronPlatformService.ts b/src/services/platforms/ElectronPlatformService.ts index d7b43e58..3494f809 100644 --- a/src/services/platforms/ElectronPlatformService.ts +++ b/src/services/platforms/ElectronPlatformService.ts @@ -93,13 +93,17 @@ export class ElectronPlatformService implements PlatformService { private isConnectionOpen = false; private operationQueue: Promise = Promise.resolve(); private queueLock = false; - private connectionState: 'disconnected' | 'connecting' | 'connected' | 'error' = 'disconnected'; + private connectionState: + | "disconnected" + | "connecting" + | "connected" + | "error" = "disconnected"; private connectionPromise: Promise | null = null; // SQLite initialization configuration private static readonly SQLITE_CONFIG = { INITIALIZATION: { - TIMEOUT_MS: 5000, // Increase timeout to 5 seconds + TIMEOUT_MS: 5000, // Increase timeout to 5 seconds RETRY_ATTEMPTS: 3, RETRY_DELAY_MS: 1000, READY_CHECK_INTERVAL_MS: 100, @@ -124,18 +128,25 @@ export class ElectronPlatformService implements PlatformService { } // Check if SQLite is already available - const isAvailable = await window.electron.ipcRenderer.invoke("sqlite-is-available"); + const isAvailable = await window.electron.ipcRenderer.invoke( + "sqlite-is-available", + ); if (!isAvailable) { return false; } // Check if database is already open - const isOpen = await window.electron.ipcRenderer.invoke("sqlite-is-db-open", { - database: this.dbName, - }); + const isOpen = await window.electron.ipcRenderer.invoke( + "sqlite-is-db-open", + { + database: this.dbName, + }, + ); if (isOpen) { - logger.info("[ElectronPlatformService] SQLite is already ready and database is open"); + logger.info( + "[ElectronPlatformService] SQLite is already ready and database is open", + ); sqliteInitState.isReady = true; sqliteInitState.isInitializing = false; sqliteInitState.lastReadyCheck = Date.now(); @@ -144,7 +155,10 @@ export class ElectronPlatformService implements PlatformService { return false; } catch (error) { - logger.warn("[ElectronPlatformService] Error checking existing readiness:", error); + logger.warn( + "[ElectronPlatformService] Error checking existing readiness:", + error, + ); return false; } }; @@ -161,8 +175,14 @@ export class ElectronPlatformService implements PlatformService { // If someone else is initializing, wait for them if (sqliteInitState.isInitializing) { - logger.info("[ElectronPlatformService] Another initialization in progress, waiting..."); - setTimeout(attemptInitialization, ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION.READY_CHECK_INTERVAL_MS); + logger.info( + "[ElectronPlatformService] Another initialization in progress, waiting...", + ); + setTimeout( + attemptInitialization, + ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION + .READY_CHECK_INTERVAL_MS, + ); return; } @@ -171,7 +191,9 @@ export class ElectronPlatformService implements PlatformService { // Verify Electron API exposure first await verifyElectronAPI(); - logger.info("[ElectronPlatformService] Electron API verification successful"); + logger.info( + "[ElectronPlatformService] Electron API verification successful", + ); if (!window.electron?.ipcRenderer) { logger.warn("[ElectronPlatformService] IPC renderer not available"); @@ -182,12 +204,16 @@ export class ElectronPlatformService implements PlatformService { // Set up ready signal handler BEFORE setting timeout window.electron.ipcRenderer.once("sqlite-ready", async () => { cleanup(); - logger.info("[ElectronPlatformService] Received SQLite ready signal"); + logger.info( + "[ElectronPlatformService] Received SQLite ready signal", + ); try { // Test SQLite operations after receiving ready signal await testSQLiteOperations(); - logger.info("[ElectronPlatformService] SQLite operations test successful"); + logger.info( + "[ElectronPlatformService] SQLite operations test successful", + ); this.isInitialized = true; sqliteInitState.isReady = true; @@ -197,43 +223,67 @@ export class ElectronPlatformService implements PlatformService { } catch (error) { sqliteInitState.error = error as Error; sqliteInitState.isInitializing = false; - logger.error("[ElectronPlatformService] SQLite operations test failed:", error); + logger.error( + "[ElectronPlatformService] SQLite operations test failed:", + error, + ); reject(error); } }); // Set up error handler - window.electron.ipcRenderer.once("database-status", (...args: unknown[]) => { - cleanup(); - const status = args[0] as { status: string; error?: string }; - if (status.status === "error") { - this.dbFatalError = true; - sqliteInitState.error = new Error(status.error || "Database initialization failed"); - sqliteInitState.isInitializing = false; - reject(sqliteInitState.error); - } - }); + window.electron.ipcRenderer.once( + "database-status", + (...args: unknown[]) => { + cleanup(); + const status = args[0] as { status: string; error?: string }; + if (status.status === "error") { + this.dbFatalError = true; + sqliteInitState.error = new Error( + status.error || "Database initialization failed", + ); + sqliteInitState.isInitializing = false; + reject(sqliteInitState.error); + } + }, + ); // Set timeout for this attempt AFTER setting up handlers this.initializationTimeout = setTimeout(() => { - if (retryCount < ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION.RETRY_ATTEMPTS) { + if ( + retryCount < + ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION + .RETRY_ATTEMPTS + ) { retryCount++; - logger.warn(`[ElectronPlatformService] SQLite initialization attempt ${retryCount} timed out, retrying...`); - setTimeout(attemptInitialization, ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION.RETRY_DELAY_MS); + logger.warn( + `[ElectronPlatformService] SQLite initialization attempt ${retryCount} timed out, retrying...`, + ); + setTimeout( + attemptInitialization, + ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION + .RETRY_DELAY_MS, + ); } else { cleanup(); sqliteInitState.isInitializing = false; - sqliteInitState.error = new Error("SQLite initialization timeout after all retries"); - logger.error("[ElectronPlatformService] SQLite initialization failed after all retries"); + sqliteInitState.error = new Error( + "SQLite initialization timeout after all retries", + ); + logger.error( + "[ElectronPlatformService] SQLite initialization failed after all retries", + ); reject(sqliteInitState.error); } }, ElectronPlatformService.SQLITE_CONFIG.INITIALIZATION.TIMEOUT_MS); - } catch (error) { cleanup(); sqliteInitState.error = error as Error; sqliteInitState.isInitializing = false; - logger.error("[ElectronPlatformService] Initialization failed:", error); + logger.error( + "[ElectronPlatformService] Initialization failed:", + error, + ); reject(error); } }; @@ -254,30 +304,30 @@ export class ElectronPlatformService implements PlatformService { // Use IPC bridge with specific methods this.sqlite = { createConnection: async (options) => { - await window.electron.ipcRenderer.invoke('sqlite-create-connection', { + await window.electron.ipcRenderer.invoke("sqlite-create-connection", { ...options, - database: this.dbName + database: this.dbName, }); }, query: async (options) => { - return await window.electron.ipcRenderer.invoke('sqlite-query', { + return await window.electron.ipcRenderer.invoke("sqlite-query", { ...options, - database: this.dbName + database: this.dbName, }); }, run: async (options) => { - return await window.electron.ipcRenderer.invoke('sqlite-run', { + return await window.electron.ipcRenderer.invoke("sqlite-run", { ...options, - database: this.dbName + database: this.dbName, }); }, execute: async (options) => { - await window.electron.ipcRenderer.invoke('sqlite-execute', { + await window.electron.ipcRenderer.invoke("sqlite-execute", { ...options, database: this.dbName, - statements: [{ statement: options.statements }] + statements: [{ statement: options.statements }], }); - } + }, } as SQLiteOperations; // Create the connection (idempotent) @@ -537,7 +587,7 @@ export class ElectronPlatformService implements PlatformService { try { // Acquire lock while (this.queueLock) { - await new Promise(resolve => setTimeout(resolve, 50)); + await new Promise((resolve) => setTimeout(resolve, 50)); } this.queueLock = true; @@ -562,20 +612,20 @@ export class ElectronPlatformService implements PlatformService { } // If we're already connected, return immediately - if (this.connectionState === 'connected') { + if (this.connectionState === "connected") { return Promise.resolve(); } // Create new connection promise this.connectionPromise = (async () => { try { - this.connectionState = 'connecting'; + this.connectionState = "connecting"; // Wait for any existing operations await this.operationQueue; // Create connection - await window.electron!.ipcRenderer.invoke('sqlite-create-connection', { + await window.electron!.ipcRenderer.invoke("sqlite-create-connection", { database: this.dbName, encrypted: false, mode: "no-encryption", @@ -583,23 +633,26 @@ export class ElectronPlatformService implements PlatformService { logger.debug("[ElectronPlatformService] Database connection created"); // Open database - await window.electron!.ipcRenderer.invoke('sqlite-open', { - database: this.dbName + await window.electron!.ipcRenderer.invoke("sqlite-open", { + database: this.dbName, }); logger.debug("[ElectronPlatformService] Database opened"); // Verify database is open - const isOpen = await window.electron!.ipcRenderer.invoke('sqlite-is-db-open', { - database: this.dbName - }); + const isOpen = await window.electron!.ipcRenderer.invoke( + "sqlite-is-db-open", + { + database: this.dbName, + }, + ); if (!isOpen) { - throw new Error('[ElectronPlatformService] Database failed to open'); + throw new Error("[ElectronPlatformService] Database failed to open"); } - this.connectionState = 'connected'; + this.connectionState = "connected"; this.isConnectionOpen = true; } catch (error) { - this.connectionState = 'error'; + this.connectionState = "error"; this.connectionPromise = null; throw error; } @@ -609,28 +662,31 @@ export class ElectronPlatformService implements PlatformService { } private async releaseConnection(): Promise { - if (this.connectionState !== 'connected') { + if (this.connectionState !== "connected") { return; } try { // Close database - await window.electron!.ipcRenderer.invoke('sqlite-close', { - database: this.dbName + await window.electron!.ipcRenderer.invoke("sqlite-close", { + database: this.dbName, }); logger.debug("[ElectronPlatformService] Database closed"); // Close connection - await window.electron!.ipcRenderer.invoke('sqlite-close-connection', { - database: this.dbName + await window.electron!.ipcRenderer.invoke("sqlite-close-connection", { + database: this.dbName, }); logger.debug("[ElectronPlatformService] Database connection closed"); - this.connectionState = 'disconnected'; + this.connectionState = "disconnected"; this.isConnectionOpen = false; } catch (error) { - logger.error("[ElectronPlatformService] Failed to close database:", error); - this.connectionState = 'error'; + logger.error( + "[ElectronPlatformService] Failed to close database:", + error, + ); + this.connectionState = "error"; } finally { this.connectionPromise = null; } @@ -650,7 +706,9 @@ export class ElectronPlatformService implements PlatformService { params: unknown[] = [], ): Promise> { if (this.dbFatalError) { - throw new Error("Database is in a fatal error state. Please restart the app."); + throw new Error( + "Database is in a fatal error state. Please restart the app.", + ); } return this.enqueueOperation(async () => { @@ -659,23 +717,33 @@ export class ElectronPlatformService implements PlatformService { await this.getConnection(); // 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"); + 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) + values: (result.values || []).map( + (row: Record) => row as T, + ), }; return processedResult; } catch (error) { - logger.error("[ElectronPlatformService] [dbQuery] Query failed:", error); + logger.error( + "[ElectronPlatformService] [dbQuery] Query failed:", + error, + ); throw error; } finally { // Release connection after query @@ -692,7 +760,9 @@ export class ElectronPlatformService implements PlatformService { params?: unknown[], ): Promise<{ changes: number; lastId?: number }> { if (this.dbFatalError) { - throw new Error("Database is in a fatal error state. Please restart the app."); + throw new Error( + "Database is in a fatal error state. Please restart the app.", + ); } return this.enqueueOperation(async () => { @@ -701,12 +771,17 @@ export class ElectronPlatformService implements PlatformService { await this.getConnection(); // Execute query - const result = await window.electron!.ipcRenderer.invoke('sqlite-run', { - database: this.dbName, - statement: sql, - values: params - }) as SQLiteQueryResult; - logger.debug("[ElectronPlatformService] [dbExec] Query executed successfully"); + const result = (await window.electron!.ipcRenderer.invoke( + "sqlite-run", + { + database: this.dbName, + statement: sql, + values: params, + }, + )) as SQLiteQueryResult; + logger.debug( + "[ElectronPlatformService] [dbExec] Query executed successfully", + ); return { changes: result.changes?.changes || 0, diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 56265545..96b1d83e 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -1889,7 +1889,7 @@ export default class AccountViewView extends Vue { logger.debug("[AccountViewView] Starting API server update", { current: this.apiServer, new: this.apiServerInput, - component: 'AccountViewView' + component: "AccountViewView", }); try { @@ -1909,29 +1909,33 @@ export default class AccountViewView extends Vue { } this.apiServer = this.apiServerInput; - logger.debug("[AccountViewView] Local state updated", { apiServer: this.apiServer }); + logger.debug("[AccountViewView] Local state updated", { + apiServer: this.apiServer, + }); // Verify the update const settings = await databaseUtil.retrieveSettingsForActiveAccount(); logger.debug("[AccountViewView] Settings verification", { retrieved: settings.apiServer, expected: this.apiServerInput, - match: settings.apiServer === this.apiServerInput + match: settings.apiServer === this.apiServerInput, }); // Show success notification - this.$notify({ - group: "alert", - type: "success", - title: "API Server Updated", - text: "API server settings saved successfully.", - }, 3000); - + this.$notify( + { + group: "alert", + type: "success", + title: "API Server Updated", + text: "API server settings saved successfully.", + }, + 3000, + ); } catch (error) { logger.error("[AccountViewView] API server update failed", { error, current: this.apiServer, - attempted: this.apiServerInput + attempted: this.apiServerInput, }); this.$notify( {