268 lines
11 KiB
JavaScript
268 lines
11 KiB
JavaScript
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || (function () {
|
|
var ownKeys = function(o) {
|
|
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
var ar = [];
|
|
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
return ar;
|
|
};
|
|
return ownKeys(o);
|
|
};
|
|
return function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
})();
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.campaignEmailsService = void 0;
|
|
const client_1 = require("@prisma/client");
|
|
const database_1 = require("../../../config/database");
|
|
const error_handler_1 = require("../../../middleware/error-handler");
|
|
const email_queue_service_1 = require("../../../services/email-queue.service");
|
|
const metrics_1 = require("../../../utils/metrics");
|
|
const crm_activity_1 = require("../../../utils/crm-activity");
|
|
const group_service_1 = require("../../social/group.service");
|
|
const achievements_service_1 = require("../../social/achievements.service");
|
|
exports.campaignEmailsService = {
|
|
async sendEmail(slug, data, senderIp) {
|
|
const campaign = await database_1.prisma.campaign.findUnique({
|
|
where: { slug },
|
|
select: {
|
|
id: true,
|
|
slug: true,
|
|
title: true,
|
|
status: true,
|
|
emailSubject: true,
|
|
emailBody: true,
|
|
allowSmtpEmail: true,
|
|
allowMailtoLink: true,
|
|
allowEmailEditing: true,
|
|
},
|
|
});
|
|
if (!campaign) {
|
|
throw new error_handler_1.AppError(404, 'Campaign not found', 'CAMPAIGN_NOT_FOUND');
|
|
}
|
|
if (campaign.status !== client_1.CampaignStatus.ACTIVE) {
|
|
throw new error_handler_1.AppError(400, 'Campaign is not active', 'CAMPAIGN_NOT_ACTIVE');
|
|
}
|
|
if (data.emailMethod === client_1.EmailMethod.SMTP && !campaign.allowSmtpEmail) {
|
|
throw new error_handler_1.AppError(400, 'SMTP email is not enabled for this campaign', 'SMTP_NOT_ALLOWED');
|
|
}
|
|
if (data.emailMethod === client_1.EmailMethod.MAILTO && !campaign.allowMailtoLink) {
|
|
throw new error_handler_1.AppError(400, 'Mailto link is not enabled for this campaign', 'MAILTO_NOT_ALLOWED');
|
|
}
|
|
// Determine subject and body (custom only if campaign allows editing)
|
|
const subject = (campaign.allowEmailEditing && data.customEmailSubject)
|
|
? data.customEmailSubject
|
|
: campaign.emailSubject;
|
|
const message = (campaign.allowEmailEditing && data.customEmailBody)
|
|
? data.customEmailBody
|
|
: campaign.emailBody;
|
|
const status = data.emailMethod === client_1.EmailMethod.SMTP
|
|
? client_1.CampaignEmailStatus.QUEUED
|
|
: client_1.CampaignEmailStatus.CLICKED;
|
|
const campaignEmail = await database_1.prisma.campaignEmail.create({
|
|
data: {
|
|
campaignId: campaign.id,
|
|
campaignSlug: campaign.slug,
|
|
userEmail: data.userEmail,
|
|
userName: data.userName,
|
|
userPostalCode: data.postalCode,
|
|
recipientEmail: data.recipientEmail,
|
|
recipientName: data.recipientName,
|
|
recipientTitle: data.recipientTitle,
|
|
recipientLevel: data.recipientLevel,
|
|
emailMethod: data.emailMethod,
|
|
subject,
|
|
message,
|
|
status,
|
|
senderIp,
|
|
},
|
|
});
|
|
if (data.emailMethod === client_1.EmailMethod.SMTP) {
|
|
await email_queue_service_1.emailQueueService.addCampaignEmail({
|
|
campaignEmailId: campaignEmail.id,
|
|
recipientEmail: data.recipientEmail,
|
|
recipientName: data.recipientName,
|
|
recipientLevel: data.recipientLevel,
|
|
userEmail: data.userEmail,
|
|
userName: data.userName,
|
|
postalCode: data.postalCode,
|
|
subject,
|
|
message,
|
|
campaignTitle: campaign.title,
|
|
});
|
|
}
|
|
(0, metrics_1.recordCampaignEmail)(campaign.id);
|
|
// CRM activity (fire-and-forget)
|
|
(0, crm_activity_1.recordCrmActivity)({
|
|
email: data.userEmail,
|
|
activityType: 'EMAIL_SENT',
|
|
title: `Sent campaign email: ${campaign.title}`,
|
|
metadata: { campaignId: campaign.id, campaignSlug: campaign.slug, recipientEmail: data.recipientEmail, emailMethod: data.emailMethod },
|
|
}).catch(() => { });
|
|
// Social group sync (fire-and-forget)
|
|
group_service_1.groupService.syncCampaignTeam(campaign.id).catch(() => { });
|
|
// Achievement check for registered users (fire-and-forget)
|
|
database_1.prisma.user.findUnique({ where: { email: data.userEmail }, select: { id: true } })
|
|
.then((user) => {
|
|
if (user)
|
|
achievements_service_1.achievementsService.checkAndUnlock(user.id, ['campaigns']).catch(() => { });
|
|
})
|
|
.catch(() => { });
|
|
// Fire-and-forget: check campaign milestones
|
|
Promise.resolve().then(() => __importStar(require('../../social/impact-stories.service'))).then(({ impactStoriesService }) => {
|
|
impactStoriesService.checkMilestones(campaign.id).catch(() => { });
|
|
}).catch(() => { });
|
|
return {
|
|
id: campaignEmail.id,
|
|
status: campaignEmail.status,
|
|
emailMethod: campaignEmail.emailMethod,
|
|
};
|
|
},
|
|
async trackMailto(slug, data, senderIp) {
|
|
const campaign = await database_1.prisma.campaign.findUnique({
|
|
where: { slug },
|
|
select: {
|
|
id: true,
|
|
slug: true,
|
|
title: true,
|
|
status: true,
|
|
emailSubject: true,
|
|
emailBody: true,
|
|
allowMailtoLink: true,
|
|
},
|
|
});
|
|
if (!campaign) {
|
|
throw new error_handler_1.AppError(404, 'Campaign not found', 'CAMPAIGN_NOT_FOUND');
|
|
}
|
|
if (campaign.status !== client_1.CampaignStatus.ACTIVE) {
|
|
throw new error_handler_1.AppError(400, 'Campaign is not active', 'CAMPAIGN_NOT_ACTIVE');
|
|
}
|
|
const campaignEmail = await database_1.prisma.campaignEmail.create({
|
|
data: {
|
|
campaignId: campaign.id,
|
|
campaignSlug: campaign.slug,
|
|
userEmail: data.userEmail,
|
|
userName: data.userName,
|
|
userPostalCode: data.postalCode,
|
|
recipientEmail: data.recipientEmail,
|
|
recipientName: data.recipientName,
|
|
recipientTitle: data.recipientTitle,
|
|
recipientLevel: data.recipientLevel,
|
|
emailMethod: client_1.EmailMethod.MAILTO,
|
|
subject: campaign.emailSubject,
|
|
message: campaign.emailBody,
|
|
status: client_1.CampaignEmailStatus.CLICKED,
|
|
senderIp,
|
|
},
|
|
});
|
|
// Social group sync (fire-and-forget)
|
|
group_service_1.groupService.syncCampaignTeam(campaign.id).catch(() => { });
|
|
// Fire-and-forget: check campaign milestones
|
|
Promise.resolve().then(() => __importStar(require('../../social/impact-stories.service'))).then(({ impactStoriesService }) => {
|
|
impactStoriesService.checkMilestones(campaign.id).catch(() => { });
|
|
}).catch(() => { });
|
|
return {
|
|
id: campaignEmail.id,
|
|
status: campaignEmail.status,
|
|
emailMethod: campaignEmail.emailMethod,
|
|
};
|
|
},
|
|
async listByCampaign(campaignId, filters) {
|
|
const { page, limit, status, emailMethod } = filters;
|
|
const skip = (page - 1) * limit;
|
|
const where = { campaignId };
|
|
if (status)
|
|
where.status = status;
|
|
if (emailMethod)
|
|
where.emailMethod = emailMethod;
|
|
const [emails, total] = await Promise.all([
|
|
database_1.prisma.campaignEmail.findMany({
|
|
where,
|
|
skip,
|
|
take: limit,
|
|
orderBy: { sentAt: 'desc' },
|
|
select: {
|
|
id: true,
|
|
userEmail: true,
|
|
userName: true,
|
|
userPostalCode: true,
|
|
recipientEmail: true,
|
|
recipientName: true,
|
|
recipientLevel: true,
|
|
emailMethod: true,
|
|
subject: true,
|
|
status: true,
|
|
sentAt: true,
|
|
},
|
|
}),
|
|
database_1.prisma.campaignEmail.count({ where }),
|
|
]);
|
|
return {
|
|
emails,
|
|
pagination: {
|
|
page,
|
|
limit,
|
|
total,
|
|
totalPages: Math.ceil(total / limit),
|
|
},
|
|
};
|
|
},
|
|
async getStats(campaignId) {
|
|
const [totals, byMethod] = await Promise.all([
|
|
database_1.prisma.campaignEmail.groupBy({
|
|
by: ['status'],
|
|
where: { campaignId },
|
|
_count: true,
|
|
}),
|
|
database_1.prisma.campaignEmail.groupBy({
|
|
by: ['emailMethod'],
|
|
where: { campaignId },
|
|
_count: true,
|
|
}),
|
|
]);
|
|
const stats = {
|
|
total: 0,
|
|
queued: 0,
|
|
sent: 0,
|
|
failed: 0,
|
|
clicked: 0,
|
|
smtpCount: 0,
|
|
mailtoCount: 0,
|
|
};
|
|
for (const row of totals) {
|
|
stats.total += row._count;
|
|
const key = row.status.toLowerCase();
|
|
if (key in stats)
|
|
stats[key] = row._count;
|
|
}
|
|
for (const row of byMethod) {
|
|
if (row.emailMethod === client_1.EmailMethod.SMTP)
|
|
stats.smtpCount = row._count;
|
|
if (row.emailMethod === client_1.EmailMethod.MAILTO)
|
|
stats.mailtoCount = row._count;
|
|
}
|
|
return stats;
|
|
},
|
|
};
|
|
//# sourceMappingURL=campaign-emails.service.js.map
|