Files
domnitor/app/Models/Setting.php

359 lines
11 KiB
PHP
Raw Normal View History

<?php
namespace App\Models;
use Core\Model;
2025-10-23 00:18:16 +03:00
use App\Services\Logger;
class Setting extends Model
{
protected static string $table = 'settings';
2025-10-23 00:18:16 +03:00
private Logger $logger;
public function __construct()
{
parent::__construct();
$this->logger = new Logger('settings');
}
/**
* Get setting by key
*/
public function getByKey(string $key): ?array
{
$stmt = $this->db->prepare("SELECT * FROM settings WHERE setting_key = ?");
$stmt->execute([$key]);
$result = $stmt->fetch();
return $result ?: null;
}
/**
* Get setting value by key
*/
public function getValue(string $key, $default = null)
{
$setting = $this->getByKey($key);
return $setting ? $setting['setting_value'] : $default;
}
/**
* Set or update setting value
*/
public function setValue(string $key, $value): bool
{
$existing = $this->getByKey($key);
if ($existing) {
$stmt = $this->db->prepare("UPDATE settings SET setting_value = ?, updated_at = NOW() WHERE setting_key = ?");
return $stmt->execute([$value, $key]);
} else {
$stmt = $this->db->prepare("INSERT INTO settings (setting_key, setting_value) VALUES (?, ?)");
return $stmt->execute([$key, $value]);
}
}
/**
* Get all settings as key-value pairs
*/
public function getAllAsKeyValue(): array
{
$settings = $this->all();
$result = [];
foreach ($settings as $setting) {
$result[$setting['setting_key']] = $setting['setting_value'];
}
return $result;
}
/**
* Get notification days as array
*/
public function getNotificationDays(): array
{
$value = $this->getValue('notification_days_before', '30,15,7,3,1');
return array_map('intval', explode(',', $value));
}
/**
* Get check interval hours
*/
public function getCheckIntervalHours(): int
{
return (int)$this->getValue('check_interval_hours', 24);
}
/**
* Update notification days
*/
public function updateNotificationDays(array $days): bool
{
$value = implode(',', array_map('intval', $days));
return $this->setValue('notification_days_before', $value);
}
/**
* Update check interval
*/
public function updateCheckInterval(int $hours): bool
{
return $this->setValue('check_interval_hours', $hours);
}
/**
* Get last check run timestamp
*/
public function getLastCheckRun(): ?string
{
return $this->getValue('last_check_run');
}
/**
* Update last check run timestamp
*/
public function updateLastCheckRun(): bool
{
return $this->setValue('last_check_run', date('Y-m-d H:i:s'));
}
Upgraded to 1.1.0 1.1.0 (2025-10-09) - **User Notifications System** - In-app notification center with 7 notification types, filtering, pagination - **Advanced Session Management** - Database-backed sessions with geolocation (country, city, ISP) - **Remote Session Control** - Terminate any device instantly with immediate logout validation - **Enhanced Profile Page** - Sidebar navigation with 4 tabs, hash-based routing (#profile, #security, #sessions) - **MVC Architecture Refactoring** - 3 new Helpers (Layout, Domain, Session), ~265 lines cleaned from views - **Geolocation Tracking** - IP-based location detection using ip-api.com, country flags with flag-icons - **Device Detection** - Browser & device type parsing (Chrome/Firefox/Safari, Desktop/Mobile/Tablet) - **Auto-Detected Cron Paths** - Settings show actual installation paths (thanks @jadeops) - **Welcome Notifications** - Sent to new users on registration or fresh install - **Upgrade Notifications** - Admins notified on system updates with version & migration count - **Web-Based Installer** - Replaces CLI, auto-generates encryption key, one-time password display - **Web-Based Updater** - `/install/update` for running new migrations with smart detection - **User Registration** - Full signup flow with email verification, password reset, resend verification - **User Management** - CRUD for users with filtering, sorting, pagination (admin-only) - **Remember Me** - 30-day secure tokens linked to sessions, cascade deletion on logout - **Session Validator** - Middleware validates sessions on every request for instant remote logout - **Consistent UI/UX** - Unified filtering, sorting, pagination across Domains, Users, Notifications, TLD Registry - **Smart Migrations** - Consolidated schema for fresh installs, incremental for upgrades - **XSS Protection** - htmlspecialchars() applied across all user-facing data (thanks @jadeops)
2025-10-09 18:02:46 +03:00
/**
* Get application version
*/
public function getAppVersion(): string
{
return $this->getValue('app_version', '1.1.3');
Upgraded to 1.1.0 1.1.0 (2025-10-09) - **User Notifications System** - In-app notification center with 7 notification types, filtering, pagination - **Advanced Session Management** - Database-backed sessions with geolocation (country, city, ISP) - **Remote Session Control** - Terminate any device instantly with immediate logout validation - **Enhanced Profile Page** - Sidebar navigation with 4 tabs, hash-based routing (#profile, #security, #sessions) - **MVC Architecture Refactoring** - 3 new Helpers (Layout, Domain, Session), ~265 lines cleaned from views - **Geolocation Tracking** - IP-based location detection using ip-api.com, country flags with flag-icons - **Device Detection** - Browser & device type parsing (Chrome/Firefox/Safari, Desktop/Mobile/Tablet) - **Auto-Detected Cron Paths** - Settings show actual installation paths (thanks @jadeops) - **Welcome Notifications** - Sent to new users on registration or fresh install - **Upgrade Notifications** - Admins notified on system updates with version & migration count - **Web-Based Installer** - Replaces CLI, auto-generates encryption key, one-time password display - **Web-Based Updater** - `/install/update` for running new migrations with smart detection - **User Registration** - Full signup flow with email verification, password reset, resend verification - **User Management** - CRUD for users with filtering, sorting, pagination (admin-only) - **Remember Me** - 30-day secure tokens linked to sessions, cascade deletion on logout - **Session Validator** - Middleware validates sessions on every request for instant remote logout - **Consistent UI/UX** - Unified filtering, sorting, pagination across Domains, Users, Notifications, TLD Registry - **Smart Migrations** - Consolidated schema for fresh installs, incremental for upgrades - **XSS Protection** - htmlspecialchars() applied across all user-facing data (thanks @jadeops)
2025-10-09 18:02:46 +03:00
}
/**
* Get application settings
*/
public function getAppSettings(): array
{
return [
'app_name' => $this->getValue('app_name', 'Domain Monitor'),
'app_url' => $this->getValue('app_url', 'http://localhost:8000'),
Upgraded to 1.1.0 1.1.0 (2025-10-09) - **User Notifications System** - In-app notification center with 7 notification types, filtering, pagination - **Advanced Session Management** - Database-backed sessions with geolocation (country, city, ISP) - **Remote Session Control** - Terminate any device instantly with immediate logout validation - **Enhanced Profile Page** - Sidebar navigation with 4 tabs, hash-based routing (#profile, #security, #sessions) - **MVC Architecture Refactoring** - 3 new Helpers (Layout, Domain, Session), ~265 lines cleaned from views - **Geolocation Tracking** - IP-based location detection using ip-api.com, country flags with flag-icons - **Device Detection** - Browser & device type parsing (Chrome/Firefox/Safari, Desktop/Mobile/Tablet) - **Auto-Detected Cron Paths** - Settings show actual installation paths (thanks @jadeops) - **Welcome Notifications** - Sent to new users on registration or fresh install - **Upgrade Notifications** - Admins notified on system updates with version & migration count - **Web-Based Installer** - Replaces CLI, auto-generates encryption key, one-time password display - **Web-Based Updater** - `/install/update` for running new migrations with smart detection - **User Registration** - Full signup flow with email verification, password reset, resend verification - **User Management** - CRUD for users with filtering, sorting, pagination (admin-only) - **Remember Me** - 30-day secure tokens linked to sessions, cascade deletion on logout - **Session Validator** - Middleware validates sessions on every request for instant remote logout - **Consistent UI/UX** - Unified filtering, sorting, pagination across Domains, Users, Notifications, TLD Registry - **Smart Migrations** - Consolidated schema for fresh installs, incremental for upgrades - **XSS Protection** - htmlspecialchars() applied across all user-facing data (thanks @jadeops)
2025-10-09 18:02:46 +03:00
'app_timezone' => $this->getValue('app_timezone', 'UTC'),
'app_version' => $this->getAppVersion()
];
}
/**
* Get email settings
*/
public function getEmailSettings(): array
{
$encryptedPassword = $this->getValue('mail_password', '');
// Decrypt password if it's encrypted
$decryptedPassword = '';
if (!empty($encryptedPassword)) {
try {
$encryption = new \Core\Encryption();
$decryptedPassword = $encryption->decrypt($encryptedPassword);
} catch (\Exception $e) {
// If decryption fails, it might be plaintext (migration scenario)
// Try to use as-is but log the issue
2025-10-23 00:18:16 +03:00
$this->logger->warning("Failed to decrypt mail_password", [
'error' => $e->getMessage()
]);
$decryptedPassword = $encryptedPassword;
}
}
return [
'mail_host' => $this->getValue('mail_host', 'smtp.mailtrap.io'),
'mail_port' => $this->getValue('mail_port', '2525'),
'mail_username' => $this->getValue('mail_username', ''),
'mail_password' => $decryptedPassword,
'mail_encryption' => $this->getValue('mail_encryption', 'tls'),
'mail_from_address' => $this->getValue('mail_from_address', 'noreply@domainmonitor.com'),
'mail_from_name' => $this->getValue('mail_from_name', 'Domain Monitor')
];
}
/**
* Update application settings
*/
public function updateAppSettings(array $settings): bool
{
$result = true;
foreach ($settings as $key => $value) {
if (!$this->setValue($key, $value)) {
$result = false;
}
}
return $result;
}
/**
* Update email settings
*/
public function updateEmailSettings(array $settings): bool
{
$result = true;
foreach ($settings as $key => $value) {
// Encrypt mail_password before storing
if ($key === 'mail_password' && !empty($value)) {
try {
$encryption = new \Core\Encryption();
$value = $encryption->encrypt($value);
} catch (\Exception $e) {
2025-10-23 00:18:16 +03:00
$this->logger->error("Failed to encrypt mail_password", [
'error' => $e->getMessage()
]);
return false;
}
}
if (!$this->setValue($key, $value)) {
$result = false;
}
}
return $result;
}
/**
* Get CAPTCHA settings
*/
public function getCaptchaSettings(): array
{
$encryptedSecret = $this->getValue('captcha_secret_key', '');
// Decrypt secret key if it's encrypted
$decryptedSecret = '';
if (!empty($encryptedSecret)) {
try {
$encryption = new \Core\Encryption();
$decryptedSecret = $encryption->decrypt($encryptedSecret);
} catch (\Exception $e) {
// If decryption fails, it might be plaintext (migration scenario)
2025-10-23 00:18:16 +03:00
$this->logger->warning("Failed to decrypt captcha_secret_key", [
'error' => $e->getMessage()
]);
$decryptedSecret = $encryptedSecret;
}
}
return [
'provider' => $this->getValue('captcha_provider', 'disabled'),
'site_key' => $this->getValue('captcha_site_key', ''),
'secret_key' => $decryptedSecret,
'score_threshold' => $this->getValue('recaptcha_v3_score_threshold', '0.5')
];
}
/**
* Update CAPTCHA settings
*/
public function updateCaptchaSettings(array $settings): bool
{
$result = true;
// Encrypt secret key before storing
if (isset($settings['captcha_secret_key']) && !empty($settings['captcha_secret_key'])) {
try {
$encryption = new \Core\Encryption();
$settings['captcha_secret_key'] = $encryption->encrypt($settings['captcha_secret_key']);
} catch (\Exception $e) {
2025-10-23 00:18:16 +03:00
$this->logger->error("Failed to encrypt captcha_secret_key", [
'error' => $e->getMessage()
]);
return false;
}
}
foreach ($settings as $key => $value) {
if (!$this->setValue($key, $value)) {
$result = false;
}
}
return $result;
}
/**
* Get 2FA settings
*/
public function getTwoFactorSettings(): array
{
return [
'policy' => $this->getValue('two_factor_policy', 'optional'),
'rate_limit_minutes' => (int)$this->getValue('two_factor_rate_limit_minutes', 15),
'email_code_expiry_minutes' => (int)$this->getValue('two_factor_email_code_expiry_minutes', 10)
];
}
/**
* Update 2FA settings
*/
public function updateTwoFactorSettings(array $settings): bool
{
$result = true;
foreach ($settings as $key => $value) {
if (!$this->setValue($key, $value)) {
$result = false;
}
}
return $result;
}
Add domain status notifications & login alerts Introduce richer notifications and domain status handling across the app. - NotificationService: Add domain status alert formatting/sending, in-app notifications for available/registered/redemption/pending_delete, richer session_new and session_failed notifications (geolocation + UA parsing) and helpers for human-readable status labels. - Auth/TwoFactor: Emit notifications for successful logins (including remember-me and 2FA) and failed login attempts; update last-login timestamp on various flows. - DomainController: Wrap bulk domain create in try/catch to handle duplicate race conditions and log failures. - WhoisService: Detect redemption_period and pending_delete statuses from WHOIS/EPP statuses. - Settings/Setting: Add settings support for notification status triggers and bump default app_version to 1.1.2; persist/update status trigger values. - Views/Layout/View helpers: Add parsing/formatting for login notification data, add new status labels/classes (available, redemption_period, pending_delete), update notification icons/colors mapping. - Top-nav & Notifications UI: Enhance dropdown with rich login/failed-login display (flags, device icons), clickable domain redirects when marking read, badge IDs for dynamic updates. - Error admin UI: Add copy error report button with robust clipboard fallback and toast UI reused from messages; improved copy UX in admin index/detail. - Installer: Add new migration 024 to installer migration lists and adjust detected toVersion to 1.1.2. - DB: Add migration file 024_add_status_notifications_v1.1.2.sql (new file). These changes add user-facing alerts for domain lifecycle events and stronger login/security notifications while improving UI feedback and robustness during bulk operations.
2026-02-08 22:58:59 +02:00
/**
* Get notification status triggers as array
* Returns which domain status changes should trigger notifications
*/
public function getNotificationStatusTriggers(): array
{
$value = $this->getValue('notification_status_triggers', 'available,registered,expired,redemption_period,pending_delete');
return array_map('trim', explode(',', $value));
}
/**
* Update notification status triggers
*/
public function updateNotificationStatusTriggers(array $triggers): bool
{
$validTriggers = ['available', 'registered', 'expired', 'redemption_period', 'pending_delete'];
$triggers = array_intersect($triggers, $validTriggers);
$value = implode(',', $triggers);
return $this->setValue('notification_status_triggers', $value);
}
/**
* Get update settings
*/
public function getUpdateSettings(): array
{
return [
'update_channel' => $this->getValue('update_channel', 'stable'),
'last_update_check' => $this->getValue('last_update_check', null),
'latest_available_version' => $this->getValue('latest_available_version', null),
'latest_release_notes' => $this->getValue('latest_release_notes', ''),
'latest_release_url' => $this->getValue('latest_release_url', ''),
'latest_release_published_at' => $this->getValue('latest_release_published_at', ''),
'installed_commit_sha' => $this->getValue('installed_commit_sha', null),
'update_backup_path' => $this->getValue('update_backup_path', null),
'update_db_backup_path' => $this->getValue('update_db_backup_path', null),
'commits_behind_count' => (int) $this->getValue('commits_behind_count', 0),
'latest_remote_sha' => $this->getValue('latest_remote_sha', ''),
'update_badge_enabled' => $this->getValue('update_badge_enabled', '1'),
];
}
/**
* Clear old notification logs
*/
public function clearOldNotificationLogs(int $daysOld = 30): int
{
$stmt = $this->db->prepare(
"DELETE FROM notification_logs WHERE sent_at < DATE_SUB(NOW(), INTERVAL ? DAY)"
);
$stmt->execute([$daysOld]);
return $stmt->rowCount();
}
}