chore: fix

This commit is contained in:
mahyargdz
2025-07-10 10:55:35 +03:30
parent 784f168e8c
commit 85fdc7af4e
9 changed files with 388 additions and 56 deletions
+3
View File
@@ -194,6 +194,9 @@ export const enum EmailMessage {
SENT_MESSAGES_RETRIEVAL_FAILED = "دریافت پیام های ارسالی با خطا مواجه شد", SENT_MESSAGES_RETRIEVAL_FAILED = "دریافت پیام های ارسالی با خطا مواجه شد",
TRASH_MESSAGES_RETRIEVED_SUCCESSFULLY = "پیام های حذف شده با موفقیت دریافت شد", TRASH_MESSAGES_RETRIEVED_SUCCESSFULLY = "پیام های حذف شده با موفقیت دریافت شد",
TRASH_MESSAGES_RETRIEVAL_FAILED = "دریافت پیام های حذف شده با خطا مواجه شد", TRASH_MESSAGES_RETRIEVAL_FAILED = "دریافت پیام های حذف شده با خطا مواجه شد",
DRAFT_UPDATED_SUCCESSFULLY = "پیش نویس با موفقیت به روز رسانی شد",
DRAFT_SENT_SUCCESSFULLY = "پیش نویس با موفقیت ارسال شد",
DRAFT_DELETED_SUCCESSFULLY = "پیش نویس با موفقیت حذف شد",
} }
export const enum SmsMessage { export const enum SmsMessage {
+4 -4
View File
@@ -26,10 +26,10 @@ export class TokensService {
id: user.id, id: user.id,
role: user.role.name, role: user.role.name,
inboxId: mailboxes?.[0] || "", inboxId: mailboxes?.[0] || "",
sentId: mailboxes?.[1] || "", draftsId: mailboxes?.[1] || "",
draftsId: mailboxes?.[2] || "", junkId: mailboxes?.[2] || "",
trashId: mailboxes?.[3] || "", sentId: mailboxes?.[3] || "",
junkId: mailboxes?.[4] || "", trashId: mailboxes?.[4] || "",
wildduckUserId: user.wildduckUserId, wildduckUserId: user.wildduckUserId,
}, },
em, em,
+1 -1
View File
@@ -39,7 +39,7 @@ export class UserMailboxParamDto {
mailboxId: string; mailboxId: string;
} }
export class UserMailboxMessageParamDto { export class MessageParamDto {
@ApiProperty({ description: "Message ID", example: "12345" }) @ApiProperty({ description: "Message ID", example: "12345" })
@Transform(({ value }) => parseInt(value)) @Transform(({ value }) => parseInt(value))
@IsNumber({}, { message: EmailMessage.MESSAGE_ID_MUST_BE_NUMBER }) @IsNumber({}, { message: EmailMessage.MESSAGE_ID_MUST_BE_NUMBER })
+5
View File
@@ -215,3 +215,8 @@ export class SendEmailDto {
@ApiPropertyOptional({ description: "Optional envelope (optional)" }) @ApiPropertyOptional({ description: "Optional envelope (optional)" })
envelope?: EmailEnvelopeDto; envelope?: EmailEnvelopeDto;
} }
//########################################################
export class UpdateDraftDto extends SendEmailDto {
// Draft ID is passed in URL parameter, not in body
}
+56 -20
View File
@@ -1,10 +1,10 @@
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common"; import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common";
import { ApiOperation, ApiResponse } from "@nestjs/swagger"; import { ApiOperation, ApiResponse } from "@nestjs/swagger";
import { QueueIdParamDto, UserMailboxMessageParamDto } from "./DTO/email-params.dto"; import { MessageParamDto, QueueIdParamDto } from "./DTO/email-params.dto";
import { MessageListQueryDto } from "./DTO/email-query.dto"; import { MessageListQueryDto } from "./DTO/email-query.dto";
import { SearchMessagesQueryDto } from "./DTO/email-query.dto"; import { SearchMessagesQueryDto } from "./DTO/email-query.dto";
import { SendEmailDto } from "./DTO/send-email.dto"; import { SendEmailDto, UpdateDraftDto } from "./DTO/send-email.dto";
import { EmailService } from "./services/email.service"; import { EmailService } from "./services/email.service";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { UserDec } from "../../common/decorators/user.decorator"; import { UserDec } from "../../common/decorators/user.decorator";
@@ -31,28 +31,20 @@ export class EmailController {
return this.emailService.getEmailStatus(params.queueId); return this.emailService.getEmailStatus(params.queueId);
} }
@Get("messages/inbox")
@ApiOperation({ summary: "List messages in a inbox" })
@ApiResponse({ status: 200, description: "List of messages" })
@ApiResponse({ status: 404, description: "Mailbox not found" })
listMessages(@UserDec("wildduckUserId") userId: string, @UserDec("inboxId") inboxId: string, @Query() query: MessageListQueryDto) {
return this.emailService.listMessages(userId, inboxId, query);
}
@Get("messages/inbox/:messageId") @Get("messages/inbox/:messageId")
@ApiOperation({ summary: "Get a specific message" }) @ApiOperation({ summary: "Get a specific message" })
@ApiResponse({ status: 200, description: "Message details" }) @ApiResponse({ status: 200, description: "Message details" })
@ApiResponse({ status: 404, description: "Message not found" }) @ApiResponse({ status: 404, description: "Message not found" })
getMessage(@UserDec("wildduckUserId") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) { getMessage(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
return this.emailService.getMessage(userId, inboxId, params.messageId); return this.emailService.getMessage(userId, params.messageId);
} }
@Delete("messages/inbox/:messageId") @Delete("messages/inbox/:messageId")
@ApiOperation({ summary: "Delete a message" }) @ApiOperation({ summary: "Delete a message" })
@ApiResponse({ status: 200, description: "Message deleted successfully" }) @ApiResponse({ status: 200, description: "Message deleted successfully" })
@ApiResponse({ status: 404, description: "Message not found" }) @ApiResponse({ status: 404, description: "Message not found" })
deleteMessage(@UserDec("wildduckUserId") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) { deleteMessage(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
return this.emailService.deleteMessage(userId, inboxId, params.messageId); return this.emailService.deleteMessage(userId, params.messageId);
} }
@Get("search") @Get("search")
@@ -66,23 +58,67 @@ export class EmailController {
@ApiOperation({ summary: "Mark a message as seen" }) @ApiOperation({ summary: "Mark a message as seen" })
@ApiResponse({ status: 200, description: "Message marked as seen successfully" }) @ApiResponse({ status: 200, description: "Message marked as seen successfully" })
@ApiResponse({ status: 404, description: "Message not found" }) @ApiResponse({ status: 404, description: "Message not found" })
markMessageAsSeen(@UserDec("wildduckUserId") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) { markMessageAsSeen(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
return this.emailService.markMessageAsSeen(userId, inboxId, Number(params.messageId)); return this.emailService.markMessageAsSeen(userId, Number(params.messageId));
}
@Get("messages/inbox")
@ApiOperation({ summary: "List messages in a inbox" })
@ApiResponse({ status: 200, description: "List of messages" })
@ApiResponse({ status: 404, description: "Mailbox not found" })
listMessages(@UserDec("wildduckUserId") userId: string, @Query() query: MessageListQueryDto) {
return this.emailService.listMessages(userId, query);
} }
@Get("messages/sent") @Get("messages/sent")
@ApiOperation({ summary: "List messages in sent mailbox" }) @ApiOperation({ summary: "List messages in sent mailbox" })
@ApiResponse({ status: 200, description: "List of sent messages" }) @ApiResponse({ status: 200, description: "List of sent messages" })
@ApiResponse({ status: 404, description: "Sent mailbox not found" }) @ApiResponse({ status: 404, description: "Sent mailbox not found" })
getSentMessages(@UserDec("wildduckUserId") userId: string, @UserDec("sentId") sentId: string, @Query() query: MessageListQueryDto) { getSentMessages(@UserDec("wildduckUserId") userId: string, @Query() query: MessageListQueryDto) {
return this.emailService.getSentMessages(userId, sentId, query); return this.emailService.getSentMessages(userId, query);
} }
@Get("messages/trash") @Get("messages/trash")
@ApiOperation({ summary: "List messages in trash mailbox" }) @ApiOperation({ summary: "List messages in trash mailbox" })
@ApiResponse({ status: 200, description: "List of trash messages" }) @ApiResponse({ status: 200, description: "List of trash messages" })
@ApiResponse({ status: 404, description: "Trash mailbox not found" }) @ApiResponse({ status: 404, description: "Trash mailbox not found" })
getTrashMessages(@UserDec("wildduckUserId") userId: string, @UserDec("trashId") trashId: string, @Query() query: MessageListQueryDto) { getTrashMessages(@UserDec("wildduckUserId") userId: string, @Query() query: MessageListQueryDto) {
return this.emailService.getTrashMessages(userId, trashId, query); return this.emailService.getTrashMessages(userId, query);
}
@Get("messages/drafts")
@ApiOperation({ summary: "List messages in drafts mailbox" })
@ApiResponse({ status: 200, description: "List of drafts messages" })
@ApiResponse({ status: 404, description: "Drafts mailbox not found" })
getDraftsMessages(@UserDec("wildduckUserId") userId: string, @Query() query: MessageListQueryDto) {
return this.emailService.getDraftsMessages(userId, query);
}
@Patch("messages/drafts/:messageId")
@ApiOperation({ summary: "Update an existing draft" })
@ApiResponse({ status: 200, description: "Draft updated successfully" })
@ApiResponse({ status: 400, description: "Invalid draft data" })
@ApiResponse({ status: 404, description: "Draft not found" })
@ApiResponse({ status: 403, description: "Not authorized to update draft" })
updateDraft(@UserDec("wildduckUserId") userEmailId: string, @Param() params: MessageParamDto, @Body() updateDraftDto: UpdateDraftDto) {
return this.emailService.updateDraft(userEmailId, Number(params.messageId), updateDraftDto);
}
@Post("messages/drafts/:messageId/send")
@ApiOperation({ summary: "Send a draft message" })
@ApiResponse({ status: 200, description: "Draft sent successfully" })
@ApiResponse({ status: 404, description: "Draft not found" })
@ApiResponse({ status: 403, description: "Not authorized to send draft" })
sendDraft(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
return this.emailService.sendDraft(userId, Number(params.messageId));
}
@Delete("messages/drafts/:messageId")
@ApiOperation({ summary: "Delete a draft message" })
@ApiResponse({ status: 200, description: "Draft deleted successfully" })
@ApiResponse({ status: 404, description: "Draft not found" })
@ApiResponse({ status: 403, description: "Not authorized to delete draft" })
deleteDraft(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
return this.emailService.deleteDraft(userId, Number(params.messageId));
} }
} }
+3 -2
View File
@@ -2,13 +2,14 @@ import { Module } from "@nestjs/common";
import { EmailController } from "./email.controller"; import { EmailController } from "./email.controller";
import { EmailService } from "./services/email.service"; import { EmailService } from "./services/email.service";
import { MailboxResolverService } from "./services/mailbox-resolver.service";
import { MailServerModule } from "../mail-server/mail-server.module"; import { MailServerModule } from "../mail-server/mail-server.module";
import { UsersModule } from "../users/users.module"; import { UsersModule } from "../users/users.module";
@Module({ @Module({
imports: [MailServerModule, UsersModule], imports: [MailServerModule, UsersModule],
controllers: [EmailController], controllers: [EmailController],
providers: [EmailService], providers: [EmailService, MailboxResolverService],
exports: [EmailService], exports: [EmailService, MailboxResolverService],
}) })
export class EmailModule {} export class EmailModule {}
+149 -29
View File
@@ -1,10 +1,11 @@
import { Injectable, Logger } from "@nestjs/common"; import { Injectable, Logger } from "@nestjs/common";
import { firstValueFrom } from "rxjs"; import { firstValueFrom } from "rxjs";
import { MailboxResolverService } from "./mailbox-resolver.service";
import { EmailMessage } from "../../../common/enums/message.enum"; import { EmailMessage } from "../../../common/enums/message.enum";
import { MailServerService } from "../../mail-server/services/mail-server.service"; import { MailServerService } from "../../mail-server/services/mail-server.service";
import { MessageListQueryDto, SearchMessagesQueryDto } from "../DTO/email-query.dto"; import { MessageListQueryDto, SearchMessagesQueryDto } from "../DTO/email-query.dto";
import { SendEmailDto } from "../DTO/send-email.dto"; import { SendEmailDto, UpdateDraftDto } from "../DTO/send-email.dto";
@Injectable() @Injectable()
export class EmailService { export class EmailService {
@@ -12,6 +13,7 @@ export class EmailService {
constructor( constructor(
private readonly mailServerService: MailServerService, private readonly mailServerService: MailServerService,
private readonly mailboxResolverService: MailboxResolverService,
// private readonly userService: UsersService, // private readonly userService: UsersService,
) {} ) {}
@@ -82,13 +84,14 @@ export class EmailService {
} }
//######################################################## //########################################################
async listMessages(userId: string, inboxId: string, query: MessageListQueryDto) { async listMessages(userId: string, query: MessageListQueryDto) {
this.logger.log(`Listing messages in mailbox ${inboxId} for user: ${userId}`);
try { try {
const { results, total: count } = await firstValueFrom(this.mailServerService.messages.listMessages(userId, inboxId, query)); const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(userId);
this.logger.log(`Listing messages in mailbox ${inboxMailboxId} for user: ${userId}`);
this.logger.log(`Found ${count} messages in mailbox ${inboxId} for user: ${userId}`); const { results, total: count } = await firstValueFrom(this.mailServerService.messages.listMessages(userId, inboxMailboxId, query));
this.logger.log(`Found ${count} messages in mailbox ${inboxMailboxId} for user: ${userId}`);
return { return {
message: EmailMessage.MESSAGES_LISTED_SUCCESSFULLY, message: EmailMessage.MESSAGES_LISTED_SUCCESSFULLY,
@@ -97,20 +100,19 @@ export class EmailService {
paginate: true, paginate: true,
}; };
} catch (error) { } catch (error) {
this.logger.error( this.logger.error(`Failed to list messages for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
`Failed to list messages for mailbox ${inboxId} and user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`,
);
throw error; throw error;
} }
} }
//######################################################## //########################################################
async getMessage(userId: string, inboxId: string, messageId: number) { async getMessage(userId: string, messageId: number) {
this.logger.log(`Getting message ${messageId} from inbox ${inboxId} for user: ${userId}`);
try { try {
await this.markMessageAsSeen(userId, inboxId, messageId); const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(userId);
const message = await firstValueFrom(this.mailServerService.messages.getMessage(userId, inboxId, messageId)); this.logger.log(`Getting message ${messageId} from inbox ${inboxMailboxId} for user: ${userId}`);
await this.markMessageAsSeen(userId, messageId);
const message = await firstValueFrom(this.mailServerService.messages.getMessage(userId, inboxMailboxId, messageId));
this.logger.log(`Retrieved message ${messageId} for user: ${userId}`); this.logger.log(`Retrieved message ${messageId} for user: ${userId}`);
@@ -124,9 +126,10 @@ export class EmailService {
} }
//######################################################## //########################################################
async deleteMessage(userId: string, inboxId: string, messageId: number) { async deleteMessage(userId: string, messageId: number) {
try { try {
const result = await firstValueFrom(this.mailServerService.messages.deleteMessage(userId, inboxId, messageId)); const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(userId);
const result = await firstValueFrom(this.mailServerService.messages.deleteMessage(userId, inboxMailboxId, messageId));
return { return {
success: true, success: true,
@@ -140,9 +143,10 @@ export class EmailService {
} }
//============================================== //==============================================
async markMessageAsSeen(userId: string, inboxId: string, messageId: number) { async markMessageAsSeen(userId: string, messageId: number) {
try { try {
const result = await firstValueFrom(this.mailServerService.messages.updateMessage(userId, inboxId, messageId, { seen: true })); const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(userId);
const result = await firstValueFrom(this.mailServerService.messages.updateMessage(userId, inboxMailboxId, messageId, { seen: true }));
return { return {
success: true, success: true,
@@ -158,14 +162,15 @@ export class EmailService {
} }
//============================================== //==============================================
async getSentMessages(userId: string, sentId: string, query: MessageListQueryDto) { async getSentMessages(userId: string, query: MessageListQueryDto) {
try { try {
const result = await firstValueFrom(this.mailServerService.messages.listMessages(userId, sentId, query)); const sentMailboxId = await this.mailboxResolverService.getSentMailboxId(userId);
const { results: sentMails, total: count } = await firstValueFrom(this.mailServerService.messages.listMessages(userId, sentMailboxId, query));
return { return {
success: true, sentMails,
message: EmailMessage.SENT_MESSAGES_RETRIEVED_SUCCESSFULLY, count,
data: result, paginate: true,
}; };
} catch (error) { } catch (error) {
this.logger.error(`Failed to get sent messages for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`); this.logger.error(`Failed to get sent messages for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -174,18 +179,133 @@ export class EmailService {
} }
//============================================== //==============================================
async getTrashMessages(userId: string, trashId: string, query: MessageListQueryDto) { async getTrashMessages(userId: string, query: MessageListQueryDto) {
try { try {
const result = await firstValueFrom(this.mailServerService.messages.listMessages(userId, trashId, query)); const trashMailboxId = await this.mailboxResolverService.getTrashMailboxId(userId);
const { results: trashMails, total: count } = await firstValueFrom(this.mailServerService.messages.listMessages(userId, trashMailboxId, query));
return { return {
success: true, trashMails,
message: EmailMessage.TRASH_MESSAGES_RETRIEVED_SUCCESSFULLY, count,
data: result, paginate: true,
}; };
} catch (error) { } catch (error) {
this.logger.error(`Failed to get trash messages for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`); this.logger.error(`Failed to get trash messages for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error; throw error;
} }
} }
async getDraftsMessages(userId: string, query: MessageListQueryDto) {
try {
const draftsMailboxId = await this.mailboxResolverService.getDraftsMailboxId(userId);
const { results: draftsMail, total: count } = await firstValueFrom(
this.mailServerService.messages.listMessages(userId, draftsMailboxId, query),
);
return {
draftsMail,
count,
paginate: true,
};
} catch (error) {
this.logger.error(`Failed to get drafts messages for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error;
}
}
//==============================================
async updateDraft(userEmailId: string, messageId: number, updateDraftDto: UpdateDraftDto) {
this.logger.log(`Updating draft ${messageId} for user ${userEmailId}`);
try {
const draftsMailboxId = await this.mailboxResolverService.getDraftsMailboxId(userEmailId);
const existingDraft = await firstValueFrom(this.mailServerService.messages.getMessage(userEmailId, draftsMailboxId, messageId));
const draftData: SendEmailDto = {
mailbox: updateDraftDto.mailbox,
from: updateDraftDto.from || { address: existingDraft.from?.address || "" },
to: updateDraftDto.to || [],
cc: updateDraftDto.cc,
bcc: updateDraftDto.bcc,
replyTo: updateDraftDto.replyTo,
headers: updateDraftDto.headers,
subject: updateDraftDto.subject,
text: updateDraftDto.text,
html: updateDraftDto.html,
attachments: updateDraftDto.attachments,
meta: updateDraftDto.meta,
sess: updateDraftDto.sess,
ip: updateDraftDto.ip,
reference: updateDraftDto.reference,
isDraft: true,
uploadOnly: true,
draft: {
mailbox: draftsMailboxId,
id: messageId,
},
};
const response = await firstValueFrom(this.mailServerService.submission.submitMessage(userEmailId, draftData));
this.logger.log(`Draft updated successfully: ${response.id}`);
return {
message: EmailMessage.DRAFT_UPDATED_SUCCESSFULLY,
messageId: response.id,
from: response.from,
to: response.to,
subject: response.subject || "Draft",
updated: response.created,
isDraft: true,
};
} catch (error) {
this.logger.error(`Failed to update draft ${messageId} for user ${userEmailId}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error;
}
}
//==============================================
async sendDraft(userId: string, messageId: number) {
this.logger.log(`Sending draft ${messageId} for user: ${userId}`);
try {
const draftsMailboxId = await this.mailboxResolverService.getDraftsMailboxId(userId);
const result = await firstValueFrom(
this.mailServerService.messages.submitDraft(userId, draftsMailboxId, messageId, {
deleteFiles: false,
}),
);
this.logger.log(`Draft ${messageId} sent successfully`);
return {
message: EmailMessage.DRAFT_SENT_SUCCESSFULLY,
messageId: result.id,
queueId: result.queueId,
result,
};
} catch (error) {
this.logger.error(`Failed to send draft ${messageId} for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error;
}
}
//==============================================
async deleteDraft(userId: string, messageId: number) {
this.logger.log(`Deleting draft ${messageId} for user: ${userId}`);
try {
const draftsMailboxId = await this.mailboxResolverService.getDraftsMailboxId(userId);
const result = await firstValueFrom(this.mailServerService.messages.deleteMessage(userId, draftsMailboxId, messageId));
this.logger.log(`Draft ${messageId} deleted successfully`);
return {
message: EmailMessage.DRAFT_DELETED_SUCCESSFULLY,
result,
};
} catch (error) {
this.logger.error(`Failed to delete draft ${messageId} for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error;
}
}
} }
@@ -0,0 +1,158 @@
import { Injectable, Logger, NotFoundException } from "@nestjs/common";
import { firstValueFrom } from "rxjs";
import { MailboxEnum } from "../../mail-server/enums/mailbox.enum";
import { MailServerService } from "../../mail-server/services/mail-server.service";
export interface UserMailboxIds {
inbox: string;
sent: string;
drafts: string;
trash: string;
junk?: string;
archive?: string;
}
@Injectable()
export class MailboxResolverService {
private readonly logger = new Logger(MailboxResolverService.name);
private readonly mailboxCache = new Map<string, UserMailboxIds>();
private readonly cacheExpiry = new Map<string, number>();
private readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
constructor(private readonly mailServerService: MailServerService) {}
/**
* Get user mailbox IDs with caching
*/
async getUserMailboxIds(userId: string): Promise<UserMailboxIds> {
const cached = this.getCachedMailboxIds(userId);
if (cached) {
return cached;
}
this.logger.log(`Fetching mailbox IDs for user: ${userId}`);
try {
const mailboxes = await firstValueFrom(this.mailServerService.mailboxes.listMailboxes(userId));
const mailboxIds: UserMailboxIds = {
inbox: this.findMailboxId(mailboxes.results, MailboxEnum.INBOX),
sent: this.findMailboxId(mailboxes.results, MailboxEnum.SENT),
drafts: this.findMailboxId(mailboxes.results, MailboxEnum.DRAFTS),
trash: this.findMailboxId(mailboxes.results, MailboxEnum.TRASH),
junk: this.findMailboxId(mailboxes.results, MailboxEnum.Junk, false),
archive: this.findMailboxId(mailboxes.results, MailboxEnum.ARCHIVE, false),
};
this.cacheMailboxIds(userId, mailboxIds);
this.logger.log(`Mailbox IDs cached for user: ${userId}`);
return mailboxIds;
} catch (error) {
this.logger.error(`Failed to fetch mailbox IDs for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error;
}
}
/**
* Get specific mailbox ID
*/
async getMailboxId(userId: string, mailboxType: keyof UserMailboxIds): Promise<string> {
const mailboxIds = await this.getUserMailboxIds(userId);
const mailboxId = mailboxIds[mailboxType];
if (!mailboxId) {
throw new NotFoundException(`${mailboxType} mailbox not found for user ${userId}`);
}
return mailboxId;
}
/**
* Get drafts mailbox ID
*/
async getDraftsMailboxId(userId: string): Promise<string> {
return this.getMailboxId(userId, "drafts");
}
/**
* Get inbox mailbox ID
*/
async getInboxMailboxId(userId: string): Promise<string> {
return this.getMailboxId(userId, "inbox");
}
/**
* Get sent mailbox ID
*/
async getSentMailboxId(userId: string): Promise<string> {
return this.getMailboxId(userId, "sent");
}
/**
* Get trash mailbox ID
*/
async getTrashMailboxId(userId: string): Promise<string> {
return this.getMailboxId(userId, "trash");
}
/**
* 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}`);
}
/**
* Clear all cache
*/
clearAllCache(): void {
this.mailboxCache.clear();
this.cacheExpiry.clear();
this.logger.log("All mailbox cache cleared");
}
/**
* 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);
if (cached && expiry && Date.now() < expiry) {
this.logger.debug(`Using cached mailbox IDs for user: ${userId}`);
return cached;
}
if (cached) {
this.logger.debug(`Cache expired for user: ${userId}`);
this.clearUserCache(userId);
}
return null;
}
/**
* 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);
}
/**
* Find mailbox ID by name
*/
private findMailboxId(mailboxes: any[], mailboxName: string, required: boolean = true): string {
const mailbox = mailboxes.find((mb) => mb.name === mailboxName || mb.path === mailboxName || mb.specialUse === mailboxName);
if (!mailbox && required) {
throw new NotFoundException(`${mailboxName} mailbox not found`);
}
return mailbox?.id || "";
}
}
@@ -0,0 +1,9 @@
export enum MailboxEnum {
INBOX = "INBOX",
DRAFTS = "Drafts",
ARCHIVE = "Archive",
Junk = "Junk",
SENT = "Sent Mail",
TRASH = "Trash",
SPAM = "spam",
}