forked from jsnbuchanan/crowd-funder-for-time-pwa
feat(db): improve settings retrieval resilience and logging
Enhance retrieveSettingsForActiveAccount with better error handling and logging while maintaining core functionality. Changes focus on making the system more debuggable and resilient without overcomplicating the logic. Key improvements: - Add structured error handling with specific try-catch blocks - Implement detailed logging with [databaseUtil] prefix for easy filtering - Add graceful fallbacks for searchBoxes parsing and missing settings - Improve error recovery paths with safe defaults - Maintain existing security model and data integrity Security: - No sensitive data in logs - Safe JSON parsing with fallbacks - Proper error boundaries - Consistent state management - Clear fallback paths Testing: - Verify settings retrieval works with/without active DID - Check error handling for invalid searchBoxes - Confirm logging provides clear debugging context - Validate fallback to default settings works
This commit is contained in:
@@ -515,49 +515,85 @@ export default class HomeView extends Vue {
|
||||
*/
|
||||
private async initializeIdentity() {
|
||||
try {
|
||||
this.allMyDids = await retrieveAccountDids();
|
||||
if (this.allMyDids.length === 0) {
|
||||
this.isCreatingIdentifier = true;
|
||||
const newDid = await generateSaveAndActivateIdentity();
|
||||
this.isCreatingIdentifier = false;
|
||||
this.allMyDids = [newDid];
|
||||
// Retrieve DIDs with better error handling
|
||||
try {
|
||||
this.allMyDids = await retrieveAccountDids();
|
||||
logConsoleAndDb(`[HomeView] Retrieved ${this.allMyDids.length} DIDs`);
|
||||
} catch (error) {
|
||||
logConsoleAndDb(`[HomeView] Failed to retrieve DIDs: ${error}`, true);
|
||||
throw new Error("Failed to load existing identities. Please try restarting the app.");
|
||||
}
|
||||
|
||||
let settings = await databaseUtil.retrieveSettingsForActiveAccount();
|
||||
if (USE_DEXIE_DB) {
|
||||
settings = await retrieveSettingsForActiveAccount();
|
||||
// Create new DID if needed
|
||||
if (this.allMyDids.length === 0) {
|
||||
try {
|
||||
this.isCreatingIdentifier = true;
|
||||
const newDid = await generateSaveAndActivateIdentity();
|
||||
this.isCreatingIdentifier = false;
|
||||
this.allMyDids = [newDid];
|
||||
logConsoleAndDb(`[HomeView] Created new identity: ${newDid}`);
|
||||
} catch (error) {
|
||||
this.isCreatingIdentifier = false;
|
||||
logConsoleAndDb(`[HomeView] Failed to create new identity: ${error}`, true);
|
||||
throw new Error("Failed to create new identity. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
// Load settings with better error context
|
||||
let settings;
|
||||
try {
|
||||
settings = await databaseUtil.retrieveSettingsForActiveAccount();
|
||||
if (USE_DEXIE_DB) {
|
||||
settings = await retrieveSettingsForActiveAccount();
|
||||
}
|
||||
logConsoleAndDb(`[HomeView] Retrieved settings for ${settings.activeDid || 'no active DID'}`);
|
||||
} catch (error) {
|
||||
logConsoleAndDb(`[HomeView] Failed to retrieve settings: ${error}`, true);
|
||||
throw new Error("Failed to load user settings. Some features may be limited.");
|
||||
}
|
||||
|
||||
// Update component state
|
||||
this.apiServer = settings.apiServer || "";
|
||||
this.activeDid = settings.activeDid || "";
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
const dbContacts = await platformService.dbQuery(
|
||||
"SELECT * FROM contacts",
|
||||
);
|
||||
this.allContacts = databaseUtil.mapQueryResultToValues(
|
||||
dbContacts,
|
||||
) as unknown as Contact[];
|
||||
if (USE_DEXIE_DB) {
|
||||
this.allContacts = await db.contacts.toArray();
|
||||
|
||||
// Load contacts with graceful fallback
|
||||
try {
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
const dbContacts = await platformService.dbQuery("SELECT * FROM contacts");
|
||||
this.allContacts = databaseUtil.mapQueryResultToValues(dbContacts) as Contact[];
|
||||
if (USE_DEXIE_DB) {
|
||||
this.allContacts = await db.contacts.toArray();
|
||||
}
|
||||
logConsoleAndDb(`[HomeView] Retrieved ${this.allContacts.length} contacts`);
|
||||
} catch (error) {
|
||||
logConsoleAndDb(`[HomeView] Failed to retrieve contacts: ${error}`, true);
|
||||
this.allContacts = []; // Ensure we have a valid empty array
|
||||
this.$notify({
|
||||
group: "alert",
|
||||
type: "warning",
|
||||
title: "Contact Loading Issue",
|
||||
text: "Some contact information may be unavailable.",
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Update remaining settings
|
||||
this.feedLastViewedClaimId = settings.lastViewedClaimId;
|
||||
this.givenName = settings.firstName || "";
|
||||
this.isFeedFilteredByVisible = !!settings.filterFeedByVisible;
|
||||
this.isFeedFilteredByNearby = !!settings.filterFeedByNearby;
|
||||
this.isRegistered = !!settings.isRegistered;
|
||||
this.lastAckedOfferToUserJwtId = settings.lastAckedOfferToUserJwtId;
|
||||
this.lastAckedOfferToUserProjectsJwtId =
|
||||
settings.lastAckedOfferToUserProjectsJwtId;
|
||||
this.lastAckedOfferToUserProjectsJwtId = settings.lastAckedOfferToUserProjectsJwtId;
|
||||
this.searchBoxes = settings.searchBoxes || [];
|
||||
this.showShortcutBvc = !!settings.showShortcutBvc;
|
||||
this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings);
|
||||
|
||||
// Check onboarding status
|
||||
if (!settings.finishedOnboarding) {
|
||||
(this.$refs.onboardingDialog as OnboardingDialog).open(
|
||||
OnboardPage.Home,
|
||||
);
|
||||
(this.$refs.onboardingDialog as OnboardingDialog).open(OnboardPage.Home);
|
||||
}
|
||||
|
||||
// someone may have have registered after sharing contact info, so recheck
|
||||
// Check registration status if needed
|
||||
if (!this.isRegistered && this.activeDid) {
|
||||
try {
|
||||
const resp = await fetchEndorserRateLimits(
|
||||
@@ -577,51 +613,62 @@ export default class HomeView extends Vue {
|
||||
});
|
||||
}
|
||||
this.isRegistered = true;
|
||||
logConsoleAndDb(`[HomeView] User ${this.activeDid} is now registered`);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore the error... just keep us unregistered
|
||||
} catch (error) {
|
||||
logConsoleAndDb(`[HomeView] Registration check failed: ${error}`, true);
|
||||
// Continue as unregistered - this is expected for new users
|
||||
}
|
||||
}
|
||||
|
||||
// this returns a Promise but we don't need to wait for it
|
||||
this.updateAllFeed();
|
||||
// Initialize feed and offers
|
||||
try {
|
||||
// Start feed update in background
|
||||
this.updateAllFeed().catch(error => {
|
||||
logConsoleAndDb(`[HomeView] Background feed update failed: ${error}`, true);
|
||||
});
|
||||
|
||||
if (this.activeDid) {
|
||||
const offersToUserData = await getNewOffersToUser(
|
||||
this.axios,
|
||||
this.apiServer,
|
||||
this.activeDid,
|
||||
this.lastAckedOfferToUserJwtId,
|
||||
);
|
||||
this.numNewOffersToUser = offersToUserData.data.length;
|
||||
this.newOffersToUserHitLimit = offersToUserData.hitLimit;
|
||||
}
|
||||
// Load new offers if we have an active DID
|
||||
if (this.activeDid) {
|
||||
const [offersToUser, offersToProjects] = await Promise.all([
|
||||
getNewOffersToUser(
|
||||
this.axios,
|
||||
this.apiServer,
|
||||
this.activeDid,
|
||||
this.lastAckedOfferToUserJwtId,
|
||||
),
|
||||
getNewOffersToUserProjects(
|
||||
this.axios,
|
||||
this.apiServer,
|
||||
this.activeDid,
|
||||
this.lastAckedOfferToUserProjectsJwtId,
|
||||
),
|
||||
]);
|
||||
|
||||
if (this.activeDid) {
|
||||
const offersToUserProjects = await getNewOffersToUserProjects(
|
||||
this.axios,
|
||||
this.apiServer,
|
||||
this.activeDid,
|
||||
this.lastAckedOfferToUserProjectsJwtId,
|
||||
);
|
||||
this.numNewOffersToUserProjects = offersToUserProjects.data.length;
|
||||
this.newOffersToUserProjectsHitLimit = offersToUserProjects.hitLimit;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
logConsoleAndDb("Error retrieving settings or feed: " + err, true);
|
||||
this.$notify(
|
||||
{
|
||||
this.numNewOffersToUser = offersToUser.data.length;
|
||||
this.newOffersToUserHitLimit = offersToUser.hitLimit;
|
||||
this.numNewOffersToUserProjects = offersToProjects.data.length;
|
||||
this.newOffersToUserProjectsHitLimit = offersToProjects.hitLimit;
|
||||
|
||||
logConsoleAndDb(
|
||||
`[HomeView] Retrieved ${this.numNewOffersToUser} user offers and ` +
|
||||
`${this.numNewOffersToUserProjects} project offers`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logConsoleAndDb(`[HomeView] Failed to initialize feed/offers: ${error}`, true);
|
||||
// Don't throw - we can continue with empty feed
|
||||
this.$notify({
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text:
|
||||
(err as { userMessage?: string })?.userMessage ||
|
||||
"There was an error retrieving your settings or the latest activity.",
|
||||
},
|
||||
5000,
|
||||
);
|
||||
type: "warning",
|
||||
title: "Feed Loading Issue",
|
||||
text: "Some feed data may be unavailable. Pull to refresh.",
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
throw error; // Re-throw to be caught by mounted()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,19 +831,24 @@ export default class HomeView extends Vue {
|
||||
* - Displays user notification
|
||||
*
|
||||
* @internal
|
||||
* Called by mounted()
|
||||
* Called by mounted() and initializeIdentity()
|
||||
* @param err Error object with optional userMessage
|
||||
*/
|
||||
private handleError(err: unknown) {
|
||||
logConsoleAndDb("Error retrieving settings or feed: " + err, true);
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
const userMessage = (err as { userMessage?: string })?.userMessage;
|
||||
|
||||
logConsoleAndDb(
|
||||
`[HomeView] Initialization error: ${errorMessage}${userMessage ? ` (${userMessage})` : ''}`,
|
||||
true
|
||||
);
|
||||
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
type: "danger",
|
||||
title: "Error",
|
||||
text:
|
||||
(err as { userMessage?: string })?.userMessage ||
|
||||
"There was an error retrieving your settings or the latest activity.",
|
||||
text: userMessage || "There was an error loading your data. Please try refreshing the page.",
|
||||
},
|
||||
5000,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user