From 5340c00ae2656999ceaf8c7ee205d7392fc0a7b6 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Sun, 28 Sep 2025 20:24:49 -0600 Subject: [PATCH 1/7] fix: remove the duplicate settings for user 0, remove other user-0-specific code, enhance errors --- src/libs/endorserServer.ts | 8 ++-- src/libs/util.ts | 89 ----------------------------------- src/views/AccountViewView.vue | 2 +- src/views/HomeView.vue | 2 +- 4 files changed, 6 insertions(+), 95 deletions(-) diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index 30bb7316..341a20f3 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -57,7 +57,7 @@ import { KeyMetaMaybeWithPrivate, } from "../interfaces/common"; import { PlanSummaryRecord } from "../interfaces/records"; -import { logger } from "../utils/logger"; +import { logger, safeStringify } from "../utils/logger"; import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { APP_SERVER } from "@/constants/app"; import { SOMEONE_UNNAMED } from "@/constants/entities"; @@ -685,7 +685,7 @@ export function serverMessageForUser(error: unknown): string | undefined { export function errorStringForLog(error: unknown) { let stringifiedError = "" + error; try { - stringifiedError = JSON.stringify(error); + stringifiedError = safeStringify(error); } catch (e) { // can happen with Dexie, eg: // TypeError: Converting circular structure to JSON @@ -697,7 +697,7 @@ export function errorStringForLog(error: unknown) { if (error && typeof error === "object" && "response" in error) { const err = error as AxiosErrorResponse; - const errorResponseText = JSON.stringify(err.response); + const errorResponseText = safeStringify(err.response); // for some reason, error.response is not included in stringify result (eg. for 400 errors on invite redemptions) if (!R.empty(errorResponseText) && !fullError.includes(errorResponseText)) { // add error.response stuff @@ -707,7 +707,7 @@ export function errorStringForLog(error: unknown) { R.equals(err.config, err.response.config) ) { // but exclude "config" because it's already in there - const newErrorResponseText = JSON.stringify( + const newErrorResponseText = safeStringify( R.omit(["config"] as never[], err.response), ); fullError += diff --git a/src/libs/util.ts b/src/libs/util.ts index 40d0fd3a..2c8c337b 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -988,11 +988,6 @@ export async function importFromMnemonic( ): Promise { const mne: string = mnemonic.trim().toLowerCase(); - // Check if this is Test User #0 - const TEST_USER_0_MNEMONIC = - "rigid shrug mobile smart veteran half all pond toilet brave review universe ship congress found yard skate elite apology jar uniform subway slender luggage"; - const isTestUser0 = mne === TEST_USER_0_MNEMONIC; - // Derive address and keys from mnemonic const [address, privateHex, publicHex] = deriveAddress(mne, derivationPath); @@ -1007,90 +1002,6 @@ export async function importFromMnemonic( // Save the new identity await saveNewIdentity(newId, mne, derivationPath); - - // Set up Test User #0 specific settings - if (isTestUser0) { - // Set up Test User #0 specific settings with enhanced error handling - const platformService = await getPlatformService(); - - try { - // First, ensure the DID-specific settings record exists - await platformService.insertNewDidIntoSettings(newId.did); - - // Then update with Test User #0 specific settings - await platformService.updateDidSpecificSettings(newId.did, { - firstName: "User Zero", - isRegistered: true, - }); - - // Verify the settings were saved correctly - const verificationResult = await platformService.dbQuery( - "SELECT firstName, isRegistered FROM settings WHERE accountDid = ?", - [newId.did], - ); - - if (verificationResult?.values?.length) { - const settings = verificationResult.values[0]; - const firstName = settings[0]; - const isRegistered = settings[1]; - - logger.debug( - "[importFromMnemonic] Test User #0 settings verification", - { - did: newId.did, - firstName, - isRegistered, - expectedFirstName: "User Zero", - expectedIsRegistered: true, - }, - ); - - // If settings weren't saved correctly, try individual updates - if (firstName !== "User Zero" || isRegistered !== 1) { - logger.warn( - "[importFromMnemonic] Test User #0 settings not saved correctly, retrying with individual updates", - ); - - await platformService.dbExec( - "UPDATE settings SET firstName = ? WHERE accountDid = ?", - ["User Zero", newId.did], - ); - - await platformService.dbExec( - "UPDATE settings SET isRegistered = ? WHERE accountDid = ?", - [1, newId.did], - ); - - // Verify again - const retryResult = await platformService.dbQuery( - "SELECT firstName, isRegistered FROM settings WHERE accountDid = ?", - [newId.did], - ); - - if (retryResult?.values?.length) { - const retrySettings = retryResult.values[0]; - logger.debug( - "[importFromMnemonic] Test User #0 settings after retry", - { - firstName: retrySettings[0], - isRegistered: retrySettings[1], - }, - ); - } - } - } else { - logger.error( - "[importFromMnemonic] Failed to verify Test User #0 settings - no record found", - ); - } - } catch (error) { - logger.error( - "[importFromMnemonic] Error setting up Test User #0 settings:", - error, - ); - // Don't throw - allow the import to continue even if settings fail - } - } } /** diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index f4cdaca8..f48071de 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -1480,7 +1480,7 @@ export default class AccountViewView extends Vue { status?: number; }; }; - logger.error("[Server Limits] Error retrieving limits:", { + logger.warn("[Server Limits] Error retrieving limits, expected for unregistered users:", { error: error instanceof Error ? error.message : String(error), did: did, apiServer: this.apiServer, diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index e8d2035a..2c51fb84 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -662,7 +662,7 @@ export default class HomeView extends Vue { }; logger.warn( - "[HomeView Settings Trace] ⚠️ Registration check failed", + "[HomeView Settings Trace] ⚠️ Registration check failed, expected for unregistered users.", { error: errorMessage, did: this.activeDid, -- 2.30.2 From 530cddfab0a8c79206c67b6aceea05c99975a987 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Mon, 29 Sep 2025 08:07:54 -0600 Subject: [PATCH 2/7] fix: linting --- src/views/AccountViewView.vue | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index f48071de..06ac3bb1 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -1480,18 +1480,21 @@ export default class AccountViewView extends Vue { status?: number; }; }; - logger.warn("[Server Limits] Error retrieving limits, expected for unregistered users:", { - error: error instanceof Error ? error.message : String(error), - did: did, - apiServer: this.apiServer, - imageServer: this.DEFAULT_IMAGE_API_SERVER, - partnerApiServer: this.partnerApiServer, - errorCode: axiosError?.response?.data?.error?.code, - errorMessage: axiosError?.response?.data?.error?.message, - httpStatus: axiosError?.response?.status, - needsUserMigration: true, - timestamp: new Date().toISOString(), - }); + logger.warn( + "[Server Limits] Error retrieving limits, expected for unregistered users:", + { + error: error instanceof Error ? error.message : String(error), + did: did, + apiServer: this.apiServer, + imageServer: this.DEFAULT_IMAGE_API_SERVER, + partnerApiServer: this.partnerApiServer, + errorCode: axiosError?.response?.data?.error?.code, + errorMessage: axiosError?.response?.data?.error?.message, + httpStatus: axiosError?.response?.status, + needsUserMigration: true, + timestamp: new Date().toISOString(), + }, + ); // this.notify.error(this.limitsMessage, TIMEOUTS.STANDARD); } finally { -- 2.30.2 From 7432525f4cdce61ea41cef4df603e95c20070b87 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 2 Oct 2025 06:29:56 +0000 Subject: [PATCH 3/7] refactor(services): align Capacitor and Web platform services with active_identity architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update CapacitorPlatformService.updateDefaultSettings() to use active_identity table instead of hard-coded id=1 - Update CapacitorPlatformService.retrieveSettingsForActiveAccount() to query by accountDid from active_identity - Add getActiveIdentity() method to CapacitorPlatformService for consistency with WebPlatformService - Update WebPlatformService.retrieveSettingsForActiveAccount() to match CapacitorPlatformService pattern - Both services now consistently use active_identity table instead of legacy MASTER_SETTINGS_KEY approach - Maintains backward compatibility with databaseUtil.ts for PWA migration support Technical details: - CapacitorPlatformService: Fixed hard-coded WHERE id = 1 → WHERE accountDid = ? - WebPlatformService: Fixed retrieval pattern to match new architecture - Platform services now aligned with migration 004 active_identity table schema - databaseUtil.ts remains unchanged for PWA-to-SQLite migration bridge --- .../platforms/CapacitorPlatformService.ts | 38 ++++++++++++++++--- src/services/platforms/WebPlatformService.ts | 14 +++++-- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts index 2db74656..57df0f9a 100644 --- a/src/services/platforms/CapacitorPlatformService.ts +++ b/src/services/platforms/CapacitorPlatformService.ts @@ -1343,10 +1343,21 @@ export class CapacitorPlatformService implements PlatformService { async updateDefaultSettings( settings: Record, ): Promise { + // Get current active DID and update that identity's settings + const activeIdentity = await this.getActiveIdentity(); + const activeDid = activeIdentity.activeDid; + + if (!activeDid) { + logger.warn( + "[CapacitorPlatformService] No active DID found, cannot update default settings", + ); + return; + } + const keys = Object.keys(settings); const setClause = keys.map((key) => `${key} = ?`).join(", "); - const sql = `UPDATE settings SET ${setClause} WHERE id = 1`; - const params = keys.map((key) => settings[key]); + const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`; + const params = [...keys.map((key) => settings[key]), activeDid]; await this.dbExec(sql, params); } @@ -1357,6 +1368,15 @@ export class CapacitorPlatformService implements PlatformService { ); } + async getActiveIdentity(): Promise<{ activeDid: string }> { + const result = await this.dbQuery( + "SELECT activeDid FROM active_identity WHERE id = 1", + ); + return { + activeDid: (result?.values?.[0]?.[0] as string) || "", + }; + } + async insertNewDidIntoSettings(did: string): Promise { // Import constants dynamically to avoid circular dependencies const { DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER } = @@ -1385,7 +1405,15 @@ export class CapacitorPlatformService implements PlatformService { string, unknown > | null> { - const result = await this.dbQuery("SELECT * FROM settings WHERE id = 1"); + // Get current active DID from active_identity table + const activeIdentity = await this.getActiveIdentity(); + const activeDid = activeIdentity.activeDid; + + if (!activeDid) { + return null; + } + + const result = await this.dbQuery("SELECT * FROM settings WHERE accountDid = ?", [activeDid]); if (result?.values?.[0]) { // Convert the row to an object const row = result.values[0]; @@ -1393,8 +1421,8 @@ export class CapacitorPlatformService implements PlatformService { const settings: Record = {}; columns.forEach((column, index) => { - if (column !== "id") { - // Exclude the id column + if (column !== "id" && column !== "accountDid") { + // Exclude the id and accountDid columns settings[column] = row[index]; } }); diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts index 3d8248f5..944bbe80 100644 --- a/src/services/platforms/WebPlatformService.ts +++ b/src/services/platforms/WebPlatformService.ts @@ -753,7 +753,15 @@ export class WebPlatformService implements PlatformService { string, unknown > | null> { - const result = await this.dbQuery("SELECT * FROM settings WHERE id = 1"); + // Get current active DID from active_identity table + const activeIdentity = await this.getActiveIdentity(); + const activeDid = activeIdentity.activeDid; + + if (!activeDid) { + return null; + } + + const result = await this.dbQuery("SELECT * FROM settings WHERE accountDid = ?", [activeDid]); if (result?.values?.[0]) { // Convert the row to an object const row = result.values[0]; @@ -761,8 +769,8 @@ export class WebPlatformService implements PlatformService { const settings: Record = {}; columns.forEach((column, index) => { - if (column !== "id") { - // Exclude the id column + if (column !== "id" && column !== "accountDid") { + // Exclude the id and accountDid columns settings[column] = row[index]; } }); -- 2.30.2 From 666bed0efd1e8937a1edfb62bd3442ddd57fe546 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 2 Oct 2025 06:31:03 +0000 Subject: [PATCH 4/7] refactor(services): align Capacitor and Web platform services with active_identity architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update CapacitorPlatformService.updateDefaultSettings() to use active_identity table instead of hard-coded id=1 - Update CapacitorPlatformService.retrieveSettingsForActiveAccount() to query by accountDid from active_identity - Add getActiveIdentity() method to CapacitorPlatformService for consistency with WebPlatformService - Update WebPlatformService.retrieveSettingsForActiveAccount() to match CapacitorPlatformService pattern - Both services now consistently use active_identity table instead of legacy MASTER_SETTINGS_KEY approach - Maintains backward compatibility with databaseUtil.ts for PWA migration support Technical details: - CapacitorPlatformService: Fixed hard-coded WHERE id = 1 → WHERE accountDid = ? - WebPlatformService: Fixed retrieval pattern to match new architecture - Platform services now aligned with migration 004 active_identity table schema - databaseUtil.ts remains unchanged for PWA-to-SQLite migration bridge --- src/services/platforms/CapacitorPlatformService.ts | 5 ++++- src/services/platforms/WebPlatformService.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts index 57df0f9a..ce9e1267 100644 --- a/src/services/platforms/CapacitorPlatformService.ts +++ b/src/services/platforms/CapacitorPlatformService.ts @@ -1413,7 +1413,10 @@ export class CapacitorPlatformService implements PlatformService { return null; } - const result = await this.dbQuery("SELECT * FROM settings WHERE accountDid = ?", [activeDid]); + const result = await this.dbQuery( + "SELECT * FROM settings WHERE accountDid = ?", + [activeDid], + ); if (result?.values?.[0]) { // Convert the row to an object const row = result.values[0]; diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts index 944bbe80..0d5605bb 100644 --- a/src/services/platforms/WebPlatformService.ts +++ b/src/services/platforms/WebPlatformService.ts @@ -761,7 +761,10 @@ export class WebPlatformService implements PlatformService { return null; } - const result = await this.dbQuery("SELECT * FROM settings WHERE accountDid = ?", [activeDid]); + const result = await this.dbQuery( + "SELECT * FROM settings WHERE accountDid = ?", + [activeDid], + ); if (result?.values?.[0]) { // Convert the row to an object const row = result.values[0]; -- 2.30.2 From 20322789a2f04686bccc6cbf90233038cb7d6217 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 2 Oct 2025 08:27:56 +0000 Subject: [PATCH 5/7] fix(AccountView): resolve stale registration status cache after identity creation - Add live registration verification to AccountView.initializeState() - When settings show unregistered but user has activeDid, verify with server - Use fetchEndorserRateLimits() matching HomeView's successful pattern - Update database and UI state immediately upon server confirmation - Eliminate need to navigate away/back to refresh registration status Technical details: - Condition: if (!this.isRegistered && this.activeDid) - Server check: fetchEndorserRateLimits(this.apiServer, this.axios, this.activeDid) - On success: $saveUserSettings({isRegistered: true}) + this.isRegistered = true - Graceful handling for actually unregistered users (expected behavior) Fixes issue where AccountView showed "Before you can publicly announce..." message immediately after User Zero identity creation, despite server confirming user was registered. Problem was Vue component state caching stale settings while database contained updated registration status. Resolves behavior reported in iOS testing: User had to navigate to HomeView and back to AccountView for registration status to update properly. --- src/views/AccountViewView.vue | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 06ac3bb1..9e722029 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -1064,6 +1064,42 @@ export default class AccountViewView extends Vue { this.hideRegisterPromptOnNewContact = !!settings.hideRegisterPromptOnNewContact; this.isRegistered = !!settings?.isRegistered; + + // If settings show unregistered but user has activeDid, verify registration status + if (!this.isRegistered && this.activeDid) { + logger.debug("[AccountViewView] Settings show unregistered, verifying with server:", { + activeDid: this.activeDid, + apiServer: this.apiServer, + }); + + try { + const { fetchEndorserRateLimits } = await import("@/libs/endorserServer"); + const resp = await fetchEndorserRateLimits( + this.apiServer, + this.axios, + this.activeDid, + ); + + if (resp.status === 200) { + logger.debug("[AccountViewView] Server confirms user IS registered, updating settings:", { + activeDid: this.activeDid, + wasRegistered: false, + nowRegistered: true, + }); + + // Update settings and state + await this.$saveUserSettings(this.activeDid, { + isRegistered: true, + }); + this.isRegistered = true; + } + } catch (error) { + logger.debug("[AccountViewView] Registration check failed (expected for unregistered users):", { + activeDid: this.activeDid, + error: error instanceof Error ? error.message : String(error), + }); + } + } this.isSearchAreasSet = !!settings.searchBoxes; this.searchBox = settings.searchBoxes?.[0] || null; this.notifyingNewActivity = !!settings.notifyingNewActivityTime; -- 2.30.2 From 7fd2c4e0c7cd9d425569454a9641188bfc273c0b Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 2 Oct 2025 08:28:35 +0000 Subject: [PATCH 6/7] fix(AccountView): resolve stale registration status cache after identity creation - Add live registration verification to AccountView.initializeState() - When settings show unregistered but user has activeDid, verify with server - Use fetchEndorserRateLimits() matching HomeView's successful pattern - Update database and UI state immediately upon server confirmation - Eliminate need to navigate away/back to refresh registration status Technical details: - Condition: if (!this.isRegistered && this.activeDid) - Server check: fetchEndorserRateLimits(this.apiServer, this.axios, this.activeDid) - On success: $saveUserSettings({isRegistered: true}) + this.isRegistered = true - Graceful handling for actually unregistered users (expected behavior) Fixes issue where AccountView showed "Before you can publicly announce..." message immediately after User Zero identity creation, despite server confirming user was registered. Problem was Vue component state caching stale settings while database contained updated registration status. Resolves behavior reported in iOS testing: User had to navigate to HomeView and back to AccountView for registration status to update properly. --- src/views/AccountViewView.vue | 45 ++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 9e722029..ec5667e0 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -1067,26 +1067,34 @@ export default class AccountViewView extends Vue { // If settings show unregistered but user has activeDid, verify registration status if (!this.isRegistered && this.activeDid) { - logger.debug("[AccountViewView] Settings show unregistered, verifying with server:", { - activeDid: this.activeDid, - apiServer: this.apiServer, - }); - + logger.debug( + "[AccountViewView] Settings show unregistered, verifying with server:", + { + activeDid: this.activeDid, + apiServer: this.apiServer, + }, + ); + try { - const { fetchEndorserRateLimits } = await import("@/libs/endorserServer"); + const { fetchEndorserRateLimits } = await import( + "@/libs/endorserServer" + ); const resp = await fetchEndorserRateLimits( this.apiServer, this.axios, this.activeDid, ); - + if (resp.status === 200) { - logger.debug("[AccountViewView] Server confirms user IS registered, updating settings:", { - activeDid: this.activeDid, - wasRegistered: false, - nowRegistered: true, - }); - + logger.debug( + "[AccountViewView] Server confirms user IS registered, updating settings:", + { + activeDid: this.activeDid, + wasRegistered: false, + nowRegistered: true, + }, + ); + // Update settings and state await this.$saveUserSettings(this.activeDid, { isRegistered: true, @@ -1094,10 +1102,13 @@ export default class AccountViewView extends Vue { this.isRegistered = true; } } catch (error) { - logger.debug("[AccountViewView] Registration check failed (expected for unregistered users):", { - activeDid: this.activeDid, - error: error instanceof Error ? error.message : String(error), - }); + logger.debug( + "[AccountViewView] Registration check failed (expected for unregistered users):", + { + activeDid: this.activeDid, + error: error instanceof Error ? error.message : String(error), + }, + ); } } this.isSearchAreasSet = !!settings.searchBoxes; -- 2.30.2 From fface3012390ec386aef9270f371b1b0b9fa1ea3 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Wed, 8 Oct 2025 15:06:16 +0000 Subject: [PATCH 7/7] fix(platforms): include accountDid in settings retrieval for both platforms - Remove accountDid exclusion from settings object construction in CapacitorPlatformService - Remove accountDid exclusion from settings object construction in WebPlatformService - Ensure accountDid is included in retrieved settings for proper DID-specific configuration handling This change ensures that the accountDid field is properly included when retrieving settings for the active account, allowing for proper DID-specific configuration management across both Capacitor (mobile) and Web platforms. Files modified: - src/services/platforms/CapacitorPlatformService.ts - src/services/platforms/WebPlatformService.ts Timestamp: Wed Oct 8 03:05:45 PM UTC 2025 --- src/services/platforms/CapacitorPlatformService.ts | 4 ++-- src/services/platforms/WebPlatformService.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts index ce9e1267..b1907f13 100644 --- a/src/services/platforms/CapacitorPlatformService.ts +++ b/src/services/platforms/CapacitorPlatformService.ts @@ -1424,8 +1424,8 @@ export class CapacitorPlatformService implements PlatformService { const settings: Record = {}; columns.forEach((column, index) => { - if (column !== "id" && column !== "accountDid") { - // Exclude the id and accountDid columns + if (column !== "id") { + // Exclude the id column settings[column] = row[index]; } }); diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts index 0d5605bb..f5edcc28 100644 --- a/src/services/platforms/WebPlatformService.ts +++ b/src/services/platforms/WebPlatformService.ts @@ -772,8 +772,8 @@ export class WebPlatformService implements PlatformService { const settings: Record = {}; columns.forEach((column, index) => { - if (column !== "id" && column !== "accountDid") { - // Exclude the id and accountDid columns + if (column !== "id") { + // Exclude the id column settings[column] = row[index]; } }); -- 2.30.2