Assist_Design/apps/bff/src/modules/notifications/notifications.controller.ts

104 lines
3.5 KiB
TypeScript
Raw Normal View History

/**
* Notifications Controller
*
* API endpoints for managing in-app notifications.
*/
import { Controller, Get, Post, Param, Query, Req, UseGuards } from "@nestjs/common";
import { RateLimit, RateLimitGuard } from "@bff/core/rate-limiting/index.js";
import type { RequestWithUser } from "@bff/modules/auth/auth.types.js";
import { NotificationService } from "./notifications.service.js";
import {
notificationListResponseSchema,
notificationUnreadCountResponseSchema,
type NotificationListResponse,
} from "@customer-portal/domain/notifications";
import { notificationQuerySchema } from "@customer-portal/domain/notifications";
import {
apiSuccessAckResponseSchema,
type ApiSuccessAckResponse,
} from "@customer-portal/domain/common";
import { createZodDto, ZodResponse } from "nestjs-zod";
class NotificationQueryDto extends createZodDto(notificationQuerySchema) {}
class NotificationListResponseDto extends createZodDto(notificationListResponseSchema) {}
class NotificationUnreadCountResponseDto extends createZodDto(
notificationUnreadCountResponseSchema
) {}
class ApiSuccessAckResponseDto extends createZodDto(apiSuccessAckResponseSchema) {}
@Controller("notifications")
@UseGuards(RateLimitGuard)
export class NotificationsController {
constructor(private readonly notificationService: NotificationService) {}
/**
* Get notifications for the current user
*/
@Get()
@RateLimit({ limit: 60, ttl: 60 })
@ZodResponse({ description: "Get notifications", type: NotificationListResponseDto })
async getNotifications(
@Req() req: RequestWithUser,
@Query() query: NotificationQueryDto
): Promise<NotificationListResponse> {
const parsedQuery = notificationQuerySchema.parse(query as unknown);
return this.notificationService.getNotifications(req.user.id, {
limit: Math.min(parsedQuery.limit, 50), // Cap at 50
offset: parsedQuery.offset,
includeRead: parsedQuery.includeRead,
});
}
/**
* Get unread notification count for the current user
*/
@Get("unread-count")
@RateLimit({ limit: 120, ttl: 60 })
@ZodResponse({ description: "Get unread count", type: NotificationUnreadCountResponseDto })
async getUnreadCount(@Req() req: RequestWithUser): Promise<{ count: number }> {
const count = await this.notificationService.getUnreadCount(req.user.id);
return { count };
}
/**
* Mark a specific notification as read
*/
@Post(":id/read")
@RateLimit({ limit: 60, ttl: 60 })
@ZodResponse({ description: "Mark as read", type: ApiSuccessAckResponseDto })
async markAsRead(
@Req() req: RequestWithUser,
@Param("id") notificationId: string
): Promise<ApiSuccessAckResponse> {
await this.notificationService.markAsRead(notificationId, req.user.id);
return { success: true };
}
/**
* Mark all notifications as read
*/
@Post("read-all")
@RateLimit({ limit: 10, ttl: 60 })
@ZodResponse({ description: "Mark all as read", type: ApiSuccessAckResponseDto })
async markAllAsRead(@Req() req: RequestWithUser): Promise<ApiSuccessAckResponse> {
await this.notificationService.markAllAsRead(req.user.id);
return { success: true };
}
/**
* Dismiss a notification (hide from UI)
*/
@Post(":id/dismiss")
@RateLimit({ limit: 60, ttl: 60 })
@ZodResponse({ description: "Dismiss notification", type: ApiSuccessAckResponseDto })
async dismiss(
@Req() req: RequestWithUser,
@Param("id") notificationId: string
): Promise<ApiSuccessAckResponse> {
await this.notificationService.dismiss(notificationId, req.user.id);
return { success: true };
}
}