import { Injectable, Logger } from '@nestjs/common'; import { BaseNotificationAdapter, NotificationDeliveryResult } from './channel-adapter.interface'; /** * Email Channel Adapter * Handles email notifications */ @Injectable() export class EmailChannelAdapter extends BaseNotificationAdapter { readonly channelType = 'email'; constructor(private readonly emailService: any) { super('email'); } async isAvailable(): Promise { try { // Check email service configuration return !!(this.emailService && await this.emailService.isConfigured?.()); } catch { return false; } } getConfigSchema(): Record { return { type: 'object', properties: { smtp: { type: 'object', properties: { host: { type: 'string' }, port: { type: 'number' }, secure: { type: 'boolean' }, auth: { type: 'object', properties: { user: { type: 'string' }, pass: { type: 'string' }, }, }, }, }, }, }; } protected transform(notification: any): any { return { to: notification.recipient, subject: notification.title, text: notification.message, html: notification.html || notification.message, attachments: notification.attachments || [], }; } async send(notification: any, preferences?: any): Promise { try { if (!this.validate(notification)) { return { success: false, error: 'Invalid notification data', }; } const emailData = this.transform(notification); const result = await this.emailService.sendMail(emailData); const deliveryResult: NotificationDeliveryResult = { success: true, messageId: result.messageId, metadata: { provider: 'smtp', ...result, }, }; this.logAttempt(notification, deliveryResult); return deliveryResult; } catch (error) { const deliveryResult: NotificationDeliveryResult = { success: false, error: error.message, }; this.logAttempt(notification, deliveryResult); return deliveryResult; } } } /** * Push Notification Channel Adapter * Handles mobile/web push notifications */ @Injectable() export class PushChannelAdapter extends BaseNotificationAdapter { readonly channelType = 'push'; constructor(private readonly pushService: any) { super('push'); } async isAvailable(): Promise { try { return !!(this.pushService && await this.pushService.isConfigured?.()); } catch { return false; } } getConfigSchema(): Record { return { type: 'object', properties: { fcm: { type: 'object', properties: { serverKey: { type: 'string' }, }, }, apns: { type: 'object', properties: { keyId: { type: 'string' }, teamId: { type: 'string' }, bundleId: { type: 'string' }, }, }, }, }; } protected transform(notification: any): any { return { title: notification.title, body: notification.message, icon: notification.icon, image: notification.image, data: notification.data || {}, actions: notification.actions || [], badge: notification.badge, sound: notification.sound || 'default', }; } async send(notification: any, preferences?: any): Promise { try { if (!notification.deviceToken) { return { success: false, error: 'Device token required for push notifications', }; } const pushData = this.transform(notification); const result = await this.pushService.send(notification.deviceToken, pushData); const deliveryResult: NotificationDeliveryResult = { success: true, messageId: result.messageId, metadata: { provider: 'fcm/apns', deviceToken: notification.deviceToken, ...result, }, }; this.logAttempt(notification, deliveryResult); return deliveryResult; } catch (error) { const deliveryResult: NotificationDeliveryResult = { success: false, error: error.message, }; this.logAttempt(notification, deliveryResult); return deliveryResult; } } } /** * WhatsApp Channel Adapter * Handles WhatsApp Business API notifications */ @Injectable() export class WhatsAppChannelAdapter extends BaseNotificationAdapter { readonly channelType = 'whatsapp'; constructor(private readonly whatsappService: any) { super('whatsapp'); } async isAvailable(): Promise { try { return !!(this.whatsappService && await this.whatsappService.isConfigured?.()); } catch { return false; } } getConfigSchema(): Record { return { type: 'object', properties: { accessToken: { type: 'string' }, phoneNumberId: { type: 'string' }, version: { type: 'string' }, webhookVerifyToken: { type: 'string' }, }, }; } protected transform(notification: any): any { return { to: notification.recipient, type: notification.template ? 'template' : 'text', text: { body: notification.message }, template: notification.template, }; } async send(notification: any, preferences?: any): Promise { try { if (!notification.recipient) { return { success: false, error: 'Recipient required for WhatsApp notifications', }; } const whatsappData = this.transform(notification); const result = await this.whatsappService.sendMessage(whatsappData); const deliveryResult: NotificationDeliveryResult = { success: true, messageId: result.messageId, metadata: { provider: 'whatsapp', recipient: notification.recipient, ...result, }, }; this.logAttempt(notification, deliveryResult); return deliveryResult; } catch (error) { const deliveryResult: NotificationDeliveryResult = { success: false, error: error.message, }; this.logAttempt(notification, deliveryResult); return deliveryResult; } } } /** * In-App Channel Adapter * Handles in-app notifications (stored in database) */ @Injectable() export class InAppChannelAdapter extends BaseNotificationAdapter { readonly channelType = 'in_app'; constructor(private readonly notificationRepository: any) { super('in_app'); } async isAvailable(): Promise { return true; // In-app is always available if DB is accessible } getConfigSchema(): Record { return { type: 'object', properties: { retention: { type: 'object', properties: { days: { type: 'number', default: 30 }, }, }, }, }; } protected transform(notification: any): any { return { user_id: notification.userId, tenant_id: notification.tenantId, type: notification.type || 'info', title: notification.title, message: notification.message, data: notification.data || null, action_url: notification.actionUrl || null, delivery_status: 'delivered', }; } async send(notification: any, preferences?: any): Promise { try { const notificationData = this.transform(notification); const saved = await this.notificationRepository.save(notificationData); const deliveryResult: NotificationDeliveryResult = { success: true, messageId: saved.id, metadata: { provider: 'database', notificationId: saved.id, }, }; this.logAttempt(notification, deliveryResult); return deliveryResult; } catch (error) { const deliveryResult: NotificationDeliveryResult = { success: false, error: error.message, }; this.logAttempt(notification, deliveryResult); return deliveryResult; } } }