open mail and click url
This commit is contained in:
parent
833470cbdb
commit
2371aa727e
72
mail-service/src/controllers/trackingController.js
Normal file
72
mail-service/src/controllers/trackingController.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { DeliveryLog } from '../models/index.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async trackOpen(req, res) {
|
||||||
|
try {
|
||||||
|
const { deliveryLogId } = req.params;
|
||||||
|
|
||||||
|
// Находим запись в DeliveryLog
|
||||||
|
const deliveryLog = await DeliveryLog.findByPk(deliveryLogId);
|
||||||
|
|
||||||
|
if (!deliveryLog) {
|
||||||
|
return res.status(404).send('Not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем время открытия, если еще не было установлено
|
||||||
|
if (!deliveryLog.opened_at) {
|
||||||
|
await deliveryLog.update({
|
||||||
|
opened_at: new Date()
|
||||||
|
});
|
||||||
|
console.log(`[Tracking] Email opened: deliveryLogId=${deliveryLogId}, campaignId=${deliveryLog.campaign_id}, subscriberId=${deliveryLog.subscriber_id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращаем прозрачный 1x1 пиксель
|
||||||
|
const pixel = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==', 'base64');
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'image/png',
|
||||||
|
'Content-Length': pixel.length,
|
||||||
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
||||||
|
'Pragma': 'no-cache',
|
||||||
|
'Expires': '0'
|
||||||
|
});
|
||||||
|
res.end(pixel);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Tracking] Error tracking email open:', err);
|
||||||
|
res.status(500).send('Error');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async trackClick(req, res) {
|
||||||
|
try {
|
||||||
|
const { deliveryLogId } = req.params;
|
||||||
|
const { url } = req.query;
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
return res.status(400).send('URL parameter required');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Находим запись в DeliveryLog
|
||||||
|
const deliveryLog = await DeliveryLog.findByPk(deliveryLogId);
|
||||||
|
|
||||||
|
if (!deliveryLog) {
|
||||||
|
return res.status(404).send('Not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем время клика, если еще не было установлено
|
||||||
|
if (!deliveryLog.clicked_at) {
|
||||||
|
await deliveryLog.update({
|
||||||
|
clicked_at: new Date()
|
||||||
|
});
|
||||||
|
console.log(`[Tracking] Email clicked: deliveryLogId=${deliveryLogId}, campaignId=${deliveryLog.campaign_id}, subscriberId=${deliveryLog.subscriber_id}, url=${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Перенаправляем на оригинальный URL
|
||||||
|
res.redirect(url);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[Tracking] Error tracking email click:', err);
|
||||||
|
res.status(500).send('Error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -8,6 +8,7 @@ import campaignRoutes from './campaign.js';
|
|||||||
import deliveryLogRoutes from './deliveryLog.js';
|
import deliveryLogRoutes from './deliveryLog.js';
|
||||||
import smtpServerRoutes from './smtpServer.js';
|
import smtpServerRoutes from './smtpServer.js';
|
||||||
import topicRoutes from './topic.js';
|
import topicRoutes from './topic.js';
|
||||||
|
import trackingRoutes from './tracking.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@ -20,5 +21,6 @@ router.use('/campaigns', campaignRoutes);
|
|||||||
router.use('/delivery-logs', deliveryLogRoutes);
|
router.use('/delivery-logs', deliveryLogRoutes);
|
||||||
router.use('/smtp-servers', smtpServerRoutes);
|
router.use('/smtp-servers', smtpServerRoutes);
|
||||||
router.use('/topics', topicRoutes);
|
router.use('/topics', topicRoutes);
|
||||||
|
router.use('/track', trackingRoutes);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
12
mail-service/src/routes/tracking.js
Normal file
12
mail-service/src/routes/tracking.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import trackingController from '../controllers/trackingController.js';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
// Трекинг открытия письма
|
||||||
|
router.get('/open/:deliveryLogId', trackingController.trackOpen);
|
||||||
|
|
||||||
|
// Трекинг клика по ссылке
|
||||||
|
router.get('/click/:deliveryLogId', trackingController.trackClick);
|
||||||
|
|
||||||
|
export default router;
|
||||||
@ -259,12 +259,26 @@ async function processEmailTask(task, topic) {
|
|||||||
});
|
});
|
||||||
console.log(`[DynamicConsumer] Transporter created successfully`);
|
console.log(`[DynamicConsumer] Transporter created successfully`);
|
||||||
|
|
||||||
|
// Добавляем трекинг-пиксель для отслеживания открытия письма
|
||||||
|
const trackingPixel = `<img src="http://${smtp.from_email}/api/mail/track/open/${deliveryLog.id}" width="1" height="1" style="display:none;" />`;
|
||||||
|
|
||||||
|
// Обрабатываем ссылки для отслеживания кликов
|
||||||
|
const htmlWithClickTracking = task.html.replace(
|
||||||
|
/<a\s+href=["']([^"']+)["']/gi,
|
||||||
|
(match, url) => {
|
||||||
|
const trackingUrl = `http://${smtp.from_email}/api/mail/track/click/${deliveryLog.id}?url=${encodeURIComponent(url)}`;
|
||||||
|
return `<a href="${trackingUrl}"`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const htmlWithTracking = htmlWithClickTracking + trackingPixel;
|
||||||
|
|
||||||
const mailOptions = {
|
const mailOptions = {
|
||||||
from: smtp.from_email,
|
from: smtp.from_email,
|
||||||
to: task.email,
|
to: task.email,
|
||||||
subject: task.subject,
|
subject: task.subject,
|
||||||
text: task.text,
|
text: task.text,
|
||||||
html: task.html,
|
html: htmlWithTracking,
|
||||||
headers: {
|
headers: {
|
||||||
'List-Unsubscribe': `<mailto:${smtp.from_email}?subject=unsubscribe>`,
|
'List-Unsubscribe': `<mailto:${smtp.from_email}?subject=unsubscribe>`,
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user