Introduces error log tracking with new ErrorLog model, controller, views, and migration. Adds admin UI for viewing, resolving, and deleting errors. Implements bulk actions for users and notification groups, refactors domain filtering/pagination, and centralizes admin access checks using Auth::requireAdmin().
215 lines
6.7 KiB
PHP
215 lines
6.7 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Core\Model;
|
|
|
|
class Domain extends Model
|
|
{
|
|
protected static string $table = 'domains';
|
|
|
|
/**
|
|
* Get all domains with their notification group
|
|
*/
|
|
public function getAllWithGroups(): array
|
|
{
|
|
$sql = "SELECT d.*, ng.name as group_name
|
|
FROM domains d
|
|
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id
|
|
ORDER BY d.status DESC, d.expiration_date ASC";
|
|
|
|
$stmt = $this->db->query($sql);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Get domains expiring within days
|
|
*/
|
|
public function getExpiringDomains(int $days): array
|
|
{
|
|
$sql = "SELECT d.*, ng.name as group_name
|
|
FROM domains d
|
|
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id
|
|
WHERE d.is_active = 1
|
|
AND d.expiration_date IS NOT NULL
|
|
AND d.expiration_date <= DATE_ADD(CURDATE(), INTERVAL ? DAY)
|
|
AND d.expiration_date >= CURDATE()
|
|
ORDER BY d.expiration_date ASC";
|
|
|
|
$stmt = $this->db->prepare($sql);
|
|
$stmt->execute([$days]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Get domains by status
|
|
*/
|
|
public function getByStatus(string $status): array
|
|
{
|
|
$sql = "SELECT d.*, ng.name as group_name
|
|
FROM domains d
|
|
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id
|
|
WHERE d.status = ?
|
|
ORDER BY d.expiration_date ASC";
|
|
|
|
$stmt = $this->db->prepare($sql);
|
|
$stmt->execute([$status]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Get domain with notification channels
|
|
*/
|
|
public function getWithChannels(int $id): ?array
|
|
{
|
|
$sql = "SELECT d.*, ng.name as group_name, ng.id as group_id
|
|
FROM domains d
|
|
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id
|
|
WHERE d.id = ?";
|
|
|
|
$stmt = $this->db->prepare($sql);
|
|
$stmt->execute([$id]);
|
|
$domain = $stmt->fetch();
|
|
|
|
if (!$domain) {
|
|
return null;
|
|
}
|
|
|
|
// Get notification channels for this domain's group
|
|
if ($domain['group_id']) {
|
|
$channelModel = new NotificationChannel();
|
|
$domain['channels'] = $channelModel->getByGroupId($domain['group_id']);
|
|
} else {
|
|
$domain['channels'] = [];
|
|
}
|
|
|
|
return $domain;
|
|
}
|
|
|
|
/**
|
|
* Check if domain exists
|
|
*/
|
|
public function existsByDomain(string $domainName): bool
|
|
{
|
|
$stmt = $this->db->prepare("SELECT COUNT(*) as count FROM domains WHERE domain_name = ?");
|
|
$stmt->execute([$domainName]);
|
|
$result = $stmt->fetch();
|
|
return $result['count'] > 0;
|
|
}
|
|
|
|
/**
|
|
* Get recent domains
|
|
*/
|
|
public function getRecent(int $limit = 5): array
|
|
{
|
|
$sql = "SELECT d.*, ng.name as group_name
|
|
FROM domains d
|
|
LEFT JOIN notification_groups ng ON d.notification_group_id = ng.id
|
|
WHERE d.is_active = 1
|
|
ORDER BY d.created_at DESC, d.id DESC
|
|
LIMIT ?";
|
|
|
|
$stmt = $this->db->prepare($sql);
|
|
$stmt->execute([$limit]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Get dashboard statistics
|
|
*/
|
|
public function getStatistics(): array
|
|
{
|
|
$stats = [
|
|
'total' => 0,
|
|
'active' => 0,
|
|
'expiring_soon' => 0,
|
|
'expired' => 0,
|
|
'inactive' => 0,
|
|
];
|
|
|
|
$sql = "SELECT status, COUNT(*) as count FROM domains WHERE is_active = 1 GROUP BY status";
|
|
$stmt = $this->db->query($sql);
|
|
$results = $stmt->fetchAll();
|
|
|
|
$stats['total'] = array_sum(array_column($results, 'count'));
|
|
|
|
foreach ($results as $row) {
|
|
$stats[strtolower($row['status'])] = $row['count'];
|
|
}
|
|
|
|
return $stats;
|
|
}
|
|
|
|
/**
|
|
* Get filtered, sorted, and paginated domains
|
|
*/
|
|
public function getFilteredPaginated(array $filters, string $sortBy, string $sortOrder, int $page, int $perPage, int $expiringThreshold = 30): array
|
|
{
|
|
// Get all domains with groups
|
|
$domains = $this->getAllWithGroups();
|
|
|
|
// Apply search filter
|
|
if (!empty($filters['search'])) {
|
|
$domains = array_filter($domains, function($domain) use ($filters) {
|
|
return stripos($domain['domain_name'], $filters['search']) !== false ||
|
|
stripos($domain['registrar'] ?? '', $filters['search']) !== false;
|
|
});
|
|
}
|
|
|
|
// Apply status filter
|
|
if (!empty($filters['status'])) {
|
|
$domains = array_filter($domains, function($domain) use ($filters, $expiringThreshold) {
|
|
if ($filters['status'] === 'expiring_soon') {
|
|
// Check if domain expires within configured threshold
|
|
if (!empty($domain['expiration_date'])) {
|
|
$daysLeft = floor((strtotime($domain['expiration_date']) - time()) / 86400);
|
|
return $daysLeft <= $expiringThreshold && $daysLeft >= 0;
|
|
}
|
|
return false;
|
|
}
|
|
return $domain['status'] === $filters['status'];
|
|
});
|
|
}
|
|
|
|
// Apply group filter
|
|
if (!empty($filters['group'])) {
|
|
$domains = array_filter($domains, function($domain) use ($filters) {
|
|
return $domain['notification_group_id'] == $filters['group'];
|
|
});
|
|
}
|
|
|
|
// Get total count after filtering
|
|
$totalDomains = count($domains);
|
|
|
|
// Apply sorting
|
|
usort($domains, function($a, $b) use ($sortBy, $sortOrder) {
|
|
$aVal = $a[$sortBy] ?? '';
|
|
$bVal = $b[$sortBy] ?? '';
|
|
|
|
$comparison = strcasecmp($aVal, $bVal);
|
|
return $sortOrder === 'desc' ? -$comparison : $comparison;
|
|
});
|
|
|
|
// Calculate pagination
|
|
$totalPages = ceil($totalDomains / $perPage);
|
|
$page = min($page, max(1, $totalPages)); // Ensure page is within valid range
|
|
$offset = ($page - 1) * $perPage;
|
|
|
|
// Slice array for current page
|
|
$paginatedDomains = array_slice($domains, $offset, $perPage);
|
|
|
|
return [
|
|
'domains' => $paginatedDomains,
|
|
'pagination' => [
|
|
'current_page' => $page,
|
|
'per_page' => $perPage,
|
|
'total' => $totalDomains,
|
|
'total_pages' => $totalPages,
|
|
'showing_from' => $totalDomains > 0 ? $offset + 1 : 0,
|
|
'showing_to' => min($offset + $perPage, $totalDomains)
|
|
]
|
|
];
|
|
}
|
|
}
|
|
|