Initial commit

Next.js + Express event management app for Hope Family Church.
This commit is contained in:
2026-07-23 15:26:47 +02:00
commit 3d381944d2
246 changed files with 57565 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
const fs = require('fs');
const path = require('path');
const { v4: uuidv4 } = require('uuid');
const DATA_DIR = path.join(__dirname, '..', '..', 'data');
const QUEUE_PATH = path.join(DATA_DIR, 'scheduled-emails.json');
function ensureStore() {
try {
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });
if (!fs.existsSync(QUEUE_PATH)) fs.writeFileSync(QUEUE_PATH, JSON.stringify({ jobs: [] }, null, 2), 'utf-8');
} catch (e) {
// Best-effort; throws will surface to caller
}
}
function loadAll() {
ensureStore();
try {
const raw = fs.readFileSync(QUEUE_PATH, 'utf-8');
const data = JSON.parse(raw);
const jobs = Array.isArray(data?.jobs) ? data.jobs : [];
return jobs;
} catch (e) {
return [];
}
}
function saveAll(jobs) {
ensureStore();
const payload = { jobs: Array.isArray(jobs) ? jobs : [] };
// Simple atomic-ish write
const tmp = QUEUE_PATH + '.tmp';
fs.writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf-8');
fs.renameSync(tmp, QUEUE_PATH);
}
/**
* Add a scheduled job
* @param {object} job { id?, eventId, createdById, scheduledAt: ISO string, payload: body for emailEventAttendees }
*/
function addJob(job) {
const now = new Date();
const id = job.id || uuidv4();
const rec = {
id,
eventId: job.eventId || null,
broadcast: !!job.broadcast,
createdById: job.createdById || null,
scheduledAt: job.scheduledAt,
createdAt: now.toISOString(),
status: 'queued', // queued | sending | sent | error
attempts: 0,
lastError: null,
payload: job.payload || {},
};
const jobs = loadAll();
jobs.push(rec);
saveAll(jobs);
return rec;
}
function listJobs(filter = {}) {
const jobs = loadAll();
// Basic filter support
return jobs.filter(j => {
if (filter.status && j.status !== filter.status) return false;
if (filter.eventId && j.eventId !== filter.eventId) return false;
return true;
});
}
function getDueJobs(now = new Date()) {
const jobs = loadAll();
const t = now instanceof Date ? now : new Date(now);
return jobs.filter(j => j.status === 'queued' && new Date(j.scheduledAt).getTime() <= t.getTime());
}
function updateJob(id, patch) {
const jobs = loadAll();
const idx = jobs.findIndex(j => j.id === id);
if (idx === -1) return null;
jobs[idx] = { ...jobs[idx], ...patch };
saveAll(jobs);
return jobs[idx];
}
function getJob(id) {
const jobs = loadAll();
return jobs.find(j => j.id === id) || null;
}
function deleteJob(id) {
const jobs = loadAll();
const filtered = jobs.filter(j => j.id !== id);
if (filtered.length === jobs.length) return false;
saveAll(filtered);
return true;
}
module.exports = {
addJob,
listJobs,
getDueJobs,
updateJob,
getJob,
deleteJob,
};