Initial commit
Next.js + Express event management app for Hope Family Church.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
const { listJobs, getJob, updateJob, deleteJob } = require('../utils/scheduledEmails');
|
||||
|
||||
// Normalize job for client UI
|
||||
function toClient(job) {
|
||||
const kind = job.broadcast ? 'broadcast' : (job.eventId ? 'attendees' : 'unknown');
|
||||
const subject = job?.payload?.subject || '';
|
||||
const html = job?.payload?.html || '';
|
||||
const text = job?.payload?.text || '';
|
||||
return {
|
||||
id: job.id,
|
||||
kind,
|
||||
eventId: job.eventId || null,
|
||||
broadcast: !!job.broadcast,
|
||||
scheduledAt: job.scheduledAt,
|
||||
createdAt: job.createdAt,
|
||||
status: job.status,
|
||||
attempts: job.attempts || 0,
|
||||
sentAt: job.sentAt || null,
|
||||
lastError: job.lastError || null,
|
||||
subject,
|
||||
hasHtml: !!html,
|
||||
hasText: !!text,
|
||||
};
|
||||
}
|
||||
|
||||
// GET /api/scheduled-emails
|
||||
// Returns jobs excluding emails sent more than a week ago
|
||||
const listScheduledEmails = async (req, res) => {
|
||||
try {
|
||||
const raw = listJobs();
|
||||
const now = new Date();
|
||||
const weekMs = 7 * 24 * 60 * 60 * 1000;
|
||||
const filtered = raw.filter(j => {
|
||||
if (j.status === 'sent' && j.sentAt) {
|
||||
const sentAt = new Date(j.sentAt).getTime();
|
||||
return (now.getTime() - sentAt) <= weekMs;
|
||||
}
|
||||
// Include queued, sending, error by default
|
||||
return true;
|
||||
})
|
||||
// Provide most-relevant first: queued -> sending -> error -> recent sent
|
||||
.sort((a, b) => {
|
||||
const order = { queued: 0, sending: 1, error: 2, sent: 3 };
|
||||
const oa = order[a.status] ?? 99;
|
||||
const ob = order[b.status] ?? 99;
|
||||
if (oa !== ob) return oa - ob;
|
||||
// Then by scheduledAt asc
|
||||
return new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime();
|
||||
})
|
||||
.map(toClient);
|
||||
|
||||
return res.json({ jobs: filtered });
|
||||
} catch (e) {
|
||||
return res.status(400).json({ message: e?.message || 'Failed to list scheduled emails' });
|
||||
}
|
||||
};
|
||||
|
||||
// PATCH /api/scheduled-emails/:id
|
||||
// Allows editing scheduledAt, subject, html/text on queued jobs only
|
||||
const updateScheduledEmail = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const job = getJob(id);
|
||||
if (!job) return res.status(404).json({ message: 'Job not found' });
|
||||
if (job.status !== 'queued') return res.status(400).json({ message: 'Only queued jobs can be edited' });
|
||||
|
||||
const { scheduledAt, subject, html, text } = req.body || {};
|
||||
|
||||
const patch = {};
|
||||
if (scheduledAt) {
|
||||
const when = new Date(scheduledAt);
|
||||
if (isNaN(when.getTime())) return res.status(400).json({ message: 'scheduledAt must be a valid ISO date-time' });
|
||||
patch.scheduledAt = when.toISOString();
|
||||
}
|
||||
if (subject != null || html != null || text != null) {
|
||||
const payload = { ...(job.payload || {}) };
|
||||
if (subject != null) payload.subject = subject;
|
||||
if (html != null || text != null) {
|
||||
// If html provided explicitly, set html; if text provided, set text
|
||||
if (html != null) payload.html = html;
|
||||
if (text != null) payload.text = text;
|
||||
}
|
||||
patch.payload = payload;
|
||||
}
|
||||
|
||||
const updated = updateJob(id, patch);
|
||||
return res.json({ message: 'Updated', job: toClient(updated) });
|
||||
} catch (e) {
|
||||
return res.status(400).json({ message: e?.message || 'Failed to update job' });
|
||||
}
|
||||
};
|
||||
|
||||
// DELETE /api/scheduled-emails/:id
|
||||
// Only queued jobs can be removed
|
||||
const deleteScheduledEmail = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const job = getJob(id);
|
||||
if (!job) return res.status(404).json({ message: 'Job not found' });
|
||||
if (job.status !== 'queued') return res.status(400).json({ message: 'Only queued jobs can be deleted' });
|
||||
const ok = deleteJob(id);
|
||||
if (!ok) return res.status(404).json({ message: 'Job not found' });
|
||||
return res.json({ message: 'Deleted' });
|
||||
} catch (e) {
|
||||
return res.status(400).json({ message: e?.message || 'Failed to delete job' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { listScheduledEmails, updateScheduledEmail, deleteScheduledEmail };
|
||||
Reference in New Issue
Block a user