feat: change the email structure into seprate module
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
import { EntityManager } from "@mikro-orm/core";
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { MailboxResolverService } from "./mailbox-resolver.service";
|
||||
import { WebSocketEvent } from "../../email/constants/email-events.constant";
|
||||
import {
|
||||
EmailDeletePayload,
|
||||
EmailSentNotificationParams,
|
||||
EmailSentPayload,
|
||||
EmailStatusNotificationParams,
|
||||
EmailStatusPayload,
|
||||
MailboxUpdateNotificationParams,
|
||||
MailboxUpdatePayload,
|
||||
UnreadCountPayload,
|
||||
} from "../../email/interfaces/email-events.interface";
|
||||
import { EmailGateway } from "../../email-gateway/email.gateway";
|
||||
import { MailServerService } from "../../mail-server/services/mail-server.service";
|
||||
import { MailboxEnum } from "../../mailbox/enums/mailbox.enum";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
|
||||
@Injectable()
|
||||
export class EmailNotificationService {
|
||||
private readonly logger = new Logger(EmailNotificationService.name);
|
||||
|
||||
constructor(
|
||||
private readonly emailGateway: EmailGateway,
|
||||
private readonly mailboxResolver: MailboxResolverService,
|
||||
private readonly mailServerService: MailServerService,
|
||||
private readonly em: EntityManager,
|
||||
) {}
|
||||
|
||||
//************************************************************************ */
|
||||
async notifyEmailSent(params: EmailSentNotificationParams) {
|
||||
try {
|
||||
const payload: EmailSentPayload = {
|
||||
userId: params.userId,
|
||||
messageId: params.messageId,
|
||||
mailboxId: params.mailboxId,
|
||||
timestamp: new Date().toISOString(),
|
||||
from: params.from,
|
||||
to: params.recipients,
|
||||
subject: params.subject,
|
||||
hasAttachments: false,
|
||||
mailboxName: MailboxEnum.SENT,
|
||||
isRead: true,
|
||||
};
|
||||
|
||||
const success = await this.emailGateway.notifyEmailSent(params.userId, payload);
|
||||
|
||||
if (success) {
|
||||
this.logger.log(`Email sent notification dispatched for user ${params.userId}: ${params.messageId}`);
|
||||
} else {
|
||||
this.logger.warn(`Failed to dispatch email sent notification for user ${params.userId}: user not connected`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to notify email sent for user ${params.userId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async notifyEmailUnread(params: EmailStatusNotificationParams) {
|
||||
try {
|
||||
const em = this.em.fork();
|
||||
const user = await em.findOne(User, {
|
||||
wildduckUserId: params.userId,
|
||||
emailEnabled: true,
|
||||
deletedAt: null,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
this.logger.warn(`User not found with wildduckUserId: ${params.userId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: EmailStatusPayload = {
|
||||
userId: user.id,
|
||||
messageId: params.messageId,
|
||||
mailboxId: params.mailboxId,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const success = await this.emailGateway.notifyEmailUnread(user.id, payload);
|
||||
await this.updateUnreadCount(params.userId);
|
||||
|
||||
if (success) {
|
||||
this.logger.log(`Email unread notification dispatched for user ${user.id}: message ${params.messageId}`);
|
||||
} else {
|
||||
this.logger.warn(`Failed to dispatch email unread notification for user ${user.id}: user not connected`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to notify email unread for user ${params.userId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async notifyEmailRead(params: EmailStatusNotificationParams) {
|
||||
try {
|
||||
// Find the user to get the correct User entity ID for websocket notifications
|
||||
const em = this.em.fork();
|
||||
const user = await em.findOne(User, {
|
||||
wildduckUserId: params.userId,
|
||||
emailEnabled: true,
|
||||
deletedAt: null,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
this.logger.warn(`User not found with wildduckUserId: ${params.userId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: EmailStatusPayload = {
|
||||
userId: user.id, // Use User entity ID for websocket
|
||||
messageId: params.messageId,
|
||||
mailboxId: params.mailboxId,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const success = await this.emailGateway.notifyEmailRead(user.id, payload);
|
||||
await this.updateUnreadCount(params.userId); // Pass wildduckUserId to updateUnreadCount
|
||||
|
||||
if (success) {
|
||||
this.logger.log(`Email read notification dispatched for user ${user.id}: message ${params.messageId}`);
|
||||
} else {
|
||||
this.logger.warn(`Failed to dispatch email read notification for user ${user.id}: user not connected`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to notify email read for user ${params.userId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async notifyEmailDeleted(params: EmailStatusNotificationParams) {
|
||||
try {
|
||||
// Find the user to get the correct User entity ID for websocket notifications
|
||||
const em = this.em.fork();
|
||||
const user = await em.findOne(User, {
|
||||
wildduckUserId: params.userId,
|
||||
emailEnabled: true,
|
||||
deletedAt: null,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
this.logger.warn(`User not found with wildduckUserId: ${params.userId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: EmailDeletePayload = {
|
||||
userId: user.id, // Use User entity ID for websocket
|
||||
messageId: params.messageId,
|
||||
mailboxId: params.mailboxId,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const success = await this.emailGateway.notifyEmailDeleted(user.id, payload);
|
||||
await this.updateUnreadCount(params.userId); // Pass wildduckUserId to updateUnreadCount
|
||||
|
||||
if (success) {
|
||||
this.logger.log(`Email deleted notification dispatched for user ${user.id}: message ${params.messageId}`);
|
||||
} else {
|
||||
this.logger.warn(`Failed to dispatch email deleted notification for user ${user.id}: user not connected`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to notify email deleted for user ${params.userId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async notifyEmailFlagged(params: EmailStatusNotificationParams) {
|
||||
try {
|
||||
// Find the user to get the correct User entity ID for websocket notifications
|
||||
const em = this.em.fork();
|
||||
const user = await em.findOne(User, {
|
||||
wildduckUserId: params.userId,
|
||||
emailEnabled: true,
|
||||
deletedAt: null,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
this.logger.warn(`User not found with wildduckUserId: ${params.userId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: EmailStatusPayload = {
|
||||
userId: user.id, // Use User entity ID for websocket
|
||||
messageId: params.messageId,
|
||||
mailboxId: params.mailboxId,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const success = await this.emailGateway.notifyEmailFlagged(user.id, payload);
|
||||
|
||||
if (success) {
|
||||
this.logger.log(`Email flagged notification dispatched for user ${user.id}: message ${params.messageId}`);
|
||||
} else {
|
||||
this.logger.warn(`Failed to dispatch email flagged notification for user ${user.id}: user not connected`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to notify email flagged for user ${params.userId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async notifyEmailUnflagged(params: EmailStatusNotificationParams) {
|
||||
try {
|
||||
// Find the user to get the correct User entity ID for websocket notifications
|
||||
const em = this.em.fork();
|
||||
const user = await em.findOne(User, {
|
||||
wildduckUserId: params.userId,
|
||||
emailEnabled: true,
|
||||
deletedAt: null,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
this.logger.warn(`User not found with wildduckUserId: ${params.userId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: EmailStatusPayload = {
|
||||
userId: user.id, // Use User entity ID for websocket
|
||||
messageId: params.messageId,
|
||||
mailboxId: params.mailboxId,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const success = await this.emailGateway.notifyEmailUnflagged(user.id, payload);
|
||||
|
||||
if (success) {
|
||||
this.logger.log(`Email unflagged notification dispatched for user ${user.id}: message ${params.messageId}`);
|
||||
} else {
|
||||
this.logger.warn(`Failed to dispatch email unflagged notification for user ${user.id}: user not connected`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to notify email unflagged for user ${params.userId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async notifyMailboxUpdate(params: MailboxUpdateNotificationParams) {
|
||||
try {
|
||||
const payload: MailboxUpdatePayload = {
|
||||
userId: params.userId,
|
||||
mailboxId: params.mailboxId,
|
||||
mailboxName: params.mailboxName,
|
||||
unreadCount: params.unreadCount,
|
||||
totalCount: params.totalCount,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const success = await this.emailGateway.notifyMailboxUpdate(params.userId, payload);
|
||||
|
||||
if (success) {
|
||||
this.logger.log(`Mailbox update notification dispatched for user ${params.userId}: ${params.mailboxName}`);
|
||||
} else {
|
||||
this.logger.warn(`Failed to dispatch mailbox update notification for user ${params.userId}: user not connected`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to notify mailbox update for user ${params.userId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
private async updateUnreadCount(userId: string) {
|
||||
try {
|
||||
const em = this.em.fork();
|
||||
|
||||
const user = await em.findOne(User, {
|
||||
wildduckUserId: userId,
|
||||
emailEnabled: true,
|
||||
deletedAt: null,
|
||||
});
|
||||
|
||||
if (!user || !user.wildduckUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inboxMailboxId = await this.mailboxResolver.getInboxMailboxId(user.wildduckUserId);
|
||||
|
||||
const { results } = await firstValueFrom(
|
||||
this.mailServerService.messages.listMessages(user.wildduckUserId, inboxMailboxId, {
|
||||
limit: 50,
|
||||
unseen: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const unreadCount = results.length;
|
||||
|
||||
const payload: UnreadCountPayload = {
|
||||
userId: user.id, // Use the actual User entity ID for the notification payload
|
||||
count: unreadCount,
|
||||
timestamp: new Date().toISOString(),
|
||||
totalUnread: unreadCount,
|
||||
mailboxCounts: {
|
||||
[inboxMailboxId]: unreadCount,
|
||||
},
|
||||
};
|
||||
|
||||
const success = await this.emailGateway.notifyUnreadCountUpdate(user.id, payload); // Use User entity ID here too
|
||||
|
||||
if (!success) {
|
||||
this.logger.warn(`Failed to update unread count for user ${user.id}: user not connected`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to update unread count for user ${userId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async broadcastSystemNotification(event: WebSocketEvent, data: Record<string, unknown>) {
|
||||
try {
|
||||
this.emailGateway.broadcastSystemMessage(event, {
|
||||
...data,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
this.logger.log(`System notification broadcasted: ${event}`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to broadcast system notification:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async getGatewayStats() {
|
||||
try {
|
||||
const connectedUsers = await this.emailGateway.getConnectedUsersCount();
|
||||
|
||||
return {
|
||||
connectedUsers,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error("Failed to get gateway statistics:", error);
|
||||
return {
|
||||
connectedUsers: 0,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user