Add Reporting page with Ivanti host findings table
Backend: - New route /api/ivanti/findings (GET cached data, POST /sync, PUT /:id/note) - Fetches all pages of hostFinding/search filtered to NTS-AEO groups, severity 8.5-9.9, Open state - SQLite cache (ivanti_findings_cache) stores slimmed findings across syncs - Separate ivanti_finding_notes table persists user notes by finding ID - Daily auto-sync on startup + 24h interval, manual sync endpoint - Notes capped at 255 chars server-side Frontend (ReportingPage): - Panel 1: Metric graphs placeholder (full width, amber theme) - Panel 2: Sortable findings table (all columns click-to-sort with ASC/DESC toggle) - Columns: Severity (color-coded badge), Title, Host, IP, DNS, SLA, Discovered, Last Found, Source, Notes - Notes column: inline editable input, saves on blur via PUT endpoint - Sync button with spinner, last-synced timestamp, error banner Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
58
backend/migrations/add_ivanti_findings_tables.js
Normal file
58
backend/migrations/add_ivanti_findings_tables.js
Normal file
@@ -0,0 +1,58 @@
|
||||
// Migration: Add ivanti_findings_cache and ivanti_finding_notes tables
|
||||
const sqlite3 = require('sqlite3').verbose();
|
||||
const path = require('path');
|
||||
|
||||
const dbPath = path.join(__dirname, '..', 'cve_database.db');
|
||||
const db = new sqlite3.Database(dbPath);
|
||||
|
||||
console.log('Starting Ivanti findings tables migration...');
|
||||
|
||||
db.serialize(() => {
|
||||
// Cache table — single row holding the latest sync result
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS ivanti_findings_cache (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
total INTEGER DEFAULT 0,
|
||||
findings_json TEXT DEFAULT '[]',
|
||||
synced_at DATETIME,
|
||||
sync_status TEXT DEFAULT 'never',
|
||||
error_message TEXT
|
||||
)
|
||||
`, (err) => {
|
||||
if (err) console.error('Error creating findings cache table:', err);
|
||||
else console.log('✓ ivanti_findings_cache table created');
|
||||
});
|
||||
|
||||
db.run(`
|
||||
INSERT OR IGNORE INTO ivanti_findings_cache (id, total, findings_json, sync_status)
|
||||
VALUES (1, 0, '[]', 'never')
|
||||
`, (err) => {
|
||||
if (err) console.error('Error seeding findings cache row:', err);
|
||||
else console.log('✓ ivanti_findings_cache row seeded');
|
||||
});
|
||||
|
||||
// Notes table — one row per finding, persists across cache refreshes
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS ivanti_finding_notes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
finding_id TEXT NOT NULL UNIQUE,
|
||||
note TEXT NOT NULL DEFAULT '',
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`, (err) => {
|
||||
if (err) console.error('Error creating finding notes table:', err);
|
||||
else console.log('✓ ivanti_finding_notes table created');
|
||||
});
|
||||
|
||||
db.run(`
|
||||
CREATE INDEX IF NOT EXISTS idx_finding_notes_finding_id
|
||||
ON ivanti_finding_notes(finding_id)
|
||||
`, (err) => {
|
||||
if (err) console.error('Error creating notes index:', err);
|
||||
else console.log('✓ finding_id index created');
|
||||
});
|
||||
});
|
||||
|
||||
db.close(() => {
|
||||
console.log('Migration complete!');
|
||||
});
|
||||
Reference in New Issue
Block a user