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.
88 lines
2.9 KiB
JavaScript
88 lines
2.9 KiB
JavaScript
/* ESPILON Honeypot Dashboard — API Client */
|
|
|
|
import { S } from './state.js';
|
|
|
|
// showToast will be set by ui.js to break circular dependency
|
|
let _showToast = (title, msg, detail, type) => console.warn('[HP]', title, msg);
|
|
|
|
export function setToastHandler(fn) {
|
|
_showToast = fn;
|
|
}
|
|
|
|
// Default render callback — set by app.js so fetchAll() always re-renders
|
|
let _defaultRender = null;
|
|
|
|
export function setDefaultRender(fn) {
|
|
_defaultRender = fn;
|
|
}
|
|
|
|
export async function api(url) {
|
|
try {
|
|
const r = await fetch(url);
|
|
if (!r.ok) throw new Error(r.status + ' ' + r.statusText);
|
|
return await r.json();
|
|
} catch (e) {
|
|
console.warn('[HP API]', url, e.message);
|
|
_showToast('API Error', url + ': ' + e.message, '', 'error');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
let _csrfToken = null;
|
|
|
|
async function _getCsrf() {
|
|
if (_csrfToken) return _csrfToken;
|
|
try {
|
|
const r = await fetch('/api/honeypot/csrf-token');
|
|
if (r.ok) { const j = await r.json(); _csrfToken = j.token; }
|
|
} catch (_) { /* ignore */ }
|
|
return _csrfToken || '';
|
|
}
|
|
|
|
export async function postApi(url, body) {
|
|
try {
|
|
const csrf = await _getCsrf();
|
|
const r = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json', 'X-CSRF-Token': csrf},
|
|
body: JSON.stringify(body || {})
|
|
});
|
|
if (r.status === 403) { _csrfToken = null; } // token expired, refresh next time
|
|
if (!r.ok) throw new Error(r.status + ' ' + r.statusText);
|
|
return await r.json();
|
|
} catch (e) {
|
|
console.warn('[HP API]', url, e.message);
|
|
_showToast('API Error', url + ': ' + e.message, '', 'error');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function fetchAll(renderAll) {
|
|
const devParam = S.selectedDevice ? '?device_id=' + S.selectedDevice : '';
|
|
const [stats, events, attackers, timeline, devices, services, history, alerts] = await Promise.all([
|
|
api('/api/honeypot/stats'),
|
|
api('/api/honeypot/events?limit=200'),
|
|
api('/api/honeypot/attackers?limit=50'),
|
|
api('/api/honeypot/timeline?hours=24&bucket=5'),
|
|
api('/api/honeypot/devices'),
|
|
api('/api/honeypot/services' + devParam),
|
|
api('/api/honeypot/history?limit=50'),
|
|
api('/api/honeypot/alerts/active')
|
|
]);
|
|
if (stats) S.stats = stats;
|
|
if (events) S.events = events.events || [];
|
|
if (attackers) S.attackers = attackers.attackers || [];
|
|
if (timeline) S.timeline = timeline.timeline || [];
|
|
if (devices) S.devices = devices.devices || [];
|
|
if (services) { S.services = services.services || {}; S.definitions = services.definitions || {}; }
|
|
if (history) S.history = history.history || [];
|
|
if (alerts) S.alerts = alerts.alerts || [];
|
|
S.lastId = S.stats.last_id || 0;
|
|
// Reset cached tabs
|
|
S.sessions = null;
|
|
S.credentials = null;
|
|
S.killchain = null;
|
|
const cb = renderAll || _defaultRender;
|
|
if (cb) cb();
|
|
}
|