Saltar al contenido principal

🚨 PDF Attachment - Immediate Fix Needed

Date: November 5, 2025, 1:00 PM
Critical Issue: Attachments are being lost between queue and send
User Feedback: "Queue works immediately"


🎯 THE SITUATION

User confirmed: Queue processes immediately (not waiting)

This means:

  • ✅ Queue is working
  • ✅ Jobs process instantly
  • But attachments are still not being sent!

🐛 THE BUG

Since queue works immediately, the attachment content is being lost during the queue serialize/deserialize cycle.

What Happens:

1. Enqueue called with payload containing:
{
attachments: [{
filename: "invoice.pdf",
content: "JVBERi0x..." (65KB base64 string),
mimeType: "application/pdf"
}]
}

2. Queue stores payload in database as JSON/JSONB
→ Payload might be too large
→ OR attachments are being excluded

3. Queue immediately retrieves payload
→ Attachments might be missing
→ OR content field is empty/null

4. executeSend() receives payload
→ NO attachments or empty content!

5. Email adapter sends without attachments
→ Empty attachment in email!

🔧 THE SOLUTION

We Need To:

STOP storing attachment content in the queue payload!

Instead:

  1. ✅ Save attachment metadata to communicationAttachment table
  2. ALSO save the base64 content to a new column
  3. ✅ Enqueue WITHOUT attachments (too large)
  4. ✅ In executeSend(), fetch attachments FROM database
  5. ✅ Send email with retrieved content

🎯 IMPLEMENTATION PLAN

Step 1: Add Content Column to Database (1 min)

Migration needed:

ALTER TABLE communication_attachment 
ADD COLUMN content TEXT;

Step 2: Save Content When Saving Attachments (2 min)

Update AttachmentHandlerService.saveAttachments():

await this.database
.insertInto("communicationAttachment")
.values({
communicationId,
filename: attachment.filename,
filePath: undefined,
fileUrl: attachment.fileUrl,
mimeType: attachment.mimeType,
sizeBytes: attachment.content ? Buffer.from(attachment.content, "base64").length : undefined,
content: attachment.content, // ← ADD THIS!
createdAt: undefined,
})
.execute();

Step 3: Exclude Attachments from Queue Payload (1 min)

Update CommunicationsService.send():

// Don't include attachments in queue (too large)
const { attachments, ...payloadWithoutAttachments } = data;

await this.queueService.enqueue({
communicationId: communication.id,
payload: {
...payloadWithoutAttachments, // ← No attachments!
content,
subject,
providerTemplateId,
communicationId: communication.id,
},
...
});

Step 4: Retrieve Attachments in executeSend() (3 min)

Update CommunicationsService.executeSend():

async executeSend(
payload: SendCommunicationDto & { communicationId: string },
): Promise<void> {
try {
// Fetch attachments from database (not in payload)
const dbAttachments = await this.attachmentHandler.getAttachments(
payload.communicationId,
);

if (dbAttachments && dbAttachments.length > 0) {
// Convert DB format to DTO format
payload.attachments = dbAttachments.map(att => ({
filename: att.filename,
content: att.content, // From database!
mimeType: att.mimeType,
}));

this.logger.log(
`Retrieved ${payload.attachments.length} attachment(s) from database`
);
}

// Send via adapter
const adapter = this.channelFactory.getAdapter(payload.channel);
const result = await adapter.send(payload);
...
}
}

⏱️ TIME ESTIMATE

  • Database migration: 1 minute
  • Code updates: 5 minutes
  • Testing: 2 minutes
  • Total: ~8 minutes

🎊 EXPECTED RESULT

After fix:

  1. ✅ Attachments saved to database WITH content
  2. ✅ Queue payload is smaller (no large base64)
  3. ✅ executeSend retrieves attachments from DB
  4. ✅ Email sent with REAL PDF attachment!

📋 NEXT STEPS

  1. Create database migration
  2. Update AttachmentHandlerService
  3. Update CommunicationsService.send()
  4. Update CommunicationsService.executeSend()
  5. Restart backend
  6. Test!

Ready to implement this fix? 🚀