forked from jsnbuchanan/crowd-funder-for-time-pwa
WIP: restore database migration system and improve error handling
- Restore runMigrations functionality for database schema migrations - Remove indexedDBMigrationService.ts (was for IndexedDB to SQLite migration) - Recreate migrationService.ts and db-sql/migration.ts for schema management - Add proper TypeScript error handling with type guards in AccountViewView - Fix CreateAndSubmitClaimResult property access in QuickActionBvcBeginView - Remove LeafletMouseEvent from Vue components array (it's a type, not component) - Add null check for UserNameDialog callback to prevent undefined assignment - Implement extractErrorMessage helper function for consistent error handling - Update router to remove database-migration route The migration system now properly handles database schema evolution across app versions, while the IndexedDB to SQLite migration service has been removed as it was specific to that one-time migration.
This commit is contained in:
@@ -68,9 +68,9 @@
|
||||
class="inline-block text-md uppercase 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-4 py-2 rounded-md"
|
||||
@click="
|
||||
() =>
|
||||
($refs.userNameDialog as UserNameDialog).open(
|
||||
(name) => (givenName = name),
|
||||
)
|
||||
($refs.userNameDialog as UserNameDialog).open((name) => {
|
||||
if (name) givenName = name;
|
||||
})
|
||||
"
|
||||
>
|
||||
Set Your Name
|
||||
@@ -980,7 +980,6 @@ import "leaflet/dist/leaflet.css";
|
||||
|
||||
import { AxiosError } from "axios";
|
||||
import { Buffer } from "buffer/";
|
||||
import Dexie from "dexie";
|
||||
import "dexie-export-import";
|
||||
// @ts-expect-error - they aren't exporting it but it's there
|
||||
import { ImportProgress } from "dexie-export-import";
|
||||
@@ -1008,29 +1007,22 @@ import {
|
||||
DEFAULT_PUSH_SERVER,
|
||||
IMAGE_TYPE_PROFILE,
|
||||
NotificationIface,
|
||||
USE_DEXIE_DB,
|
||||
} from "../constants/app";
|
||||
import {
|
||||
db,
|
||||
logConsoleAndDb,
|
||||
retrieveSettingsForActiveAccount,
|
||||
updateAccountSettings,
|
||||
} from "../db/index";
|
||||
import { Contact } from "../db/tables/contacts";
|
||||
import {
|
||||
DEFAULT_PASSKEY_EXPIRATION_MINUTES,
|
||||
MASTER_SETTINGS_KEY,
|
||||
} from "../db/tables/settings";
|
||||
import { DEFAULT_PASSKEY_EXPIRATION_MINUTES } from "../db/tables/settings";
|
||||
import * as databaseUtil from "../db/databaseUtil";
|
||||
import {
|
||||
clearPasskeyToken,
|
||||
EndorserRateLimits,
|
||||
ImageRateLimits,
|
||||
ErrorResponse,
|
||||
} from "../interfaces";
|
||||
|
||||
import {
|
||||
clearPasskeyToken,
|
||||
errorStringForLog,
|
||||
fetchEndorserRateLimits,
|
||||
fetchImageRateLimits,
|
||||
getHeaders,
|
||||
ImageRateLimits,
|
||||
tokenExpiryTimeDescription,
|
||||
} from "../libs/endorserServer";
|
||||
import {
|
||||
@@ -1043,11 +1035,39 @@ import { logger } from "../utils/logger";
|
||||
|
||||
const inputImportFileNameRef = ref<Blob>();
|
||||
|
||||
// Type guard for API errors
|
||||
function isApiError(error: unknown): error is {
|
||||
response?: { data?: { error?: { message?: string } | string } };
|
||||
} {
|
||||
return typeof error === "object" && error !== null && "response" in error;
|
||||
}
|
||||
|
||||
// Type guard for standard errors
|
||||
function isError(error: unknown): error is Error {
|
||||
return error instanceof Error;
|
||||
}
|
||||
|
||||
// Helper function to extract error message
|
||||
function extractErrorMessage(error: unknown): string {
|
||||
if (isApiError(error)) {
|
||||
const apiError = error.response?.data?.error;
|
||||
if (typeof apiError === "string") {
|
||||
return apiError;
|
||||
}
|
||||
if (typeof apiError === "object" && apiError?.message) {
|
||||
return apiError.message;
|
||||
}
|
||||
}
|
||||
if (isError(error)) {
|
||||
return error.message;
|
||||
}
|
||||
return "An unknown error occurred";
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
EntityIcon,
|
||||
ImageMethodDialog,
|
||||
LeafletMouseEvent,
|
||||
LMap,
|
||||
LMarker,
|
||||
LTileLayer,
|
||||
@@ -1158,11 +1178,6 @@ export default class AccountViewView extends Vue {
|
||||
databaseUtil.logConsoleAndDb(
|
||||
"Error loading profile: " + errorStringForLog(error),
|
||||
);
|
||||
if (USE_DEXIE_DB) {
|
||||
logConsoleAndDb(
|
||||
"Error loading profile: " + errorStringForLog(error),
|
||||
);
|
||||
}
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -1238,11 +1253,7 @@ export default class AccountViewView extends Vue {
|
||||
* Initializes component state with values from the database or defaults.
|
||||
*/
|
||||
async initializeState() {
|
||||
let settings = await databaseUtil.retrieveSettingsForActiveAccount();
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
settings = await retrieveSettingsForActiveAccount();
|
||||
}
|
||||
const settings = await databaseUtil.retrieveSettingsForActiveAccount();
|
||||
|
||||
this.activeDid = settings.activeDid || "";
|
||||
this.apiServer = settings.apiServer || "";
|
||||
@@ -1288,12 +1299,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
showContactGivesInline: this.showContactGives,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
showContactGivesInline: this.showContactGives,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async toggleShowGeneralAdvanced() {
|
||||
@@ -1301,12 +1306,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
showGeneralAdvanced: this.showGeneralAdvanced,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
showGeneralAdvanced: this.showGeneralAdvanced,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async toggleProdWarning() {
|
||||
@@ -1314,12 +1313,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
warnIfProdServer: this.warnIfProdServer,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
warnIfProdServer: this.warnIfProdServer,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async toggleTestWarning() {
|
||||
@@ -1327,12 +1320,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
warnIfTestServer: this.warnIfTestServer,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
warnIfTestServer: this.warnIfTestServer,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async toggleShowShortcutBvc() {
|
||||
@@ -1340,12 +1327,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
showShortcutBvc: this.showShortcutBvc,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
showShortcutBvc: this.showShortcutBvc,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
readableDate(timeStr: string) {
|
||||
@@ -1404,11 +1385,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
notifyingNewActivityTime: timeText,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
notifyingNewActivityTime: timeText,
|
||||
});
|
||||
}
|
||||
this.notifyingNewActivity = true;
|
||||
this.notifyingNewActivityTime = timeText;
|
||||
}
|
||||
@@ -1425,11 +1401,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
notifyingNewActivityTime: "",
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
notifyingNewActivityTime: "",
|
||||
});
|
||||
}
|
||||
this.notifyingNewActivity = false;
|
||||
this.notifyingNewActivityTime = "";
|
||||
}
|
||||
@@ -1475,12 +1446,6 @@ export default class AccountViewView extends Vue {
|
||||
notifyingReminderMessage: message,
|
||||
notifyingReminderTime: timeText,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
notifyingReminderMessage: message,
|
||||
notifyingReminderTime: timeText,
|
||||
});
|
||||
}
|
||||
this.notifyingReminder = true;
|
||||
this.notifyingReminderMessage = message || "";
|
||||
this.notifyingReminderTime = timeText;
|
||||
@@ -1500,12 +1465,6 @@ export default class AccountViewView extends Vue {
|
||||
notifyingReminderMessage: "",
|
||||
notifyingReminderTime: "",
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
notifyingReminderMessage: "",
|
||||
notifyingReminderTime: "",
|
||||
});
|
||||
}
|
||||
this.notifyingReminder = false;
|
||||
this.notifyingReminderMessage = "";
|
||||
this.notifyingReminderTime = "";
|
||||
@@ -1522,12 +1481,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
hideRegisterPromptOnNewContact: newSetting,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
hideRegisterPromptOnNewContact: newSetting,
|
||||
});
|
||||
}
|
||||
this.hideRegisterPromptOnNewContact = newSetting;
|
||||
}
|
||||
|
||||
@@ -1535,12 +1488,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
passkeyExpirationMinutes: this.passkeyExpirationMinutes,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
passkeyExpirationMinutes: this.passkeyExpirationMinutes,
|
||||
});
|
||||
}
|
||||
clearPasskeyToken();
|
||||
this.passkeyExpirationDescription = tokenExpiryTimeDescription();
|
||||
}
|
||||
@@ -1552,14 +1499,6 @@ export default class AccountViewView extends Vue {
|
||||
notifyingReminderMessage: "",
|
||||
notifyingReminderTime: "",
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
notifyingNewActivityTime: "",
|
||||
notifyingReminderMessage: "",
|
||||
notifyingReminderTime: "",
|
||||
});
|
||||
}
|
||||
this.notifyingNewActivity = false;
|
||||
this.notifyingNewActivityTime = "";
|
||||
this.notifyingReminder = false;
|
||||
@@ -1599,9 +1538,7 @@ export default class AccountViewView extends Vue {
|
||||
* @returns {Promise<Blob>} The generated blob object.
|
||||
*/
|
||||
private async generateDatabaseBlob(): Promise<Blob> {
|
||||
if (USE_DEXIE_DB) {
|
||||
return await db.export({ prettyJson: true });
|
||||
}
|
||||
// TODO: Implement this for SQLite
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
@@ -1704,30 +1641,7 @@ export default class AccountViewView extends Vue {
|
||||
*/
|
||||
async submitImportFile() {
|
||||
if (inputImportFileNameRef.value != null) {
|
||||
if (USE_DEXIE_DB) {
|
||||
await db
|
||||
.delete()
|
||||
.then(async () => {
|
||||
// BulkError: settings.bulkAdd(): 1 of 21 operations failed. Errors: ConstraintError: Key already exists in the object store.
|
||||
await Dexie.import(inputImportFileNameRef.value as Blob, {
|
||||
progressCallback: this.progressCallback,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error("Error importing file:", error);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error Importing",
|
||||
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.",
|
||||
},
|
||||
-1,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
// TODO: implement this for SQLite
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1818,12 +1732,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDidSpecificSettings(did, {
|
||||
isRegistered: true,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
isRegistered: true,
|
||||
});
|
||||
}
|
||||
this.isRegistered = true;
|
||||
} catch (err) {
|
||||
logger.error("Got an error updating settings:", err);
|
||||
@@ -1886,12 +1794,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
apiServer: this.apiServerInput,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
apiServer: this.apiServerInput,
|
||||
});
|
||||
}
|
||||
this.apiServer = this.apiServerInput;
|
||||
}
|
||||
|
||||
@@ -1899,12 +1801,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
partnerApiServer: this.partnerApiServerInput,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
partnerApiServer: this.partnerApiServerInput,
|
||||
});
|
||||
}
|
||||
this.partnerApiServer = this.partnerApiServerInput;
|
||||
}
|
||||
|
||||
@@ -1912,12 +1808,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
webPushServer: this.webPushServerInput,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
webPushServer: this.webPushServerInput,
|
||||
});
|
||||
}
|
||||
this.webPushServer = this.webPushServerInput;
|
||||
this.$notify(
|
||||
{
|
||||
@@ -1936,12 +1826,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
profileImageUrl: imgUrl,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
profileImageUrl: imgUrl,
|
||||
});
|
||||
}
|
||||
this.profileImageUrl = imgUrl;
|
||||
//console.log("Got image URL:", imgUrl);
|
||||
},
|
||||
@@ -2005,12 +1889,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDefaultSettings({
|
||||
profileImageUrl: undefined,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await db.open();
|
||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||
profileImageUrl: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
this.profileImageUrl = undefined;
|
||||
} catch (error) {
|
||||
@@ -2022,11 +1900,6 @@ export default class AccountViewView extends Vue {
|
||||
await databaseUtil.updateDidSpecificSettings(this.activeDid, {
|
||||
profileImageUrl: undefined,
|
||||
});
|
||||
if (USE_DEXIE_DB) {
|
||||
await updateAccountSettings(this.activeDid, {
|
||||
profileImageUrl: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
this.profileImageUrl = undefined;
|
||||
|
||||
@@ -2107,14 +1980,8 @@ export default class AccountViewView extends Vue {
|
||||
databaseUtil.logConsoleAndDb(
|
||||
"Error saving profile: " + errorStringForLog(error),
|
||||
);
|
||||
if (USE_DEXIE_DB) {
|
||||
logConsoleAndDb("Error saving profile: " + errorStringForLog(error));
|
||||
}
|
||||
const errorMessage: string =
|
||||
error.response?.data?.error?.message ||
|
||||
error.response?.data?.error ||
|
||||
error.message ||
|
||||
"There was an error saving your profile.";
|
||||
extractErrorMessage(error) || "There was an error saving your profile.";
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -2202,13 +2069,8 @@ export default class AccountViewView extends Vue {
|
||||
databaseUtil.logConsoleAndDb(
|
||||
"Error deleting profile: " + errorStringForLog(error),
|
||||
);
|
||||
if (USE_DEXIE_DB) {
|
||||
logConsoleAndDb("Error deleting profile: " + errorStringForLog(error));
|
||||
}
|
||||
const errorMessage: string =
|
||||
error.response?.data?.error?.message ||
|
||||
error.response?.data?.error ||
|
||||
error.message ||
|
||||
extractErrorMessage(error) ||
|
||||
"There was an error deleting your profile.";
|
||||
this.$notify(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user