it0/packages/services/notification-service/src/interfaces/rest/controllers/notification-user.controlle...

74 lines
2.3 KiB
TypeScript

import {
Controller,
Get,
Post,
Body,
Query,
Headers,
UnauthorizedException,
ParseIntPipe,
DefaultValuePipe,
} from '@nestjs/common';
import { NotificationRepository } from '../../../infrastructure/repositories/notification.repository';
import * as jwt from 'jsonwebtoken';
/**
* User-facing notification endpoints.
* Kong enforces JWT — we extract tenantId/userId from Authorization header.
*/
@Controller('api/v1/notifications')
export class NotificationUserController {
constructor(private readonly repo: NotificationRepository) {}
/** GET /api/v1/notifications/me — notifications visible to this user */
@Get('me')
async getMyNotifications(
@Headers('authorization') auth: string,
@Query('limit', new DefaultValuePipe(50), ParseIntPipe) limit: number,
@Query('offset', new DefaultValuePipe(0), ParseIntPipe) offset: number,
) {
const { tenantId, userId } = this.extractJwt(auth);
return this.repo.findForUser({ tenantId, userId, limit: Math.min(limit, 100), offset });
}
/** GET /api/v1/notifications/me/unread-count */
@Get('me/unread-count')
async getUnreadCount(@Headers('authorization') auth: string) {
const { tenantId, userId } = this.extractJwt(auth);
const count = await this.repo.getUnreadCount(tenantId, userId);
return { count };
}
/**
* POST /api/v1/notifications/me/mark-read
* Body: { notificationId?: string } — omit to mark ALL as read
*/
@Post('me/mark-read')
async markRead(
@Headers('authorization') auth: string,
@Body() body: { notificationId?: string },
) {
const { tenantId, userId } = this.extractJwt(auth);
if (body?.notificationId) {
await this.repo.markRead(body.notificationId, userId, tenantId);
} else {
await this.repo.markAllRead(tenantId, userId);
}
return { ok: true };
}
private extractJwt(auth: string): { tenantId: string; userId: string } {
if (!auth?.startsWith('Bearer ')) {
throw new UnauthorizedException('Missing authorization header');
}
const token = auth.slice(7);
const secret = process.env.JWT_SECRET || 'dev-secret';
try {
const payload = jwt.verify(token, secret) as any;
return { tenantId: payload.tenantId, userId: payload.sub };
} catch {
throw new UnauthorizedException('Invalid JWT');
}
}
}