/* ESPILON Honeypot Dashboard — Sidebar Renders */
import { S } from './state.js';
import { $id, escHtml, formatTime, countryFlag, sevColor, layerForType, layerColor, emptyState } from './utils.js';
import { renderSevDonut } from './charts.js';
// ── Severity Stats ──────────────────────────────────────────
export function renderSevStats() {
const el = $id('sev-stats');
if (!el) return;
const bySev = (S.stats && S.stats.by_severity) ? S.stats.by_severity : {};
const sevs = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'];
const counts = [];
let total = 0;
for (let i = 0; i < sevs.length; i++) {
const c = bySev[sevs[i]] || 0;
counts.push(c);
total += c;
}
let html = '';
// Donut placeholder
html += '
';
// Count list
html += '';
for (let i = 0; i < sevs.length; i++) {
html += '
';
html += '' + sevs[i] + '';
html += '' + counts[i] + '';
html += '
';
}
html += '
';
el.innerHTML = html;
// Render donut into the container
const donutEl = $id('sev-donut-sb');
if (donutEl) {
const statsObj = {};
for (let i = 0; i < sevs.length; i++) statsObj[sevs[i]] = counts[i];
renderSevDonut(donutEl, statsObj);
}
}
// ── Layer Stats ─────────────────────────────────────────────
export function renderLayerStats() {
const el = $id('layer-stats');
if (!el) return;
const layerCounts = { L2: 0, L3: 0, L4: 0, L7: 0 };
if (S.events) {
for (let i = 0; i < S.events.length; i++) {
const l = layerForType(S.events[i].event_type);
if (layerCounts[l] !== undefined) layerCounts[l]++;
}
}
const layers = ['L2', 'L3', 'L4', 'L7'];
let html = '';
for (let i = 0; i < layers.length; i++) {
const l = layers[i];
html += '
';
html += '
' + layerCounts[l] + '
';
html += '
' + l + '
';
html += '
';
}
html += '
';
el.innerHTML = html;
}
// ── Attackers ───────────────────────────────────────────────
export function renderAttackers() {
const el = $id('attackers-list');
if (!el) return;
const list = S.attackers || [];
const top = list.slice(0, 10);
if (!top.length) {
el.innerHTML = emptyState('\u{1F464}', 'No attackers yet', '');
return;
}
const maxCount = top[0].total_events || top[0].count || 1;
let html = '';
for (let i = 0; i < top.length; i++) {
const a = top[i];
const cnt = a.total_events || a.count || 0;
const pct = Math.round((cnt / maxCount) * 100);
const flag = a.country_code ? countryFlag(a.country_code) + ' ' : '';
const vendor = a.vendor || '';
html += '';
html += '
';
html += '' + flag + escHtml(a.ip) + '';
html += '' + cnt + '';
html += '
';
if (vendor) {
html += '
' + escHtml(vendor) + '
';
}
html += '
';
html += '
';
}
el.innerHTML = html;
}
// ── Alerts ──────────────────────────────────────────────────
export function renderAlerts() {
const el = $id('alerts-list');
const badge = $id('alert-badge');
const banner = $id('alert-banner');
const bannerCount = $id('alert-banner-count');
const alerts = S.alerts || [];
const unacked = alerts.filter(function(a) { return !a.acknowledged; });
// Badge
if (badge) {
badge.textContent = unacked.length;
badge.style.display = unacked.length > 0 ? '' : 'none';
}
// Banner
if (banner) {
banner.style.display = unacked.length > 0 ? '' : 'none';
}
if (bannerCount) {
bannerCount.textContent = unacked.length;
}
// List
if (!el) return;
if (!alerts.length) {
el.innerHTML = emptyState('\u{1F514}', 'No alerts', '');
return;
}
let html = '';
for (let i = 0; i < alerts.length; i++) {
const a = alerts[i];
const borderCol = sevColor(a.severity || 'MEDIUM');
const acked = a.acknowledged;
html += '';
html += '
';
html += '
' + escHtml(a.message || a.detail || 'Alert') + '
';
if (!acked) {
html += '
';
}
html += '
';
if (a.timestamp) {
html += '
' + formatTime(a.timestamp) + '
';
}
html += '
';
}
el.innerHTML = html;
}
// ── Command History ─────────────────────────────────────────
export function renderHistory() {
const el = $id('cmd-history');
if (!el) return;
const list = S.history || [];
const items = list.slice(0, 20);
if (!items.length) {
el.innerHTML = emptyState('\u{2328}\u{FE0F}', 'No commands yet', '');
return;
}
let html = '';
for (let i = 0; i < items.length; i++) {
const h = items[i];
let statusDot;
if (h.status === 'completed') {
statusDot = 'var(--sev-low)';
} else if (h.status === 'pending' || h.status === 'sent') {
statusDot = 'var(--sev-med)';
} else {
statusDot = 'var(--sev-high)';
}
let cmdName = h.command_name || h.command || '?';
if (cmdName.length > 24) cmdName = cmdName.substring(0, 24) + '\u2026';
const timeStr = h.sent_at ? formatTime(h.sent_at) : '';
html += '';
html += '';
html += '' + escHtml(cmdName) + '';
html += '' + timeStr + '';
html += '
';
}
el.innerHTML = html;
}
// ── Full Sidebar Render ─────────────────────────────────────
export function renderSidebar() {
renderSevStats();
renderLayerStats();
renderAttackers();
renderAlerts();
renderHistory();
}