forked from jsnbuchanan/crowd-funder-for-time-pwa
- Revert ProfileService from broken /api/partner/userProfile endpoint to working /api/partner/userProfileForIssuer/${did}
- Fix location data display by restoring single profile object response parsing
- Remove complex array handling logic that was unnecessary for current user profiles
- Restore original working functionality that was broken by recent refactoring
Problem: Recent ProfileService creation changed endpoint from working userProfileForIssuer/${did}
to broken userProfile (list endpoint), causing location data to not display properly.
Solution: Revert to original working endpoint and response parsing logic that returns
single profile objects with location data instead of arrays of all profiles.
Files changed:
- src/services/ProfileService.ts: Restore working endpoint and simplify response parsing
Testing: Profile loading now works correctly for both existing and new profiles,
location data is properly extracted and displayed, maps render correctly.
208 lines
5.3 KiB
TypeScript
208 lines
5.3 KiB
TypeScript
/**
|
|
* 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();
|
|
};
|