275 lines
6.9 KiB
TypeScript
275 lines
6.9 KiB
TypeScript
// 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<Record<string, ServiceStatus>> {
|
|
const results: Record<string, ServiceStatus> = {};
|
|
|
|
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<ServiceStatus> {
|
|
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<boolean> {
|
|
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<T>(
|
|
requestFn: () => Promise<T>,
|
|
maxRetries: number = 3,
|
|
baseDelay: number = 1000
|
|
): Promise<T> {
|
|
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<string, { data: any; expires: number }>();
|
|
|
|
/**
|
|
* Get cached response
|
|
*/
|
|
static get<T>(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; |