forked from trent_larson/crowd-funder-for-time-pwa
add more error handling and messaging when there are bad DB errors
This commit is contained in:
@@ -87,9 +87,79 @@ const DEFAULT_SETTINGS: Settings = {
|
|||||||
|
|
||||||
// Event handler to initialize the non-sensitive database with default settings
|
// Event handler to initialize the non-sensitive database with default settings
|
||||||
db.on("populate", async () => {
|
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<void> {
|
||||||
|
// 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<number> {
|
||||||
|
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.
|
// Manage the encryption key.
|
||||||
|
|
||||||
// It's not really secure to maintain the secret next to the user's data.
|
// It's not really secure to maintain the secret next to the user's data.
|
||||||
@@ -183,15 +253,6 @@ export async function retrieveSettingsForActiveAccount(): Promise<Settings> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateDefaultSettings(
|
|
||||||
settingsChanges: Settings,
|
|
||||||
): Promise<void> {
|
|
||||||
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(
|
export async function updateAccountSettings(
|
||||||
accountDid: string,
|
accountDid: string,
|
||||||
settingsChanges: Settings,
|
settingsChanges: Settings,
|
||||||
|
|||||||
@@ -539,20 +539,23 @@ export const generateSaveAndActivateIdentity = async (): Promise<string> => {
|
|||||||
const identity = JSON.stringify(newId);
|
const identity = JSON.stringify(newId);
|
||||||
|
|
||||||
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
// one of the few times we use accountsDBPromise directly; try to avoid more usage
|
||||||
const accountsDB = await accountsDBPromise;
|
try {
|
||||||
await accountsDB.accounts.add({
|
const accountsDB = await accountsDBPromise;
|
||||||
dateCreated: new Date().toISOString(),
|
await accountsDB.accounts.add({
|
||||||
derivationPath: derivationPath,
|
dateCreated: new Date().toISOString(),
|
||||||
did: newId.did,
|
derivationPath: derivationPath,
|
||||||
identity: identity,
|
did: newId.did,
|
||||||
mnemonic: mnemonic,
|
identity: identity,
|
||||||
publicKeyHex: newId.keys[0].publicKeyHex,
|
mnemonic: mnemonic,
|
||||||
});
|
publicKeyHex: newId.keys[0].publicKeyHex,
|
||||||
|
});
|
||||||
await updateDefaultSettings({ activeDid: newId.did });
|
|
||||||
//console.log("Updated default settings in util");
|
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 });
|
await updateAccountSettings(newId.did, { isRegistered: false });
|
||||||
|
|
||||||
return newId.did;
|
return newId.did;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -618,6 +618,7 @@
|
|||||||
leave-to-class="opacity-0"
|
leave-to-class="opacity-0"
|
||||||
>
|
>
|
||||||
<div v-if="showContactImport()" class="mt-4">
|
<div v-if="showContactImport()" class="mt-4">
|
||||||
|
<!-- Bulk import has an error
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<button
|
<button
|
||||||
class="block text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-6"
|
class="block text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-6"
|
||||||
@@ -628,6 +629,7 @@
|
|||||||
(which doesn't include Identifier Data)
|
(which doesn't include Identifier Data)
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<button
|
<button
|
||||||
class="block text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-6"
|
class="block text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-6"
|
||||||
@@ -1623,7 +1625,7 @@ export default class AccountViewView extends Vue {
|
|||||||
async submitImportFile() {
|
async submitImportFile() {
|
||||||
if (inputImportFileNameRef.value != null) {
|
if (inputImportFileNameRef.value != null) {
|
||||||
await db.delete()
|
await db.delete()
|
||||||
.then(async () => {
|
.then(async (x) => {
|
||||||
await Dexie.import(inputImportFileNameRef.value as Blob, {
|
await Dexie.import(inputImportFileNameRef.value as Blob, {
|
||||||
progressCallback: this.progressCallback,
|
progressCallback: this.progressCallback,
|
||||||
})
|
})
|
||||||
@@ -1635,9 +1637,9 @@ export default class AccountViewView extends Vue {
|
|||||||
group: "alert",
|
group: "alert",
|
||||||
type: "danger",
|
type: "danger",
|
||||||
title: "Error Importing",
|
title: "Error Importing",
|
||||||
text: "There was an error importing that file. Your contacts may have been affected, so you may want to use the other import method.",
|
text: "There was an error in the import. Your identities and contacts may have been affected, so you may have to restore your identifier and use the contact import method.",
|
||||||
},
|
},
|
||||||
5000,
|
-1,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,13 @@
|
|||||||
size="128"
|
size="128"
|
||||||
></font-awesome>
|
></font-awesome>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="hitError">
|
||||||
|
<span class="text-xl">Error Creating Identity</span>
|
||||||
|
<font-awesome icon="exclamation-triangle" class="fa-fw text-red-500 ml-2"></font-awesome>
|
||||||
|
<p class="text-sm text-gray-500">
|
||||||
|
Try fully restarting the app. If that doesn't work, back up all data (identities and other data) and reinstall the app.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<span class="text-xl">Created!</span>
|
<span class="text-xl">Created!</span>
|
||||||
<font-awesome
|
<font-awesome
|
||||||
@@ -62,14 +69,24 @@ import QuickNav from "../components/QuickNav.vue";
|
|||||||
@Component({ components: { QuickNav } })
|
@Component({ components: { QuickNav } })
|
||||||
export default class NewIdentifierView extends Vue {
|
export default class NewIdentifierView extends Vue {
|
||||||
loading = true;
|
loading = true;
|
||||||
|
hitError = false;
|
||||||
$router!: Router;
|
$router!: Router;
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await generateSaveAndActivateIdentity();
|
this.loading = true;
|
||||||
this.loading = false;
|
this.hitError = false;
|
||||||
setTimeout(() => {
|
generateSaveAndActivateIdentity()
|
||||||
this.$router.push({ name: "home" });
|
.then(() => {
|
||||||
}, 1000);
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user