const express = require('express'); const path = require('path'); const fs = require('fs'); const { requireAuth, requireRole } = require('../middleware/auth'); const logAudit = require('../helpers/auditLog'); const { processVulnerabilityReport } = require('../helpers/excelProcessor'); function createWeeklyReportsRouter(db, upload) { const router = express.Router(); // Helper to sanitize filename function sanitizePathSegment(segment) { if (!segment || typeof segment !== 'string') return ''; return segment .replace(/\0/g, '') .replace(/\.\./g, '') .replace(/[\/\\]/g, '') .trim(); } // Helper to generate week label function getWeekLabel(date) { const now = new Date(); const uploadDate = new Date(date); const daysDiff = Math.floor((now - uploadDate) / (1000 * 60 * 60 * 24)); if (daysDiff < 7) { return "This week's report"; } else if (daysDiff < 14) { return "Last week's report"; } else { const month = uploadDate.getMonth() + 1; const day = uploadDate.getDate(); const year = uploadDate.getFullYear(); return `Week of ${month.toString().padStart(2, '0')}/${day.toString().padStart(2, '0')}/${year}`; } } // POST /api/weekly-reports/upload - Upload and process vulnerability report router.post('/upload', requireAuth(db), requireRole(db, 'editor', 'admin'), upload.single('file'), async (req, res) => { const uploadedFile = req.file; if (!uploadedFile) { return res.status(400).json({ error: 'No file uploaded' }); } // Validate file extension const ext = path.extname(uploadedFile.originalname).toLowerCase(); if (ext !== '.xlsx') { fs.unlinkSync(uploadedFile.path); // Clean up temp file return res.status(400).json({ error: 'Only .xlsx files are allowed' }); } const timestamp = Date.now(); const sanitizedName = sanitizePathSegment(uploadedFile.originalname); const reportsDir = path.join(__dirname, '..', 'uploads', 'weekly_reports'); // Create directory if it doesn't exist if (!fs.existsSync(reportsDir)) { fs.mkdirSync(reportsDir, { recursive: true }); } const originalFilename = `${timestamp}_original_${sanitizedName}`; const processedFilename = `${timestamp}_processed_${sanitizedName}`; const originalPath = path.join(reportsDir, originalFilename); const processedPath = path.join(reportsDir, processedFilename); try { // Move uploaded file to permanent location fs.renameSync(uploadedFile.path, originalPath); // Process the file with Python script const result = await processVulnerabilityReport(originalPath, processedPath); const uploadDate = new Date().toISOString().split('T')[0]; // Update previous current reports to not current db.run('UPDATE weekly_reports SET is_current = 0 WHERE is_current = 1', (err) => { if (err) { console.error('Error updating previous current reports:', err); } }); // Insert new report record const insertSql = ` INSERT INTO weekly_reports ( upload_date, week_label, original_filename, processed_filename, original_file_path, processed_file_path, row_count_original, row_count_processed, uploaded_by, is_current ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1) `; const weekLabel = getWeekLabel(uploadDate); db.run( insertSql, [ uploadDate, weekLabel, sanitizedName, processedFilename, originalPath, processedPath, result.original_rows, result.processed_rows, req.user.id ], function (err) { if (err) { console.error('Error inserting weekly report:', err); return res.status(500).json({ error: 'Failed to save report metadata' }); } // Log audit entry logAudit( db, req.user.id, req.user.username, 'UPLOAD_WEEKLY_REPORT', 'weekly_reports', this.lastID, JSON.stringify({ filename: sanitizedName, rows: result.processed_rows }), req.ip ); res.json({ success: true, id: this.lastID, original_rows: result.original_rows, processed_rows: result.processed_rows, week_label: weekLabel }); } ); } catch (error) { // Clean up files on error if (fs.existsSync(originalPath)) fs.unlinkSync(originalPath); if (fs.existsSync(processedPath)) fs.unlinkSync(processedPath); console.error('Error processing vulnerability report:', error); res.status(500).json({ error: error.message || 'Failed to process report' }); } }); // GET /api/weekly-reports - List all reports router.get('/', requireAuth(db), (req, res) => { const sql = ` SELECT id, upload_date, week_label, original_filename, processed_filename, row_count_original, row_count_processed, is_current, uploaded_at FROM weekly_reports ORDER BY upload_date DESC, uploaded_at DESC `; db.all(sql, [], (err, rows) => { if (err) { console.error('Error fetching weekly reports:', err); return res.status(500).json({ error: 'Failed to fetch reports' }); } res.json(rows); }); }); // GET /api/weekly-reports/:id/download/:type - Download report file router.get('/:id/download/:type', requireAuth(db), (req, res) => { const { id, type } = req.params; if (type !== 'original' && type !== 'processed') { return res.status(400).json({ error: 'Invalid download type. Use "original" or "processed"' }); } const sql = `SELECT original_file_path, processed_file_path, original_filename FROM weekly_reports WHERE id = ?`; db.get(sql, [id], (err, row) => { if (err) { console.error('Error fetching report:', err); return res.status(500).json({ error: 'Failed to fetch report' }); } if (!row) { return res.status(404).json({ error: 'Report not found' }); } const filePath = type === 'original' ? row.original_file_path : row.processed_file_path; if (!fs.existsSync(filePath)) { return res.status(404).json({ error: 'File not found on disk' }); } // Log audit entry logAudit( db, req.user.id, req.user.username, 'DOWNLOAD_WEEKLY_REPORT', 'weekly_reports', id, JSON.stringify({ type }), req.ip ); const downloadName = type === 'original' ? row.original_filename : row.original_filename.replace('.xlsx', '_processed.xlsx'); res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); res.setHeader('Content-Disposition', `attachment; filename="${downloadName}"`); res.sendFile(filePath); }); }); // DELETE /api/weekly-reports/:id - Delete report (admin only) router.delete('/:id', requireAuth(db), requireRole(db, 'admin'), (req, res) => { const { id } = req.params; const sql = 'SELECT original_file_path, processed_file_path FROM weekly_reports WHERE id = ?'; db.get(sql, [id], (err, row) => { if (err) { console.error('Error fetching report for deletion:', err); return res.status(500).json({ error: 'Failed to fetch report' }); } if (!row) { return res.status(404).json({ error: 'Report not found' }); } // Delete database record db.run('DELETE FROM weekly_reports WHERE id = ?', [id], (err) => { if (err) { console.error('Error deleting report:', err); return res.status(500).json({ error: 'Failed to delete report' }); } // Delete files if (fs.existsSync(row.original_file_path)) { fs.unlinkSync(row.original_file_path); } if (fs.existsSync(row.processed_file_path)) { fs.unlinkSync(row.processed_file_path); } // Log audit entry logAudit( db, req.user.id, req.user.username, 'DELETE_WEEKLY_REPORT', 'weekly_reports', id, null, req.ip ); res.json({ success: true }); }); }); }); return router; } module.exports = createWeeklyReportsRouter;