Feat: onglet SMTP dans l'administration

- Formulaire d'édition des paramètres SMTP (serveur, port, chiffrement,
  utilisateur, mot de passe, expéditeur) stockés dans data/smtp_settings.json
  (écrit par www-data, contrairement au .env en lecture seule)
- Test de connexion SMTP avec logs PHPMailer complets (DEBUG_SERVER)
- Envoi d'email de test avec contenu personnalisé anti-spam
- src/SmtpSettings.php : lecture/écriture smtp_settings.json avec fallback env()
- mailer.php : lit les paramètres depuis SmtpSettings en priorité
- admin.js : indicateurs spinner sur les boutons pendant le traitement
This commit is contained in:
Cedric Abonnel
2026-05-13 10:53:03 +02:00
parent 4cc4a01534
commit 26ada9b54e
5 changed files with 377 additions and 8 deletions
+129
View File
@@ -1991,9 +1991,138 @@ switch ($action) {
}
}
if ($tab === 'smtp') {
if (!isAdmin()) {
http_response_code(403);
exit;
}
require_once BASE_PATH . '/src/SmtpSettings.php';
$adminData['smtp_config'] = [
'host' => smtpCfg('host', 'SMTP_HOST'),
'port' => smtpCfg('port', 'SMTP_PORT'),
'secure' => smtpCfg('secure', 'SMTP_SECURE'),
'user' => smtpCfg('user', 'SMTP_USER'),
'has_pass' => smtpCfg('pass', 'SMTP_PASS') !== '',
'from' => smtpCfg('from', 'SMTP_FROM'),
'from_name' => smtpCfg('from_name', 'SMTP_FROM_NAME'),
];
$adminData['smtp_test'] = $_SESSION['smtp_test_result'] ?? null;
unset($_SESSION['smtp_test_result']);
}
include BASE_PATH . '/templates/admin.php';
break;
case 'admin_smtp_save':
requireAuth();
if (!isAdmin()) {
http_response_code(403);
exit;
}
require_once BASE_PATH . '/src/SmtpSettings.php';
saveSmtpSettings([
'host' => $_POST['smtp_host'] ?? '',
'port' => $_POST['smtp_port'] ?? '',
'secure' => $_POST['smtp_secure'] ?? '',
'user' => $_POST['smtp_user'] ?? '',
'pass' => $_POST['smtp_pass'] ?? '',
'from' => $_POST['smtp_from'] ?? '',
'from_name' => $_POST['smtp_from_name'] ?? '',
]);
header('Location: /admin/smtp?saved=1');
exit;
case 'admin_smtp_test':
requireAuth();
if (!isAdmin()) {
http_response_code(403);
exit;
}
require_once BASE_PATH . '/src/SmtpSettings.php';
require_once BASE_PATH . '/src/mailer.php';
$mode = in_array($_POST['mode'] ?? '', ['connect', 'send'], true) ? $_POST['mode'] : 'connect';
$testEmail = trim($_POST['test_email'] ?? '');
if ($testEmail !== '' && !filter_var($testEmail, FILTER_VALIDATE_EMAIL)) {
$testEmail = '';
}
$smtpLogs = [];
$smtpOk = false;
$smtpErrMsg = '';
try {
$mail = new \PHPMailer\PHPMailer\PHPMailer(true);
$mail->isSMTP();
$mail->Host = smtpCfg('host', 'SMTP_HOST', 'localhost');
$mail->Port = (int)smtpCfg('port', 'SMTP_PORT', '587');
$_stUser = smtpCfg('user', 'SMTP_USER');
$_stPass = smtpCfg('pass', 'SMTP_PASS');
$mail->SMTPAuth = ($_stUser !== '' || $_stPass !== '');
$mail->Username = $_stUser;
$mail->Password = $_stPass;
$smtpSecure = strtolower(smtpCfg('secure', 'SMTP_SECURE', 'tls'));
if ($smtpSecure === 'ssl') {
$mail->SMTPSecure = \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS;
} elseif ($smtpSecure === 'tls') {
$mail->SMTPSecure = \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
}
$mail->Timeout = 15;
$mail->SMTPOptions = ['ssl' => ['verify_peer' => true, 'verify_peer_name' => true, 'allow_self_signed' => false]];
$mail->SMTPDebug = \PHPMailer\PHPMailer\SMTP::DEBUG_SERVER;
$mail->Debugoutput = static function (string $str) use (&$smtpLogs): void {
$smtpLogs[] = rtrim($str);
};
if ($mode === 'send' && $testEmail !== '') {
$mail->CharSet = 'UTF-8';
$mail->isHTML(true);
$_smtpFrom = smtpCfg('from', 'SMTP_FROM', 'no-reply@varlog.a5l.fr');
$_smtpFromName = smtpCfg('from_name', 'SMTP_FROM_NAME', 'varlog');
$mail->setFrom($_smtpFrom, $_smtpFromName);
$mail->addAddress($testEmail);
$_siteName = siteTitle();
$_siteUrl = rtrim(APP_URL, '/');
$_sentAt = date('d/m/Y à H\hi', time());
$mail->Subject = 'Vérification de la configuration email — ' . $_siteName;
$mail->Body = '<!DOCTYPE html><html lang="fr"><head><meta charset="UTF-8">'
. '<meta name="viewport" content="width=device-width,initial-scale=1"></head>'
. '<body style="font-family:sans-serif;color:#1a1a1a;max-width:520px;margin:0 auto;padding:32px 16px">'
. '<p>Bonjour,</p>'
. '<p>Cet email confirme que la configuration SMTP de <strong>' . htmlspecialchars($_siteName) . '</strong> fonctionne correctement.</p>'
. '<p>Envoyé le ' . $_sentAt . ' depuis <a href="' . htmlspecialchars($_siteUrl) . '">' . htmlspecialchars($_siteUrl) . '</a>.</p>'
. '<hr style="border:none;border-top:1px solid #e5e7eb;margin:28px 0">'
. '<p style="color:#6b7280;font-size:0.82em">Vous recevez cet email car un administrateur a effectué un test de configuration depuis l\'interface d\'administration de ' . htmlspecialchars($_siteName) . '.'
. ' Si vous n\'attendiez pas cet email, vous pouvez l\'ignorer.</p>'
. '</body></html>';
$mail->AltBody = "Bonjour,\r\n\r\n"
. "Cet email confirme que la configuration SMTP de {$_siteName} fonctionne correctement.\r\n\r\n"
. "Envoyé le {$_sentAt} depuis {$_siteUrl}.\r\n\r\n"
. "--\r\n"
. "Vous recevez cet email car un administrateur a effectué un test de configuration depuis l'interface d'administration de {$_siteName}."
. " Si vous n'attendiez pas cet email, vous pouvez l'ignorer.";
$mail->send();
} else {
$mail->smtpConnect();
$mail->smtpClose();
}
$smtpOk = true;
} catch (\Exception $e) {
$smtpErrMsg = $e->getMessage();
}
$_SESSION['smtp_test_result'] = [
'success' => $smtpOk,
'error' => $smtpErrMsg,
'logs' => $smtpLogs,
'mode' => $mode,
'email' => $testEmail,
'ts' => date('d/m/Y H:i:s'),
];
header('Location: /admin/smtp');
exit;
case 'admin_bulk_delete':
requireAuth();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {