docs: add Postgres migration plan and Kiro spec
- docs/guides/postgres-migration-plan.md: full migration manual with phases, port allocation, rollback plan, and timeline - .kiro/specs/postgres-migration/: requirements, design, and tasks - Replaces findings_json blob with individual indexed rows - Enables per-BU closed counts via SQL queries - Uses existing Postgres instance (port 5432), new cve_dashboard DB - Testing on port 3003, cutover to 3001 with 30s downtime
This commit is contained in:
@@ -781,6 +781,9 @@ async function syncFindings(db) {
|
||||
[allFindings.length, JSON.stringify(allFindings)]
|
||||
);
|
||||
|
||||
// Invalidate in-memory cache so next read parses fresh data
|
||||
invalidateFindingsCache();
|
||||
|
||||
console.log(`[Ivanti Findings] Sync complete — ${allFindings.length} findings`);
|
||||
|
||||
// Archive detection — compare previous vs current to detect disappeared/returned findings
|
||||
@@ -890,6 +893,18 @@ function dbAll(db, sql, params = []) {
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// In-memory findings cache — avoids re-parsing the large JSON blob on every request.
|
||||
// Invalidated on sync (when findings_json is updated).
|
||||
// ---------------------------------------------------------------------------
|
||||
let _findingsCache = null; // { findings, total, synced_at, sync_status, error_message }
|
||||
let _findingsCacheAt = null; // synced_at value when cache was built
|
||||
|
||||
function invalidateFindingsCache() {
|
||||
_findingsCache = null;
|
||||
_findingsCacheAt = null;
|
||||
}
|
||||
|
||||
function readState(db) {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.get(
|
||||
@@ -897,9 +912,21 @@ function readState(db) {
|
||||
(err, row) => {
|
||||
if (err) return reject(err);
|
||||
if (!row) return resolve({ total: 0, findings: [], synced_at: null, sync_status: 'never', error_message: null });
|
||||
|
||||
// Return cached if synced_at hasn't changed
|
||||
if (_findingsCache && _findingsCacheAt === row.synced_at) {
|
||||
return resolve({ ..._findingsCache });
|
||||
}
|
||||
|
||||
let findings = [];
|
||||
try { findings = JSON.parse(row.findings_json || '[]'); } catch (_) { /* leave empty */ }
|
||||
resolve({ total: row.total || 0, findings, synced_at: row.synced_at, sync_status: row.sync_status, error_message: row.error_message });
|
||||
const state = { total: row.total || 0, findings, synced_at: row.synced_at, sync_status: row.sync_status, error_message: row.error_message };
|
||||
|
||||
// Store in cache
|
||||
_findingsCache = state;
|
||||
_findingsCacheAt = row.synced_at;
|
||||
|
||||
resolve({ ...state });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user