First push
This commit is contained in:
199
index.php
Normal file
199
index.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/functions.php';
|
||||
|
||||
$nodes = get_all_nodes();
|
||||
$stats = global_stats();
|
||||
$t_ips = top_ips();
|
||||
$t_ports = top_ports();
|
||||
$recent = recent_connections(DASH_RECENT_LIMIT);
|
||||
$node_count = count($nodes);
|
||||
$enabled_count = count(array_filter($nodes, fn($n) => (bool)$n['enabled']));
|
||||
|
||||
// Optional per-node filter
|
||||
$filter_node = isset($_GET['node']) ? (int)$_GET['node'] : null;
|
||||
if ($filter_node) {
|
||||
$t_ips = top_ips_by_node($filter_node);
|
||||
$recent = recent_connections(DASH_RECENT_LIMIT, $filter_node);
|
||||
}
|
||||
|
||||
$max_ip_cnt = $t_ips ? max(array_column($t_ips, 'cnt')) : 1;
|
||||
$max_port_cnt = $t_ports ? max(array_column($t_ports, 'cnt')) : 1;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="refresh" content="30">
|
||||
<title>Dashboard – portspoof concentrator</title>
|
||||
<style>
|
||||
<?php include __DIR__ . '/includes/style.php'; ?>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>portspoof<span>concentrator</span></h1>
|
||||
<nav>
|
||||
<a href="index.php" class="active">Dashboard</a>
|
||||
<a href="nodes.php">Nodes</a>
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
|
||||
<!-- Stat cards -->
|
||||
<div class="stats-row">
|
||||
<div class="stat-card">
|
||||
<div class="label">Total connections</div>
|
||||
<div class="value"><?= number_format($stats['total']) ?></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">Last <?= RATE_WINDOW_SECONDS ?>s</div>
|
||||
<div class="value"><?= number_format($stats['recent']) ?></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">Nodes</div>
|
||||
<div class="value"><?= $enabled_count ?> <span class="muted" style="font-size:1rem">/ <?= $node_count ?></span></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">Last connection</div>
|
||||
<div class="value" style="font-size:1rem"><?= $stats['last'] ? h($stats['last']) : '—' ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Node filter -->
|
||||
<?php if (count($nodes) > 1): ?>
|
||||
<div class="card" style="padding:.75rem 1.25rem">
|
||||
<span class="muted" style="font-size:.8rem">Filter by node:</span>
|
||||
<a href="index.php" <?= !$filter_node ? 'style="color:var(--accent)"' : '' ?>>All</a>
|
||||
<?php foreach ($nodes as $n): ?>
|
||||
<a href="index.php?node=<?= (int)$n['id'] ?>"
|
||||
<?= $filter_node === (int)$n['id'] ? 'style="color:var(--accent)"' : '' ?>>
|
||||
<?= h($n['name']) ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Top IPs + Top Ports -->
|
||||
<div class="two-col">
|
||||
<div class="card">
|
||||
<h2>Top source IPs</h2>
|
||||
<?php if (empty($t_ips)): ?>
|
||||
<p class="muted">No data yet.</p>
|
||||
<?php else: ?>
|
||||
<table>
|
||||
<thead><tr><th>IP</th><th>Connections</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($t_ips as $row): ?>
|
||||
<tr>
|
||||
<td><code><?= h($row['src_ip']) ?></code></td>
|
||||
<td><?= number_format($row['cnt']) ?></td>
|
||||
<td style="width:40%">
|
||||
<div class="bar-wrap">
|
||||
<div class="bar" style="width:<?= round($row['cnt'] / $max_ip_cnt * 100) ?>%"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Top targeted ports</h2>
|
||||
<?php if (empty($t_ports)): ?>
|
||||
<p class="muted">No data yet.</p>
|
||||
<?php else: ?>
|
||||
<table>
|
||||
<thead><tr><th>Port</th><th>Connections</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($t_ports as $row): ?>
|
||||
<tr>
|
||||
<td><code><?= (int)$row['dst_port'] ?></code></td>
|
||||
<td><?= number_format($row['cnt']) ?></td>
|
||||
<td style="width:40%">
|
||||
<div class="bar-wrap">
|
||||
<div class="bar" style="width:<?= round($row['cnt'] / $max_port_cnt * 100) ?>%"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent connections -->
|
||||
<div class="card">
|
||||
<h2>Recent connections <?= $filter_node ? '(filtered)' : '' ?></h2>
|
||||
<?php if (empty($recent)): ?>
|
||||
<p class="muted">No connections ingested yet. Make sure at least one node is configured and the fetch cron is running.</p>
|
||||
<?php else: ?>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time (UTC)</th>
|
||||
<th>Node</th>
|
||||
<th>Source IP</th>
|
||||
<th>Src port</th>
|
||||
<th>Dst port</th>
|
||||
<th>Banner len</th>
|
||||
<th>Banner preview</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($recent as $c): ?>
|
||||
<tr>
|
||||
<td><?= h($c['occurred_at']) ?></td>
|
||||
<td><?= h($c['node_name']) ?></td>
|
||||
<td><code><?= h($c['src_ip']) ?></code></td>
|
||||
<td><?= (int)$c['src_port'] ?></td>
|
||||
<td><code><?= (int)$c['dst_port'] ?></code></td>
|
||||
<td><?= (int)$c['banner_len'] ?></td>
|
||||
<td>
|
||||
<?php if ($c['banner_hex']): ?>
|
||||
<code title="<?= h($c['banner_hex']) ?>" style="font-size:.75rem">
|
||||
<?= h(mb_substr(banner_text($c['banner_hex']), 0, 40)) ?>
|
||||
</code>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Node status table -->
|
||||
<div class="card">
|
||||
<h2>Node status</h2>
|
||||
<?php if (empty($nodes)): ?>
|
||||
<p class="muted">No nodes configured. <a href="nodes.php">Add one</a>.</p>
|
||||
<?php else: ?>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Name</th><th>API URL</th><th>Enabled</th><th>Last fetched</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($nodes as $n): ?>
|
||||
<tr>
|
||||
<td><a href="index.php?node=<?= (int)$n['id'] ?>"><?= h($n['name']) ?></a></td>
|
||||
<td><code><?= h($n['api_url']) ?></code></td>
|
||||
<td><?= $n['enabled'] ? '<span class="badge ok">yes</span>' : '<span class="badge">no</span>' ?></td>
|
||||
<td><?= $n['last_fetched_at'] ? h($n['last_fetched_at']) : '<span class="muted">never</span>' ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<p class="muted" style="font-size:.75rem; text-align:right">
|
||||
Auto-refreshes every 30 s · <?= date('Y-m-d H:i:s') ?> server time
|
||||
</p>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user