"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