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