espilon-source/tools/C3PO/hp_dashboard/static/hp/js/utils.js
Eun0us 79c2a4d4bf c3po: full server rewrite with modular routes and honeypot dashboard
Replace monolithic CLI and web server with route-based Flask API.
New routes: api_commands, api_build, api_can, api_monitor, api_ota,
api_tunnel. Add honeypot security dashboard with real-time SSE,
MITRE ATT&CK mapping, kill chain analysis.

New TUI with commander/help modules. Add session management,
tunnel proxy core, CAN bus data store. Docker support.
2026-02-28 20:12:27 +01:00

104 lines
3.5 KiB
JavaScript

/* ESPILON Honeypot Dashboard — Utilities */
export const $id = id => document.getElementById(id);
export function debounce(fn, ms) {
let tid;
return function(...args) { clearTimeout(tid); tid = setTimeout(() => fn.apply(this, args), ms); };
}
export function escHtml(s) {
if (!s) return '';
return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}
export function formatTime(ts) {
if (!ts) return '--:--:--';
const d = new Date(typeof ts === 'number' ? (ts < 1e12 ? ts * 1000 : ts) : ts);
return d.toLocaleTimeString('en-GB');
}
export function formatDuration(s) {
if (!s || s <= 0) return '0s';
const d = Math.floor(s / 86400), h = Math.floor(s % 86400 / 3600),
m = Math.floor(s % 3600 / 60), sec = Math.floor(s % 60);
if (d > 0) return d + 'd ' + h + 'h';
if (h > 0) return h + 'h ' + m + 'm';
if (m > 0) return m + 'm ' + sec + 's';
return sec + 's';
}
export function countryFlag(code) {
if (!code || code.length !== 2) return '';
return String.fromCodePoint(...[...code.toUpperCase()].map(c => 0x1F1E6 + c.charCodeAt(0) - 65));
}
export function animateCounter(el, target) {
if (!el) return;
target = parseInt(target) || 0;
const start = parseInt(el.textContent) || 0;
if (start === target) return;
const duration = 400, startTime = performance.now();
function step(now) {
const p = Math.min((now - startTime) / duration, 1);
const ease = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(start + (target - start) * ease);
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
export function sevColor(s) {
return {CRITICAL:'var(--sev-crit)', HIGH:'var(--sev-high)', MEDIUM:'var(--sev-med)', LOW:'var(--sev-low)'}[s] || 'var(--text-secondary)';
}
export function sevClass(s) {
return {CRITICAL:'sev-critical', HIGH:'sev-high', MEDIUM:'sev-medium', LOW:'sev-low'}[s] || '';
}
export function layerForType(t) {
if (!t) return 'L3';
if (t.startsWith('WIFI_') || t.startsWith('ARP_')) return 'L2';
if (t === 'SVC_CONNECT') return 'L4';
if (t.startsWith('SVC_')) return 'L7';
return 'L3';
}
export function layerColor(l) {
return {L2:'var(--layer-l2)', L3:'var(--layer-l3)', L4:'var(--layer-l4)', L7:'var(--layer-l7)'}[l] || 'var(--text-secondary)';
}
export function svcIcon(name) {
const icons = {
ssh:'🔒', telnet:'💻', http:'🌐', mqtt:'📡',
ftp:'📁', dns:'🏷️', snmp:'📊', tftp:'📂',
coap:'⚙️', redis:'💾', rtsp:'🎥', mysql:'🗃️',
modbus:'⚙️', upnp:'🔌', sip:'📞', telnet_alt:'💻',
wifi:'📶', net:'🌐'
};
return icons[(name || '').toLowerCase()] || '⚙️';
}
export function maskPassword(p) {
if (!p) return '';
if (p.length <= 3) return p;
return p.charAt(0) + '*'.repeat(Math.min(p.length - 2, 8)) + p.charAt(p.length - 1);
}
export function emptyState(icon, title, subtitle) {
return '<div class="empty-state">'
+ '<div class="empty-state-icon">' + icon + '</div>'
+ '<div class="empty-state-title">' + escHtml(title) + '</div>'
+ (subtitle ? '<div class="empty-state-sub">' + escHtml(subtitle) + '</div>' : '')
+ '</div>';
}
export function skeletonRows(n) {
let h = '';
for (let i = 0; i < n; i++) {
h += '<div class="skeleton skeleton-row"></div>';
}
return h;
}