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.
75 lines
2.6 KiB
JavaScript
75 lines
2.6 KiB
JavaScript
/* ESPILON Honeypot Dashboard — Router & Navigation */
|
|
|
|
import { S } from './state.js';
|
|
import { $id } from './utils.js';
|
|
|
|
// Tab render functions — set by app.js via setTabRenderers().
|
|
const _tabRenderers = {};
|
|
|
|
export function setTabRenderers(renderers) {
|
|
Object.assign(_tabRenderers, renderers);
|
|
}
|
|
|
|
export function switchTab(tab) {
|
|
S.tab = tab;
|
|
|
|
// Highlight active nav button
|
|
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
|
|
const btn = $id('tab-' + tab);
|
|
if (btn) btn.classList.add('active');
|
|
|
|
// Toggle search bar visibility (only on events tab)
|
|
const sb = $id('search-bar');
|
|
if (sb) sb.style.display = tab === 'timeline' ? '' : 'none';
|
|
|
|
// Collapse filter panel when leaving events tab
|
|
const fp = $id('filter-panel');
|
|
if (fp) fp.classList.remove('active');
|
|
|
|
// Clear events badge when switching to events
|
|
if (tab === 'timeline') {
|
|
const badge = $id('nav-badge-events');
|
|
if (badge) { badge.textContent = '0'; badge.style.display = 'none'; }
|
|
}
|
|
|
|
// Close mobile nav
|
|
$id('nav-tabs')?.classList.remove('nav-open');
|
|
|
|
// Render the tab
|
|
const renderer = _tabRenderers[tab];
|
|
if (renderer) renderer();
|
|
}
|
|
|
|
// ── Keyboard Shortcuts ──────────────────────────────────────
|
|
// extraHandlers: { onEscape, onRefresh, onToggleSound }
|
|
|
|
export function setupKeyboardShortcuts(extraHandlers) {
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT' || e.target.tagName === 'TEXTAREA') {
|
|
if (e.key === 'Escape') e.target.blur();
|
|
return;
|
|
}
|
|
switch (e.key) {
|
|
case '/':
|
|
e.preventDefault();
|
|
if (S.tab === 'timeline') $id('search-input')?.focus();
|
|
break;
|
|
case 'Escape':
|
|
if (extraHandlers?.onEscape) extraHandlers.onEscape();
|
|
break;
|
|
case '1': switchTab('overview'); break;
|
|
case '2': switchTab('timeline'); break;
|
|
case '3': switchTab('sessions'); break;
|
|
case '4': switchTab('credentials'); break;
|
|
case '5': switchTab('killchain'); break;
|
|
case '6': switchTab('mitre'); break;
|
|
case 'r':
|
|
if (extraHandlers?.onRefresh) extraHandlers.onRefresh();
|
|
break;
|
|
case 's':
|
|
if (extraHandlers?.onToggleSound) extraHandlers.onToggleSound();
|
|
break;
|
|
}
|
|
});
|
|
}
|