import { Controller, Get, Put, Patch, Body, Param, Query, UseGuards, Inject, } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiQuery, ApiProperty } from '@nestjs/swagger'; import { NotificationChannel as PrismaChannel } from '@prisma/client'; import { IsBoolean, IsEnum, IsString } from 'class-validator'; import { CurrentUser, type JwtPayload } from '@modules/auth'; import { NOTIFICATION_REPOSITORY, type INotificationRepository, NOTIFICATION_PREFERENCE_REPOSITORY, type INotificationPreferenceRepository, } from '../../domain'; import { TemplateService } from '../../infrastructure/services/template.service'; class UpdatePreferenceDto { @ApiProperty({ enum: PrismaChannel, description: 'Notification channel' }) @IsEnum(PrismaChannel) channel!: PrismaChannel; @ApiProperty({ description: 'Event type identifier' }) @IsString() eventType!: string; @ApiProperty({ description: 'Whether the preference is enabled' }) @IsBoolean() enabled!: boolean; } @ApiTags('notifications') @ApiBearerAuth('JWT') @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') @ApiOperation({ summary: 'Get notification history' }) @ApiResponse({ status: 200, description: 'Notification history retrieved' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) @ApiQuery({ name: 'limit', required: false, type: Number }) async getHistory( @CurrentUser() user: JwtPayload, @Query('limit') limit?: number, ) { return this.notificationRepo.findByUserId(user.sub, limit ?? 50); } @Get('preferences') @ApiOperation({ summary: 'Get notification preferences' }) @ApiResponse({ status: 200, description: 'Preferences retrieved' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async getPreferences(@CurrentUser() user: JwtPayload) { return this.preferenceRepo.findByUserId(user.sub); } @Put('preferences') @ApiOperation({ summary: 'Update a notification preference' }) @ApiResponse({ status: 200, description: 'Preference updated' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async updatePreference( @CurrentUser() user: JwtPayload, @Body() dto: UpdatePreferenceDto, ) { return this.preferenceRepo.upsert(user.sub, dto.channel, dto.eventType, dto.enabled); } @Get('unread') @ApiOperation({ summary: 'Get unread notifications' }) @ApiResponse({ status: 200, description: 'Unread notifications retrieved' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) @ApiQuery({ name: 'limit', required: false, type: Number }) async getUnread( @CurrentUser() user: JwtPayload, @Query('limit') limit?: number, ) { const [notifications, count] = await Promise.all([ this.notificationRepo.findUnreadByUserId(user.sub, limit ?? 50), this.notificationRepo.countUnreadByUserId(user.sub), ]); return { notifications, unreadCount: count }; } @Patch(':id/read') @ApiOperation({ summary: 'Mark a notification as read' }) @ApiResponse({ status: 200, description: 'Notification marked as read' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async markAsRead( @CurrentUser() user: JwtPayload, @Param('id') id: string, ) { await this.notificationRepo.markAsRead(id, user.sub); return { success: true }; } @Patch('read-all') @ApiOperation({ summary: 'Mark all notifications as read' }) @ApiResponse({ status: 200, description: 'All notifications marked as read' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async markAllAsRead(@CurrentUser() user: JwtPayload) { const count = await this.notificationRepo.markAllAsRead(user.sub); return { markedCount: count }; } @Get('templates') @ApiOperation({ summary: 'Get available notification templates' }) @ApiResponse({ status: 200, description: 'Templates retrieved' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async getTemplates() { return { templates: this.templateService.getTemplateKeys() }; } }