import { Injectable } from "@nestjs/common"; import { v4 as uuidv4 } from "uuid"; import { EmailHeaderDto } from "../DTO/send-email.dto"; import { EmailHeaderKey, Priority } from "../enums/email-header.enum"; import { IGenerateAntiThreadingHeadersOptions, IGenerateGmailAntiThreadingHeadersOptions, IGenerateMarketingHeadersOptions, IGenerateTransactionalHeadersOptions, IImageDetectionResult, } from "../interfaces/email-header.interface"; @Injectable() export class EmailHeadersService { private readonly hostDomain = "danakcorp.com"; private readonly hostDomain2 = "storage.danakcorp.com"; //****************************************** */ generateAntiThreadingHeaders(options: IGenerateAntiThreadingHeadersOptions): EmailHeaderDto[] { const headers: EmailHeaderDto[] = []; headers.push({ key: EmailHeaderKey.References, value: `<${uuidv4()}@${options.businessDomain}>`, }); headers.push({ key: EmailHeaderKey.ThreadIndex, value: this.generateThreadIndex(), }); headers.push({ key: EmailHeaderKey.XMailer, value: `Danak Mail Service v1.0`, }); headers.push({ key: EmailHeaderKey.XPriority, value: this.getPriorityValue(options.priority || Priority.Normal), }); headers.push({ key: EmailHeaderKey.XMSMailPriority, value: options.priority || Priority.Normal, }); headers.push({ key: EmailHeaderKey.MIMEVersion, value: "1.0", }); headers.push({ key: EmailHeaderKey.XSpamStatus, value: "No", }); headers.push({ key: EmailHeaderKey.XSpamScore, value: "0.0", }); headers.push({ key: EmailHeaderKey.XFeedbackID, value: `${uuidv4()}:${options.businessDomain}`, }); headers.push({ key: EmailHeaderKey.XEntityRefID, value: uuidv4(), }); if (options.hasHostedImages) { headers.push(...this.generateImageAntiSpamHeaders(options)); } if (options.isTransactional) { headers.push({ key: EmailHeaderKey.XAutoResponseSuppress, value: "All", }); headers.push({ key: EmailHeaderKey.XEntityID, value: "transactional", }); headers.push({ key: EmailHeaderKey.Precedence, value: "bulk", }); } if (options.isMarketing) { headers.push({ key: EmailHeaderKey.XEntityID, value: "marketing", }); headers.push({ key: EmailHeaderKey.ListUnsubscribePost, value: "List-Unsubscribe=One-Click", }); if (options.listUnsubscribe) { headers.push({ key: EmailHeaderKey.ListUnsubscribe, value: options.listUnsubscribe, }); } } if (options.listId) { headers.push({ key: EmailHeaderKey.ListId, value: options.listId, }); } return headers; } //****************************************** */ generateImageAntiSpamHeaders(options: IGenerateAntiThreadingHeadersOptions): EmailHeaderDto[] { const headers: EmailHeaderDto[] = []; if (!options.hasHostedImages) { return headers; } 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; } //****************************************** */ detectImagesInContent(htmlContent: string): IImageDetectionResult { if (!htmlContent) { return { hasHostedImages: false, imageCount: 0, trustedImageSources: [], danakCorpImages: [], }; } // Regex to find img tags with src attributes const imgRegex = /]+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[] { const headers: EmailHeaderDto[] = []; // Generate completely unique Message-ID for each email const uniqueMessageId = this.generateMessageId(options.businessDomain, true); headers.push({ key: EmailHeaderKey.MessageId, value: uniqueMessageId, }); // Clear any potential threading references headers.push({ key: EmailHeaderKey.References, value: `<${uuidv4()}@${options.businessDomain}>`, }); headers.push({ key: EmailHeaderKey.InReplyTo, value: `<${uuidv4()}@${options.businessDomain}>`, }); // Gmail-specific thread breaking headers.push({ key: EmailHeaderKey.XGmailThreadBreak, value: options.conversationBreaker || uuidv4(), }); // Additional thread prevention headers.push({ key: EmailHeaderKey.ThreadTopic, value: uuidv4(), }); return headers; } //****************************************** */ generateTransactionalHeaders(options: IGenerateTransactionalHeadersOptions): EmailHeaderDto[] { const headers: EmailHeaderDto[] = []; // Base anti-threading headers headers.push( ...this.generateAntiThreadingHeaders({ businessName: options.businessName, businessDomain: options.businessDomain, isTransactional: true, }), ); // Transactional-specific headers headers.push({ key: EmailHeaderKey.XTransactionID, value: options.transactionId || uuidv4(), }); if (options.category) { headers.push({ key: EmailHeaderKey.XCategory, value: options.category, }); } // Mark as important transactional email headers.push({ key: EmailHeaderKey.Importance, value: "high", }); headers.push({ key: EmailHeaderKey.XAutoResponseSuppress, value: "All", }); return headers; } //****************************************** */ generateMarketingHeaders(options: IGenerateMarketingHeadersOptions): EmailHeaderDto[] { const headers: EmailHeaderDto[] = []; // Base anti-threading headers headers.push( ...this.generateAntiThreadingHeaders({ businessName: options.businessName, businessDomain: options.businessDomain, isMarketing: true, listId: options.listId, listUnsubscribe: `<${options.unsubscribeUrl}>`, }), ); // Marketing-specific headers if (options.campaignId) { headers.push({ key: EmailHeaderKey.XCampaignID, value: options.campaignId, }); } headers.push({ key: EmailHeaderKey.XMarketingEmail, value: "true", }); // Required for bulk marketing emails headers.push({ key: EmailHeaderKey.Precedence, value: "bulk", }); return headers; } //****************************************** */ private generateMessageId(domain: string, extraUnique = false): string { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 15); const uuid = extraUnique ? uuidv4() : ""; return `<${timestamp}.${random}${uuid}@${domain}>`; } //****************************************** */ private generateThreadIndex(): string { // Generate a unique thread index for Outlook const timestamp = Date.now().toString(16); const random = Math.random().toString(16).substring(2, 10); return `${timestamp}${random}`.toUpperCase(); } //****************************************** */ private getPriorityValue(priority: Priority): string { return priority === Priority.High ? "1" : priority === Priority.Low ? "5" : "3"; } }