const nocoDB = require('../services/nocodb'); const { validateEmail } = require('../utils/validators'); class CustomRecipientsController { /** * Get all custom recipients for a campaign */ async getRecipientsByCampaign(req, res, next) { try { const { slug } = req.params; // Get campaign first to verify it exists and get ID const campaign = await nocoDB.getCampaignBySlug(slug); if (!campaign) { return res.status(404).json({ error: 'Campaign not found' }); } // Check if custom recipients are enabled for this campaign // Use NocoDB column title, not camelCase if (!campaign['Allow Custom Recipients']) { return res.json({ recipients: [], message: 'Custom recipients not enabled for this campaign' }); } // Get custom recipients for this campaign using slug const recipients = await nocoDB.getCustomRecipientsBySlug(slug); res.json({ success: true, recipients: recipients || [], count: recipients ? recipients.length : 0 }); } catch (error) { console.error('Error fetching custom recipients:', error); next(error); } } /** * Create a single custom recipient */ async createRecipient(req, res, next) { try { const { slug } = req.params; const { recipient_name, recipient_email, recipient_title, recipient_organization, notes } = req.body; // Validate required fields if (!recipient_name || !recipient_email) { return res.status(400).json({ error: 'Recipient name and email are required' }); } // Validate email format if (!validateEmail(recipient_email)) { return res.status(400).json({ error: 'Invalid email format' }); } // Get campaign to verify it exists and get ID const campaign = await nocoDB.getCampaignBySlug(slug); if (!campaign) { return res.status(404).json({ error: 'Campaign not found' }); } // Check if custom recipients are enabled for this campaign // Use NocoDB column title, not camelCase field name if (!campaign['Allow Custom Recipients']) { console.warn('Custom recipients not enabled. Campaign data:', campaign); return res.status(403).json({ error: 'Custom recipients not enabled for this campaign' }); } // Create the recipient // Use campaign.ID (NocoDB system field) not campaign.id const recipientData = { campaign_id: campaign.ID, campaign_slug: slug, recipient_name, recipient_email, recipient_title: recipient_title || null, recipient_organization: recipient_organization || null, notes: notes || null, is_active: true }; const newRecipient = await nocoDB.createCustomRecipient(recipientData); res.status(201).json({ success: true, recipient: newRecipient, message: 'Recipient created successfully' }); } catch (error) { console.error('Error creating custom recipient:', error); next(error); } } /** * Bulk create custom recipients */ async bulkCreateRecipients(req, res, next) { try { const { slug } = req.params; const { recipients } = req.body; // Validate input if (!Array.isArray(recipients) || recipients.length === 0) { return res.status(400).json({ error: 'Recipients array is required and must not be empty' }); } // Get campaign to verify it exists and get ID const campaign = await nocoDB.getCampaignBySlug(slug); if (!campaign) { return res.status(404).json({ error: 'Campaign not found' }); } // Check if custom recipients are enabled for this campaign if (!campaign.allow_custom_recipients) { return res.status(403).json({ error: 'Custom recipients not enabled for this campaign' }); } const results = { success: [], failed: [], total: recipients.length }; // Process each recipient for (const recipient of recipients) { try { // Validate required fields if (!recipient.recipient_name || !recipient.recipient_email) { results.failed.push({ recipient, error: 'Missing required fields (name or email)' }); continue; } // Validate email format if (!validateEmail(recipient.recipient_email)) { results.failed.push({ recipient, error: 'Invalid email format' }); continue; } // Create the recipient const recipientData = { campaign_id: campaign.id, campaign_slug: slug, recipient_name: recipient.recipient_name, recipient_email: recipient.recipient_email, recipient_title: recipient.recipient_title || null, recipient_organization: recipient.recipient_organization || null, notes: recipient.notes || null, is_active: true }; const newRecipient = await nocoDB.createCustomRecipient(recipientData); results.success.push(newRecipient); } catch (error) { results.failed.push({ recipient, error: error.message || 'Unknown error' }); } } res.status(201).json({ success: true, results, message: `Successfully created ${results.success.length} of ${results.total} recipients` }); } catch (error) { console.error('Error bulk creating custom recipients:', error); next(error); } } /** * Update a custom recipient */ async updateRecipient(req, res, next) { try { const { slug, id } = req.params; const { recipient_name, recipient_email, recipient_title, recipient_organization, notes, is_active } = req.body; // Validate email if provided if (recipient_email && !validateEmail(recipient_email)) { return res.status(400).json({ error: 'Invalid email format' }); } // Get campaign to verify it exists const campaign = await nocoDB.getCampaignBySlug(slug); if (!campaign) { return res.status(404).json({ error: 'Campaign not found' }); } // Build update data (only include provided fields) const updateData = {}; if (recipient_name !== undefined) updateData.recipient_name = recipient_name; if (recipient_email !== undefined) updateData.recipient_email = recipient_email; if (recipient_title !== undefined) updateData.recipient_title = recipient_title; if (recipient_organization !== undefined) updateData.recipient_organization = recipient_organization; if (notes !== undefined) updateData.notes = notes; if (is_active !== undefined) updateData.is_active = is_active; // Update the recipient const updatedRecipient = await nocoDB.updateCustomRecipient(id, updateData); if (!updatedRecipient) { return res.status(404).json({ error: 'Recipient not found' }); } res.json({ success: true, recipient: updatedRecipient, message: 'Recipient updated successfully' }); } catch (error) { console.error('Error updating custom recipient:', error); next(error); } } /** * Delete a custom recipient */ async deleteRecipient(req, res, next) { try { const { slug, id } = req.params; // Get campaign to verify it exists const campaign = await nocoDB.getCampaignBySlug(slug); if (!campaign) { return res.status(404).json({ error: 'Campaign not found' }); } // Delete the recipient const deleted = await nocoDB.deleteCustomRecipient(id); if (!deleted) { return res.status(404).json({ error: 'Recipient not found' }); } res.json({ success: true, message: 'Recipient deleted successfully' }); } catch (error) { console.error('Error deleting custom recipient:', error); next(error); } } /** * Delete all custom recipients for a campaign */ async deleteAllRecipients(req, res, next) { try { const { slug } = req.params; // Get campaign to verify it exists and get ID const campaign = await nocoDB.getCampaignBySlug(slug); if (!campaign) { return res.status(404).json({ error: 'Campaign not found' }); } // Delete all recipients for this campaign const deletedCount = await nocoDB.deleteCustomRecipientsByCampaign(campaign.id); res.json({ success: true, deletedCount, message: `Successfully deleted ${deletedCount} recipient(s)` }); } catch (error) { console.error('Error deleting all custom recipients:', error); next(error); } } } module.exports = new CustomRecipientsController();