From 1129a13e20a2512075f52366bf9bf98d0ab6bffe Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Fri, 23 May 2025 12:35:16 -0600 Subject: [PATCH] add more error handling and messaging when there are bad DB errors --- src/db/index.ts | 81 +++++++++++++++++++++++++++++---- src/libs/util.ts | 29 ++++++------ src/views/AccountViewView.vue | 8 ++-- src/views/NewIdentifierView.vue | 27 +++++++++-- 4 files changed, 114 insertions(+), 31 deletions(-) diff --git a/src/db/index.ts b/src/db/index.ts index 839094c3..444a98db 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -87,9 +87,79 @@ const DEFAULT_SETTINGS: Settings = { // Event handler to initialize the non-sensitive database with default settings db.on("populate", async () => { - await db.settings.add(DEFAULT_SETTINGS); + try { + await db.settings.add(DEFAULT_SETTINGS); + } catch (error) { + console.error("Error populating the database with default settings:", error); + } }); +// Helper function to safely open the database with retries +async function safeOpenDatabase(retries = 1, delay = 500): Promise { + // console.log("Starting safeOpenDatabase with retries:", retries); + for (let i = 0; i < retries; i++) { + try { + // console.log(`Attempt ${i + 1}: Checking if database is open...`); + if (!db.isOpen()) { + // console.log(`Attempt ${i + 1}: Database is closed, attempting to open...`); + + // Create a promise that rejects after 5 seconds + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error('Database open timed out')), 500); + }); + + // Race between the open operation and the timeout + const openPromise = db.open(); + // console.log(`Attempt ${i + 1}: Waiting for db.open() promise...`); + await Promise.race([openPromise, timeoutPromise]); + + // If we get here, the open succeeded + // console.log(`Attempt ${i + 1}: Database opened successfully`); + return; + } + // console.log(`Attempt ${i + 1}: Database was already open`); + return; + } catch (error) { + console.error(`Attempt ${i + 1}: Database open failed:`, error); + if (i < retries - 1) { + console.log(`Attempt ${i + 1}: Waiting ${delay}ms before retry...`); + await new Promise(resolve => setTimeout(resolve, delay)); + } else { + throw error; + } + } + } +} + +export async function updateDefaultSettings( + settingsChanges: Settings, +): Promise { + delete settingsChanges.accountDid; // just in case + // ensure there is no "id" that would override the key + delete settingsChanges.id; + try { + try { + // console.log("Database state before open:", db.isOpen() ? "open" : "closed"); + // console.log("Database name:", db.name); + // console.log("Database version:", db.verno); + await safeOpenDatabase(); + } catch (openError: unknown) { + console.error("Failed to open database:", openError); + const errorMessage = openError instanceof Error ? openError.message : String(openError); + throw new Error(`Database connection failed: ${errorMessage}. Please try again or restart the app.`); + } + const result = await db.settings.update(MASTER_SETTINGS_KEY, settingsChanges); + return result; + } catch (error) { + console.error("Error updating default settings:", error); + if (error instanceof Error) { + throw error; // Re-throw if it's already an Error with a message + } else { + throw new Error(`Failed to update settings: ${error}`); + } + } +} + // Manage the encryption key. // It's not really secure to maintain the secret next to the user's data. @@ -183,15 +253,6 @@ export async function retrieveSettingsForActiveAccount(): Promise { } } -export async function updateDefaultSettings( - settingsChanges: Settings, -): Promise { - delete settingsChanges.accountDid; // just in case - // ensure there is no "id" that would override the key - delete settingsChanges.id; - await db.settings.update(MASTER_SETTINGS_KEY, settingsChanges); -} - export async function updateAccountSettings( accountDid: string, settingsChanges: Settings, diff --git a/src/libs/util.ts b/src/libs/util.ts index 1d2fa031..b98f747c 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -539,20 +539,23 @@ export const generateSaveAndActivateIdentity = async (): Promise => { const identity = JSON.stringify(newId); // one of the few times we use accountsDBPromise directly; try to avoid more usage - const accountsDB = await accountsDBPromise; - await accountsDB.accounts.add({ - dateCreated: new Date().toISOString(), - derivationPath: derivationPath, - did: newId.did, - identity: identity, - mnemonic: mnemonic, - publicKeyHex: newId.keys[0].publicKeyHex, - }); - - await updateDefaultSettings({ activeDid: newId.did }); - //console.log("Updated default settings in util"); + try { + const accountsDB = await accountsDBPromise; + await accountsDB.accounts.add({ + dateCreated: new Date().toISOString(), + derivationPath: derivationPath, + did: newId.did, + identity: identity, + mnemonic: mnemonic, + publicKeyHex: newId.keys[0].publicKeyHex, + }); + + await updateDefaultSettings({ activeDid: newId.did }); + } catch (error) { + console.error("Failed to update default settings:", error); + throw new Error("Failed to set default settings. Please try again or restart the app."); + } await updateAccountSettings(newId.did, { isRegistered: false }); - return newId.did; }; diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index c944e847..e2d7b0e2 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -618,6 +618,7 @@ leave-to-class="opacity-0" >
+
+
+ Error Creating Identity + +

+ Try fully restarting the app. If that doesn't work, back up all data (identities and other data) and reinstall the app. +

+
Created! { - this.$router.push({ name: "home" }); - }, 1000); + this.loading = true; + this.hitError = false; + generateSaveAndActivateIdentity() + .then(() => { + this.loading = false; + setTimeout(() => { + this.$router.push({ name: "home" }); + }, 1000); + }) + .catch((error) => { + this.loading = false; + this.hitError = true; + console.error('Failed to generate identity:', error); + }); } }