Init Git Prod Mars 2026

This commit is contained in:
Oleg Barykin committed 2026-03-05 10:25:39 +01:00
commit d81efe93b1
362 files changed
+86627

No files matched your search

+271
View File
@@ -0,0 +1,271 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../includes/bootstrap_global.php';
/**
* UI helper: affiche une jolie carte centrée puis exit.
* $type: 'error' | 'info' | 'success'
*/
function ui_exit(string $title, string $message, int $status = 400, string $type = 'error', ?string $cta_href = '/', ?string $cta_label = null): void {
http_response_code($status);
$title = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
$message = nl2br(htmlspecialchars($message, ENT_QUOTES, 'UTF-8'));
$cta_href = $cta_href ? htmlspecialchars($cta_href, ENT_QUOTES, 'UTF-8') : null;
$cta_label = $cta_label ?? 'OK';
// Styles d'icône selon le type
$iconBorder = '#B91C1C'; // rouge par défaut (error)
$iconBg = 'rgba(185,28,28,.08)';
$iconRing = 'rgba(185,28,28,.25)';
$checkCSS = ''; // pour success
if ($type === 'info') {
$iconBorder = '#1D4ED8';
$iconBg = 'rgba(29,78,216,.08)';
$iconRing = 'rgba(29,78,216,.25)';
} elseif ($type === 'success') {
$iconBorder = '#1E8050';
$iconBg = 'rgba(30,128,80,.08)';
$iconRing = 'rgba(30,128,80,.20)';
$checkCSS = 'width:48px;height:36px;border:5px solid #1e8050;border-top:0;border-left:0;transform:rotate(45deg);';
}
echo '<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">';
echo '<title>'.$title.'</title>';
echo '<style>
html,body{height:100%;margin:0}
body{display:flex;align-items:center;justify-content:center;background:#8CA9FF;font-family:Montserrat,system-ui,Segoe UI,Roboto,Arial}
.card{max-width:720px;width:94%;background:#fff;border:1px solid #e5e7eb;border-radius:14px;box-shadow:0 8px 24px rgba(0,0,0,.08);padding:28px;text-align:center}
h1{margin:6px 0 10px;font-size:clamp(22px,2.4vw,28px)}
p{margin:0 0 18px;color:#6b7280;font-size:clamp(16px,2.2vw,18px)}
.row{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:8px}
.btn{padding:10px 14px;border-radius:10px;text-decoration:none;display:inline-block}
.btn-primary{background:#2563eb;color:#fff}
.btn-outline{border:1px solid #d1d5db;color:#374151;background:#fff}
</style></head><body><div class="card">';
// Icône
echo '<div style="width:102px;height:102px;border-radius:50%;background:'.$iconBg.';border:3px solid '.$iconRing.';display:flex;align-items:center;justify-content:center;margin:0 auto 20px;">';
if ($type === 'success') {
echo '<div style="'.$checkCSS.'"></div>';
} elseif ($type === 'info') {
echo '<div style="width:14px;height:48px;border-left:5px solid '.$iconBorder.';position:relative;">
<div style="position:absolute;top:-18px;left:-5px;width:10px;height:10px;background:'.$iconBorder.';border-radius:50%;"></div>
</div>';
} else { // error
echo '<div style="position:relative;width:48px;height:48px;">
<div style="position:absolute;top:0;left:22px;width:5px;height:48px;background:'.$iconBorder.';transform:rotate(45deg);border-radius:2px;"></div>
<div style="position:absolute;top:0;left:22px;width:5px;height:48px;background:'.$iconBorder.';transform:rotate(-45deg);border-radius:2px;"></div>
</div>';
}
echo '</div>';
echo '<h1>'.$title.'</h1>';
echo '<p>'.$message.'</p>';
if ($cta_href) {
echo '<div class="row"><a class="btn btn-outline" href="'.$cta_href.'">'.$cta_label.'</a></div>';
}
echo '</div></body></html>';
exit;
}
// =====================
// 1) Token présent & valide
// =====================
$rawToken = trim((string)($_GET['token'] ?? ''));
$rawAL = trim((string)($_GET['al'] ?? ''));
if ($rawAL !== '' && !preg_match('/^[A-Fa-f0-9]{64}$/', $rawAL)) {
$rawAL = ''; // on ignore si format suspect
}
if ($rawToken === '') {
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_lien_invalide'] ?? 'Lien invalide.',
400,
'error',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
);
}
// Optionnel : on vérifie le format attendu (bin2hex(32) => 64 hexdigits)
if (!preg_match('/^[A-Fa-f0-9]{64}$/', $rawToken)) {
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_lien_invalide'] ?? 'Lien invalide.',
400,
'error',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
);
}
// =====================
// 2) Récupération du compte
// =====================
$stmt = $pdo->prepare("SELECT * FROM compte WHERE token_validation = :token LIMIT 1");
$stmt->execute(['token' => $rawToken]);
$compte = $stmt->fetch();
if (!$compte) {
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_token_invalide'] ?? 'Token invalide.',
400,
'error',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
);
}
// =====================
// 3) Validité temporelle (6h)
// =====================
try {
$created_at = new DateTime($compte['cree_le']);
} catch (Throwable $e) {
// Par prudence : si la date est illisible, on refuse
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_lien_invalide'] ?? 'Lien invalide.',
400,
'error',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
);
}
$now = new DateTime();
$diffH = ($now->getTimestamp() - $created_at->getTimestamp()) / 3600;
if ($diffH > 6) {
// Supprimer les enregistrements expirés
try {
$pdo->beginTransaction();
$pdo->prepare("DELETE FROM compte WHERE id = :id")->execute(['id' => $compte['id']]);
$ref_id = (int)$compte['reference_id'];
if (($compte['role'] ?? '') === 'praticien') {
$pdo->prepare("DELETE FROM praticien WHERE id = :id")->execute(['id' => $ref_id]);
} else {
$pdo->prepare("DELETE FROM client WHERE id = :id")->execute(['id' => $ref_id]);
}
$pdo->commit();
} catch (Throwable $e) {
if ($pdo->inTransaction()) $pdo->rollBack();
error_log('Validation email - purge expirée échouée: '.$e->getMessage());
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_serveur'] ?? 'Erreur serveur.',
500,
'error',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
);
}
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_lien_expire'] ?? 'Le lien de validation a expiré.',
410,
'info',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
);
}
// =====================
// 4) Valider l'email (maj compte)
// =====================
try {
$stmt = $pdo->prepare("UPDATE compte SET email_valide = 1, token_validation = NULL WHERE id = :id");
$stmt->execute(['id' => $compte['id']]);
} catch (Throwable $e) {
error_log('Validation email - UPDATE échouée: '.$e->getMessage());
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_serveur'] ?? 'Erreur serveur.',
500,
'error',
'/',
$translations['retour_acceuil'] ?? 'Retour à l’accueil'
);
}
// =====================
// 4bis) Auto-login (si al fourni et valide)
// =====================
if ($rawAL !== '') {
// On recharge le compte pour lire token_autologin + expire (plus propre)
$stmt = $pdo->prepare("SELECT id, role, reference_id, token_autologin, token_autologin_expire
FROM compte
WHERE id = :id
LIMIT 1");
$stmt->execute(['id' => (int)$compte['id']]);
$c = $stmt->fetch(PDO::FETCH_ASSOC);
if ($c && !empty($c['token_autologin']) && hash_equals((string)$c['token_autologin'], $rawAL)) {
// Expiration (obligatoire)
$expOk = false;
if (!empty($c['token_autologin_expire'])) {
try {
$exp = new DateTime((string)$c['token_autologin_expire']);
$expOk = (new DateTime()) <= $exp;
} catch (Throwable $e) {
$expOk = false;
}
}
if ($expOk) {
// One-time : on invalide le token autologin
$pdo->prepare("UPDATE compte
SET token_autologin = NULL, token_autologin_expire = NULL
WHERE id = :id")
->execute(['id' => (int)$c['id']]);
// Connexion session (même logique pour client/praticien)
session_regenerate_id(true);
$_SESSION['compte_id'] = (int)$c['id']; // <-- IMPORTANT (secure.php l'exige)
$_SESSION['role'] = (string)$c['role'];
$_SESSION['reference_id'] = (int)$c['reference_id'];
if ($_SESSION['role'] === 'client') {
$_SESSION['client_id'] = (int)$c['reference_id'];
header('Location: /public/client/informations.php');
exit;
}
if ($_SESSION['role'] === 'praticien') {
$_SESSION['praticien_id'] = (int)$c['reference_id'];
header('Location: /public/praticien/informations.php');
exit;
}
// fallback si role inconnu
header('Location: /connexion.php');
exit;
}
}
}
// =====================
// 5) Écran final (succès)
// =====================
// Le texte de succès chez toi contient un lien HTML ; on affiche un message “texte” + un vrai bouton vers la connexion.
$successMsg = isset($translations['success_validation_texte'])
? strip_tags((string)$translations['success_validation_texte'])
: 'Votre compte est confirmé. Vous pouvez maintenant vous connecter.';
ui_exit(
$translations['success_validation_titre'] ?? 'Compte confirmé !',
$successMsg,
200,
'success',
'/connexion.php',
$translations['btn_se_connecter'] ?? 'Se connecter'
);