chore: update webscoket connetion and polling service
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user