2603.9 frequesnt IPs added

This commit is contained in:
2026-03-13 16:13:56 -04:00
parent 1e57388299
commit 29f20eb15c
6 changed files with 121 additions and 2 deletions

70
api/frequent_ips.php Normal file
View File

@@ -0,0 +1,70 @@
<?php
/**
* GET /api/frequent_ips.php
*
* Returns all source IPs with a total connection count >= the configured
* threshold (frequent_ip_threshold setting, default 5), sorted highest first.
*
* Authentication (any one of):
* - Active web session (logged-in browser)
* - Authorization: Bearer <TRIGGER_TOKEN>
* - ?token=<TRIGGER_TOKEN>
*
* Optional query parameters:
* threshold int Override the configured minimum connection count
*/
require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/functions.php';
header('Content-Type: application/json');
// ── Auth ──────────────────────────────────────────────────────────────────────
$session_ok = false;
if (auth_enabled()) {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$session_ok = !empty($_SESSION['authenticated']);
}
$token_ok = false;
if (TRIGGER_TOKEN !== '') {
$provided = '';
$auth_header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (str_starts_with($auth_header, 'Bearer ')) {
$provided = substr($auth_header, 7);
}
if ($provided === '' && isset($_REQUEST['token'])) {
$provided = $_REQUEST['token'];
}
$token_ok = $provided !== '' && hash_equals(TRIGGER_TOKEN, $provided);
}
if (!$session_ok && !$token_ok) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
// ── Query ─────────────────────────────────────────────────────────────────────
$configured_threshold = max(1, (int)get_setting('frequent_ip_threshold', '5'));
$threshold = isset($_GET['threshold'])
? max(1, (int)$_GET['threshold'])
: $configured_threshold;
$rows = frequent_ips($threshold);
echo json_encode([
'threshold' => $threshold,
'configured_threshold' => $configured_threshold,
'count' => count($rows),
'ips' => array_map(fn($r) => [
'src_ip' => $r['src_ip'],
'total_connections' => (int)$r['total_connections'],
'first_seen' => $r['first_seen'],
'last_seen' => $r['last_seen'],
], $rows),
], JSON_PRETTY_PRINT);

View File

@@ -198,6 +198,27 @@ function set_setting(string $key, string $value): void {
$s->execute([$key, $value]);
}
// ── Frequent IPs ─────────────────────────────────────────────────────────────
/**
* Return all IPs with a total connection count >= $min_connections, sorted
* highest-count first. Includes first and last seen timestamps.
*/
function frequent_ips(int $min_connections): array {
$s = db()->prepare(
'SELECT src_ip,
COUNT(*) AS total_connections,
MIN(occurred_at) AS first_seen,
MAX(occurred_at) AS last_seen
FROM connections
GROUP BY src_ip
HAVING total_connections >= ?
ORDER BY total_connections DESC'
);
$s->execute([$min_connections]);
return $s->fetchAll();
}
// ── Purge ─────────────────────────────────────────────────────────────────────
/**

View File

@@ -39,3 +39,4 @@ CREATE TABLE IF NOT EXISTS settings (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT IGNORE INTO settings (key_name, value) VALUES ('retention_days', '7');
INSERT IGNORE INTO settings (key_name, value) VALUES ('frequent_ip_threshold', '5');

View File

@@ -34,10 +34,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
set_setting('retention_days', (string)$days);
$success = 'Retention period saved.';
}
} elseif ($action === 'frequent_ip_threshold') {
$threshold = (int)($_POST['frequent_ip_threshold'] ?? 0);
if ($threshold < 1) {
$errors[] = 'Threshold must be at least 1.';
} else {
set_setting('frequent_ip_threshold', (string)$threshold);
$success = 'Frequent IP threshold saved.';
}
}
}
$retention_days = (int)get_setting('retention_days', '7');
$retention_days = (int)get_setting('retention_days', '7');
$frequent_ip_threshold = (int)get_setting('frequent_ip_threshold', '5');
?>
<!DOCTYPE html>
<html lang="en">
@@ -106,6 +116,22 @@ $retention_days = (int)get_setting('retention_days', '7');
</form>
</section>
<section class="card">
<h2>Frequent IP threshold</h2>
<p class="muted" style="margin-bottom:1rem;font-size:.85rem">
Minimum number of connections for an IP to appear in
<code>api/frequent_ips.php</code>.
</p>
<form method="post">
<input type="hidden" name="action" value="frequent_ip_threshold">
<label>Minimum connections
<input type="number" name="frequent_ip_threshold" min="1" max="99999"
value="<?= $frequent_ip_threshold ?>" style="width:120px">
</label>
<button type="submit">Save</button>
</form>
</section>
</main>
<?php include __DIR__ . '/includes/footer.php'; ?>
</body>

View File

@@ -50,5 +50,6 @@ $pdo->exec(
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
);
$pdo->exec("INSERT IGNORE INTO settings (key_name, value) VALUES ('retention_days', '7')");
$pdo->exec("INSERT IGNORE INTO settings (key_name, value) VALUES ('frequent_ip_threshold', '5')");
echo "Database '" . DB_NAME . "' and tables created/migrated successfully.\n";

View File

@@ -1,2 +1,2 @@
<?php
define('APP_VERSION', '2603.8');
define('APP_VERSION', '2603.9');