Severity: High
Type: Bug
Scope: Admin, Notifications
Labels: bug, security, Official Campaign
Description
AdminService.suspendCampaign (src/admin/admin.service.ts, lines ~17–58) writes the campaign status change to the database and inserts an AuditLog entry, then calls this.notificationsService.sendCampaignSuspensionEmail(...). The implementation of sendCampaignSuspensionEmail (src/notifications/notifications.service.ts, lines ~159–173) has a TODO: replace with real mailer call marker and currently only emits a logger line — no email is queued, no in-app notification is created, and the recipient field is a synthetic creator-${creatorId}@platform.internal that cannot receive mail.
Result: an admin can confidently suspend a campaign, see the API return 200, and the creator receives nothing — meaning a campaign can be frozen with the creator unaware, blocking support requests and refunds.
Recommendation
- Replace the stub with a real email template + Bull enqueue: render
suspend-campaign-email.ts, push to QUEUE_EMAIL, and also create a Notification row for in-app delivery.
- Add an integration test (
AdminService.suspendCampaign + an injected mock NotificationsService) that asserts sendCampaignSuspensionEmail is invoked and the payload contains the real user.email (resolved through prisma.user.findUnique).
- Surface the suspension asynchronously: have the controller return 202 if notification enqueuing fails so admins see the partial failure instead of a silent success.
Severity: High
Type: Bug
Scope: Admin, Notifications
Labels:
bug,security,Official CampaignDescription
AdminService.suspendCampaign(src/admin/admin.service.ts, lines ~17–58) writes the campaign status change to the database and inserts anAuditLogentry, then callsthis.notificationsService.sendCampaignSuspensionEmail(...). The implementation ofsendCampaignSuspensionEmail(src/notifications/notifications.service.ts, lines ~159–173) has aTODO: replace with real mailer callmarker and currently only emits a logger line — no email is queued, no in-app notification is created, and the recipient field is a syntheticcreator-${creatorId}@platform.internalthat cannot receive mail.Result: an admin can confidently suspend a campaign, see the API return
200, and the creator receives nothing — meaning a campaign can be frozen with the creator unaware, blocking support requests and refunds.Recommendation
suspend-campaign-email.ts, push toQUEUE_EMAIL, and also create aNotificationrow for in-app delivery.AdminService.suspendCampaign+ an injected mockNotificationsService) that assertssendCampaignSuspensionEmailis invoked and the payload contains the realuser.email(resolved throughprisma.user.findUnique).