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.
110 lines
5.9 KiB
HTML
110 lines
5.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}ESPILON{% endblock %}</title>
|
|
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
|
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.8/dist/cdn.min.js"></script>
|
|
<script src="{{ url_for('static', filename='js/utils.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='js/store.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='js/data-table.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='js/commander.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='js/resizer.js') }}"></script>
|
|
{% block head %}{% endblock %}
|
|
</head>
|
|
<body x-data x-init="$store.app.init()">
|
|
|
|
<!-- Tab Bar -->
|
|
<div class="tabs">
|
|
<div class="tab-brand">ε</div>
|
|
<a href="/dashboard" class="tab {% if active_page == 'dashboard' %}active{% endif %}">Devices</a>
|
|
<a href="/terminal" class="tab {% if active_page == 'terminal' %}active{% endif %}">Terminal</a>
|
|
<a href="/cameras" class="tab {% if active_page == 'cameras' %}active{% endif %}">Cameras</a>
|
|
<a href="/mlat" class="tab {% if active_page == 'mlat' %}active{% endif %}">MLAT</a>
|
|
<a href="/canbus" class="tab {% if active_page == 'canbus' %}active{% endif %}">CAN Bus</a>
|
|
<a href="/network" class="tab {% if active_page == 'network' %}active{% endif %}">Network</a>
|
|
<a href="/tunnel" class="tab {% if active_page == 'tunnel' %}active{% endif %}">Tunnel</a>
|
|
<a href="/fakeap" class="tab {% if active_page == 'fakeap' %}active{% endif %}">FakeAP</a>
|
|
<a href="/redteam" class="tab {% if active_page == 'redteam' %}active{% endif %}">Red Team</a>
|
|
<a href="/honeypot" class="tab {% if active_page == 'honeypot' %}active{% endif %}">Honeypot</a>
|
|
<a href="/ota" class="tab {% if active_page == 'ota' %}active{% endif %}">OTA</a>
|
|
<a href="/system" class="tab {% if active_page == 'system' %}active{% endif %}">System</a>
|
|
<div class="tab-spacer"></div>
|
|
<a href="/logout" class="tab tab-right">Logout</a>
|
|
</div>
|
|
|
|
<!-- App Layout: Sidebar + Main -->
|
|
<div class="app-layout" :class="{ 'sidebar-collapsed': $store.app.sidebarCollapsed }">
|
|
|
|
<!-- Sidebar -->
|
|
<aside class="sidebar">
|
|
<div class="sidebar-header">
|
|
<span class="sidebar-title" x-show="!$store.app.sidebarCollapsed">Devices</span>
|
|
<button class="btn-icon" @click="$store.app.toggleSidebar()" x-text="$store.app.sidebarCollapsed ? '\u25B6' : '\u25C0'"></button>
|
|
</div>
|
|
<div class="sidebar-body" x-show="!$store.app.sidebarCollapsed">
|
|
<div class="tree">
|
|
<template x-if="$store.app.connectedDevices().length > 0">
|
|
<div>
|
|
<div class="tree-group-label">Connected</div>
|
|
<template x-for="dev in $store.app.connectedDevices()" :key="dev.id">
|
|
<a :href="'/device/' + dev.id" class="tree-item">
|
|
<span class="tree-icon"><span class="statusbar-dot ok"></span></span>
|
|
<span class="tree-label" x-text="dev.id"></span>
|
|
</a>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
<template x-if="$store.app.offlineDevices().length > 0">
|
|
<div>
|
|
<div class="tree-group-label">Offline</div>
|
|
<template x-for="dev in $store.app.offlineDevices()" :key="dev.id">
|
|
<a :href="'/device/' + dev.id" class="tree-item">
|
|
<span class="tree-icon"><span class="statusbar-dot err"></span></span>
|
|
<span class="tree-label" x-text="dev.id"></span>
|
|
</a>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
<template x-if="$store.app.devices.length === 0">
|
|
<div class="text-muted text-xs" style="padding: 8px;">No devices</div>
|
|
</template>
|
|
</div>
|
|
{% block sidebar %}{% endblock %}
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Main Content -->
|
|
<main class="main-content">
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
</div>
|
|
|
|
<!-- Status Bar -->
|
|
<div class="statusbar">
|
|
<div class="statusbar-left">
|
|
<span class="statusbar-item">
|
|
<span class="statusbar-dot" :class="$store.app.serverOnline ? 'ok' : 'err'"></span>
|
|
<span x-text="$store.app.serverOnline ? 'Connected' : 'Disconnected'"></span>
|
|
</span>
|
|
<span class="statusbar-sep">|</span>
|
|
<span class="statusbar-item" x-text="$store.app.stats.connected_devices + ' device(s)'"></span>
|
|
<span class="statusbar-sep">|</span>
|
|
<span class="statusbar-item" x-text="($store.app.stats.active_cameras || 0) + ' cam(s)'"></span>
|
|
<span class="statusbar-sep">|</span>
|
|
<span class="statusbar-item" x-text="($store.app.stats.multilateration_scanners || 0) + ' scanner(s)'"></span>
|
|
</div>
|
|
<div class="statusbar-right">
|
|
<span class="statusbar-item">ESPILON C2</span>
|
|
</div>
|
|
</div>
|
|
|
|
{% block scripts %}{% endblock %}
|
|
</body>
|
|
</html>
|