diff --git a/src/components/TopMessage.vue b/src/components/TopMessage.vue index 519a0585..7cd8f3b3 100644 --- a/src/components/TopMessage.vue +++ b/src/components/TopMessage.vue @@ -18,6 +18,7 @@ import { Component, Vue, Prop } from "vue-facing-decorator"; import { AppString, NotificationIface } from "../constants/app"; import { PlatformServiceMixin } from "../utils/PlatformServiceMixin"; import { createNotifyHelpers, TIMEOUTS } from "../utils/notify"; +import { logger } from "../utils/logger"; @Component({ mixins: [PlatformServiceMixin], @@ -29,6 +30,7 @@ export default class TopMessage extends Vue { // - Cache management: this.$refreshSettings(), this.$clearAllCaches() // - Ultra-concise database methods: this.$db(), this.$exec(), this.$query() // - All methods use smart caching with TTL for massive performance gains + // - FIXED: Now properly respects database settings without forcing API server overrides $notify!: (notification: NotificationIface, timeout?: number) => void; @@ -42,26 +44,49 @@ export default class TopMessage extends Vue { this.notify = createNotifyHelpers(this.$notify); try { - // Ultra-concise cached settings loading - replaces 50+ lines of logic! - const settings = await this.$accountSettings(undefined, { - activeDid: undefined, - apiServer: AppString.PROD_ENDORSER_API_SERVER, + // Load settings without overriding database values - fixes settings inconsistency + logger.info("[TopMessage] šŸ“„ Loading settings without overrides..."); + const settings = await this.$accountSettings(); + + logger.info("[TopMessage] šŸ“Š Settings loaded:", { + activeDid: settings.activeDid, + apiServer: settings.apiServer, + warnIfTestServer: settings.warnIfTestServer, + warnIfProdServer: settings.warnIfProdServer, + component: "TopMessage", + timestamp: new Date().toISOString(), }); + // Only show warnings if the user has explicitly enabled them if ( settings.warnIfTestServer && + settings.apiServer && settings.apiServer !== AppString.PROD_ENDORSER_API_SERVER ) { const didPrefix = settings.activeDid?.slice(11, 15); this.message = "You're not using prod, user " + didPrefix; + logger.info("[TopMessage] āš ļø Test server warning displayed:", { + apiServer: settings.apiServer, + didPrefix: didPrefix, + }); } else if ( settings.warnIfProdServer && + settings.apiServer && settings.apiServer === AppString.PROD_ENDORSER_API_SERVER ) { const didPrefix = settings.activeDid?.slice(11, 15); this.message = "You are using prod, user " + didPrefix; + logger.info("[TopMessage] āš ļø Production server warning displayed:", { + apiServer: settings.apiServer, + didPrefix: didPrefix, + }); + } else { + logger.debug( + "[TopMessage] ā„¹ļø No warnings displayed - conditions not met", + ); } } catch (err: unknown) { + logger.error("[TopMessage] āŒ Error loading settings:", err); this.notify.error(JSON.stringify(err), TIMEOUTS.MODAL); } } diff --git a/src/main.ts b/src/main.ts index a58becb7..cc05e386 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,15 @@ const platform = process.env.VITE_PLATFORM || "web"; logger.info(`[Main] šŸš€ Loading TimeSafari for platform: ${platform}`); +// Log all relevant environment variables for boot-time debugging +logger.info("[Main] šŸŒ Boot-time environment configuration:", { + platform: process.env.VITE_PLATFORM, + defaultEndorserApiServer: process.env.VITE_DEFAULT_ENDORSER_API_SERVER, + defaultPartnerApiServer: process.env.VITE_DEFAULT_PARTNER_API_SERVER, + nodeEnv: process.env.NODE_ENV, + timestamp: new Date().toISOString(), +}); + // Dynamically import the appropriate main entry point if (platform === "capacitor") { logger.info(`[Main] šŸ“± Loading Capacitor-specific entry point`); diff --git a/src/router/index.ts b/src/router/index.ts index f80d7a48..e63d54b0 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -337,6 +337,22 @@ router.beforeEach(async (to, _from, next) => { }); try { + // Log boot-time configuration on first navigation + if (!_from) { + logger.info( + "[Router] šŸš€ First navigation detected - logging boot-time configuration:", + { + platform: process.env.VITE_PLATFORM, + defaultEndorserApiServer: + process.env.VITE_DEFAULT_ENDORSER_API_SERVER, + defaultPartnerApiServer: process.env.VITE_DEFAULT_PARTNER_API_SERVER, + nodeEnv: process.env.NODE_ENV, + targetRoute: to.path, + timestamp: new Date().toISOString(), + }, + ); + } + // Skip identity check for routes that handle identity creation manually const skipIdentityRoutes = [ "/start", diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 9f8db67b..1c368676 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -58,7 +58,10 @@ v-if="!isRegistered" :passkeys-enabled="PASSKEYS_ENABLED" :given-name="givenName" - message="Before you can publicly announce a new project or time commitment, a friend needs to register you." + :message=" + `Before you can publicly announce a new project or time commitment, ` + + `a friend needs to register you.` + " /> @@ -925,7 +928,10 @@ export default class AccountViewView extends Vue { // This prevents the "Cannot read properties of undefined (reading 'Default')" error if (L.Icon.Default) { // Type-safe way to handle Leaflet icon prototype - const iconDefault = L.Icon.Default.prototype as Record; + const iconDefault = L.Icon.Default.prototype as unknown as Record< + string, + unknown + >; if ("_getIconUrl" in iconDefault) { delete iconDefault._getIconUrl; } @@ -947,14 +953,25 @@ export default class AccountViewView extends Vue { * @throws Will display specific messages to the user based on different errors. */ async mounted(): Promise { - this.profileService = createProfileService( - this.axios, - this.partnerApiServer, - ); try { await this.initializeState(); await this.processIdentity(); + // FIXED: Create ProfileService AFTER settings are loaded to get correct partnerApiServer + this.profileService = createProfileService( + this.axios, + this.partnerApiServer, + ); + + logger.info( + "[AccountViewView] āœ… ProfileService created with correct partnerApiServer:", + { + partnerApiServer: this.partnerApiServer, + component: "AccountViewView", + timestamp: new Date().toISOString(), + }, + ); + if (this.isRegistered) { try { const profile = await this.profileService.loadProfile(this.activeDid); diff --git a/src/views/TestView.vue b/src/views/TestView.vue index df879ad2..f6f1888d 100644 --- a/src/views/TestView.vue +++ b/src/views/TestView.vue @@ -91,13 +91,95 @@ name: 'shared-photo', query: { fileName }, }" - class="block w-full text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2" + class="block w-full text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2" data-testId="fileUploadButton" > Go to Shared Page + +
+

URL Flow Testing

+

+ Test claim and partner server URL flow from initialization to change + propagation. +

+ +
+
+

Current URL State

+
+
+ API Server: + {{ apiServer || "Not Set" }} +
+
+ Partner API Server: + {{ partnerApiServer || "Not Set" }} +
+
+ Active DID: + {{ activeDid || "Not Set" }} +
+
+ Platform: + {{ getCurrentPlatform() }} +
+
+
+ +
+ + + + + + + + + + + +
+ +
+

URL Flow Test Results

+
+
+ {{ result }} +
+
+
+
+
+

Passkeys

See console for results. @@ -326,6 +408,11 @@ export default class Help extends Vue { showEntityGridTest = false; showPlatformServiceTest = false; + // for URL flow testing + isUrlTestRunning = false; + urlTestResults: string[] = []; + partnerApiServer: string | undefined; + /** * Computed properties for template streamlining * Eliminates repeated classes and logic in template @@ -534,24 +621,93 @@ export default class Help extends Vue { } /** - * Component initialization - * * Loads user settings and account information for testing interface * Uses PlatformServiceMixin for database access */ async mounted() { - const settings = await this.$accountSettings(); - this.activeDid = settings.activeDid || ""; - this.apiServer = settings.apiServer || ""; - this.userName = settings.firstName; - - const account = await retrieveAccountMetadata(this.activeDid); - if (this.activeDid) { - if (account) { - this.credIdHex = account.passkeyCredIdHex as string; - } else { - alert("No account found for DID " + this.activeDid); + logger.info( + "[TestView] šŸš€ Component mounting - starting URL flow tracking", + ); + + // Boot-time logging for initial configuration + logger.info("[TestView] šŸŒ Boot-time configuration detected:", { + platform: process.env.VITE_PLATFORM, + defaultEndorserApiServer: process.env.VITE_DEFAULT_ENDORSER_API_SERVER, + defaultPartnerApiServer: process.env.VITE_DEFAULT_PARTNER_API_SERVER, + nodeEnv: process.env.NODE_ENV, + timestamp: new Date().toISOString(), + }); + + try { + // Track settings loading + logger.info("[TestView] šŸ“„ Loading account settings..."); + const settings = await this.$accountSettings(); + + logger.info("[TestView] šŸ“Š Settings loaded:", { + activeDid: settings.activeDid, + apiServer: settings.apiServer, + partnerApiServer: settings.partnerApiServer, + isRegistered: settings.isRegistered, + firstName: settings.firstName, + }); + + // Update component state + this.activeDid = settings.activeDid || ""; + this.apiServer = settings.apiServer || ""; + this.partnerApiServer = settings.partnerApiServer || ""; + this.userName = settings.firstName; + + logger.info("[TestView] āœ… Component state updated:", { + activeDid: this.activeDid, + apiServer: this.apiServer, + partnerApiServer: this.partnerApiServer, + }); + + // Load account metadata + if (this.activeDid) { + logger.info( + "[TestView] šŸ” Loading account metadata for DID:", + this.activeDid, + ); + const account = await retrieveAccountMetadata(this.activeDid); + + if (account) { + this.credIdHex = account.passkeyCredIdHex as string; + logger.info("[TestView] āœ… Account metadata loaded:", { + did: account.did, + hasPasskey: !!account.passkeyCredIdHex, + passkeyId: account.passkeyCredIdHex, + }); + } else { + logger.warn( + "[TestView] āš ļø No account found for DID:", + this.activeDid, + ); + alert("No account found for DID " + this.activeDid); + } } + + logger.info("[TestView] šŸŽÆ Component initialization complete:", { + activeDid: this.activeDid, + apiServer: this.apiServer, + partnerApiServer: this.partnerApiServer, + hasPasskey: !!this.credIdHex, + platform: this.getCurrentPlatform(), + }); + } catch (error) { + logger.error( + "[TestView] āŒ Error during component initialization:", + error, + ); + this.$notify( + { + group: "error", + type: "error", + title: "Initialization Error", + text: `Failed to initialize component: ${error instanceof Error ? error.message : String(error)}`, + }, + 5000, + ); } } @@ -824,5 +980,276 @@ export default class Help extends Vue { ); } } + + /** + * Tests the URL flow from initialization to change propagation. + * This simulates the flow where a user's DID is set, and then the + * claim and partner server URLs are updated. + */ + public async testUrlFlow() { + this.isUrlTestRunning = true; + this.urlTestResults = []; + + try { + logger.info("[TestView] šŸ”¬ Starting comprehensive URL flow test"); + this.addUrlTestResult("šŸš€ Starting URL flow test..."); + + // Test 1: Current state + this.addUrlTestResult(`šŸ“Š Current State:`); + this.addUrlTestResult(` - API Server: ${this.apiServer || "Not Set"}`); + this.addUrlTestResult( + ` - Partner API Server: ${this.partnerApiServer || "Not Set"}`, + ); + this.addUrlTestResult(` - Active DID: ${this.activeDid || "Not Set"}`); + this.addUrlTestResult(` - Platform: ${this.getCurrentPlatform()}`); + + // Test 2: Load fresh settings + this.addUrlTestResult(`\nšŸ“„ Testing Settings Loading:`); + const startTime = Date.now(); + const settings = await this.$accountSettings(); + const loadTime = Date.now() - startTime; + + this.addUrlTestResult(` - Settings loaded in ${loadTime}ms`); + this.addUrlTestResult( + ` - API Server from settings: ${settings.apiServer || "Not Set"}`, + ); + this.addUrlTestResult( + ` - Partner API Server from settings: ${settings.partnerApiServer || "Not Set"}`, + ); + + // Test 3: Database query + this.addUrlTestResult(`\nšŸ’¾ Testing Database Query:`); + const dbStartTime = Date.now(); + const dbResult = await this.$dbQuery( + "SELECT apiServer, partnerApiServer, activeDid FROM settings WHERE id = ? OR accountDid = ?", + [1, this.activeDid || ""], + ); + const dbTime = Date.now() - dbStartTime; + + if (dbResult?.values) { + this.addUrlTestResult(` - Database query completed in ${dbTime}ms`); + this.addUrlTestResult( + ` - Raw DB values: ${JSON.stringify(dbResult.values)}`, + ); + } else { + this.addUrlTestResult( + ` - Database query failed or returned no results`, + ); + } + + // Test 4: Environment variables + this.addUrlTestResult(`\nšŸŒ Testing Environment Variables:`); + this.addUrlTestResult( + ` - VITE_PLATFORM: ${import.meta.env.VITE_PLATFORM || "Not Set"}`, + ); + this.addUrlTestResult( + ` - VITE_DEFAULT_ENDORSER_API_SERVER: ${import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER || "Not Set"}`, + ); + this.addUrlTestResult( + ` - VITE_DEFAULT_PARTNER_API_SERVER: ${import.meta.env.VITE_DEFAULT_PARTNER_API_SERVER || "Not Set"}`, + ); + + // Test 5: Constants + this.addUrlTestResult(`\nšŸ“‹ Testing App Constants:`); + this.addUrlTestResult( + ` - PROD_ENDORSER_API_SERVER: ${AppString.PROD_ENDORSER_API_SERVER}`, + ); + this.addUrlTestResult( + ` - PROD_PARTNER_API_SERVER: ${AppString.PROD_PARTNER_API_SERVER}`, + ); + + // Test 6: Change detection + this.addUrlTestResult(`\nšŸ”„ Testing Change Detection:`); + const originalApiServer = this.apiServer; + const originalPartnerServer = this.partnerApiServer; + + // Simulate a change + this.addUrlTestResult(` - Original API Server: ${originalApiServer}`); + this.addUrlTestResult( + ` - Original Partner Server: ${originalPartnerServer}`, + ); + + // Test 7: Settings update + this.addUrlTestResult(`\nšŸ’¾ Testing Settings Update:`); + const testChanges = { + apiServer: + originalApiServer === "https://api.endorser.ch" + ? "https://test-api.endorser.ch" + : "https://api.endorser.ch", + }; + + this.addUrlTestResult( + ` - Attempting to change API Server to: ${testChanges.apiServer}`, + ); + const updateResult = await this.$saveSettings(testChanges); + this.addUrlTestResult( + ` - Update result: ${updateResult ? "Success" : "Failed"}`, + ); + + // Test 8: Verify change propagation + this.addUrlTestResult(`\nāœ… Testing Change Propagation:`); + const newSettings = await this.$accountSettings(); + this.addUrlTestResult( + ` - New API Server from settings: ${newSettings.apiServer || "Not Set"}`, + ); + this.addUrlTestResult( + ` - Component state API Server: ${this.apiServer || "Not Set"}`, + ); + this.addUrlTestResult( + ` - Change propagated: ${newSettings.apiServer === this.apiServer ? "Yes" : "No"}`, + ); + + // Test 9: Revert changes + this.addUrlTestResult(`\nšŸ”„ Reverting Changes:`); + const revertResult = await this.$saveSettings({ + apiServer: originalApiServer, + }); + this.addUrlTestResult( + ` - Revert result: ${revertResult ? "Success" : "Failed"}`, + ); + + // Test 10: Final verification + this.addUrlTestResult(`\nšŸŽÆ Final Verification:`); + const finalSettings = await this.$accountSettings(); + this.addUrlTestResult( + ` - Final API Server: ${finalSettings.apiServer || "Not Set"}`, + ); + this.addUrlTestResult( + ` - Matches original: ${finalSettings.apiServer === originalApiServer ? "Yes" : "No"}`, + ); + + this.addUrlTestResult(`\nāœ… URL flow test completed successfully!`); + logger.info("[TestView] āœ… URL flow test completed successfully"); + } catch (error) { + const errorMsg = `āŒ URL flow test failed: ${error instanceof Error ? error.message : String(error)}`; + this.addUrlTestResult(errorMsg); + logger.error("[TestView] āŒ URL flow test failed:", error); + } finally { + this.isUrlTestRunning = false; + } + } + + /** + * Adds a result to the URL test results array. + */ + private addUrlTestResult(message: string) { + this.urlTestResults.push(message); + } + + /** + * Changes the API server to the production URL. + */ + public changeApiServer() { + const currentServer = this.apiServer; + const newServer = + currentServer === "https://api.endorser.ch" + ? "https://test-api.endorser.ch" + : "https://api.endorser.ch"; + + logger.info("[TestView] šŸ”„ Changing API server:", { + from: currentServer, + to: newServer, + }); + + this.apiServer = newServer; + this.addUrlTestResult( + `API Server changed from ${currentServer} to ${newServer}`, + ); + } + + /** + * Changes the partner API server to the production URL. + */ + public changePartnerApiServer() { + const currentServer = this.partnerApiServer; + const newServer = + currentServer === "https://partner-api.endorser.ch" + ? "https://test-partner-api.endorser.ch" + : "https://partner-api.endorser.ch"; + + logger.info("[TestView] šŸ”„ Changing partner API server:", { + from: currentServer, + to: newServer, + }); + + this.partnerApiServer = newServer; + this.addUrlTestResult( + `Partner API Server changed from ${currentServer} to ${newServer}`, + ); + } + + /** + * Resets all URL-related settings to their initial values. + */ + public resetToDefaults() { + this.apiServer = AppString.TEST_ENDORSER_API_SERVER; + this.partnerApiServer = AppString.TEST_PARTNER_API_SERVER; + this.activeDid = ""; + this.addUrlTestResult("URL Flow Test Results Reset to Defaults."); + } + + /** + * Refreshes settings from the database to verify changes. + */ + public async refreshSettings() { + try { + logger.info("[TestView] šŸ”„ Refreshing settings from database"); + const settings = await this.$accountSettings(); + + // Update component state + this.apiServer = settings.apiServer || ""; + this.partnerApiServer = settings.partnerApiServer || ""; + + logger.info("[TestView] āœ… Settings refreshed:", { + apiServer: this.apiServer, + partnerApiServer: this.partnerApiServer, + }); + + this.addUrlTestResult( + `Settings refreshed - API Server: ${this.apiServer}, Partner API Server: ${this.partnerApiServer}`, + ); + } catch (error) { + logger.error("[TestView] āŒ Error refreshing settings:", error); + this.addUrlTestResult( + `Error refreshing settings: ${error instanceof Error ? error.message : String(error)}`, + ); + } + } + + /** + * Logs the current environment state to the console. + */ + public logEnvironmentState() { + logger.info("[TestView] 🌐 Current Environment State:", { + VITE_PLATFORM: import.meta.env.VITE_PLATFORM, + VITE_DEFAULT_ENDORSER_API_SERVER: import.meta.env + .VITE_DEFAULT_ENDORSER_API_SERVER, + VITE_DEFAULT_PARTNER_API_SERVER: import.meta.env + .VITE_DEFAULT_PARTNER_API_SERVER, + NODE_ENV: process.env.NODE_ENV, + activeDid: this.activeDid, + apiServer: this.apiServer, + partnerApiServer: this.partnerApiServer, + }); + this.$notify({ + group: "info", + type: "info", + title: "Environment State Logged", + text: "Current environment state logged to console.", + }); + } + + /** + * Gets the current platform based on the API server. + */ + public getCurrentPlatform(): string { + if (this.apiServer?.includes(AppString.PROD_ENDORSER_API_SERVER)) { + return "Production"; + } else if (this.apiServer?.includes(AppString.TEST_ENDORSER_API_SERVER)) { + return "Test"; + } else { + return "Unknown"; + } + } }