Sortieungsoptionen in Verwaltung, Umstellung der PDF generation auf generierung bei abgabe und ablage auf dem Sateisystem um einen Festen Stand zu garantieren

This commit is contained in:
2026-03-17 16:20:36 +01:00
parent 2aa4e6f037
commit a92694f693
8 changed files with 386 additions and 14 deletions

View File

@@ -2,9 +2,16 @@
const { db } = require('../database');
const { requireAuth, requireVerwaltung } = require('../middleware/auth');
const { generatePDF } = require('../services/pdf-service');
const { generatePDFToBuffer } = require('../services/pdf-service');
const { getHolidaysForDateRange } = require('../services/feiertage-service');
const { hasRole } = require('../helpers/utils');
const fs = require('fs');
const {
fileExists,
savePdfBufferAtomic,
resolvePdfLocationFromTimesheetRow,
resolveAbsolutePathFromRelative,
} = require('../services/pdf-storage-service');
/** Plausibilitätsprüfung Projektnummer: 7 Ziffern, beginnt mit 5, dann YY (Jahr), dann 4 freie Ziffern (z.B. 5260001). */
function isValidProjectNumber(value) {
@@ -553,18 +560,90 @@ function registerTimesheetRoutes(app) {
const isVerwaltung = hasRole(req, 'verwaltung') || hasRole(req, 'admin');
// Prüfe ob User Verwaltung/Admin ist oder ob das Timesheet dem User gehört
db.get(`SELECT user_id FROM weekly_timesheets WHERE id = ?`, [timesheetId], (err, timesheet) => {
if (err || !timesheet) {
return res.status(404).send('Stundenzettel nicht gefunden');
db.get(
`SELECT wt.id, wt.user_id, wt.week_start, wt.week_end, wt.version, wt.submitted_at, wt.pdf_path,
u.firstname, u.lastname
FROM weekly_timesheets wt
JOIN users u ON wt.user_id = u.id
WHERE wt.id = ?`,
[timesheetId],
async (err, timesheet) => {
if (err || !timesheet) {
return res.status(404).send('Stundenzettel nicht gefunden');
}
// Zugriff erlauben wenn Verwaltung/Admin ODER wenn Timesheet dem User gehört
if (!(isVerwaltung || timesheet.user_id === userId)) {
return res.status(403).send('Zugriff verweigert');
}
const inline = req.query.inline === 'true';
// Zielpfad bestimmen: DB-Pfad bevorzugen, sonst berechnen
const computedLocation = resolvePdfLocationFromTimesheetRow(timesheet);
const relativePath = timesheet.pdf_path ? String(timesheet.pdf_path) : computedLocation.relativePath;
const absolutePath = timesheet.pdf_path ? resolveAbsolutePathFromRelative(relativePath) : computedLocation.absolutePath;
const filename = computedLocation.filename;
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('X-Content-Type-Options', 'nosniff');
if (inline) {
res.setHeader('Content-Disposition', `inline; filename="${filename}"`);
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
} else {
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
// Marker setzen, dass PDF heruntergeladen wurde (nur bei Download, nicht bei Vorschau)
const downloadedBy = req.session.userId;
if (downloadedBy) {
db.run(
`UPDATE weekly_timesheets
SET pdf_downloaded_at = CURRENT_TIMESTAMP,
pdf_downloaded_by = ?
WHERE id = ?`,
[downloadedBy, timesheetId],
(updateErr) => {
if (updateErr) {
console.error('Fehler beim Setzen des Download-Markers:', updateErr);
}
}
);
}
}
try {
const exists = await fileExists(absolutePath);
if (exists) {
const stream = fs.createReadStream(absolutePath);
stream.on('error', (streamErr) => {
console.error('Fehler beim Lesen der PDF-Datei:', streamErr);
if (!res.headersSent) res.status(500);
res.end('Fehler beim Lesen der PDF-Datei');
});
return stream.pipe(res);
}
// Nicht vorhanden: versionstreu generieren, speichern, dann senden
const pdfBuffer = await generatePDFToBuffer(timesheetId, req);
await savePdfBufferAtomic(absolutePath, pdfBuffer);
// Relativen Pfad in DB speichern (optional, Fehler nicht fatal)
if (!timesheet.pdf_path) {
db.run('UPDATE weekly_timesheets SET pdf_path = ? WHERE id = ?', [relativePath, timesheetId], (pathErr) => {
if (pathErr) {
console.warn('Warnung beim Speichern des PDF-Pfads:', pathErr.message);
}
});
}
return res.end(pdfBuffer);
} catch (e) {
console.error('Fehler beim Generieren/Speichern der PDF:', e);
if (!res.headersSent) res.status(500);
return res.end('Fehler beim Erstellen des PDFs');
}
}
// Zugriff erlauben wenn Verwaltung/Admin ODER wenn Timesheet dem User gehört
if (isVerwaltung || timesheet.user_id === userId) {
generatePDF(timesheetId, req, res);
} else {
res.status(403).send('Zugriff verweigert');
}
});
);
});
}