espilon-source/tools/C3PO/templates/dashboard.html
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

72 lines
3.5 KiB
HTML

{% extends "base.html" %}
{% block title %}Devices - ESPILON{% endblock %}
{% block content %}
<div class="page" x-data="deviceList()" x-init="init()">
<div class="panel" style="flex:1;">
<div class="panel-header">
<span>Devices <span x-text="'(' + rows.length + ')'"></span></span>
<div class="panel-header-actions">
<input type="text" class="input" placeholder="Filter..." x-model="filter" style="width:180px;">
</div>
</div>
<div class="panel-body">
<table class="dt">
<thead>
<tr>
<th @click="toggleSort('id')" :class="sortClass('id')">ID</th>
<th @click="toggleSort('status')" :class="sortClass('status')" class="col-shrink">Status</th>
<th @click="toggleSort('ip')" :class="sortClass('ip')">Address</th>
<th @click="toggleSort('chip')" :class="sortClass('chip')" class="col-shrink">Chip</th>
<th>Modules</th>
<th @click="toggleSort('connected_for_seconds')" :class="sortClass('connected_for_seconds')" class="col-right">Uptime</th>
<th @click="toggleSort('last_seen_ago_seconds')" :class="sortClass('last_seen_ago_seconds')" class="col-right">Last Seen</th>
</tr>
</thead>
<tbody>
<template x-for="d in pagedRows" :key="d.id">
<tr class="clickable" @click="window.location='/device/'+d.id">
<td x-text="d.id"></td>
<td>
<span class="badge" :class="d.status==='Connected' ? 'badge-ok' : 'badge-warn'" x-text="d.status"></span>
</td>
<td x-text="(d.ip||'-')+':'+(d.port||'-')"></td>
<td x-text="d.chip || '-'"></td>
<td>
<template x-for="m in (d.modules||'').split(',').filter(Boolean)" :key="m">
<span class="badge" x-text="m" style="margin-right:2px;"></span>
</template>
</td>
<td class="col-right" x-text="formatDuration(d.connected_for_seconds)"></td>
<td class="col-right" x-text="formatDuration(d.last_seen_ago_seconds)+' ago'"></td>
</tr>
</template>
</tbody>
</table>
<template x-if="!loading && rows.length === 0">
<div class="dt-empty">No devices connected</div>
</template>
</div>
<div class="panel-footer" x-show="totalPages > 1">
Page <span x-text="page + 1"></span> / <span x-text="totalPages"></span>
<button class="btn btn-sm" @click="page = Math.max(0, page-1)" :disabled="page === 0">&laquo;</button>
<button class="btn btn-sm" @click="page = Math.min(totalPages-1, page+1)" :disabled="page >= totalPages-1">&raquo;</button>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function deviceList() {
return {
...dataTable({ defaultSort: 'status', defaultDir: 'asc', extract: d => d.devices || [] }),
init() {
this.refresh('/api/devices');
setInterval(() => this.refresh('/api/devices'), 5000);
}
};
}
</script>
{% endblock %}