@@ -181,5 +181,17 @@ $this->pdo->exec("
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
)
|
||||
");
|
||||
|
||||
$this->pdo->exec("
|
||||
CREATE TABLE IF NOT EXISTS audit_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
action TEXT NOT NULL,
|
||||
entity_type TEXT NOT NULL,
|
||||
entity_id INTEGER,
|
||||
details TEXT,
|
||||
username TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
)
|
||||
");
|
||||
}
|
||||
}
|
||||
+136
-13
@@ -121,7 +121,7 @@ class Repository
|
||||
return $row ? Alert::fromRow($row) : null;
|
||||
}
|
||||
|
||||
public function getAlerts(int $limit = 100, int $offset = 0, ?string $status = null, ?string $severity = null): array
|
||||
public function getAlerts(int $limit = 100, int $offset = 0, ?string $status = null, ?string $severity = null, ?string $since = null, ?string $until = null): array
|
||||
{
|
||||
$where = [];
|
||||
$params = [];
|
||||
@@ -134,6 +134,14 @@ class Repository
|
||||
$where[] = 'severity = ?';
|
||||
$params[] = $severity;
|
||||
}
|
||||
if ($since) {
|
||||
$where[] = 'created_at >= ?';
|
||||
$params[] = $since;
|
||||
}
|
||||
if ($until) {
|
||||
$where[] = 'created_at <= ?';
|
||||
$params[] = $until;
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM alerts";
|
||||
if ($where) {
|
||||
@@ -187,29 +195,52 @@ class Repository
|
||||
$stmt->execute([$line, $sourceId, $sourceName, $level]);
|
||||
}
|
||||
|
||||
public function searchLogEntries(string $query, int $limit = 200, int $offset = 0): array
|
||||
public function searchLogEntries(string $query, int $limit = 200, int $offset = 0, ?string $since = null, ?string $until = null): array
|
||||
{
|
||||
$query = trim($query);
|
||||
if ($query === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$where = [];
|
||||
$params = [];
|
||||
|
||||
if ($since) {
|
||||
$where[] = 'e.created_at >= ?';
|
||||
$params[] = $since;
|
||||
}
|
||||
if ($until) {
|
||||
$where[] = 'e.created_at <= ?';
|
||||
$params[] = $until;
|
||||
}
|
||||
|
||||
if ($query === '*') {
|
||||
$stmt = $this->db->pdo()->prepare(
|
||||
"SELECT * FROM log_entries ORDER BY created_at DESC LIMIT ? OFFSET ?"
|
||||
);
|
||||
$stmt->execute([$limit, $offset]);
|
||||
$sql = "SELECT * FROM log_entries e";
|
||||
if ($where) {
|
||||
$sql .= ' WHERE ' . implode(' AND ', $where);
|
||||
}
|
||||
$sql .= " ORDER BY e.created_at DESC LIMIT ? OFFSET ?";
|
||||
$params[] = $limit;
|
||||
$params[] = $offset;
|
||||
$stmt = $this->db->pdo()->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
$like = $this->toLikePattern($query);
|
||||
$stmt = $this->db->pdo()->prepare(
|
||||
"SELECT e.* FROM log_entries e
|
||||
WHERE e.line LIKE ?
|
||||
ORDER BY e.created_at DESC
|
||||
LIMIT ? OFFSET ?"
|
||||
);
|
||||
$stmt->execute([$like, $limit, $offset]);
|
||||
$where[] = 'e.line LIKE ?';
|
||||
$params[] = $like;
|
||||
$sql = "SELECT e.* FROM log_entries e";
|
||||
|
||||
if ($where) {
|
||||
$sql .= ' WHERE ' . implode(' AND ', $where);
|
||||
}
|
||||
$sql .= " ORDER BY e.created_at DESC LIMIT ? OFFSET ?";
|
||||
$params[] = $limit;
|
||||
$params[] = $offset;
|
||||
|
||||
$stmt = $this->db->pdo()->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
@@ -338,4 +369,96 @@ class Repository
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- Audit Log ---
|
||||
|
||||
public function logAudit(string $action, string $entityType, ?int $entityId = null, ?string $details = null, ?string $username = null): void
|
||||
{
|
||||
$stmt = $this->db->pdo()->prepare(
|
||||
"INSERT INTO audit_log (action, entity_type, entity_id, details, username) VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->execute([$action, $entityType, $entityId, $details, $username]);
|
||||
}
|
||||
|
||||
public function getAuditLog(int $limit = 50): array
|
||||
{
|
||||
return $this->db->pdo()->prepare(
|
||||
"SELECT * FROM audit_log ORDER BY created_at DESC LIMIT ?"
|
||||
)->execute([$limit])->fetchAll();
|
||||
}
|
||||
|
||||
// --- Retention ---
|
||||
|
||||
public function purgeOldData(int $logDays = 30, int $alertDays = 90): array
|
||||
{
|
||||
$deletedLogs = $this->db->pdo()->prepare(
|
||||
"DELETE FROM log_entries WHERE created_at < datetime('now', ?)"
|
||||
)->execute(['-' . $logDays . ' days'])->rowCount();
|
||||
|
||||
$deletedAlerts = $this->db->pdo()->prepare(
|
||||
"DELETE FROM alerts WHERE status = 'resolved' AND created_at < datetime('now', ?)"
|
||||
)->execute(['-' . $alertDays . ' days'])->rowCount();
|
||||
|
||||
$this->db->pdo()->exec("DELETE FROM rate_limiter WHERE window_start < " . (time() - 86400));
|
||||
|
||||
return ['log_entries_deleted' => $deletedLogs, 'alerts_deleted' => $deletedAlerts];
|
||||
}
|
||||
|
||||
// --- Log Context ---
|
||||
|
||||
public function getLogContext(int $id, int $before = 5, int $after = 5): array
|
||||
{
|
||||
$beforeStmt = $this->db->pdo()->prepare(
|
||||
"SELECT * FROM log_entries WHERE id < ? ORDER BY id DESC LIMIT ?"
|
||||
);
|
||||
$beforeStmt->execute([$id, $before]);
|
||||
$beforeRows = array_reverse($beforeStmt->fetchAll());
|
||||
|
||||
$current = $this->db->pdo()->prepare("SELECT * FROM log_entries WHERE id = ?");
|
||||
$current->execute([$id]);
|
||||
$currentRow = $current->fetch();
|
||||
|
||||
$afterStmt = $this->db->pdo()->prepare(
|
||||
"SELECT * FROM log_entries WHERE id > ? ORDER BY id ASC LIMIT ?"
|
||||
);
|
||||
$afterStmt->execute([$id, $after]);
|
||||
$afterRows = $afterStmt->fetchAll();
|
||||
|
||||
return [
|
||||
'before' => $beforeRows,
|
||||
'current' => $currentRow,
|
||||
'after' => $afterRows,
|
||||
];
|
||||
}
|
||||
|
||||
// --- Bulk Operations ---
|
||||
|
||||
public function bulkUpdateAlertStatus(array $ids, AlertStatus $status): int
|
||||
{
|
||||
if (empty($ids)) return 0;
|
||||
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
||||
$stmt = $this->db->pdo()->prepare(
|
||||
"UPDATE alerts SET status = ? WHERE id IN ($placeholders)"
|
||||
);
|
||||
$stmt->execute(array_merge([$status->value], $ids));
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
public function exportAlerts(array $ids): array
|
||||
{
|
||||
if (empty($ids)) return [];
|
||||
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
||||
$stmt = $this->db->pdo()->prepare("SELECT * FROM alerts WHERE id IN ($placeholders) ORDER BY id");
|
||||
$stmt->execute($ids);
|
||||
return array_map(fn(array $r) => Alert::fromRow($r), $stmt->fetchAll());
|
||||
}
|
||||
|
||||
public function exportLogs(array $ids): array
|
||||
{
|
||||
if (empty($ids)) return [];
|
||||
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
||||
$stmt = $this->db->pdo()->prepare("SELECT * FROM log_entries WHERE id IN ($placeholders) ORDER BY id");
|
||||
$stmt->execute($ids);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user