Files
domnitor/app/Models/Setting.php

317 lines
9.0 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.0');
}
/**
* 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;
}
/**
* 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();
}
}