2603.8 purging old records
This commit is contained in:
25
cron/purge.php
Executable file
25
cron/purge.php
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* portspoof_concentrator – purge cron
|
||||
*
|
||||
* Deletes connections older than the retention_days setting.
|
||||
* Must be run from the command line — exits immediately if called over HTTP.
|
||||
*
|
||||
* Recommended crontab entry (once daily at 02:00):
|
||||
* 0 2 * * * /usr/bin/php /path/to/portspoof_concentrator/cron/purge.php >> /var/log/portspoof_concentrator/purge.log 2>&1
|
||||
*/
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
http_response_code(403);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
require_once __DIR__ . '/../includes/functions.php';
|
||||
|
||||
$days = max(1, (int)get_setting('retention_days', '7'));
|
||||
$deleted = purge_old_connections();
|
||||
|
||||
echo sprintf("[%s] Purged %d connection(s) older than %d day(s).\n",
|
||||
date('Y-m-d H:i:s'), $deleted, $days);
|
||||
@@ -181,6 +181,38 @@ function run_fetch(): array {
|
||||
return $results;
|
||||
}
|
||||
|
||||
// ── Settings ──────────────────────────────────────────────────────────────────
|
||||
|
||||
function get_setting(string $key, string $default = ''): string {
|
||||
$s = db()->prepare('SELECT value FROM settings WHERE key_name = ?');
|
||||
$s->execute([$key]);
|
||||
$row = $s->fetchColumn();
|
||||
return $row !== false ? $row : $default;
|
||||
}
|
||||
|
||||
function set_setting(string $key, string $value): void {
|
||||
$s = db()->prepare(
|
||||
'INSERT INTO settings (key_name, value) VALUES (?, ?)
|
||||
ON DUPLICATE KEY UPDATE value = VALUES(value)'
|
||||
);
|
||||
$s->execute([$key, $value]);
|
||||
}
|
||||
|
||||
// ── Purge ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Delete connections older than retention_days setting.
|
||||
* Returns the number of rows deleted.
|
||||
*/
|
||||
function purge_old_connections(): int {
|
||||
$days = max(1, (int)get_setting('retention_days', '7'));
|
||||
$s = db()->prepare(
|
||||
'DELETE FROM connections WHERE occurred_at < NOW() - INTERVAL ? DAY'
|
||||
);
|
||||
$s->execute([$days]);
|
||||
return $s->rowCount();
|
||||
}
|
||||
|
||||
// ── Upstream version check ────────────────────────────────────────────────────
|
||||
|
||||
define('UPSTREAM_VERSION_URL', 'https://git.ny.daprogs.com/api/v1/repos/DAProgs/portspoof_concentrator/raw/version.php?ref=main');
|
||||
|
||||
@@ -56,7 +56,7 @@ code { font-family: 'Cascadia Code', 'Fira Mono', monospace; font-size: .8rem; c
|
||||
|
||||
label { display: block; margin-bottom: .75rem; font-size: .85rem; color: var(--muted); }
|
||||
label.inline { display: flex; align-items: center; gap: .5rem; color: var(--text); }
|
||||
label input[type=text], label input[type=url], label input[type=password] {
|
||||
label input[type=text], label input[type=url], label input[type=password], label input[type=number] {
|
||||
display: block; width: 100%; margin-top: .3rem;
|
||||
background: var(--bg); border: 1px solid var(--border); border-radius: 5px;
|
||||
color: var(--text); padding: .45rem .6rem; font-size: .9rem;
|
||||
|
||||
67
purge.php
Normal file
67
purge.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* portspoof_concentrator – HTTP purge trigger
|
||||
*
|
||||
* Runs the same purge logic as cron/purge.php when called over HTTP.
|
||||
* Deletes connections older than the retention_days setting.
|
||||
*
|
||||
* Authentication (any one of):
|
||||
* - Active web session (logged-in browser)
|
||||
* - Authorization: Bearer <TRIGGER_TOKEN>
|
||||
* - ?token=<TRIGGER_TOKEN>
|
||||
*
|
||||
* Usage:
|
||||
* GET/POST /purge.php
|
||||
* GET/POST /purge.php?token=<secret>
|
||||
* GET/POST /purge.php (with header: Authorization: Bearer <secret>)
|
||||
*
|
||||
* Always returns JSON.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// ── Run purge ─────────────────────────────────────────────────────────────────
|
||||
|
||||
$started_at = microtime(true);
|
||||
$retention_days = max(1, (int)get_setting('retention_days', '7'));
|
||||
$deleted = purge_old_connections();
|
||||
$elapsed_ms = (int)round((microtime(true) - $started_at) * 1000);
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'elapsed_ms' => $elapsed_ms,
|
||||
'retention_days' => $retention_days,
|
||||
'deleted' => $deleted,
|
||||
], JSON_PRETTY_PRINT);
|
||||
@@ -31,3 +31,11 @@ CREATE TABLE IF NOT EXISTS connections (
|
||||
KEY idx_dst_port (dst_port),
|
||||
CONSTRAINT fk_conn_node FOREIGN KEY (node_id) REFERENCES nodes (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key_name VARCHAR(64) NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (key_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT IGNORE INTO settings (key_name, value) VALUES ('retention_days', '7');
|
||||
|
||||
36
settings.php
36
settings.php
@@ -1,11 +1,15 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/auth.php';
|
||||
require_once __DIR__ . '/includes/functions.php';
|
||||
require_login();
|
||||
|
||||
$errors = [];
|
||||
$success = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'password') {
|
||||
$current = $_POST['current_password'] ?? '';
|
||||
$new = $_POST['new_password'] ?? '';
|
||||
$confirm = $_POST['confirm_password'] ?? '';
|
||||
@@ -21,7 +25,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
} else {
|
||||
$success = 'Password updated successfully.';
|
||||
}
|
||||
|
||||
} elseif ($action === 'retention') {
|
||||
$days = (int)($_POST['retention_days'] ?? 0);
|
||||
if ($days < 1) {
|
||||
$errors[] = 'Retention period must be at least 1 day.';
|
||||
} else {
|
||||
set_setting('retention_days', (string)$days);
|
||||
$success = 'Retention period saved.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$retention_days = (int)get_setting('retention_days', '7');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@@ -48,10 +64,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<main>
|
||||
|
||||
<?php if ($success): ?>
|
||||
<div class="alert ok"><?= htmlspecialchars($success, ENT_QUOTES, 'UTF-8') ?></div>
|
||||
<div class="alert ok"><?= h($success) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($errors as $e): ?>
|
||||
<div class="alert err"><?= htmlspecialchars($e, ENT_QUOTES, 'UTF-8') ?></div>
|
||||
<div class="alert err"><?= h($e) ?></div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<section class="card">
|
||||
@@ -60,6 +76,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<p class="muted">Authentication is disabled. Set <code>UI_PASS_HASH</code> in <code>config.php</code> to enable it.</p>
|
||||
<?php else: ?>
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="password">
|
||||
<label>Current password
|
||||
<input type="password" name="current_password" autocomplete="current-password" required>
|
||||
</label>
|
||||
@@ -74,6 +91,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>Data retention</h2>
|
||||
<p class="muted" style="margin-bottom:1rem;font-size:.85rem">
|
||||
Connections older than the retention period are removed by the daily purge cron.
|
||||
</p>
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="retention">
|
||||
<label>Retention period <small>(days)</small>
|
||||
<input type="number" name="retention_days" min="1" max="3650"
|
||||
value="<?= $retention_days ?>" style="width:120px">
|
||||
</label>
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
<?php include __DIR__ . '/includes/footer.php'; ?>
|
||||
</body>
|
||||
|
||||
10
setup.php
10
setup.php
@@ -41,4 +41,14 @@ if ($row > 0) {
|
||||
echo "Dropped legacy uq_event unique key.\n";
|
||||
}
|
||||
|
||||
// Migration: create settings table and seed default retention if upgrading
|
||||
$pdo->exec(
|
||||
"CREATE TABLE IF NOT EXISTS settings (
|
||||
key_name VARCHAR(64) NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
PRIMARY KEY (key_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
||||
);
|
||||
$pdo->exec("INSERT IGNORE INTO settings (key_name, value) VALUES ('retention_days', '7')");
|
||||
|
||||
echo "Database '" . DB_NAME . "' and tables created/migrated successfully.\n";
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<?php
|
||||
define('APP_VERSION', '2603.6');
|
||||
define('APP_VERSION', '2603.8');
|
||||
|
||||
Reference in New Issue
Block a user