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

+292
View File
@@ -0,0 +1,292 @@
<?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;
}
// =====================
// Accès direct interdit
// =====================
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
ui_exit(
$translations['err_acces_incorrect_titre'] ?? 'Accès incorrect',
$translations['err_acces_incorrect_msg'] ?? "Cette page doit être appelée depuis le formulaire d'inscription.",
405,
'info',
'inscription_client.php',
$translations['retour_formulaire'] ?? 'Retour au formulaire'
);
}
// =====================
// Données & vérifs
// =====================
$nom = trim($_POST['nom'] ?? '');
$prenom = trim($_POST['prenom'] ?? '');
$email = trim($_POST['email'] ?? '');
$mdp = $_POST['mot_de_passe'] ?? '';
// Champs obligatoires
if ($nom === '' || $prenom === '' || $email === '' || $mdp === '') {
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_champs_obligatoires'] ?? 'Merci de remplir tous les champs obligatoires.',
400,
'error',
'inscription_client.php',
$translations['retour_formulaire'] ?? 'Retour au formulaire'
);
}
// Email valide + pas d’injection CR/LF
if (!filter_var($email, FILTER_VALIDATE_EMAIL) || preg_match('/[\r\n]/', $email)) {
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_email_non_valide'] ?? 'Email invalide.',
400,
'error',
'inscription_client.php'
);
}
// Acceptation CGU
$accept_cgu = isset($_POST['accept_cgu']) ? 1 : 0;
if ($accept_cgu !== 1) {
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['cgu_obligatoire'] ?? 'Vous devez accepter les CGU.',
400,
'info',
'inscription_client.php'
);
}
// =====================
// Transaction (client + compte)
// =====================
try {
$pdo->beginTransaction();
// Email déjà pris ?
$stmt = $pdo->prepare("SELECT id FROM compte WHERE email = :email LIMIT 1");
$stmt->execute(['email' => $email]);
if ($stmt->fetch()) {
$pdo->rollBack();
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_email_deja_utilise'] ?? 'Cet email est déjà utilisé.',
409,
'error',
'inscription_client.php'
);
}
// 1) Insert client
$stmt = $pdo->prepare("INSERT INTO client (nom, prenom, email, accepted_cgu) VALUES (:nom, :prenom, :email, :accepted_cgu)");
$stmt->execute([
'nom' => $nom,
'prenom' => $prenom,
'email' => $email,
'accepted_cgu' => $accept_cgu
]);
$client_id = (int)$pdo->lastInsertId();
// 2) Créer compte
$mot_de_passe_hash = password_hash($mdp, PASSWORD_DEFAULT);
$token = bin2hex(random_bytes(32));
$token_autologin = bin2hex(random_bytes(32));
$token_autologin_expire = (new DateTime('+60 minutes'))->format('Y-m-d H:i:s');
$stmt = $pdo->prepare("
INSERT INTO compte (email, mot_de_passe, role, reference_id, token_validation, email_valide, token_autologin, token_autologin_expire)
VALUES (:email, :mdp, 'client', :ref_id, :token, 0, :al, :al_exp)
");
$stmt->execute([
'email' => $email,
'mdp' => $mot_de_passe_hash,
'ref_id' => $client_id,
'token' => $token,
'al' => $token_autologin,
'al_exp' => $token_autologin_expire,
]);
// Commit avant l’envoi du mail
$pdo->commit();
} catch (Throwable $e) {
if ($pdo->inTransaction()) $pdo->rollBack();
error_log('Inscription client - DB error: '.$e->getMessage());
ui_exit(
$translations['err_titre'] ?? 'Un problème est survenu',
$translations['err_serveur'] ?? 'Erreur serveur.',
500,
'error',
'/'
);
}
// =====================
// Email de validation
// =====================
$validation_url = "https://uslugi.fr/validation.php?token=" . urlencode($token) . "&al=" . urlencode($token_autologin);
$validation_url_html = htmlspecialchars($validation_url, ENT_QUOTES, 'UTF-8');
$prenom_nom_safe = htmlspecialchars("$prenom $nom", ENT_QUOTES, 'UTF-8');
$sujet = $translations['mail_valid_sujet'];
$html = "<!DOCTYPE html>
<html lang='fr'>
<head>
<meta charset='UTF-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<style>
body { font-family: Arial, sans-serif; background-color:#f4f4f4; padding:20px; }
.mail-container { max-width:600px; margin:auto; background:#fff; padding:30px; border-radius:8px; box-shadow:0 0 10px rgba(0,0,0,0.1); }
.title { font-size:20px; font-weight:bold; color:#333; margin-bottom:20px; }
.btn { display:inline-block; margin-top:20px; padding:12px 24px; background-color:#E03C3F; color:#fff; text-decoration:none; border-radius:999px; font-weight:bold; }
.footer { margin-top:30px; font-size:13px; color:#888; }
.link-fallback { word-break: break-all; font-size:13px; margin-top:15px; color:#555; }
</style>
</head>
<body>
<div class='mail-container'>
<div class='title'>".sprintf($translations['mail_valid_bonjour_nom'], $prenom_nom_safe)."</div>
<p>".$translations['mail_valid_merci']."</p>
<p>".$translations['mail_valid_action']."</p>
<a href='$validation_url_html' class='btn'>".$translations['mail_valid_btn']."</a>
<p class='link-fallback'>
".$translations['mail_valid_fallback']."<br>
<a href='$validation_url_html'>$validation_url_html</a>
</p>
<div class='footer'>
".$translations['mail_valid_expire']."<br>
".$translations['mail_valid_ignore']."<br><br>
".$translations['mail_valid_signature']."
</div>
</div>
</body>
</html>";
$text = strip_tags(
sprintf($translations['mail_valid_bonjour_nom'], "$prenom $nom")."\n\n".
$translations['mail_valid_merci']."\n".
$translations['mail_valid_action']."\n".
($translations['mail_valid_fallback'] ?? 'Si le bouton ne fonctionne pas, utilisez ce lien :')."\n".
$validation_url."\n\n".
$translations['mail_valid_expire']."\n".
$translations['mail_valid_ignore']."\n".
($translations['mail_valid_signature'] ?? 'USLUGI.FR')
);
// Envoi du mail (non bloquant)
list($sent, $err) = uslugi_mail(
$email,
$sujet,
$html,
$text,
['to_name' => "$prenom $nom"]
);
if (!$sent) {
error_log("Validation mail NOT sent to $email : $err");
// On ne bloque pas l'utilisateur.
}
// =====================
// Écran final (succès)
// =====================
echo '
<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>'.htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8').'</title>
<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:600px;width:98%;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}
.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">
<div style="width:102px;height:102px;border-radius:50%;background:rgba(30,128,80,.08);border:3px solid rgba(30,128,80,.20);display:flex;align-items:center;justify-content:center;margin:0 auto 20px;">
<div style="width:48px;height:36px;border:5px solid #1e8050;border-top:0;border-left:0;transform:rotate(45deg);"></div>
</div>
<h1>'.htmlspecialchars($translations['insc_merci_titre'] ?? 'Merci !', ENT_QUOTES, 'UTF-8').'</h1>
<p>'.htmlspecialchars($translations['insc_merci_ligne1'] ?? 'Votre compte a bien été créé.', ENT_QUOTES, 'UTF-8').'</p>
<p>'.htmlspecialchars($translations['insc_merci_ligne2'] ?? 'Vérifiez vos emails pour valider votre adresse.', ENT_QUOTES, 'UTF-8').'</p>
<div class="row">
<a class="btn btn-outline" href="/">'.htmlspecialchars($translations['retour_acceuil'] ?? 'Retour à l’accueil', ENT_QUOTES, 'UTF-8').'</a>
</div>
</div>
</body></html>';