Refactor email handling to use EmailHelper and auto-detect encryption

Extracted all email sending logic into a new EmailHelper class for centralized management and improved error handling. Updated AuthController, SettingsController, and EmailChannel to use EmailHelper for sending emails and logging. Added auto-detection of SMTP encryption based on port in both backend (SettingsController) and frontend (settings view), improving user experience and reducing misconfiguration. Enhanced logging for email operations and improved UI feedback for encryption selection.
This commit is contained in:
Hosteroid
2025-10-14 00:27:50 +03:00
parent 24e732c10a
commit 02bcc73261
5 changed files with 599 additions and 276 deletions

View File

@@ -7,16 +7,20 @@ use App\Models\User;
use App\Models\Setting;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use App\Helpers\EmailHelper;
use App\Services\Logger;
class AuthController extends Controller
{
private User $userModel;
private Setting $settingModel;
private Logger $logger;
public function __construct()
{
$this->userModel = new User();
$this->settingModel = new Setting();
$this->logger = new Logger('auth');
}
/**
@@ -690,43 +694,21 @@ class AuthController extends Controller
*/
private function sendVerificationEmail($email, $fullName, $token)
{
try {
$emailSettings = $this->settingModel->getEmailSettings();
$appSettings = $this->settingModel->getAppSettings();
$result = EmailHelper::sendVerificationEmail($email, $fullName, $token);
$verifyUrl = $appSettings['app_url'] . '/verify-email?token=' . $token;
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = $emailSettings['mail_host'];
$mail->SMTPAuth = !empty($emailSettings['mail_username']);
$mail->Username = $emailSettings['mail_username'];
$mail->Password = $emailSettings['mail_password'];
$mail->SMTPSecure = $emailSettings['mail_encryption'];
$mail->Port = (int)$emailSettings['mail_port'];
$mail->CharSet = 'UTF-8';
$mail->setFrom($emailSettings['mail_from_address'], $emailSettings['mail_from_name']);
$mail->addAddress($email, $fullName);
$mail->isHTML(true);
$mail->Subject = 'Verify Your Email Address';
$mail->Body = "
<h2>Welcome to Domain Monitor!</h2>
<p>Hello {$fullName},</p>
<p>Thank you for registering. Please click the link below to verify your email address:</p>
<p><a href='{$verifyUrl}' style='background: #4A90E2; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;'>Verify Email Address</a></p>
<p>Or copy and paste this URL into your browser:</p>
<p>{$verifyUrl}</p>
<p>This link will expire in 24 hours.</p>
<p>If you did not create an account, please ignore this email.</p>
";
$mail->send();
} catch (Exception $e) {
if (!$result['success']) {
// Log error but don't fail the registration
error_log('Failed to send verification email: ' . $e->getMessage());
$this->logger->error('Failed to send verification email', [
'email' => $email,
'full_name' => $fullName,
'debug_info' => $result['debug_info'] ?? null,
'error' => $result['error'] ?? null
]);
} else {
$this->logger->info('Verification email sent successfully', [
'email' => $email,
'full_name' => $fullName
]);
}
}
@@ -735,43 +717,21 @@ class AuthController extends Controller
*/
private function sendPasswordResetEmail($email, $fullName, $token)
{
try {
$emailSettings = $this->settingModel->getEmailSettings();
$appSettings = $this->settingModel->getAppSettings();
$result = EmailHelper::sendPasswordResetEmail($email, $fullName, $token);
$resetUrl = $appSettings['app_url'] . '/reset-password?token=' . $token;
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = $emailSettings['mail_host'];
$mail->SMTPAuth = !empty($emailSettings['mail_username']);
$mail->Username = $emailSettings['mail_username'];
$mail->Password = $emailSettings['mail_password'];
$mail->SMTPSecure = $emailSettings['mail_encryption'];
$mail->Port = (int)$emailSettings['mail_port'];
$mail->CharSet = 'UTF-8';
$mail->setFrom($emailSettings['mail_from_address'], $emailSettings['mail_from_name']);
$mail->addAddress($email, $fullName);
$mail->isHTML(true);
$mail->Subject = 'Reset Your Password';
$mail->Body = "
<h2>Password Reset Request</h2>
<p>Hello {$fullName},</p>
<p>We received a request to reset your password. Click the link below to create a new password:</p>
<p><a href='{$resetUrl}' style='background: #4A90E2; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;'>Reset Password</a></p>
<p>Or copy and paste this URL into your browser:</p>
<p>{$resetUrl}</p>
<p>This link will expire in 1 hour.</p>
<p>If you did not request a password reset, please ignore this email and your password will remain unchanged.</p>
";
$mail->send();
} catch (Exception $e) {
if (!$result['success']) {
// Log error
error_log('Failed to send password reset email: ' . $e->getMessage());
$this->logger->error('Failed to send password reset email', [
'email' => $email,
'full_name' => $fullName,
'debug_info' => $result['debug_info'] ?? null,
'error' => $result['error'] ?? null
]);
} else {
$this->logger->info('Password reset email sent successfully', [
'email' => $email,
'full_name' => $fullName
]);
}
}

View File

@@ -5,15 +5,19 @@ namespace App\Controllers;
use Core\Controller;
use Core\Auth;
use App\Models\Setting;
use App\Helpers\EmailHelper;
use App\Services\Logger;
class SettingsController extends Controller
{
private Setting $settingModel;
private Logger $logger;
public function __construct()
{
Auth::requireAdmin();
$this->settingModel = new Setting();
$this->logger = new Logger('settings');
}
public function index()
@@ -250,12 +254,36 @@ class SettingsController extends Controller
$this->verifyCsrf('/settings#email');
try {
$port = (int)trim($_POST['mail_port'] ?? '2525');
$encryption = trim($_POST['mail_encryption'] ?? 'tls');
// Auto-detect encryption based on port if not explicitly set
$originalEncryption = $encryption;
if (empty($encryption) || $encryption === 'tls') {
if ($port === 465) {
$encryption = 'ssl'; // Port 465 should use SSL
$this->logger->info('Auto-detected SSL encryption for port 465', [
'port' => $port,
'original_encryption' => $originalEncryption,
'detected_encryption' => $encryption
]);
} elseif ($port === 587) {
$encryption = 'tls'; // Port 587 should use TLS
$this->logger->info('Auto-detected TLS encryption for port 587', [
'port' => $port,
'original_encryption' => $originalEncryption,
'detected_encryption' => $encryption
]);
}
// For other ports, keep the user's selection
}
$emailSettings = [
'mail_host' => trim($_POST['mail_host'] ?? ''),
'mail_port' => trim($_POST['mail_port'] ?? '2525'),
'mail_port' => $port,
'mail_username' => trim($_POST['mail_username'] ?? ''),
'mail_password' => trim($_POST['mail_password'] ?? ''),
'mail_encryption' => trim($_POST['mail_encryption'] ?? 'tls'),
'mail_encryption' => $encryption,
'mail_from_address' => trim($_POST['mail_from_address'] ?? ''),
'mail_from_name' => trim($_POST['mail_from_name'] ?? 'Domain Monitor')
];
@@ -381,111 +409,31 @@ class SettingsController extends Controller
return;
}
try {
// Get current email settings
$emailSettings = $this->settingModel->getEmailSettings();
$appSettings = $this->settingModel->getAppSettings();
// Use EmailHelper to send test email
$result = EmailHelper::sendTestEmail($testEmail);
// Create PHPMailer instance
$mail = new \PHPMailer\PHPMailer\PHPMailer(true);
if ($result['success']) {
$_SESSION['success'] = $result['message'];
$this->logger->info('Test email sent successfully', [
'email' => $testEmail
]);
} else {
// Log detailed error information for debugging
$this->logger->error('Test email failed', [
'email' => $testEmail,
'debug_info' => $result['debug_info'] ?? null,
'error' => $result['error'] ?? null
]);
// Server settings
$mail->isSMTP();
$mail->Host = $emailSettings['mail_host'];
$mail->SMTPAuth = !empty($emailSettings['mail_username']);
$mail->Username = $emailSettings['mail_username'];
$mail->Password = $emailSettings['mail_password'];
$mail->SMTPSecure = $emailSettings['mail_encryption'];
$mail->Port = $emailSettings['mail_port'];
$_SESSION['error'] = $result['message'];
// Recipients
$mail->setFrom($emailSettings['mail_from_address'], $emailSettings['mail_from_name']);
$mail->addAddress($testEmail);
// Content
$mail->isHTML(true);
$mail->Subject = 'Test Email from ' . $appSettings['app_name'];
$appName = htmlspecialchars($appSettings['app_name']);
$appUrl = htmlspecialchars($appSettings['app_url']);
$currentTime = date('F j, Y g:i A');
$mail->Body = "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #4A90E2; color: white; padding: 20px; border-radius: 5px 5px 0 0; }
.content { background: #f9f9f9; padding: 20px; border: 1px solid #ddd; }
.success { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; padding: 12px; border-radius: 4px; margin: 15px 0; }
.info-table { width: 100%; margin-top: 15px; }
.info-table td { padding: 8px; border-bottom: 1px solid #ddd; }
.info-table td:first-child { font-weight: bold; width: 150px; }
.footer { background: #333; color: white; padding: 10px; text-align: center; font-size: 12px; border-radius: 0 0 5px 5px; }
</style>
</head>
<body>
<div class='container'>
<div class='header'>
<h2>✅ Email Test Successful!</h2>
</div>
<div class='content'>
<div class='success'>
<strong>Success!</strong> Your email configuration is working correctly.
</div>
<p>This is a test email from <strong>{$appName}</strong>.</p>
<p>If you're seeing this message, it means your SMTP settings are configured properly and emails are being delivered successfully.</p>
<table class='info-table'>
<tr>
<td>SMTP Host:</td>
<td>" . htmlspecialchars($emailSettings['mail_host']) . "</td>
</tr>
<tr>
<td>SMTP Port:</td>
<td>" . htmlspecialchars($emailSettings['mail_port']) . "</td>
</tr>
<tr>
<td>Encryption:</td>
<td>" . htmlspecialchars($emailSettings['mail_encryption'] ?: 'None') . "</td>
</tr>
<tr>
<td>From Address:</td>
<td>" . htmlspecialchars($emailSettings['mail_from_address']) . "</td>
</tr>
<tr>
<td>Test Time:</td>
<td>{$currentTime}</td>
</tr>
</table>
</div>
<div class='footer'>
<p>This is an automated test message from {$appName}</p>
<p style='margin-top: 5px;'><a href='{$appUrl}' style='color: #4A90E2;'>Visit Dashboard</a></p>
</div>
</div>
</body>
</html>
";
$mail->AltBody = "Email Test Successful!\n\n" .
"This is a test email from {$appName}.\n" .
"Your SMTP configuration is working correctly.\n\n" .
"SMTP Host: {$emailSettings['mail_host']}\n" .
"SMTP Port: {$emailSettings['mail_port']}\n" .
"From: {$emailSettings['mail_from_address']}\n" .
"Test Time: {$currentTime}";
$mail->send();
$_SESSION['success'] = "Test email sent successfully to {$testEmail}. Please check your inbox.";
$this->redirect('/settings#email');
} catch (\Exception $e) {
$_SESSION['error'] = "Failed to send test email: " . $e->getMessage();
$this->redirect('/settings#email');
// In development, show more detailed error
if (($_ENV['APP_ENV'] ?? 'production') === 'development') {
$_SESSION['error'] .= " (Debug: " . ($result['debug_info'] ?? $result['error']) . ")";
}
}
$this->redirect('/settings#email');
}
}

429
app/Helpers/EmailHelper.php Normal file
View File

@@ -0,0 +1,429 @@
<?php
namespace App\Helpers;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use App\Models\Setting;
use App\Services\Logger;
class EmailHelper
{
private static ?Setting $settingModel = null;
private static ?Logger $logger = null;
/**
* Get the Setting model instance
*/
private static function getSettingModel(): Setting
{
if (self::$settingModel === null) {
self::$settingModel = new Setting();
}
return self::$settingModel;
}
/**
* Get the Logger instance
*/
private static function getLogger(): Logger
{
if (self::$logger === null) {
self::$logger = new Logger('email');
}
return self::$logger;
}
/**
* Get email settings from database
*/
public static function getEmailSettings(): array
{
return self::getSettingModel()->getEmailSettings();
}
/**
* Get app settings from database
*/
public static function getAppSettings(): array
{
return self::getSettingModel()->getAppSettings();
}
/**
* Create and configure a PHPMailer instance with proper settings
*/
public static function createMailer(): PHPMailer
{
$emailSettings = self::getEmailSettings();
$mail = new PHPMailer(true);
// Server settings
$mail->isSMTP();
$mail->Host = $emailSettings['mail_host'];
$mail->SMTPAuth = !empty($emailSettings['mail_username']);
$mail->Username = $emailSettings['mail_username'];
$mail->Password = $emailSettings['mail_password'];
$mail->Port = (int)$emailSettings['mail_port'];
// Configure encryption based on port
$port = (int)$emailSettings['mail_port'];
$encryption = $emailSettings['mail_encryption'];
// Auto-detect encryption for common ports
if ($port === 465) {
// Port 465 typically uses SSL
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
} elseif ($port === 587) {
// Port 587 typically uses TLS
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
} elseif ($port === 25 || $port === 2525) {
// Port 25/2525 might use TLS or no encryption
if ($encryption === 'tls') {
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
} else {
$mail->SMTPSecure = '';
}
} else {
// Use configured encryption for other ports
if ($encryption === 'ssl') {
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
} elseif ($encryption === 'tls') {
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
} else {
$mail->SMTPSecure = '';
}
}
// Configure timeouts and SSL options
$mail->Timeout = 30; // 30 seconds total timeout
$mail->SMTPOptions = [
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
]
];
// Set character encoding
$mail->CharSet = 'UTF-8';
return $mail;
}
/**
* Send a test email
*/
public static function sendTestEmail(string $toEmail): array
{
try {
$emailSettings = self::getEmailSettings();
$appSettings = self::getAppSettings();
$mail = self::createMailer();
// Set sender
$mail->setFrom($emailSettings['mail_from_address'], $emailSettings['mail_from_name']);
$mail->addAddress($toEmail);
// Content
$mail->isHTML(true);
$mail->Subject = 'Test Email from ' . $appSettings['app_name'];
$appName = htmlspecialchars($appSettings['app_name']);
$appUrl = htmlspecialchars($appSettings['app_url']);
$currentTime = date('F j, Y g:i A');
$mail->Body = "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #4A90E2; color: white; padding: 20px; border-radius: 5px 5px 0 0; }
.content { background: #f9f9f9; padding: 20px; border: 1px solid #ddd; }
.success { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; padding: 12px; border-radius: 4px; margin: 15px 0; }
.info-table { width: 100%; margin-top: 15px; }
.info-table td { padding: 8px; border-bottom: 1px solid #ddd; }
.info-table td:first-child { font-weight: bold; width: 150px; }
.footer { background: #333; color: white; padding: 10px; text-align: center; font-size: 12px; border-radius: 0 0 5px 5px; }
</style>
</head>
<body>
<div class='container'>
<div class='header'>
<h2>✅ Email Test Successful!</h2>
</div>
<div class='content'>
<div class='success'>
<strong>Success!</strong> Your email configuration is working correctly.
</div>
<p>This is a test email from <strong>{$appName}</strong>.</p>
<p>If you're seeing this message, it means your SMTP settings are configured properly and emails are being delivered successfully.</p>
<table class='info-table'>
<tr>
<td>SMTP Host:</td>
<td>" . htmlspecialchars($emailSettings['mail_host']) . "</td>
</tr>
<tr>
<td>SMTP Port:</td>
<td>" . htmlspecialchars($emailSettings['mail_port']) . "</td>
</tr>
<tr>
<td>Encryption:</td>
<td>" . htmlspecialchars($emailSettings['mail_encryption'] ?: 'None') . "</td>
</tr>
<tr>
<td>From Address:</td>
<td>" . htmlspecialchars($emailSettings['mail_from_address']) . "</td>
</tr>
<tr>
<td>Test Time:</td>
<td>{$currentTime}</td>
</tr>
</table>
</div>
<div class='footer'>
<p>This is an automated test message from {$appName}</p>
<p style='margin-top: 5px;'><a href='{$appUrl}' style='color: #4A90E2;'>Visit Dashboard</a></p>
</div>
</div>
</body>
</html>
";
$mail->AltBody = "Email Test Successful!\n\n" .
"This is a test email from {$appName}.\n" .
"Your SMTP configuration is working correctly.\n\n" .
"SMTP Host: {$emailSettings['mail_host']}\n" .
"SMTP Port: {$emailSettings['mail_port']}\n" .
"From: {$emailSettings['mail_from_address']}\n" .
"Test Time: {$currentTime}";
$mail->send();
return [
'success' => true,
'message' => "Test email sent successfully to {$toEmail}. Please check your inbox."
];
} catch (Exception $e) {
$errorMessage = "Failed to send test email: " . $e->getMessage();
$debugInfo = $mail->ErrorInfo ?? 'No debug info available';
// Log the error using the application's logger
self::getLogger()->error($errorMessage, [
'email' => $toEmail,
'smtp_error' => $debugInfo,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'message' => $errorMessage,
'error' => $e->getMessage(),
'debug_info' => $debugInfo
];
}
}
/**
* Send a notification email
*/
public static function sendNotificationEmail(string $toEmail, string $subject, string $message, array $data = []): array
{
try {
$emailSettings = self::getEmailSettings();
$appSettings = self::getAppSettings();
$mail = self::createMailer();
// Set sender
$mail->setFrom($emailSettings['mail_from_address'], $emailSettings['mail_from_name']);
$mail->addAddress($toEmail);
// Content
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = self::formatHtmlBody($message, $data, $appSettings);
$mail->AltBody = strip_tags($message);
$mail->send();
return [
'success' => true,
'message' => "Email sent successfully to {$toEmail}"
];
} catch (Exception $e) {
$errorMessage = "Failed to send email: " . $e->getMessage();
$debugInfo = $mail->ErrorInfo ?? 'No debug info available';
// Log the error using the application's logger
self::getLogger()->error($errorMessage, [
'email' => $toEmail,
'subject' => $subject,
'smtp_error' => $debugInfo,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'message' => $errorMessage,
'error' => $e->getMessage(),
'debug_info' => $debugInfo
];
}
}
/**
* Format HTML email body
*/
private static function formatHtmlBody(string $message, array $data, array $appSettings): string
{
$messageHtml = nl2br(htmlspecialchars($message));
$appName = htmlspecialchars($appSettings['app_name']);
$appUrl = htmlspecialchars($appSettings['app_url']);
// Build domain link if domain ID is available
$domainLink = '';
if (isset($data['domain_id'])) {
$domainUrl = rtrim($appUrl, '/') . '/domains/' . $data['domain_id'];
$domainLink = "<p style='margin-top: 15px;'><a href='$domainUrl' class='button'>View Domain Details</a></p>";
}
return "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #4A90E2; color: white; padding: 20px; border-radius: 5px 5px 0 0; }
.content { background: #f9f9f9; padding: 20px; border: 1px solid #ddd; }
.footer { background: #333; color: white; padding: 10px; text-align: center; font-size: 12px; border-radius: 0 0 5px 5px; }
.button { display: inline-block; padding: 10px 20px; background: #4A90E2; color: white; text-decoration: none; border-radius: 5px; margin-top: 10px; }
</style>
</head>
<body>
<div class='container'>
<div class='header'>
<h2>🔔 {$appName} Alert</h2>
</div>
<div class='content'>
<p>$messageHtml</p>
$domainLink
</div>
<div class='footer'>
<p>This is an automated message from {$appName}</p>
<p style='margin-top: 5px;'><a href='$appUrl' style='color: #4A90E2;'>Visit Dashboard</a></p>
</div>
</div>
</body>
</html>
";
}
/**
* Send verification email
*/
public static function sendVerificationEmail(string $email, string $fullName, string $token): array
{
try {
$appSettings = self::getAppSettings();
$verifyUrl = $appSettings['app_url'] . '/verify-email?token=' . $token;
$subject = 'Verify Your Email Address';
$message = "
<h2>Welcome to Domain Monitor!</h2>
<p>Hello {$fullName},</p>
<p>Thank you for registering. Please click the link below to verify your email address:</p>
<p><a href='{$verifyUrl}' style='background: #4A90E2; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;'>Verify Email Address</a></p>
<p>Or copy and paste this URL into your browser:</p>
<p>{$verifyUrl}</p>
<p>This link will expire in 24 hours.</p>
<p>If you did not create an account, please ignore this email.</p>
";
return self::sendNotificationEmail($email, $subject, $message);
} catch (\Exception $e) {
$errorMessage = "Failed to send verification email: " . $e->getMessage();
// Log the error using the application's logger
self::getLogger()->error($errorMessage, [
'email' => $email,
'full_name' => $fullName,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'message' => $errorMessage,
'error' => $e->getMessage()
];
}
}
/**
* Send password reset email
*/
public static function sendPasswordResetEmail(string $email, string $fullName, string $token): array
{
try {
$appSettings = self::getAppSettings();
$resetUrl = $appSettings['app_url'] . '/reset-password?token=' . $token;
$subject = 'Reset Your Password';
$message = "
<h2>Password Reset Request</h2>
<p>Hello {$fullName},</p>
<p>We received a request to reset your password. Click the link below to create a new password:</p>
<p><a href='{$resetUrl}' style='background: #4A90E2; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;'>Reset Password</a></p>
<p>Or copy and paste this URL into your browser:</p>
<p>{$resetUrl}</p>
<p>This link will expire in 1 hour.</p>
<p>If you did not request a password reset, please ignore this email and your password will remain unchanged.</p>
";
return self::sendNotificationEmail($email, $subject, $message);
} catch (\Exception $e) {
$errorMessage = "Failed to send password reset email: " . $e->getMessage();
// Log the error using the application's logger
self::getLogger()->error($errorMessage, [
'email' => $email,
'full_name' => $fullName,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'message' => $errorMessage,
'error' => $e->getMessage()
];
}
}
/**
* Get email subject based on data
*/
public static function getEmailSubject(array $data): string
{
if (isset($data['domain'])) {
$daysLeft = $data['days_left'];
if ($daysLeft <= 0) {
return "🚨 URGENT: Domain {$data['domain']} has EXPIRED";
}
if ($daysLeft == 1) {
return "⚠️ CRITICAL: Domain {$data['domain']} expires TOMORROW";
}
return "⚠️ Domain Expiration Alert: {$data['domain']} ({$daysLeft} days)";
}
return "Domain Monitor Alert";
}
}

View File

@@ -5,104 +5,53 @@ namespace App\Services\Channels;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use App\Models\Setting;
use App\Helpers\EmailHelper;
use App\Services\Logger;
class EmailChannel implements NotificationChannelInterface
{
private Logger $logger;
public function __construct()
{
$this->logger = new Logger('email_channel');
}
public function send(array $config, string $message, array $data = []): bool
{
$mail = new PHPMailer(true);
try {
// Get email settings from database
$settingModel = new Setting();
$emailSettings = $settingModel->getEmailSettings();
$appSettings = $settingModel->getAppSettings();
$result = EmailHelper::sendNotificationEmail(
$config['email'],
EmailHelper::getEmailSubject($data),
$message,
$data
);
// Server settings
$mail->isSMTP();
$mail->Host = $emailSettings['mail_host'];
$mail->SMTPAuth = !empty($emailSettings['mail_username']);
$mail->Username = $emailSettings['mail_username'];
$mail->Password = $emailSettings['mail_password'];
$mail->SMTPSecure = $emailSettings['mail_encryption'];
$mail->Port = $emailSettings['mail_port'];
if (!$result['success']) {
$this->logger->error("Email send failed via EmailChannel", [
'email' => $config['email'],
'subject' => EmailHelper::getEmailSubject($data),
'debug_info' => $result['debug_info'] ?? null,
'error' => $result['error'] ?? null
]);
return false;
}
// Recipients
$mail->setFrom($emailSettings['mail_from_address'], $emailSettings['mail_from_name']);
$mail->addAddress($config['email']);
$this->logger->info("Email sent successfully via EmailChannel", [
'email' => $config['email'],
'subject' => EmailHelper::getEmailSubject($data)
]);
// Content
$mail->isHTML(true);
$mail->Subject = $this->getSubject($data);
$mail->Body = $this->formatHtmlBody($message, $data, $appSettings);
$mail->AltBody = strip_tags($message);
$mail->send();
return true;
} catch (Exception $e) {
error_log("Email send failed: {$mail->ErrorInfo}");
} catch (\Exception $e) {
$this->logger->error("Email send exception in EmailChannel", [
'email' => $config['email'],
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return false;
}
}
private function getSubject(array $data): string
{
if (isset($data['domain'])) {
$daysLeft = $data['days_left'];
if ($daysLeft <= 0) {
return "🚨 URGENT: Domain {$data['domain']} has EXPIRED";
}
if ($daysLeft == 1) {
return "⚠️ CRITICAL: Domain {$data['domain']} expires TOMORROW";
}
return "⚠️ Domain Expiration Alert: {$data['domain']} ({$daysLeft} days)";
}
return "Domain Monitor Alert";
}
private function formatHtmlBody(string $message, array $data, array $appSettings): string
{
$messageHtml = nl2br(htmlspecialchars($message));
$appName = htmlspecialchars($appSettings['app_name']);
$appUrl = htmlspecialchars($appSettings['app_url']);
// Build domain link if domain ID is available
$domainLink = '';
if (isset($data['domain_id'])) {
$domainUrl = rtrim($appUrl, '/') . '/domains/' . $data['domain_id'];
$domainLink = "<p style='margin-top: 15px;'><a href='$domainUrl' class='button'>View Domain Details</a></p>";
}
return "
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #4A90E2; color: white; padding: 20px; border-radius: 5px 5px 0 0; }
.content { background: #f9f9f9; padding: 20px; border: 1px solid #ddd; }
.footer { background: #333; color: white; padding: 10px; text-align: center; font-size: 12px; border-radius: 0 0 5px 5px; }
.button { display: inline-block; padding: 10px 20px; background: #4A90E2; color: white; text-decoration: none; border-radius: 5px; margin-top: 10px; }
</style>
</head>
<body>
<div class='container'>
<div class='header'>
<h2>🔔 {$appName} Alert</h2>
</div>
<div class='content'>
<p>$messageHtml</p>
$domainLink
</div>
<div class='footer'>
<p>This is an automated message from {$appName}</p>
<p style='margin-top: 5px;'><a href='$appUrl' style='color: #4A90E2;'>Visit Dashboard</a></p>
</div>
</div>
</body>
</html>
";
}
}

View File

@@ -203,24 +203,19 @@ foreach ($notificationPresets as $key => $preset) {
<div>
<label for="mail_encryption" class="block text-sm font-medium text-gray-700 mb-2">
Encryption
Encryption <span class="text-blue-500 text-xs">(Auto-detected by port)</span>
</label>
<select id="mail_encryption" name="mail_encryption"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary">
<option value="tls" <?= $emailSettings['mail_encryption'] === 'tls' ? 'selected' : '' ?>>TLS</option>
<option value="ssl" <?= $emailSettings['mail_encryption'] === 'ssl' ? 'selected' : '' ?>>SSL</option>
<option value="tls" <?= $emailSettings['mail_encryption'] === 'tls' ? 'selected' : '' ?>>TLS (STARTTLS)</option>
<option value="ssl" <?= $emailSettings['mail_encryption'] === 'ssl' ? 'selected' : '' ?>>SSL (SMTPS)</option>
<option value="" <?= empty($emailSettings['mail_encryption']) ? 'selected' : '' ?>>None</option>
</select>
</div>
<div class="md:col-span-2">
<div class="bg-gray-50 border border-gray-200 rounded-lg p-3">
<p class="text-xs text-gray-600">
<i class="fas fa-info-circle text-gray-400 mr-1"></i>
<strong>Protocol:</strong> This application uses SMTP (Simple Mail Transfer Protocol) for sending emails.
<p class="text-xs text-gray-500 mt-1">
<i class="fas fa-magic text-blue-600 mr-1"></i>
<span id="encryption-help">Will auto-update based on port selection</span>
</p>
</div>
</div>
<div>
<label for="mail_username" class="block text-sm font-medium text-gray-700 mb-2">
@@ -648,6 +643,48 @@ foreach ($notificationPresets as $key => $preset) {
</div>
<script>
// Auto-update encryption based on port
function updateEncryptionByPort() {
const portField = document.getElementById('mail_port');
const encryptionField = document.getElementById('mail_encryption');
const helpText = document.getElementById('encryption-help');
if (!portField || !encryptionField) return;
const port = parseInt(portField.value);
// Auto-select encryption based on port
if (port === 465) {
encryptionField.value = 'ssl';
helpText.innerHTML = '<i class="fas fa-check text-green-600 mr-1"></i>Port 465 detected: SSL encryption selected';
helpText.className = 'text-xs text-green-600 mt-1';
} else if (port === 587) {
encryptionField.value = 'tls';
helpText.innerHTML = '<i class="fas fa-check text-green-600 mr-1"></i>Port 587 detected: TLS encryption selected';
helpText.className = 'text-xs text-green-600 mt-1';
} else if (port === 25 || port === 2525) {
// Keep current selection but show info
helpText.innerHTML = '<i class="fas fa-info text-blue-600 mr-1"></i>Port ' + port + ': Choose TLS or None based on your server';
helpText.className = 'text-xs text-blue-600 mt-1';
} else {
helpText.innerHTML = '<i class="fas fa-question text-gray-600 mr-1"></i>Custom port: Choose encryption manually';
helpText.className = 'text-xs text-gray-600 mt-1';
}
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
// Set up port change listener
const portField = document.getElementById('mail_port');
if (portField) {
portField.addEventListener('input', updateEncryptionByPort);
portField.addEventListener('change', updateEncryptionByPort);
// Run once on page load
updateEncryptionByPort();
}
});
// Tab switching
function switchTab(tabName) {
// Hide all tabs