import { ForbiddenException, Injectable, NotFoundException, } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { DbClient } from '../prisma/db-client'; import { PermissionKey } from '../auth/permissions/permission-catalog'; import { Notification, NotificationPriority, Prisma, } from '../../generated/prisma'; export interface CreateNotificationInput { userId?: string; priority: NotificationPriority; message: string; relatedType?: string; relatedId?: string; } export interface NotificationFilter { userId?: string; unreadOnly?: boolean; } @Injectable() export class NotificationsService { constructor(private readonly prisma: PrismaService) {} async record( input: CreateNotificationInput, tx?: Prisma.TransactionClient, ): Promise { const db: DbClient = tx ?? this.prisma; return db.notification.create({ data: { ...input } }); } /** * One notification per user who may act on it: active account, active * role, permission granted - the same rule the PermissionsGuard applies. * Returns how many were written. */ async recordForPermission( permission: PermissionKey, input: Omit, tx?: Prisma.TransactionClient, ): Promise { const db: DbClient = tx ?? this.prisma; const recipients = await db.user.findMany({ where: { isActive: true, role: { status: 'ACTIVE', permissions: { some: { permission: { key: permission } } }, }, }, select: { id: true }, }); if (recipients.length === 0) { return 0; } const { count } = await db.notification.createMany({ data: recipients.map((recipient) => ({ ...input, userId: recipient.id })), }); return count; } async findAll(filter: NotificationFilter = {}): Promise { return this.prisma.notification.findMany({ where: { OR: filter.userId ? [{ userId: filter.userId }, { userId: null }] : undefined, read: filter.unreadOnly ? false : undefined, }, orderBy: { createdAt: 'desc' }, take: 100, }); } async markRead(id: string, actingUserId: string): Promise { const notification = await this.prisma.notification.findUnique({ where: { id }, }); if (!notification) { throw new NotFoundException('Notification not found'); } if (notification.userId !== null && notification.userId !== actingUserId) { throw new ForbiddenException("Cannot modify another user's notification"); } return this.prisma.notification.update({ where: { id }, data: { read: true }, }); } }