Skip to content

Implement real campaign suspension notifications with email and in-app delivery#35

Open
Just-Bamford wants to merge 3 commits into
OrbitChainLabs:mainfrom
Just-Bamford:fix/campaign-suspension-notifications
Open

Implement real campaign suspension notifications with email and in-app delivery#35
Just-Bamford wants to merge 3 commits into
OrbitChainLabs:mainfrom
Just-Bamford:fix/campaign-suspension-notifications

Conversation

@Just-Bamford

Copy link
Copy Markdown

Problem Statement

AdminService.suspendCampaign had a critical notification failure that left campaign creators completely unaware of suspensions:

this pr Closes #6

  • No Real Email Delivery: The sendCampaignSuspensionEmail method only logged to console with a TODO comment - no actual email was sent
  • Invalid Email Address: Used a synthetic email creator-${creatorId}@platform.internal that cannot receive mail
  • No In-App Notification: Creators had zero in-app visibility of the suspension
  • Silent Failures: API returned 200 OK even when notifications weren't sent, hiding the problem from admins
  • User Impact: Campaigns could be frozen without creators knowing, blocking support requests, refunds, and legitimate appeals

Result: Admins could confidently suspend a campaign, see the API return success, but the creator receives nothing - leaving them confused and unable to respond.


Solution Implemented

1. Professional Email Template

Added campaignSuspensionTemplate to email-templates.ts:

  • Branded HTML email with warning banner
  • Clear display of campaign title and suspension reason
  • "What this means" section explaining the impact
  • Bullet points: campaign hidden, donations blocked, funds remain secure
  • Support contact button with configurable email
  • Professional footer with compliance text

2. Real Email Delivery via Bull Queue

Replaced the TODO stub in notifications.service.ts with full implementation:

  • Fetches real creator from database using prisma.user.findUnique
  • Validates creator exists and has valid email address
  • Renders HTML template with suspension details
  • Queues email job via Bull (QUEUE_EMAIL) for async processing
  • Bypasses user notification preferences (suspension emails are critical)
  • Comprehensive logging at each step

3. In-App Notification Creation

Creates a persistent notification in the database:

  • Type: CAMPAIGN_UPDATED (using existing enum)
  • Title: "Campaign Suspended"
  • Message: Includes campaign title and suspension reason
  • Links to campaign via relatedId field
  • Marked as unread by default
  • Visible in creator's notification center

4. Graceful Error Handling

Updated AdminService.suspendCampaign:

  • Campaign suspension and audit log happen first (transactional)
  • Notification sending wrapped in try-catch block
  • Returns notificationSent: boolean flag to indicate delivery status
  • Logs errors but doesn't throw (campaign is already suspended)
  • Allows admins to see notification failures and take manual action

5. API Transparency

Updated AdminController.suspendCampaign:

  • Returns { message: string, notificationSent: boolean }
  • Frontend can check notificationSent flag
  • If false, frontend should alert admin to manually notify creator
  • Maintains clear separation: suspension succeeded, notification may have failed

Notification Flow

Admin suspends campaign

Update campaign.status = CANCELLED

Write audit log

[TRY]
Fetch creator from database (prisma.user.findUnique)

Validate email exists

Render HTML template with suspension data

Queue email job via Bull (QUEUE_EMAIL)

Create in-app notification (prisma.notification.create)

Log success

Return { message: "...", notificationSent: true }
[CATCH]
Log error (creator not found, no email, queue failure)

Return { message: "...", notificationSent: false }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HIGH] — Admin "suspend campaign" flow writes an AuditLog and resolves successfully, but never actually notifies the creator

1 participant