fix: add missing email-status GET route, remove old schema validation on email POST
This commit is contained in:
@@ -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')) {
|
||||
|
||||
Reference in New Issue
Block a user