chore: changes

This commit is contained in:
mahyargdz
2025-07-09 16:59:26 +03:30
parent 092abd2895
commit 784f168e8c
6 changed files with 120 additions and 31 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ declare module "fastify" {
} }
} }
export const UserDec = createParamDecorator((data: keyof ITokenPayload | undefined, ctx: ExecutionContext) => { export const UserDec = createParamDecorator((data: keyof ITokenPayload, ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest<FastifyRequest>(); const req = ctx.switchToHttp().getRequest<FastifyRequest>();
const user = req.user; const user = req.user;
+8
View File
@@ -186,6 +186,14 @@ export const enum EmailMessage {
SEND_TIME_MUST_BE_DATE = "زمان ارسال باید یک تاریخ معتبر باشد", SEND_TIME_MUST_BE_DATE = "زمان ارسال باید یک تاریخ معتبر باشد",
UPLOAD_ONLY_MUST_BE_BOOLEAN = "حالت فقط آپلود باید بولین باشد", UPLOAD_ONLY_MUST_BE_BOOLEAN = "حالت فقط آپلود باید بولین باشد",
ENVELOPE_MUST_BE_VALID = "envelope باید معتبر باشد", ENVELOPE_MUST_BE_VALID = "envelope باید معتبر باشد",
// Message operations
MESSAGE_MARKED_AS_SEEN_SUCCESSFULLY = "پیام با موفقیت به عنوان خوانده شده علامت گذاری شد",
MESSAGE_MARK_AS_SEEN_FAILED = "علامت گذاری پیام با خطا مواجه شد",
SENT_MESSAGES_RETRIEVED_SUCCESSFULLY = "پیام های ارسالی با موفقیت دریافت شد",
SENT_MESSAGES_RETRIEVAL_FAILED = "دریافت پیام های ارسالی با خطا مواجه شد",
TRASH_MESSAGES_RETRIEVED_SUCCESSFULLY = "پیام های حذف شده با موفقیت دریافت شد",
TRASH_MESSAGES_RETRIEVAL_FAILED = "دریافت پیام های حذف شده با خطا مواجه شد",
} }
export const enum SmsMessage { export const enum SmsMessage {
@@ -18,6 +18,15 @@ export class LocalJwtStrategy extends PassportStrategy(Strategy, LOCAL_JWT_STRAT
} }
async validate(payload: ITokenPayload) { async validate(payload: ITokenPayload) {
return { id: payload.id, role: payload.role }; return {
id: payload.id,
role: payload.role,
inboxId: payload.inboxId,
draftsId: payload.draftsId,
junkId: payload.junkId,
sentId: payload.sentId,
trashId: payload.trashId,
wildduckUserId: payload.wildduckUserId,
};
} }
} }
+2 -2
View File
@@ -1,6 +1,6 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty } from "@nestjs/swagger";
import { Transform } from "class-transformer"; import { Transform } from "class-transformer";
import { IsNotEmpty, IsNumberString, IsString } from "class-validator"; import { IsNotEmpty, IsNumber, IsNumberString, IsString } from "class-validator";
import { EmailMessage } from "../../../common/enums/message.enum"; import { EmailMessage } from "../../../common/enums/message.enum";
@@ -42,6 +42,6 @@ export class UserMailboxParamDto {
export class UserMailboxMessageParamDto { export class UserMailboxMessageParamDto {
@ApiProperty({ description: "Message ID", example: "12345" }) @ApiProperty({ description: "Message ID", example: "12345" })
@Transform(({ value }) => parseInt(value)) @Transform(({ value }) => parseInt(value))
@IsNumberString({ no_symbols: true }, { message: EmailMessage.MESSAGE_ID_MUST_BE_NUMBER }) @IsNumber({}, { message: EmailMessage.MESSAGE_ID_MUST_BE_NUMBER })
messageId: number; messageId: number;
} }
+32 -8
View File
@@ -1,4 +1,4 @@
import { Body, Controller, Delete, Get, Param, 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 { QueueIdParamDto, UserMailboxMessageParamDto } from "./DTO/email-params.dto";
@@ -9,8 +9,8 @@ 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";
@Controller("email")
@AuthGuards() @AuthGuards()
@Controller("email")
export class EmailController { export class EmailController {
constructor(private readonly emailService: EmailService) {} constructor(private readonly emailService: EmailService) {}
@@ -19,8 +19,8 @@ export class EmailController {
@ApiResponse({ status: 201, description: "Email sent successfully" }) @ApiResponse({ status: 201, description: "Email sent successfully" })
@ApiResponse({ status: 400, description: "Invalid email data" }) @ApiResponse({ status: 400, description: "Invalid email data" })
@ApiResponse({ status: 403, description: "Not authorized to send email" }) @ApiResponse({ status: 403, description: "Not authorized to send email" })
sendEmail(@UserDec("id") userId: string, @Body() sendEmailDto: SendEmailDto) { sendEmail(@UserDec("wildduckUserId") userEmailId: string, @Body() sendEmailDto: SendEmailDto) {
return this.emailService.sendEmail(userId, sendEmailDto); return this.emailService.sendEmail(userEmailId, sendEmailDto);
} }
@Get("status/:queueId") @Get("status/:queueId")
@@ -35,7 +35,7 @@ export class EmailController {
@ApiOperation({ summary: "List messages in a inbox" }) @ApiOperation({ summary: "List messages in a inbox" })
@ApiResponse({ status: 200, description: "List of messages" }) @ApiResponse({ status: 200, description: "List of messages" })
@ApiResponse({ status: 404, description: "Mailbox not found" }) @ApiResponse({ status: 404, description: "Mailbox not found" })
listMessages(@UserDec("id") userId: string, @UserDec("inboxId") inboxId: string, @Query() query: MessageListQueryDto) { listMessages(@UserDec("wildduckUserId") userId: string, @UserDec("inboxId") inboxId: string, @Query() query: MessageListQueryDto) {
return this.emailService.listMessages(userId, inboxId, query); return this.emailService.listMessages(userId, inboxId, query);
} }
@@ -43,7 +43,7 @@ export class EmailController {
@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("id") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) { getMessage(@UserDec("wildduckUserId") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) {
return this.emailService.getMessage(userId, inboxId, params.messageId); return this.emailService.getMessage(userId, inboxId, params.messageId);
} }
@@ -51,14 +51,38 @@ export class EmailController {
@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("id") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) { deleteMessage(@UserDec("wildduckUserId") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) {
return this.emailService.deleteMessage(userId, inboxId, params.messageId); return this.emailService.deleteMessage(userId, inboxId, params.messageId);
} }
@Get("search") @Get("search")
@ApiOperation({ summary: "Search messages across all mailboxes" }) @ApiOperation({ summary: "Search messages across all mailboxes" })
@ApiResponse({ status: 200, description: "Search results" }) @ApiResponse({ status: 200, description: "Search results" })
searchMessages(@UserDec("id") userId: string, @Query() query: SearchMessagesQueryDto) { searchMessages(@UserDec("wildduckUserId") userId: string, @Query() query: SearchMessagesQueryDto) {
return this.emailService.searchMessages(userId, query); return this.emailService.searchMessages(userId, query);
} }
@Patch("messages/:messageId/seen")
@ApiOperation({ summary: "Mark a message as seen" })
@ApiResponse({ status: 200, description: "Message marked as seen successfully" })
@ApiResponse({ status: 404, description: "Message not found" })
markMessageAsSeen(@UserDec("wildduckUserId") userId: string, @UserDec("inboxId") inboxId: string, @Param() params: UserMailboxMessageParamDto) {
return this.emailService.markMessageAsSeen(userId, inboxId, Number(params.messageId));
}
@Get("messages/sent")
@ApiOperation({ summary: "List messages in sent mailbox" })
@ApiResponse({ status: 200, description: "List of sent messages" })
@ApiResponse({ status: 404, description: "Sent mailbox not found" })
getSentMessages(@UserDec("wildduckUserId") userId: string, @UserDec("sentId") sentId: string, @Query() query: MessageListQueryDto) {
return this.emailService.getSentMessages(userId, sentId, query);
}
@Get("messages/trash")
@ApiOperation({ summary: "List messages in trash mailbox" })
@ApiResponse({ status: 200, description: "List of trash messages" })
@ApiResponse({ status: 404, description: "Trash mailbox not found" })
getTrashMessages(@UserDec("wildduckUserId") userId: string, @UserDec("trashId") trashId: string, @Query() query: MessageListQueryDto) {
return this.emailService.getTrashMessages(userId, trashId, query);
}
} }
+67 -19
View File
@@ -3,7 +3,6 @@ import { firstValueFrom } from "rxjs";
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 { UsersService } from "../../users/services/users.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 } from "../DTO/send-email.dto";
@@ -13,12 +12,12 @@ export class EmailService {
constructor( constructor(
private readonly mailServerService: MailServerService, private readonly mailServerService: MailServerService,
private readonly userService: UsersService, // private readonly userService: UsersService,
) {} ) {}
//######################################################## //########################################################
async sendEmail(userId: string, sendEmailDto: SendEmailDto) { async sendEmail(userEmailId: string, sendEmailDto: SendEmailDto) {
const userEmailId = await this.userService.getUserEmailIdFromId(userId); // const userEmailId = await this.userService.getUserEmailIdFromId(userId);
this.logger.log(`Sending email from user ${userEmailId} to ${sendEmailDto.to.map((t) => t.address).join(", ")}`); this.logger.log(`Sending email from user ${userEmailId} to ${sendEmailDto.to.map((t) => t.address).join(", ")}`);
@@ -87,13 +86,15 @@ export class EmailService {
this.logger.log(`Listing messages in mailbox ${inboxId} for user: ${userId}`); this.logger.log(`Listing messages in mailbox ${inboxId} for user: ${userId}`);
try { try {
const response = await firstValueFrom(this.mailServerService.messages.listMessages(userId, inboxId, query)); const { results, total: count } = await firstValueFrom(this.mailServerService.messages.listMessages(userId, inboxId, query));
this.logger.log(`Found ${response.results.length} messages in mailbox ${inboxId} for user: ${userId}`); this.logger.log(`Found ${count} messages in mailbox ${inboxId} for user: ${userId}`);
return { return {
message: EmailMessage.MESSAGES_LISTED_SUCCESSFULLY, message: EmailMessage.MESSAGES_LISTED_SUCCESSFULLY,
...response, results,
count,
paginate: true,
}; };
} catch (error) { } catch (error) {
this.logger.error( this.logger.error(
@@ -104,17 +105,17 @@ export class EmailService {
} }
//######################################################## //########################################################
async getMessage(userId: string, mailboxId: string, messageId: number) { async getMessage(userId: string, inboxId: string, messageId: number) {
this.logger.log(`Getting message ${messageId} from mailbox ${mailboxId} for user: ${userId}`); this.logger.log(`Getting message ${messageId} from inbox ${inboxId} for user: ${userId}`);
try { try {
const response = await firstValueFrom(this.mailServerService.messages.getMessage(userId, mailboxId, messageId)); await this.markMessageAsSeen(userId, inboxId, messageId);
const message = await firstValueFrom(this.mailServerService.messages.getMessage(userId, inboxId, messageId));
this.logger.log(`Retrieved message ${messageId} for user: ${userId}`); this.logger.log(`Retrieved message ${messageId} for user: ${userId}`);
return { return {
message: EmailMessage.MESSAGE_RETRIEVED_SUCCESSFULLY, message,
...response,
}; };
} catch (error) { } catch (error) {
this.logger.error(`Failed to get message ${messageId} for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`); this.logger.error(`Failed to get message ${messageId} for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -123,21 +124,68 @@ export class EmailService {
} }
//######################################################## //########################################################
async deleteMessage(userId: string, mailboxId: string, messageId: number) { async deleteMessage(userId: string, inboxId: string, messageId: number) {
this.logger.log(`Deleting message ${messageId} from mailbox ${mailboxId} for user: ${userId}`);
try { try {
const response = await firstValueFrom(this.mailServerService.messages.deleteMessage(userId, mailboxId, messageId)); const result = await firstValueFrom(this.mailServerService.messages.deleteMessage(userId, inboxId, messageId));
this.logger.log(`Deleted message ${messageId} for user: ${userId}`);
return { return {
success: true,
message: EmailMessage.MESSAGE_DELETED_SUCCESSFULLY, message: EmailMessage.MESSAGE_DELETED_SUCCESSFULLY,
...response, result,
}; };
} catch (error) { } catch (error) {
this.logger.error(`Failed to delete message ${messageId} for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`); this.logger.error(`Failed to delete message ${messageId} for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error; throw error;
} }
} }
//==============================================
async markMessageAsSeen(userId: string, inboxId: string, messageId: number) {
try {
const result = await firstValueFrom(this.mailServerService.messages.updateMessage(userId, inboxId, messageId, { seen: true }));
return {
success: true,
message: EmailMessage.MESSAGE_MARKED_AS_SEEN_SUCCESSFULLY,
data: result,
};
} catch (error) {
this.logger.error(
`Failed to mark message ${messageId} as seen for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`,
);
throw error;
}
}
//==============================================
async getSentMessages(userId: string, sentId: string, query: MessageListQueryDto) {
try {
const result = await firstValueFrom(this.mailServerService.messages.listMessages(userId, sentId, query));
return {
success: true,
message: EmailMessage.SENT_MESSAGES_RETRIEVED_SUCCESSFULLY,
data: result,
};
} catch (error) {
this.logger.error(`Failed to get sent messages for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error;
}
}
//==============================================
async getTrashMessages(userId: string, trashId: string, query: MessageListQueryDto) {
try {
const result = await firstValueFrom(this.mailServerService.messages.listMessages(userId, trashId, query));
return {
success: true,
message: EmailMessage.TRASH_MESSAGES_RETRIEVED_SUCCESSFULLY,
data: result,
};
} catch (error) {
this.logger.error(`Failed to get trash messages for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
throw error;
}
}
} }