chore: websocket
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { MailServerService } from "../../mail-server/services/mail-server.service";
|
||||
import { EmailGateway } from "../email.gateway";
|
||||
import { MailboxResolverService } from "./mailbox-resolver.service";
|
||||
import { EmailNotificationPayload } from "../interfaces/email-events.interface";
|
||||
|
||||
@Injectable()
|
||||
export class EmailNotificationService {
|
||||
private readonly logger = new Logger(EmailNotificationService.name);
|
||||
|
||||
constructor(
|
||||
private readonly emailGateway: EmailGateway,
|
||||
private readonly mailServerService: MailServerService,
|
||||
private readonly mailboxResolverService: MailboxResolverService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Handle new incoming message and emit WebSocket notification
|
||||
* This method can be called by webhook handlers, polling services, etc.
|
||||
*/
|
||||
async handleNewMessage(userId: string, messageId: number, mailboxId?: string) {
|
||||
try {
|
||||
this.logger.log(`Processing new message notification for user ${userId}, message ${messageId}`);
|
||||
|
||||
// Get the mailbox ID if not provided
|
||||
const targetMailboxId = mailboxId || (await this.mailboxResolverService.getInboxMailboxId(userId));
|
||||
|
||||
// Fetch the message details
|
||||
const message = await firstValueFrom(this.mailServerService.messages.getMessage(userId, targetMailboxId, messageId));
|
||||
|
||||
// Determine mailbox name
|
||||
const mailboxName = await this.getMailboxName(userId, targetMailboxId);
|
||||
|
||||
// Create notification payload
|
||||
const notificationPayload: EmailNotificationPayload = {
|
||||
messageId,
|
||||
userId,
|
||||
from: {
|
||||
name: message.from?.name || message.from?.address?.split("@")[0] || "Unknown",
|
||||
address: message.from?.address || "unknown@example.com",
|
||||
},
|
||||
to:
|
||||
message.to?.map((recipient) => ({
|
||||
name: recipient.name || recipient.address?.split("@")[0] || "Unknown",
|
||||
address: recipient.address || "unknown@example.com",
|
||||
})) || [],
|
||||
subject: message.subject || "No Subject",
|
||||
preview: this.extractPreview(
|
||||
typeof message.text === "string" ? message.text : Array.isArray(message.html) ? message.html.join("") : message.html || "",
|
||||
),
|
||||
hasAttachments: Array.isArray(message.attachments) && message.attachments.length > 0,
|
||||
received: message.idate || new Date().toISOString(),
|
||||
mailboxId: targetMailboxId,
|
||||
mailboxName,
|
||||
isRead: message.seen || false,
|
||||
isFlagged: message.flagged || false,
|
||||
size: message.size || 0,
|
||||
};
|
||||
|
||||
// Emit WebSocket notification
|
||||
await this.emailGateway.notifyNewMessage(notificationPayload);
|
||||
|
||||
this.logger.log(`New message notification sent for user ${userId}, message ${messageId}`);
|
||||
|
||||
return notificationPayload;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to process new message notification for user ${userId}, message ${messageId}: ${
|
||||
error instanceof Error ? error.message : "Unknown error"
|
||||
}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle bulk new messages (useful for initial sync or batch processing)
|
||||
*/
|
||||
async handleBulkNewMessages(userId: string, messageIds: number[], mailboxId?: string) {
|
||||
this.logger.log(`Processing bulk new message notifications for user ${userId}, ${messageIds.length} messages`);
|
||||
|
||||
const results = [];
|
||||
for (const messageId of messageIds) {
|
||||
try {
|
||||
const result = await this.handleNewMessage(userId, messageId, mailboxId);
|
||||
results.push(result);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to process message ${messageId} in bulk notification: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate new message arrival (for testing purposes)
|
||||
*/
|
||||
async simulateNewMessage(userId: string, messageData: Partial<EmailNotificationPayload>) {
|
||||
this.logger.log(`Simulating new message for user ${userId}`);
|
||||
|
||||
const simulatedPayload: EmailNotificationPayload = {
|
||||
messageId: Math.floor(Math.random() * 10000),
|
||||
userId,
|
||||
from: {
|
||||
name: "Test Sender",
|
||||
address: "test@example.com",
|
||||
},
|
||||
to: [
|
||||
{
|
||||
name: "Test Recipient",
|
||||
address: "recipient@example.com",
|
||||
},
|
||||
],
|
||||
subject: "Test Message",
|
||||
preview: "This is a test message for WebSocket notification",
|
||||
hasAttachments: false,
|
||||
received: new Date().toISOString(),
|
||||
mailboxId: "inbox",
|
||||
mailboxName: "Inbox",
|
||||
isRead: false,
|
||||
isFlagged: false,
|
||||
size: 1024,
|
||||
...messageData,
|
||||
};
|
||||
|
||||
await this.emailGateway.notifyNewMessage(simulatedPayload);
|
||||
|
||||
return simulatedPayload;
|
||||
}
|
||||
|
||||
private async getMailboxName(userId: string, mailboxId: string): Promise<string> {
|
||||
try {
|
||||
const mailboxIds = await this.mailboxResolverService.getUserMailboxIds(userId);
|
||||
|
||||
// Check which mailbox this ID corresponds to
|
||||
if (mailboxIds.inbox === mailboxId) return "Inbox";
|
||||
if (mailboxIds.sent === mailboxId) return "Sent";
|
||||
if (mailboxIds.drafts === mailboxId) return "Drafts";
|
||||
if (mailboxIds.trash === mailboxId) return "Trash";
|
||||
if (mailboxIds.junk === mailboxId) return "Junk";
|
||||
if (mailboxIds.archive === mailboxId) return "Archive";
|
||||
if (mailboxIds.favorite === mailboxId) return "Favorite";
|
||||
|
||||
return "Unknown";
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to get mailbox name for ${mailboxId}: ${error}`);
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private extractPreview(content: string): string {
|
||||
// Remove HTML tags and get first 150 characters
|
||||
const textContent = content.replace(/<[^>]*>/g, "").trim();
|
||||
return textContent.length > 150 ? textContent.substring(0, 150) + "..." : textContent;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user