Start integrating the onboarding flow with backend 7

This commit is contained in:
Urtzi Alfaro
2025-09-05 22:46:28 +02:00
parent 069954981a
commit 548a2ddd11
28 changed files with 5544 additions and 1014 deletions

View File

@@ -0,0 +1,275 @@
/**
* 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;