const http = require("http"); const url = require("url"); const path = require("path"); const logger = require("./logger"); const database = require("./database"); class TrackingServer { constructor() { this.server = null; this.port = process.env.TRACKING_PORT || 3000; this.trackingDomain = process.env.TRACKING_DOMAIN || `http://localhost:${this.port}`; } // 1x1 transparent pixel GIF get trackingPixel() { return Buffer.from( "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7", "base64" ); } async start() { this.server = http.createServer((req, res) => { this.handleRequest(req, res); }); return new Promise((resolve, reject) => { this.server.listen(this.port, (err) => { if (err) { reject(err); } else { console.log(`📊 Tracking server started on ${this.trackingDomain}`); logger.info("Tracking server started", { port: this.port, domain: this.trackingDomain, }); resolve(); } }); }); } async stop() { if (this.server) { return new Promise((resolve) => { this.server.close(() => { console.log("📊 Tracking server stopped"); logger.info("Tracking server stopped"); resolve(); }); }); } } async handleRequest(req, res) { const parsedUrl = url.parse(req.url, true); const pathname = parsedUrl.pathname; try { if (pathname.startsWith("/track/open/")) { await this.handleOpenTracking(req, res, parsedUrl); } else if (pathname.startsWith("/track/click/")) { await this.handleClickTracking(req, res, parsedUrl); } else if (pathname === "/health") { this.handleHealthCheck(req, res); } else { this.handle404(req, res); } } catch (error) { logger.error("Tracking request error", { error: error.message, url: req.url, userAgent: req.headers["user-agent"], }); this.handle500(req, res); } } async handleOpenTracking(req, res, parsedUrl) { const pathParts = parsedUrl.pathname.split("/"); const trackingId = pathParts[3]; // /track/open/{trackingId} if (!trackingId) { return this.handle400(req, res, "Missing tracking ID"); } // Log the email open const trackingData = { trackingId, event: "email_open", timestamp: new Date().toISOString(), ip: req.connection.remoteAddress || req.headers["x-forwarded-for"], userAgent: req.headers["user-agent"], referer: req.headers.referer, }; logger.info("Email opened", trackingData); // Store in database try { await database.storeTrackingEvent(trackingId, "open", trackingData); } catch (error) { logger.error("Failed to store tracking event", { trackingId, event: "open", error: error.message, }); } // Return 1x1 transparent pixel res.writeHead(200, { "Content-Type": "image/gif", "Content-Length": this.trackingPixel.length, "Cache-Control": "no-cache, no-store, must-revalidate", Pragma: "no-cache", Expires: "0", }); res.end(this.trackingPixel); } async handleClickTracking(req, res, parsedUrl) { const pathParts = parsedUrl.pathname.split("/"); const trackingId = pathParts[3]; // /track/click/{trackingId} const linkId = pathParts[4]; // /track/click/{trackingId}/{linkId} const targetUrl = parsedUrl.query.url; if (!trackingId || !targetUrl) { return this.handle400(req, res, "Missing tracking ID or target URL"); } // Log the click const trackingData = { trackingId, linkId, event: "email_click", targetUrl, timestamp: new Date().toISOString(), ip: req.connection.remoteAddress || req.headers["x-forwarded-for"], userAgent: req.headers["user-agent"], referer: req.headers.referer, }; logger.info("Email link clicked", trackingData); // Store in database try { await database.storeTrackingEvent(trackingId, "click", trackingData); } catch (error) { logger.error("Failed to store tracking event", { trackingId, event: "click", error: error.message, }); } // Redirect to target URL res.writeHead(302, { Location: targetUrl, "Cache-Control": "no-cache", }); res.end(); } handleHealthCheck(req, res) { res.writeHead(200, { "Content-Type": "application/json" }); res.end( JSON.stringify({ status: "healthy", timestamp: new Date().toISOString(), uptime: process.uptime(), }) ); } handle400(req, res, message) { res.writeHead(400, { "Content-Type": "text/plain" }); res.end(`Bad Request: ${message}`); } handle404(req, res) { res.writeHead(404, { "Content-Type": "text/plain" }); res.end("Not Found"); } handle500(req, res) { res.writeHead(500, { "Content-Type": "text/plain" }); res.end("Internal Server Error"); } // Generate tracking URLs generateOpenTrackingUrl(emailId) { return `${this.trackingDomain}/track/open/${emailId}`; } generateClickTrackingUrl(emailId, linkId, targetUrl) { const encodedUrl = encodeURIComponent(targetUrl); return `${this.trackingDomain}/track/click/${emailId}/${linkId}?url=${encodedUrl}`; } // Add tracking to email content addTrackingToEmail(htmlContent, emailId) { if (!htmlContent) return htmlContent; // Add tracking pixel just before closing body tag const trackingPixel = ``; if (htmlContent.includes("")) { return htmlContent.replace("", `${trackingPixel}`); } else { // If no body tag, append at the end return htmlContent + trackingPixel; } } // Replace links with tracking URLs addClickTrackingToEmail(htmlContent, emailId) { if (!htmlContent) return htmlContent; let linkId = 0; return htmlContent.replace( /]*href=["']([^"']+)["'][^>]*)>/gi, (match, attributes, href) => { linkId++; // Skip if already a tracking URL or mailto/tel links if ( href.includes("/track/click/") || href.startsWith("mailto:") || href.startsWith("tel:") ) { return match; } const trackingUrl = this.generateClickTrackingUrl( emailId, linkId, href ); return `