2025-12-23 11:36:44 +09:00
|
|
|
/**
|
|
|
|
|
* Notifications Controller
|
|
|
|
|
*
|
|
|
|
|
* API endpoints for managing in-app notifications.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
Controller,
|
|
|
|
|
Get,
|
|
|
|
|
Post,
|
|
|
|
|
Param,
|
|
|
|
|
Query,
|
|
|
|
|
Req,
|
|
|
|
|
UseGuards,
|
|
|
|
|
ParseIntPipe,
|
|
|
|
|
DefaultValuePipe,
|
|
|
|
|
ParseBoolPipe,
|
|
|
|
|
} 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 type { NotificationListResponse } from "@customer-portal/domain/notifications";
|
2025-12-26 10:30:09 +09:00
|
|
|
import type { ApiSuccessAckResponse } from "@customer-portal/domain/common";
|
2025-12-23 11:36:44 +09:00
|
|
|
|
|
|
|
|
@Controller("notifications")
|
|
|
|
|
@UseGuards(RateLimitGuard)
|
|
|
|
|
export class NotificationsController {
|
|
|
|
|
constructor(private readonly notificationService: NotificationService) {}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get notifications for the current user
|
|
|
|
|
*/
|
|
|
|
|
@Get()
|
|
|
|
|
@RateLimit({ limit: 60, ttl: 60 })
|
|
|
|
|
async getNotifications(
|
|
|
|
|
@Req() req: RequestWithUser,
|
|
|
|
|
@Query("limit", new DefaultValuePipe(20), ParseIntPipe) limit: number,
|
|
|
|
|
@Query("offset", new DefaultValuePipe(0), ParseIntPipe) offset: number,
|
|
|
|
|
@Query("includeRead", new DefaultValuePipe(true), ParseBoolPipe)
|
|
|
|
|
includeRead: boolean
|
|
|
|
|
): Promise<NotificationListResponse> {
|
|
|
|
|
return this.notificationService.getNotifications(req.user.id, {
|
|
|
|
|
limit: Math.min(limit, 50), // Cap at 50
|
|
|
|
|
offset,
|
|
|
|
|
includeRead,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get unread notification count for the current user
|
|
|
|
|
*/
|
|
|
|
|
@Get("unread-count")
|
|
|
|
|
@RateLimit({ limit: 120, ttl: 60 })
|
|
|
|
|
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 })
|
|
|
|
|
async markAsRead(
|
|
|
|
|
@Req() req: RequestWithUser,
|
|
|
|
|
@Param("id") notificationId: string
|
2025-12-26 10:30:09 +09:00
|
|
|
): Promise<ApiSuccessAckResponse> {
|
2025-12-23 11:36:44 +09:00
|
|
|
await this.notificationService.markAsRead(notificationId, req.user.id);
|
|
|
|
|
return { success: true };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Mark all notifications as read
|
|
|
|
|
*/
|
|
|
|
|
@Post("read-all")
|
|
|
|
|
@RateLimit({ limit: 10, ttl: 60 })
|
2025-12-26 10:30:09 +09:00
|
|
|
async markAllAsRead(@Req() req: RequestWithUser): Promise<ApiSuccessAckResponse> {
|
2025-12-23 11:36:44 +09:00
|
|
|
await this.notificationService.markAllAsRead(req.user.id);
|
|
|
|
|
return { success: true };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Dismiss a notification (hide from UI)
|
|
|
|
|
*/
|
|
|
|
|
@Post(":id/dismiss")
|
|
|
|
|
@RateLimit({ limit: 60, ttl: 60 })
|
|
|
|
|
async dismiss(
|
|
|
|
|
@Req() req: RequestWithUser,
|
|
|
|
|
@Param("id") notificationId: string
|
2025-12-26 10:30:09 +09:00
|
|
|
): Promise<ApiSuccessAckResponse> {
|
2025-12-23 11:36:44 +09:00
|
|
|
await this.notificationService.dismiss(notificationId, req.user.id);
|
|
|
|
|
return { success: true };
|
|
|
|
|
}
|
|
|
|
|
}
|