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
|
||||
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.
|
||||
|
||||
// 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(
|
||||
accountDid: string,
|
||||
settingsChanges: Settings,
|
||||
|
||||
@@ -539,20 +539,23 @@ export const generateSaveAndActivateIdentity = async (): Promise<string> => {
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@@ -618,6 +618,7 @@
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<div v-if="showContactImport()" class="mt-4">
|
||||
<!-- Bulk import has an error
|
||||
<div class="flex justify-center">
|
||||
<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"
|
||||
@@ -628,6 +629,7 @@
|
||||
(which doesn't include Identifier Data)
|
||||
</button>
|
||||
</div>
|
||||
-->
|
||||
<div class="flex justify-center">
|
||||
<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"
|
||||
@@ -1623,7 +1625,7 @@ export default class AccountViewView extends Vue {
|
||||
async submitImportFile() {
|
||||
if (inputImportFileNameRef.value != null) {
|
||||
await db.delete()
|
||||
.then(async () => {
|
||||
.then(async (x) => {
|
||||
await Dexie.import(inputImportFileNameRef.value as Blob, {
|
||||
progressCallback: this.progressCallback,
|
||||
})
|
||||
@@ -1635,9 +1637,9 @@ export default class AccountViewView extends Vue {
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
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"
|
||||
></font-awesome>
|
||||
</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>
|
||||
<span class="text-xl">Created!</span>
|
||||
<font-awesome
|
||||
@@ -62,14 +69,24 @@ import QuickNav from "../components/QuickNav.vue";
|
||||
@Component({ components: { QuickNav } })
|
||||
export default class NewIdentifierView extends Vue {
|
||||
loading = true;
|
||||
hitError = false;
|
||||
$router!: Router;
|
||||
|
||||
async mounted() {
|
||||
await generateSaveAndActivateIdentity();
|
||||
this.loading = false;
|
||||
setTimeout(() => {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user