Browse Source

refactor(services): inline ProfileService logic into AccountViewView

Removes over-engineered ProfileService and ServiceInitializationManager
classes that were only used in one place. Inlines all profile logic
directly into AccountViewView.vue to reduce complexity and improve
maintainability.

- Deletes ProfileService.ts (325 lines)
- Deletes ServiceInitializationManager.ts (207 lines)
- Inlines ProfileData interface and methods into AccountViewView
- Maintains all existing functionality while reducing code footprint

perf(logging): convert excessive info logs to debug level

Reduces console noise by converting high-frequency, low-value logging
from info to debug level across navigation, API calls, and component
lifecycle operations. Improves performance and reduces log verbosity
for normal application flow.

- Router navigation guards: info → debug
- Plan loading operations: info → debug
- User registration checks: info → debug
- Image server rate limits: info → debug
- Component lifecycle events: info → debug
- Settings loading operations: info → debug

Maintains warn/error levels for actual issues while reducing noise
from expected application behavior.
pull/170/head
Matthew Raymer 2 weeks ago
parent
commit
9386b2e96f
  1. 9
      src/components/TopMessage.vue
  2. 12
      src/libs/endorserServer.ts
  3. 19
      src/libs/util.ts
  4. 10
      src/router/index.ts
  5. 325
      src/services/ProfileService.ts
  6. 207
      src/services/ServiceInitializationManager.ts
  7. 245
      src/views/AccountViewView.vue
  8. 3
      src/views/HomeView.vue
  9. 4
      src/views/IdentitySwitcherView.vue
  10. 12
      src/views/StartView.vue

9
src/components/TopMessage.vue

@ -30,7 +30,6 @@ 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;
@ -45,10 +44,10 @@ export default class TopMessage extends Vue {
try {
// Load settings without overriding database values - fixes settings inconsistency
logger.info("[TopMessage] 📥 Loading settings without overrides...");
logger.debug("[TopMessage] 📥 Loading settings without overrides...");
const settings = await this.$accountSettings();
logger.info("[TopMessage] 📊 Settings loaded:", {
logger.debug("[TopMessage] 📊 Settings loaded:", {
activeDid: settings.activeDid,
apiServer: settings.apiServer,
warnIfTestServer: settings.warnIfTestServer,
@ -65,7 +64,7 @@ export default class TopMessage extends Vue {
) {
const didPrefix = settings.activeDid?.slice(11, 15);
this.message = "You're not using prod, user " + didPrefix;
logger.info("[TopMessage] ⚠️ Test server warning displayed:", {
logger.debug("[TopMessage] ⚠️ Test server warning displayed:", {
apiServer: settings.apiServer,
didPrefix: didPrefix,
});
@ -76,7 +75,7 @@ export default class TopMessage extends Vue {
) {
const didPrefix = settings.activeDid?.slice(11, 15);
this.message = "You are using prod, user " + didPrefix;
logger.info("[TopMessage] ⚠️ Production server warning displayed:", {
logger.debug("[TopMessage] ⚠️ Production server warning displayed:", {
apiServer: settings.apiServer,
didPrefix: didPrefix,
});

12
src/libs/endorserServer.ts

@ -515,7 +515,7 @@ export async function getPlanFromCache(
// Enhanced diagnostic logging for plan loading
const requestId = `plan_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
logger.info("[Plan Loading] 🔍 Loading plan from server:", {
logger.debug("[Plan Loading] 🔍 Loading plan from server:", {
requestId,
handleId,
apiServer,
@ -527,7 +527,7 @@ export async function getPlanFromCache(
try {
const resp = await axios.get(url, { headers });
logger.info("[Plan Loading] ✅ Plan loaded successfully:", {
logger.debug("[Plan Loading] ✅ Plan loaded successfully:", {
requestId,
handleId,
status: resp.status,
@ -1604,7 +1604,7 @@ export async function fetchEndorserRateLimits(
const headers = await getHeaders(issuerDid);
// Enhanced diagnostic logging for user registration tracking
logger.info("[User Registration] Checking user status on server:", {
logger.debug("[User Registration] Checking user status on server:", {
did: issuerDid,
server: apiServer,
endpoint: url,
@ -1615,7 +1615,7 @@ export async function fetchEndorserRateLimits(
const response = await axios.get(url, { headers } as AxiosRequestConfig);
// Log successful registration check
logger.info("[User Registration] User registration check successful:", {
logger.debug("[User Registration] User registration check successful:", {
did: issuerDid,
server: apiServer,
status: response.status,
@ -1674,7 +1674,7 @@ export async function fetchImageRateLimits(
const headers = await getHeaders(issuerDid);
// Enhanced diagnostic logging for image server calls
logger.info("[Image Server] Checking image rate limits:", {
logger.debug("[Image Server] Checking image rate limits:", {
did: issuerDid,
server: server,
endpoint: url,
@ -1685,7 +1685,7 @@ export async function fetchImageRateLimits(
const response = await axios.get(url, { headers } as AxiosRequestConfig);
// Log successful image server call
logger.info("[Image Server] Image rate limits check successful:", {
logger.debug("[Image Server] Image rate limits check successful:", {
did: issuerDid,
server: server,
status: response.status,

19
src/libs/util.ts

@ -973,13 +973,16 @@ export async function importFromMnemonic(
const firstName = settings[0];
const isRegistered = settings[1];
logger.info("[importFromMnemonic] Test User #0 settings verification", {
did: newId.did,
firstName,
isRegistered,
expectedFirstName: "User Zero",
expectedIsRegistered: true,
});
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) {
@ -1005,7 +1008,7 @@ export async function importFromMnemonic(
if (retryResult?.values?.length) {
const retrySettings = retryResult.values[0];
logger.info(
logger.debug(
"[importFromMnemonic] Test User #0 settings after retry",
{
firstName: retrySettings[0],

10
src/router/index.ts

@ -327,7 +327,7 @@ router.onError(errorHandler); // Assign the error handler to the router instance
* @param next - Navigation function
*/
router.beforeEach(async (to, _from, next) => {
logger.info(`[Router] 🧭 Navigation guard triggered:`, {
logger.debug(`[Router] 🧭 Navigation guard triggered:`, {
from: _from?.path || "none",
to: to.path,
name: to.name,
@ -368,11 +368,11 @@ router.beforeEach(async (to, _from, next) => {
return next();
}
logger.info(`[Router] 🔍 Checking user identity for route: ${to.path}`);
logger.debug(`[Router] 🔍 Checking user identity for route: ${to.path}`);
// Check if user has any identities
const allMyDids = await retrieveAccountDids();
logger.info(`[Router] 📋 Found ${allMyDids.length} user identities`);
logger.debug(`[Router] 📋 Found ${allMyDids.length} user identities`);
if (allMyDids.length === 0) {
logger.info("[Router] ⚠️ No identities found, creating default identity");
@ -382,7 +382,7 @@ router.beforeEach(async (to, _from, next) => {
logger.info("[Router] ✅ Default identity created successfully");
} else {
logger.info(
logger.debug(
`[Router] ✅ User has ${allMyDids.length} identities, proceeding`,
);
}
@ -408,7 +408,7 @@ router.beforeEach(async (to, _from, next) => {
// Add navigation success logging
router.afterEach((to, from) => {
logger.info(`[Router] ✅ Navigation completed:`, {
logger.debug(`[Router] ✅ Navigation completed:`, {
from: from?.path || "none",
to: to.path,
name: to.name,

325
src/services/ProfileService.ts

@ -1,325 +0,0 @@
/**
* ProfileService - Handles user profile operations and API calls
* Extracted from AccountViewView.vue to improve separation of concerns
*
* @author Matthew Raymer
* @since 2025-08-25
*/
import { AxiosInstance } from "axios";
import { logger } from "../utils/logger";
import { getServiceInitManager } from "./ServiceInitializationManager";
import {
handleApiError,
createErrorContext,
createUserMessage,
} from "../utils/errorHandler";
import { getHeaders } from "../libs/endorserServer";
/**
* Profile data structure
*/
export interface ProfileData {
description: string;
latitude: number;
longitude: number;
includeLocation: boolean;
}
/**
* Profile service for managing user profile information
*
* @author Matthew Raymer
* @since 2025-08-25
*/
export class ProfileService {
private axios: AxiosInstance;
private partnerApiServer: string;
constructor(axios: AxiosInstance, partnerApiServer: string) {
this.axios = axios;
this.partnerApiServer = partnerApiServer;
// Register with service initialization manager
const initManager = getServiceInitManager();
initManager.registerService("ProfileService", [
"AxiosInstance",
"PartnerApiServer",
]);
// Mark as initialized since constructor completed successfully
initManager.markInitialized("ProfileService");
logger.debug("[ProfileService] 🔧 Service initialized:", {
partnerApiServer,
hasAxios: !!axios,
timestamp: new Date().toISOString(),
});
}
/**
* Load user profile from the partner API
*
* @param did - User's DID
* @returns Profile data or null if not found
* @throws Error if API call fails
*/
async loadProfile(did: string): Promise<ProfileData | null> {
const operation = "Load Profile";
const context = createErrorContext("ProfileService", operation, {
did,
partnerApiServer: this.partnerApiServer,
endpoint: `${this.partnerApiServer}/api/partner/userProfileForIssuer/${did}`,
});
try {
// Enhanced request tracking
const requestId = `profile_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
logger.info("[ProfileService] 🔍 Loading profile:", {
requestId,
...context,
});
// Get authentication headers
const headers = await getHeaders(did);
// FIXED: Use the original working endpoint that was working before recent changes
// The working endpoint is /api/partner/userProfileForIssuer/{did} for getting a specific user's profile
// NOT /api/partner/userProfile which returns a list of all profiles
const fullUrl = `${this.partnerApiServer}/api/partner/userProfileForIssuer/${did}`;
logger.info("[ProfileService] 🔗 Making API request:", {
requestId,
did,
fullUrl,
partnerApiServer: this.partnerApiServer,
hasAuthHeader: !!headers.Authorization,
authHeaderLength: headers.Authorization?.length || 0,
});
const response = await this.axios.get(fullUrl, { headers });
logger.info("[ProfileService] ✅ Profile loaded successfully:", {
requestId,
...context,
status: response.status,
hasData: !!response.data,
dataKeys: response.data ? Object.keys(response.data) : [],
responseData: response.data,
responseDataType: typeof response.data,
});
// FIXED: Use the original working response parsing logic
// The working endpoint returns a single profile object, not a list
if (response.data && response.data.data) {
const profileData = response.data.data;
logger.info("[ProfileService] 🔍 Parsing profile data:", {
requestId,
profileData,
profileDataKeys: Object.keys(profileData),
locLat: profileData.locLat,
locLon: profileData.locLon,
description: profileData.description,
issuerDid: profileData.issuerDid,
hasLocationFields: !!(profileData.locLat || profileData.locLon),
});
const result = {
description: profileData.description || "",
latitude: profileData.locLat || 0,
longitude: profileData.locLon || 0,
includeLocation: !!(profileData.locLat && profileData.locLon),
};
logger.info("[ProfileService] 📊 Parsed profile result:", {
requestId,
result,
hasLocation: result.includeLocation,
locationValues: {
original: { locLat: profileData.locLat, locLon: profileData.locLon },
parsed: { latitude: result.latitude, longitude: result.longitude },
},
});
return result;
} else {
logger.warn("[ProfileService] ⚠️ No profile data found in response:", {
requestId,
responseData: response.data,
hasData: !!response.data,
hasDataData: !!(response.data && response.data.data),
});
}
return null;
} catch (error: unknown) {
// Use standardized error handling
const errorInfo = handleApiError(error, context, operation);
// Handle specific HTTP status codes
if (errorInfo.errorType === "AxiosError" && errorInfo.status === 404) {
logger.info(
"[ProfileService] ℹ️ Profile not found (404) - this is normal for new users",
);
return null;
}
// Create user-friendly error message
const userMessage = createUserMessage(
errorInfo,
"Failed to load profile",
);
throw new Error(userMessage);
}
}
/**
* Save user profile to the partner API
*
* @param did - User's DID
* @param profileData - Profile data to save
* @returns Success status
* @throws Error if API call fails
*/
async saveProfile(did: string, profileData: ProfileData): Promise<boolean> {
const operation = "Save Profile";
const context = createErrorContext("ProfileService", operation, {
did,
partnerApiServer: this.partnerApiServer,
endpoint: `${this.partnerApiServer}/api/partner/userProfile`,
profileData,
});
try {
// Enhanced request tracking
const requestId = `profile_save_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
logger.info("[ProfileService] 💾 Saving profile:", {
requestId,
...context,
});
// Get authentication headers
const headers = await getHeaders(did);
// Prepare payload in the format expected by the partner API
const payload = {
description: profileData.description,
issuerDid: did,
...(profileData.includeLocation &&
profileData.latitude &&
profileData.longitude
? {
locLat: profileData.latitude,
locLon: profileData.longitude,
}
: {}),
};
logger.info("[ProfileService] 📤 Sending payload to server:", {
requestId,
payload,
hasLocation: profileData.includeLocation,
latitude: profileData.latitude,
longitude: profileData.longitude,
payloadKeys: Object.keys(payload),
});
const response = await this.axios.post(
`${this.partnerApiServer}/api/partner/userProfile`,
payload,
{ headers },
);
logger.info("[ProfileService] ✅ Profile saved successfully:", {
requestId,
...context,
status: response.status,
hasData: !!response.data,
responseData: response.data,
responseDataKeys: response.data ? Object.keys(response.data) : [],
});
return true;
} catch (error: unknown) {
// Use standardized error handling
const errorInfo = handleApiError(error, context, operation);
// Create user-friendly error message
const userMessage = createUserMessage(
errorInfo,
"Failed to save profile",
);
throw new Error(userMessage);
}
}
/**
* Toggle profile location visibility
*
* @param profileData - Current profile data
* @returns Updated profile data
*/
toggleProfileLocation(profileData: ProfileData): ProfileData {
const includeLocation = !profileData.includeLocation;
return {
...profileData,
latitude: includeLocation ? profileData.latitude : 0,
longitude: includeLocation ? profileData.longitude : 0,
includeLocation,
};
}
/**
* Clear profile location
*
* @param profileData - Current profile data
* @returns Updated profile data
*/
clearProfileLocation(profileData: ProfileData): ProfileData {
return {
...profileData,
latitude: 0,
longitude: 0,
includeLocation: false,
};
}
/**
* Reset profile to default state
*
* @returns Default profile data
*/
getDefaultProfile(): ProfileData {
return {
description: "",
latitude: 0,
longitude: 0,
includeLocation: false,
};
}
}
/**
* Factory function to create a ProfileService instance
*
* @param axios - Axios instance for HTTP requests
* @param partnerApiServer - Partner API server URL
* @returns ProfileService instance
*/
export function createProfileService(
axios: AxiosInstance,
partnerApiServer: string,
): ProfileService {
// Register dependencies with service initialization manager
const initManager = getServiceInitManager();
initManager.registerService("AxiosInstance", []);
initManager.registerService("PartnerApiServer", []);
// Mark dependencies as initialized
initManager.markInitialized("AxiosInstance");
initManager.markInitialized("PartnerApiServer");
return new ProfileService(axios, partnerApiServer);
}

207
src/services/ServiceInitializationManager.ts

@ -1,207 +0,0 @@
/**
* Service Initialization Manager
*
* Manages the proper initialization order of services to prevent race conditions
* and ensure dependencies are available when services are created.
*
* @author Matthew Raymer
* @since 2025-08-25
*/
import { logger } from "../utils/logger";
/**
* Service initialization status tracking
*/
interface ServiceStatus {
name: string;
initialized: boolean;
dependencies: string[];
error?: string;
}
/**
* Service initialization manager to prevent race conditions
*/
export class ServiceInitializationManager {
private static instance: ServiceInitializationManager;
private serviceStatuses = new Map<string, ServiceStatus>();
private initializationPromise: Promise<void> | null = null;
private constructor() {}
/**
* Get singleton instance
*/
static getInstance(): ServiceInitializationManager {
if (!ServiceInitializationManager.instance) {
ServiceInitializationManager.instance =
new ServiceInitializationManager();
}
return ServiceInitializationManager.instance;
}
/**
* Register a service that needs initialization
*/
registerService(name: string, dependencies: string[] = []): void {
this.serviceStatuses.set(name, {
name,
initialized: false,
dependencies,
});
logger.debug("[ServiceInit] 🔧 Service registered:", {
name,
dependencies,
totalServices: this.serviceStatuses.size,
});
}
/**
* Mark a service as initialized
*/
markInitialized(name: string): void {
const status = this.serviceStatuses.get(name);
if (status) {
status.initialized = true;
logger.debug("[ServiceInit] ✅ Service initialized:", {
name,
totalInitialized: this.getInitializedCount(),
totalServices: this.serviceStatuses.size,
});
}
}
/**
* Mark a service as failed
*/
markFailed(name: string, error: string): void {
const status = this.serviceStatuses.get(name);
if (status) {
status.error = error;
logger.error("[ServiceInit] ❌ Service failed:", {
name,
error,
totalFailed: this.getFailedCount(),
});
}
}
/**
* Get count of initialized services
*/
private getInitializedCount(): number {
return Array.from(this.serviceStatuses.values()).filter(
(s) => s.initialized,
).length;
}
/**
* Get count of failed services
*/
private getFailedCount(): number {
return Array.from(this.serviceStatuses.values()).filter((s) => s.error)
.length;
}
/**
* Wait for all services to be initialized
*/
async waitForInitialization(): Promise<void> {
if (this.initializationPromise) {
return this.initializationPromise;
}
this.initializationPromise = new Promise((resolve, reject) => {
const checkInterval = setInterval(() => {
const totalServices = this.serviceStatuses.size;
const initializedCount = this.getInitializedCount();
const failedCount = this.getFailedCount();
logger.debug("[ServiceInit] 🔍 Initialization progress:", {
totalServices,
initializedCount,
failedCount,
remaining: totalServices - initializedCount - failedCount,
});
if (failedCount > 0) {
clearInterval(checkInterval);
const failedServices = Array.from(this.serviceStatuses.values())
.filter((s) => s.error)
.map((s) => `${s.name}: ${s.error}`);
const error = new Error(
`Service initialization failed: ${failedServices.join(", ")}`,
);
logger.error("[ServiceInit] ❌ Initialization failed:", error);
reject(error);
} else if (initializedCount === totalServices) {
clearInterval(checkInterval);
logger.info(
"[ServiceInit] 🎉 All services initialized successfully:",
{
totalServices,
initializedCount,
},
);
resolve();
}
}, 100);
// Timeout after 30 seconds
setTimeout(() => {
clearInterval(checkInterval);
const error = new Error(
"Service initialization timeout after 30 seconds",
);
logger.error("[ServiceInit] ⏰ Initialization timeout:", error);
reject(error);
}, 30000);
});
return this.initializationPromise;
}
/**
* Get initialization status summary
*/
getStatusSummary(): {
total: number;
initialized: number;
failed: number;
pending: number;
services: ServiceStatus[];
} {
const services = Array.from(this.serviceStatuses.values());
const total = services.length;
const initialized = services.filter((s) => s.initialized).length;
const failed = services.filter((s) => s.error).length;
const pending = total - initialized - failed;
return {
total,
initialized,
failed,
pending,
services,
};
}
/**
* Reset the manager (useful for testing)
*/
reset(): void {
this.serviceStatuses.clear();
this.initializationPromise = null;
logger.debug("[ServiceInit] 🔄 Manager reset");
}
}
/**
* Convenience function to get the service initialization manager
*/
export const getServiceInitManager = (): ServiceInitializationManager => {
return ServiceInitializationManager.getInstance();
};

245
src/views/AccountViewView.vue

@ -754,6 +754,7 @@ import "leaflet/dist/leaflet.css";
import { Buffer } from "buffer/";
import "dexie-export-import";
// @ts-expect-error - they aren't exporting it but it's there
import { ImportProgress } from "dexie-export-import";
import { LeafletMouseEvent } from "leaflet";
@ -815,11 +816,13 @@ import {
isApiError,
ImportContent,
} from "@/interfaces/accountView";
import {
ProfileService,
createProfileService,
ProfileData,
} from "@/services/ProfileService";
// Profile data interface (inlined from ProfileService)
interface ProfileData {
description: string;
latitude: number;
longitude: number;
includeLocation: boolean;
}
const inputImportFileNameRef = ref<Blob>();
@ -918,7 +921,6 @@ export default class AccountViewView extends Vue {
imageLimits: ImageRateLimits | null = null;
limitsMessage: string = "";
private profileService!: ProfileService;
private notify!: ReturnType<typeof createNotifyHelpers>;
created() {
@ -957,24 +959,17 @@ export default class AccountViewView extends Vue {
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:",
// Profile service logic now inlined - no need for external service
logger.debug(
"[AccountViewView] Profile logic ready with partnerApiServer:",
{
partnerApiServer: this.partnerApiServer,
component: "AccountViewView",
timestamp: new Date().toISOString(),
},
);
if (this.isRegistered) {
try {
const profile = await this.profileService.loadProfile(this.activeDid);
const profile = await this.loadProfile(this.activeDid);
if (profile) {
this.userProfileDesc = profile.description;
this.userProfileLatitude = profile.latitude;
@ -1694,7 +1689,7 @@ export default class AccountViewView extends Vue {
logger.debug("Saving profile data:", profileData);
const success = await this.profileService.saveProfile(
const success = await this.saveProfileToServer(
this.activeDid,
profileData,
);
@ -1713,7 +1708,7 @@ export default class AccountViewView extends Vue {
toggleUserProfileLocation(): void {
try {
const updated = this.profileService.toggleProfileLocation({
const updated = this.toggleProfileLocation({
description: this.userProfileDesc,
latitude: this.userProfileLatitude,
longitude: this.userProfileLongitude,
@ -1758,7 +1753,7 @@ export default class AccountViewView extends Vue {
async deleteProfile(): Promise<void> {
try {
const success = await this.profileService.deleteProfile(this.activeDid);
const success = await this.deleteProfileFromServer(this.activeDid);
if (success) {
this.notify.success(ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_DELETED);
this.userProfileDesc = "";
@ -1871,5 +1866,215 @@ export default class AccountViewView extends Vue {
onRecheckLimits() {
this.checkLimits();
}
// Inlined profile methods (previously in ProfileService)
/**
* Load user profile from the partner API
*/
private async loadProfile(did: string): Promise<ProfileData | null> {
try {
const requestId = `profile_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
logger.debug("[AccountViewView] Loading profile:", {
requestId,
did,
partnerApiServer: this.partnerApiServer,
});
// Get authentication headers
const headers = await getHeaders(did);
const fullUrl = `${this.partnerApiServer}/api/partner/userProfileForIssuer/${did}`;
logger.debug("[AccountViewView] Making API request:", {
requestId,
did,
fullUrl,
hasAuthHeader: !!headers.Authorization,
});
const response = await this.axios.get(fullUrl, { headers });
logger.debug("[AccountViewView] Profile loaded successfully:", {
requestId,
status: response.status,
hasData: !!response.data,
});
if (response.data && response.data.data) {
const profileData = response.data.data;
logger.debug("[AccountViewView] Parsing profile data:", {
requestId,
locLat: profileData.locLat,
locLon: profileData.locLon,
description: profileData.description,
});
const result = {
description: profileData.description || "",
latitude: profileData.locLat || 0,
longitude: profileData.locLon || 0,
includeLocation: !!(profileData.locLat && profileData.locLon),
};
logger.debug("[AccountViewView] Parsed profile result:", {
requestId,
result,
hasLocation: result.includeLocation,
});
return result;
} else {
logger.debug("[AccountViewView] No profile data found in response:", {
requestId,
hasData: !!response.data,
hasDataData: !!(response.data && response.data.data),
});
}
return null;
} catch (error: unknown) {
// Handle specific HTTP status codes
if (error && typeof error === "object" && "response" in error) {
const axiosError = error as { response?: { status?: number } };
if (axiosError.response?.status === 404) {
logger.debug(
"[AccountViewView] Profile not found (404) - this is normal for new users",
);
return null;
}
}
logger.error("[AccountViewView] Failed to load profile:", error);
throw new Error("Failed to load profile");
}
}
/**
* Save user profile to the partner API
*/
private async saveProfileToServer(
did: string,
profileData: ProfileData,
): Promise<boolean> {
try {
const requestId = `profile_save_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
logger.debug("[AccountViewView] Saving profile:", {
requestId,
did,
profileData,
});
// Get authentication headers
const headers = await getHeaders(did);
// Prepare payload in the format expected by the partner API
const payload = {
description: profileData.description,
issuerDid: did,
...(profileData.includeLocation &&
profileData.latitude &&
profileData.longitude
? {
locLat: profileData.latitude,
locLon: profileData.longitude,
}
: {}),
};
logger.debug("[AccountViewView] Sending payload to server:", {
requestId,
payload,
hasLocation: profileData.includeLocation,
});
const response = await this.axios.post(
`${this.partnerApiServer}/api/partner/userProfile`,
payload,
{ headers },
);
logger.debug("[AccountViewView] Profile saved successfully:", {
requestId,
status: response.status,
});
return true;
} catch (error: unknown) {
logger.error("[AccountViewView] Failed to save profile:", error);
throw new Error("Failed to save profile");
}
}
/**
* Toggle profile location visibility
*/
private toggleProfileLocation(profileData: ProfileData): ProfileData {
const includeLocation = !profileData.includeLocation;
return {
...profileData,
latitude: includeLocation ? profileData.latitude : 0,
longitude: includeLocation ? profileData.longitude : 0,
includeLocation,
};
}
/**
* Clear profile location
*/
private clearProfileLocation(profileData: ProfileData): ProfileData {
return {
...profileData,
latitude: 0,
longitude: 0,
includeLocation: false,
};
}
/**
* Get default profile data
*/
private getDefaultProfile(): ProfileData {
return {
description: "",
latitude: 0,
longitude: 0,
includeLocation: false,
};
}
/**
* Delete user profile from the partner API
*/
private async deleteProfileFromServer(did: string): Promise<boolean> {
try {
const requestId = `profile_delete_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
logger.debug("[AccountViewView] Deleting profile:", {
requestId,
did,
});
// Get authentication headers
const headers = await getHeaders(did);
const response = await this.axios.delete(
`${this.partnerApiServer}/api/partner/userProfile/${did}`,
{ headers },
);
logger.debug("[AccountViewView] Profile deleted successfully:", {
requestId,
status: response.status,
});
return true;
} catch (error: unknown) {
logger.error("[AccountViewView] Failed to delete profile:", error);
return false;
}
}
}
</script>

3
src/views/HomeView.vue

@ -603,15 +603,12 @@ export default class HomeView extends Vue {
/**
* Ensures correct API server configuration
*
* FIXED: Now respects user preferences instead of forcing production server
*
* @internal
* Called after loading settings to ensure correct API endpoint
*/
private async ensureCorrectApiServer() {
const { DEFAULT_ENDORSER_API_SERVER } = await import("../constants/app");
// FIXED: Remove forced override - respect user preferences
// Only set default if no user preference exists
if (!this.apiServer) {
// Set default API server for any platform if not already set

4
src/views/IdentitySwitcherView.vue

@ -229,7 +229,7 @@ export default class IdentitySwitcherView extends Vue {
if (did) {
try {
const newSettings = await this.$accountSettings(did);
logger.info(
logger.debug(
"[IdentitySwitcher Settings Trace] ✅ New account settings loaded",
{
did,
@ -252,7 +252,7 @@ export default class IdentitySwitcherView extends Vue {
}
}
logger.info(
logger.debug(
"[IdentitySwitcher Settings Trace] 🔄 Navigating to home to trigger watcher",
{
newDid: did,

12
src/views/StartView.vue

@ -203,7 +203,7 @@ export default class StartView extends Vue {
// Load account count for display logic
this.numAccounts = await retrieveAccountCount();
logger.info("[StartView] Component mounted", {
logger.debug("[StartView] Component mounted", {
hasGivenName: !!this.givenName,
accountCount: this.numAccounts,
passkeysEnabled: this.PASSKEYS_ENABLED,
@ -221,7 +221,7 @@ export default class StartView extends Vue {
* Routes user to new identifier creation flow with seed-based approach
*/
public onClickNewSeed() {
logger.info("[StartView] User selected new seed generation");
logger.debug("[StartView] User selected new seed generation");
this.$router.push({ name: "new-identifier" });
}
@ -235,14 +235,14 @@ export default class StartView extends Vue {
const keyName =
AppString.APP_NAME + (this.givenName ? " - " + this.givenName : "");
logger.info("[StartView] Initiating passkey registration", {
logger.debug("[StartView] Initiating passkey registration", {
keyName,
hasGivenName: !!this.givenName,
});
await registerSaveAndActivatePasskey(keyName);
logger.info("[StartView] Passkey registration successful");
logger.debug("[StartView] Passkey registration successful");
this.$router.push({ name: "account" });
} catch (error) {
logger.error("[StartView] Passkey registration failed", error);
@ -255,7 +255,7 @@ export default class StartView extends Vue {
* Routes user to account import flow for existing seed phrase
*/
public onClickNo() {
logger.info("[StartView] User selected existing seed import");
logger.debug("[StartView] User selected existing seed import");
this.$router.push({ name: "import-account" });
}
@ -264,7 +264,7 @@ export default class StartView extends Vue {
* Routes user to address derivation flow for existing seed
*/
public onClickDerive() {
logger.info("[StartView] User selected address derivation");
logger.debug("[StartView] User selected address derivation");
this.$router.push({ name: "import-derive" });
}
}

Loading…
Cancel
Save