fix sso core folder
This commit is contained in:
166
apps/sso-core/src/domain/notifications.service.ts
Normal file
166
apps/sso-core/src/domain/notifications.service.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../infra/prisma.service';
|
||||
import { NotificationPublisherService } from '../infra/notification-publisher.service';
|
||||
|
||||
@Injectable()
|
||||
export class NotificationsService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly publisher: NotificationPublisherService
|
||||
) {}
|
||||
|
||||
async create(
|
||||
userId: string,
|
||||
type: string,
|
||||
title: string,
|
||||
message: string,
|
||||
payload?: Record<string, unknown>,
|
||||
options?: { skipPublish?: boolean }
|
||||
) {
|
||||
const notification = await this.prisma.notification.create({
|
||||
data: {
|
||||
userId,
|
||||
type,
|
||||
title,
|
||||
message,
|
||||
payload: (payload ?? undefined) as never
|
||||
}
|
||||
});
|
||||
|
||||
if (!options?.skipPublish) {
|
||||
await this.publisher.publish({
|
||||
userId,
|
||||
type,
|
||||
title,
|
||||
message,
|
||||
payload: {
|
||||
notificationId: notification.id,
|
||||
...(payload ?? {})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this.toResponse(notification);
|
||||
}
|
||||
|
||||
async publishRealtime(userId: string, type: string, title: string, message: string, payload?: Record<string, unknown>) {
|
||||
await this.publisher.publish({ userId, type, title, message, payload });
|
||||
}
|
||||
|
||||
async list(userId: string, limit = 30, unreadOnly = false) {
|
||||
const notifications = await this.prisma.notification.findMany({
|
||||
where: {
|
||||
userId,
|
||||
...(unreadOnly ? { isRead: false } : {})
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: Math.min(Math.max(limit, 1), 100)
|
||||
});
|
||||
|
||||
const inviteIds = notifications
|
||||
.filter((item) => item.type === 'family_invite')
|
||||
.map((item) => (item.payload as { inviteId?: string } | null)?.inviteId)
|
||||
.filter((inviteId): inviteId is string => Boolean(inviteId));
|
||||
|
||||
const pendingInviteIds = new Set<string>();
|
||||
if (inviteIds.length) {
|
||||
const invites = await this.prisma.familyInvite.findMany({
|
||||
where: { id: { in: inviteIds }, status: 'PENDING' }
|
||||
});
|
||||
invites.forEach((invite) => pendingInviteIds.add(invite.id));
|
||||
}
|
||||
|
||||
const staleIds: string[] = [];
|
||||
const filtered = notifications.filter((item) => {
|
||||
if (item.type !== 'family_invite') return true;
|
||||
const inviteId = (item.payload as { inviteId?: string } | null)?.inviteId;
|
||||
if (!inviteId || !pendingInviteIds.has(inviteId)) {
|
||||
staleIds.push(item.id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (staleIds.length) {
|
||||
await this.prisma.notification.deleteMany({
|
||||
where: { id: { in: staleIds }, userId }
|
||||
});
|
||||
}
|
||||
|
||||
return { notifications: filtered.map((item) => this.toResponse(item)) };
|
||||
}
|
||||
|
||||
async unreadCount(userId: string) {
|
||||
const count = await this.prisma.notification.count({
|
||||
where: { userId, isRead: false }
|
||||
});
|
||||
return { count };
|
||||
}
|
||||
|
||||
async markRead(userId: string, notificationId: string) {
|
||||
return this.delete(userId, notificationId);
|
||||
}
|
||||
|
||||
async markAllRead(userId: string) {
|
||||
return this.deleteAll(userId);
|
||||
}
|
||||
|
||||
async delete(userId: string, notificationId: string) {
|
||||
const existing = await this.prisma.notification.findFirst({
|
||||
where: { id: notificationId, userId }
|
||||
});
|
||||
if (!existing) {
|
||||
throw new NotFoundException('Уведомление не найдено');
|
||||
}
|
||||
await this.prisma.notification.delete({ where: { id: notificationId } });
|
||||
return { count: 1 };
|
||||
}
|
||||
|
||||
async deleteAll(userId: string) {
|
||||
const result = await this.prisma.notification.deleteMany({ where: { userId } });
|
||||
return { count: result.count };
|
||||
}
|
||||
|
||||
async dismissFamilyInvite(userId: string, inviteId: string) {
|
||||
const items = await this.prisma.notification.findMany({
|
||||
where: { userId, type: 'family_invite' }
|
||||
});
|
||||
const ids = items
|
||||
.filter((item) => {
|
||||
const payload = item.payload as { inviteId?: string } | null;
|
||||
return payload?.inviteId === inviteId;
|
||||
})
|
||||
.map((item) => item.id);
|
||||
|
||||
if (!ids.length) {
|
||||
return { count: 0 };
|
||||
}
|
||||
|
||||
await this.prisma.notification.deleteMany({
|
||||
where: { id: { in: ids }, userId }
|
||||
});
|
||||
return { count: ids.length };
|
||||
}
|
||||
|
||||
private toResponse(notification: {
|
||||
id: string;
|
||||
userId: string;
|
||||
type: string;
|
||||
title: string;
|
||||
message: string;
|
||||
payload: unknown;
|
||||
isRead: boolean;
|
||||
createdAt: Date;
|
||||
}) {
|
||||
return {
|
||||
id: notification.id,
|
||||
userId: notification.userId,
|
||||
type: notification.type,
|
||||
title: notification.title,
|
||||
message: notification.message,
|
||||
payloadJson: notification.payload ? JSON.stringify(notification.payload) : '',
|
||||
isRead: notification.isRead,
|
||||
createdAt: notification.createdAt.toISOString()
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user