settingModel = new Setting(); // Ensure only admins can access settings if (!isset($_SESSION['role']) || $_SESSION['role'] !== 'admin') { $_SESSION['error'] = 'Access denied. Admin privileges required.'; $this->redirect('/'); exit; } } public function index() { $settings = $this->settingModel->getAllAsKeyValue(); $appSettings = $this->settingModel->getAppSettings(); $emailSettings = $this->settingModel->getEmailSettings(); $captchaSettings = $this->settingModel->getCaptchaSettings(); // Predefined notification day options $notificationPresets = [ 'minimal' => [ 'label' => 'Minimal (30, 7, 1 days)', 'value' => '30,7,1' ], 'standard' => [ 'label' => 'Standard (60, 30, 21, 14, 7, 5, 3, 2, 1 days)', 'value' => '60,30,21,14,7,5,3,2,1' ], 'frequent' => [ 'label' => 'Frequent (90, 60, 45, 30, 21, 14, 10, 7, 5, 3, 2, 1 days)', 'value' => '90,60,45,30,21,14,10,7,5,3,2,1' ], 'business' => [ 'label' => 'Business Focused (60, 30, 14, 7, 3, 1 days)', 'value' => '60,30,14,7,3,1' ], 'conservative' => [ 'label' => 'Conservative (30, 15, 7, 3, 1 days)', 'value' => '30,15,7,3,1' ], 'custom' => [ 'label' => 'Custom', 'value' => 'custom' ] ]; // Check interval presets $checkIntervalPresets = [ ['label' => 'Every 6 hours', 'value' => 6], ['label' => 'Every 12 hours', 'value' => 12], ['label' => 'Daily (24 hours)', 'value' => 24], ['label' => 'Every 2 days (48 hours)', 'value' => 48], ['label' => 'Weekly (168 hours)', 'value' => 168] ]; $this->view('settings/index', [ 'settings' => $settings, 'appSettings' => $appSettings, 'emailSettings' => $emailSettings, 'captchaSettings' => $captchaSettings, 'notificationPresets' => $notificationPresets, 'checkIntervalPresets' => $checkIntervalPresets, 'title' => 'Settings' ]); } public function update() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('/settings'); return; } // CSRF Protection $this->verifyCsrf('/settings#monitoring'); try { // Update notification days $notificationPreset = $_POST['notification_preset'] ?? 'standard'; if ($notificationPreset === 'custom') { // Custom days entered by user $customDays = trim($_POST['custom_notification_days'] ?? ''); if (empty($customDays)) { $_SESSION['error'] = 'Please enter notification days for custom preset'; $this->redirect('/settings#monitoring'); return; } // Validate custom days (comma-separated integers) $daysArray = array_map('trim', explode(',', $customDays)); $daysArray = array_filter($daysArray, function($day) { return is_numeric($day) && $day > 0; }); if (empty($daysArray)) { $_SESSION['error'] = 'Invalid notification days format. Use comma-separated numbers (e.g., 30,15,7,1)'; $this->redirect('/settings#monitoring'); return; } // Sort in descending order rsort($daysArray, SORT_NUMERIC); $notificationDays = implode(',', $daysArray); } else { // Use preset value $notificationDays = $_POST['notification_days_before'] ?? '30,15,7,3,1'; } // Update check interval $checkInterval = (int)($_POST['check_interval_hours'] ?? 24); if ($checkInterval < 1 || $checkInterval > 720) { // Max 30 days $_SESSION['error'] = 'Check interval must be between 1 and 720 hours'; $this->redirect('/settings#monitoring'); return; } // Save settings $this->settingModel->setValue('notification_days_before', $notificationDays); $this->settingModel->setValue('check_interval_hours', $checkInterval); $_SESSION['success'] = 'Settings updated successfully'; $this->redirect('/settings#monitoring'); } catch (\Exception $e) { $_SESSION['error'] = 'Failed to update settings: ' . $e->getMessage(); $this->redirect('/settings#monitoring'); } } public function testCron() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('/settings'); return; } // CSRF Protection $this->verifyCsrf('/settings'); // Update last check run time to show the test worked $this->settingModel->updateLastCheckRun(); $_SESSION['info'] = 'Test notification sent (feature coming soon). Last check time updated.'; $this->redirect('/settings'); } public function clearLogs() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('/settings'); return; } // CSRF Protection $this->verifyCsrf('/settings#maintenance'); try { // Clear notification logs older than 30 days $stmt = $this->settingModel->db->prepare( "DELETE FROM notification_logs WHERE sent_at < DATE_SUB(NOW(), INTERVAL 30 DAY)" ); $stmt->execute(); $deleted = $stmt->rowCount(); $_SESSION['success'] = "Cleared $deleted old notification log(s)"; $this->redirect('/settings#maintenance'); } catch (\Exception $e) { $_SESSION['error'] = 'Failed to clear logs: ' . $e->getMessage(); $this->redirect('/settings#maintenance'); } } public function updateApp() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('/settings'); return; } // CSRF Protection $this->verifyCsrf('/settings#app'); try { $appSettings = [ 'app_name' => trim($_POST['app_name'] ?? 'Domain Monitor'), 'app_url' => trim($_POST['app_url'] ?? 'http://localhost:8000'), 'app_timezone' => trim($_POST['app_timezone'] ?? 'UTC') ]; // Validate app_name if (empty($appSettings['app_name'])) { $_SESSION['error'] = 'Application name is required'; $this->redirect('/settings#app'); return; } // Validate app_url if (empty($appSettings['app_url']) || !filter_var($appSettings['app_url'], FILTER_VALIDATE_URL)) { $_SESSION['error'] = 'Please enter a valid application URL'; $this->redirect('/settings#app'); return; } // Validate timezone $validTimezones = timezone_identifiers_list(); if (!in_array($appSettings['app_timezone'], $validTimezones)) { $_SESSION['error'] = 'Invalid timezone selected'; $this->redirect('/settings#app'); return; } // Update app settings $this->settingModel->updateAppSettings($appSettings); // Update registration settings $registrationEnabled = isset($_POST['registration_enabled']) ? '1' : '0'; $requireEmailVerification = isset($_POST['require_email_verification']) ? '1' : '0'; $this->settingModel->setValue('registration_enabled', $registrationEnabled); $this->settingModel->setValue('require_email_verification', $requireEmailVerification); $_SESSION['success'] = 'Application settings updated successfully'; $this->redirect('/settings#app'); } catch (\Exception $e) { $_SESSION['error'] = 'Failed to update application settings: ' . $e->getMessage(); $this->redirect('/settings#app'); } } public function updateEmail() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('/settings'); return; } // CSRF Protection $this->verifyCsrf('/settings#email'); try { $emailSettings = [ 'mail_host' => trim($_POST['mail_host'] ?? ''), 'mail_port' => trim($_POST['mail_port'] ?? '2525'), 'mail_username' => trim($_POST['mail_username'] ?? ''), 'mail_password' => trim($_POST['mail_password'] ?? ''), 'mail_encryption' => trim($_POST['mail_encryption'] ?? 'tls'), 'mail_from_address' => trim($_POST['mail_from_address'] ?? ''), 'mail_from_name' => trim($_POST['mail_from_name'] ?? 'Domain Monitor') ]; // Validate required fields if (empty($emailSettings['mail_host'])) { $_SESSION['error'] = 'Mail host is required'; $this->redirect('/settings#email'); return; } if (empty($emailSettings['mail_from_address']) || !filter_var($emailSettings['mail_from_address'], FILTER_VALIDATE_EMAIL)) { $_SESSION['error'] = 'Please enter a valid from email address'; $this->redirect('/settings#email'); return; } // Validate port if (!is_numeric($emailSettings['mail_port']) || $emailSettings['mail_port'] < 1 || $emailSettings['mail_port'] > 65535) { $_SESSION['error'] = 'Please enter a valid port number (1-65535)'; $this->redirect('/settings#email'); return; } $this->settingModel->updateEmailSettings($emailSettings); $_SESSION['success'] = 'Email settings updated successfully'; $this->redirect('/settings#email'); } catch (\Exception $e) { $_SESSION['error'] = 'Failed to update email settings: ' . $e->getMessage(); $this->redirect('/settings#email'); } } public function updateCaptcha() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('/settings'); return; } // CSRF Protection $this->verifyCsrf('/settings#security'); try { $captchaProvider = trim($_POST['captcha_provider'] ?? 'disabled'); $captchaSiteKey = trim($_POST['captcha_site_key'] ?? ''); $captchaSecretKey = trim($_POST['captcha_secret_key'] ?? ''); $recaptchaV3Threshold = trim($_POST['recaptcha_v3_score_threshold'] ?? '0.5'); // Validate provider $validProviders = ['disabled', 'recaptcha_v2', 'recaptcha_v3', 'turnstile']; if (!in_array($captchaProvider, $validProviders)) { $_SESSION['error'] = 'Invalid CAPTCHA provider selected'; $this->redirect('/settings#security'); return; } // If CAPTCHA is enabled, validate keys if ($captchaProvider !== 'disabled') { if (empty($captchaSiteKey)) { $_SESSION['error'] = 'Site key is required when CAPTCHA is enabled'; $this->redirect('/settings#security'); return; } if (empty($captchaSecretKey)) { $_SESSION['error'] = 'Secret key is required when CAPTCHA is enabled'; $this->redirect('/settings#security'); return; } } // Validate v3 score threshold if ($captchaProvider === 'recaptcha_v3') { $threshold = floatval($recaptchaV3Threshold); if ($threshold < 0.0 || $threshold > 1.0) { $_SESSION['error'] = 'reCAPTCHA v3 score threshold must be between 0.0 and 1.0'; $this->redirect('/settings#security'); return; } } // Prepare settings array $captchaSettings = [ 'captcha_provider' => $captchaProvider, 'captcha_site_key' => $captchaSiteKey, 'recaptcha_v3_score_threshold' => $recaptchaV3Threshold ]; // Only update secret key if provided (to allow updating other settings without re-entering secret) if (!empty($captchaSecretKey)) { $captchaSettings['captcha_secret_key'] = $captchaSecretKey; } // Update CAPTCHA settings $this->settingModel->updateCaptchaSettings($captchaSettings); $_SESSION['success'] = 'CAPTCHA settings updated successfully'; $this->redirect('/settings#security'); } catch (\Exception $e) { $_SESSION['error'] = 'Failed to update CAPTCHA settings: ' . $e->getMessage(); $this->redirect('/settings#security'); } } public function testEmail() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->redirect('/settings'); return; } // CSRF Protection $this->verifyCsrf('/settings#email'); $testEmail = trim($_POST['test_email'] ?? ''); if (empty($testEmail) || !filter_var($testEmail, FILTER_VALIDATE_EMAIL)) { $_SESSION['error'] = 'Please enter a valid email address'; $this->redirect('/settings#email'); return; } try { // Get current email settings $emailSettings = $this->settingModel->getEmailSettings(); $appSettings = $this->settingModel->getAppSettings(); // Create PHPMailer instance $mail = new \PHPMailer\PHPMailer\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->SMTPSecure = $emailSettings['mail_encryption']; $mail->Port = $emailSettings['mail_port']; // 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 = "

✅ Email Test Successful!

Success! Your email configuration is working correctly.

This is a test email from {$appName}.

If you're seeing this message, it means your SMTP settings are configured properly and emails are being delivered successfully.

SMTP Host: " . htmlspecialchars($emailSettings['mail_host']) . "
SMTP Port: " . htmlspecialchars($emailSettings['mail_port']) . "
Encryption: " . htmlspecialchars($emailSettings['mail_encryption'] ?: 'None') . "
From Address: " . htmlspecialchars($emailSettings['mail_from_address']) . "
Test Time: {$currentTime}
"; $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'); } } }