mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2026-03-10 17:21:45 +00:00
- Implement messaging system factory pattern - Fix all transaction notification blockers - Complete listener logic for all notification types
141 lines
3.7 KiB
TypeScript
141 lines
3.7 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
|
import { NotificationsService } from './notifications.service';
|
|
import { NotificationChannel } from '../enums/notification-channel.enum';
|
|
import { NotificationScope } from '../enums/notification-scope.enum';
|
|
|
|
/**
|
|
* User notification preferences
|
|
* Determines which channels are enabled for a user
|
|
*/
|
|
export interface NotificationPreferences {
|
|
/** Whether push notifications are enabled */
|
|
isPushEnabled: boolean;
|
|
|
|
/** Whether email notifications are enabled */
|
|
isEmailEnabled: boolean;
|
|
|
|
/** Whether SMS notifications are enabled */
|
|
isSmsEnabled: boolean;
|
|
}
|
|
|
|
/**
|
|
* Payload for sending a notification
|
|
*/
|
|
export interface NotificationPayload {
|
|
/** ID of the user to notify */
|
|
userId: string;
|
|
|
|
/** Notification title */
|
|
title: string;
|
|
|
|
/** Notification message body */
|
|
message: string;
|
|
|
|
/** Category/type of notification */
|
|
scope: NotificationScope;
|
|
|
|
/**
|
|
* User's notification preferences
|
|
* If not provided, defaults to push-only
|
|
*/
|
|
preferences?: NotificationPreferences;
|
|
|
|
/** Additional data to attach to the notification */
|
|
data?: Record<string, any>;
|
|
}
|
|
|
|
/**
|
|
* NotificationFactory
|
|
*
|
|
* Central service for sending notifications.
|
|
* Independent service with no external dependencies (microservice-ready).
|
|
*
|
|
* Handles:
|
|
* - Channel routing based on provided preferences
|
|
* - Parallel notification delivery
|
|
* - Error handling
|
|
*
|
|
* Note: Caller is responsible for providing user preferences.
|
|
* This keeps the factory independent and testable.
|
|
*
|
|
* Usage:
|
|
* await notificationFactory.send({
|
|
* userId: 'user-123',
|
|
* title: 'Transaction Alert',
|
|
* message: 'You spent $50.00',
|
|
* scope: NotificationScope.CHILD_SPENDING,
|
|
* preferences: {
|
|
* isPushEnabled: true,
|
|
* isEmailEnabled: false,
|
|
* isSmsEnabled: false,
|
|
* },
|
|
* });
|
|
*/
|
|
@Injectable()
|
|
export class NotificationFactory {
|
|
private readonly logger = new Logger(NotificationFactory.name);
|
|
|
|
constructor(
|
|
private readonly notificationsService: NotificationsService,
|
|
) {}
|
|
|
|
/**
|
|
* Send a notification to a user
|
|
* Routes to enabled channels based on provided preferences
|
|
*
|
|
* @param payload - Notification payload including preferences
|
|
*/
|
|
async send(payload: NotificationPayload): Promise<void> {
|
|
try {
|
|
this.logger.log(`Sending notification to user ${payload.userId} - ${payload.title}`);
|
|
|
|
const preferences = payload.preferences || {
|
|
isPushEnabled: true,
|
|
isEmailEnabled: false,
|
|
isSmsEnabled: false,
|
|
};
|
|
|
|
const promises: Promise<any>[] = [];
|
|
|
|
if (preferences.isPushEnabled) {
|
|
this.logger.debug(`Routing to PUSH channel for user ${payload.userId}`);
|
|
promises.push(
|
|
this.sendToChannel(payload, NotificationChannel.PUSH)
|
|
);
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
|
|
this.logger.log(
|
|
`Notification sent to user ${payload.userId} via ${promises.length} channel(s)`
|
|
);
|
|
} catch (error: any) {
|
|
this.logger.error(
|
|
`Failed to send notification to user ${payload.userId}: ${error?.message || 'Unknown error'}`,
|
|
error?.stack
|
|
);
|
|
// Don't throw - prevents breaking the main business flow
|
|
// Notification failures should not break transactions, etc.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send notification via a specific channel
|
|
* Creates the notification record and publishes it for delivery
|
|
*/
|
|
private async sendToChannel(
|
|
payload: NotificationPayload,
|
|
channel: NotificationChannel
|
|
): Promise<void> {
|
|
await this.notificationsService.createNotification({
|
|
userId: payload.userId,
|
|
title: payload.title,
|
|
message: payload.message,
|
|
scope: payload.scope,
|
|
channel,
|
|
data: payload.data,
|
|
});
|
|
}
|
|
}
|
|
|