From a5eb5cacaf1853400610ade0d8be685415d591d4 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 3 Jul 2025 06:31:43 +0000 Subject: [PATCH] Clean up console logging and fix platform detection issues - Reduce migration/platform logging verbosity in development mode (~80-90% less noise) - Fix AbsurdSQL platform check to support 'development' alongside 'web' - Add missing WebPlatformService methods (isWorker, initSharedArrayBuffer) - Fix Electron API endpoint resolution to prevent JSON parsing errors - Prevent circular database logging that caused [DB-PREVENTED-INFO] spam Console now shows only essential information while preserving all errors/warnings --- src/libs/endorserServer.ts | 6 +- src/main.electron.ts | 50 ++++++++++++--- src/services/AbsurdSqlDatabaseService.ts | 16 +++-- src/services/migrationService.ts | 22 ++++--- src/services/platforms/WebPlatformService.ts | 34 ++++++++-- src/utils/PlatformServiceMixin.ts | 8 ++- src/utils/logger.ts | 5 +- src/views/HomeView.vue | 67 ++++++++++++++++---- 8 files changed, 162 insertions(+), 46 deletions(-) diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index f657bd04..723fe3dc 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -512,7 +512,11 @@ export async function getPlanFromCache( cred = resp.data.data[0]; planCache.set(handleId, cred); } else { - logger.log( + // Use debug level for development to reduce console noise + const isDevelopment = process.env.VITE_PLATFORM === "development"; + const log = isDevelopment ? logger.debug : logger.log; + + log( "[EndorserServer] Plan cache is empty for handle", handleId, " Got data:", diff --git a/src/main.electron.ts b/src/main.electron.ts index d8ca0a08..04fcb9af 100644 --- a/src/main.electron.ts +++ b/src/main.electron.ts @@ -33,6 +33,31 @@ import { initializeApp } from "./main.common"; import { handleApiError } from "./services/api"; import { logger } from "./utils/logger"; +// **CRITICAL**: Disable any existing service workers that might intercept API calls +// Service workers from web sessions can persist and cause issues in Electron +// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition +navigator.serviceWorker + ?.getRegistrations() + .then(function (registrations) { + for (const registration of registrations) { + logger.debug( + "[Electron] Unregistering service worker:", + registration.scope, + ); + registration.unregister(); + } + if (registrations.length > 0) { + logger.log( + "[Electron] Cleaned up", + registrations.length, + "service worker registrations", + ); + } + }) + .catch((error) => { + logger.debug("[Electron] Service worker cleanup failed:", error); + }); + logger.log("[Electron] Starting initialization"); logger.log("[Electron] Platform:", process.env.VITE_PLATFORM); @@ -61,14 +86,23 @@ if (typeof window !== "undefined" && window.require) { // **CRITICAL FIX**: Disable any existing service worker that might be intercepting API calls try { if (navigator.serviceWorker?.getRegistrations) { - navigator.serviceWorker.getRegistrations().then(function(registrations) { - for(let registration of registrations) { - console.log("[Electron] Unregistering service worker:", registration.scope); - registration.unregister(); - } - }).catch(error => { - console.log("[Electron] Failed to unregister service workers:", error); - }); + navigator.serviceWorker + .getRegistrations() + .then(function (registrations) { + for (const registration of registrations) { + console.log( + "[Electron] Unregistering service worker:", + registration.scope, + ); + registration.unregister(); + } + }) + .catch((error) => { + console.log( + "[Electron] Failed to unregister service workers:", + error, + ); + }); } } catch (error) { console.log("[Electron] Service worker cleanup not available:", error); diff --git a/src/services/AbsurdSqlDatabaseService.ts b/src/services/AbsurdSqlDatabaseService.ts index da307611..fb7ffe7b 100644 --- a/src/services/AbsurdSqlDatabaseService.ts +++ b/src/services/AbsurdSqlDatabaseService.ts @@ -70,11 +70,13 @@ class AbsurdSqlDatabaseService implements DatabaseService { return; } - // **PLATFORM CHECK**: AbsurdSqlDatabaseService should only run on web platform + // **PLATFORM CHECK**: AbsurdSqlDatabaseService should only run on web-based platforms // This prevents SharedArrayBuffer checks and web-specific initialization on Electron/Capacitor - if (process.env.VITE_PLATFORM !== "web") { + // Allow both 'web' (production) and 'development' (dev server) platforms + const webBasedPlatforms = ["web", "development"]; + if (!webBasedPlatforms.includes(process.env.VITE_PLATFORM || "")) { throw new Error( - `AbsurdSqlDatabaseService is only supported on web platform. Current platform: ${process.env.VITE_PLATFORM}`, + `AbsurdSqlDatabaseService is only supported on web-based platforms (web, development). Current platform: ${process.env.VITE_PLATFORM}`, ); } @@ -97,12 +99,16 @@ class AbsurdSqlDatabaseService implements DatabaseService { // **SHARED ARRAY BUFFER FALLBACK**: Only needed for web platform // This check handles Safari and other browsers without SharedArrayBuffer support if (typeof SharedArrayBuffer === "undefined") { - logger.debug("[AbsurdSqlDatabaseService] SharedArrayBuffer not available, using fallback mode"); + logger.debug( + "[AbsurdSqlDatabaseService] SharedArrayBuffer not available, using fallback mode", + ); const stream = SQL.FS.open(path, "a+"); await stream.node.contents.readIfFallback(); SQL.FS.close(stream); } else { - logger.debug("[AbsurdSqlDatabaseService] SharedArrayBuffer available, using optimized mode"); + logger.debug( + "[AbsurdSqlDatabaseService] SharedArrayBuffer available, using optimized mode", + ); } this.db = new SQL.Database(path, { filename: true }); diff --git a/src/services/migrationService.ts b/src/services/migrationService.ts index 46853f33..93e769f4 100644 --- a/src/services/migrationService.ts +++ b/src/services/migrationService.ts @@ -400,12 +400,17 @@ async function isSchemaAlreadyPresent( * ``` */ export async function runMigrations( - sqlExec: (sql: string, params?: unknown[]) => Promise, + sqlExec: (sql: string, params?: unknown[]) => Promise, sqlQuery: (sql: string, params?: unknown[]) => Promise, extractMigrationNames: (result: T) => Set, ): Promise { + const isDevelopment = process.env.VITE_PLATFORM === "development"; + + // Use debug level for routine migration messages in development + const migrationLog = isDevelopment ? logger.debug : logger.log; + try { - logger.log("📋 [Migration] Starting migration process..."); + migrationLog("📋 [Migration] Starting migration process..."); // Step 1: Create migrations table if it doesn't exist // Note: We use IF NOT EXISTS here because this is infrastructure, not a business migration @@ -431,7 +436,7 @@ export async function runMigrations( return; } - logger.log( + migrationLog( `📊 [Migration] Found ${migrations.length} total migrations, ${appliedMigrations.size} already applied`, ); @@ -458,7 +463,7 @@ export async function runMigrations( await sqlExec("INSERT INTO migrations (name) VALUES (?)", [ migration.name, ]); - logger.log( + migrationLog( `✅ [Migration] Marked existing schema as applied: ${migration.name}`, ); skippedCount++; @@ -473,7 +478,7 @@ export async function runMigrations( } // Apply the migration - logger.log(`🔄 [Migration] Applying migration: ${migration.name}`); + migrationLog(`🔄 [Migration] Applying migration: ${migration.name}`); try { // Execute the migration SQL @@ -496,7 +501,7 @@ export async function runMigrations( migration.name, ]); - logger.log(`🎉 [Migration] Successfully applied: ${migration.name}`); + migrationLog(`🎉 [Migration] Successfully applied: ${migration.name}`); appliedCount++; } catch (error) { logger.error(`❌ [Migration] Error applying ${migration.name}:`, error); @@ -512,7 +517,7 @@ export async function runMigrations( (errorMessage.includes("table") && errorMessage.includes("already exists")) ) { - logger.log( + migrationLog( `⚠️ [Migration] ${migration.name} appears already applied (${errorMessage}). Validating and marking as complete.`, ); @@ -533,7 +538,7 @@ export async function runMigrations( await sqlExec("INSERT INTO migrations (name) VALUES (?)", [ migration.name, ]); - logger.log(`✅ [Migration] Marked as applied: ${migration.name}`); + migrationLog(`✅ [Migration] Marked as applied: ${migration.name}`); appliedCount++; } catch (insertError) { // If we can't insert the migration record, log it but don't fail @@ -569,6 +574,7 @@ export async function runMigrations( ); } + // Always show completion message logger.log( `🎉 [Migration] Migration process complete! Summary: ${appliedCount} applied, ${skippedCount} skipped`, ); diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts index cabba801..ea2c3eb0 100644 --- a/src/services/platforms/WebPlatformService.ts +++ b/src/services/platforms/WebPlatformService.ts @@ -48,15 +48,21 @@ export class WebPlatformService implements PlatformService { constructor() { WebPlatformService.instanceCount++; - // Only warn if multiple instances (which shouldn't happen with singleton) - if (WebPlatformService.instanceCount > 1) { - console.error( - `[WebPlatformService] ERROR: Multiple instances created! Count: ${WebPlatformService.instanceCount}`, - ); - } else { - console.log(`[WebPlatformService] Initializing web platform service`); + // Use debug level logging for development mode to reduce console noise + const isDevelopment = process.env.VITE_PLATFORM === "development"; + const log = isDevelopment ? logger.debug : logger.log; + + log("[WebPlatformService] Initializing web platform service"); + + // Only initialize SharedArrayBuffer setup for web platforms + if (this.isWorker()) { + log("[WebPlatformService] Skipping initBackend call in worker context"); + return; } + // Initialize shared array buffer for main thread + this.initSharedArrayBuffer(); + // Start worker initialization but don't await it in constructor this.workerInitPromise = this.initializeWorker(); } @@ -629,4 +635,18 @@ export class WebPlatformService implements PlatformService { async rotateCamera(): Promise { throw new Error("Camera rotation not implemented in web platform"); } + + /** + * Checks if running in a worker context + */ + private isWorker(): boolean { + return typeof window === "undefined"; + } + + /** + * Initialize SharedArrayBuffer setup (handled by initBackend in initializeWorker) + */ + private initSharedArrayBuffer(): void { + // SharedArrayBuffer initialization is handled by initBackend call in initializeWorker + } } diff --git a/src/utils/PlatformServiceMixin.ts b/src/utils/PlatformServiceMixin.ts index 2f7f12fb..742a5628 100644 --- a/src/utils/PlatformServiceMixin.ts +++ b/src/utils/PlatformServiceMixin.ts @@ -460,15 +460,17 @@ export const PlatformServiceMixin = { } const settings = await this.$getSettings(MASTER_SETTINGS_KEY, defaults); - + // **ELECTRON-SPECIFIC FIX**: Apply platform-specific API server override // This ensures Electron always uses production endpoints regardless of cached settings if (process.env.VITE_PLATFORM === "electron") { // Import constants dynamically to get platform-specific values - const { DEFAULT_ENDORSER_API_SERVER } = await import("../constants/app"); + const { DEFAULT_ENDORSER_API_SERVER } = await import( + "../constants/app" + ); settings.apiServer = DEFAULT_ENDORSER_API_SERVER; } - + return (this as any)._setCached( cacheKey, settings, diff --git a/src/utils/logger.ts b/src/utils/logger.ts index d61982fb..99625f69 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -43,10 +43,13 @@ export const logger = { } // Only log to database for important messages (not routine operations) + // Skip database logging for migration messages to avoid circular dependency if ( !message.includes("[CapacitorPlatformService]") && !message.includes("[CapacitorMigration]") && - !message.includes("[DB-Integrity]") + !message.includes("[DB-Integrity]") && + !message.includes("[Migration]") && + !message.includes("[IndexedDBMigrationService]") ) { const argsString = args.length > 0 ? " - " + safeStringify(args) : ""; logToDb(message + argsString); diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 1353d32d..3bd240c9 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -557,6 +557,10 @@ export default class HomeView extends Vue { // Update component state this.apiServer = settings.apiServer || ""; + + // **CRITICAL**: Ensure correct API server for platform + await this.ensureCorrectApiServer(); + this.activeDid = settings.activeDid || ""; // Load contacts with graceful fallback @@ -676,6 +680,22 @@ export default class HomeView extends Vue { } } + /** + * Ensures API server is correctly set for the current platform + * For Electron, always use production endpoint regardless of saved settings + * + * @internal + * Called after loading settings to ensure correct API endpoint + */ + private async ensureCorrectApiServer() { + if (process.env.VITE_PLATFORM === "electron") { + // **CRITICAL FIX**: Always use production API server for Electron + // This prevents the capacitor-electron:// protocol from being used for API calls + const { DEFAULT_ENDORSER_API_SERVER } = await import("../constants/app"); + this.apiServer = DEFAULT_ENDORSER_API_SERVER; + } + } + /** * Loads user settings from storage using ultra-concise mixin utilities * Sets component state for: @@ -696,6 +716,10 @@ export default class HomeView extends Vue { }); this.apiServer = settings.apiServer || ""; + + // **CRITICAL**: Ensure correct API server for platform + await this.ensureCorrectApiServer(); + this.activeDid = settings.activeDid || ""; this.feedLastViewedClaimId = settings.lastViewedClaimId; this.givenName = settings.firstName || ""; @@ -934,13 +958,15 @@ export default class HomeView extends Vue { } /** - * Updates feed with latest activity + * Updates feed data from endorser service with error handling + * - Retrieves new gives from API + * - Processes records through filters + * - Updates last viewed claim ID + * - Handles paging if needed * * @internal * @callGraph - * Called by: - * - loadMoreGives() - * - initializeIdentity() + * Called by: loadFeedData(), manual refresh * Calls: * - retrieveGives() * - processFeedResults() @@ -948,12 +974,10 @@ export default class HomeView extends Vue { * - handleFeedError() * * @chain - * loadMoreGives() -> updateAllFeed() - * initializeIdentity() -> updateAllFeed() + * loadFeedData() -> updateAllFeed() -> retrieveGives() * * @requires * - this.apiServer - * - this.activeDid * - this.feedPreviousOldestId * * @modifies @@ -1350,13 +1374,28 @@ export default class HomeView extends Vue { } /** - * Retrieve claims in reverse chronological order + * Retrieves gift data from endorser API with error handling + * - Fetches gives from API endpoint + * - Handles authentication headers + * - Processes API response with comprehensive error handling + * + * @public + * @callGraph + * Called by: updateAllFeed() + * Calls: + * - getHeaders() + * - fetch() + * + * @chain + * updateAllFeed() -> retrieveGives() -> getHeaders() + * + * @requires + * - this.activeDid + * - this.$notify * - * @internal - * Called by updateAllFeed() * @param endorserApiServer API server URL - * @param beforeId OptioCalled by updateAllFeed()nal ID to fetch earlier results - * @returns claims in reverse chronological order + * @param beforeId Optional ID for pagination + * @returns Promise resolving to API response data */ async retrieveGives(endorserApiServer: string, beforeId?: string) { const beforeQuery = beforeId == null ? "" : "&beforeId=" + beforeId; @@ -1365,6 +1404,7 @@ export default class HomeView extends Vue { this.activeDid, doNotShowErrorAgain ? undefined : this.$notify, ); + // retrieve headers for this user, but if an error happens then report it but proceed with the fetch with no header const response = await fetch( endorserApiServer + @@ -1380,7 +1420,8 @@ export default class HomeView extends Vue { throw await response.text(); } - const results = await response.json(); + const responseText = await response.text(); + const results = JSON.parse(responseText); if (results.data) { return results;