Files
gongxue-base/apps/server/src/notifications/notifications.controller.ts

91 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
Controller,
Get,
Put,
Param,
ParseIntPipe,
Query,
Req,
Sse,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { Observable, interval, map, merge } from 'rxjs';
import { NotificationsService } from './notifications.service';
import { NotificationQueryDto } from './dto/notification.dto';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RequirePermission } from '../auth/decorators/permission.decorator';
interface AuthenticatedUser {
id: number;
username: string;
permissions: string[];
}
interface AuthenticatedRequest extends Request {
user: AuthenticatedUser;
}
@UseGuards(JwtAuthGuard)
@RequirePermission('notification:view')
@Controller('notifications')
export class NotificationsController {
constructor(private readonly service: NotificationsService) {}
@Get()
async findAll(
@Req() req: AuthenticatedRequest,
@Query() query: NotificationQueryDto,
) {
const userId = req.user.id;
return this.service.findByUser(userId, query.after, query.limit ?? 20);
}
@Get('unread-count')
async unreadCount(@Req() req: AuthenticatedRequest) {
const userId = req.user.id;
const count = await this.service.getUnreadCount(userId);
return { count };
}
@Sse('stream')
stream(@Req() req: AuthenticatedRequest): Observable<MessageEvent> {
const userId = req.user.id;
req.on('close', () => {
this.service.unsubscribe(userId);
});
// 每 25s 发送一次空消息作为心跳,避免空闲连接被 Nginx 等中间层超时掐断。
// 空 data 会被前端 EventSource 收到并忽略JSON.parse 失败)。
return merge(
this.service.subscribe(userId).pipe(
map((notification) => ({
data: JSON.stringify({
id: notification.id,
type: notification.type,
title: notification.title,
content: notification.content,
link: notification.link,
createdAt: notification.createdAt,
}),
} as MessageEvent)),
),
interval(25_000).pipe(map(() => ({ data: '' } as MessageEvent))),
);
}
@Put(':id/read')
async markRead(
@Param('id', ParseIntPipe) id: number,
@Req() req: AuthenticatedRequest,
) {
await this.service.markRead(+id, req.user.id);
return { success: true };
}
@Put('read-all')
async markAllRead(@Req() req: AuthenticatedRequest) {
await this.service.markAllRead(req.user.id);
return { success: true };
}
}