146 lines
6.3 KiB
PHP
146 lines
6.3 KiB
PHP
<?php
|
||
if (($_GET['page'] ?? '') !== 'audit') return;
|
||
require_once __DIR__ . '/../../public/includes/init_admin.php';
|
||
|
||
$roles = (array)($_SESSION['role'] ?? []);
|
||
if (!in_array('admin', $roles, true)) {
|
||
echo '<div class="container my-4"><div class="alert alert-danger">'
|
||
. htmlspecialchars((string)($translations['audite_access_denied'] ?? 'Accès refusé.'), ENT_QUOTES, 'UTF-8')
|
||
. '</div></div>';
|
||
return;
|
||
}
|
||
|
||
require_once __DIR__.'/_lib_audit.php';
|
||
// reauth (10 min)
|
||
$mustReauth=function(int $ttl=600){$t=(int)($_SESSION['admin_reauth_at']??0);return ($t===0||(time()-$t)>$ttl);};
|
||
if ($mustReauth()){ $qs=http_build_query(['page'=>'reauth','redir'=>($_SERVER['REQUEST_URI']??'index.php?page=maintenance')]); header('Location:?'.$qs); exit; }
|
||
if (!isset($pdo)) { $db = realpath(__DIR__.'/../../includes/db.php'); if ($db) require_once $db; }
|
||
if (!isset($pdo) || !($pdo instanceof PDO)) {
|
||
echo '<div class="container my-4"><div class="alert alert-danger">'
|
||
. htmlspecialchars((string)($translations['audite_db_unavailable'] ?? 'DB indisponible.'), ENT_QUOTES, 'UTF-8')
|
||
. '</div></div>';
|
||
return;
|
||
}
|
||
try { $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(Throwable) {}
|
||
|
||
$e = static fn($s)=>htmlspecialchars((string)$s,ENT_QUOTES,'UTF-8');
|
||
if (empty($_SESSION['csrf_token'])) $_SESSION['csrf_token']=bin2hex(random_bytes(32));
|
||
$CSRF=$_SESSION['csrf_token'];
|
||
|
||
// filtres
|
||
$qAdmin = (int)($_GET['admin_id'] ?? 0);
|
||
$qAct = trim($_GET['action'] ?? '');
|
||
$qFrom = trim($_GET['from'] ?? '');
|
||
$qTo = trim($_GET['to'] ?? '');
|
||
$page = max(1, (int)($_GET['p'] ?? 1));
|
||
$per = min(200, max(10,(int)($_GET['per'] ?? 50)));
|
||
|
||
$w=[]; $p=[];
|
||
if ($qAdmin>0){ $w[]='admin_id=:aid'; $p[':aid']=$qAdmin; }
|
||
if ($qAct!==''){ $w[]='action=:act'; $p[':act']=$qAct; }
|
||
if ($qFrom!==''){ $w[]='created_at>=:f'; $p[':f']=$qFrom.' 00:00:00'; }
|
||
if ($qTo!==''){ $w[]='created_at<=:t'; $p[':t']=$qTo.' 23:59:59'; }
|
||
$where = $w?('WHERE '.implode(' AND ',$w)):'';
|
||
|
||
$cnt = $pdo->prepare("SELECT COUNT(*) FROM admin_audit $where");
|
||
$cnt->execute($p); $total=(int)$cnt->fetchColumn();
|
||
$pages = max(1,(int)ceil($total/$per));
|
||
$off = ($page-1)*$per;
|
||
|
||
$sql = "SELECT id,admin_id,action,scope,target_table,target_id,meta_json,INET6_NTOA(ip) ip,user_agent,created_at
|
||
FROM admin_audit $where ORDER BY id DESC LIMIT $per OFFSET $off";
|
||
$stmt=$pdo->prepare($sql); $stmt->execute($p);
|
||
$rows=$stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
?>
|
||
<div class="shadow p-4 rounded bg-light">
|
||
<div class="container my-4">
|
||
<div class="d-flex align-items-center gap-2 mb-3">
|
||
<h3 class="m-0"><?= $e($translations['audite_title'] ?? 'Journal d’audit (actions admin)') ?></h3>
|
||
<span class="badge text-bg-secondary">
|
||
<?php
|
||
$tpl = (string)($translations['audite_total_badge'] ?? '{N} lignes');
|
||
echo $e(str_replace('{N}', (string)((int)$total), $tpl));
|
||
?>
|
||
</span>
|
||
</div>
|
||
|
||
<form class="row g-2 mb-3" method="get">
|
||
<input type="hidden" name="page" value="audit">
|
||
<div class="col-6 col-md-2">
|
||
<input class="form-control" name="admin_id" type="number"
|
||
placeholder="<?= $e($translations['audite_ph_admin_id'] ?? 'admin_id') ?>"
|
||
value="<?= $qAdmin ?>">
|
||
</div>
|
||
<div class="col-6 col-md-2">
|
||
<input class="form-control" name="action"
|
||
placeholder="<?= $e($translations['audite_ph_action'] ?? 'action') ?>"
|
||
value="<?= $e($qAct) ?>">
|
||
</div>
|
||
<div class="col-6 col-md-2"><input class="form-control" name="from" type="date" value="<?= $e($qFrom) ?>"></div>
|
||
<div class="col-6 col-md-2"><input class="form-control" name="to" type="date" value="<?= $e($qTo) ?>"></div>
|
||
<div class="col-6 col-md-2">
|
||
<select class="form-select" name="per">
|
||
<?php foreach ([25,50,100,200] as $n): ?>
|
||
<option value="<?= $n ?>" <?= $per===$n?'selected':'' ?>>
|
||
<?php
|
||
$tpl = (string)($translations['audite_per_page'] ?? '{N}/page');
|
||
echo $e(str_replace('{N}', (string)$n, $tpl));
|
||
?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="col-6 col-md-2 d-grid">
|
||
<button class="btn btn-outline-secondary"><?= $e($translations['audite_btn_filter'] ?? 'Filtrer') ?></button>
|
||
</div>
|
||
</form>
|
||
|
||
<div class="card">
|
||
<div class="table-responsive">
|
||
<table class="table table-sm table-striped align-middle mb-0 mobile-cards">
|
||
<thead><tr>
|
||
<th><?= $e($translations['audite_th_id'] ?? '#') ?></th>
|
||
<th><?= $e($translations['audite_th_admin'] ?? 'Admin') ?></th>
|
||
<th><?= $e($translations['audite_th_action'] ?? 'Action') ?></th>
|
||
<th><?= $e($translations['audite_th_scope'] ?? 'Scope') ?></th>
|
||
<th><?= $e($translations['audite_th_target'] ?? 'Cible') ?></th>
|
||
<th><?= $e($translations['audite_th_ip'] ?? 'IP') ?></th>
|
||
<th><?= $e($translations['audite_th_date'] ?? 'Date') ?></th>
|
||
<th><?= $e($translations['audite_th_meta'] ?? 'Meta') ?></th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<?php foreach($rows as $r): ?>
|
||
<tr>
|
||
<td><?= (int)$r['id'] ?></td>
|
||
<td><?= (int)$r['admin_id'] ?></td>
|
||
<td><span class="badge text-bg-dark"><?= $e($r['action']) ?></span></td>
|
||
<td><?= $e($r['scope'] ?? '') ?></td>
|
||
<td><small><?= $e($r['target_table'] ?? '') ?><?= $r['target_id']?(' #'.(int)$r['target_id']):'' ?></small></td>
|
||
<td><small><?= $e($r['ip'] ?? '') ?></small></td>
|
||
<td><small><?= $e($r['created_at']) ?></small></td>
|
||
<td><code class="small"><?= $e($r['meta_json'] ?? '') ?></code></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php if ($pages>1): ?>
|
||
<div class="card-footer d-flex justify-content-between">
|
||
<div>
|
||
<?php
|
||
$tpl = (string)($translations['audite_page_x_y'] ?? 'Page {X} / {Y}');
|
||
echo $e(str_replace(['{X}','{Y}'], [(string)$page, (string)$pages], $tpl));
|
||
?>
|
||
</div>
|
||
<div class="btn-group">
|
||
<?php for($i=max(1,$page-2);$i<=min($pages,$page+2);$i++):
|
||
$q=$_GET; $q['p']=$i; ?>
|
||
<a class="btn btn-sm <?= $i===$page?'btn-dark':'btn-outline-dark' ?>" href="?<?= http_build_query($q) ?>"><?= $i ?></a>
|
||
<?php endfor; ?>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|