chore: fix
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 })
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {}
|
||||||
|
|||||||
@@ -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",
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user