/** * Notifications Controller * * REST API endpoints for managing notifications */ import { Controller, Get, Post, Patch, Delete, Param, Query, UseGuards, ParseIntPipe, DefaultValuePipe, NotFoundException, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; import { NotificationService } from '../services/notification.service'; import { JwtAuthGuard } from '../guards/jwt-auth.guard'; import { CurrentUser, UserPayload } from '../decorators/current-user.decorator'; import { Notification } from '@domain/entities/notification.entity'; class NotificationResponseDto { id: string; type: string; priority: string; title: string; message: string; metadata?: Record; read: boolean; readAt?: string; actionUrl?: string; createdAt: string; } @ApiTags('Notifications') @ApiBearerAuth() @Controller('notifications') @UseGuards(JwtAuthGuard) export class NotificationsController { constructor(private readonly notificationService: NotificationService) {} /** * Get user's notifications */ @Get() @ApiOperation({ summary: 'Get user notifications' }) @ApiResponse({ status: 200, description: 'Notifications retrieved successfully' }) @ApiQuery({ name: 'read', required: false, description: 'Filter by read status' }) @ApiQuery({ name: 'page', required: false, description: 'Page number (default: 1)' }) @ApiQuery({ name: 'limit', required: false, description: 'Items per page (default: 20)' }) async getNotifications( @CurrentUser() user: UserPayload, @Query('read') read?: string, @Query('page', new DefaultValuePipe(1), ParseIntPipe) page?: number, @Query('limit', new DefaultValuePipe(20), ParseIntPipe) limit?: number ): Promise<{ notifications: NotificationResponseDto[]; total: number; page: number; pageSize: number; }> { page = page || 1; limit = limit || 20; const filters: any = { userId: user.id, read: read !== undefined ? read === 'true' : undefined, offset: (page - 1) * limit, limit, }; const { notifications, total } = await this.notificationService.getNotifications(filters); return { notifications: notifications.map(n => this.mapToDto(n)), total, page, pageSize: limit, }; } /** * Get unread notifications */ @Get('unread') @ApiOperation({ summary: 'Get unread notifications' }) @ApiResponse({ status: 200, description: 'Unread notifications retrieved successfully' }) @ApiQuery({ name: 'limit', required: false, description: 'Number of notifications (default: 50)', }) async getUnreadNotifications( @CurrentUser() user: UserPayload, @Query('limit', new DefaultValuePipe(50), ParseIntPipe) limit?: number ): Promise { limit = limit || 50; const notifications = await this.notificationService.getUnreadNotifications(user.id, limit); return notifications.map(n => this.mapToDto(n)); } /** * Get unread count */ @Get('unread/count') @ApiOperation({ summary: 'Get unread notifications count' }) @ApiResponse({ status: 200, description: 'Unread count retrieved successfully' }) async getUnreadCount(@CurrentUser() user: UserPayload): Promise<{ count: number }> { const count = await this.notificationService.getUnreadCount(user.id); return { count }; } /** * Get notification by ID */ @Get(':id') @ApiOperation({ summary: 'Get notification by ID' }) @ApiResponse({ status: 200, description: 'Notification retrieved successfully' }) @ApiResponse({ status: 404, description: 'Notification not found' }) async getNotificationById( @CurrentUser() user: UserPayload, @Param('id') id: string ): Promise { const notification = await this.notificationService.getNotificationById(id); if (!notification || notification.userId !== user.id) { throw new NotFoundException('Notification not found'); } return this.mapToDto(notification); } /** * Mark notification as read */ @Patch(':id/read') @ApiOperation({ summary: 'Mark notification as read' }) @ApiResponse({ status: 200, description: 'Notification marked as read' }) @ApiResponse({ status: 404, description: 'Notification not found' }) async markAsRead( @CurrentUser() user: UserPayload, @Param('id') id: string ): Promise<{ success: boolean }> { const notification = await this.notificationService.getNotificationById(id); if (!notification || notification.userId !== user.id) { throw new NotFoundException('Notification not found'); } await this.notificationService.markAsRead(id); return { success: true }; } /** * Mark all notifications as read */ @Post('read-all') @ApiOperation({ summary: 'Mark all notifications as read' }) @ApiResponse({ status: 200, description: 'All notifications marked as read' }) async markAllAsRead(@CurrentUser() user: UserPayload): Promise<{ success: boolean }> { await this.notificationService.markAllAsRead(user.id); return { success: true }; } /** * Delete notification */ @Delete(':id') @ApiOperation({ summary: 'Delete notification' }) @ApiResponse({ status: 200, description: 'Notification deleted' }) @ApiResponse({ status: 404, description: 'Notification not found' }) async deleteNotification( @CurrentUser() user: UserPayload, @Param('id') id: string ): Promise<{ success: boolean }> { const notification = await this.notificationService.getNotificationById(id); if (!notification || notification.userId !== user.id) { throw new NotFoundException('Notification not found'); } await this.notificationService.deleteNotification(id); return { success: true }; } /** * Map notification entity to DTO */ private mapToDto(notification: Notification): NotificationResponseDto { return { id: notification.id, type: notification.type, priority: notification.priority, title: notification.title, message: notification.message, metadata: notification.metadata, read: notification.read, readAt: notification.readAt?.toISOString(), actionUrl: notification.actionUrl, createdAt: notification.createdAt.toISOString(), }; } }