fix: add missing email-status GET route, remove old schema validation on email POST

This commit is contained in:
BizzleBot
2026-03-05 13:20:05 +00:00
parent e37b838718
commit 8c49fe1ec8
+31 -18
View File
@@ -272,49 +272,62 @@ router.get('/:id/pdf', async (req, res) => {
}
});
// ─────────────── GET /api/timesheets/:id/email-status ───────────────
router.get('/:id/email-status', async (req, res) => {
const { id } = req.params;
const timesheet = await req.prisma.timesheet.findUnique({ where: { id }, select: { userId: true } });
if (!timesheet) return res.status(404).json({ error: 'Not found' });
const isAdmin = req.user.role === 'admin' || req.user.role === 'super_admin';
if (timesheet.userId !== req.user.id && !isAdmin) return res.status(403).json({ error: 'Access denied' });
const limit = checkEmailLimit(id);
res.json({ count: limit.count, max: MAX_EMAILS, cooldownMs: limit.cooldownMs, allowed: limit.allowed });
});
// ─────────────── POST /api/timesheets/:id/email ───────────────
router.post('/:id/email', validateBody(emailTimesheetSchema), async (req, res) => {
router.post('/:id/email', async (req, res) => {
try {
const { id } = req.params;
const { to, subject, message } = req.validated;
const timesheet = await req.prisma.timesheet.findUnique({
where: { id },
include: { user: { select: { id: true, name: true, email: true } } },
});
if (!timesheet) {
return res.status(404).json({ error: 'Timesheet not found' });
}
if (!timesheet) return res.status(404).json({ error: 'Timesheet not found' });
// Only owner or admin
const isAdmin = req.user.role === 'admin' || req.user.role === 'super_admin';
if (timesheet.userId !== req.user.id && !isAdmin) {
return res.status(403).json({ error: 'Access denied' });
}
// Rate limit check
const limit = checkEmailLimit(id);
if (!limit.allowed) {
const minutesLeft = Math.ceil(limit.cooldownMs / 60000);
if (limit.count >= MAX_EMAILS) {
return res.status(429).json({ error: `Maximum ${MAX_EMAILS} emails per timesheet reached.`, count: limit.count, max: MAX_EMAILS });
}
return res.status(429).json({ error: `Please wait ${minutesLeft} minute${minutesLeft !== 1 ? 's' : ''} before sending again.`, cooldownMs: limit.cooldownMs, count: limit.count });
}
const weekStart = timesheet.weekStart.toISOString().split('T')[0];
const data = await loadTimesheetData(req.prisma, timesheet.userId, weekStart);
const pdfBuffer = await generateTimesheetPDF({
userName: data.user.name,
weekStart,
entries: data.entries,
status: timesheet.status,
});
const pdfBuffer = await generateTimesheetPDF({ userName: data.user.name, weekStart, entries: data.entries, status: timesheet.status });
const pdfFilename = buildPdfFilename(data.user.name, weekStart);
const adminEmail = process.env.ADMIN_EMAIL || 'Office@CoastalContractingFL.com';
await sendTimesheetEmail({
to,
subject: subject || `Timesheet – ${data.user.name} – Week of ${weekStart}`,
message,
to: adminEmail,
subject: `Timesheet – ${data.user.name} – Week of ${weekStart}`,
message: `${data.user.name} has submitted their timesheet for the week of ${weekStart}.`,
pdfBuffer,
pdfFilename,
fromName: data.user.name,
});
res.json({ message: `Timesheet emailed to ${to}` });
recordEmailSent(id);
const newCount = limit.count + 1;
res.json({ message: `Timesheet emailed to ${adminEmail}`, count: newCount, max: MAX_EMAILS, remaining: MAX_EMAILS - newCount });
} catch (err) {
console.error('Email timesheet error:', err);
if (err.message && err.message.includes('SMTP')) {