From 29f20eb15c554cade8fdabfef6dc7b2717091108 Mon Sep 17 00:00:00 2001 From: DAProgs Date: Fri, 13 Mar 2026 16:13:56 -0400 Subject: [PATCH] 2603.9 frequesnt IPs added --- api/frequent_ips.php | 70 ++++++++++++++++++++++++++++++++++++++++++ includes/functions.php | 21 +++++++++++++ schema.sql | 1 + settings.php | 28 ++++++++++++++++- setup.php | 1 + version.php | 2 +- 6 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 api/frequent_ips.php diff --git a/api/frequent_ips.php b/api/frequent_ips.php new file mode 100644 index 0000000..59136c1 --- /dev/null +++ b/api/frequent_ips.php @@ -0,0 +1,70 @@ += the configured + * threshold (frequent_ip_threshold setting, default 5), sorted highest first. + * + * Authentication (any one of): + * - Active web session (logged-in browser) + * - Authorization: Bearer + * - ?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); diff --git a/includes/functions.php b/includes/functions.php index 52cb989..b8cf553 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -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 ───────────────────────────────────────────────────────────────────── /** diff --git a/schema.sql b/schema.sql index e4ebe8d..8807e43 100644 --- a/schema.sql +++ b/schema.sql @@ -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'); diff --git a/settings.php b/settings.php index c277206..cd622a9 100644 --- a/settings.php +++ b/settings.php @@ -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'); ?> @@ -106,6 +116,22 @@ $retention_days = (int)get_setting('retention_days', '7'); +
+

Frequent IP threshold

+

+ Minimum number of connections for an IP to appear in + api/frequent_ips.php. +

+
+ + + +
+
+ diff --git a/setup.php b/setup.php index a86bb8f..6dc24d7 100644 --- a/setup.php +++ b/setup.php @@ -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"; diff --git a/version.php b/version.php index 9947dc0..9e0dcba 100644 --- a/version.php +++ b/version.php @@ -1,2 +1,2 @@