2025-10-08 14:23:07 +03:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Controllers;
|
|
|
|
|
|
|
|
|
|
use Core\Controller;
|
|
|
|
|
use App\Models\Domain;
|
|
|
|
|
use App\Models\NotificationGroup;
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
use App\Models\SslCertificate;
|
2025-10-08 14:23:07 +03:00
|
|
|
use App\Services\WhoisService;
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
use App\Services\SslService;
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
class DomainController extends Controller
|
|
|
|
|
{
|
|
|
|
|
private Domain $domainModel;
|
|
|
|
|
private NotificationGroup $groupModel;
|
|
|
|
|
private WhoisService $whoisService;
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
private SslCertificate $sslCertificateModel;
|
|
|
|
|
private SslService $sslService;
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
public function __construct()
|
|
|
|
|
{
|
|
|
|
|
$this->domainModel = new Domain();
|
|
|
|
|
$this->groupModel = new NotificationGroup();
|
|
|
|
|
$this->whoisService = new WhoisService();
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
$this->sslCertificateModel = new SslCertificate();
|
|
|
|
|
$this->sslService = new SslService();
|
2025-10-08 14:23:07 +03:00
|
|
|
}
|
|
|
|
|
|
2025-10-20 21:08:09 +03:00
|
|
|
/**
|
|
|
|
|
* Check domain access based on isolation mode
|
|
|
|
|
*/
|
|
|
|
|
private function checkDomainAccess(int $id): ?array
|
|
|
|
|
{
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
return $this->domainModel->findWithIsolation($id, $userId);
|
|
|
|
|
} else {
|
|
|
|
|
return $this->domainModel->find($id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
public function index()
|
|
|
|
|
{
|
2025-10-20 17:04:13 +03:00
|
|
|
// Get current user and isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
// Get filter parameters
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
$search = \App\Helpers\InputValidator::sanitizeSearch($_GET['search'] ?? '', 100);
|
2025-10-08 14:23:07 +03:00
|
|
|
$status = $_GET['status'] ?? '';
|
|
|
|
|
$groupId = $_GET['group'] ?? '';
|
2025-10-12 12:46:16 +03:00
|
|
|
$tag = $_GET['tag'] ?? '';
|
2025-10-08 14:23:07 +03:00
|
|
|
$sortBy = $_GET['sort'] ?? 'domain_name';
|
|
|
|
|
$sortOrder = $_GET['order'] ?? 'asc';
|
|
|
|
|
$page = max(1, (int)($_GET['page'] ?? 1));
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
// Remember per_page preference via cookie
|
|
|
|
|
if (isset($_GET['per_page'])) {
|
|
|
|
|
$perPage = max(10, min(100, (int)$_GET['per_page']));
|
|
|
|
|
setcookie('domains_per_page', (string)$perPage, time() + 365 * 24 * 60 * 60, '/');
|
|
|
|
|
} else {
|
|
|
|
|
$perPage = max(10, min(100, (int)($_COOKIE['domains_per_page'] ?? 25)));
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
2025-10-08 18:54:34 +03:00
|
|
|
// Get expiring threshold from settings
|
|
|
|
|
$notificationDays = $settingModel->getNotificationDays();
|
|
|
|
|
$expiringThreshold = !empty($notificationDays) ? max($notificationDays) : 30;
|
|
|
|
|
|
2025-10-10 14:01:19 +03:00
|
|
|
// Prepare filters array
|
|
|
|
|
$filters = [
|
|
|
|
|
'search' => $search,
|
|
|
|
|
'status' => $status,
|
2025-10-12 12:46:16 +03:00
|
|
|
'group' => $groupId,
|
|
|
|
|
'tag' => $tag
|
2025-10-10 14:01:19 +03:00
|
|
|
];
|
2025-10-08 14:23:07 +03:00
|
|
|
|
2025-10-10 14:01:19 +03:00
|
|
|
// Get filtered and paginated domains using model
|
2025-10-20 18:13:57 +03:00
|
|
|
$result = $this->domainModel->getFilteredPaginated($filters, $sortBy, $sortOrder, $page, $perPage, $expiringThreshold, $isolationMode === 'isolated' ? $userId : null);
|
2025-10-08 14:23:07 +03:00
|
|
|
|
2025-10-20 18:13:57 +03:00
|
|
|
// Get groups and tags based on isolation mode
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$groups = $this->groupModel->getAllWithChannelCount($userId);
|
|
|
|
|
$allTags = $this->domainModel->getAllTags($userId);
|
|
|
|
|
} else {
|
|
|
|
|
$groups = $this->groupModel->getAllWithChannelCount();
|
|
|
|
|
$allTags = $this->domainModel->getAllTags();
|
|
|
|
|
}
|
2025-10-12 12:46:16 +03:00
|
|
|
|
2025-10-25 02:04:00 +03:00
|
|
|
// Get available tags for bulk operations
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage($userId);
|
|
|
|
|
} else {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage();
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// Format domains for display
|
2025-10-10 14:01:19 +03:00
|
|
|
$formattedDomains = \App\Helpers\DomainHelper::formatMultiple($result['domains']);
|
2025-10-08 14:23:07 +03:00
|
|
|
|
2025-10-20 17:40:43 +03:00
|
|
|
// Get users for transfer functionality (admin only)
|
|
|
|
|
$users = [];
|
|
|
|
|
if (\Core\Auth::isAdmin()) {
|
|
|
|
|
$userModel = new \App\Models\User();
|
|
|
|
|
$users = $userModel->all();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$this->view('domains/index', [
|
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
|
|
|
'domains' => $formattedDomains,
|
2025-10-08 14:23:07 +03:00
|
|
|
'groups' => $groups,
|
2025-10-12 12:46:16 +03:00
|
|
|
'allTags' => $allTags,
|
2025-10-25 02:04:00 +03:00
|
|
|
'availableTags' => $availableTags,
|
2025-10-20 17:40:43 +03:00
|
|
|
'users' => $users,
|
2025-10-08 14:23:07 +03:00
|
|
|
'filters' => [
|
|
|
|
|
'search' => $search,
|
|
|
|
|
'status' => $status,
|
|
|
|
|
'group' => $groupId,
|
2025-10-12 12:46:16 +03:00
|
|
|
'tag' => $tag,
|
2025-10-08 14:23:07 +03:00
|
|
|
'sort' => $sortBy,
|
|
|
|
|
'order' => $sortOrder
|
|
|
|
|
],
|
2025-10-10 14:01:19 +03:00
|
|
|
'pagination' => $result['pagination'],
|
2025-10-08 14:23:07 +03:00
|
|
|
'title' => 'Domains'
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
/**
|
|
|
|
|
* Export domains as CSV or JSON
|
|
|
|
|
*/
|
|
|
|
|
public function export()
|
|
|
|
|
{
|
|
|
|
|
$logger = new \App\Services\Logger('export');
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
$format = $_GET['format'] ?? 'csv';
|
|
|
|
|
$logger->info("Domains export started", ['format' => $format, 'user_id' => $userId]);
|
|
|
|
|
|
|
|
|
|
if (!in_array($format, ['csv', 'json'])) {
|
|
|
|
|
$_SESSION['error'] = 'Invalid export format';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get all domains with groups and tags
|
|
|
|
|
$domains = $this->domainModel->getAllWithGroups($isolationMode === 'isolated' ? $userId : null);
|
|
|
|
|
|
|
|
|
|
$exportData = [];
|
|
|
|
|
foreach ($domains as $domain) {
|
|
|
|
|
$exportData[] = [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'status' => $domain['status'] ?? '',
|
|
|
|
|
'registrar' => $domain['registrar'] ?? '',
|
|
|
|
|
'expiration_date' => $domain['expiration_date'] ?? '',
|
|
|
|
|
'tags' => $domain['tags'] ?? '',
|
|
|
|
|
'notification_group' => $domain['group_name'] ?? '',
|
|
|
|
|
'notes' => $domain['notes'] ?? ''
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$date = date('Y-m-d');
|
|
|
|
|
$filename = "domains_export_{$date}";
|
|
|
|
|
|
|
|
|
|
// Clean any prior output buffers to prevent header conflicts
|
|
|
|
|
while (ob_get_level()) {
|
|
|
|
|
ob_end_clean();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($format === 'json') {
|
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
|
header("Content-Disposition: attachment; filename=\"{$filename}.json\"");
|
|
|
|
|
header('Cache-Control: no-cache, must-revalidate');
|
|
|
|
|
header('Pragma: no-cache');
|
|
|
|
|
echo json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
|
|
|
} else {
|
|
|
|
|
// Build CSV in memory to avoid fopen('php://output') issues
|
|
|
|
|
$csvContent = $this->buildCsv($exportData, ['domain_name', 'status', 'registrar', 'expiration_date', 'tags', 'notification_group', 'notes']);
|
|
|
|
|
$logger->info("CSV content built", ['bytes' => strlen($csvContent)]);
|
|
|
|
|
|
|
|
|
|
header('Content-Type: text/csv; charset=utf-8');
|
|
|
|
|
header("Content-Disposition: attachment; filename=\"{$filename}.csv\"");
|
|
|
|
|
header('Content-Length: ' . strlen($csvContent));
|
|
|
|
|
header('Cache-Control: no-cache, must-revalidate');
|
|
|
|
|
header('Pragma: no-cache');
|
|
|
|
|
echo $csvContent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$logger->info("Domains export completed successfully");
|
|
|
|
|
exit;
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
$logger->error("Domains export failed", [
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
'file' => $e->getFile(),
|
|
|
|
|
'line' => $e->getLine()
|
|
|
|
|
]);
|
|
|
|
|
$_SESSION['error'] = 'Export failed: ' . $e->getMessage();
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build CSV string in memory from array data
|
|
|
|
|
*/
|
|
|
|
|
private function buildCsv(array $rows, array $headers): string
|
|
|
|
|
{
|
|
|
|
|
$handle = fopen('php://temp', 'r+');
|
|
|
|
|
fputcsv($handle, $headers, ',', '"', '\\');
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
|
fputcsv($handle, array_values($row), ',', '"', '\\');
|
|
|
|
|
}
|
|
|
|
|
rewind($handle);
|
|
|
|
|
$csv = stream_get_contents($handle);
|
|
|
|
|
fclose($handle);
|
|
|
|
|
return $csv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Import domains from CSV or JSON file
|
|
|
|
|
*/
|
|
|
|
|
public function import()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains/bulk-add');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains/bulk-add');
|
|
|
|
|
|
2026-03-02 11:17:58 +02:00
|
|
|
$logger = new \App\Services\Logger('import');
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$logger->info('Domains import started', ['user_id' => $userId]);
|
|
|
|
|
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
if (!isset($_FILES['import_file']) || $_FILES['import_file']['error'] !== UPLOAD_ERR_OK) {
|
2026-03-02 11:17:58 +02:00
|
|
|
$logger->warning('No valid file uploaded for domains import');
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
$_SESSION['error'] = 'Please select a valid file to import';
|
|
|
|
|
$this->redirect('/domains/bulk-add');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$file = $_FILES['import_file'];
|
2026-03-02 11:17:58 +02:00
|
|
|
$logger->info('Import file received', [
|
|
|
|
|
'filename' => $file['name'],
|
|
|
|
|
'size' => $file['size']
|
|
|
|
|
]);
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
|
|
|
|
|
// Validate file size (5MB max for domains)
|
|
|
|
|
if ($file['size'] > 5242880) {
|
2026-03-02 11:17:58 +02:00
|
|
|
$logger->warning('Import file too large', ['size' => $file['size']]);
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
$_SESSION['error'] = 'File is too large. Maximum size is 5MB';
|
|
|
|
|
$this->redirect('/domains/bulk-add');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
|
|
|
|
if (!in_array($ext, ['csv', 'json'])) {
|
2026-03-02 11:17:58 +02:00
|
|
|
$logger->warning('Invalid file type for domains import', ['extension' => $ext]);
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
$_SESSION['error'] = 'Invalid file type. Please upload a CSV or JSON file';
|
|
|
|
|
$this->redirect('/domains/bulk-add');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$content = file_get_contents($file['tmp_name']);
|
|
|
|
|
$domainsData = [];
|
|
|
|
|
|
|
|
|
|
if ($ext === 'json') {
|
|
|
|
|
$parsed = json_decode($content, true);
|
|
|
|
|
if (!is_array($parsed)) {
|
2026-03-02 11:17:58 +02:00
|
|
|
$logger->error('Invalid JSON file for domains import');
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
$_SESSION['error'] = 'Invalid JSON file';
|
|
|
|
|
$this->redirect('/domains/bulk-add');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$domainsData = $parsed;
|
|
|
|
|
} else {
|
|
|
|
|
$lines = array_filter(explode("\n", $content));
|
|
|
|
|
$header = null;
|
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
|
$row = str_getcsv(trim($line), ',', '"', '\\');
|
|
|
|
|
if (!$header) {
|
|
|
|
|
$header = array_map('strtolower', array_map('trim', $row));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$item = [];
|
|
|
|
|
foreach ($header as $i => $col) {
|
|
|
|
|
$item[$col] = $row[$i] ?? '';
|
|
|
|
|
}
|
|
|
|
|
$domainsData[] = $item;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (empty($domainsData)) {
|
2026-03-02 11:17:58 +02:00
|
|
|
$logger->warning('No domains found in import file');
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
$_SESSION['error'] = 'No domains found in file';
|
|
|
|
|
$this->redirect('/domains/bulk-add');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 11:17:58 +02:00
|
|
|
$logger->info('Domains data parsed from file', ['entries' => count($domainsData)]);
|
|
|
|
|
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
|
|
|
|
|
// Form-level notification group
|
|
|
|
|
$formGroupId = (int)($_POST['notification_group_id'] ?? 0);
|
|
|
|
|
|
|
|
|
|
$added = 0;
|
|
|
|
|
$skipped = 0;
|
|
|
|
|
$errors = [];
|
|
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$invalidImported = 0;
|
|
|
|
|
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
foreach ($domainsData as $row) {
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$domainName = trim($row['domain_name'] ?? '');
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
if (empty($domainName)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$domainCheck = \App\Helpers\InputValidator::validateRootDomain($domainName);
|
|
|
|
|
if (!$domainCheck['valid']) {
|
|
|
|
|
$invalidImported++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$domainName = $domainCheck['domain'];
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
|
|
|
|
|
if ($this->domainModel->existsByDomain($domainName)) {
|
|
|
|
|
$skipped++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Fetch WHOIS data
|
|
|
|
|
$whoisData = $this->whoisService->getDomainInfo($domainName);
|
|
|
|
|
|
|
|
|
|
if (!$whoisData) {
|
|
|
|
|
$errors[] = $domainName;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$status = $this->whoisService->getDomainStatus(
|
|
|
|
|
$whoisData['expiration_date'] ?? null,
|
|
|
|
|
$whoisData['status'] ?? [],
|
|
|
|
|
$whoisData
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Determine notification group: from file column or form fallback
|
|
|
|
|
$groupId = null;
|
|
|
|
|
$groupName = trim($row['notification_group'] ?? '');
|
|
|
|
|
if (!empty($groupName)) {
|
|
|
|
|
$groupStmt = $this->groupModel->findByName($groupName, $isolationMode === 'isolated' ? $userId : null);
|
|
|
|
|
if ($groupStmt) {
|
|
|
|
|
$groupId = $groupStmt['id'];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!$groupId && $formGroupId > 0) {
|
|
|
|
|
$groupId = $formGroupId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$domainId = $this->domainModel->create([
|
|
|
|
|
'domain_name' => $domainName,
|
|
|
|
|
'registrar' => $whoisData['registrar'] ?? null,
|
|
|
|
|
'registrar_url' => $whoisData['registrar_url'] ?? null,
|
|
|
|
|
'expiration_date' => $whoisData['expiration_date'] ?? null,
|
|
|
|
|
'updated_date' => $whoisData['updated_date'] ?? null,
|
|
|
|
|
'abuse_email' => $whoisData['abuse_email'] ?? null,
|
|
|
|
|
'status' => $status,
|
|
|
|
|
'whois_data' => json_encode($whoisData),
|
|
|
|
|
'notes' => trim($row['notes'] ?? ''),
|
|
|
|
|
'last_checked' => date('Y-m-d H:i:s'),
|
|
|
|
|
'notification_group_id' => $groupId,
|
|
|
|
|
'user_id' => $isolationMode === 'isolated' ? $userId : null
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Handle tags from file
|
|
|
|
|
$fileTags = trim($row['tags'] ?? '');
|
|
|
|
|
if (!empty($fileTags) && $domainId) {
|
|
|
|
|
$tagModel->updateDomainTags($domainId, $fileTags, $userId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($domainId) {
|
|
|
|
|
$added++;
|
|
|
|
|
}
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$errors[] = $domainName;
|
|
|
|
|
$logger->error('Domain import failed', ['domain' => $domainName, 'error' => $e->getMessage()]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 11:17:58 +02:00
|
|
|
$logger->info('Domains import completed', [
|
|
|
|
|
'added' => $added,
|
|
|
|
|
'skipped' => $skipped,
|
|
|
|
|
'failed' => count($errors)
|
|
|
|
|
]);
|
|
|
|
|
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
$msg = "{$added} domain(s) imported successfully";
|
|
|
|
|
if ($skipped > 0) $msg .= ", {$skipped} skipped (already exist)";
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
if ($invalidImported > 0) $msg .= ", {$invalidImported} rejected (not root domains)";
|
Add import/export and update system
Implement CSV/JSON import and export for domains, notification groups and tags (with masking for sensitive channel data), including size/format validation, in-memory CSV building, and logging. Add tag transfer and bulk transfer actions (admin-only). Introduce a new update system: Add UpdateController and UpdateService, migration 025_add_update_system_v1.1.3.sql, and installer changes to include the new migration and version handling; provide endpoints to check, apply, rollback and configure updates. Update helpers and UI bits: add getUpdateBadgeInfo in LayoutHelper, update notification icons/redirects, and add getMaxUploadSize in ViewHelper. Misc: add NotificationGroup::findByName, tweak .gitignore backups path, and update related views and routes.
2026-02-11 17:43:23 +02:00
|
|
|
if (!empty($errors)) $msg .= ", " . count($errors) . " failed";
|
|
|
|
|
$_SESSION['success'] = $msg;
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
public function create()
|
|
|
|
|
{
|
2025-10-20 18:50:32 +03:00
|
|
|
// Get groups based on isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$groups = $this->groupModel->getAllWithChannelCount($userId);
|
|
|
|
|
} else {
|
|
|
|
|
$groups = $this->groupModel->getAllWithChannelCount();
|
|
|
|
|
}
|
2025-10-25 02:04:00 +03:00
|
|
|
|
|
|
|
|
// Get available tags for the new tag system
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage($userId);
|
|
|
|
|
} else {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage();
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
$this->view('domains/create', [
|
|
|
|
|
'groups' => $groups,
|
2025-10-25 02:04:00 +03:00
|
|
|
'availableTags' => $availableTags,
|
2025-10-08 14:23:07 +03:00
|
|
|
'title' => 'Add Domain'
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function store()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains/create');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains/create');
|
|
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$domainName = \App\Helpers\InputValidator::sanitizeDomainInput(trim($_POST['domain_name'] ?? ''));
|
2025-10-08 14:23:07 +03:00
|
|
|
$groupId = !empty($_POST['notification_group_id']) ? (int)$_POST['notification_group_id'] : null;
|
2025-10-12 12:46:16 +03:00
|
|
|
$tagsInput = trim($_POST['tags'] ?? '');
|
2025-10-20 19:05:33 +03:00
|
|
|
$userId = \Core\Auth::id();
|
2025-10-08 14:23:07 +03:00
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
// Validate root domain (not a subdomain, respects multi-level TLDs)
|
|
|
|
|
$domainCheck = \App\Helpers\InputValidator::validateRootDomain($domainName);
|
|
|
|
|
if (!$domainCheck['valid']) {
|
|
|
|
|
$_SESSION['error'] = $domainCheck['error'];
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
$this->redirect('/domains/create');
|
|
|
|
|
return;
|
|
|
|
|
}
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$domainName = $domainCheck['domain'];
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
|
2025-10-12 12:46:16 +03:00
|
|
|
// Validate tags
|
|
|
|
|
$tagValidation = \App\Helpers\InputValidator::validateTags($tagsInput);
|
|
|
|
|
if (!$tagValidation['valid']) {
|
|
|
|
|
$_SESSION['error'] = $tagValidation['error'];
|
|
|
|
|
$this->redirect('/domains/create');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$tags = $tagValidation['tags'];
|
|
|
|
|
|
2025-10-20 19:02:56 +03:00
|
|
|
// Validate notification group in isolation mode
|
|
|
|
|
if ($groupId) {
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$group = $this->groupModel->find($groupId);
|
|
|
|
|
if (!$group || $group['user_id'] != $userId) {
|
|
|
|
|
$_SESSION['error'] = 'You can only assign domains to your own notification groups';
|
|
|
|
|
$this->redirect('/domains/create');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
// Check if domain already exists
|
|
|
|
|
if ($this->domainModel->existsByDomain($domainName)) {
|
|
|
|
|
$_SESSION['error'] = 'Domain already exists';
|
|
|
|
|
$this->redirect('/domains/create');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get WHOIS information
|
|
|
|
|
$whoisData = $this->whoisService->getDomainInfo($domainName);
|
|
|
|
|
|
|
|
|
|
if (!$whoisData) {
|
|
|
|
|
$_SESSION['error'] = 'Could not retrieve WHOIS information for this domain';
|
|
|
|
|
$this->redirect('/domains/create');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create domain
|
2025-11-18 13:22:49 +02:00
|
|
|
$status = $this->whoisService->getDomainStatus($whoisData['expiration_date'], $whoisData['status'] ?? [], $whoisData);
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
// Warn if domain is available (not registered)
|
|
|
|
|
if ($status === 'available') {
|
|
|
|
|
$_SESSION['warning'] = "Note: '$domainName' appears to be AVAILABLE (not registered). You're monitoring an unregistered domain.";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$id = $this->domainModel->create([
|
|
|
|
|
'domain_name' => $domainName,
|
|
|
|
|
'notification_group_id' => $groupId,
|
|
|
|
|
'registrar' => $whoisData['registrar'],
|
|
|
|
|
'registrar_url' => $whoisData['registrar_url'] ?? null,
|
|
|
|
|
'expiration_date' => $whoisData['expiration_date'],
|
|
|
|
|
'updated_date' => $whoisData['updated_date'] ?? null,
|
|
|
|
|
'abuse_email' => $whoisData['abuse_email'] ?? null,
|
|
|
|
|
'last_checked' => date('Y-m-d H:i:s'),
|
|
|
|
|
'status' => $status,
|
|
|
|
|
'whois_data' => json_encode($whoisData),
|
2025-10-20 19:02:56 +03:00
|
|
|
'is_active' => 1,
|
|
|
|
|
'user_id' => $userId
|
|
|
|
|
]);
|
|
|
|
|
|
2025-10-25 12:40:47 +03:00
|
|
|
// Handle tags using the new tag system
|
|
|
|
|
if (!empty($tags)) {
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
$tagModel->updateDomainTags($id, $tags, $userId);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 19:02:56 +03:00
|
|
|
// Log domain creation
|
|
|
|
|
$logger = new \App\Services\Logger();
|
|
|
|
|
$logger->info('Domain created', [
|
|
|
|
|
'domain_id' => $id,
|
|
|
|
|
'domain_name' => $domainName,
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
'status' => $status,
|
|
|
|
|
'expiration_date' => $whoisData['expiration_date'],
|
|
|
|
|
'notification_group_id' => $groupId
|
2025-10-08 14:23:07 +03:00
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if ($status !== 'available') {
|
|
|
|
|
$_SESSION['success'] = "Domain '$domainName' added successfully";
|
|
|
|
|
}
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function edit($params = [])
|
|
|
|
|
{
|
|
|
|
|
$id = $params['id'] ?? 0;
|
2025-10-25 02:04:00 +03:00
|
|
|
|
|
|
|
|
// Get current user and isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
// Get domain with tags and groups
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$domain = $this->domainModel->getWithTagsAndGroups($id, $userId);
|
|
|
|
|
} else {
|
|
|
|
|
$domain = $this->domainModel->getWithTagsAndGroups($id);
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 18:50:32 +03:00
|
|
|
// Get groups based on isolation mode
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$groups = $this->groupModel->getAllWithChannelCount($userId);
|
|
|
|
|
} else {
|
|
|
|
|
$groups = $this->groupModel->getAllWithChannelCount();
|
|
|
|
|
}
|
2025-10-25 02:04:00 +03:00
|
|
|
|
|
|
|
|
// Get available tags for the new tag system
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage($userId);
|
|
|
|
|
} else {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage();
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
2025-10-25 02:04:00 +03:00
|
|
|
// Get referrer for cancel button
|
|
|
|
|
$referrer = $_GET['from'] ?? '/domains/' . $domain['id'];
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$this->view('domains/edit', [
|
|
|
|
|
'domain' => $domain,
|
|
|
|
|
'groups' => $groups,
|
2025-10-25 02:04:00 +03:00
|
|
|
'availableTags' => $availableTags,
|
|
|
|
|
'referrer' => $referrer,
|
2025-10-08 14:23:07 +03:00
|
|
|
'title' => 'Edit Domain'
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function update($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$id = (int)($params['id'] ?? 0);
|
2025-10-20 21:08:09 +03:00
|
|
|
$domain = $this->checkDomainAccess($id);
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-22 18:59:05 +03:00
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$groupId = !empty($_POST['notification_group_id']) ? (int)$_POST['notification_group_id'] : null;
|
|
|
|
|
$isActive = isset($_POST['is_active']) ? 1 : 0;
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$dnsMonitoringEnabled = isset($_POST['dns_monitoring_enabled']) ? 1 : 0;
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
$sslMonitoringEnabled = isset($_POST['ssl_monitoring_enabled']) ? 1 : 0;
|
2025-10-12 12:46:16 +03:00
|
|
|
$tagsInput = trim($_POST['tags'] ?? '');
|
2025-10-21 16:13:58 +03:00
|
|
|
$manualExpirationDate = !empty($_POST['manual_expiration_date']) ? $_POST['manual_expiration_date'] : null;
|
2025-10-12 12:46:16 +03:00
|
|
|
|
|
|
|
|
// Validate tags
|
|
|
|
|
$tagValidation = \App\Helpers\InputValidator::validateTags($tagsInput);
|
|
|
|
|
if (!$tagValidation['valid']) {
|
|
|
|
|
$_SESSION['error'] = $tagValidation['error'];
|
|
|
|
|
$this->redirect('/domains/' . $id . '/edit');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$tags = $tagValidation['tags'];
|
2025-10-20 19:02:56 +03:00
|
|
|
|
|
|
|
|
// Validate notification group in isolation mode
|
|
|
|
|
if ($groupId) {
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$group = $this->groupModel->find($groupId);
|
|
|
|
|
if (!$group || $group['user_id'] != $userId) {
|
|
|
|
|
$_SESSION['error'] = 'You can only assign domains to your own notification groups';
|
|
|
|
|
$this->redirect('/domains/' . $id . '/edit');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
// Check if monitoring status changed
|
|
|
|
|
$statusChanged = ($domain['is_active'] != $isActive);
|
|
|
|
|
$oldGroupId = $domain['notification_group_id'];
|
|
|
|
|
|
|
|
|
|
$this->domainModel->update($id, [
|
|
|
|
|
'notification_group_id' => $groupId,
|
2025-10-21 16:13:58 +03:00
|
|
|
'is_active' => $isActive,
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
'dns_monitoring_enabled' => $dnsMonitoringEnabled,
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
'ssl_monitoring_enabled' => $sslMonitoringEnabled,
|
2025-10-21 16:13:58 +03:00
|
|
|
'expiration_date' => $manualExpirationDate
|
2025-10-08 14:23:07 +03:00
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Send notification if monitoring status changed and has notification group
|
|
|
|
|
if ($statusChanged && $groupId) {
|
|
|
|
|
$notificationService = new \App\Services\NotificationService();
|
|
|
|
|
|
|
|
|
|
if ($isActive) {
|
|
|
|
|
// Monitoring activated
|
|
|
|
|
$message = "🟢 Domain monitoring has been ACTIVATED for {$domain['domain_name']}\n\n" .
|
|
|
|
|
"The domain will now be monitored regularly and you'll receive expiration alerts.";
|
|
|
|
|
$subject = "✅ Monitoring Activated: {$domain['domain_name']}";
|
|
|
|
|
} else {
|
|
|
|
|
// Monitoring deactivated
|
|
|
|
|
$message = "🔴 Domain monitoring has been DEACTIVATED for {$domain['domain_name']}\n\n" .
|
|
|
|
|
"You will no longer receive alerts for this domain until monitoring is re-enabled.";
|
|
|
|
|
$subject = "⏸️ Monitoring Paused: {$domain['domain_name']}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$notificationService->sendToGroup($groupId, $subject, $message);
|
|
|
|
|
}
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
// Send notification if SSL monitoring changed and has notification group
|
|
|
|
|
$sslMonitoringChanged = (($domain['ssl_monitoring_enabled'] ?? 0) != $sslMonitoringEnabled);
|
|
|
|
|
if ($sslMonitoringChanged && $groupId) {
|
|
|
|
|
$notificationService = new \App\Services\NotificationService();
|
|
|
|
|
|
|
|
|
|
if ($sslMonitoringEnabled) {
|
|
|
|
|
$message = "🟢 SSL monitoring has been ENABLED for {$domain['domain_name']}\n\n" .
|
|
|
|
|
"The root certificate and monitored SSL endpoints will now be checked automatically.";
|
|
|
|
|
$subject = "✅ SSL Monitoring Enabled: {$domain['domain_name']}";
|
|
|
|
|
} else {
|
|
|
|
|
$message = "🔴 SSL monitoring has been DISABLED for {$domain['domain_name']}\n\n" .
|
|
|
|
|
"SSL certificates will no longer be checked until monitoring is re-enabled.";
|
|
|
|
|
$subject = "⏸️ SSL Monitoring Disabled: {$domain['domain_name']}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$notificationService->sendToGroup($groupId, $subject, $message);
|
|
|
|
|
}
|
|
|
|
|
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
// Send notification if DNS monitoring changed and has notification group
|
|
|
|
|
$dnsMonitoringChanged = (($domain['dns_monitoring_enabled'] ?? 1) != $dnsMonitoringEnabled);
|
|
|
|
|
if ($dnsMonitoringChanged && $groupId) {
|
|
|
|
|
$notificationService = new \App\Services\NotificationService();
|
|
|
|
|
|
|
|
|
|
if ($dnsMonitoringEnabled) {
|
|
|
|
|
$message = "🟢 DNS monitoring has been ENABLED for {$domain['domain_name']}\n\n" .
|
|
|
|
|
"DNS records will be checked for changes and you'll receive alerts when they change.";
|
|
|
|
|
$subject = "✅ DNS Monitoring Enabled: {$domain['domain_name']}";
|
|
|
|
|
} else {
|
|
|
|
|
$message = "🔴 DNS monitoring has been DISABLED for {$domain['domain_name']}\n\n" .
|
|
|
|
|
"DNS records will no longer be checked. You will not receive DNS change alerts.";
|
|
|
|
|
$subject = "⏸️ DNS Monitoring Disabled: {$domain['domain_name']}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$notificationService->sendToGroup($groupId, $subject, $message);
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
// Also send notification if group changed and monitoring is active
|
|
|
|
|
if (!$statusChanged && $isActive && $oldGroupId != $groupId) {
|
|
|
|
|
$notificationService = new \App\Services\NotificationService();
|
|
|
|
|
|
|
|
|
|
if ($groupId) {
|
|
|
|
|
// Assigned to new group
|
|
|
|
|
$groupModel = new NotificationGroup();
|
|
|
|
|
$group = $groupModel->find($groupId);
|
|
|
|
|
$groupName = $group ? $group['name'] : 'Unknown Group';
|
|
|
|
|
|
|
|
|
|
$message = "🔔 Notification group updated for {$domain['domain_name']}\n\n" .
|
|
|
|
|
"This domain is now assigned to: {$groupName}\n" .
|
|
|
|
|
"You will receive expiration alerts through this notification group.";
|
|
|
|
|
$subject = "📬 Group Changed: {$domain['domain_name']}";
|
|
|
|
|
|
|
|
|
|
$notificationService->sendToGroup($groupId, $subject, $message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-25 02:04:00 +03:00
|
|
|
// Handle tags using the new tag system
|
|
|
|
|
if (!empty($tags)) {
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
$tagModel->updateDomainTags($id, $tags, $userId);
|
|
|
|
|
} else {
|
|
|
|
|
// Remove all tags from domain
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
$tagModel->removeAllFromDomain($id);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$_SESSION['success'] = 'Domain updated successfully';
|
|
|
|
|
$this->redirect('/domains/' . $id);
|
|
|
|
|
}
|
|
|
|
|
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
/**
|
|
|
|
|
* Perform WHOIS lookup and persist results.
|
|
|
|
|
* @return string|null Status message on success, null on failure.
|
|
|
|
|
*/
|
|
|
|
|
private function performWhoisRefresh(int $id, array $domain): ?string
|
2025-10-08 14:23:07 +03:00
|
|
|
{
|
2025-10-21 15:16:56 +03:00
|
|
|
$logger = new \App\Services\Logger();
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$whoisData = $this->whoisService->getDomainInfo($domain['domain_name']);
|
|
|
|
|
|
|
|
|
|
if (!$whoisData) {
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$logger->error('WHOIS refresh failed', [
|
2025-10-21 15:16:56 +03:00
|
|
|
'domain_id' => $id,
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'user_id' => \Core\Auth::id()
|
|
|
|
|
]);
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
return null;
|
2025-10-08 14:23:07 +03:00
|
|
|
}
|
|
|
|
|
|
2025-10-21 16:13:58 +03:00
|
|
|
$expirationDate = $whoisData['expiration_date'] ?? $domain['expiration_date'];
|
2025-11-18 13:22:49 +02:00
|
|
|
$status = $this->whoisService->getDomainStatus($expirationDate, $whoisData['status'] ?? [], $whoisData);
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
$this->domainModel->update($id, [
|
|
|
|
|
'registrar' => $whoisData['registrar'],
|
|
|
|
|
'registrar_url' => $whoisData['registrar_url'] ?? null,
|
2025-10-21 16:13:58 +03:00
|
|
|
'expiration_date' => $expirationDate,
|
2025-10-08 14:23:07 +03:00
|
|
|
'updated_date' => $whoisData['updated_date'] ?? null,
|
|
|
|
|
'abuse_email' => $whoisData['abuse_email'] ?? null,
|
|
|
|
|
'last_checked' => date('Y-m-d H:i:s'),
|
|
|
|
|
'status' => $status,
|
|
|
|
|
'whois_data' => json_encode($whoisData)
|
|
|
|
|
]);
|
|
|
|
|
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$logger->info('WHOIS refresh completed', [
|
2025-10-21 15:16:56 +03:00
|
|
|
'domain_id' => $id,
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'new_status' => $status,
|
|
|
|
|
'registrar' => $whoisData['registrar'],
|
|
|
|
|
'expiration_date' => $whoisData['expiration_date'],
|
|
|
|
|
'user_id' => \Core\Auth::id()
|
|
|
|
|
]);
|
|
|
|
|
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
return 'WHOIS updated';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Perform DNS lookup and persist results.
|
|
|
|
|
* @return string Status message (always returns, even on zero records).
|
|
|
|
|
*/
|
|
|
|
|
private function performDnsRefresh(int $id, array $domain): string
|
|
|
|
|
{
|
|
|
|
|
$logger = new \App\Services\Logger('dns');
|
|
|
|
|
|
|
|
|
|
$dnsService = new \App\Services\DnsService();
|
|
|
|
|
$dnsModel = new \App\Models\DnsRecord();
|
|
|
|
|
|
|
|
|
|
$existingHosts = $dnsModel->getDistinctHosts($id);
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$records = $dnsService->refreshExisting($domain['domain_name'], $existingHosts);
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$totalRecords = array_sum(array_map('count', $records));
|
|
|
|
|
|
|
|
|
|
if ($totalRecords === 0) {
|
|
|
|
|
$logger->warning('DNS refresh returned no records', [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
]);
|
|
|
|
|
return 'DNS: no records found';
|
|
|
|
|
}
|
|
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$this->enrichIpDetails($records, $dnsService);
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
|
|
|
|
|
$stats = $dnsModel->saveSnapshot($id, $records);
|
|
|
|
|
$this->domainModel->update($id, ['dns_last_checked' => date('Y-m-d H:i:s')]);
|
|
|
|
|
|
|
|
|
|
$logger->info('DNS refresh completed', [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'total' => $totalRecords,
|
|
|
|
|
'added' => $stats['added'],
|
|
|
|
|
'updated' => $stats['updated'],
|
|
|
|
|
'removed' => $stats['removed'],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return "DNS updated ({$totalRecords} records)";
|
|
|
|
|
}
|
|
|
|
|
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
/**
|
|
|
|
|
* Fetch and persist the latest SSL certificate snapshot for a host.
|
|
|
|
|
*
|
|
|
|
|
* @return array{id:int,hostname:string,port:int,display_target:string,status:string,error:?string}
|
|
|
|
|
*/
|
|
|
|
|
private function performSslRefreshForHost(int $domainId, string $hostname, int $port = 443): array
|
|
|
|
|
{
|
|
|
|
|
$snapshot = $this->sslService->fetchCertificateSnapshot($hostname, $port);
|
|
|
|
|
$id = $this->sslCertificateModel->saveSnapshot($domainId, $hostname, $snapshot, $port);
|
|
|
|
|
$this->domainModel->update($domainId, ['ssl_last_checked' => $snapshot['last_checked']]);
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'id' => $id,
|
|
|
|
|
'hostname' => $hostname,
|
|
|
|
|
'port' => $port,
|
|
|
|
|
'display_target' => $this->sslService->formatTargetLabel($hostname, $port),
|
|
|
|
|
'status' => $snapshot['status'],
|
|
|
|
|
'error' => $snapshot['last_error'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the SSL endpoints that should be checked for a domain.
|
|
|
|
|
* Falls back to the root domain on 443 until a root target is explicitly tracked.
|
|
|
|
|
*
|
|
|
|
|
* @return array<int,array{hostname:string,port:int}>
|
|
|
|
|
*/
|
|
|
|
|
private function getSslMonitorTargets(int $domainId, string $rootDomain): array
|
|
|
|
|
{
|
|
|
|
|
$rootDomain = strtolower($rootDomain);
|
|
|
|
|
$targets = $this->sslCertificateModel->getDistinctTargets($domainId);
|
|
|
|
|
$hasTrackedRootTarget = false;
|
|
|
|
|
|
|
|
|
|
foreach ($targets as $target) {
|
|
|
|
|
if ($target['hostname'] === $rootDomain) {
|
|
|
|
|
$hasTrackedRootTarget = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$hasTrackedRootTarget) {
|
|
|
|
|
$targets[] = [
|
|
|
|
|
'hostname' => $rootDomain,
|
|
|
|
|
'port' => 443,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usort($targets, static function (array $a, array $b): int {
|
|
|
|
|
$hostnameCompare = strcasecmp($a['hostname'], $b['hostname']);
|
|
|
|
|
if ($hostnameCompare !== 0) {
|
|
|
|
|
return $hostnameCompare;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $a['port'] <=> $b['port'];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return $targets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Count tracked root-domain SSL endpoints for delete safeguards.
|
|
|
|
|
*/
|
|
|
|
|
private function countStoredRootSslTargets(int $domainId, string $rootDomain): int
|
|
|
|
|
{
|
|
|
|
|
$rootDomain = strtolower($rootDomain);
|
|
|
|
|
$targets = $this->sslCertificateModel->getDistinctTargets($domainId);
|
|
|
|
|
|
|
|
|
|
return count(array_filter($targets, static fn(array $target): bool => $target['hostname'] === $rootDomain));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determine whether the certificate row represents the default root SSL target.
|
|
|
|
|
*/
|
|
|
|
|
private function isDefaultRootSslTarget(array $certificate, string $rootDomain): bool
|
|
|
|
|
{
|
|
|
|
|
return strtolower($certificate['hostname']) === strtolower($rootDomain)
|
|
|
|
|
&& (int)($certificate['port'] ?? 443) === 443;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get formatted SSL certificates for rendering.
|
|
|
|
|
*/
|
|
|
|
|
private function getFormattedSslCertificates(int $domainId, string $rootDomain): array
|
|
|
|
|
{
|
|
|
|
|
$rawCertificates = $this->sslCertificateModel->getByDomain($domainId);
|
|
|
|
|
$rootDomain = strtolower($rootDomain);
|
|
|
|
|
$rootTargetCount = count(array_filter(
|
|
|
|
|
$rawCertificates,
|
|
|
|
|
static fn(array $certificate): bool => strtolower($certificate['hostname']) === $rootDomain
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
$certificates = array_map(
|
|
|
|
|
fn(array $certificate) => $this->formatSslCertificate($certificate, $rootDomain, $rootTargetCount),
|
|
|
|
|
$rawCertificates
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
usort($certificates, function (array $a, array $b): int {
|
|
|
|
|
if ($a['is_root'] !== $b['is_root']) {
|
|
|
|
|
return $a['is_root'] ? -1 : 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$hostnameCompare = strcasecmp($a['hostname'], $b['hostname']);
|
|
|
|
|
if ($hostnameCompare !== 0) {
|
|
|
|
|
return $hostnameCompare;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $a['port'] <=> $b['port'];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return $certificates;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Prepare a single SSL certificate row for the view.
|
|
|
|
|
*/
|
|
|
|
|
private function formatSslCertificate(array $certificate, string $rootDomain, int $rootTargetCount): array
|
|
|
|
|
{
|
|
|
|
|
$certificate['hostname'] = strtolower($certificate['hostname']);
|
|
|
|
|
$certificate['port'] = (int)($certificate['port'] ?? 443);
|
|
|
|
|
$certificate['is_root'] = $this->isDefaultRootSslTarget($certificate, $rootDomain);
|
|
|
|
|
$certificate['display_target'] = $this->sslService->formatTargetLabel($certificate['hostname'], $certificate['port']);
|
|
|
|
|
$certificate['can_delete'] = !$certificate['is_root'] || $rootTargetCount > 1;
|
|
|
|
|
$certificate['san_list'] = !empty($certificate['san_list'])
|
|
|
|
|
? (json_decode($certificate['san_list'], true) ?: [])
|
|
|
|
|
: [];
|
|
|
|
|
$certificate['raw_data'] = !empty($certificate['raw_data'])
|
|
|
|
|
? (json_decode($certificate['raw_data'], true) ?: [])
|
|
|
|
|
: [];
|
|
|
|
|
$certificate['issuer_organization'] = $this->extractCertificateDnValue(
|
|
|
|
|
is_array($certificate['raw_data']['issuer'] ?? null) ? $certificate['raw_data']['issuer'] : [],
|
|
|
|
|
'O'
|
|
|
|
|
);
|
|
|
|
|
$certificate['subject_organization'] = $this->extractCertificateDnValue(
|
|
|
|
|
is_array($certificate['raw_data']['subject'] ?? null) ? $certificate['raw_data']['subject'] : [],
|
|
|
|
|
'O'
|
|
|
|
|
);
|
|
|
|
|
$certificate['days_remaining'] = $certificate['days_remaining'] !== null
|
|
|
|
|
? (int)$certificate['days_remaining']
|
|
|
|
|
: null;
|
|
|
|
|
$certificate['is_trusted'] = !empty($certificate['is_trusted']);
|
|
|
|
|
$certificate['is_self_signed'] = !empty($certificate['is_self_signed']);
|
|
|
|
|
|
|
|
|
|
return array_merge($certificate, $this->getSslStatusMeta($certificate['status'] ?? 'invalid'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extract a human-readable distinguished name field from parsed certificate data.
|
|
|
|
|
*/
|
|
|
|
|
private function extractCertificateDnValue(array $parts, string $field): ?string
|
|
|
|
|
{
|
|
|
|
|
if (!array_key_exists($field, $parts)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$value = $parts[$field];
|
|
|
|
|
if (is_array($value)) {
|
|
|
|
|
$values = array_values(array_filter(array_map(static function ($item): ?string {
|
|
|
|
|
if (!is_scalar($item)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$item = trim((string)$item);
|
|
|
|
|
return $item !== '' ? $item : null;
|
|
|
|
|
}, $value)));
|
|
|
|
|
|
|
|
|
|
return !empty($values) ? implode(', ', $values) : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_scalar($value)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$value = trim((string)$value);
|
|
|
|
|
return $value !== '' ? $value : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get CSS classes and labels for an SSL status.
|
|
|
|
|
*/
|
|
|
|
|
private function getSslStatusMeta(string $status): array
|
|
|
|
|
{
|
|
|
|
|
return match ($status) {
|
|
|
|
|
'valid' => [
|
|
|
|
|
'status_label' => 'Valid & Trusted',
|
|
|
|
|
'status_icon' => 'fa-check-circle',
|
|
|
|
|
'status_badge_class' => 'bg-green-100 dark:bg-green-500/10 text-green-800 dark:text-green-400 border-green-200 dark:border-green-800',
|
|
|
|
|
'card_border_class' => 'border-green-200 dark:border-green-800',
|
|
|
|
|
'header_class' => 'bg-green-50 dark:bg-green-500/10 border-green-200 dark:border-green-800',
|
|
|
|
|
'accent_class' => 'text-green-600 dark:text-green-400',
|
|
|
|
|
],
|
|
|
|
|
'expiring' => [
|
|
|
|
|
'status_label' => 'Expiring Soon',
|
|
|
|
|
'status_icon' => 'fa-exclamation-triangle',
|
|
|
|
|
'status_badge_class' => 'bg-amber-100 dark:bg-amber-500/10 text-amber-800 dark:text-amber-400 border-amber-200 dark:border-amber-800',
|
|
|
|
|
'card_border_class' => 'border-amber-200 dark:border-amber-800',
|
|
|
|
|
'header_class' => 'bg-amber-50 dark:bg-amber-500/10 border-amber-200 dark:border-amber-800',
|
|
|
|
|
'accent_class' => 'text-amber-600 dark:text-amber-400',
|
|
|
|
|
],
|
|
|
|
|
'expired' => [
|
|
|
|
|
'status_label' => 'Expired',
|
|
|
|
|
'status_icon' => 'fa-times-circle',
|
|
|
|
|
'status_badge_class' => 'bg-red-100 dark:bg-red-500/10 text-red-800 dark:text-red-400 border-red-200 dark:border-red-800',
|
|
|
|
|
'card_border_class' => 'border-red-200 dark:border-red-800',
|
|
|
|
|
'header_class' => 'bg-red-50 dark:bg-red-500/10 border-red-200 dark:border-red-800',
|
|
|
|
|
'accent_class' => 'text-red-600 dark:text-red-400',
|
|
|
|
|
],
|
|
|
|
|
default => [
|
|
|
|
|
'status_label' => 'Invalid / Untrusted',
|
|
|
|
|
'status_icon' => 'fa-ban',
|
|
|
|
|
'status_badge_class' => 'bg-red-100 dark:bg-red-500/10 text-red-800 dark:text-red-400 border-red-200 dark:border-red-800',
|
|
|
|
|
'card_border_class' => 'border-red-200 dark:border-red-800',
|
|
|
|
|
'header_class' => 'bg-red-50 dark:bg-red-500/10 border-red-200 dark:border-red-800',
|
|
|
|
|
'accent_class' => 'text-red-600 dark:text-red-400',
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build SSL summary counts for the tab.
|
|
|
|
|
*/
|
|
|
|
|
private function buildSslStats(array $certificates): array
|
|
|
|
|
{
|
|
|
|
|
$stats = [
|
|
|
|
|
'total' => count($certificates),
|
|
|
|
|
'valid' => 0,
|
|
|
|
|
'expiring' => 0,
|
|
|
|
|
'expired' => 0,
|
|
|
|
|
'invalid' => 0,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
foreach ($certificates as $certificate) {
|
|
|
|
|
$status = $certificate['status'] ?? 'invalid';
|
|
|
|
|
if (isset($stats[$status])) {
|
|
|
|
|
$stats[$status]++;
|
|
|
|
|
} else {
|
|
|
|
|
$stats['invalid']++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$stats['issues'] = $stats['expired'] + $stats['invalid'];
|
|
|
|
|
return $stats;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Ensure SSL monitoring is enabled before allowing SSL checks.
|
|
|
|
|
*/
|
|
|
|
|
private function ensureSslMonitoringEnabled(array $domain, int $id): bool
|
|
|
|
|
{
|
|
|
|
|
if (!empty($domain['ssl_monitoring_enabled'])) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$_SESSION['warning'] = 'SSL monitoring is disabled for this domain';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parse certificate ids from a comma-separated POST value.
|
|
|
|
|
*/
|
|
|
|
|
private function parseSslCertificateIds(?string $rawIds): array
|
|
|
|
|
{
|
|
|
|
|
if ($rawIds === null || trim($rawIds) === '') {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ids = array_map('intval', explode(',', $rawIds));
|
|
|
|
|
$ids = array_filter($ids, static fn(int $id): bool => $id > 0);
|
|
|
|
|
return array_values(array_unique($ids));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build a safe internal return path for the current domain page.
|
|
|
|
|
*/
|
|
|
|
|
private function getSafeDomainReturnPath(int $id, string $fallbackHash = ''): string
|
|
|
|
|
{
|
|
|
|
|
$fallback = '/domains/' . $id . $fallbackHash;
|
|
|
|
|
$returnTo = trim((string)($_POST['return_to'] ?? ''));
|
|
|
|
|
|
|
|
|
|
if ($returnTo === '') {
|
|
|
|
|
return $fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$parts = parse_url($returnTo);
|
|
|
|
|
if ($parts === false) {
|
|
|
|
|
return $fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$path = $parts['path'] ?? '';
|
|
|
|
|
if ($path !== '/domains/' . $id) {
|
|
|
|
|
return $fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$fragment = '';
|
|
|
|
|
if (!empty($parts['fragment']) && preg_match('/^[a-z0-9_-]+$/i', $parts['fragment'])) {
|
|
|
|
|
$fragment = '#' . $parts['fragment'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $path . $fragment;
|
|
|
|
|
}
|
|
|
|
|
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
/**
|
|
|
|
|
* Redirect back to the originating page (domain view or list).
|
|
|
|
|
*/
|
|
|
|
|
private function redirectBackToDomain(int $id, string $hash = ''): void
|
|
|
|
|
{
|
2025-10-08 14:23:07 +03:00
|
|
|
$referer = $_SERVER['HTTP_REFERER'] ?? '';
|
|
|
|
|
if (strpos($referer, '/domains/' . $id) !== false) {
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
$this->redirect($this->getSafeDomainReturnPath($id, $hash));
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
} else {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function refreshWhois($params = [])
|
|
|
|
|
{
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$result = $this->performWhoisRefresh($id, $domain);
|
|
|
|
|
|
|
|
|
|
if ($result === null) {
|
|
|
|
|
$_SESSION['error'] = 'Could not retrieve WHOIS information';
|
2025-10-08 14:23:07 +03:00
|
|
|
} else {
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$_SESSION['success'] = 'WHOIS information refreshed';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirectBackToDomain($id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function refreshAll($params = [])
|
|
|
|
|
{
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
2025-10-08 14:23:07 +03:00
|
|
|
$this->redirect('/domains');
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$messages = [];
|
|
|
|
|
$messages[] = $this->performWhoisRefresh($id, $domain) ?? 'WHOIS failed';
|
|
|
|
|
if (!empty($domain['dns_monitoring_enabled'])) {
|
|
|
|
|
$messages[] = $this->performDnsRefresh($id, $domain);
|
|
|
|
|
} else {
|
|
|
|
|
$messages[] = 'DNS skipped (monitoring disabled)';
|
2025-10-08 14:23:07 +03:00
|
|
|
}
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
if (!empty($domain['ssl_monitoring_enabled'])) {
|
|
|
|
|
$targets = $this->getSslMonitorTargets($id, $domain['domain_name']);
|
|
|
|
|
$refreshed = 0;
|
|
|
|
|
foreach ($targets as $target) {
|
|
|
|
|
$this->performSslRefreshForHost($id, $target['hostname'], $target['port']);
|
|
|
|
|
$refreshed++;
|
|
|
|
|
}
|
|
|
|
|
$messages[] = 'SSL updated (' . $refreshed . ' endpoint' . ($refreshed === 1 ? '' : 's') . ')';
|
|
|
|
|
} else {
|
|
|
|
|
$messages[] = 'SSL skipped (monitoring disabled)';
|
|
|
|
|
}
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
|
|
|
|
|
$_SESSION['success'] = 'Domain refreshed: ' . implode(', ', $messages);
|
|
|
|
|
$this->redirectBackToDomain($id);
|
2025-10-08 14:23:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function delete($params = [])
|
|
|
|
|
{
|
|
|
|
|
$id = $params['id'] ?? 0;
|
2025-10-20 21:08:09 +03:00
|
|
|
$domain = $this->checkDomainAccess($id);
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->domainModel->delete($id);
|
|
|
|
|
$_SESSION['success'] = 'Domain deleted successfully';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function show($params = [])
|
|
|
|
|
{
|
|
|
|
|
$id = $params['id'] ?? 0;
|
2025-10-20 21:08:09 +03:00
|
|
|
|
|
|
|
|
// Get current user and isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
2025-10-25 02:04:00 +03:00
|
|
|
// Get domain with tags and groups
|
2025-10-20 21:08:09 +03:00
|
|
|
if ($isolationMode === 'isolated') {
|
2025-10-25 02:04:00 +03:00
|
|
|
$domain = $this->domainModel->getWithTagsAndGroups($id, $userId);
|
2025-10-20 21:08:09 +03:00
|
|
|
} else {
|
2025-10-25 02:04:00 +03:00
|
|
|
$domain = $this->domainModel->getWithTagsAndGroups($id);
|
2025-10-20 21:08:09 +03:00
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
if (!$domain) {
|
2026-02-09 00:20:17 +02:00
|
|
|
$_SESSION['error'] = 'You do not have permission to view this domain.';
|
2025-10-08 14:23:07 +03:00
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$logModel = new \App\Models\NotificationLog();
|
|
|
|
|
$logs = $logModel->getByDomain($id, 20);
|
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
|
|
|
|
|
|
|
|
// Format domain for display
|
|
|
|
|
$formattedDomain = \App\Helpers\DomainHelper::formatForDisplay($domain);
|
|
|
|
|
|
|
|
|
|
// Parse WHOIS data for display
|
|
|
|
|
$whoisData = json_decode($domain['whois_data'] ?? '{}', true);
|
|
|
|
|
if (!empty($whoisData['status']) && is_array($whoisData['status'])) {
|
|
|
|
|
$formattedDomain['parsedStatuses'] = \App\Helpers\DomainHelper::parseWhoisStatuses($whoisData['status']);
|
|
|
|
|
} else {
|
|
|
|
|
$formattedDomain['parsedStatuses'] = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate active channel count
|
|
|
|
|
if (!empty($domain['channels'])) {
|
|
|
|
|
$formattedDomain['activeChannelCount'] = \App\Helpers\DomainHelper::getActiveChannelCount($domain['channels']);
|
|
|
|
|
}
|
2025-10-25 02:04:00 +03:00
|
|
|
|
|
|
|
|
// Get available tags for the new tag system
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage($userId);
|
|
|
|
|
} else {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage();
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
// Get DNS records for the DNS tab
|
|
|
|
|
$dnsModel = new \App\Models\DnsRecord();
|
|
|
|
|
$dnsRecords = $dnsModel->getByDomainGrouped($id);
|
|
|
|
|
$dnsRecordCount = $dnsModel->countByDomain($id);
|
|
|
|
|
$dnsHasCloudflare = $dnsModel->hasCloudflare($id);
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
$sslCertificates = $this->getFormattedSslCertificates($id, $domain['domain_name']);
|
|
|
|
|
$sslStats = $this->buildSslStats($sslCertificates);
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
|
|
|
|
|
// Extract cached IP details (PTR, ASN, geo) from stored raw_data
|
|
|
|
|
$dnsIpDetails = [];
|
|
|
|
|
foreach (['A', 'AAAA'] as $type) {
|
|
|
|
|
if (!empty($dnsRecords[$type])) {
|
|
|
|
|
foreach ($dnsRecords[$type] as $r) {
|
|
|
|
|
if (!empty($r['raw_data']) && !empty($r['value'])) {
|
|
|
|
|
$raw = json_decode($r['raw_data'], true);
|
|
|
|
|
if (!empty($raw['_ip_info'])) {
|
|
|
|
|
$dnsIpDetails[$r['value']] = $raw['_ip_info'];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$viewTemplate = $settingModel->getValue('domain_view_template', 'detailed');
|
|
|
|
|
$templateName = $viewTemplate === 'detailed' ? 'domains/view-detailed' : 'domains/view';
|
|
|
|
|
|
|
|
|
|
$this->view($templateName, [
|
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
|
|
|
'domain' => $formattedDomain,
|
2026-03-04 11:16:56 +02:00
|
|
|
'whoisData' => $whoisData,
|
2025-10-08 14:23:07 +03:00
|
|
|
'logs' => $logs,
|
2025-10-25 02:04:00 +03:00
|
|
|
'availableTags' => $availableTags,
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
'dnsRecords' => $dnsRecords,
|
|
|
|
|
'dnsRecordCount' => $dnsRecordCount,
|
|
|
|
|
'dnsHasCloudflare' => $dnsHasCloudflare,
|
|
|
|
|
'dnsIpDetails' => $dnsIpDetails,
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
'sslCertificates' => $sslCertificates,
|
|
|
|
|
'sslStats' => $sslStats,
|
2025-10-08 14:23:07 +03:00
|
|
|
'title' => $domain['domain_name']
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function bulkAdd()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
2025-10-20 18:50:32 +03:00
|
|
|
// Get groups based on isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$groups = $this->groupModel->getAllWithChannelCount($userId);
|
|
|
|
|
} else {
|
|
|
|
|
$groups = $this->groupModel->getAllWithChannelCount();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-25 02:04:00 +03:00
|
|
|
// Get available tags for the new tag system
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage($userId);
|
|
|
|
|
} else {
|
|
|
|
|
$availableTags = $tagModel->getAllWithUsage();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$this->view('domains/bulk-add', [
|
|
|
|
|
'groups' => $groups,
|
2025-10-25 02:04:00 +03:00
|
|
|
'availableTags' => $availableTags,
|
2025-10-08 14:23:07 +03:00
|
|
|
'title' => 'Bulk Add Domains'
|
|
|
|
|
]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains/bulk-add');
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
// POST - Process bulk add
|
|
|
|
|
$domainsText = trim($_POST['domains'] ?? '');
|
|
|
|
|
$groupId = !empty($_POST['notification_group_id']) ? (int)$_POST['notification_group_id'] : null;
|
2025-10-12 12:46:16 +03:00
|
|
|
$tagsInput = trim($_POST['tags'] ?? '');
|
2025-10-20 19:05:33 +03:00
|
|
|
$userId = \Core\Auth::id();
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
if (empty($domainsText)) {
|
|
|
|
|
$_SESSION['error'] = 'Please enter at least one domain';
|
|
|
|
|
$this->redirect('/domains/bulk-add');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-12 12:46:16 +03:00
|
|
|
// Validate tags
|
|
|
|
|
$tagValidation = \App\Helpers\InputValidator::validateTags($tagsInput);
|
|
|
|
|
if (!$tagValidation['valid']) {
|
|
|
|
|
$_SESSION['error'] = $tagValidation['error'];
|
|
|
|
|
$this->redirect('/domains/bulk-add');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$tags = $tagValidation['tags'];
|
|
|
|
|
|
2025-10-20 19:02:56 +03:00
|
|
|
// Validate notification group in isolation mode
|
|
|
|
|
if ($groupId) {
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$group = $this->groupModel->find($groupId);
|
|
|
|
|
if (!$group || $group['user_id'] != $userId) {
|
|
|
|
|
$_SESSION['error'] = 'You can only assign domains to your own notification groups';
|
|
|
|
|
$this->redirect('/domains/bulk-add');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
// Split by new lines, sanitize each, and filter empties
|
|
|
|
|
$rawLines = array_filter(array_map('trim', explode("\n", $domainsText)));
|
|
|
|
|
$domainNames = [];
|
|
|
|
|
$invalidDomains = [];
|
|
|
|
|
foreach ($rawLines as $line) {
|
|
|
|
|
$cleaned = \App\Helpers\InputValidator::sanitizeDomainInput($line);
|
|
|
|
|
if (empty($cleaned)) continue;
|
|
|
|
|
$check = \App\Helpers\InputValidator::validateRootDomain($cleaned);
|
|
|
|
|
if (!$check['valid']) {
|
|
|
|
|
$invalidDomains[] = $cleaned;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$domainNames[] = $check['domain'];
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
$added = 0;
|
|
|
|
|
$skipped = 0;
|
|
|
|
|
$availableCount = 0;
|
|
|
|
|
$errors = [];
|
2025-10-20 19:02:56 +03:00
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
|
|
|
|
|
// Log bulk add start
|
|
|
|
|
$logger = new \App\Services\Logger();
|
|
|
|
|
$logger->info('Bulk domain add started', [
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
'domain_count' => count($domainNames),
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
'invalid_count' => count($invalidDomains),
|
2025-10-20 19:02:56 +03:00
|
|
|
'notification_group_id' => $groupId,
|
|
|
|
|
'tags' => $tags
|
|
|
|
|
]);
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
foreach ($domainNames as $domainName) {
|
|
|
|
|
// Skip if already exists
|
|
|
|
|
if ($this->domainModel->existsByDomain($domainName)) {
|
|
|
|
|
$skipped++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get WHOIS information
|
|
|
|
|
$whoisData = $this->whoisService->getDomainInfo($domainName);
|
|
|
|
|
|
|
|
|
|
if (!$whoisData) {
|
|
|
|
|
$errors[] = $domainName;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-18 13:22:49 +02:00
|
|
|
$status = $this->whoisService->getDomainStatus($whoisData['expiration_date'], $whoisData['status'] ?? [], $whoisData);
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
// Track available domains
|
|
|
|
|
if ($status === 'available') {
|
|
|
|
|
$availableCount++;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-08 22:58:59 +02:00
|
|
|
try {
|
|
|
|
|
$domainId = $this->domainModel->create([
|
|
|
|
|
'domain_name' => $domainName,
|
|
|
|
|
'notification_group_id' => $groupId,
|
|
|
|
|
'registrar' => $whoisData['registrar'],
|
|
|
|
|
'registrar_url' => $whoisData['registrar_url'] ?? null,
|
|
|
|
|
'expiration_date' => $whoisData['expiration_date'],
|
|
|
|
|
'updated_date' => $whoisData['updated_date'] ?? null,
|
|
|
|
|
'abuse_email' => $whoisData['abuse_email'] ?? null,
|
|
|
|
|
'last_checked' => date('Y-m-d H:i:s'),
|
|
|
|
|
'status' => $status,
|
|
|
|
|
'whois_data' => json_encode($whoisData),
|
|
|
|
|
'is_active' => 1,
|
|
|
|
|
'user_id' => \Core\Auth::id()
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Handle tags using the new tag system
|
|
|
|
|
if (!empty($tags) && $domainId) {
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
$tagModel->updateDomainTags($domainId, $tags, $userId);
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
|
2026-02-08 22:58:59 +02:00
|
|
|
$added++;
|
|
|
|
|
} catch (\PDOException $e) {
|
|
|
|
|
// Handle duplicate key (race condition between existsByDomain check and insert)
|
|
|
|
|
if (str_contains($e->getMessage(), 'Duplicate entry')) {
|
|
|
|
|
$skipped++;
|
|
|
|
|
} else {
|
|
|
|
|
$logger->error('Failed to add domain in bulk', [
|
|
|
|
|
'domain' => $domainName,
|
|
|
|
|
'error' => $e->getMessage()
|
|
|
|
|
]);
|
|
|
|
|
$errors[] = $domainName;
|
|
|
|
|
}
|
2025-10-25 12:40:47 +03:00
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
}
|
|
|
|
|
|
2025-10-20 19:02:56 +03:00
|
|
|
// Log bulk add completion
|
|
|
|
|
$logger->info('Bulk domain add completed', [
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
'added' => $added,
|
|
|
|
|
'skipped' => $skipped,
|
|
|
|
|
'errors' => count($errors),
|
|
|
|
|
'available_count' => $availableCount
|
|
|
|
|
]);
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$message = "Added $added domain(s)";
|
|
|
|
|
if ($skipped > 0) $message .= ", skipped $skipped duplicate(s)";
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
if (count($invalidDomains) > 0) $message .= ", " . count($invalidDomains) . " rejected (not root domains)";
|
2025-10-08 14:23:07 +03:00
|
|
|
if (count($errors) > 0) $message .= ", failed to add " . count($errors) . " domain(s)";
|
|
|
|
|
|
|
|
|
|
if ($availableCount > 0) {
|
|
|
|
|
$_SESSION['warning'] = "Note: $availableCount domain(s) appear to be AVAILABLE (not registered).";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = $message;
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function bulkRefresh()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$domainIds = $_POST['domain_ids'] ?? [];
|
|
|
|
|
|
|
|
|
|
if (empty($domainIds)) {
|
|
|
|
|
$_SESSION['error'] = 'No domains selected';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// Validate bulk operation size
|
|
|
|
|
$sizeError = \App\Helpers\InputValidator::validateArraySize($domainIds, 1000, 'Domain selection');
|
|
|
|
|
if ($sizeError) {
|
|
|
|
|
$_SESSION['error'] = $sizeError;
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 21:08:09 +03:00
|
|
|
// Get current user and isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
2025-10-21 15:16:56 +03:00
|
|
|
// Log bulk refresh start
|
|
|
|
|
$logger = new \App\Services\Logger();
|
|
|
|
|
$logger->info('Bulk domain refresh started', [
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
'domain_count' => count($domainIds),
|
|
|
|
|
'isolation_mode' => $isolationMode,
|
|
|
|
|
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
|
|
|
|
|
]);
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$refreshed = 0;
|
|
|
|
|
$failed = 0;
|
|
|
|
|
|
|
|
|
|
foreach ($domainIds as $id) {
|
2025-10-20 21:08:09 +03:00
|
|
|
// Check domain access based on isolation mode
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$domain = $this->domainModel->findWithIsolation($id, $userId);
|
|
|
|
|
} else {
|
|
|
|
|
$domain = $this->domainModel->find($id);
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
if (!$domain) continue;
|
|
|
|
|
|
|
|
|
|
$whoisData = $this->whoisService->getDomainInfo($domain['domain_name']);
|
|
|
|
|
|
|
|
|
|
if (!$whoisData) {
|
2025-10-21 15:16:56 +03:00
|
|
|
$logger->warning('Bulk refresh failed for domain - WHOIS data not retrieved', [
|
|
|
|
|
'domain_id' => $id,
|
|
|
|
|
'domain_name' => $domain['domain_name'] ?? 'unknown',
|
|
|
|
|
'user_id' => $userId
|
|
|
|
|
]);
|
2025-10-08 14:23:07 +03:00
|
|
|
$failed++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-21 16:13:58 +03:00
|
|
|
// Use WHOIS expiration date if available, otherwise preserve manual expiration date
|
|
|
|
|
$expirationDate = $whoisData['expiration_date'] ?? $domain['expiration_date'];
|
|
|
|
|
|
2025-11-18 13:22:49 +02:00
|
|
|
$status = $this->whoisService->getDomainStatus($expirationDate, $whoisData['status'] ?? [], $whoisData);
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
$this->domainModel->update($id, [
|
|
|
|
|
'registrar' => $whoisData['registrar'],
|
|
|
|
|
'registrar_url' => $whoisData['registrar_url'] ?? null,
|
2025-10-21 16:13:58 +03:00
|
|
|
'expiration_date' => $expirationDate,
|
2025-10-08 14:23:07 +03:00
|
|
|
'updated_date' => $whoisData['updated_date'] ?? null,
|
|
|
|
|
'abuse_email' => $whoisData['abuse_email'] ?? null,
|
|
|
|
|
'last_checked' => date('Y-m-d H:i:s'),
|
|
|
|
|
'status' => $status,
|
|
|
|
|
'whois_data' => json_encode($whoisData)
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$refreshed++;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-21 15:16:56 +03:00
|
|
|
// Log bulk refresh completion
|
|
|
|
|
$logger->info('Bulk domain refresh completed', [
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
'total_domains' => count($domainIds),
|
|
|
|
|
'refreshed' => $refreshed,
|
|
|
|
|
'failed' => $failed,
|
|
|
|
|
'success_rate' => count($domainIds) > 0 ? round(($refreshed / count($domainIds)) * 100, 2) . '%' : '0%'
|
|
|
|
|
]);
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$_SESSION['success'] = "Refreshed $refreshed domain(s)" . ($failed > 0 ? ", $failed failed" : '');
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function bulkDelete()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$domainIds = $_POST['domain_ids'] ?? [];
|
|
|
|
|
|
|
|
|
|
if (empty($domainIds)) {
|
|
|
|
|
$_SESSION['error'] = 'No domains selected';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// Validate bulk operation size
|
|
|
|
|
$sizeError = \App\Helpers\InputValidator::validateArraySize($domainIds, 1000, 'Domain selection');
|
|
|
|
|
if ($sizeError) {
|
|
|
|
|
$_SESSION['error'] = $sizeError;
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$deleted = 0;
|
|
|
|
|
foreach ($domainIds as $id) {
|
|
|
|
|
if ($this->domainModel->delete($id)) {
|
|
|
|
|
$deleted++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = "Deleted $deleted domain(s)";
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function bulkAssignGroup()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$domainIds = $_POST['domain_ids'] ?? [];
|
|
|
|
|
$groupId = !empty($_POST['group_id']) ? (int)$_POST['group_id'] : null;
|
2025-10-20 19:05:33 +03:00
|
|
|
$userId = \Core\Auth::id();
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
if (empty($domainIds)) {
|
|
|
|
|
$_SESSION['error'] = 'No domains selected';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// Validate bulk operation size
|
|
|
|
|
$sizeError = \App\Helpers\InputValidator::validateArraySize($domainIds, 1000, 'Domain selection');
|
|
|
|
|
if ($sizeError) {
|
|
|
|
|
$_SESSION['error'] = $sizeError;
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 19:02:56 +03:00
|
|
|
// Validate notification group in isolation mode
|
|
|
|
|
if ($groupId) {
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$group = $this->groupModel->find($groupId);
|
|
|
|
|
if (!$group || $group['user_id'] != $userId) {
|
|
|
|
|
$_SESSION['error'] = 'You can only assign domains to your own notification groups';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$updated = 0;
|
|
|
|
|
foreach ($domainIds as $id) {
|
|
|
|
|
if ($this->domainModel->update($id, ['notification_group_id' => $groupId])) {
|
|
|
|
|
$updated++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = "Updated $updated domain(s)";
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function bulkToggleStatus()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$domainIds = $_POST['domain_ids'] ?? [];
|
|
|
|
|
$isActive = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 1;
|
|
|
|
|
|
|
|
|
|
if (empty($domainIds)) {
|
|
|
|
|
$_SESSION['error'] = 'No domains selected';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// Validate bulk operation size
|
|
|
|
|
$sizeError = \App\Helpers\InputValidator::validateArraySize($domainIds, 1000, 'Domain selection');
|
|
|
|
|
if ($sizeError) {
|
|
|
|
|
$_SESSION['error'] = $sizeError;
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 21:08:09 +03:00
|
|
|
// Get current user and isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
2025-10-08 14:23:07 +03:00
|
|
|
$updated = 0;
|
|
|
|
|
foreach ($domainIds as $id) {
|
2025-10-20 21:08:09 +03:00
|
|
|
// Check domain access based on isolation mode
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$domain = $this->domainModel->findWithIsolation($id, $userId);
|
|
|
|
|
} else {
|
|
|
|
|
$domain = $this->domainModel->find($id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($domain && $this->domainModel->update($id, ['is_active' => $isActive])) {
|
2025-10-08 14:23:07 +03:00
|
|
|
$updated++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$status = $isActive ? 'enabled' : 'disabled';
|
|
|
|
|
$_SESSION['success'] = "Monitoring $status for $updated domain(s)";
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
2025-10-08 20:56:25 +03:00
|
|
|
|
|
|
|
|
public function updateNotes($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
2025-10-08 20:56:25 +03:00
|
|
|
$id = (int)($params['id'] ?? 0);
|
2025-10-20 21:08:09 +03:00
|
|
|
$domain = $this->checkDomainAccess($id);
|
2025-10-08 20:56:25 +03:00
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$notes = $_POST['notes'] ?? '';
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
// Validate notes length
|
|
|
|
|
$lengthError = \App\Helpers\InputValidator::validateLength($notes, 5000, 'Notes');
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$viewTemplate = $settingModel->getValue('domain_view_template', 'detailed');
|
|
|
|
|
$redirect = '/domains/' . $id . ($viewTemplate === 'detailed' ? '#overview' : '');
|
|
|
|
|
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
if ($lengthError) {
|
|
|
|
|
$_SESSION['error'] = $lengthError;
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$this->redirect($redirect);
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-08 20:56:25 +03:00
|
|
|
$this->domainModel->update($id, [
|
|
|
|
|
'notes' => $notes
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = 'Notes updated successfully';
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$this->redirect($redirect);
|
2025-10-08 20:56:25 +03:00
|
|
|
}
|
2025-10-12 12:46:16 +03:00
|
|
|
|
|
|
|
|
public function bulkAddTags()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$domainIds = $_POST['domain_ids'] ?? [];
|
|
|
|
|
$tagToAdd = trim($_POST['tag'] ?? '');
|
|
|
|
|
|
|
|
|
|
if (empty($domainIds) || empty($tagToAdd)) {
|
|
|
|
|
$_SESSION['error'] = 'Invalid request';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate tag format
|
|
|
|
|
if (!preg_match('/^[a-z0-9-]+$/', $tagToAdd)) {
|
|
|
|
|
$_SESSION['error'] = 'Invalid tag format (use only letters, numbers, and hyphens)';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 21:08:09 +03:00
|
|
|
// Get current user and isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
2025-10-25 12:40:47 +03:00
|
|
|
// Initialize Tag model
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
|
|
|
|
|
// Find or create the tag
|
|
|
|
|
$tag = $tagModel->findByName($tagToAdd, $userId);
|
|
|
|
|
if (!$tag) {
|
|
|
|
|
// Create new tag
|
|
|
|
|
$tagId = $tagModel->create([
|
|
|
|
|
'name' => $tagToAdd,
|
|
|
|
|
'color' => 'bg-gray-100 text-gray-700 border-gray-300',
|
|
|
|
|
'description' => '',
|
|
|
|
|
'user_id' => $userId
|
|
|
|
|
]);
|
|
|
|
|
if (!$tagId) {
|
|
|
|
|
$_SESSION['error'] = 'Failed to create tag';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$tagId = $tag['id'];
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-12 12:46:16 +03:00
|
|
|
$updated = 0;
|
|
|
|
|
foreach ($domainIds as $id) {
|
2025-10-20 21:08:09 +03:00
|
|
|
// Check domain access based on isolation mode
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$domain = $this->domainModel->findWithIsolation($id, $userId);
|
|
|
|
|
} else {
|
|
|
|
|
$domain = $this->domainModel->find($id);
|
|
|
|
|
}
|
2025-10-12 12:46:16 +03:00
|
|
|
if (!$domain) continue;
|
|
|
|
|
|
2025-10-25 12:40:47 +03:00
|
|
|
// Add tag to domain using Tag model
|
|
|
|
|
if ($tagModel->addToDomain($id, $tagId)) {
|
|
|
|
|
$updated++;
|
2025-10-12 12:46:16 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = "Tag '$tagToAdd' added to $updated domain(s)";
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function bulkRemoveTags()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$domainIds = $_POST['domain_ids'] ?? [];
|
|
|
|
|
|
|
|
|
|
if (empty($domainIds)) {
|
|
|
|
|
$_SESSION['error'] = 'No domains selected';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 21:08:09 +03:00
|
|
|
// Get current user and isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
2025-10-25 02:04:00 +03:00
|
|
|
$tagModel = new \App\Models\Tag();
|
2025-10-12 12:46:16 +03:00
|
|
|
$updated = 0;
|
|
|
|
|
foreach ($domainIds as $id) {
|
2025-10-20 21:08:09 +03:00
|
|
|
// Check domain access based on isolation mode
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$domain = $this->domainModel->findWithIsolation($id, $userId);
|
|
|
|
|
} else {
|
|
|
|
|
$domain = $this->domainModel->find($id);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-25 02:04:00 +03:00
|
|
|
if ($domain && $tagModel->removeAllFromDomain($id)) {
|
2025-10-12 12:46:16 +03:00
|
|
|
$updated++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = "Tags removed from $updated domain(s)";
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
2025-10-20 17:04:13 +03:00
|
|
|
|
2025-10-25 02:04:00 +03:00
|
|
|
/**
|
|
|
|
|
* Bulk remove specific tag from domains
|
|
|
|
|
*/
|
|
|
|
|
public function bulkRemoveSpecificTag()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$domainIds = $_POST['domain_ids'] ?? [];
|
|
|
|
|
$tagId = (int)($_POST['tag_id'] ?? 0);
|
|
|
|
|
|
|
|
|
|
if (empty($domainIds) || !$tagId) {
|
|
|
|
|
$_SESSION['error'] = 'Invalid request';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
$tag = $tagModel->find($tagId);
|
|
|
|
|
if (!$tag) {
|
|
|
|
|
$_SESSION['error'] = 'Tag not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get current user and isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
$removed = 0;
|
|
|
|
|
foreach ($domainIds as $domainId) {
|
|
|
|
|
// Check domain access based on isolation mode
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$domain = $this->domainModel->findWithIsolation($domainId, $userId);
|
|
|
|
|
} else {
|
|
|
|
|
$domain = $this->domainModel->find($domainId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($domain && $tagModel->removeFromDomain($domainId, $tagId)) {
|
|
|
|
|
$removed++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = "Tag '{$tag['name']}' removed from $removed domain(s)";
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bulk assign existing tag to domains
|
|
|
|
|
*/
|
|
|
|
|
public function bulkAssignExistingTag()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CSRF Protection
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$domainIds = $_POST['domain_ids'] ?? [];
|
|
|
|
|
$tagId = (int)($_POST['tag_id'] ?? 0);
|
|
|
|
|
|
|
|
|
|
if (empty($domainIds) || !$tagId) {
|
|
|
|
|
$_SESSION['error'] = 'Invalid request';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
$tag = $tagModel->find($tagId);
|
|
|
|
|
if (!$tag) {
|
|
|
|
|
$_SESSION['error'] = 'Tag not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get current user and isolation mode
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
$added = 0;
|
|
|
|
|
foreach ($domainIds as $domainId) {
|
|
|
|
|
// Check domain access based on isolation mode
|
|
|
|
|
if ($isolationMode === 'isolated') {
|
|
|
|
|
$domain = $this->domainModel->findWithIsolation($domainId, $userId);
|
|
|
|
|
} else {
|
|
|
|
|
$domain = $this->domainModel->find($domainId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($domain && $tagModel->addToDomain($domainId, $tagId)) {
|
|
|
|
|
$added++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = "Tag '{$tag['name']}' added to $added domain(s)";
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-20 17:04:13 +03:00
|
|
|
/**
|
|
|
|
|
* Transfer domain to another user (Admin only)
|
|
|
|
|
*/
|
|
|
|
|
public function transfer()
|
|
|
|
|
{
|
|
|
|
|
\Core\Auth::requireAdmin();
|
|
|
|
|
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$domainId = (int)($_POST['domain_id'] ?? 0);
|
|
|
|
|
$targetUserId = (int)($_POST['target_user_id'] ?? 0);
|
|
|
|
|
|
|
|
|
|
if (!$domainId || !$targetUserId) {
|
|
|
|
|
$_SESSION['error'] = 'Invalid domain or user selected';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate domain exists
|
|
|
|
|
$domain = $this->domainModel->find($domainId);
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate target user exists
|
|
|
|
|
$userModel = new \App\Models\User();
|
|
|
|
|
$targetUser = $userModel->find($targetUserId);
|
|
|
|
|
if (!$targetUser) {
|
|
|
|
|
$_SESSION['error'] = 'Target user not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$logger = new \App\Services\Logger('transfer');
|
|
|
|
|
|
2025-10-20 17:04:13 +03:00
|
|
|
try {
|
|
|
|
|
$this->domainModel->update($domainId, ['user_id' => $targetUserId]);
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
|
|
|
|
|
$groupUnlinked = false;
|
|
|
|
|
if (!empty($domain['notification_group_id'])) {
|
|
|
|
|
$groupModel = new \App\Models\NotificationGroup();
|
|
|
|
|
$group = $groupModel->find($domain['notification_group_id']);
|
|
|
|
|
if ($group && $group['user_id'] != $targetUserId) {
|
|
|
|
|
$this->domainModel->update($domainId, ['notification_group_id' => null]);
|
|
|
|
|
$groupUnlinked = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
$tagsRemoved = $tagModel->removeOtherUserTagsFromDomain($domainId, $targetUserId);
|
|
|
|
|
|
|
|
|
|
$logger->info('Domain transferred', [
|
|
|
|
|
'domain_id' => $domainId,
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'from_user_id' => $domain['user_id'],
|
|
|
|
|
'to_user_id' => $targetUserId,
|
|
|
|
|
'to_username' => $targetUser['username'],
|
|
|
|
|
'group_unlinked' => $groupUnlinked,
|
|
|
|
|
'tags_removed' => $tagsRemoved,
|
|
|
|
|
'admin_user_id' => \Core\Auth::id(),
|
|
|
|
|
]);
|
2025-10-20 17:04:13 +03:00
|
|
|
|
|
|
|
|
$_SESSION['success'] = "Domain '{$domain['domain_name']}' transferred to {$targetUser['username']}";
|
|
|
|
|
} catch (\Exception $e) {
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$logger->error('Domain transfer failed', [
|
|
|
|
|
'domain_id' => $domainId,
|
|
|
|
|
'to_user_id' => $targetUserId,
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
]);
|
2025-10-20 17:04:13 +03:00
|
|
|
$_SESSION['error'] = 'Failed to transfer domain. Please try again.';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bulk transfer domains to another user (Admin only)
|
|
|
|
|
*/
|
|
|
|
|
public function bulkTransfer()
|
|
|
|
|
{
|
|
|
|
|
\Core\Auth::requireAdmin();
|
|
|
|
|
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$domainIds = $_POST['domain_ids'] ?? [];
|
|
|
|
|
$targetUserId = (int)($_POST['target_user_id'] ?? 0);
|
|
|
|
|
|
|
|
|
|
if (empty($domainIds) || !$targetUserId) {
|
|
|
|
|
$_SESSION['error'] = 'No domains selected or invalid user';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate target user exists
|
|
|
|
|
$userModel = new \App\Models\User();
|
|
|
|
|
$targetUser = $userModel->find($targetUserId);
|
|
|
|
|
if (!$targetUser) {
|
|
|
|
|
$_SESSION['error'] = 'Target user not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$groupModel = new \App\Models\NotificationGroup();
|
|
|
|
|
$tagModel = new \App\Models\Tag();
|
|
|
|
|
$logger = new \App\Services\Logger('transfer');
|
|
|
|
|
|
2025-10-20 17:04:13 +03:00
|
|
|
$transferred = 0;
|
|
|
|
|
foreach ($domainIds as $domainId) {
|
|
|
|
|
$domainId = (int)$domainId;
|
|
|
|
|
if ($domainId > 0) {
|
|
|
|
|
try {
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$domain = $this->domainModel->find($domainId);
|
2025-10-20 17:04:13 +03:00
|
|
|
$this->domainModel->update($domainId, ['user_id' => $targetUserId]);
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
|
|
|
|
|
$groupUnlinked = false;
|
|
|
|
|
if ($domain && !empty($domain['notification_group_id'])) {
|
|
|
|
|
$group = $groupModel->find($domain['notification_group_id']);
|
|
|
|
|
if ($group && $group['user_id'] != $targetUserId) {
|
|
|
|
|
$this->domainModel->update($domainId, ['notification_group_id' => null]);
|
|
|
|
|
$groupUnlinked = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tagsRemoved = $tagModel->removeOtherUserTagsFromDomain($domainId, $targetUserId);
|
|
|
|
|
|
|
|
|
|
$logger->info('Domain transferred (bulk)', [
|
|
|
|
|
'domain_id' => $domainId,
|
|
|
|
|
'domain_name' => $domain['domain_name'] ?? 'unknown',
|
|
|
|
|
'from_user_id' => $domain['user_id'] ?? null,
|
|
|
|
|
'to_user_id' => $targetUserId,
|
|
|
|
|
'to_username' => $targetUser['username'],
|
|
|
|
|
'group_unlinked' => $groupUnlinked,
|
|
|
|
|
'tags_removed' => $tagsRemoved,
|
|
|
|
|
'admin_user_id' => \Core\Auth::id(),
|
|
|
|
|
]);
|
|
|
|
|
|
2025-10-20 17:04:13 +03:00
|
|
|
$transferred++;
|
|
|
|
|
} catch (\Exception $e) {
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$logger->error('Domain transfer failed (bulk)', [
|
|
|
|
|
'domain_id' => $domainId,
|
|
|
|
|
'to_user_id' => $targetUserId,
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
]);
|
2025-10-20 17:04:13 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
$logger->info('Bulk domain transfer completed', [
|
|
|
|
|
'transferred' => $transferred,
|
|
|
|
|
'total_requested' => count($domainIds),
|
|
|
|
|
'to_user_id' => $targetUserId,
|
|
|
|
|
'to_username' => $targetUser['username'],
|
|
|
|
|
'admin_user_id' => \Core\Auth::id(),
|
|
|
|
|
]);
|
|
|
|
|
|
2025-10-20 17:04:13 +03:00
|
|
|
$_SESSION['success'] = "$transferred domain(s) transferred to {$targetUser['username']}";
|
|
|
|
|
$this->redirect('/domains');
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ========================================
|
|
|
|
|
// DNS MONITORING
|
|
|
|
|
// ========================================
|
|
|
|
|
|
|
|
|
|
public function refreshDns($params = [])
|
|
|
|
|
{
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
Add DNS monitoring and refresh functionality
Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
2026-03-08 14:32:05 +02:00
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$result = $this->performDnsRefresh($id, $domain);
|
|
|
|
|
|
|
|
|
|
if (strpos($result, 'no records') !== false) {
|
|
|
|
|
$_SESSION['warning'] = 'No DNS records found for this domain';
|
|
|
|
|
} else {
|
|
|
|
|
$_SESSION['success'] = $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirectBackToDomain($id, '#dns');
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
}
|
|
|
|
|
|
Enhance DNS discovery, validation & transfers
Add comprehensive DNS management and input validation, plus safer transfer and logging behavior.
- Add CronHelper utilities for cron scripts and unify logging/formatting.
- Improve InputValidator: sanitizeDomainInput and validateRootDomain (handles multi-level TLDs) and use throughout domain import/create flows to reject subdomains.
- DomainController: refactor DNS refresh to support quick/deep discovery (background deep scans), add endpoints to discover, add/delete/bulk-delete DNS records, import BIND zone files, enrich IP metadata via enrichIpDetails, and strengthen bulk import/reporting messages.
- DnsRecord model: add source column handling (discovered/manual/imported), avoid auto-deleting manual/imported records, and add helpers for deleting, bulk deleting, manual adding and importing zone records.
- Tag, NotificationGroup and Domain transfer logic: unlink groups when ownership changes, remove tags that belong to other users, add audit logging via Logger and improved bulk transfer reporting. TagController/View: show transferable users for admins and skip global tags on transfer.
- Notification channels (Discord, Mattermost, etc.) and EmailHelper: allow explicit subjects and improve payload fields based on notification type.
- Add new migration 029_add_dns_record_source.sql and wire it into the installer; update migrations detection.
- Add new views/partials for confirm/import/transfer modals, update various domain/group/tag templates, and update cron scripts and routes for discovery.
These changes preserve manual/imported DNS records, improve root-domain validation, enable background deep discovery, and add better logging/audit trails for transfers and imports.
2026-03-10 22:54:28 +02:00
|
|
|
/**
|
|
|
|
|
* Discover DNS records via Quick Scan (synchronous) or Deep Scan (background).
|
|
|
|
|
*/
|
|
|
|
|
public function discoverDns($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$logger = new \App\Services\Logger('dns');
|
|
|
|
|
$mode = $_POST['mode'] ?? 'quick';
|
|
|
|
|
|
|
|
|
|
if ($mode === 'deep') {
|
|
|
|
|
$domainName = escapeshellarg($domain['domain_name']);
|
|
|
|
|
$scriptPath = realpath(__DIR__ . '/../../cron/discover_dns.php');
|
|
|
|
|
|
|
|
|
|
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
|
|
|
|
$cmd = "start /b php " . escapeshellarg($scriptPath) . " --domain $domainName";
|
|
|
|
|
} else {
|
|
|
|
|
$cmd = "nohup php " . escapeshellarg($scriptPath) . " --domain $domainName > /dev/null 2>&1 &";
|
|
|
|
|
}
|
|
|
|
|
exec($cmd);
|
|
|
|
|
|
|
|
|
|
$logger->info('Deep DNS scan started (background)', [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
]);
|
|
|
|
|
$_SESSION['info'] = 'Deep scan started in background. New records will appear when discovery completes — refresh the page to see them.';
|
|
|
|
|
} else {
|
|
|
|
|
$dnsService = new \App\Services\DnsService();
|
|
|
|
|
$dnsModel = new \App\Models\DnsRecord();
|
|
|
|
|
|
|
|
|
|
$records = $dnsService->quickScan($domain['domain_name']);
|
|
|
|
|
$totalRecords = array_sum(array_map('count', $records));
|
|
|
|
|
|
|
|
|
|
$this->enrichIpDetails($records, $dnsService);
|
|
|
|
|
|
|
|
|
|
$stats = $dnsModel->saveSnapshot($id, $records);
|
|
|
|
|
$this->domainModel->update($id, ['dns_last_checked' => date('Y-m-d H:i:s')]);
|
|
|
|
|
|
|
|
|
|
$logger->info('Quick DNS scan completed', [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'total' => $totalRecords,
|
|
|
|
|
'added' => $stats['added'],
|
|
|
|
|
]);
|
|
|
|
|
$_SESSION['success'] = "Quick scan complete: {$totalRecords} records found";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirectBackToDomain($id, '#dns');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a single DNS record manually.
|
|
|
|
|
*/
|
|
|
|
|
public function addDnsRecord($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$logger = new \App\Services\Logger('dns');
|
|
|
|
|
$dnsModel = new \App\Models\DnsRecord();
|
|
|
|
|
|
|
|
|
|
$type = strtoupper(trim($_POST['record_type'] ?? ''));
|
|
|
|
|
$host = trim($_POST['host'] ?? '@');
|
|
|
|
|
$value = trim($_POST['value'] ?? '');
|
|
|
|
|
$ttl = !empty($_POST['ttl']) ? (int)$_POST['ttl'] : 3600;
|
|
|
|
|
$priority = !empty($_POST['priority']) ? (int)$_POST['priority'] : null;
|
|
|
|
|
|
|
|
|
|
$validTypes = ['A', 'AAAA', 'MX', 'TXT', 'NS', 'CNAME', 'SOA', 'SRV', 'CAA'];
|
|
|
|
|
if (!in_array($type, $validTypes) || $value === '') {
|
|
|
|
|
$_SESSION['error'] = 'Invalid record type or missing value';
|
|
|
|
|
$this->redirectBackToDomain($id, '#dns');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$dnsModel->addManualRecord($id, $type, $host, $value, $ttl, $priority);
|
|
|
|
|
|
|
|
|
|
$logger->info('Manual DNS record added', [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'type' => $type,
|
|
|
|
|
'host' => $host,
|
|
|
|
|
'value' => $value,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = "DNS record added: {$type} {$host}";
|
|
|
|
|
$this->redirectBackToDomain($id, '#dns');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete a single DNS record.
|
|
|
|
|
*/
|
|
|
|
|
public function deleteDnsRecord($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$recordId = (int)($params['recordId'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$logger = new \App\Services\Logger('dns');
|
|
|
|
|
$dnsModel = new \App\Models\DnsRecord();
|
|
|
|
|
|
|
|
|
|
if ($dnsModel->deleteRecord($recordId, $id)) {
|
|
|
|
|
$logger->info('DNS record deleted', [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'record_id' => $recordId,
|
|
|
|
|
]);
|
|
|
|
|
$_SESSION['success'] = 'DNS record deleted';
|
|
|
|
|
} else {
|
|
|
|
|
$_SESSION['error'] = 'DNS record not found';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirectBackToDomain($id, '#dns');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bulk delete DNS records.
|
|
|
|
|
*/
|
|
|
|
|
public function bulkDeleteDnsRecords($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$logger = new \App\Services\Logger('dns');
|
|
|
|
|
$dnsModel = new \App\Models\DnsRecord();
|
|
|
|
|
|
|
|
|
|
$ids = $_POST['record_ids'] ?? [];
|
|
|
|
|
if (!is_array($ids)) {
|
|
|
|
|
$ids = [];
|
|
|
|
|
}
|
|
|
|
|
$ids = array_map('intval', $ids);
|
|
|
|
|
|
|
|
|
|
$count = $dnsModel->bulkDeleteRecords($ids, $id);
|
|
|
|
|
|
|
|
|
|
$logger->info('Bulk DNS records deleted', [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'count' => $count,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = "Deleted {$count} DNS record(s)";
|
|
|
|
|
$this->redirectBackToDomain($id, '#dns');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Import DNS records from a BIND zone file.
|
|
|
|
|
*/
|
|
|
|
|
public function importDnsZone($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$logger = new \App\Services\Logger('dns');
|
|
|
|
|
$dnsService = new \App\Services\DnsService();
|
|
|
|
|
$dnsModel = new \App\Models\DnsRecord();
|
|
|
|
|
|
|
|
|
|
$content = '';
|
|
|
|
|
if (!empty($_FILES['zone_file']['tmp_name'])) {
|
|
|
|
|
$content = file_get_contents($_FILES['zone_file']['tmp_name']);
|
|
|
|
|
} elseif (!empty($_POST['zone_content'])) {
|
|
|
|
|
$content = $_POST['zone_content'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (trim($content) === '') {
|
|
|
|
|
$_SESSION['error'] = 'No zone file content provided';
|
|
|
|
|
$this->redirectBackToDomain($id, '#dns');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$parsed = $dnsService->parseBindZone($content, $domain['domain_name']);
|
|
|
|
|
$totalParsed = array_sum(array_map('count', $parsed));
|
|
|
|
|
|
|
|
|
|
if ($totalParsed === 0) {
|
|
|
|
|
$_SESSION['error'] = 'No valid DNS records found in zone file';
|
|
|
|
|
$this->redirectBackToDomain($id, '#dns');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$count = $dnsModel->addImportedRecords($id, $parsed);
|
|
|
|
|
|
|
|
|
|
$logger->info('DNS zone file imported', [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'parsed' => $totalParsed,
|
|
|
|
|
'imported' => $count,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$_SESSION['success'] = "Imported {$count} DNS records from zone file";
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$_SESSION['error'] = 'Failed to parse zone file: ' . $e->getMessage();
|
|
|
|
|
$logger->error('DNS zone import failed', [
|
|
|
|
|
'domain_name' => $domain['domain_name'],
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirectBackToDomain($id, '#dns');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enrich A/AAAA records in-place with IP metadata (PTR, ASN, geo).
|
|
|
|
|
*/
|
|
|
|
|
private function enrichIpDetails(array &$records, \App\Services\DnsService $dnsService): void
|
|
|
|
|
{
|
|
|
|
|
$ips = [];
|
|
|
|
|
foreach (['A', 'AAAA'] as $type) {
|
|
|
|
|
if (!empty($records[$type])) {
|
|
|
|
|
foreach ($records[$type] as $r) {
|
|
|
|
|
if (!empty($r['value'])) {
|
|
|
|
|
$ips[] = $r['value'];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!empty($ips)) {
|
|
|
|
|
$ipDetails = $dnsService->lookupIpDetails($ips);
|
|
|
|
|
foreach (['A', 'AAAA'] as $type) {
|
|
|
|
|
if (!empty($records[$type])) {
|
|
|
|
|
foreach ($records[$type] as &$rec) {
|
|
|
|
|
if (!empty($rec['value']) && isset($ipDetails[$rec['value']])) {
|
|
|
|
|
$rec['raw']['_ip_info'] = $ipDetails[$rec['value']];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
unset($rec);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add SSL monitoring (Svc, model, cron, UI)
Introduce SSL certificate monitoring: add SslService for fetching/parsing certs and parsing monitor targets, SslCertificate model for storing snapshots and managing monitored targets, and cron/check_ssl.php for scheduled checks. Extend DomainController with many SSL endpoints and helpers (add/refresh/bulk refresh/delete/bulk delete, snapshot handling, formatting, stats, safety checks) and surface SSL data in domain views. Add NotificationService helpers to create/send SSL alerts, update Installer to include new migration, add migration 028 to create ssl_certificates table, bump app version default to 1.1.5, update changelog, and modify routes and templates to include SSL tab and related UI. Logs and basic validation/error handling are included to surface SSL issues and protect default root-target behavior.
2026-03-08 21:12:09 +02:00
|
|
|
/**
|
|
|
|
|
* Add a monitored SSL hostname and fetch its certificate immediately.
|
|
|
|
|
*/
|
|
|
|
|
public function addSslHost($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$this->ensureSslMonitoringEnabled($domain, $id)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$input = \App\Helpers\InputValidator::sanitizeText($_POST['hostname'] ?? '');
|
|
|
|
|
$target = $this->sslService->parseMonitorTarget($input, $domain['domain_name']);
|
|
|
|
|
|
|
|
|
|
if ($target === null) {
|
|
|
|
|
$_SESSION['error'] = 'Enter a valid subdomain, full hostname, or host:port under this domain';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$alreadyTracked = $this->sslCertificateModel->findByDomainAndHost(
|
|
|
|
|
$id,
|
|
|
|
|
$target['hostname'],
|
|
|
|
|
$target['port']
|
|
|
|
|
) !== null;
|
|
|
|
|
$result = $this->performSslRefreshForHost($id, $target['hostname'], $target['port']);
|
|
|
|
|
|
|
|
|
|
if (in_array($result['status'], ['invalid', 'expired'], true)) {
|
|
|
|
|
$_SESSION['warning'] = ($alreadyTracked ? 'SSL certificate refreshed' : 'SSL certificate added')
|
|
|
|
|
. ' for ' . $result['display_target'] . ', but an issue was detected'
|
|
|
|
|
. ($result['error'] ? ': ' . $result['error'] : '.');
|
|
|
|
|
} else {
|
|
|
|
|
$_SESSION['success'] = ($alreadyTracked ? 'SSL certificate refreshed for ' : 'SSL certificate added for ')
|
|
|
|
|
. $result['display_target'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Refresh all monitored SSL hosts for the domain.
|
|
|
|
|
* Ensures the root hostname is always checked.
|
|
|
|
|
*/
|
|
|
|
|
public function refreshAllSsl($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$this->ensureSslMonitoringEnabled($domain, $id)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$targets = $this->getSslMonitorTargets($id, $domain['domain_name']);
|
|
|
|
|
|
|
|
|
|
$results = [];
|
|
|
|
|
foreach ($targets as $target) {
|
|
|
|
|
$results[] = $this->performSslRefreshForHost($id, $target['hostname'], $target['port']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$issues = array_filter($results, static function (array $result): bool {
|
|
|
|
|
return in_array($result['status'], ['invalid', 'expired'], true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!empty($issues)) {
|
|
|
|
|
$_SESSION['warning'] = 'SSL check completed for ' . count($results) . ' endpoint(s); ' . count($issues) . ' issue(s) detected.';
|
|
|
|
|
} else {
|
|
|
|
|
$_SESSION['success'] = 'SSL certificates refreshed for ' . count($results) . ' endpoint(s).';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Refresh a single monitored SSL host.
|
|
|
|
|
*/
|
|
|
|
|
public function refreshSsl($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$certificateId = (int)($params['certificateId'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$this->ensureSslMonitoringEnabled($domain, $id)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$certificate = $this->sslCertificateModel->findByDomainAndId($id, $certificateId);
|
|
|
|
|
if (!$certificate) {
|
|
|
|
|
$_SESSION['error'] = 'SSL certificate not found';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$result = $this->performSslRefreshForHost($id, $certificate['hostname'], (int)($certificate['port'] ?? 443));
|
|
|
|
|
|
|
|
|
|
if (in_array($result['status'], ['invalid', 'expired'], true)) {
|
|
|
|
|
$_SESSION['warning'] = 'SSL certificate checked for ' . $result['display_target']
|
|
|
|
|
. ($result['error'] ? ': ' . $result['error'] : '.');
|
|
|
|
|
} else {
|
|
|
|
|
$_SESSION['success'] = 'SSL certificate refreshed for ' . $result['display_target'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Refresh selected monitored SSL hosts.
|
|
|
|
|
*/
|
|
|
|
|
public function bulkRefreshSsl($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$this->ensureSslMonitoringEnabled($domain, $id)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ids = $this->parseSslCertificateIds($_POST['certificate_ids'] ?? '');
|
|
|
|
|
if (empty($ids)) {
|
|
|
|
|
$_SESSION['warning'] = 'Select at least one SSL certificate to check';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$results = [];
|
|
|
|
|
foreach ($ids as $certificateId) {
|
|
|
|
|
$certificate = $this->sslCertificateModel->findByDomainAndId($id, $certificateId);
|
|
|
|
|
if ($certificate) {
|
|
|
|
|
$results[] = $this->performSslRefreshForHost(
|
|
|
|
|
$id,
|
|
|
|
|
$certificate['hostname'],
|
|
|
|
|
(int)($certificate['port'] ?? 443)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (empty($results)) {
|
|
|
|
|
$_SESSION['error'] = 'No valid SSL certificates were selected';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$issues = array_filter($results, static function (array $result): bool {
|
|
|
|
|
return in_array($result['status'], ['invalid', 'expired'], true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!empty($issues)) {
|
|
|
|
|
$_SESSION['warning'] = 'Checked ' . count($results) . ' SSL certificate(s); ' . count($issues) . ' issue(s) detected.';
|
|
|
|
|
} else {
|
|
|
|
|
$_SESSION['success'] = 'Checked ' . count($results) . ' SSL certificate(s).';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete a monitored SSL host.
|
|
|
|
|
*/
|
|
|
|
|
public function deleteSsl($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$certificateId = (int)($params['certificateId'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$certificate = $this->sslCertificateModel->findByDomainAndId($id, $certificateId);
|
|
|
|
|
if (!$certificate) {
|
|
|
|
|
$_SESSION['error'] = 'SSL certificate not found';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($this->isDefaultRootSslTarget($certificate, $domain['domain_name'])
|
|
|
|
|
&& $this->countStoredRootSslTargets($id, $domain['domain_name']) <= 1) {
|
|
|
|
|
$_SESSION['error'] = 'Add another root SSL endpoint first if you want to replace the default port 443 check';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->sslCertificateModel->deleteByDomainAndId($id, $certificateId);
|
|
|
|
|
$_SESSION['success'] = 'SSL certificate removed for '
|
|
|
|
|
. $this->sslService->formatTargetLabel($certificate['hostname'], (int)($certificate['port'] ?? 443));
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delete selected monitored SSL hosts.
|
|
|
|
|
*/
|
|
|
|
|
public function bulkDeleteSsl($params = [])
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->verifyCsrf('/domains');
|
|
|
|
|
|
|
|
|
|
$id = (int)($params['id'] ?? 0);
|
|
|
|
|
$domain = $this->checkDomainAccess($id);
|
|
|
|
|
|
|
|
|
|
if (!$domain) {
|
|
|
|
|
$_SESSION['error'] = 'Domain not found';
|
|
|
|
|
$this->redirect('/domains');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ids = $this->parseSslCertificateIds($_POST['certificate_ids'] ?? '');
|
|
|
|
|
if (empty($ids)) {
|
|
|
|
|
$_SESSION['warning'] = 'Select at least one SSL certificate to remove';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$storedRootTargetCount = $this->countStoredRootSslTargets($id, $domain['domain_name']);
|
|
|
|
|
$selectedCertificates = [];
|
|
|
|
|
foreach ($ids as $certificateId) {
|
|
|
|
|
$certificate = $this->sslCertificateModel->findByDomainAndId($id, $certificateId);
|
|
|
|
|
if ($certificate) {
|
|
|
|
|
$selectedCertificates[] = $certificate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$selectedRootTargetCount = count(array_filter(
|
|
|
|
|
$selectedCertificates,
|
|
|
|
|
fn(array $certificate): bool => strtolower($certificate['hostname']) === strtolower($domain['domain_name'])
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
$deletableIds = [];
|
|
|
|
|
foreach ($selectedCertificates as $certificate) {
|
|
|
|
|
if ($this->isDefaultRootSslTarget($certificate, $domain['domain_name'])
|
|
|
|
|
&& ($storedRootTargetCount - $selectedRootTargetCount) < 1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$deletableIds[] = (int)$certificate['id'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (empty($deletableIds)) {
|
|
|
|
|
$_SESSION['warning'] = 'No removable SSL certificates were selected. Add another root endpoint first if you want to replace port 443.';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$deleted = $this->sslCertificateModel->deleteByDomainAndIds($id, $deletableIds);
|
|
|
|
|
$_SESSION['success'] = 'Removed ' . $deleted . ' SSL certificate(s).';
|
|
|
|
|
$this->redirectBackToDomain($id, '#ssl');
|
2025-10-20 17:04:13 +03:00
|
|
|
}
|
2025-10-25 02:04:00 +03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get tags for specific domains (API endpoint)
|
|
|
|
|
*/
|
|
|
|
|
public function getTagsForDomains()
|
|
|
|
|
{
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
|
|
|
$this->json(['error' => 'Method not allowed'], 405);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get JSON input
|
|
|
|
|
$input = json_decode(file_get_contents('php://input'), true);
|
|
|
|
|
|
|
|
|
|
if (!isset($input['domain_ids']) || !is_array($input['domain_ids'])) {
|
|
|
|
|
$this->json(['error' => 'Invalid domain IDs'], 400);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$domainIds = array_map('intval', $input['domain_ids']);
|
|
|
|
|
$userId = \Core\Auth::id();
|
|
|
|
|
$settingModel = new \App\Models\Setting();
|
|
|
|
|
$isolationMode = $settingModel->getValue('user_isolation_mode', 'shared');
|
|
|
|
|
|
|
|
|
|
// Get tags that are assigned to the specified domains
|
|
|
|
|
$tags = $this->domainModel->getTagsForDomains($domainIds, $isolationMode === 'isolated' ? $userId : null);
|
|
|
|
|
|
|
|
|
|
$this->json(['tags' => $tags]);
|
|
|
|
|
}
|
2025-10-08 14:23:07 +03:00
|
|
|
}
|
|
|
|
|
|