Files
ms1inscription-v5/php/sync_static_db.php
stephan 3e3a0f1de7 Add static sync functionality and update UI for development tools
This commit introduces the `fxAdminStaticSyncAllowed` function to control access to static database synchronization tools, ensuring only authorized users in development and pre-production environments can access them. The UI is updated to display a new section for these tools in the admin interface, enhancing the overall user experience and access management for development tasks. Additionally, the `.gitignore` file is updated to exclude the `/output/` directory from version control.
2026-06-22 14:08:16 -04:00

221 lines
9.5 KiB
PHP

<?php
/**
* Tableau de bord sync tables statiques — préprod dev, superadm seulement.
* Source : devinscription_preprod
*/
require_once('inc_start_time.php');
require_once('inc_fonctions.php');
require_once('inc_fx_messages.php');
require_once('inc_fx_static_sync.php');
if (!fxAdminStaticSyncAllowed()) {
header('HTTP/1.1 403 Forbidden');
echo 'Accès refusé.';
exit;
}
function ssEsc($str) {
return htmlspecialchars((string)$str, ENT_QUOTES, 'UTF-8');
}
$strCsrf = fxStaticSyncEnsureCsrfToken();
$strFlash = '';
$strFlashType = 'info';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'sync') {
if (!fxStaticSyncValidateCsrf($_POST['csrf'] ?? '')) {
$strFlash = 'Jeton CSRF invalide.';
$strFlashType = 'bad';
} else {
$strTable = trim($_POST['table'] ?? '');
$strEnvId = trim($_POST['env'] ?? '');
$tabDef = fxStaticSyncFindTableDef($strTable);
$tabEnv = fxStaticSyncFindEnvDef($strEnvId);
if (!$tabDef || !$tabEnv || !empty($tabEnv['is_source'])) {
$strFlash = 'Paramètres invalides.';
$strFlashType = 'bad';
} else {
$tabConnections = fxStaticSyncOpenConnections();
$objSource = $tabConnections['dev_preprod'];
$objTarget = $tabConnections[$strEnvId];
if (!$objSource || !$objTarget) {
$strFlash = 'Connexion BD impossible.';
$strFlashType = 'bad';
} else {
$tabResult = fxStaticSyncCopyTable($objSource, $objTarget, $strTable);
if ($tabResult['ok']) {
$strFlash = $tabDef['label'] . ' → ' . $tabEnv['label'] . ' : ' . $tabResult['message'];
$strFlashType = 'ok';
} else {
$strFlash = $tabResult['message'];
$strFlashType = 'bad';
}
}
}
}
}
$tabConnections = fxStaticSyncOpenConnections();
$tabEnvs = fxStaticSyncEnvironmentDefinitions();
$tabReport = fxStaticSyncBuildReport($tabConnections);
$strDetailTable = trim($_GET['detail_table'] ?? '');
$strDetailEnv = trim($_GET['detail_env'] ?? '');
$tabDetail = null;
if ($strDetailTable !== '' && $strDetailEnv !== '') {
$tabDetail = fxStaticSyncGetDiffDetail($strDetailTable, $strDetailEnv, $tabConnections);
}
?><!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sync tables statiques</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; background: #f4f6f8; color: #222; }
h1 { font-size: 1.4rem; margin: 0 0 8px; }
.sub { color: #555; margin-bottom: 20px; font-size: 0.95rem; }
.flash { padding: 10px 14px; border-radius: 6px; margin-bottom: 16px; }
.flash-ok { background: #d4edda; border: 1px solid #b7dfc3; }
.flash-bad { background: #f8d7da; border: 1px solid #f1b8be; }
.flash-info { background: #e8f0fe; border: 1px solid #c5d7fb; }
table { width: 100%; border-collapse: collapse; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.08); }
th, td { border: 1px solid #ddd; padding: 8px 10px; vertical-align: top; font-size: 0.9rem; }
th { background: #eef2f7; text-align: left; }
.ss-ok { background: #d4edda; color: #155724; }
.ss-warn { background: #fff3cd; color: #856404; }
.ss-bad { background: #f8d7da; color: #721c24; }
.ss-muted { background: #e9ecef; color: #495057; }
.badge { display: inline-block; padding: 3px 8px; border-radius: 4px; font-size: 0.82rem; font-weight: 600; }
.count { font-size: 0.82rem; color: #444; margin-top: 4px; }
.btn { display: inline-block; margin-top: 6px; padding: 4px 10px; font-size: 0.82rem; border: 1px solid #888; background: #fff; border-radius: 4px; cursor: pointer; text-decoration: none; color: #222; }
.btn:hover { background: #f0f0f0; }
.btn-sync { border-color: #2e6da4; color: #1a4f7a; }
.detail-box { margin-top: 24px; background: #fff; padding: 16px; border: 1px solid #ccc; border-radius: 6px; }
.detail-box h2 { margin-top: 0; font-size: 1.1rem; }
.detail-list { margin: 0; padding-left: 18px; }
.detail-list li { margin-bottom: 8px; }
.legend { margin: 16px 0; font-size: 0.85rem; }
.legend span { display: inline-block; margin-right: 12px; padding: 2px 8px; border-radius: 4px; }
code { background: #f1f1f1; padding: 1px 4px; border-radius: 3px; }
</style>
</head>
<body>
<h1>Sync tables statiques</h1>
<p class="sub">
Source de vérité : <strong>dev préprod</strong> (<code><?php echo ssEsc($arrConDatabasePreProd['db'] ?? ''); ?></code>).
Comparaison par clé métier + contenu. Le bouton Sync écrase la table cible avec la source.
</p>
<?php if ($strFlash !== '') { ?>
<div class="flash flash-<?php echo ssEsc($strFlashType); ?>"><?php echo ssEsc($strFlash); ?></div>
<?php } ?>
<div class="legend">
<span class="ss-ok">Aligné</span>
<span class="ss-warn">Manque ou contenu différent</span>
<span class="ss-bad">Lignes en trop</span>
<span class="ss-muted">Table absente / connexion</span>
</div>
<table>
<thead>
<tr>
<th>Table</th>
<th>Source<br><small><?php echo ssEsc($tabEnvs['dev_preprod']['config']['db'] ?? ''); ?></small></th>
<?php foreach ($tabEnvs as $strEnvId => $tabEnv) {
if (!empty($tabEnv['is_source'])) {
continue;
} ?>
<th><?php echo ssEsc($tabEnv['label']); ?><br><small><?php echo ssEsc($tabEnv['config']['db'] ?? ''); ?></small></th>
<?php } ?>
</tr>
</thead>
<tbody>
<?php foreach ($tabReport as $tabRow) {
$tabDef = $tabRow['def']; ?>
<tr>
<td>
<strong><?php echo ssEsc($tabDef['label']); ?></strong><br>
<code><?php echo ssEsc($tabDef['table']); ?></code>
</td>
<td class="ss-muted">
<span class="badge"><?php echo (int)$tabRow['source_count']; ?> ligne(s)</span>
</td>
<?php foreach ($tabEnvs as $strEnvId => $tabEnv) {
if (!empty($tabEnv['is_source'])) {
continue;
}
$tabCmp = isset($tabRow['envs'][$strEnvId]) ? $tabRow['envs'][$strEnvId] : null;
if (!$tabCmp) {
continue;
}
$strClass = fxStaticSyncStatusClass($tabCmp['status']);
$strLabel = fxStaticSyncStatusLabel($tabCmp['status']);
?>
<td class="<?php echo ssEsc($strClass); ?>">
<span class="badge"><?php echo ssEsc($strLabel); ?></span>
<div class="count">
<?php echo (int)$tabCmp['target_count']; ?> / <?php echo (int)$tabCmp['source_count']; ?> ligne(s)
</div>
<?php if ($tabCmp['status'] !== 'ok' && $tabCmp['status'] !== 'no_connection') { ?>
<a class="btn" href="?detail_table=<?php echo urlencode($tabDef['table']); ?>&amp;detail_env=<?php echo urlencode($strEnvId); ?>">Détails</a>
<?php } ?>
<?php if ($tabCmp['status'] !== 'no_connection') { ?>
<form method="post" style="display:inline;" onsubmit="return confirm('Écraser <?php echo ssEsc($tabDef['table']); ?> sur <?php echo ssEsc($tabEnv['label']); ?> avec la source dev préprod ?');">
<input type="hidden" name="action" value="sync">
<input type="hidden" name="csrf" value="<?php echo ssEsc($strCsrf); ?>">
<input type="hidden" name="table" value="<?php echo ssEsc($tabDef['table']); ?>">
<input type="hidden" name="env" value="<?php echo ssEsc($strEnvId); ?>">
<button type="submit" class="btn btn-sync">Sync →</button>
</form>
<?php } ?>
</td>
<?php } ?>
</tr>
<?php } ?>
</tbody>
</table>
<?php if (is_array($tabDetail) && !empty($tabDetail['compare'])) {
$tabCmp = $tabDetail['compare'];
$tabDef = fxStaticSyncFindTableDef($strDetailTable);
$tabEnv = fxStaticSyncFindEnvDef($strDetailEnv);
?>
<div class="detail-box">
<h2>Détails — <?php echo ssEsc($tabDef['label'] ?? $strDetailTable); ?> → <?php echo ssEsc($tabEnv['label'] ?? $strDetailEnv); ?></h2>
<p>
Manquantes : <?php echo count($tabCmp['missing_keys']); ?> —
Différences : <?php echo count($tabCmp['diff_keys']); ?> —
En trop : <?php echo count($tabCmp['extra_keys']); ?>
<a class="btn" href="sync_static_db.php" style="margin-left:12px;">Fermer</a>
</p>
<?php if (!empty($tabDetail['samples'])) { ?>
<ul class="detail-list">
<?php foreach ($tabDetail['samples'] as $tabSample) { ?>
<li>
<strong><?php echo ssEsc(fxStaticSyncFormatKeyPreview($tabSample['key'])); ?></strong>
(<?php echo ssEsc($tabSample['type']); ?>)
<?php if ($tabDef['table'] === 'info' && $tabSample['type'] === 'diff') { ?>
<br>Source : <em><?php echo ssEsc($tabSample['source_texte']); ?></em>
<br>Cible : <em><?php echo ssEsc($tabSample['target_texte']); ?></em>
<?php } ?>
</li>
<?php } ?>
</ul>
<?php } else { ?>
<p>Aucun écart à afficher.</p>
<?php } ?>
</div>
<?php } ?>
</body>
</html>