chore: update webscoket connetion and polling service
This commit is contained in:
@@ -280,6 +280,7 @@ export const enum EmailMessage {
|
||||
MESSAGE_REMOVED_FROM_FAVORITE_SUCCESSFULLY = "پیام با موفقیت از مورد علاقه حذف شد",
|
||||
TRASH_EMPTYED_SUCCESSFULLY = "سطل زباله با موفقیت خالی شد",
|
||||
SPAM_EMPTYED_SUCCESSFULLY = "همه پیام های اسپم با موفقیت حذف شدند",
|
||||
SAVE_DRAFT_SUCCESS = "پیش نویس با موفقیت ذخیره شد",
|
||||
}
|
||||
|
||||
export const enum WebSocketMessage {
|
||||
|
||||
@@ -28,11 +28,7 @@ import { EmailPollingService } from "../email-utils/services/email-polling.servi
|
||||
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
@UseFilters(WsExceptionFilter)
|
||||
@WebSocketGateway({
|
||||
namespace: WEBSOCKET_NAMESPACES.EMAIL,
|
||||
cors: { origin: true, credentials: true },
|
||||
transports: ["websocket", "polling"],
|
||||
})
|
||||
@WebSocketGateway({ namespace: WEBSOCKET_NAMESPACES.EMAIL, cors: { origin: true, credentials: true }, transports: ["websocket", "polling"] })
|
||||
export class EmailGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
|
||||
@WebSocketServer()
|
||||
server: Server;
|
||||
@@ -86,7 +82,7 @@ export class EmailGateway implements OnGatewayInit, OnGatewayConnection, OnGatew
|
||||
|
||||
// Start email polling for this user
|
||||
try {
|
||||
await this.emailPollingService.startPollingForUser(user.id, client.id);
|
||||
await this.emailPollingService.startPollingForUser(user.wildduckUserId, user.id, client.id);
|
||||
this.logger.log(`Email polling started for user ${user.id}`);
|
||||
} catch (pollingError) {
|
||||
this.logger.error(`Failed to start email polling for user ${user.id}:`, pollingError);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export interface EmailPollingJobData {
|
||||
wildduckUserId: string;
|
||||
userId: string;
|
||||
lastCheckTimestamp?: string;
|
||||
connectionId: string;
|
||||
|
||||
@@ -23,47 +23,41 @@ export class EmailPollingProcessor extends WorkerProcessor {
|
||||
}
|
||||
|
||||
async process(job: Job<EmailPollingJobData>): Promise<void> {
|
||||
const { userId, lastCheckTimestamp, connectionId } = job.data;
|
||||
const { wildduckUserId, userId, lastCheckTimestamp, connectionId } = job.data;
|
||||
|
||||
this.logger.debug(`Processing email polling job for user ${userId} (connection: ${connectionId})`);
|
||||
|
||||
try {
|
||||
// Check if user is still connected before processing
|
||||
if (!this.emailGateway.isUserConnected(userId)) {
|
||||
this.logger.debug(`User ${userId} no longer connected, skipping email check`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get user's mailbox information
|
||||
const mailboxIds = await this.mailboxResolverService.getUserMailboxIds(userId);
|
||||
const mailboxIds = await this.mailboxResolverService.getUserMailboxIds(wildduckUserId);
|
||||
if (!mailboxIds.inbox) {
|
||||
this.logger.warn(`No inbox found for user ${userId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate time range for checking new emails
|
||||
const checkSince = lastCheckTimestamp ? new Date(lastCheckTimestamp) : new Date(Date.now() - 5 * 60 * 1000);
|
||||
const now = new Date();
|
||||
|
||||
this.logger.debug(`Checking emails for user ${userId} since ${checkSince.toISOString()}`);
|
||||
|
||||
// Get new emails from inbox since last check
|
||||
const newEmails = await this.getRecentEmails(userId, mailboxIds.inbox, checkSince);
|
||||
const newEmails = await this.getRecentEmails(wildduckUserId, mailboxIds.inbox, checkSince);
|
||||
|
||||
if (newEmails.length > 0) {
|
||||
this.logger.log(`Found ${newEmails.length} new emails for user ${userId}`);
|
||||
|
||||
// Notify user of each new email via websocket
|
||||
this.logger.log(`Found ${newEmails.length} new emails for user ${wildduckUserId}`);
|
||||
|
||||
const lastEmail = newEmails[newEmails.length - 1];
|
||||
await this.emailGateway.notifyNewEmail(userId, {
|
||||
wildduckUserId: userId,
|
||||
wildduckUserId,
|
||||
userId,
|
||||
messageId: lastEmail.id,
|
||||
date: lastEmail.date || new Date().toISOString(),
|
||||
from: lastEmail.from,
|
||||
to: lastEmail.to || [],
|
||||
subject: lastEmail.subject,
|
||||
userId,
|
||||
timestamp: lastEmail.date || new Date().toISOString(),
|
||||
mailboxId: lastEmail.mailbox,
|
||||
mailboxName: "Inbox",
|
||||
@@ -72,7 +66,7 @@ export class EmailPollingProcessor extends WorkerProcessor {
|
||||
});
|
||||
|
||||
// Update unread count
|
||||
const unreadStats = await this.getUnreadCounts(userId, mailboxIds);
|
||||
const unreadStats = await this.getUnreadCounts(wildduckUserId, mailboxIds);
|
||||
await this.emailGateway.notifyUnreadCountUpdate(userId, {
|
||||
userId,
|
||||
totalUnread: unreadStats.total,
|
||||
@@ -96,15 +90,15 @@ export class EmailPollingProcessor extends WorkerProcessor {
|
||||
throw error; // Re-throw to trigger retry mechanism
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to get recent emails from a mailbox
|
||||
private async getRecentEmails(userId: string, mailboxId: string, since: Date) {
|
||||
//=======================================================
|
||||
private async getRecentEmails(wildduckUserId: string, mailboxId: string, since: Date) {
|
||||
try {
|
||||
const response = await firstValueFrom(
|
||||
this.mailServerService.messages.searchMessages(userId, {
|
||||
this.mailServerService.messages.searchMessages(wildduckUserId, {
|
||||
mailbox: mailboxId,
|
||||
limit: 50, // Limit to avoid too many results
|
||||
order: "desc", // Most recent first
|
||||
limit: 50,
|
||||
order: "desc",
|
||||
unseen: true,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -117,22 +111,20 @@ export class EmailPollingProcessor extends WorkerProcessor {
|
||||
return emailDate >= since;
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(`Error fetching recent emails for user ${userId}:`, error);
|
||||
this.logger.error(`Error fetching recent emails for user ${wildduckUserId}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to get unread counts from all mailboxes
|
||||
private async getUnreadCounts(userId: string, mailboxIds: UserMailboxIds) {
|
||||
//=======================================================
|
||||
private async getUnreadCounts(wildduckUserId: string, mailboxIds: UserMailboxIds) {
|
||||
try {
|
||||
const mailboxCounts: Record<string, number> = {};
|
||||
let total = 0;
|
||||
|
||||
// Get unread count from each mailbox
|
||||
for (const [mailboxName, mailboxId] of Object.entries(mailboxIds)) {
|
||||
if (mailboxId) {
|
||||
try {
|
||||
const response = await firstValueFrom(this.mailServerService.mailboxes.getMailbox(userId, mailboxId));
|
||||
const response = await firstValueFrom(this.mailServerService.mailboxes.getMailbox(wildduckUserId, mailboxId));
|
||||
|
||||
if (response.success && response.unseen !== undefined) {
|
||||
mailboxCounts[mailboxName] = response.unseen;
|
||||
@@ -150,7 +142,7 @@ export class EmailPollingProcessor extends WorkerProcessor {
|
||||
mailboxCounts,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`Error calculating unread counts for user ${userId}:`, error);
|
||||
this.logger.error(`Error calculating unread counts for user ${wildduckUserId}:`, error);
|
||||
return {
|
||||
total: 0,
|
||||
mailboxCounts: {},
|
||||
|
||||
@@ -14,11 +14,12 @@ export class EmailPollingService {
|
||||
constructor(@InjectQueue(EMAIL_QUEUE_CONSTANTS.EMAIL_POLLING_QUEUE) private readonly emailPollingQueue: Queue<EmailPollingJobData>) {}
|
||||
|
||||
//=======================================================
|
||||
async startPollingForUser(userId: string, connectionId: string): Promise<void> {
|
||||
async startPollingForUser(wildduckUserId: string, userId: string, connectionId: string): Promise<void> {
|
||||
try {
|
||||
await this.stopPollingForUser(userId);
|
||||
|
||||
const jobData: EmailPollingJobData = {
|
||||
wildduckUserId,
|
||||
userId,
|
||||
connectionId,
|
||||
lastCheckTimestamp: new Date().toISOString(),
|
||||
@@ -54,6 +55,7 @@ export class EmailPollingService {
|
||||
async stopPollingForUser(userId: string): Promise<void> {
|
||||
try {
|
||||
const activeJob = this.activeJobs.get(userId);
|
||||
console.log(activeJob);
|
||||
if (!activeJob) {
|
||||
this.logger.debug(`No active polling job found for user ${userId}`);
|
||||
return;
|
||||
@@ -140,9 +142,9 @@ export class EmailPollingService {
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
async restartPollingForUser(userId: string, connectionId: string): Promise<void> {
|
||||
async restartPollingForUser(wildduckUserId: string, userId: string, connectionId: string): Promise<void> {
|
||||
this.logger.log(`Restarting email polling for user ${userId}`);
|
||||
await this.stopPollingForUser(userId);
|
||||
await this.startPollingForUser(userId, connectionId);
|
||||
await this.startPollingForUser(wildduckUserId, userId, connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ export class MailboxResolverService {
|
||||
/**
|
||||
* Get user mailbox IDs with caching
|
||||
*/
|
||||
async getUserMailboxIds(userId: string): Promise<UserMailboxIds> {
|
||||
const cached = this.getCachedMailboxIds(userId);
|
||||
async getUserMailboxIds(wildduckUserId: string): Promise<UserMailboxIds> {
|
||||
const cached = this.getCachedMailboxIds(wildduckUserId);
|
||||
if (cached) return cached;
|
||||
|
||||
this.logger.log(`Fetching mailbox IDs for user: ${userId}`);
|
||||
this.logger.log(`Fetching mailbox IDs for user: ${wildduckUserId}`);
|
||||
|
||||
try {
|
||||
const mailboxes = await firstValueFrom(this.mailServerService.mailboxes.listMailboxes(userId));
|
||||
const mailboxes = await firstValueFrom(this.mailServerService.mailboxes.listMailboxes(wildduckUserId));
|
||||
|
||||
const mailboxIds: UserMailboxIds = {
|
||||
inbox: this.findMailboxId(mailboxes.results, MailboxEnum.INBOX),
|
||||
@@ -37,12 +37,12 @@ export class MailboxResolverService {
|
||||
// favorite: this.findMailboxId(mailboxes.results, MailboxEnum.FAVORITE, false),
|
||||
};
|
||||
|
||||
this.cacheMailboxIds(userId, mailboxIds);
|
||||
this.cacheMailboxIds(wildduckUserId, mailboxIds);
|
||||
|
||||
this.logger.log(`Mailbox IDs cached for user: ${userId}`);
|
||||
this.logger.log(`Mailbox IDs cached for user: ${wildduckUserId}`);
|
||||
return mailboxIds;
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to fetch mailbox IDs for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
this.logger.error(`Failed to fetch mailbox IDs for user ${wildduckUserId}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -50,12 +50,12 @@ export class MailboxResolverService {
|
||||
/**
|
||||
* Get specific mailbox ID
|
||||
*/
|
||||
async getMailboxId(userId: string, mailboxType: keyof UserMailboxIds): Promise<string> {
|
||||
const mailboxIds = await this.getUserMailboxIds(userId);
|
||||
async getMailboxId(wildduckUserId: string, mailboxType: keyof UserMailboxIds): Promise<string> {
|
||||
const mailboxIds = await this.getUserMailboxIds(wildduckUserId);
|
||||
const mailboxId = mailboxIds[mailboxType];
|
||||
|
||||
if (!mailboxId) {
|
||||
throw new NotFoundException(`${mailboxType} mailbox not found for user ${userId}`);
|
||||
throw new NotFoundException(`${mailboxType} mailbox not found for user ${wildduckUserId}`);
|
||||
}
|
||||
|
||||
return mailboxId;
|
||||
@@ -73,37 +73,37 @@ export class MailboxResolverService {
|
||||
/**
|
||||
* Get drafts mailbox ID
|
||||
*/
|
||||
async getDraftsMailboxId(userId: string): Promise<string> {
|
||||
return this.getMailboxId(userId, "drafts");
|
||||
async getDraftsMailboxId(wildduckUserId: string): Promise<string> {
|
||||
return this.getMailboxId(wildduckUserId, "drafts");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get inbox mailbox ID
|
||||
*/
|
||||
async getInboxMailboxId(userId: string): Promise<string> {
|
||||
return this.getMailboxId(userId, "inbox");
|
||||
async getInboxMailboxId(wildduckUserId: string): Promise<string> {
|
||||
return this.getMailboxId(wildduckUserId, "inbox");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sent mailbox ID
|
||||
*/
|
||||
async getSentMailboxId(userId: string): Promise<string> {
|
||||
return this.getMailboxId(userId, "sent");
|
||||
async getSentMailboxId(wildduckUserId: string): Promise<string> {
|
||||
return this.getMailboxId(wildduckUserId, "sent");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trash mailbox ID
|
||||
*/
|
||||
async getTrashMailboxId(userId: string): Promise<string> {
|
||||
return this.getMailboxId(userId, "trash");
|
||||
async getTrashMailboxId(wildduckUserId: string): Promise<string> {
|
||||
return this.getMailboxId(wildduckUserId, "trash");
|
||||
}
|
||||
|
||||
async getJunkMailboxId(userId: string): Promise<string> {
|
||||
return this.getMailboxId(userId, "junk");
|
||||
async getJunkMailboxId(wildduckUserId: string): Promise<string> {
|
||||
return this.getMailboxId(wildduckUserId, "junk");
|
||||
}
|
||||
|
||||
async getArchiveMailboxId(userId: string): Promise<string> {
|
||||
return this.getMailboxId(userId, "archive");
|
||||
async getArchiveMailboxId(wildduckUserId: string): Promise<string> {
|
||||
return this.getMailboxId(wildduckUserId, "archive");
|
||||
}
|
||||
|
||||
// async getFavoriteMailboxId(userId: string): Promise<string> {
|
||||
@@ -113,10 +113,10 @@ export class MailboxResolverService {
|
||||
/**
|
||||
* Clear cache for a specific user
|
||||
*/
|
||||
clearUserCache(userId: string): void {
|
||||
this.mailboxCache.delete(userId);
|
||||
this.cacheExpiry.delete(userId);
|
||||
this.logger.log(`Cache cleared for user: ${userId}`);
|
||||
clearUserCache(wildduckUserId: string): void {
|
||||
this.mailboxCache.delete(wildduckUserId);
|
||||
this.cacheExpiry.delete(wildduckUserId);
|
||||
this.logger.log(`Cache cleared for user: ${wildduckUserId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,18 +131,18 @@ export class MailboxResolverService {
|
||||
/**
|
||||
* Get cached mailbox IDs if not expired
|
||||
*/
|
||||
private getCachedMailboxIds(userId: string): UserMailboxIds | null {
|
||||
const cached = this.mailboxCache.get(userId);
|
||||
const expiry = this.cacheExpiry.get(userId);
|
||||
private getCachedMailboxIds(wildduckUserId: string): UserMailboxIds | null {
|
||||
const cached = this.mailboxCache.get(wildduckUserId);
|
||||
const expiry = this.cacheExpiry.get(wildduckUserId);
|
||||
|
||||
if (cached && expiry && Date.now() < expiry) {
|
||||
this.logger.debug(`Using cached mailbox IDs for user: ${userId}`);
|
||||
this.logger.debug(`Using cached mailbox IDs for user: ${wildduckUserId}`);
|
||||
return cached;
|
||||
}
|
||||
|
||||
if (cached) {
|
||||
this.logger.debug(`Cache expired for user: ${userId}`);
|
||||
this.clearUserCache(userId);
|
||||
this.logger.debug(`Cache expired for user: ${wildduckUserId}`);
|
||||
this.clearUserCache(wildduckUserId);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -151,9 +151,9 @@ export class MailboxResolverService {
|
||||
/**
|
||||
* Cache mailbox IDs with expiry
|
||||
*/
|
||||
private cacheMailboxIds(userId: string, mailboxIds: UserMailboxIds): void {
|
||||
this.mailboxCache.set(userId, mailboxIds);
|
||||
this.cacheExpiry.set(userId, Date.now() + this.CACHE_DURATION);
|
||||
private cacheMailboxIds(wildduckUserId: string, mailboxIds: UserMailboxIds): void {
|
||||
this.mailboxCache.set(wildduckUserId, mailboxIds);
|
||||
this.cacheExpiry.set(wildduckUserId, Date.now() + this.CACHE_DURATION);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,13 +49,13 @@ export const EMAIL_QUEUE_CONSTANTS = {
|
||||
EMAIL_BULK_ACTION_DELAY: 1000,
|
||||
EMAIL_BULK_ACTION_BATCH_SIZE: 250,
|
||||
EMAIL_BULK_ACTION_ATTEMPTS: 3,
|
||||
EMAIL_BULK_ACTION_BACKOFF_DELAY: 2000, // 2 seconds
|
||||
EMAIL_BULK_ACTION_BACKOFF_DELAY: 2000,
|
||||
EMAIL_BULK_ACTION_PRIORITY: 1,
|
||||
//
|
||||
EMAIL_POLLING_INTERVAL: 30000, // 30 seconds
|
||||
EMAIL_POLLING_REMOVE_ON_COMPLETE: 5, // Keep last 5 completed jobs
|
||||
EMAIL_POLLING_REMOVE_ON_FAIL: 10, // Keep last 10 failed jobs
|
||||
EMAIL_POLLING_ATTEMPTS: 3, // Retry failed jobs 3 times
|
||||
EMAIL_POLLING_BACKOFF_DELAY: 2000, // 2 seconds
|
||||
EMAIL_POLLING_INTERVAL: 3000,
|
||||
EMAIL_POLLING_REMOVE_ON_COMPLETE: true,
|
||||
EMAIL_POLLING_REMOVE_ON_FAIL: 10,
|
||||
EMAIL_POLLING_ATTEMPTS: 3,
|
||||
EMAIL_POLLING_BACKOFF_DELAY: 2000,
|
||||
EMAIL_POLLING_PRIORITY: 1,
|
||||
} as const;
|
||||
|
||||
@@ -115,24 +115,24 @@ export class EmailController {
|
||||
@ApiResponse({ status: 206, description: "Some messages marked as read, some failed" })
|
||||
@ApiResponse({ status: 404, description: "Mailbox not found" })
|
||||
@ApiResponse({ status: 403, description: "Not authorized to mark messages as read" })
|
||||
markAllMessagesAsRead(@UserDec("wildduckUserId") userId: string, @Query() query: MailboxIdQueryDto) {
|
||||
return this.emailService.markAllMessagesAsRead(userId, query.mailbox);
|
||||
markAllMessagesAsRead(@UserDec("wildduckUserId") wildduckUserId: string, @UserDec("id") userId: string, @Query() query: MailboxIdQueryDto) {
|
||||
return this.emailService.markAllMessagesAsRead(wildduckUserId, userId, query.mailbox);
|
||||
}
|
||||
|
||||
@Patch("messages/empty-trash")
|
||||
@ApiOperation({ summary: "Empty trash" })
|
||||
@ApiResponse({ status: 200, description: "Trash emptied successfully" })
|
||||
@ApiResponse({ status: 404, description: "Trash not found" })
|
||||
emptyTrash(@UserDec("wildduckUserId") userId: string, @Query() query: MailboxIdQueryDto) {
|
||||
return this.emailService.emptyTrash(userId, query.mailbox);
|
||||
emptyTrash(@UserDec("wildduckUserId") wildduckUserId: string, @UserDec("id") userId: string, @Query() query: MailboxIdQueryDto) {
|
||||
return this.emailService.emptyTrash(wildduckUserId, userId, query.mailbox);
|
||||
}
|
||||
|
||||
@Patch("messages/empty-spam")
|
||||
@ApiOperation({ summary: "Empty spam" })
|
||||
@ApiResponse({ status: 200, description: "Spam emptied successfully" })
|
||||
@ApiResponse({ status: 404, description: "Spam not found" })
|
||||
emptySpam(@UserDec("wildduckUserId") userId: string, @Query() query: MailboxIdQueryDto) {
|
||||
return this.emailService.emptySpam(userId, query.mailbox);
|
||||
emptySpam(@UserDec("wildduckUserId") wildduckUserId: string, @UserDec("id") userId: string, @Query() query: MailboxIdQueryDto) {
|
||||
return this.emailService.emptySpam(wildduckUserId, userId, query.mailbox);
|
||||
}
|
||||
|
||||
@Get("messages/inbox")
|
||||
|
||||
@@ -17,6 +17,10 @@ import { User } from "../users/entities/user.entity";
|
||||
imports: [
|
||||
BullModule.registerQueue({
|
||||
name: EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_QUEUE,
|
||||
defaultJobOptions: {
|
||||
removeOnComplete: true,
|
||||
removeOnFail: false,
|
||||
},
|
||||
}),
|
||||
MailServerModule,
|
||||
TemplatesModule,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export interface IEmailBulkActionJob {
|
||||
wildduckUserId: string;
|
||||
userId: string;
|
||||
mailboxId: string;
|
||||
}
|
||||
|
||||
@@ -31,17 +31,17 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
switch (name) {
|
||||
case EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_SEEN_ALL:
|
||||
this.logger.debug(`Processing bulk action for: ${data.wildduckUserId}`);
|
||||
await this.markAllMessagesAsRead(data.wildduckUserId, data.mailboxId);
|
||||
await this.markAllMessagesAsRead(data.wildduckUserId, data.userId, data.mailboxId);
|
||||
break;
|
||||
|
||||
case EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_EMPTY_TRASH:
|
||||
this.logger.debug(`Processing bulk action for: ${data.wildduckUserId}`);
|
||||
await this.emptyTrash(data.wildduckUserId, data.mailboxId);
|
||||
await this.emptyTrash(data.wildduckUserId, data.userId, data.mailboxId);
|
||||
break;
|
||||
|
||||
case EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_EMPTY_SPAM:
|
||||
this.logger.debug(`Processing bulk action for: ${data.wildduckUserId}`);
|
||||
await this.emptySpam(data.wildduckUserId, data.mailboxId);
|
||||
await this.emptySpam(data.wildduckUserId, data.userId, data.mailboxId);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -55,12 +55,11 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
}
|
||||
|
||||
//==============================================
|
||||
private async markAllMessagesAsRead(wildduckUserId: string, mailboxId: string) {
|
||||
private async markAllMessagesAsRead(wildduckUserId: string, userId: string, mailboxId: string) {
|
||||
this.logger.log(`Marking all messages as read for user ${wildduckUserId} in mailbox ${mailboxId}`);
|
||||
|
||||
try {
|
||||
// Get all unread messages from the mailbox with a reasonable limit
|
||||
const limit = EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_BATCH_SIZE; // Process in batches to avoid memory issues
|
||||
const limit = EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_BATCH_SIZE;
|
||||
let totalMarked = 0;
|
||||
let hasMore = true;
|
||||
let next: string | undefined;
|
||||
@@ -72,11 +71,10 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
};
|
||||
|
||||
while (hasMore) {
|
||||
// Fetch unread messages in batches
|
||||
const query: Record<string, unknown> = {
|
||||
limit,
|
||||
order: "desc",
|
||||
unseen: true, // Only get unread messages
|
||||
unseen: true,
|
||||
};
|
||||
|
||||
if (next) {
|
||||
@@ -90,7 +88,6 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
break;
|
||||
}
|
||||
|
||||
// Mark each message as seen
|
||||
for (const message of response.results) {
|
||||
try {
|
||||
await this.emailService.markMessageAsSeen(wildduckUserId, message.id, mailboxId);
|
||||
@@ -105,7 +102,6 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there are more messages
|
||||
next = typeof response.nextCursor === "string" ? response.nextCursor : undefined;
|
||||
hasMore = !!next && response.results.length === limit;
|
||||
}
|
||||
@@ -113,12 +109,14 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
results.totalMarked = totalMarked;
|
||||
|
||||
await this.emailNotificationService.notifyEmailRead({
|
||||
userId: wildduckUserId,
|
||||
userId,
|
||||
messageId: results.successful[0],
|
||||
mailboxId,
|
||||
});
|
||||
|
||||
this.logger.log(`Marked ${totalMarked} messages as read for user ${wildduckUserId} in mailbox ${mailboxId}, ${results.failed.length} failed`);
|
||||
this.logger.verbose(
|
||||
`Marked ${totalMarked} messages as read for user ${wildduckUserId} in mailbox ${mailboxId}, ${results.failed.length} failed`,
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -133,7 +131,7 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
|
||||
//==============================================
|
||||
|
||||
private async emptyTrash(wildduckUserId: string, mailboxId: string) {
|
||||
private async emptyTrash(wildduckUserId: string, userId: string, mailboxId: string) {
|
||||
this.logger.log(`Emptying trash for user ${wildduckUserId} in mailbox ${mailboxId}`);
|
||||
|
||||
try {
|
||||
@@ -145,7 +143,7 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
}
|
||||
|
||||
await this.emailNotificationService.notifyEmailDeleted({
|
||||
userId: wildduckUserId,
|
||||
userId,
|
||||
messageId: randomInt(1, 500),
|
||||
mailboxId,
|
||||
});
|
||||
@@ -162,7 +160,7 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
}
|
||||
|
||||
//==============================================
|
||||
private async emptySpam(wildduckUserId: string, mailboxId: string) {
|
||||
private async emptySpam(wildduckUserId: string, userId: string, mailboxId: string) {
|
||||
this.logger.log(`Emptying spam for user ${wildduckUserId} in mailbox ${mailboxId}`);
|
||||
|
||||
try {
|
||||
@@ -174,7 +172,7 @@ export class EmailBulkActionProcessor extends WorkerHost {
|
||||
}
|
||||
|
||||
await this.emailNotificationService.notifyEmailDeleted({
|
||||
userId: wildduckUserId,
|
||||
userId,
|
||||
messageId: randomInt(1, 500),
|
||||
mailboxId,
|
||||
});
|
||||
|
||||
@@ -88,7 +88,7 @@ export class EmailService {
|
||||
});
|
||||
|
||||
return {
|
||||
message: EmailMessage.EMAIL_SENDING_SUCCESS,
|
||||
message: sendEmailDto.isDraft ? EmailMessage.SAVE_DRAFT_SUCCESS : EmailMessage.EMAIL_SENDING_SUCCESS,
|
||||
messageId: response.message.id,
|
||||
queueId: response.message.queueId,
|
||||
};
|
||||
@@ -109,15 +109,18 @@ export class EmailService {
|
||||
async searchMessages(wildduckUserId: string, query: SearchMessagesQueryDto) {
|
||||
const wildDuckQuery: SearchMessagesMailServerQueryDto = {
|
||||
...query,
|
||||
limit: query.limit || 20,
|
||||
limit: 100,
|
||||
page: query.page || 1,
|
||||
order: query.order || "desc",
|
||||
searchable: true,
|
||||
query: query.search,
|
||||
from: query.from || query.search,
|
||||
// mailbox: query.mailbox,
|
||||
// subject: query.subject || query.search,
|
||||
// from: query.from || query.search,
|
||||
// to: query.to || query.search,
|
||||
};
|
||||
|
||||
delete wildDuckQuery?.mailbox;
|
||||
delete wildDuckQuery.search;
|
||||
|
||||
const response = await firstValueFrom(this.mailServerService.messages.searchMessages(wildduckUserId, wildDuckQuery));
|
||||
|
||||
@@ -283,11 +286,12 @@ export class EmailService {
|
||||
}
|
||||
|
||||
//==============================================
|
||||
async markAllMessagesAsRead(wildduckUserId: string, mailboxId: string) {
|
||||
async markAllMessagesAsRead(wildduckUserId: string, userId: string, mailboxId: string) {
|
||||
await this.emailBulkActionQueue.add(
|
||||
EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_SEEN_ALL,
|
||||
{
|
||||
wildduckUserId,
|
||||
userId,
|
||||
mailboxId,
|
||||
},
|
||||
{
|
||||
@@ -308,11 +312,24 @@ export class EmailService {
|
||||
}
|
||||
|
||||
//==============================================
|
||||
async emptyTrash(wildduckUserId: string, mailboxId: string) {
|
||||
await this.emailBulkActionQueue.add(EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_EMPTY_TRASH, {
|
||||
wildduckUserId,
|
||||
mailboxId,
|
||||
});
|
||||
async emptyTrash(wildduckUserId: string, userId: string, mailboxId: string) {
|
||||
await this.emailBulkActionQueue.add(
|
||||
EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_EMPTY_TRASH,
|
||||
{
|
||||
wildduckUserId,
|
||||
userId,
|
||||
mailboxId,
|
||||
},
|
||||
{
|
||||
delay: EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_DELAY,
|
||||
priority: EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_PRIORITY,
|
||||
attempts: EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_ATTEMPTS,
|
||||
backoff: {
|
||||
type: "exponential",
|
||||
delay: EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_BACKOFF_DELAY,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -321,11 +338,24 @@ export class EmailService {
|
||||
}
|
||||
|
||||
//==============================================
|
||||
async emptySpam(wildduckUserId: string, mailboxId: string) {
|
||||
await this.emailBulkActionQueue.add(EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_EMPTY_SPAM, {
|
||||
wildduckUserId,
|
||||
mailboxId,
|
||||
});
|
||||
async emptySpam(wildduckUserId: string, userId: string, mailboxId: string) {
|
||||
await this.emailBulkActionQueue.add(
|
||||
EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_EMPTY_SPAM,
|
||||
{
|
||||
wildduckUserId,
|
||||
userId,
|
||||
mailboxId,
|
||||
},
|
||||
{
|
||||
delay: EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_DELAY,
|
||||
priority: EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_PRIORITY,
|
||||
attempts: EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_ATTEMPTS,
|
||||
backoff: {
|
||||
type: "exponential",
|
||||
delay: EMAIL_QUEUE_CONSTANTS.EMAIL_BULK_ACTION_BACKOFF_DELAY,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
||||
@@ -27,6 +27,8 @@ export class UpdateMessageDto {
|
||||
}
|
||||
|
||||
export class SearchMessagesMailServerQueryDto {
|
||||
search?: string;
|
||||
|
||||
q?: string;
|
||||
|
||||
mailbox?: string;
|
||||
|
||||
Reference in New Issue
Block a user