fix: bug in the template

This commit is contained in:
mahyargdz
2025-08-04 16:40:49 +03:30
parent f8fcb7512c
commit 7e65f049fc
2 changed files with 43 additions and 141 deletions
+3 -88
View File
@@ -1,5 +1,5 @@
import { ApiProperty, ApiPropertyOptional, PartialType } from "@nestjs/swagger"; import { ApiProperty, ApiPropertyOptional, PartialType } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Exclude, Type } from "class-transformer";
import { import {
IsArray, IsArray,
IsBoolean, IsBoolean,
@@ -120,10 +120,7 @@ export class EmailEnvelopeDto {
//######################################################## //########################################################
export class SendEmailDto { export class SendEmailDto {
// @IsNotEmpty({ message: EmailMessage.FROM_ADDRESS_REQUIRED }) @Exclude()
// @ValidateNested()
// @Type(() => EmailRecipientDto)
// @ApiProperty({ description: "Address for the From: header" })
from: EmailRecipientDto; from: EmailRecipientDto;
@IsOptional() @IsOptional()
@@ -153,17 +150,9 @@ export class SendEmailDto {
@ApiPropertyOptional({ description: "Addresses for the Bcc: header (optional)", type: [EmailRecipientDto] }) @ApiPropertyOptional({ description: "Addresses for the Bcc: header (optional)", type: [EmailRecipientDto] })
bcc?: EmailRecipientDto[]; bcc?: EmailRecipientDto[];
// @IsOptional() @Exclude()
// @IsArray({ message: EmailMessage.HEADERS_MUST_BE_ARRAY })
// @ValidateNested({ each: true })
// @Type(() => EmailHeaderDto)
// @ApiPropertyOptional({
// description: "Custom headers for the message. If reference message is set then In-Reply-To and References headers are set automatically",
// type: [EmailHeaderDto],
// })
headers?: EmailHeaderDto[]; headers?: EmailHeaderDto[];
// @IsOptional()
@IsNotEmpty({ message: EmailMessage.SUBJECT_REQUIRED }) @IsNotEmpty({ message: EmailMessage.SUBJECT_REQUIRED })
@IsString({ message: EmailMessage.SUBJECT_MUST_BE_STRING }) @IsString({ message: EmailMessage.SUBJECT_MUST_BE_STRING })
@ApiPropertyOptional({ description: "Message subject. If not then resolved from Reference message (optional)", example: "Test email" }) @ApiPropertyOptional({ description: "Message subject. If not then resolved from Reference message (optional)", example: "Test email" })
@@ -188,24 +177,6 @@ export class SendEmailDto {
@ApiPropertyOptional({ description: "Attachments for the message (optional)", type: [EmailAttachmentDto] }) @ApiPropertyOptional({ description: "Attachments for the message (optional)", type: [EmailAttachmentDto] })
attachments?: EmailAttachmentDto[]; attachments?: EmailAttachmentDto[];
@IsOptional()
@ApiPropertyOptional({ description: "Optional metadata, must be an object or JSON formatted string (optional)" })
meta?: Record<string, unknown>;
@IsOptional()
@IsString({ message: EmailMessage.SESSION_ID_MUST_BE_STRING })
@ApiPropertyOptional({ description: "Session identifier for the logs (optional)" })
sess?: string;
@IsOptional()
@IsString({ message: EmailMessage.IP_ADDRESS_MUST_BE_STRING })
@ApiPropertyOptional({ description: "IP address for the logs (optional)" })
ip?: string;
// @IsOptional()
// @ApiPropertyOptional({ description: "Reference message data (optional)" })
// reference?: any;
@IsOptional() @IsOptional()
@IsBoolean({ message: EmailMessage.IS_DRAFT_MUST_BE_BOOLEAN }) @IsBoolean({ message: EmailMessage.IS_DRAFT_MUST_BE_BOOLEAN })
@ApiPropertyOptional({ description: "Is the message a draft or not (optional)", default: false }) @ApiPropertyOptional({ description: "Is the message a draft or not (optional)", default: false })
@@ -237,62 +208,6 @@ export class SendEmailDto {
@Type(() => EmailEnvelopeDto) @Type(() => EmailEnvelopeDto)
@ApiPropertyOptional({ description: "Optional envelope (optional)" }) @ApiPropertyOptional({ description: "Optional envelope (optional)" })
envelope?: EmailEnvelopeDto; envelope?: EmailEnvelopeDto;
// @IsOptional()
// @IsEnum(EmailType, { message: "Email type must be one of: general, transactional, marketing, anti-threading" })
// @ApiPropertyOptional({
// description: "Email type for appropriate header configuration",
// enum: EmailType,
// default: EmailType.GENERAL,
// example: EmailType.TRANSACTIONAL,
// })
// emailType?: EmailType;
// @IsOptional()
// @IsString({ message: EmailMessage.HEADER_VALUE_MUST_BE_STRING })
// @ApiPropertyOptional({ description: "Transaction ID for transactional emails (optional)", example: "ORDER-123456" })
// transactionId?: string;
// @IsOptional()
// @IsString({ message: EmailMessage.HEADER_VALUE_MUST_BE_STRING })
// @ApiPropertyOptional({ description: "Category for transactional emails (optional)", example: "order-confirmation" })
// category?: string;
// @IsOptional()
// @IsString({ message: EmailMessage.HEADER_VALUE_MUST_BE_STRING })
// @ApiPropertyOptional({ description: "List ID for marketing emails (optional)", example: "newsletter-subscribers" })
// listId?: string;
// @IsOptional()
// @IsString({ message: EmailMessage.HEADER_VALUE_MUST_BE_STRING })
// @ApiPropertyOptional({ description: "Unsubscribe URL for marketing emails (optional)", example: "https://yourdomain.com/unsubscribe?token=xyz" })
// unsubscribeUrl?: string;
// @IsOptional()
// @IsString({ message: EmailMessage.HEADER_VALUE_MUST_BE_STRING })
// @ApiPropertyOptional({ description: "Campaign ID for marketing emails (optional)", example: "CAMPAIGN-2024-001" })
// campaignId?: string;
// @IsOptional()
// @IsEnum(Priority, { message: EmailMessage.PRIORITY_MUST_BE_ONE_OF_HIGH_NORMAL_LOW })
// @ApiPropertyOptional({ description: "Email priority (optional)", enum: Priority, default: Priority.Normal })
// priority?: Priority;
// @IsOptional()
// @IsBoolean({ message: EmailMessage.IS_DRAFT_MUST_BE_BOOLEAN })
// @ApiPropertyOptional({ description: "Force Gmail thread break for anti-threading emails (optional)", default: false })
// forceGmailBreak?: boolean;
// @IsOptional()
// @IsString({ message: EmailMessage.HEADER_VALUE_MUST_BE_STRING })
// @ApiPropertyOptional({ description: "Conversation breaker for anti-threading emails (optional)" })
// conversationBreaker?: string;
// @IsOptional()
// @IsNotEmpty()
// @IsString({ message: EmailMessage.MAILBOX_MUST_BE_STRING })
// @ApiPropertyOptional({ description: "ID of the Mailbox (optional)", example: "507f1f77bcf86cd799439012" })
// mailbox?: string;
} }
//######################################################## //########################################################
+40 -53
View File
@@ -10,7 +10,6 @@ import { EmailMessage } from "../../../common/enums/message.enum";
import { EmailNotificationService } from "../../email-utils/services/email-notification.service"; import { EmailNotificationService } from "../../email-utils/services/email-notification.service";
import { MailboxResolverService } from "../../email-utils/services/mailbox-resolver.service"; import { MailboxResolverService } from "../../email-utils/services/mailbox-resolver.service";
import { SearchMessagesMailServerQueryDto } from "../../mail-server/DTO/message.dto"; import { SearchMessagesMailServerQueryDto } from "../../mail-server/DTO/message.dto";
import { MessageHeaders } from "../../mail-server/interfaces/messages-response.interface";
import { MailServerService } from "../../mail-server/services/mail-server.service"; import { MailServerService } from "../../mail-server/services/mail-server.service";
import { TemplateProcessorService } from "../../templates/services/template-processor.service"; import { TemplateProcessorService } from "../../templates/services/template-processor.service";
import { User } from "../../users/entities/user.entity"; import { User } from "../../users/entities/user.entity";
@@ -18,7 +17,7 @@ import { UserRepository } from "../../users/repositories/user.repository";
import { EMAIL_QUEUE_CONSTANTS } from "../constants/email-events.constant"; import { EMAIL_QUEUE_CONSTANTS } from "../constants/email-events.constant";
import { BulkActionDto, BulkActionType } from "../DTO/bulk-actions.dto"; import { BulkActionDto, BulkActionType } from "../DTO/bulk-actions.dto";
import { MessageListQueryDto, SearchMessagesQueryDto } from "../DTO/email-query.dto"; import { MessageListQueryDto, SearchMessagesQueryDto } from "../DTO/email-query.dto";
import { EmailRecipientDto, SendEmailDto, UpdateDraftDto } from "../DTO/send-email.dto"; import { EmailAttachmentDto, EmailRecipientDto, SendEmailDto, UpdateDraftDto } from "../DTO/send-email.dto";
import { PriorityEnum } from "../enums/email-header.enum"; import { PriorityEnum } from "../enums/email-header.enum";
import { IEmailMap } from "../interfaces/email-map.interface"; import { IEmailMap } from "../interfaces/email-map.interface";
@@ -477,7 +476,7 @@ export class EmailService {
} }
//============================================== //==============================================
async updateDraft(wildduckUserId: string, messageId: number, updateDraftDto: UpdateDraftDto) { async updateDraft(wildduckUserId: string, messageId: number, updateDraftDto: UpdateDraftDto, generateTemplate: boolean = false) {
const draftsMailboxId = await this.mailboxResolverService.getDraftsMailboxId(wildduckUserId); const draftsMailboxId = await this.mailboxResolverService.getDraftsMailboxId(wildduckUserId);
const existingDraft = await firstValueFrom(this.mailServerService.messages.getMessage(wildduckUserId, draftsMailboxId, messageId)); const existingDraft = await firstValueFrom(this.mailServerService.messages.getMessage(wildduckUserId, draftsMailboxId, messageId));
@@ -490,41 +489,41 @@ export class EmailService {
cc: updateDraftDto.cc, cc: updateDraftDto.cc,
bcc: updateDraftDto.bcc, bcc: updateDraftDto.bcc,
replyTo: updateDraftDto.replyTo, replyTo: updateDraftDto.replyTo,
headers: updateDraftDto.headers,
subject: updateDraftDto.subject, subject: updateDraftDto.subject,
text: updateDraftDto.text, text: updateDraftDto.text,
html: updateDraftDto.html, html: updateDraftDto.html,
attachments: updateDraftDto.attachments, attachments: updateDraftDto.attachments,
meta: updateDraftDto.meta,
sess: updateDraftDto.sess,
ip: updateDraftDto.ip,
isDraft: true, isDraft: true,
uploadOnly: true, uploadOnly: true,
}; };
// const user = await this.userRepository.findOne({ wildduckUserId, deletedAt: null }, { populate: ["business"] }); if (generateTemplate) {
const user = await this.userRepository.findOne({ wildduckUserId, deletedAt: null }, { populate: ["business"] });
// if (user && user.business && (updateDraftDto.text || updateDraftDto.html)) { if (!user || !user.business) throw new BadRequestException(EmailMessage.USER_NOT_FOUND);
// const processedTemplate = await this.templateProcessorService.processEmailContent(user.business.id, user.id, {
// text: updateDraftDto.text!,
// html: updateDraftDto.html!,
// subject: updateDraftDto.subject,
// });
// if (processedTemplate.isHtml) { if (updateDraftDto.text || updateDraftDto.html) {
// draftData.html = processedTemplate.content; const processedTemplate = await this.templateProcessorService.processEmailContent(user.business.id, user.id, {
// draftData.text = ""; text: updateDraftDto.text!,
// } else { html: updateDraftDto.html!,
// draftData.text = processedTemplate.content; subject: updateDraftDto.subject,
// draftData.html = ""; });
// }
// const headers = this.generateHeadersForEmailType(user, { if (processedTemplate.isHtml) {
// html: draftData.html, draftData.html = processedTemplate.content;
// text: draftData.text, draftData.text = "";
// }); } else {
// draftData.headers = [...(draftData.headers || []), ...headers]; draftData.text = processedTemplate.content;
// } draftData.html = "";
}
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 as SendEmailDto)); const response = await firstValueFrom(this.mailServerService.submission.submitMessage(wildduckUserId, draftData as SendEmailDto));
@@ -540,38 +539,26 @@ export class EmailService {
const draftsMailboxId = await this.mailboxResolverService.getDraftsMailboxId(wildduckUserId); const draftsMailboxId = await this.mailboxResolverService.getDraftsMailboxId(wildduckUserId);
const message = await firstValueFrom(this.mailServerService.messages.getMessage(wildduckUserId, draftsMailboxId, messageId)); const message = await firstValueFrom(this.mailServerService.messages.getMessage(wildduckUserId, draftsMailboxId, messageId));
const updateMessage: UpdateDraftDto = {
const user = await this.userRepository.findOne({ wildduckUserId, deletedAt: null }, { populate: ["business"] }); from: message.from,
to: message.to,
if (!user) throw new BadRequestException(EmailMessage.USER_NOT_FOUND); cc: message.cc,
bcc: message.bcc,
const processedTemplate = await this.templateProcessorService.processEmailContent(user.business.id, user.id, { replyTo: message.inReplyTo ? { address: message.inReplyTo } : undefined,
text: message.text || "",
html: message.html?.[0] || "",
subject: message.subject, subject: message.subject,
}); text: message.text,
if (processedTemplate.isHtml) {
message.html = [processedTemplate.content];
message.text = "";
} else {
message.text = processedTemplate.content;
message.html = [];
}
const headers = this.generateHeadersForEmailType(user, {
html: message.html?.[0] || "", html: message.html?.[0] || "",
text: message.text || "", attachments: message.attachments as unknown as EmailAttachmentDto[],
}); isDraft: true,
message.headers = { ...message.headers, ...headers } as unknown as MessageHeaders; uploadOnly: true,
};
const { messageId: updatedMessageId } = await this.updateDraft(wildduckUserId, messageId, updateMessage, true);
const result = await firstValueFrom( const result = await firstValueFrom(
this.mailServerService.messages.submitDraft(wildduckUserId, draftsMailboxId, messageId, { deleteFiles: false }), this.mailServerService.messages.submitDraft(wildduckUserId, draftsMailboxId, Number(updatedMessageId), { deleteFiles: false }),
); );
await firstValueFrom(this.mailServerService.messages.deleteMessage(wildduckUserId, draftsMailboxId, messageId));
this.logger.log(`Draft message ${messageId} deleted from drafts folder after sending`);
await this.emailNotificationService.notifyEmailSentFromDraft({ await this.emailNotificationService.notifyEmailSentFromDraft({
wildduckUserId, wildduckUserId,
userId, userId,