275 lines
8.5 KiB
TypeScript
275 lines
8.5 KiB
TypeScript
|
|
/**
|
||
|
|
* Alert Processor service API implementation
|
||
|
|
* Note: Alert Processor is a background service that doesn't expose direct HTTP APIs
|
||
|
|
* This service provides utilities and types for working with alert processing
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { apiClient } from '../client/apiClient';
|
||
|
|
import type {
|
||
|
|
AlertMessage,
|
||
|
|
AlertResponse,
|
||
|
|
AlertUpdateRequest,
|
||
|
|
AlertFilters,
|
||
|
|
AlertQueryParams,
|
||
|
|
AlertDashboardData,
|
||
|
|
NotificationSettings,
|
||
|
|
ChannelRoutingConfig,
|
||
|
|
WebhookConfig,
|
||
|
|
WebhookPayload,
|
||
|
|
AlertProcessingStatus,
|
||
|
|
ProcessingMetrics,
|
||
|
|
SSEAlertMessage,
|
||
|
|
PaginatedResponse,
|
||
|
|
} from '../types/alert_processor';
|
||
|
|
|
||
|
|
class AlertProcessorService {
|
||
|
|
private readonly baseUrl = '/alerts';
|
||
|
|
private readonly notificationUrl = '/notifications';
|
||
|
|
private readonly webhookUrl = '/webhooks';
|
||
|
|
|
||
|
|
// Alert Management (these would be exposed via other services like inventory, production, etc.)
|
||
|
|
async getAlerts(
|
||
|
|
tenantId: string,
|
||
|
|
queryParams?: AlertQueryParams
|
||
|
|
): Promise<PaginatedResponse<AlertResponse>> {
|
||
|
|
const params = new URLSearchParams();
|
||
|
|
|
||
|
|
if (queryParams?.severity?.length) {
|
||
|
|
queryParams.severity.forEach(s => params.append('severity', s));
|
||
|
|
}
|
||
|
|
if (queryParams?.type?.length) {
|
||
|
|
queryParams.type.forEach(t => params.append('type', t));
|
||
|
|
}
|
||
|
|
if (queryParams?.service?.length) {
|
||
|
|
queryParams.service.forEach(s => params.append('service', s));
|
||
|
|
}
|
||
|
|
if (queryParams?.item_type?.length) {
|
||
|
|
queryParams.item_type.forEach(it => params.append('item_type', it));
|
||
|
|
}
|
||
|
|
if (queryParams?.date_from) params.append('date_from', queryParams.date_from);
|
||
|
|
if (queryParams?.date_to) params.append('date_to', queryParams.date_to);
|
||
|
|
if (queryParams?.status) params.append('status', queryParams.status);
|
||
|
|
if (queryParams?.search) params.append('search', queryParams.search);
|
||
|
|
if (queryParams?.limit) params.append('limit', queryParams.limit.toString());
|
||
|
|
if (queryParams?.offset) params.append('offset', queryParams.offset.toString());
|
||
|
|
if (queryParams?.sort_by) params.append('sort_by', queryParams.sort_by);
|
||
|
|
if (queryParams?.sort_order) params.append('sort_order', queryParams.sort_order);
|
||
|
|
|
||
|
|
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||
|
|
return apiClient.get<PaginatedResponse<AlertResponse>>(
|
||
|
|
`${this.baseUrl}/tenants/${tenantId}${queryString}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async getAlert(tenantId: string, alertId: string): Promise<AlertResponse> {
|
||
|
|
return apiClient.get<AlertResponse>(
|
||
|
|
`${this.baseUrl}/tenants/${tenantId}/${alertId}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async updateAlert(
|
||
|
|
tenantId: string,
|
||
|
|
alertId: string,
|
||
|
|
updateData: AlertUpdateRequest
|
||
|
|
): Promise<AlertResponse> {
|
||
|
|
return apiClient.put<AlertResponse>(
|
||
|
|
`${this.baseUrl}/tenants/${tenantId}/${alertId}`,
|
||
|
|
updateData
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async dismissAlert(tenantId: string, alertId: string): Promise<AlertResponse> {
|
||
|
|
return apiClient.put<AlertResponse>(
|
||
|
|
`${this.baseUrl}/tenants/${tenantId}/${alertId}`,
|
||
|
|
{ status: 'dismissed' }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async acknowledgeAlert(
|
||
|
|
tenantId: string,
|
||
|
|
alertId: string,
|
||
|
|
notes?: string
|
||
|
|
): Promise<AlertResponse> {
|
||
|
|
return apiClient.put<AlertResponse>(
|
||
|
|
`${this.baseUrl}/tenants/${tenantId}/${alertId}`,
|
||
|
|
{ status: 'acknowledged', notes }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async resolveAlert(
|
||
|
|
tenantId: string,
|
||
|
|
alertId: string,
|
||
|
|
notes?: string
|
||
|
|
): Promise<AlertResponse> {
|
||
|
|
return apiClient.put<AlertResponse>(
|
||
|
|
`${this.baseUrl}/tenants/${tenantId}/${alertId}`,
|
||
|
|
{ status: 'resolved', notes }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Dashboard Data
|
||
|
|
async getDashboardData(tenantId: string): Promise<AlertDashboardData> {
|
||
|
|
return apiClient.get<AlertDashboardData>(
|
||
|
|
`${this.baseUrl}/tenants/${tenantId}/dashboard`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Notification Settings
|
||
|
|
async getNotificationSettings(tenantId: string): Promise<NotificationSettings> {
|
||
|
|
return apiClient.get<NotificationSettings>(
|
||
|
|
`${this.notificationUrl}/tenants/${tenantId}/settings`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async updateNotificationSettings(
|
||
|
|
tenantId: string,
|
||
|
|
settings: Partial<NotificationSettings>
|
||
|
|
): Promise<NotificationSettings> {
|
||
|
|
return apiClient.put<NotificationSettings>(
|
||
|
|
`${this.notificationUrl}/tenants/${tenantId}/settings`,
|
||
|
|
settings
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async getChannelRoutingConfig(): Promise<ChannelRoutingConfig> {
|
||
|
|
return apiClient.get<ChannelRoutingConfig>(`${this.notificationUrl}/routing-config`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Webhook Management
|
||
|
|
async getWebhooks(tenantId: string): Promise<WebhookConfig[]> {
|
||
|
|
return apiClient.get<WebhookConfig[]>(`${this.webhookUrl}/tenants/${tenantId}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
async createWebhook(
|
||
|
|
tenantId: string,
|
||
|
|
webhook: Omit<WebhookConfig, 'tenant_id'>
|
||
|
|
): Promise<WebhookConfig> {
|
||
|
|
return apiClient.post<WebhookConfig>(
|
||
|
|
`${this.webhookUrl}/tenants/${tenantId}`,
|
||
|
|
{ ...webhook, tenant_id: tenantId }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async updateWebhook(
|
||
|
|
tenantId: string,
|
||
|
|
webhookId: string,
|
||
|
|
webhook: Partial<WebhookConfig>
|
||
|
|
): Promise<WebhookConfig> {
|
||
|
|
return apiClient.put<WebhookConfig>(
|
||
|
|
`${this.webhookUrl}/tenants/${tenantId}/${webhookId}`,
|
||
|
|
webhook
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async deleteWebhook(tenantId: string, webhookId: string): Promise<{ message: string }> {
|
||
|
|
return apiClient.delete<{ message: string }>(
|
||
|
|
`${this.webhookUrl}/tenants/${tenantId}/${webhookId}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async testWebhook(
|
||
|
|
tenantId: string,
|
||
|
|
webhookId: string
|
||
|
|
): Promise<{ success: boolean; message: string }> {
|
||
|
|
return apiClient.post<{ success: boolean; message: string }>(
|
||
|
|
`${this.webhookUrl}/tenants/${tenantId}/${webhookId}/test`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Processing Status and Metrics
|
||
|
|
async getProcessingStatus(
|
||
|
|
tenantId: string,
|
||
|
|
alertId: string
|
||
|
|
): Promise<AlertProcessingStatus> {
|
||
|
|
return apiClient.get<AlertProcessingStatus>(
|
||
|
|
`${this.baseUrl}/tenants/${tenantId}/${alertId}/processing-status`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
async getProcessingMetrics(tenantId: string): Promise<ProcessingMetrics> {
|
||
|
|
return apiClient.get<ProcessingMetrics>(
|
||
|
|
`${this.baseUrl}/tenants/${tenantId}/processing-metrics`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// SSE (Server-Sent Events) connection helpers
|
||
|
|
getSSEUrl(tenantId: string): string {
|
||
|
|
const baseUrl = apiClient.getAxiosInstance().defaults.baseURL;
|
||
|
|
return `${baseUrl}/sse/tenants/${tenantId}/alerts`;
|
||
|
|
}
|
||
|
|
|
||
|
|
createSSEConnection(tenantId: string, token?: string): EventSource {
|
||
|
|
const sseUrl = this.getSSEUrl(tenantId);
|
||
|
|
const urlWithToken = token ? `${sseUrl}?token=${token}` : sseUrl;
|
||
|
|
|
||
|
|
return new EventSource(urlWithToken);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Utility methods for working with alerts
|
||
|
|
static formatAlertMessage(alert: AlertMessage): string {
|
||
|
|
return `[${alert.severity.toUpperCase()}] ${alert.title}: ${alert.message}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
static getAlertIcon(alert: AlertMessage): string {
|
||
|
|
const iconMap: Record<string, string> = {
|
||
|
|
inventory_low: '📦',
|
||
|
|
quality_issue: '⚠️',
|
||
|
|
delivery_delay: '🚚',
|
||
|
|
production_delay: '🏭',
|
||
|
|
equipment_failure: '🔧',
|
||
|
|
food_safety: '🦠',
|
||
|
|
temperature_alert: '🌡️',
|
||
|
|
expiry_warning: '⏰',
|
||
|
|
forecast_accuracy: '📊',
|
||
|
|
demand_spike: '📈',
|
||
|
|
supplier_issue: '🏢',
|
||
|
|
cost_optimization: '💰',
|
||
|
|
revenue_opportunity: '💡',
|
||
|
|
};
|
||
|
|
return iconMap[alert.type] || '🔔';
|
||
|
|
}
|
||
|
|
|
||
|
|
static getSeverityColor(severity: string): string {
|
||
|
|
const colorMap: Record<string, string> = {
|
||
|
|
urgent: '#dc2626', // red-600
|
||
|
|
high: '#ea580c', // orange-600
|
||
|
|
medium: '#d97706', // amber-600
|
||
|
|
low: '#65a30d', // lime-600
|
||
|
|
};
|
||
|
|
return colorMap[severity] || '#6b7280'; // gray-500
|
||
|
|
}
|
||
|
|
|
||
|
|
// Message queuing helpers (for RabbitMQ integration)
|
||
|
|
static createAlertMessage(
|
||
|
|
tenantId: string,
|
||
|
|
alert: Omit<AlertMessage, 'id' | 'tenant_id' | 'timestamp'>
|
||
|
|
): AlertMessage {
|
||
|
|
return {
|
||
|
|
id: `alert_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||
|
|
tenant_id: tenantId,
|
||
|
|
timestamp: new Date().toISOString(),
|
||
|
|
...alert,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
static validateWebhookSignature(
|
||
|
|
payload: string,
|
||
|
|
signature: string,
|
||
|
|
secret: string
|
||
|
|
): boolean {
|
||
|
|
// This would typically use crypto.createHmac for HMAC-SHA256 verification
|
||
|
|
// Implementation depends on the specific signature algorithm used
|
||
|
|
const crypto = window.crypto || (window as any).msCrypto;
|
||
|
|
if (!crypto?.subtle) {
|
||
|
|
console.warn('WebCrypto API not available for signature verification');
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Simplified example - actual implementation would use proper HMAC verification
|
||
|
|
return signature.length > 0 && secret.length > 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Create and export singleton instance
|
||
|
|
export const alertProcessorService = new AlertProcessorService();
|
||
|
|
export default alertProcessorService;
|