feat(notifications): add multi-channel notification module with Email, FCM, templates, and event listeners
- Domain: NotificationLog/NotificationPreference entities, repositories, channel value object - Infrastructure: EmailService (nodemailer/SMTP), FcmService (firebase-admin), TemplateService (Handlebars) - Application: SendNotification CQRS command, UserRegistered + AgentVerified event listeners - Presentation: NotificationsController with history, preferences, and templates endpoints - Prisma: NotificationLog and NotificationPreference models with proper indexes - Templates: Vietnamese notification templates for user.registered, agent.verified, listing.approved, inquiry.received, password.reset Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Put,
|
||||
Body,
|
||||
Query,
|
||||
UseGuards,
|
||||
Inject,
|
||||
} from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { CurrentUser } from '@modules/auth/presentation/decorators';
|
||||
import { type JwtPayload } from '@modules/auth';
|
||||
import {
|
||||
NOTIFICATION_REPOSITORY,
|
||||
type INotificationRepository,
|
||||
NOTIFICATION_PREFERENCE_REPOSITORY,
|
||||
type INotificationPreferenceRepository,
|
||||
} from '../../domain';
|
||||
import { TemplateService } from '../../infrastructure/services/template.service';
|
||||
import { IsBoolean, IsEnum, IsString } from 'class-validator';
|
||||
import { NotificationChannel as PrismaChannel } from '@prisma/client';
|
||||
|
||||
class UpdatePreferenceDto {
|
||||
@IsEnum(PrismaChannel)
|
||||
channel!: PrismaChannel;
|
||||
|
||||
@IsString()
|
||||
eventType!: string;
|
||||
|
||||
@IsBoolean()
|
||||
enabled!: boolean;
|
||||
}
|
||||
|
||||
@Controller('notifications')
|
||||
@UseGuards(AuthGuard('jwt'))
|
||||
export class NotificationsController {
|
||||
constructor(
|
||||
@Inject(NOTIFICATION_REPOSITORY)
|
||||
private readonly notificationRepo: INotificationRepository,
|
||||
@Inject(NOTIFICATION_PREFERENCE_REPOSITORY)
|
||||
private readonly preferenceRepo: INotificationPreferenceRepository,
|
||||
private readonly templateService: TemplateService,
|
||||
) {}
|
||||
|
||||
@Get('history')
|
||||
async getHistory(
|
||||
@CurrentUser() user: JwtPayload,
|
||||
@Query('limit') limit?: number,
|
||||
) {
|
||||
return this.notificationRepo.findByUserId(user.sub, limit ?? 50);
|
||||
}
|
||||
|
||||
@Get('preferences')
|
||||
async getPreferences(@CurrentUser() user: JwtPayload) {
|
||||
return this.preferenceRepo.findByUserId(user.sub);
|
||||
}
|
||||
|
||||
@Put('preferences')
|
||||
async updatePreference(
|
||||
@CurrentUser() user: JwtPayload,
|
||||
@Body() dto: UpdatePreferenceDto,
|
||||
) {
|
||||
return this.preferenceRepo.upsert(user.sub, dto.channel, dto.eventType, dto.enabled);
|
||||
}
|
||||
|
||||
@Get('templates')
|
||||
async getTemplates() {
|
||||
return { templates: this.templateService.getTemplateKeys() };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user