chore: update the template processor
This commit is contained in:
@@ -92,7 +92,6 @@
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@types/supertest": "^6.0.3",
|
||||
"@types/web-push": "^3.6.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.35.1",
|
||||
"@typescript-eslint/parser": "^8.35.1",
|
||||
"eslint": "^9.30.1",
|
||||
|
||||
@@ -281,6 +281,8 @@ export const enum EmailMessage {
|
||||
TRASH_EMPTYED_SUCCESSFULLY = "سطل زباله با موفقیت خالی شد",
|
||||
SPAM_EMPTYED_SUCCESSFULLY = "همه پیام های اسپم با موفقیت حذف شدند",
|
||||
SAVE_DRAFT_SUCCESS = "پیش نویس با موفقیت ذخیره شد",
|
||||
HTML_CONTENT_REQUIRED = "محتوای HTML مورد نیاز است",
|
||||
TEXT_CONTENT_REQUIRED = "TEXT_CONTENT_REQUIRED",
|
||||
}
|
||||
|
||||
export const enum WebSocketMessage {
|
||||
|
||||
@@ -76,7 +76,6 @@ export class EmailPollingProcessor extends WorkerProcessor {
|
||||
timestamp: email.date || new Date().toISOString(),
|
||||
mailboxId: email.mailbox,
|
||||
mailboxName: "Inbox",
|
||||
isRead: !!email.seen,
|
||||
hasAttachments: !!(email.attachments && Array.isArray(email.attachments) && email.attachments.length > 0),
|
||||
});
|
||||
|
||||
|
||||
@@ -80,6 +80,16 @@ export class EmailNotificationService {
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async notifyEmailSentFromDraft(emailSentFromDraftData: IEmailSentPayload) {
|
||||
try {
|
||||
await this.emailGateway.notifyEmailSent(emailSentFromDraftData.userId, emailSentFromDraftData);
|
||||
await this.updateUnreadCount(emailSentFromDraftData.wildduckUserId, emailSentFromDraftData.userId);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to notify email sent from draft for user ${emailSentFromDraftData.userId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
//************************************************************************ */
|
||||
async notifyEmailDeleted(emailDeletedData: IEmailDeletePayload) {
|
||||
try {
|
||||
|
||||
@@ -157,15 +157,15 @@ export class SendEmailDto {
|
||||
@ApiPropertyOptional({ description: "Message subject. If not then resolved from Reference message (optional)", example: "Test email" })
|
||||
subject: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNotEmpty({ message: EmailMessage.TEXT_CONTENT_REQUIRED })
|
||||
@IsString({ message: EmailMessage.TEXT_CONTENT_MUST_BE_STRING })
|
||||
@ApiPropertyOptional({ description: "Plaintext message (optional)" })
|
||||
text?: string;
|
||||
text: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNotEmpty({ message: EmailMessage.HTML_CONTENT_REQUIRED })
|
||||
@IsString({ message: EmailMessage.HTML_CONTENT_MUST_BE_STRING })
|
||||
@ApiPropertyOptional({ description: "HTML formatted message (optional)" })
|
||||
html?: string;
|
||||
html: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray({ message: EmailMessage.ATTACHMENTS_MUST_BE_ARRAY })
|
||||
|
||||
@@ -207,8 +207,8 @@ export class EmailController {
|
||||
@ApiResponse({ status: 200, description: "Draft sent successfully" })
|
||||
@ApiResponse({ status: 404, description: "Draft not found" })
|
||||
@ApiResponse({ status: 403, description: "Not authorized to send draft" })
|
||||
sendDraft(@UserDec("wildduckUserId") userId: string, @Param() params: MessageParamDto) {
|
||||
return this.emailService.sendDraft(userId, Number(params.messageId));
|
||||
sendDraft(@UserDec() user: User, @Param() params: MessageParamDto) {
|
||||
return this.emailService.sendDraft(user.wildduckUserId, user.id, Number(params.messageId));
|
||||
}
|
||||
|
||||
@Delete("messages/drafts/:messageId")
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// BASE INTERFACES
|
||||
// ====================
|
||||
|
||||
import { MessageAddress } from "../../mail-server/interfaces/messages-response.interface";
|
||||
|
||||
export interface IBaseEventPayload {
|
||||
userId: string;
|
||||
timestamp: string;
|
||||
@@ -23,21 +25,15 @@ export interface IBaseMessageMailboxEventPayload extends IBaseMessageEventPayloa
|
||||
// COMMON EMAIL INTERFACES
|
||||
// ====================
|
||||
|
||||
export interface IEmailContact {
|
||||
name?: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
export interface IEmailMetadata {
|
||||
subject: string;
|
||||
hasAttachments: boolean;
|
||||
mailboxName: string;
|
||||
isRead: boolean;
|
||||
}
|
||||
|
||||
export interface IEmailContent extends IEmailMetadata {
|
||||
from: IEmailContact;
|
||||
to: IEmailContact[];
|
||||
from: MessageAddress;
|
||||
to: MessageAddress[];
|
||||
preview?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,10 +58,10 @@ export class EmailService {
|
||||
|
||||
if (processedTemplate.isHtml) {
|
||||
sendEmailDto.html = processedTemplate.content;
|
||||
sendEmailDto.text = undefined;
|
||||
sendEmailDto.text = "";
|
||||
} else {
|
||||
sendEmailDto.text = processedTemplate.content;
|
||||
sendEmailDto.html = undefined;
|
||||
sendEmailDto.html = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,6 @@ export class EmailService {
|
||||
from: sendEmailDto.from,
|
||||
hasAttachments: false,
|
||||
mailboxName: "Sent",
|
||||
isRead: true,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
@@ -514,10 +513,10 @@ export class EmailService {
|
||||
|
||||
if (processedTemplate.isHtml) {
|
||||
draftData.html = processedTemplate.content;
|
||||
draftData.text = undefined;
|
||||
draftData.text = "";
|
||||
} else {
|
||||
draftData.text = processedTemplate.content;
|
||||
draftData.html = undefined;
|
||||
draftData.html = "";
|
||||
}
|
||||
|
||||
const headers = this.generateHeadersForEmailType(user, {
|
||||
@@ -538,12 +537,27 @@ export class EmailService {
|
||||
}
|
||||
|
||||
//==============================================
|
||||
async sendDraft(wildduckUserId: string, messageId: number) {
|
||||
async sendDraft(wildduckUserId: string, userId: string, messageId: number) {
|
||||
const draftsMailboxId = await this.mailboxResolverService.getDraftsMailboxId(wildduckUserId);
|
||||
const result = await firstValueFrom(
|
||||
this.mailServerService.messages.submitDraft(wildduckUserId, draftsMailboxId, messageId, { deleteFiles: false }),
|
||||
);
|
||||
|
||||
const message = await firstValueFrom(this.mailServerService.messages.getMessage(wildduckUserId, draftsMailboxId, messageId));
|
||||
|
||||
await this.emailNotificationService.notifyEmailSentFromDraft({
|
||||
wildduckUserId,
|
||||
userId,
|
||||
from: message.from,
|
||||
to: message.to!,
|
||||
subject: message.subject,
|
||||
hasAttachments: false,
|
||||
mailboxName: message.mailbox,
|
||||
messageId: Number(message.id),
|
||||
mailboxId: message.mailbox,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return {
|
||||
message: EmailMessage.DRAFT_SENT_SUCCESSFULLY,
|
||||
messageId: result.message.id,
|
||||
|
||||
@@ -180,7 +180,7 @@ export type PersonalityDataType = {
|
||||
};
|
||||
|
||||
export interface EmailContent {
|
||||
text?: string;
|
||||
html?: string;
|
||||
text: string;
|
||||
html: string;
|
||||
subject?: string;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export class TemplateProcessorService {
|
||||
|
||||
if (!selectedTemplate) {
|
||||
return {
|
||||
content: emailContent.text || emailContent.html || "",
|
||||
content: emailContent.html || emailContent.text || "",
|
||||
isHtml: false,
|
||||
hasTemplate: false,
|
||||
};
|
||||
@@ -45,23 +45,21 @@ export class TemplateProcessorService {
|
||||
|
||||
//**************************************************** */
|
||||
private async injectContentIntoTemplate(templateHtml: string | undefined, emailContent: EmailContent) {
|
||||
if (!templateHtml) {
|
||||
return emailContent.text || emailContent.html || "";
|
||||
}
|
||||
const { html, text } = emailContent;
|
||||
|
||||
if (!templateHtml) return html || text || "";
|
||||
|
||||
let processedHtml = templateHtml;
|
||||
|
||||
// Content to inject (prefer text, fallback to html)
|
||||
const contentToInject = emailContent.text || this.stripHtmlTags(emailContent.html || "");
|
||||
// const contentToInject = this.stripHtmlTags(html || text);
|
||||
const contentToInject = html || text;
|
||||
|
||||
// Replace content placeholders
|
||||
const contentPlaceholders = [/\{\{content\}\}/gi, /\{\{message\}\}/gi, /\{\{body\}\}/gi, /\{content\}/gi, /\{message\}/gi, /\{body\}/gi];
|
||||
|
||||
contentPlaceholders.forEach((placeholder) => {
|
||||
processedHtml = processedHtml.replace(placeholder, contentToInject);
|
||||
});
|
||||
|
||||
// Replace subject placeholders if subject exists
|
||||
if (emailContent.subject) {
|
||||
const subjectPlaceholders = [/\{\{subject\}\}/gi, /\{\{title\}\}/gi, /\{subject\}/gi, /\{title\}/gi];
|
||||
|
||||
@@ -74,15 +72,15 @@ export class TemplateProcessorService {
|
||||
}
|
||||
|
||||
//**************************************************** */
|
||||
private stripHtmlTags(html: string): string {
|
||||
return html
|
||||
.replace(/<[^>]*>/g, "")
|
||||
.replace(/ /g, " ")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.trim();
|
||||
}
|
||||
// private stripHtmlTags(html: string): string {
|
||||
// return html
|
||||
// .replace(/<[^>]*>/g, "")
|
||||
// .replace(/ /g, " ")
|
||||
// .replace(/&/g, "&")
|
||||
// .replace(/</g, "<")
|
||||
// .replace(/>/g, ">")
|
||||
// .replace(/"/g, '"')
|
||||
// .replace(/'/g, "'")
|
||||
// .trim();
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user