// src/api/services/index.ts /** * Main API Services Index * Central import point for all service modules */ // Import all service classes export { AuthService, authService } from './authService'; export { DataService, dataService } from './dataService'; export { TrainingService, trainingService } from './trainingService'; export { ForecastingService, forecastingService } from './forecastingService'; export { NotificationService, notificationService } from './notificationService'; export { TenantService, tenantService } from './tenantService'; // Import base API client for custom implementations export { apiClient } from '../base/apiClient'; // Re-export all types from the main types file export * from '../types/api'; // Export additional service-specific types export type { DashboardStats, UploadResponse, DataValidation, } from './dataService'; export type { TrainingJobProgress, ModelMetrics, TrainingConfiguration, } from './trainingService'; export type { SingleForecastRequest, BatchForecastRequest, ForecastAlert, QuickForecast, BatchForecastStatus, } from './forecastingService'; export type { NotificationCreate, NotificationResponse, NotificationHistory, NotificationTemplate, NotificationStats, BulkNotificationRequest, BulkNotificationStatus, } from './notificationService'; export type { TenantCreate, TenantUpdate, TenantSettings, TenantStats, TenantUser, InviteUser, } from './tenantService'; // Create a unified API object for convenience export const api = { auth: authService, data: dataService, training: trainingService, forecasting: forecastingService, notifications: notificationService, tenant: tenantService, } as const; // Type for the unified API object export type ApiServices = typeof api; // Service status type for monitoring export interface ServiceStatus { service: string; status: 'healthy' | 'degraded' | 'down'; lastChecked: Date; responseTime?: number; error?: string; } // Health check utilities export class ApiHealthChecker { private static healthCheckEndpoints = { auth: '/auth/health', data: '/data/health', training: '/training/health', forecasting: '/forecasting/health', notifications: '/notifications/health', tenant: '/tenants/health', }; /** * Check health of all services */ static async checkAllServices(): Promise> { const results: Record = {}; for (const [serviceName, endpoint] of Object.entries(this.healthCheckEndpoints)) { results[serviceName] = await this.checkService(serviceName, endpoint); } return results; } /** * Check health of a specific service */ static async checkService(serviceName: string, endpoint: string): Promise { const startTime = Date.now(); try { const response = await apiClient.get(endpoint, { timeout: 5000 }); const responseTime = Date.now() - startTime; return { service: serviceName, status: response.status === 200 ? 'healthy' : 'degraded', lastChecked: new Date(), responseTime, }; } catch (error: any) { return { service: serviceName, status: 'down', lastChecked: new Date(), responseTime: Date.now() - startTime, error: error.message || 'Unknown error', }; } } /** * Check if core services are available */ static async checkCoreServices(): Promise { const coreServices = ['auth', 'data', 'forecasting']; const results = await this.checkAllServices(); return coreServices.every( service => results[service]?.status === 'healthy' ); } } // Error handling utilities export class ApiErrorHandler { /** * Handle common API errors */ static handleError(error: any): never { if (error.response) { // Server responded with error status const { status, data } = error.response; switch (status) { case 401: throw new Error('Authentication required. Please log in again.'); case 403: throw new Error('You do not have permission to perform this action.'); case 404: throw new Error('The requested resource was not found.'); case 429: throw new Error('Too many requests. Please try again later.'); case 500: throw new Error('Server error. Please try again later.'); default: throw new Error(data?.message || `Request failed with status ${status}`); } } else if (error.request) { // Network error throw new Error('Network error. Please check your connection.'); } else { // Other error throw new Error(error.message || 'An unexpected error occurred.'); } } /** * Retry failed requests with exponential backoff */ static async retryRequest( requestFn: () => Promise, maxRetries: number = 3, baseDelay: number = 1000 ): Promise { let lastError: any; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await requestFn(); } catch (error: any) { lastError = error; // Don't retry on certain errors if (error.response?.status === 401 || error.response?.status === 403) { throw error; } // Don't retry on last attempt if (attempt === maxRetries) { break; } // Wait before retrying with exponential backoff const delay = baseDelay * Math.pow(2, attempt); await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError; } } // Request cache utilities for performance optimization export class ApiCache { private static cache = new Map(); /** * Get cached response */ static get(key: string): T | null { const cached = this.cache.get(key); if (cached && cached.expires > Date.now()) { return cached.data; } // Remove expired cache entry if (cached) { this.cache.delete(key); } return null; } /** * Set cached response */ static set(key: string, data: any, ttlMs: number = 300000): void { // 5 minutes default const expires = Date.now() + ttlMs; this.cache.set(key, { data, expires }); } /** * Clear cache */ static clear(): void { this.cache.clear(); } /** * Clear expired entries */ static cleanup(): void { const now = Date.now(); for (const [key, value] of this.cache.entries()) { if (value.expires <= now) { this.cache.delete(key); } } } /** * Generate cache key */ static generateKey(method: string, url: string, params?: any): string { const paramStr = params ? JSON.stringify(params) : ''; return `${method}:${url}:${paramStr}`; } } // Export default as the unified API object export default api;