Init Git Prod Mars 2026
This commit is contained in:
commit
d81efe93b1
362 files changed
+86627
No files matched your search
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
if (($_GET['page'] ?? '') !== 'backup_db') 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['backup_access_denied'] ?? 'Accès refusé.'), ENT_QUOTES, 'UTF-8')
|
||||
. '</div></div>';
|
||||
return;
|
||||
}
|
||||
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['backup_db_unavailable'] ?? 'DB indisponible.'), ENT_QUOTES, 'UTF-8')
|
||||
. '</div></div>';
|
||||
return;
|
||||
}
|
||||
try{$pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);}catch(Throwable){}
|
||||
|
||||
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; }
|
||||
|
||||
$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'];
|
||||
|
||||
$siteRoot=realpath(__DIR__.'/../../');
|
||||
$backupDir=$siteRoot.'/backups/db';
|
||||
@is_dir($backupDir) || @mkdir($backupDir,0755,true);
|
||||
|
||||
$msg=null; $ok=null;
|
||||
|
||||
// Params
|
||||
$retentionDays = max(0,(int)($_POST['retention'] ?? ($_GET['retention'] ?? 30)));
|
||||
$useGzip = true;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD']==='POST') {
|
||||
if (!hash_equals($CSRF, $_POST['csrf'] ?? '')) {
|
||||
$msg = (string)($translations['backup_csrf_invalid'] ?? 'CSRF invalide.');
|
||||
}
|
||||
else {
|
||||
$act = $_POST['action'] ?? '';
|
||||
if ($act==='create') {
|
||||
// Dump via PDO (fallback si mysqldump non dispo)
|
||||
$dbName = $pdo->query("SELECT DATABASE()")->fetchColumn();
|
||||
$fname = 'db_'.$dbName.'_'.date('Ymd_His').'.sql';
|
||||
$tmp = $backupDir.'/'.$fname;
|
||||
|
||||
// --- export ---
|
||||
$fh = @fopen($tmp,'wb');
|
||||
if(!$fh){
|
||||
$tpl = (string)($translations['backup_cannot_write_dir'] ?? "Impossible d'écrire dans {DIR}");
|
||||
$msg = str_replace('{DIR}', (string)$backupDir, $tpl);
|
||||
}
|
||||
else {
|
||||
fwrite($fh, "-- Backup of `$dbName` on ".date('c')."\nSET FOREIGN_KEY_CHECKS=0;\n");
|
||||
// tables
|
||||
$tables = $pdo->query("SHOW FULL TABLES WHERE Table_Type='BASE TABLE'")->fetchAll(PDO::FETCH_COLUMN);
|
||||
foreach ($tables as $t) {
|
||||
// DDL
|
||||
$row = $pdo->query("SHOW CREATE TABLE `$t`")->fetch(PDO::FETCH_ASSOC);
|
||||
fwrite($fh, "\n-- Structure `$t`\nDROP TABLE IF EXISTS `$t`;\n".$row['Create Table'].";\n");
|
||||
// DML
|
||||
$st = $pdo->query("SELECT * FROM `$t`", PDO::FETCH_ASSOC);
|
||||
$cols = array_map(fn($c)=>'`'.$c.'`', array_keys($st->fetch() ?: []));
|
||||
if ($cols) {
|
||||
// Refaire la requête pour ne pas “perdre” la première ligne
|
||||
$st = $pdo->query("SELECT * FROM `$t`", PDO::FETCH_ASSOC);
|
||||
$colList = implode(',', $cols);
|
||||
$placeholders = '(' . implode(',', array_fill(0, count($cols), '?')) . ')';
|
||||
$batch=[]; $n=0;
|
||||
foreach($st as $row){
|
||||
$vals = array_values($row);
|
||||
$vals = array_map(function($v) use($pdo){ return isset($v)?$pdo->quote((string)$v):"NULL"; }, $vals);
|
||||
$batch[] = '('.implode(',', $vals).')';
|
||||
if (++$n % 200 === 0) { fwrite($fh, "INSERT INTO `$t` ($colList) VALUES\n".implode(",\n",$batch).";\n"); $batch=[]; }
|
||||
}
|
||||
if ($batch){ fwrite($fh, "INSERT INTO `$t` ($colList) VALUES\n".implode(",\n",$batch).";\n"); }
|
||||
}
|
||||
}
|
||||
fwrite($fh, "SET FOREIGN_KEY_CHECKS=1;\n");
|
||||
fclose($fh);
|
||||
|
||||
// gzip
|
||||
if ($useGzip) {
|
||||
$gz = $tmp.'.gz';
|
||||
$gzh = gzopen($gz,'wb9');
|
||||
$src = fopen($tmp,'rb');
|
||||
while(!feof($src)){ gzwrite($gzh, fread($src, 1<<20)); }
|
||||
fclose($src); gzclose($gzh);
|
||||
@unlink($tmp);
|
||||
$final=$gz;
|
||||
} else { $final=$tmp; }
|
||||
|
||||
$tpl = (string)($translations['backup_created_ok'] ?? 'Sauvegarde créée : {FILE}');
|
||||
$ok = str_replace('{FILE}', basename($final), $tpl);
|
||||
|
||||
AdminAudit::log($pdo, [
|
||||
'admin_id'=>(int)($_SESSION['user_id']??0),
|
||||
'action'=>'backup_create',
|
||||
'scope'=>'maintenance',
|
||||
'meta'=>['file'=>basename($final),'dir'=>'backups/db']
|
||||
]);
|
||||
}
|
||||
}
|
||||
elseif ($act==='prune') {
|
||||
$cut = time()-($retentionDays*86400);
|
||||
$n=0; foreach (glob($backupDir.'/db_*.sql*') as $f){ if(filemtime($f)<$cut){ @unlink($f); $n++; } }
|
||||
|
||||
$tpl = (string)($translations['backup_retention_applied'] ?? 'Rétention appliquée : {N} fichier(s) supprimé(s) (> {D} j)');
|
||||
$ok = str_replace(['{N}','{D}'], [(string)$n, (string)$retentionDays], $tpl);
|
||||
|
||||
AdminAudit::log($pdo, [
|
||||
'admin_id'=>(int)($_SESSION['user_id']??0),
|
||||
'action'=>'backup_prune',
|
||||
'scope'=>'maintenance',
|
||||
'meta'=>['retention_days'=>$retentionDays]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Liste
|
||||
$files = array_map('basename', glob($backupDir.'/db_*.sql*') ?: []);
|
||||
usort($files, fn($a,$b)=>strcmp($b,$a)); // plus récent d'abord
|
||||
?>
|
||||
<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['backup_title'] ?? 'Sauvegardes de la base') ?></h3>
|
||||
<span class="badge text-bg-secondary">
|
||||
<?php
|
||||
$tpl = (string)($translations['backup_files_badge'] ?? '{N} fichier(s)');
|
||||
echo $e(str_replace('{N}', (string)count($files), $tpl));
|
||||
?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<?php if($ok): ?><div class="alert alert-success"><?= $e($ok) ?></div><?php endif; ?>
|
||||
<?php if($msg): ?><div class="alert alert-danger"><?= $e($msg) ?></div><?php endif; ?>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<form method="post" class="row g-3">
|
||||
<input type="hidden" name="csrf" value="<?= $e($CSRF) ?>">
|
||||
<div class="col-12 d-flex gap-2">
|
||||
<button class="btn btn-dark" name="action" value="create"
|
||||
onclick="return confirm('<?= $e($translations['backup_confirm_create'] ?? 'Créer un dump SQL compressé ?') ?>')">
|
||||
<?= $e($translations['backup_btn_create'] ?? 'Sauvegarder') ?>
|
||||
</button>
|
||||
<div class="ms-auto"></div>
|
||||
<div class="input-group" style="max-width:320px">
|
||||
<span class="input-group-text"><?= $e($translations['backup_retention_label'] ?? 'Rétention (jours)') ?></span>
|
||||
<input class="form-control" type="number" min="0" name="retention" value="<?= (int)$retentionDays ?>">
|
||||
<button class="btn btn-outline-danger" name="action" value="prune"
|
||||
onclick="return confirm('<?= $e($translations['backup_confirm_prune'] ?? 'Supprimer les backups plus anciens ?') ?>')">
|
||||
<?= $e($translations['backup_btn_prune'] ?? 'Go') ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer small text-muted">
|
||||
<?php
|
||||
$tpl = (string)($translations['backup_folder_line'] ?? 'Dossier: {DIR}');
|
||||
echo $e(str_replace('{DIR}', (string)$backupDir, $tpl));
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm mb-0 mobile-cards">
|
||||
<thead><tr>
|
||||
<th><?= $e($translations['backup_th_file'] ?? 'Fichier') ?></th>
|
||||
<th><?= $e($translations['backup_th_size'] ?? 'Taille') ?></th>
|
||||
<th><?= $e($translations['backup_th_date'] ?? 'Date') ?></th>
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<?php foreach($files as $f):
|
||||
$full=$backupDir.'/'.$f; ?>
|
||||
<tr>
|
||||
<td><code><?= $e($f) ?></code></td>
|
||||
<td><?= number_format((filesize($full)?:0)/1024,1) ?> KB</td>
|
||||
<td><?= date('Y-m-d H:i:s', filemtime($full)?:time()) ?></td>
|
||||
<td class="text-end">
|
||||
<a class="btn btn-sm btn-outline-primary"
|
||||
href="/admin/tools/backup_download.php?f=<?= $e(urlencode($f)) ?>&csrf=<?= $e($CSRF) ?>">
|
||||
<?= $e($translations['backup_btn_download'] ?? 'Télécharger') ?>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; if(!$files): ?>
|
||||
<tr><td colspan="4" class="text-muted p-3"><?= $e($translations['backup_none_yet'] ?? 'Aucun backup encore.') ?></td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in new issue
Block a user