// Migration: Add atlas_known column to atlas_action_plans_cache // // Distinguishes between hosts Atlas actively tracks (atlas_known = true) // and hosts that were synced but Atlas has no data for (atlas_known = false). // The badge only renders for atlas_known hosts, preventing noise from BUs // not covered by Atlas. // // Safe to re-run — uses ADD COLUMN IF NOT EXISTS pattern. // // Usage: node backend/migrations/add_atlas_known_column.js const pool = require('../db'); async function migrate() { console.log('Starting atlas_known column migration...'); // Add column (IF NOT EXISTS not supported for ADD COLUMN in all PG versions, use DO block) await pool.query(` DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_name = 'atlas_action_plans_cache' AND column_name = 'atlas_known' ) THEN ALTER TABLE atlas_action_plans_cache ADD COLUMN atlas_known BOOLEAN NOT NULL DEFAULT FALSE; END IF; END $$; `); console.log('✓ atlas_known column ready'); // Backfill: mark hosts that have at least one plan as atlas_known = true const { rowCount } = await pool.query(` UPDATE atlas_action_plans_cache SET atlas_known = true WHERE has_action_plan = true `); console.log(`✓ Backfilled ${rowCount} rows with atlas_known = true (hosts with plans)`); // Also mark hosts belonging to managed BUs as atlas_known // These are the BUs Atlas is supposed to cover const managedBUs = (process.env.IVANTI_MANAGED_BUS || 'NTS-AEO-ACCESS-ENG,NTS-AEO-STEAM') .split(',').map(b => b.trim()).filter(Boolean); const patterns = managedBUs.map(b => `%${b}%`); const { rowCount: buCount } = await pool.query(` UPDATE atlas_action_plans_cache SET atlas_known = true WHERE host_id IN ( SELECT DISTINCT host_id FROM ivanti_findings WHERE bu_ownership ILIKE ANY($1::text[]) ) `, [patterns]); console.log(`✓ Backfilled ${buCount} rows for managed BU hosts as atlas_known = true`); console.log('Migration complete.'); } migrate() .then(() => { pool.end(); }) .catch((err) => { console.error('Migration failed:', err); pool.end(); process.exit(1); });