chore: change all to have mailbox id in query
This commit is contained in:
@@ -147,8 +147,8 @@ export const enum EmailMessage {
|
|||||||
MESSAGE_DELETED_SUCCESSFULLY = "ایمیل با موفقیت حذف شد",
|
MESSAGE_DELETED_SUCCESSFULLY = "ایمیل با موفقیت حذف شد",
|
||||||
USER_ID_REQUIRED = "شناسه کاربری مورد نیاز است",
|
USER_ID_REQUIRED = "شناسه کاربری مورد نیاز است",
|
||||||
USER_ID_MUST_BE_STRING = "شناسه کاربری باید یک رشته باشد",
|
USER_ID_MUST_BE_STRING = "شناسه کاربری باید یک رشته باشد",
|
||||||
MAILBOX_ID_REQUIRED = "شناسه مستندک ایمیل مورد نیاز است",
|
MAILBOX_ID_REQUIRED = "شناسه میل باکس ایمیل مورد نیاز است",
|
||||||
MAILBOX_ID_MUST_BE_STRING = "شناسه مستندک ایمیل باید یک رشته باشد",
|
MAILBOX_ID_MUST_BE_STRING = "شناسه میل باکس ایمیل باید یک رشته باشد",
|
||||||
QUEUE_ID_REQUIRED = "شناسه پیشنویس ایمیل مورد نیاز است",
|
QUEUE_ID_REQUIRED = "شناسه پیشنویس ایمیل مورد نیاز است",
|
||||||
QUEUE_ID_MUST_BE_STRING = "شناسه پیشنویس ایمیل باید یک رشته باشد",
|
QUEUE_ID_MUST_BE_STRING = "شناسه پیشنویس ایمیل باید یک رشته باشد",
|
||||||
MESSAGE_ID_MUST_BE_NUMBER = "شناسه ایمیل باید یک عدد باشد",
|
MESSAGE_ID_MUST_BE_NUMBER = "شناسه ایمیل باید یک عدد باشد",
|
||||||
@@ -223,6 +223,7 @@ export const enum EmailMessage {
|
|||||||
PRIORITY_MUST_BE_ONE_OF_HIGH_NORMAL_LOW = "اولویت باید یکی از مقادیر high, normal, low باشد",
|
PRIORITY_MUST_BE_ONE_OF_HIGH_NORMAL_LOW = "اولویت باید یکی از مقادیر high, normal, low باشد",
|
||||||
SUBJECT_REQUIRED = "موضوع ایمیل مورد نیاز است",
|
SUBJECT_REQUIRED = "موضوع ایمیل مورد نیاز است",
|
||||||
ALL_MESSAGES_DELETED_SUCCESSFULLY = "همه پیام ها با موفقیت حذف شدند",
|
ALL_MESSAGES_DELETED_SUCCESSFULLY = "همه پیام ها با موفقیت حذف شدند",
|
||||||
|
MAILBOX_ID_MUST_BE_MONGO_ID = "شناسه میل باکس ایمیل باید یک آی دی معتبر باشد",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum WebSocketMessage {
|
export const enum WebSocketMessage {
|
||||||
|
|||||||
@@ -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, IsNumber, IsNumberString, IsString } from "class-validator";
|
import { IsMongoId, IsNotEmpty, IsNumber, IsNumberString, IsString } from "class-validator";
|
||||||
|
|
||||||
import { EmailMessage } from "../../../common/enums/message.enum";
|
import { EmailMessage } from "../../../common/enums/message.enum";
|
||||||
|
|
||||||
@@ -11,11 +11,12 @@ export class UserIdParamDto {
|
|||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MailboxIdParamDto {
|
export class MailboxIdQueryDto {
|
||||||
@ApiProperty({ description: "Mailbox ID", example: "507f1f77bcf86cd799439012" })
|
|
||||||
@IsString({ message: EmailMessage.MAILBOX_ID_MUST_BE_STRING })
|
|
||||||
@IsNotEmpty({ message: EmailMessage.MAILBOX_ID_REQUIRED })
|
@IsNotEmpty({ message: EmailMessage.MAILBOX_ID_REQUIRED })
|
||||||
mailboxId: string;
|
@IsString({ message: EmailMessage.MAILBOX_ID_MUST_BE_STRING })
|
||||||
|
@IsMongoId({ message: EmailMessage.MAILBOX_ID_MUST_BE_MONGO_ID })
|
||||||
|
@ApiProperty({ description: "Mailbox ID", example: "507f1f77bcf86cd799439012" })
|
||||||
|
mailbox: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MessageIdParamDto {
|
export class MessageIdParamDto {
|
||||||
@@ -32,7 +33,7 @@ export class QueueIdParamDto {
|
|||||||
queueId: string;
|
queueId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserMailboxParamDto {
|
export class MailboxParamDto {
|
||||||
@ApiProperty({ description: "Mailbox ID", example: "507f1f77bcf86cd799439012" })
|
@ApiProperty({ description: "Mailbox ID", example: "507f1f77bcf86cd799439012" })
|
||||||
@IsString({ message: EmailMessage.MAILBOX_ID_MUST_BE_STRING })
|
@IsString({ message: EmailMessage.MAILBOX_ID_MUST_BE_STRING })
|
||||||
@IsNotEmpty({ message: EmailMessage.MAILBOX_ID_REQUIRED })
|
@IsNotEmpty({ message: EmailMessage.MAILBOX_ID_REQUIRED })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestj
|
|||||||
import { ApiOperation, ApiResponse } from "@nestjs/swagger";
|
import { ApiOperation, ApiResponse } from "@nestjs/swagger";
|
||||||
|
|
||||||
import { BulkActionDto } from "./DTO/bulk-actions.dto";
|
import { BulkActionDto } from "./DTO/bulk-actions.dto";
|
||||||
import { MessageParamDto, QueueIdParamDto } from "./DTO/email-params.dto";
|
import { MailboxIdQueryDto, 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, UpdateDraftDto } from "./DTO/send-email.dto";
|
import { SendEmailDto, UpdateDraftDto } from "./DTO/send-email.dto";
|
||||||
@@ -40,16 +40,16 @@ 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("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
getMessage(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.getMessage(userId, params.messageId);
|
return this.emailService.getMessage(userId, params.messageId, query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete("messages/:messageId")
|
@Delete("messages/: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, @Param() params: MessageParamDto) {
|
deleteMessage(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.deleteMessage(userId, params.messageId);
|
return this.emailService.deleteMessage(userId, params.messageId, query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("search")
|
@Get("search")
|
||||||
@@ -63,8 +63,8 @@ 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, @Param() params: MessageParamDto) {
|
markMessageAsSeen(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.markMessageAsSeen(userId, Number(params.messageId));
|
return this.emailService.markMessageAsSeen(userId, Number(params.messageId), query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("messages/inbox")
|
@Get("messages/inbox")
|
||||||
@@ -156,8 +156,8 @@ export class EmailController {
|
|||||||
@ApiResponse({ status: 200, description: "Message moved to archive successfully" })
|
@ApiResponse({ status: 200, description: "Message moved to archive successfully" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found" })
|
@ApiResponse({ status: 404, description: "Message not found" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
||||||
moveMessageToArchive(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
moveMessageToArchive(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.moveMessageToArchive(userId, Number(params.messageId));
|
return this.emailService.moveMessageToArchive(userId, Number(params.messageId), query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch("messages/:messageId/favorite")
|
@Patch("messages/:messageId/favorite")
|
||||||
@@ -165,8 +165,8 @@ export class EmailController {
|
|||||||
@ApiResponse({ status: 200, description: "Message moved to favorite successfully" })
|
@ApiResponse({ status: 200, description: "Message moved to favorite successfully" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found" })
|
@ApiResponse({ status: 404, description: "Message not found" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
||||||
moveMessageToFavorite(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
moveMessageToFavorite(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.moveMessageToFavorite(userId, Number(params.messageId));
|
return this.emailService.moveMessageToFavorite(userId, Number(params.messageId), query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch("messages/:messageId/trash")
|
@Patch("messages/:messageId/trash")
|
||||||
@@ -174,8 +174,8 @@ export class EmailController {
|
|||||||
@ApiResponse({ status: 200, description: "Message moved to trash successfully" })
|
@ApiResponse({ status: 200, description: "Message moved to trash successfully" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found" })
|
@ApiResponse({ status: 404, description: "Message not found" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
||||||
moveMessageToTrash(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
moveMessageToTrash(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.moveMessageToTrash(userId, Number(params.messageId));
|
return this.emailService.moveMessageToTrash(userId, Number(params.messageId), query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch("messages/:messageId/unarchive")
|
@Patch("messages/:messageId/unarchive")
|
||||||
@@ -183,8 +183,8 @@ export class EmailController {
|
|||||||
@ApiResponse({ status: 200, description: "Message moved from archive to inbox successfully" })
|
@ApiResponse({ status: 200, description: "Message moved from archive to inbox successfully" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found in archive" })
|
@ApiResponse({ status: 404, description: "Message not found in archive" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
||||||
moveMessageToInboxFromArchive(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
moveMessageToInboxFromArchive(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.moveMessageToInboxFromArchive(userId, Number(params.messageId));
|
return this.emailService.moveMessageToInboxFromArchive(userId, Number(params.messageId), query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch("messages/:messageId/unfavorite")
|
@Patch("messages/:messageId/unfavorite")
|
||||||
@@ -192,8 +192,8 @@ export class EmailController {
|
|||||||
@ApiResponse({ status: 200, description: "Message moved from favorite to inbox successfully" })
|
@ApiResponse({ status: 200, description: "Message moved from favorite to inbox successfully" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found in favorite" })
|
@ApiResponse({ status: 404, description: "Message not found in favorite" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
||||||
moveMessageToInboxFromFavorite(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
moveMessageToInboxFromFavorite(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.moveMessageToInboxFromFavorite(userId, Number(params.messageId));
|
return this.emailService.moveMessageToInboxFromFavorite(userId, Number(params.messageId), query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch("messages/:messageId/restore")
|
@Patch("messages/:messageId/restore")
|
||||||
@@ -201,8 +201,8 @@ export class EmailController {
|
|||||||
@ApiResponse({ status: 200, description: "Message restored from trash to inbox successfully" })
|
@ApiResponse({ status: 200, description: "Message restored from trash to inbox successfully" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found in trash" })
|
@ApiResponse({ status: 404, description: "Message not found in trash" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to restore message" })
|
@ApiResponse({ status: 403, description: "Not authorized to restore message" })
|
||||||
moveMessageToInboxFromTrash(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
moveMessageToInboxFromTrash(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.moveMessageToInboxFromTrash(userId, Number(params.messageId));
|
return this.emailService.moveMessageToInboxFromTrash(userId, Number(params.messageId), query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch("messages/:messageId/junk")
|
@Patch("messages/:messageId/junk")
|
||||||
@@ -210,8 +210,8 @@ export class EmailController {
|
|||||||
@ApiResponse({ status: 200, description: "Message moved to junk successfully" })
|
@ApiResponse({ status: 200, description: "Message moved to junk successfully" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found" })
|
@ApiResponse({ status: 404, description: "Message not found" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
||||||
moveMessageToJunk(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
moveMessageToJunk(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.moveMessageToJunk(userId, Number(params.messageId));
|
return this.emailService.moveMessageToJunk(userId, Number(params.messageId), query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch("messages/:messageId/not-junk")
|
@Patch("messages/:messageId/not-junk")
|
||||||
@@ -219,8 +219,8 @@ export class EmailController {
|
|||||||
@ApiResponse({ status: 200, description: "Message moved from junk to inbox successfully" })
|
@ApiResponse({ status: 200, description: "Message moved from junk to inbox successfully" })
|
||||||
@ApiResponse({ status: 404, description: "Message not found in junk" })
|
@ApiResponse({ status: 404, description: "Message not found in junk" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
@ApiResponse({ status: 403, description: "Not authorized to move message" })
|
||||||
moveMessageToInboxFromJunk(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
moveMessageToInboxFromJunk(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.moveMessageToInboxFromJunk(userId, Number(params.messageId));
|
return this.emailService.moveMessageToInboxFromJunk(userId, Number(params.messageId), query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("messages/bulk-action")
|
@Post("messages/bulk-action")
|
||||||
@@ -232,8 +232,8 @@ export class EmailController {
|
|||||||
@ApiResponse({ status: 206, description: "Bulk action partially completed" })
|
@ApiResponse({ status: 206, description: "Bulk action partially completed" })
|
||||||
@ApiResponse({ status: 400, description: "Invalid bulk action data" })
|
@ApiResponse({ status: 400, description: "Invalid bulk action data" })
|
||||||
@ApiResponse({ status: 403, description: "Not authorized to perform bulk action" })
|
@ApiResponse({ status: 403, description: "Not authorized to perform bulk action" })
|
||||||
performBulkAction(@UserDec("wildduckUserId") userId: string, @Body() bulkActionDto: BulkActionDto) {
|
performBulkAction(@UserDec("wildduckUserId") userId: string, @Body() bulkActionDto: BulkActionDto, @Query() query: MailboxIdQueryDto) {
|
||||||
return this.emailService.performBulkAction(userId, bulkActionDto);
|
return this.emailService.performBulkAction(userId, bulkActionDto, query.mailbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Domain Access Management Endpoints
|
// // Domain Access Management Endpoints
|
||||||
|
|||||||
@@ -25,6 +25,13 @@ export const enum EmailHeaderKey {
|
|||||||
Importance = "Importance",
|
Importance = "Importance",
|
||||||
XCampaignID = "X-Campaign-ID",
|
XCampaignID = "X-Campaign-ID",
|
||||||
XMarketingEmail = "X-Marketing-Email",
|
XMarketingEmail = "X-Marketing-Email",
|
||||||
|
// Image-related anti-spam headers
|
||||||
|
XImageSource = "X-Image-Source",
|
||||||
|
XContentSecurity = "X-Content-Security",
|
||||||
|
XImageValidation = "X-Image-Validation",
|
||||||
|
XOriginVerified = "X-Origin-Verified",
|
||||||
|
XImageCount = "X-Image-Count",
|
||||||
|
XTrustedSources = "X-Trusted-Sources",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Priority {
|
export enum Priority {
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ export class EmailGateway implements OnGatewayInit, OnGatewayConnection, OnGatew
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
// Leave user room
|
|
||||||
const userRoom = WEBSOCKET_ROOMS.USER(userInfo.userId);
|
const userRoom = WEBSOCKET_ROOMS.USER(userInfo.userId);
|
||||||
client.leave(userRoom);
|
client.leave(userRoom);
|
||||||
|
|
||||||
@@ -137,12 +136,8 @@ export class EmailGateway implements OnGatewayInit, OnGatewayConnection, OnGatew
|
|||||||
}
|
}
|
||||||
//************************************************************************ */
|
//************************************************************************ */
|
||||||
async notifyEmailSent(userId: string, data: EmailSentPayload) {
|
async notifyEmailSent(userId: string, data: EmailSentPayload) {
|
||||||
return await this.sendUserNotification(
|
const content = `Email ${data.messageId} sent to ${data.to.map((t) => t.address).join(", ")}`;
|
||||||
userId,
|
return await this.sendUserNotification(userId, WEBSOCKET_EVENTS.EMAIL_SENT, data, content);
|
||||||
WEBSOCKET_EVENTS.EMAIL_SENT,
|
|
||||||
data,
|
|
||||||
`Email ${data.messageId} sent to ${data.to.map((t) => t.address).join(", ")}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
//************************************************************************ */
|
//************************************************************************ */
|
||||||
async notifyUnreadCountUpdate(userId: string, data: UnreadCountPayload) {
|
async notifyUnreadCountUpdate(userId: string, data: UnreadCountPayload) {
|
||||||
@@ -193,19 +188,6 @@ export class EmailGateway implements OnGatewayInit, OnGatewayConnection, OnGatew
|
|||||||
return userIds.filter((userId) => this.isUserConnected(userId));
|
return userIds.filter((userId) => this.isUserConnected(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
//************************************************************************ */
|
|
||||||
async getBusinessConnectedUsers(businessId: string): Promise<string[]> {
|
|
||||||
try {
|
|
||||||
// Note: Currently returning all connected users since business filtering
|
|
||||||
// requires additional user data lookup. This can be enhanced later.
|
|
||||||
this.logger.warn(`getBusinessConnectedUsers called for business ${businessId} - returning all connected users for now`);
|
|
||||||
return this.getConnectedUserIds();
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Failed to get business connected users for ${businessId}:`, error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// PRIVATE HELPER METHODS
|
// PRIVATE HELPER METHODS
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -9,9 +9,14 @@ export interface IGenerateAntiThreadingHeadersOptions {
|
|||||||
isTransactional?: boolean;
|
isTransactional?: boolean;
|
||||||
isMarketing?: boolean;
|
isMarketing?: boolean;
|
||||||
priority?: Priority;
|
priority?: Priority;
|
||||||
|
// Image-related options
|
||||||
|
hasHostedImages?: boolean;
|
||||||
|
imageCount?: number;
|
||||||
|
trustedImageSources?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGenerateGmailAntiThreadingHeadersOptions extends Pick<IGenerateAntiThreadingHeadersOptions, "businessDomain"> {
|
export interface IGenerateGmailAntiThreadingHeadersOptions {
|
||||||
|
businessDomain: string;
|
||||||
conversationBreaker?: string;
|
conversationBreaker?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,3 +30,10 @@ export interface IGenerateMarketingHeadersOptions
|
|||||||
campaignId?: string;
|
campaignId?: string;
|
||||||
unsubscribeUrl: string;
|
unsubscribeUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IImageDetectionResult {
|
||||||
|
hasHostedImages: boolean;
|
||||||
|
imageCount: number;
|
||||||
|
trustedImageSources: string[];
|
||||||
|
danakCorpImages: string[];
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export interface UserMailboxIds {
|
||||||
|
inbox: string;
|
||||||
|
sent: string;
|
||||||
|
drafts: string;
|
||||||
|
trash: string;
|
||||||
|
junk: string;
|
||||||
|
archive: string;
|
||||||
|
favorite: string;
|
||||||
|
}
|
||||||
@@ -6,9 +6,7 @@ import { EMAIL_QUEUE_CONSTANTS } from "../constants/email-events.constant";
|
|||||||
import { EmailMonitoringJob, EmailSchedulerJob } from "../interfaces/email-events.interface";
|
import { EmailMonitoringJob, EmailSchedulerJob } from "../interfaces/email-events.interface";
|
||||||
import { EmailMonitoringService } from "../services/email-monitoring.service";
|
import { EmailMonitoringService } from "../services/email-monitoring.service";
|
||||||
|
|
||||||
@Processor(EMAIL_QUEUE_CONSTANTS.EMAIL_QUEUE, {
|
@Processor(EMAIL_QUEUE_CONSTANTS.EMAIL_QUEUE)
|
||||||
concurrency: 5,
|
|
||||||
})
|
|
||||||
export class EmailMonitoringProcessor extends WorkerHost {
|
export class EmailMonitoringProcessor extends WorkerHost {
|
||||||
private readonly logger = new Logger(EmailMonitoringProcessor.name);
|
private readonly logger = new Logger(EmailMonitoringProcessor.name);
|
||||||
|
|
||||||
|
|||||||
@@ -8,27 +8,27 @@ import {
|
|||||||
IGenerateGmailAntiThreadingHeadersOptions,
|
IGenerateGmailAntiThreadingHeadersOptions,
|
||||||
IGenerateMarketingHeadersOptions,
|
IGenerateMarketingHeadersOptions,
|
||||||
IGenerateTransactionalHeadersOptions,
|
IGenerateTransactionalHeadersOptions,
|
||||||
|
IImageDetectionResult,
|
||||||
} from "../interfaces/email-header.interface";
|
} from "../interfaces/email-header.interface";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EmailHeadersService {
|
export class EmailHeadersService {
|
||||||
|
private readonly hostDomain = "danakcorp.com";
|
||||||
|
private readonly hostDomain2 = "storage.danakcorp.com";
|
||||||
//****************************************** */
|
//****************************************** */
|
||||||
generateAntiThreadingHeaders(options: IGenerateAntiThreadingHeadersOptions): EmailHeaderDto[] {
|
generateAntiThreadingHeaders(options: IGenerateAntiThreadingHeadersOptions): EmailHeaderDto[] {
|
||||||
const headers: EmailHeaderDto[] = [];
|
const headers: EmailHeaderDto[] = [];
|
||||||
|
|
||||||
// Add References header with unique value to break threading
|
|
||||||
headers.push({
|
headers.push({
|
||||||
key: EmailHeaderKey.References,
|
key: EmailHeaderKey.References,
|
||||||
value: `<${uuidv4()}@${options.businessDomain}>`,
|
value: `<${uuidv4()}@${options.businessDomain}>`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add unique Thread-Index to prevent Outlook threading
|
|
||||||
headers.push({
|
headers.push({
|
||||||
key: EmailHeaderKey.ThreadIndex,
|
key: EmailHeaderKey.ThreadIndex,
|
||||||
value: this.generateThreadIndex(),
|
value: this.generateThreadIndex(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Anti-spam headers
|
|
||||||
headers.push({
|
headers.push({
|
||||||
key: EmailHeaderKey.XMailer,
|
key: EmailHeaderKey.XMailer,
|
||||||
value: `Danak Mail Service v1.0`,
|
value: `Danak Mail Service v1.0`,
|
||||||
@@ -44,7 +44,6 @@ export class EmailHeadersService {
|
|||||||
value: options.priority || Priority.Normal,
|
value: options.priority || Priority.Normal,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add MIME version
|
|
||||||
headers.push({
|
headers.push({
|
||||||
key: EmailHeaderKey.MIMEVersion,
|
key: EmailHeaderKey.MIMEVersion,
|
||||||
value: "1.0",
|
value: "1.0",
|
||||||
@@ -60,19 +59,20 @@ export class EmailHeadersService {
|
|||||||
value: "0.0",
|
value: "0.0",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add feedback loop headers
|
|
||||||
headers.push({
|
headers.push({
|
||||||
key: EmailHeaderKey.XFeedbackID,
|
key: EmailHeaderKey.XFeedbackID,
|
||||||
value: `${uuidv4()}:${options.businessDomain}`,
|
value: `${uuidv4()}:${options.businessDomain}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add entity reference ID
|
|
||||||
headers.push({
|
headers.push({
|
||||||
key: EmailHeaderKey.XEntityRefID,
|
key: EmailHeaderKey.XEntityRefID,
|
||||||
value: uuidv4(),
|
value: uuidv4(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Content classification headers
|
if (options.hasHostedImages) {
|
||||||
|
headers.push(...this.generateImageAntiSpamHeaders(options));
|
||||||
|
}
|
||||||
|
|
||||||
if (options.isTransactional) {
|
if (options.isTransactional) {
|
||||||
headers.push({
|
headers.push({
|
||||||
key: EmailHeaderKey.XAutoResponseSuppress,
|
key: EmailHeaderKey.XAutoResponseSuppress,
|
||||||
@@ -106,7 +106,6 @@ export class EmailHeadersService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add List-ID if provided
|
|
||||||
if (options.listId) {
|
if (options.listId) {
|
||||||
headers.push({
|
headers.push({
|
||||||
key: EmailHeaderKey.ListId,
|
key: EmailHeaderKey.ListId,
|
||||||
@@ -114,30 +113,107 @@ export class EmailHeadersService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add authentication headers
|
return headers;
|
||||||
// if (options.businessDomain) {
|
}
|
||||||
// headers.push({
|
|
||||||
// key: EmailHeaderKey.AuthenticationResults,
|
|
||||||
// value: `${options.businessDomain}; dkim=pass; spf=pass; dmarc=pass`,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Generate unique Message-ID to prevent threading
|
//****************************************** */
|
||||||
// const messageId = options.messageId || this.generateMessageId(options.businessDomain);
|
generateImageAntiSpamHeaders(options: IGenerateAntiThreadingHeadersOptions): EmailHeaderDto[] {
|
||||||
// headers.push({
|
const headers: EmailHeaderDto[] = [];
|
||||||
// key: EmailHeaderKey.MessageId,
|
|
||||||
// value: messageId,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Add custom headers to help with deliverability
|
if (!options.hasHostedImages) {
|
||||||
// headers.push({
|
return headers;
|
||||||
// key: "X-Originating-IP",
|
}
|
||||||
// value: "[" + this.getServerIP() + "]",
|
|
||||||
// });
|
headers.push({
|
||||||
|
key: EmailHeaderKey.XImageSource,
|
||||||
|
value: this.hostDomain2,
|
||||||
|
});
|
||||||
|
|
||||||
|
headers.push({
|
||||||
|
key: EmailHeaderKey.XContentSecurity,
|
||||||
|
value: "verified",
|
||||||
|
});
|
||||||
|
|
||||||
|
headers.push({
|
||||||
|
key: EmailHeaderKey.XImageValidation,
|
||||||
|
value: "trusted-source",
|
||||||
|
});
|
||||||
|
|
||||||
|
headers.push({
|
||||||
|
key: EmailHeaderKey.XOriginVerified,
|
||||||
|
value: `${this.hostDomain}-${Date.now()}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.imageCount && options.imageCount > 0) {
|
||||||
|
headers.push({
|
||||||
|
key: EmailHeaderKey.XImageCount,
|
||||||
|
value: options.imageCount.toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.trustedImageSources && options.trustedImageSources.length > 0) {
|
||||||
|
headers.push({
|
||||||
|
key: EmailHeaderKey.XTrustedSources,
|
||||||
|
value: options.trustedImageSources.join(", "),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//****************************************** */
|
||||||
|
detectImagesInContent(htmlContent: string): IImageDetectionResult {
|
||||||
|
if (!htmlContent) {
|
||||||
|
return {
|
||||||
|
hasHostedImages: false,
|
||||||
|
imageCount: 0,
|
||||||
|
trustedImageSources: [],
|
||||||
|
danakCorpImages: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regex to find img tags with src attributes
|
||||||
|
const imgRegex = /<img[^>]+src\s*=\s*["']([^"']+)["'][^>]*>/gi;
|
||||||
|
const matches = [...htmlContent.matchAll(imgRegex)];
|
||||||
|
|
||||||
|
const allImages = matches.map((match) => match[1]);
|
||||||
|
const danakCorpImages = allImages.filter((src) => src.includes(this.hostDomain) || src.includes(this.hostDomain2));
|
||||||
|
|
||||||
|
// Also check for CSS background images
|
||||||
|
const cssImageRegex = /background-image\s*:\s*url\(["']?([^"')]+)["']?\)/gi;
|
||||||
|
const cssMatches = [...htmlContent.matchAll(cssImageRegex)];
|
||||||
|
const cssImages = cssMatches.map((match) => match[1]);
|
||||||
|
const danakCorpCssImages = cssImages.filter((src) => src.includes(this.hostDomain) || src.includes(this.hostDomain2));
|
||||||
|
|
||||||
|
const allDanakCorpImages = [...danakCorpImages, ...danakCorpCssImages];
|
||||||
|
const hasHostedImages = allDanakCorpImages.length > 0;
|
||||||
|
|
||||||
|
// Get unique domains from danakcorp images
|
||||||
|
const trustedImageSources = [
|
||||||
|
...new Set(
|
||||||
|
allDanakCorpImages
|
||||||
|
.map((src) => {
|
||||||
|
try {
|
||||||
|
const url = new URL(src.startsWith("//") ? "https:" + src : src);
|
||||||
|
return url.hostname;
|
||||||
|
} catch {
|
||||||
|
// If URL parsing fails, extract domain manually
|
||||||
|
const match = src.match(/(?:https?:\/\/)?(?:www\.)?([^/]+)/);
|
||||||
|
return match ? match[1] : src;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((domain) => domain.includes(this.hostDomain)),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasHostedImages,
|
||||||
|
imageCount: allDanakCorpImages.length,
|
||||||
|
trustedImageSources,
|
||||||
|
danakCorpImages: allDanakCorpImages,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
//****************************************** */
|
//****************************************** */
|
||||||
generateGmailAntiThreadingHeaders(options: IGenerateGmailAntiThreadingHeadersOptions): EmailHeaderDto[] {
|
generateGmailAntiThreadingHeaders(options: IGenerateGmailAntiThreadingHeadersOptions): EmailHeaderDto[] {
|
||||||
const headers: EmailHeaderDto[] = [];
|
const headers: EmailHeaderDto[] = [];
|
||||||
@@ -268,20 +344,7 @@ export class EmailHeadersService {
|
|||||||
}
|
}
|
||||||
//****************************************** */
|
//****************************************** */
|
||||||
|
|
||||||
private getPriorityValue(priority: "high" | "normal" | "low"): string {
|
private getPriorityValue(priority: Priority): string {
|
||||||
switch (priority) {
|
return priority === Priority.High ? "1" : priority === Priority.Low ? "5" : "3";
|
||||||
case "high":
|
|
||||||
return "1";
|
|
||||||
case "low":
|
|
||||||
return "5";
|
|
||||||
default:
|
|
||||||
return "3";
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// private getServerIP(): string {
|
|
||||||
// // In a real implementation, you might want to get the actual server IP
|
|
||||||
// // For now, returning a placeholder
|
|
||||||
// return "127.0.0.1";
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,10 @@ export class EmailService {
|
|||||||
name: user.displayName,
|
name: user.displayName,
|
||||||
};
|
};
|
||||||
|
|
||||||
const headers = this.generateHeadersForEmailType(user);
|
const headers = this.generateHeadersForEmailType(user, {
|
||||||
|
html: sendEmailDto.html,
|
||||||
|
text: sendEmailDto.text,
|
||||||
|
});
|
||||||
|
|
||||||
sendEmailDto.headers = [...headers];
|
sendEmailDto.headers = [...headers];
|
||||||
|
|
||||||
@@ -177,21 +180,21 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
async getMessage(wildduckUserId: string, messageId: number) {
|
async getMessage(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
try {
|
try {
|
||||||
const messageLocation = await this.findMessageMailbox(wildduckUserId, messageId);
|
this.logger.log(`Getting message ${messageId} from ${mailboxId} mailbox for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
if (!messageLocation) throw new BadRequestException(EmailMessage.MESSAGE_NOT_FOUND);
|
await this.markMessageAsSeen(wildduckUserId, messageId, mailboxId);
|
||||||
|
const message = await firstValueFrom(this.mailServerService.messages.getMessage(wildduckUserId, mailboxId, messageId));
|
||||||
|
|
||||||
this.logger.log(`Getting message ${messageId} from ${messageLocation.mailboxName} mailbox for user: ${wildduckUserId}`);
|
this.logger.log(`Retrieved message ${messageId} from ${mailboxId} for user: ${wildduckUserId}`);
|
||||||
|
const mailboxName = await this.mailboxResolverService.getMailboxName(wildduckUserId, mailboxId);
|
||||||
await this.markMessageAsSeen(wildduckUserId, messageId);
|
|
||||||
const message = await firstValueFrom(this.mailServerService.messages.getMessage(wildduckUserId, messageLocation.mailboxId, messageId));
|
|
||||||
|
|
||||||
this.logger.log(`Retrieved message ${messageId} from ${messageLocation.mailboxName} for user: ${wildduckUserId}`);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message,
|
message: {
|
||||||
|
...message,
|
||||||
|
mailboxName,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Failed to get message ${messageId} for user ${wildduckUserId}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
this.logger.error(`Failed to get message ${messageId} for user ${wildduckUserId}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||||
@@ -200,17 +203,13 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//########################################################
|
//########################################################
|
||||||
async deleteMessage(wildduckUserId: string, messageId: number) {
|
async deleteMessage(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
try {
|
try {
|
||||||
const messageLocation = await this.findMessageMailbox(wildduckUserId, messageId);
|
this.logger.log(`Deleting message ${messageId} from ${mailboxId} mailbox for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
if (!messageLocation) throw new BadRequestException(EmailMessage.MESSAGE_NOT_FOUND);
|
const result = await firstValueFrom(this.mailServerService.messages.deleteMessage(wildduckUserId, mailboxId, messageId));
|
||||||
|
|
||||||
this.logger.log(`Deleting message ${messageId} from ${messageLocation.mailboxName} mailbox for user: ${wildduckUserId}`);
|
this.logger.log(`Message ${messageId} deleted successfully from ${mailboxId}`);
|
||||||
|
|
||||||
const result = await firstValueFrom(this.mailServerService.messages.deleteMessage(wildduckUserId, messageLocation.mailboxId, messageId));
|
|
||||||
|
|
||||||
this.logger.log(`Message ${messageId} deleted successfully from ${messageLocation.mailboxName}`);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: EmailMessage.MESSAGE_DELETED_SUCCESSFULLY,
|
message: EmailMessage.MESSAGE_DELETED_SUCCESSFULLY,
|
||||||
@@ -243,24 +242,18 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
async markMessageAsSeen(wildduckUserId: string, messageId: number) {
|
async markMessageAsSeen(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
try {
|
try {
|
||||||
const messageLocation = await this.findMessageMailbox(wildduckUserId, messageId);
|
this.logger.log(`Marking message ${messageId} as seen in ${mailboxId} mailbox for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
if (!messageLocation) throw new BadRequestException(EmailMessage.MESSAGE_NOT_FOUND);
|
const { updated } = await firstValueFrom(this.mailServerService.messages.updateMessage(wildduckUserId, mailboxId, messageId, { seen: true }));
|
||||||
|
|
||||||
this.logger.log(`Marking message ${messageId} as seen in ${messageLocation.mailboxName} mailbox for user: ${wildduckUserId}`);
|
this.logger.log(`Message ${messageId} marked as seen successfully in ${mailboxId}`);
|
||||||
|
|
||||||
const { updated } = await firstValueFrom(
|
|
||||||
this.mailServerService.messages.updateMessage(wildduckUserId, messageLocation.mailboxId, messageId, { seen: true }),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.logger.log(`Message ${messageId} marked as seen successfully in ${messageLocation.mailboxName}`);
|
|
||||||
|
|
||||||
await this.emailNotificationService.notifyEmailRead({
|
await this.emailNotificationService.notifyEmailRead({
|
||||||
userId: wildduckUserId,
|
userId: wildduckUserId,
|
||||||
messageId,
|
messageId,
|
||||||
mailboxId: messageLocation.mailboxId,
|
mailboxId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -468,6 +461,12 @@ export class EmailService {
|
|||||||
if (processedTemplate.hasTemplate) {
|
if (processedTemplate.hasTemplate) {
|
||||||
this.logger.log(`Draft content processed through template for business: ${user.business.id}`);
|
this.logger.log(`Draft content processed through template for business: ${user.business.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const headers = this.generateHeadersForEmailType(user, {
|
||||||
|
html: draftData.html,
|
||||||
|
text: draftData.text,
|
||||||
|
});
|
||||||
|
draftData.headers = [...(draftData.headers || []), ...headers];
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await firstValueFrom(this.mailServerService.submission.submitMessage(wildduckUserId, draftData));
|
const response = await firstValueFrom(this.mailServerService.submission.submitMessage(wildduckUserId, draftData));
|
||||||
@@ -535,25 +534,21 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
async moveMessageToArchive(wildduckUserId: string, messageId: number) {
|
async moveMessageToArchive(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
this.logger.log(`Moving message ${messageId} to archive for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} to archive for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const messageLocation = await this.findMessageMailbox(wildduckUserId, messageId);
|
|
||||||
|
|
||||||
if (!messageLocation) throw new BadRequestException(EmailMessage.MESSAGE_NOT_FOUND);
|
|
||||||
|
|
||||||
const archiveMailboxId = await this.mailboxResolverService.getArchiveMailboxId(wildduckUserId);
|
const archiveMailboxId = await this.mailboxResolverService.getArchiveMailboxId(wildduckUserId);
|
||||||
|
|
||||||
this.logger.log(`Moving message ${messageId} from ${messageLocation.mailboxName} to ${MailboxEnum.ARCHIVE} for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} from ${mailboxId} to ${MailboxEnum.ARCHIVE} for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
const result = await firstValueFrom(
|
||||||
this.mailServerService.messages.updateMessage(wildduckUserId, messageLocation.mailboxId, messageId, {
|
this.mailServerService.messages.updateMessage(wildduckUserId, mailboxId, messageId, {
|
||||||
moveTo: archiveMailboxId,
|
moveTo: archiveMailboxId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.log(`Message ${messageId} moved to archive successfully from ${messageLocation.mailboxName}`);
|
this.logger.log(`Message ${messageId} moved to archive successfully from ${mailboxId}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -569,25 +564,21 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
async moveMessageToFavorite(wildduckUserId: string, messageId: number) {
|
async moveMessageToFavorite(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
this.logger.log(`Moving message ${messageId} to favorite for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} to favorite for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const messageLocation = await this.findMessageMailbox(wildduckUserId, messageId);
|
|
||||||
|
|
||||||
if (!messageLocation) throw new BadRequestException(EmailMessage.MESSAGE_NOT_FOUND);
|
|
||||||
|
|
||||||
const favoriteMailboxId = await this.mailboxResolverService.getFavoriteMailboxId(wildduckUserId);
|
const favoriteMailboxId = await this.mailboxResolverService.getFavoriteMailboxId(wildduckUserId);
|
||||||
|
|
||||||
this.logger.log(`Moving message ${messageId} from ${messageLocation.mailboxName} to ${MailboxEnum.FAVORITE} for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} from ${mailboxId} to ${MailboxEnum.FAVORITE} for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
const result = await firstValueFrom(
|
||||||
this.mailServerService.messages.updateMessage(wildduckUserId, messageLocation.mailboxId, messageId, {
|
this.mailServerService.messages.updateMessage(wildduckUserId, mailboxId, messageId, {
|
||||||
moveTo: favoriteMailboxId,
|
moveTo: favoriteMailboxId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.log(`Message ${messageId} moved to favorite successfully from ${messageLocation.mailboxName}`);
|
this.logger.log(`Message ${messageId} moved to favorite successfully from ${mailboxId}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -603,25 +594,21 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
async moveMessageToTrash(wildduckUserId: string, messageId: number) {
|
async moveMessageToTrash(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
this.logger.log(`Moving message ${messageId} to trash for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} to trash for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const messageLocation = await this.findMessageMailbox(wildduckUserId, messageId);
|
|
||||||
|
|
||||||
if (!messageLocation) throw new BadRequestException(EmailMessage.MESSAGE_NOT_FOUND);
|
|
||||||
|
|
||||||
const trashMailboxId = await this.mailboxResolverService.getTrashMailboxId(wildduckUserId);
|
const trashMailboxId = await this.mailboxResolverService.getTrashMailboxId(wildduckUserId);
|
||||||
|
|
||||||
this.logger.log(`Moving message ${messageId} from ${messageLocation.mailboxName} to ${MailboxEnum.TRASH} for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} from ${mailboxId} to ${MailboxEnum.TRASH} for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
const result = await firstValueFrom(
|
||||||
this.mailServerService.messages.updateMessage(wildduckUserId, messageLocation.mailboxId, messageId, {
|
this.mailServerService.messages.updateMessage(wildduckUserId, mailboxId, messageId, {
|
||||||
moveTo: trashMailboxId,
|
moveTo: trashMailboxId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.log(`Message ${messageId} moved to trash successfully from ${messageLocation.mailboxName}`);
|
this.logger.log(`Message ${messageId} moved to trash successfully from ${mailboxId}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -637,15 +624,14 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
async moveMessageToInboxFromArchive(wildduckUserId: string, messageId: number) {
|
async moveMessageToInboxFromArchive(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
this.logger.log(`Moving message ${messageId} from archive to inbox for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} from archive to inbox for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(wildduckUserId);
|
const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(wildduckUserId);
|
||||||
const archiveMailboxId = await this.mailboxResolverService.getArchiveMailboxId(wildduckUserId);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
const result = await firstValueFrom(
|
||||||
this.mailServerService.messages.updateMessage(wildduckUserId, archiveMailboxId, messageId, {
|
this.mailServerService.messages.updateMessage(wildduckUserId, mailboxId, messageId, {
|
||||||
moveTo: inboxMailboxId,
|
moveTo: inboxMailboxId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -666,15 +652,14 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
async moveMessageToInboxFromFavorite(wildduckUserId: string, messageId: number) {
|
async moveMessageToInboxFromFavorite(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
this.logger.log(`Moving message ${messageId} from favorite to inbox for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} from favorite to inbox for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(wildduckUserId);
|
const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(wildduckUserId);
|
||||||
const favoriteMailboxId = await this.mailboxResolverService.getFavoriteMailboxId(wildduckUserId);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
const result = await firstValueFrom(
|
||||||
this.mailServerService.messages.updateMessage(wildduckUserId, favoriteMailboxId, messageId, {
|
this.mailServerService.messages.updateMessage(wildduckUserId, mailboxId, messageId, {
|
||||||
moveTo: inboxMailboxId,
|
moveTo: inboxMailboxId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -695,15 +680,14 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
async moveMessageToInboxFromTrash(wildduckUserId: string, messageId: number) {
|
async moveMessageToInboxFromTrash(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
this.logger.log(`Moving message ${messageId} from trash to inbox for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} from trash to inbox for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(wildduckUserId);
|
const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(wildduckUserId);
|
||||||
const trashMailboxId = await this.mailboxResolverService.getTrashMailboxId(wildduckUserId);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
const result = await firstValueFrom(
|
||||||
this.mailServerService.messages.updateMessage(wildduckUserId, trashMailboxId, messageId, {
|
this.mailServerService.messages.updateMessage(wildduckUserId, mailboxId, messageId, {
|
||||||
moveTo: inboxMailboxId,
|
moveTo: inboxMailboxId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -724,25 +708,21 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
async moveMessageToJunk(wildduckUserId: string, messageId: number) {
|
async moveMessageToJunk(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
this.logger.log(`Moving message ${messageId} to junk for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} to junk for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const messageLocation = await this.findMessageMailbox(wildduckUserId, messageId);
|
|
||||||
|
|
||||||
if (!messageLocation) throw new BadRequestException(EmailMessage.MESSAGE_NOT_FOUND);
|
|
||||||
|
|
||||||
const junkMailboxId = await this.mailboxResolverService.getJunkMailboxId(wildduckUserId);
|
const junkMailboxId = await this.mailboxResolverService.getJunkMailboxId(wildduckUserId);
|
||||||
|
|
||||||
this.logger.log(`Moving message ${messageId} from ${messageLocation.mailboxName} to ${MailboxEnum.Junk} for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} from ${mailboxId} to ${MailboxEnum.Junk} for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
const result = await firstValueFrom(
|
||||||
this.mailServerService.messages.updateMessage(wildduckUserId, messageLocation.mailboxId, messageId, {
|
this.mailServerService.messages.updateMessage(wildduckUserId, mailboxId, messageId, {
|
||||||
moveTo: junkMailboxId,
|
moveTo: junkMailboxId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.log(`Message ${messageId} moved to junk successfully from ${messageLocation.mailboxName}`);
|
this.logger.log(`Message ${messageId} moved to junk successfully from ${mailboxId}`);
|
||||||
|
|
||||||
const domainTag = await this.getUserBusinessId(wildduckUserId);
|
const domainTag = await this.getUserBusinessId(wildduckUserId);
|
||||||
|
|
||||||
@@ -769,15 +749,14 @@ export class EmailService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
async moveMessageToInboxFromJunk(wildduckUserId: string, messageId: number) {
|
async moveMessageToInboxFromJunk(wildduckUserId: string, messageId: number, mailboxId: string) {
|
||||||
this.logger.log(`Moving message ${messageId} from junk to inbox for user: ${wildduckUserId}`);
|
this.logger.log(`Moving message ${messageId} from junk to inbox for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(wildduckUserId);
|
const inboxMailboxId = await this.mailboxResolverService.getInboxMailboxId(wildduckUserId);
|
||||||
const junkMailboxId = await this.mailboxResolverService.getJunkMailboxId(wildduckUserId);
|
|
||||||
|
|
||||||
const result = await firstValueFrom(
|
const result = await firstValueFrom(
|
||||||
this.mailServerService.messages.updateMessage(wildduckUserId, junkMailboxId, messageId, {
|
this.mailServerService.messages.updateMessage(wildduckUserId, mailboxId, messageId, {
|
||||||
moveTo: inboxMailboxId,
|
moveTo: inboxMailboxId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -811,7 +790,7 @@ export class EmailService {
|
|||||||
//==============================================
|
//==============================================
|
||||||
// BULK ACTIONS
|
// BULK ACTIONS
|
||||||
//==============================================
|
//==============================================
|
||||||
async performBulkAction(wildduckUserId: string, bulkActionDto: BulkActionDto) {
|
async performBulkAction(wildduckUserId: string, bulkActionDto: BulkActionDto, mailboxId: string) {
|
||||||
this.logger.log(`Performing bulk action ${bulkActionDto.action} on ${bulkActionDto.messageIds.length} messages for user: ${wildduckUserId}`);
|
this.logger.log(`Performing bulk action ${bulkActionDto.action} on ${bulkActionDto.messageIds.length} messages for user: ${wildduckUserId}`);
|
||||||
|
|
||||||
const results = {
|
const results = {
|
||||||
@@ -824,34 +803,34 @@ export class EmailService {
|
|||||||
try {
|
try {
|
||||||
switch (bulkActionDto.action) {
|
switch (bulkActionDto.action) {
|
||||||
case BulkActionType.SEEN:
|
case BulkActionType.SEEN:
|
||||||
await this.markMessageAsSeen(wildduckUserId, messageId);
|
await this.markMessageAsSeen(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
case BulkActionType.DELETE:
|
case BulkActionType.DELETE:
|
||||||
await this.deleteMessage(wildduckUserId, messageId);
|
await this.deleteMessage(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
case BulkActionType.ARCHIVE:
|
case BulkActionType.ARCHIVE:
|
||||||
await this.moveMessageToArchive(wildduckUserId, messageId);
|
await this.moveMessageToArchive(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
case BulkActionType.UNARCHIVE:
|
case BulkActionType.UNARCHIVE:
|
||||||
await this.moveMessageToInboxFromArchive(wildduckUserId, messageId);
|
await this.moveMessageToInboxFromArchive(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
case BulkActionType.FAVORITE:
|
case BulkActionType.FAVORITE:
|
||||||
await this.moveMessageToFavorite(wildduckUserId, messageId);
|
await this.moveMessageToFavorite(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
case BulkActionType.UNFAVORITE:
|
case BulkActionType.UNFAVORITE:
|
||||||
await this.moveMessageToInboxFromFavorite(wildduckUserId, messageId);
|
await this.moveMessageToInboxFromFavorite(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
case BulkActionType.JUNK:
|
case BulkActionType.JUNK:
|
||||||
await this.moveMessageToJunk(wildduckUserId, messageId);
|
await this.moveMessageToJunk(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
case BulkActionType.NOTJUNK:
|
case BulkActionType.NOTJUNK:
|
||||||
await this.moveMessageToInboxFromJunk(wildduckUserId, messageId);
|
await this.moveMessageToInboxFromJunk(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
case BulkActionType.TRASH:
|
case BulkActionType.TRASH:
|
||||||
await this.moveMessageToTrash(wildduckUserId, messageId);
|
await this.moveMessageToTrash(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
case BulkActionType.RESTORE:
|
case BulkActionType.RESTORE:
|
||||||
await this.moveMessageToInboxFromTrash(wildduckUserId, messageId);
|
await this.moveMessageToInboxFromTrash(wildduckUserId, messageId, mailboxId);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new BadRequestException(`Unknown action: ${bulkActionDto.action}`);
|
throw new BadRequestException(`Unknown action: ${bulkActionDto.action}`);
|
||||||
@@ -920,42 +899,6 @@ export class EmailService {
|
|||||||
return transformedQuery;
|
return transformedQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
//########################################################
|
|
||||||
|
|
||||||
private async findMessageMailbox(userId: string, messageId: number): Promise<{ mailboxId: string; mailboxName: string } | null> {
|
|
||||||
try {
|
|
||||||
const mailboxIds = await this.mailboxResolverService.getUserMailboxIds(userId);
|
|
||||||
|
|
||||||
const mailboxesToSearch = [
|
|
||||||
{ id: mailboxIds.inbox, name: MailboxEnum.INBOX },
|
|
||||||
{ id: mailboxIds.sent, name: MailboxEnum.SENT },
|
|
||||||
{ id: mailboxIds.drafts, name: MailboxEnum.DRAFTS },
|
|
||||||
{ id: mailboxIds.trash, name: MailboxEnum.TRASH },
|
|
||||||
{ id: mailboxIds.archive, name: MailboxEnum.ARCHIVE },
|
|
||||||
{ id: mailboxIds.favorite, name: MailboxEnum.FAVORITE },
|
|
||||||
{ id: mailboxIds.junk, name: MailboxEnum.Junk },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const mailbox of mailboxesToSearch) {
|
|
||||||
if (!mailbox.id) continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await firstValueFrom(this.mailServerService.messages.getMessage(userId, mailbox.id, messageId));
|
|
||||||
this.logger.log(`Message ${messageId} found in ${mailbox.name} mailbox`);
|
|
||||||
return { mailboxId: mailbox.id, mailboxName: mailbox.name };
|
|
||||||
} catch (_error) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.warn(`Message ${messageId} not found in any mailbox for user ${userId}`);
|
|
||||||
return null;
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Failed to find message ${messageId} for user ${userId}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
private async getUserBusinessId(wildduckUserId: string) {
|
private async getUserBusinessId(wildduckUserId: string) {
|
||||||
const user = await this.userRepository.findOne({ wildduckUserId, deletedAt: null }, { populate: ["business"] });
|
const user = await this.userRepository.findOne({ wildduckUserId, deletedAt: null }, { populate: ["business"] });
|
||||||
@@ -964,68 +907,21 @@ export class EmailService {
|
|||||||
|
|
||||||
//==============================================
|
//==============================================
|
||||||
|
|
||||||
//########################################################
|
private generateHeadersForEmailType(user: User, emailContent?: { html?: string; text?: string }) {
|
||||||
private generateHeadersForEmailType(user: User) {
|
|
||||||
// const emailType = sendEmailDto.emailType || EmailType.GENERAL;
|
|
||||||
const businessName = user.business.name;
|
const businessName = user.business.name;
|
||||||
const businessDomain = user.emailAddress.split("@")[1];
|
const businessDomain = user.emailAddress.split("@")[1];
|
||||||
|
|
||||||
|
const htmlContent = emailContent?.html || "";
|
||||||
|
const imageDetection = this.emailHeadersService.detectImagesInContent(htmlContent);
|
||||||
|
|
||||||
return this.emailHeadersService.generateAntiThreadingHeaders({
|
return this.emailHeadersService.generateAntiThreadingHeaders({
|
||||||
businessName,
|
businessName,
|
||||||
businessDomain,
|
businessDomain,
|
||||||
isTransactional: true,
|
isTransactional: true,
|
||||||
priority: Priority.Normal,
|
priority: Priority.Normal,
|
||||||
|
hasHostedImages: imageDetection.hasHostedImages,
|
||||||
|
imageCount: imageDetection.imageCount,
|
||||||
|
trustedImageSources: imageDetection.trustedImageSources,
|
||||||
});
|
});
|
||||||
|
|
||||||
// switch (emailType) {
|
|
||||||
// case EmailType.TRANSACTIONAL:
|
|
||||||
// return this.emailHeadersService.generateTransactionalHeaders({
|
|
||||||
// businessName,
|
|
||||||
// businessDomain,
|
|
||||||
// transactionId: sendEmailDto.transactionId,
|
|
||||||
// category: sendEmailDto.category,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// case EmailType.MARKETING:
|
|
||||||
// if (!sendEmailDto.listId || !sendEmailDto.unsubscribeUrl) {
|
|
||||||
// throw new BadRequestException("Marketing emails require listId and unsubscribeUrl");
|
|
||||||
// }
|
|
||||||
// return this.emailHeadersService.generateMarketingHeaders({
|
|
||||||
// businessName,
|
|
||||||
// businessDomain,
|
|
||||||
// listId: sendEmailDto.listId,
|
|
||||||
// unsubscribeUrl: sendEmailDto.unsubscribeUrl,
|
|
||||||
// campaignId: sendEmailDto.campaignId,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// case EmailType.ANTI_THREADING: {
|
|
||||||
// const antiThreadingHeaders = this.emailHeadersService.generateAntiThreadingHeaders({
|
|
||||||
// businessName,
|
|
||||||
// businessDomain,
|
|
||||||
// isTransactional: true,
|
|
||||||
// priority: sendEmailDto.priority as Priority,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Add Gmail-specific headers if requested
|
|
||||||
// if (sendEmailDto.forceGmailBreak) {
|
|
||||||
// const gmailHeaders = this.emailHeadersService.generateGmailAntiThreadingHeaders({
|
|
||||||
// businessDomain,
|
|
||||||
// conversationBreaker: sendEmailDto.conversationBreaker,
|
|
||||||
// });
|
|
||||||
// antiThreadingHeaders.push(...gmailHeaders);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return antiThreadingHeaders;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// case EmailType.GENERAL:
|
|
||||||
// default:
|
|
||||||
// return this.emailHeadersService.generateAntiThreadingHeaders({
|
|
||||||
// businessName,
|
|
||||||
// businessDomain,
|
|
||||||
// isTransactional: true,
|
|
||||||
// priority: Priority.Normal,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,7 @@ import { firstValueFrom } from "rxjs";
|
|||||||
import { MailboxInfo } from "../../mail-server/interfaces/mailboxes-response.interface";
|
import { MailboxInfo } from "../../mail-server/interfaces/mailboxes-response.interface";
|
||||||
import { MailServerService } from "../../mail-server/services/mail-server.service";
|
import { MailServerService } from "../../mail-server/services/mail-server.service";
|
||||||
import { MailboxEnum } from "../../mailbox/enums/mailbox.enum";
|
import { MailboxEnum } from "../../mailbox/enums/mailbox.enum";
|
||||||
|
import { UserMailboxIds } from "../interfaces/user-mailbox.interface";
|
||||||
export interface UserMailboxIds {
|
|
||||||
inbox: string;
|
|
||||||
sent: string;
|
|
||||||
drafts: string;
|
|
||||||
trash: string;
|
|
||||||
junk: string;
|
|
||||||
archive: string;
|
|
||||||
favorite: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MailboxResolverService {
|
export class MailboxResolverService {
|
||||||
@@ -70,6 +61,15 @@ export class MailboxResolverService {
|
|||||||
return mailboxId;
|
return mailboxId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get mailbox name by ID
|
||||||
|
*/
|
||||||
|
async getMailboxName(wildduckUserId: string, mailboxId: string): Promise<string> {
|
||||||
|
const mailboxes = await firstValueFrom(this.mailServerService.mailboxes.listMailboxes(wildduckUserId));
|
||||||
|
const mailbox = mailboxes.results.find((mb) => mb.id === mailboxId);
|
||||||
|
return mailbox?.name || "";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get drafts mailbox ID
|
* Get drafts mailbox ID
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { BusinessInterceptor } from "../../core/interceptors/business.intercepto
|
|||||||
|
|
||||||
@Controller("users")
|
@Controller("users")
|
||||||
@ApiHeader({ name: "x-business-id", description: "Business ID" })
|
@ApiHeader({ name: "x-business-id", description: "Business ID" })
|
||||||
@UseInterceptors(BusinessInterceptor)
|
|
||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
export class UsersController {
|
export class UsersController {
|
||||||
constructor(private readonly usersService: UsersService) {}
|
constructor(private readonly usersService: UsersService) {}
|
||||||
@@ -26,6 +25,7 @@ export class UsersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
@UseInterceptors(BusinessInterceptor)
|
||||||
@ApiOperation({ summary: "Create a new email user" })
|
@ApiOperation({ summary: "Create a new email user" })
|
||||||
@ApiResponse({ status: 201, description: "Email user created successfully" })
|
@ApiResponse({ status: 201, description: "Email user created successfully" })
|
||||||
createEmailUser(@Body() createEmailUserDto: CreateEmailUserDto, @BusinessDec("id") businessId: string) {
|
createEmailUser(@Body() createEmailUserDto: CreateEmailUserDto, @BusinessDec("id") businessId: string) {
|
||||||
@@ -33,6 +33,7 @@ export class UsersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
|
@UseInterceptors(BusinessInterceptor)
|
||||||
@ApiOperation({ summary: "Get email users for business" })
|
@ApiOperation({ summary: "Get email users for business" })
|
||||||
@ApiResponse({ status: 200, description: "Email users retrieved successfully" })
|
@ApiResponse({ status: 200, description: "Email users retrieved successfully" })
|
||||||
getEmailUsers(@BusinessDec("id") businessId: string, @Query() queryDto: UserListQueryDto) {
|
getEmailUsers(@BusinessDec("id") businessId: string, @Query() queryDto: UserListQueryDto) {
|
||||||
@@ -40,6 +41,7 @@ export class UsersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(":id")
|
@Delete(":id")
|
||||||
|
@UseInterceptors(BusinessInterceptor)
|
||||||
@ApiOperation({ summary: "Delete email user" })
|
@ApiOperation({ summary: "Delete email user" })
|
||||||
@ApiResponse({ status: 200, description: "Email user deleted successfully" })
|
@ApiResponse({ status: 200, description: "Email user deleted successfully" })
|
||||||
deleteEmailUser(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
deleteEmailUser(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||||
@@ -47,6 +49,7 @@ export class UsersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(":id")
|
@Patch(":id")
|
||||||
|
@UseInterceptors(BusinessInterceptor)
|
||||||
@ApiOperation({ summary: "Update email user" })
|
@ApiOperation({ summary: "Update email user" })
|
||||||
@ApiResponse({ status: 200, description: "Email user updated successfully" })
|
@ApiResponse({ status: 200, description: "Email user updated successfully" })
|
||||||
updateEmailUser(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string, @Body() body: UpdateEmailUserDto) {
|
updateEmailUser(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string, @Body() body: UpdateEmailUserDto) {
|
||||||
@@ -54,6 +57,7 @@ export class UsersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(":id/quota")
|
@Patch(":id/quota")
|
||||||
|
@UseInterceptors(BusinessInterceptor)
|
||||||
@ApiOperation({ summary: "Update email user quota" })
|
@ApiOperation({ summary: "Update email user quota" })
|
||||||
@ApiResponse({ status: 200, description: "Email user quota updated successfully" })
|
@ApiResponse({ status: 200, description: "Email user quota updated successfully" })
|
||||||
updateEmailUserQuota(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string, @Body() body: { quota: number }) {
|
updateEmailUserQuota(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string, @Body() body: { quota: number }) {
|
||||||
@@ -61,6 +65,7 @@ export class UsersController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(":id/toggle-status")
|
@Patch(":id/toggle-status")
|
||||||
|
@UseInterceptors(BusinessInterceptor)
|
||||||
@ApiOperation({ summary: "Update email user status" })
|
@ApiOperation({ summary: "Update email user status" })
|
||||||
@ApiResponse({ status: 200, description: "Email user status updated successfully" })
|
@ApiResponse({ status: 200, description: "Email user status updated successfully" })
|
||||||
updateEmailUserStatus(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
updateEmailUserStatus(@Param() paramDto: ParamDto, @BusinessDec("id") businessId: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user