Skip to main content
THREAT LEVEL: ELEVATED ·
Loading live feed…
· --:--:--
● N new articles fetched press R to reload
SEV
SOURCE
TITLE
TIME
Connecting to feed...
No articles in this panel
Try refreshing or check another panel
SECFEED
Admin Panel
// Feed Source Management
// Security & Audit
TOTAL FEEDS
ENABLED
ERRORS
TOTAL ARTICLES
// Add new feed source
// Feeds
Loading...

Edit Feed Source

BACKEND: CONNECTED
LAST FETCH:
NEXT FETCH:
1-9 panels
R refresh
S save
ESC close
↑↓ navigate
tag. // Depends on: currentUser, checkAuth, showToast, ALL_INTERESTS, loadVendorsList, allVendorsList const OB_ROLES = [ { key: 'security-engineer', label: 'Security Engineer', icon: '🛡', defaultVendors: ['microsoft','cisco','palo-alto','crowdstrike','fortinet'] }, { key: 'soc-analyst', label: 'SOC Analyst', icon: '📡', defaultVendors: ['microsoft','splunk','crowdstrike','sentinelone','elastic'] }, { key: 'devsecops', label: 'DevSecOps', icon: '⚙', defaultVendors: ['github','aws','google','microsoft','kubernetes'] }, { key: 'ciso-manager', label: 'Manager / CISO', icon: '📊', defaultVendors: ['microsoft','cisco','crowdstrike','palo-alto','fortinet'] }, { key: 'researcher-student', label: 'Researcher / Student', icon: '🔬', defaultVendors: [] }, ]; const OB_TOP_VENDORS = [ 'microsoft','cisco','fortinet','palo-alto','crowdstrike','sentinelone', 'aws','google','apple','ivanti' ]; let obSelectedRole = null; let obSelectedSev = 'HIGH'; let obSelectedCats = new Set(); let obSelectedVendors = new Set(); let obCurrentStep = 1; function shouldShowOnboarding() { if (!currentUser) return false; return currentUser.onboardingDone === false; } function openOnboarding() { obCurrentStep = 1; obSelectedRole = null; obSelectedSev = 'HIGH'; obSelectedCats = new Set(); obSelectedVendors = new Set(['microsoft','cisco','fortinet','palo-alto','crowdstrike','sentinelone','aws','google','apple','ivanti']); renderObRoles(); renderObSeverities(); renderObCategories(); // vendors: need allVendorsList loaded loadVendorsList().then(() => renderObVendors()); // populate email in step 3 const emailEl = document.getElementById('ob-email-display'); if (emailEl && currentUser) emailEl.textContent = currentUser.email || '—'; // setup digest radio styling document.querySelectorAll('.ob-digest-opt').forEach(lbl => { const radio = lbl.querySelector('input[type=radio]'); radio.addEventListener('change', () => { document.querySelectorAll('.ob-digest-opt').forEach(l => { const checked = l.querySelector('input[type=radio]').checked; l.style.borderColor = checked ? 'var(--red)' : 'var(--border2)'; l.style.background = checked ? 'rgba(204,0,0,.05)' : 'transparent'; }); }); }); obGoTo(1); document.getElementById('onboarding-overlay').style.display = 'flex'; document.body.style.overflow = 'hidden'; } function renderObRoles() { const grid = document.getElementById('ob-roles-grid'); if (!grid) return; grid.innerHTML = OB_ROLES.map(r => ` `).join(''); } function selectObRole(key) { obSelectedRole = key; renderObRoles(); // pre-select default vendors for this role const role = OB_ROLES.find(r => r.key === key); if (role) { obSelectedVendors = new Set(OB_TOP_VENDORS); role.defaultVendors.forEach(v => obSelectedVendors.add(v)); } } function renderObSeverities() { const grid = document.getElementById('ob-sev-grid'); if (!grid) return; const sevs = [ { key: 'CRITICAL', label: 'CRITICAL only', color: 'var(--red)' }, { key: 'HIGH', label: 'HIGH & above', color: 'var(--orange)' }, { key: 'MEDIUM', label: 'MEDIUM & above', color: 'var(--amber)' }, { key: 'INFO', label: 'Everything', color: 'var(--blue)' }, ]; grid.innerHTML = sevs.map(s => ` `).join(''); } function selectObSev(key) { obSelectedSev = key; renderObSeverities(); } function renderObCategories() { const grid = document.getElementById('ob-cats-grid'); if (!grid) return; grid.innerHTML = ALL_INTERESTS.map(i => ` `).join(''); } function toggleObCat(key, checked) { if (checked) obSelectedCats.add(key); else obSelectedCats.delete(key); renderObCategories(); } function renderObVendors() { const grid = document.getElementById('ob-vendors-grid'); if (!grid) return; const topVendors = allVendorsList.filter(v => OB_TOP_VENDORS.includes(v.key)); grid.innerHTML = topVendors.map(v => ` `).join(''); } function toggleObVendor(key, checked) { if (checked) obSelectedVendors.add(key); else obSelectedVendors.delete(key); renderObVendors(); } function obGoTo(step) { obCurrentStep = step; [1,2,3].forEach(s => { const el = document.getElementById(`ob-step-${s}`); if (el) el.style.display = (s === step) ? '' : 'none'; }); const labels = ['', 'Step 1 of 3 — Your role', 'Step 2 of 3 — Interests', 'Step 3 of 3 — Digest']; const lbl = document.getElementById('ob-step-label'); if (lbl) lbl.textContent = labels[step] || ''; const pct = step === 1 ? '33%' : step === 2 ? '66%' : '100%'; const bar = document.getElementById('ob-progress'); if (bar) bar.style.width = pct; } function obNext(step) { obGoTo(step); } async function skipOnboarding() { try { await fetch('/api/me/onboarding', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ onboardingDone: true, role: obSelectedRole || 'security-engineer' }) }); } catch {} closeOnboarding(); } async function finishOnboarding() { const btn = document.getElementById('ob-finish-btn'); const msg = document.getElementById('ob-finish-msg'); btn.disabled = true; btn.textContent = 'Saving...'; const digestVal = document.querySelector('input[name="ob-digest"]:checked')?.value || 'morning'; const digestPrefs = digestVal === 'both' ? ['morning','evening'] : digestVal === 'none' ? [] : [digestVal]; try { await Promise.all([ fetch('/api/me/preferences', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: currentUser.name, minSeverity: obSelectedSev, digestPrefs }) }), fetch('/api/me/vendors', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ vendors: [...obSelectedVendors] }) }), fetch('/api/me/interests', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ interests: [...obSelectedCats] }) }), fetch('/api/me/onboarding', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ onboardingDone: true, role: obSelectedRole || 'security-engineer' }) }) ]); msg.style.color = 'var(--green)'; msg.textContent = '✓ Your feed is configured!'; btn.textContent = '✓ DONE'; await checkAuth(); if (typeof loadArticles === 'function') loadArticles(); setTimeout(() => closeOnboarding(), 1200); showToast && showToast('Welcome to SECFEED! Your feed is personalized.'); } catch { msg.style.color = 'var(--red)'; msg.textContent = '✗ Save failed — please try again.'; btn.disabled = false; btn.textContent = 'FINISH SETUP ✓'; } } function closeOnboarding() { document.getElementById('onboarding-overlay').style.display = 'none'; document.body.style.overflow = ''; }