// Ivanti Archive Routes — list, stats, and transition history for archived findings const express = require('express'); const VALID_STATES = ['ACTIVE', 'ARCHIVED', 'RETURNED', 'CLOSED']; function createIvantiArchiveRouter(db, requireAuth) { const router = express.Router(); // All routes require authentication router.use(requireAuth(db)); // GET / — List archive records with optional ?state= filter router.get('/', async (req, res) => { const { state } = req.query; if (state && !VALID_STATES.includes(state)) { return res.status(400).json({ error: 'Invalid state parameter. Valid values: ACTIVE, ARCHIVED, RETURNED, CLOSED' }); } try { let query = 'SELECT * FROM ivanti_finding_archives'; const params = []; if (state) { query += ' WHERE current_state = ?'; params.push(state); } query += ' ORDER BY last_transition_at DESC'; const archives = await new Promise((resolve, reject) => { db.all(query, params, (err, rows) => { if (err) reject(err); else resolve(rows || []); }); }); res.json({ archives, total: archives.length }); } catch (err) { console.error('Archive list error:', err); res.status(500).json({ error: 'Failed to fetch archive records' }); } }); // GET /stats — Summary counts by state router.get('/stats', async (req, res) => { try { const rows = await new Promise((resolve, reject) => { db.all( `SELECT current_state, COUNT(*) as count FROM ivanti_finding_archives GROUP BY current_state`, (err, rows) => { if (err) reject(err); else resolve(rows || []); } ); }); const stats = { ACTIVE: 0, ARCHIVED: 0, RETURNED: 0, CLOSED: 0 }; let total = 0; for (const row of rows) { if (stats.hasOwnProperty(row.current_state)) { stats[row.current_state] = row.count; } total += row.count; } res.json({ ...stats, total }); } catch (err) { console.error('Archive stats error:', err); res.status(500).json({ error: 'Failed to fetch archive stats' }); } }); // GET /:findingId/history — Transition history for a finding router.get('/:findingId/history', async (req, res) => { const { findingId } = req.params; try { const archive = await new Promise((resolve, reject) => { db.get( 'SELECT id FROM ivanti_finding_archives WHERE finding_id = ?', [findingId], (err, row) => { if (err) reject(err); else resolve(row); } ); }); if (!archive) { return res.json({ finding_id: findingId, transitions: [] }); } const transitions = await new Promise((resolve, reject) => { db.all( `SELECT * FROM ivanti_archive_transitions WHERE archive_id = ? ORDER BY transitioned_at DESC`, [archive.id], (err, rows) => { if (err) reject(err); else resolve(rows || []); } ); }); res.json({ finding_id: findingId, transitions }); } catch (err) { console.error('Archive history error:', err); res.status(500).json({ error: 'Failed to fetch transition history' }); } }); return router; } module.exports = createIvantiArchiveRouter;