Initial Commit
This commit is contained in:
143
app/Models/Domain.php
Normal file
143
app/Models/Domain.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
68
app/Models/NotificationChannel.php
Normal file
68
app/Models/NotificationChannel.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Core\Model;
|
||||
|
||||
class NotificationChannel extends Model
|
||||
{
|
||||
protected static string $table = 'notification_channels';
|
||||
|
||||
/**
|
||||
* Get channels by notification group ID
|
||||
*/
|
||||
public function getByGroupId(int $groupId): array
|
||||
{
|
||||
return $this->where('notification_group_id', $groupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active channels by notification group ID
|
||||
*/
|
||||
public function getActiveByGroupId(int $groupId): array
|
||||
{
|
||||
$sql = "SELECT * FROM notification_channels
|
||||
WHERE notification_group_id = ? AND is_active = 1";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$groupId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create channel with JSON config
|
||||
*/
|
||||
public function createChannel(int $groupId, string $type, array $config): int
|
||||
{
|
||||
return $this->create([
|
||||
'notification_group_id' => $groupId,
|
||||
'channel_type' => $type,
|
||||
'channel_config' => json_encode($config),
|
||||
'is_active' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update channel config
|
||||
*/
|
||||
public function updateConfig(int $id, array $config): bool
|
||||
{
|
||||
$sql = "UPDATE notification_channels SET channel_config = ?, updated_at = NOW() WHERE id = ?";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
return $stmt->execute([json_encode($config), $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle channel active status
|
||||
*/
|
||||
public function toggleActive(int $id): bool
|
||||
{
|
||||
$sql = "UPDATE notification_channels
|
||||
SET is_active = NOT is_active, updated_at = NOW()
|
||||
WHERE id = ?";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
}
|
||||
|
||||
65
app/Models/NotificationGroup.php
Normal file
65
app/Models/NotificationGroup.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Core\Model;
|
||||
|
||||
class NotificationGroup extends Model
|
||||
{
|
||||
protected static string $table = 'notification_groups';
|
||||
|
||||
/**
|
||||
* Get all groups with channel count
|
||||
*/
|
||||
public function getAllWithChannelCount(): array
|
||||
{
|
||||
$sql = "SELECT ng.*,
|
||||
COUNT(DISTINCT nc.id) as channel_count,
|
||||
COUNT(DISTINCT d.id) as domain_count
|
||||
FROM notification_groups ng
|
||||
LEFT JOIN notification_channels nc ON ng.id = nc.notification_group_id
|
||||
LEFT JOIN domains d ON ng.id = d.notification_group_id
|
||||
GROUP BY ng.id
|
||||
ORDER BY ng.name ASC";
|
||||
|
||||
$stmt = $this->db->query($sql);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get group with channels and domains
|
||||
*/
|
||||
public function getWithDetails(int $id): ?array
|
||||
{
|
||||
$group = $this->find($id);
|
||||
|
||||
if (!$group) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get channels
|
||||
$channelModel = new NotificationChannel();
|
||||
$group['channels'] = $channelModel->getByGroupId($id);
|
||||
|
||||
// Get domains
|
||||
$domainModel = new Domain();
|
||||
$group['domains'] = $domainModel->where('notification_group_id', $id);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete group and handle relationships
|
||||
*/
|
||||
public function deleteWithRelations(int $id): bool
|
||||
{
|
||||
// The database CASCADE will handle channels
|
||||
// But we need to set domains to NULL
|
||||
$sql = "UPDATE domains SET notification_group_id = NULL WHERE notification_group_id = ?";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$id]);
|
||||
|
||||
return $this->delete($id);
|
||||
}
|
||||
}
|
||||
|
||||
75
app/Models/NotificationLog.php
Normal file
75
app/Models/NotificationLog.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Core\Model;
|
||||
|
||||
class NotificationLog extends Model
|
||||
{
|
||||
protected static string $table = 'notification_logs';
|
||||
|
||||
/**
|
||||
* Log a notification
|
||||
*/
|
||||
public function log(int $domainId, string $type, string $channel, string $message, bool $success, ?string $error = null): int
|
||||
{
|
||||
return $this->create([
|
||||
'domain_id' => $domainId,
|
||||
'notification_type' => $type,
|
||||
'channel_type' => $channel,
|
||||
'message' => $message,
|
||||
'status' => $success ? 'sent' : 'failed',
|
||||
'error_message' => $error
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logs for a domain
|
||||
*/
|
||||
public function getByDomain(int $domainId, int $limit = 50): array
|
||||
{
|
||||
$sql = "SELECT * FROM notification_logs
|
||||
WHERE domain_id = ?
|
||||
ORDER BY sent_at DESC
|
||||
LIMIT ?";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$domainId, $limit]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent logs
|
||||
*/
|
||||
public function getRecent(int $limit = 100): array
|
||||
{
|
||||
$sql = "SELECT nl.*, d.domain_name
|
||||
FROM notification_logs nl
|
||||
JOIN domains d ON nl.domain_id = d.id
|
||||
ORDER BY nl.sent_at DESC
|
||||
LIMIT ?";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$limit]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if notification was sent recently
|
||||
*/
|
||||
public function wasSentRecently(int $domainId, string $type, int $hoursAgo = 24): bool
|
||||
{
|
||||
$sql = "SELECT COUNT(*) as count FROM notification_logs
|
||||
WHERE domain_id = ?
|
||||
AND notification_type = ?
|
||||
AND status = 'sent'
|
||||
AND sent_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$domainId, $type, $hoursAgo]);
|
||||
$result = $stmt->fetch();
|
||||
|
||||
return $result['count'] > 0;
|
||||
}
|
||||
}
|
||||
|
||||
155
app/Models/TldImportLog.php
Normal file
155
app/Models/TldImportLog.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Core\Model;
|
||||
|
||||
class TldImportLog extends Model
|
||||
{
|
||||
protected static string $table = 'tld_import_logs';
|
||||
|
||||
/**
|
||||
* Create a new import log entry
|
||||
*/
|
||||
public function startImport(string $importType, ?string $ianaPublicationDate = null): int
|
||||
{
|
||||
return $this->create([
|
||||
'import_type' => $importType,
|
||||
'iana_publication_date' => $ianaPublicationDate,
|
||||
'status' => 'running',
|
||||
'started_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete an import log entry
|
||||
*/
|
||||
public function completeImport(int $logId, array $stats, ?string $status = null, ?string $errorMessage = null, ?array $details = null): bool
|
||||
{
|
||||
$data = [
|
||||
'total_tlds' => $stats['total_tlds'] ?? 0,
|
||||
'new_tlds' => $stats['new_tlds'] ?? 0,
|
||||
'updated_tlds' => $stats['updated_tlds'] ?? 0,
|
||||
'failed_tlds' => $stats['failed_tlds'] ?? 0,
|
||||
'completed_at' => date('Y-m-d H:i:s'),
|
||||
'status' => $status ?? ($errorMessage ? 'failed' : 'completed'),
|
||||
'error_message' => $errorMessage
|
||||
];
|
||||
|
||||
if ($details !== null) {
|
||||
$data['details'] = json_encode($details);
|
||||
}
|
||||
|
||||
return $this->update($logId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an import log entry (for progress tracking)
|
||||
*/
|
||||
public function update(int $logId, array $data, ?string $status = null, ?string $errorMessage = null, ?array $details = null): bool
|
||||
{
|
||||
if ($status !== null) {
|
||||
$data['status'] = $status;
|
||||
}
|
||||
|
||||
if ($errorMessage !== null) {
|
||||
$data['error_message'] = $errorMessage;
|
||||
}
|
||||
|
||||
if ($details !== null) {
|
||||
$data['details'] = json_encode($details);
|
||||
}
|
||||
|
||||
return parent::update($logId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent import logs
|
||||
*/
|
||||
public function getRecent(int $limit = 10): array
|
||||
{
|
||||
$sql = "SELECT *,
|
||||
COALESCE(new_tlds, 0) as new_tlds
|
||||
FROM tld_import_logs
|
||||
ORDER BY started_at DESC
|
||||
LIMIT ?";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$limit]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get import statistics
|
||||
*/
|
||||
public function getImportStatistics(): array
|
||||
{
|
||||
$stats = [
|
||||
'total_imports' => 0,
|
||||
'successful_imports' => 0,
|
||||
'failed_imports' => 0,
|
||||
'last_import' => null,
|
||||
'total_tlds_imported' => 0
|
||||
];
|
||||
|
||||
// Total imports
|
||||
$stmt = $this->db->query("SELECT COUNT(*) as count FROM tld_import_logs");
|
||||
$stats['total_imports'] = $stmt->fetch()['count'];
|
||||
|
||||
// Successful imports
|
||||
$stmt = $this->db->query("SELECT COUNT(*) as count FROM tld_import_logs WHERE status = 'completed'");
|
||||
$stats['successful_imports'] = $stmt->fetch()['count'];
|
||||
|
||||
// Failed imports
|
||||
$stmt = $this->db->query("SELECT COUNT(*) as count FROM tld_import_logs WHERE status = 'failed'");
|
||||
$stats['failed_imports'] = $stmt->fetch()['count'];
|
||||
|
||||
// Last import
|
||||
$stmt = $this->db->query("SELECT * FROM tld_import_logs ORDER BY started_at DESC LIMIT 1");
|
||||
$lastImport = $stmt->fetch();
|
||||
if ($lastImport) {
|
||||
$stats['last_import'] = $lastImport['started_at'];
|
||||
}
|
||||
|
||||
// Total TLDs imported
|
||||
$stmt = $this->db->query("SELECT SUM(total_tlds) as total FROM tld_import_logs WHERE status = 'completed'");
|
||||
$result = $stmt->fetch();
|
||||
$stats['total_tlds_imported'] = $result['total'] ?? 0;
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get import logs with pagination
|
||||
*/
|
||||
public function getPaginated(int $page = 1, int $perPage = 20): array
|
||||
{
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$sql = "SELECT *,
|
||||
COALESCE(new_tlds, 0) as new_tlds
|
||||
FROM tld_import_logs
|
||||
ORDER BY started_at DESC
|
||||
LIMIT ? OFFSET ?";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$perPage, $offset]);
|
||||
$logs = $stmt->fetchAll();
|
||||
|
||||
// Get total count
|
||||
$countStmt = $this->db->query("SELECT COUNT(*) as count FROM tld_import_logs");
|
||||
$total = $countStmt->fetch()['count'];
|
||||
|
||||
return [
|
||||
'logs' => $logs,
|
||||
'pagination' => [
|
||||
'current_page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'total' => $total,
|
||||
'total_pages' => ceil($total / $perPage),
|
||||
'showing_from' => $total > 0 ? $offset + 1 : 0,
|
||||
'showing_to' => min($offset + $perPage, $total)
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
253
app/Models/TldRegistry.php
Normal file
253
app/Models/TldRegistry.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Core\Model;
|
||||
|
||||
class TldRegistry extends Model
|
||||
{
|
||||
protected static string $table = 'tld_registry';
|
||||
|
||||
/**
|
||||
* Get TLD by domain extension
|
||||
*/
|
||||
public function getByTld(string $tld): ?array
|
||||
{
|
||||
// Ensure TLD starts with dot
|
||||
if (!str_starts_with($tld, '.')) {
|
||||
$tld = '.' . $tld;
|
||||
}
|
||||
|
||||
$stmt = $this->db->prepare("SELECT * FROM tld_registry WHERE tld = ? AND is_active = 1");
|
||||
$stmt->execute([$tld]);
|
||||
return $stmt->fetch() ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active TLDs
|
||||
*/
|
||||
public function getAllActive(): array
|
||||
{
|
||||
$stmt = $this->db->query("SELECT * FROM tld_registry WHERE is_active = 1 ORDER BY tld ASC");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TLDs that need updating (older than specified days)
|
||||
*/
|
||||
public function getTldsNeedingUpdate(int $daysOld = 30): array
|
||||
{
|
||||
$sql = "SELECT * FROM tld_registry
|
||||
WHERE is_active = 1
|
||||
AND (updated_at < DATE_SUB(NOW(), INTERVAL ? DAY)
|
||||
OR updated_at IS NULL)
|
||||
ORDER BY updated_at ASC";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$daysOld]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update TLD registry entry
|
||||
*/
|
||||
public function createOrUpdate(array $data): int
|
||||
{
|
||||
$tld = $data['tld'];
|
||||
|
||||
// Check if TLD already exists
|
||||
$existing = $this->getByTld($tld);
|
||||
|
||||
if ($existing) {
|
||||
// Update existing record
|
||||
$this->update($existing['id'], $data);
|
||||
return $existing['id'];
|
||||
} else {
|
||||
// Create new record
|
||||
return $this->create($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TLD statistics
|
||||
*/
|
||||
public function getStatistics(): array
|
||||
{
|
||||
$stats = [
|
||||
'total' => 0,
|
||||
'active' => 0,
|
||||
'with_rdap' => 0,
|
||||
'with_whois' => 0,
|
||||
'recently_updated' => 0,
|
||||
'needs_update' => 0
|
||||
];
|
||||
|
||||
// Total TLDs
|
||||
$stmt = $this->db->query("SELECT COUNT(*) as count FROM tld_registry");
|
||||
$stats['total'] = $stmt->fetch()['count'];
|
||||
|
||||
// Active TLDs
|
||||
$stmt = $this->db->query("SELECT COUNT(*) as count FROM tld_registry WHERE is_active = 1");
|
||||
$stats['active'] = $stmt->fetch()['count'];
|
||||
|
||||
// TLDs with RDAP servers
|
||||
$stmt = $this->db->query("SELECT COUNT(*) as count FROM tld_registry WHERE rdap_servers IS NOT NULL AND rdap_servers != '[]' AND is_active = 1");
|
||||
$stats['with_rdap'] = $stmt->fetch()['count'];
|
||||
|
||||
// TLDs with WHOIS servers
|
||||
$stmt = $this->db->query("SELECT COUNT(*) as count FROM tld_registry WHERE whois_server IS NOT NULL AND whois_server != '' AND is_active = 1");
|
||||
$stats['with_whois'] = $stmt->fetch()['count'];
|
||||
|
||||
// Recently updated (last 7 days)
|
||||
$stmt = $this->db->query("SELECT COUNT(*) as count FROM tld_registry WHERE updated_at > DATE_SUB(NOW(), INTERVAL 7 DAY) AND is_active = 1");
|
||||
$stats['recently_updated'] = $stmt->fetch()['count'];
|
||||
|
||||
// Needs update (older than 30 days)
|
||||
$stmt = $this->db->query("SELECT COUNT(*) as count FROM tld_registry WHERE updated_at < DATE_SUB(NOW(), INTERVAL 30 DAY) AND is_active = 1");
|
||||
$stats['needs_update'] = $stmt->fetch()['count'];
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TLDs by search term
|
||||
*/
|
||||
public function search(string $search): array
|
||||
{
|
||||
$search = '%' . $search . '%';
|
||||
$sql = "SELECT * FROM tld_registry
|
||||
WHERE (LOWER(tld) LIKE LOWER(?) OR LOWER(whois_server) LIKE LOWER(?) OR LOWER(registry_url) LIKE LOWER(?))
|
||||
ORDER BY tld ASC";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$search, $search, $search]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TLDs with pagination and sorting
|
||||
*/
|
||||
public function getPaginated(int $page = 1, int $perPage = 50, string $search = '', string $sort = 'tld', string $order = 'asc', string $status = '', string $dataType = ''): array
|
||||
{
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
// Validate sort column
|
||||
$allowedSorts = ['tld', 'rdap_servers', 'whois_server', 'updated_at', 'is_active'];
|
||||
if (!in_array($sort, $allowedSorts)) {
|
||||
$sort = 'tld';
|
||||
}
|
||||
|
||||
// Validate order
|
||||
$order = strtolower($order) === 'desc' ? 'DESC' : 'ASC';
|
||||
|
||||
// Build WHERE clause
|
||||
$whereConditions = [];
|
||||
$params = [];
|
||||
|
||||
// Search filter
|
||||
if (!empty($search)) {
|
||||
$searchParam = '%' . $search . '%';
|
||||
$whereConditions[] = "(LOWER(tld) LIKE LOWER(?) OR LOWER(whois_server) LIKE LOWER(?) OR LOWER(registry_url) LIKE LOWER(?))";
|
||||
$params = array_merge($params, [$searchParam, $searchParam, $searchParam]);
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if ($status === 'active') {
|
||||
$whereConditions[] = "is_active = 1";
|
||||
} elseif ($status === 'inactive') {
|
||||
$whereConditions[] = "is_active = 0";
|
||||
}
|
||||
|
||||
// Data type filter
|
||||
if ($dataType === 'with_rdap') {
|
||||
$whereConditions[] = "(rdap_servers IS NOT NULL AND rdap_servers != '' AND rdap_servers != '[]')";
|
||||
} elseif ($dataType === 'with_whois') {
|
||||
$whereConditions[] = "(whois_server IS NOT NULL AND whois_server != '')";
|
||||
} elseif ($dataType === 'with_registry') {
|
||||
$whereConditions[] = "(registry_url IS NOT NULL AND registry_url != '')";
|
||||
} elseif ($dataType === 'missing_data') {
|
||||
$whereConditions[] = "((rdap_servers IS NULL OR rdap_servers = '' OR rdap_servers = '[]') AND (whois_server IS NULL OR whois_server = '') AND (registry_url IS NULL OR registry_url = ''))";
|
||||
}
|
||||
|
||||
$whereClause = !empty($whereConditions) ? 'WHERE ' . implode(' AND ', $whereConditions) : '';
|
||||
|
||||
// Build ORDER BY clause
|
||||
$orderBy = "ORDER BY $sort $order";
|
||||
if ($sort === 'tld') {
|
||||
$orderBy .= ", tld ASC"; // Secondary sort for consistent results
|
||||
}
|
||||
|
||||
// Build main query
|
||||
$sql = "SELECT * FROM tld_registry $whereClause $orderBy LIMIT ? OFFSET ?";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute(array_merge($params, [$perPage, $offset]));
|
||||
$tlds = $stmt->fetchAll();
|
||||
|
||||
// Get total count
|
||||
$countSql = "SELECT COUNT(*) as count FROM tld_registry $whereClause";
|
||||
$countStmt = $this->db->prepare($countSql);
|
||||
$countStmt->execute($params);
|
||||
$total = $countStmt->fetch()['count'];
|
||||
|
||||
return [
|
||||
'tlds' => $tlds,
|
||||
'pagination' => [
|
||||
'current_page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'total' => $total,
|
||||
'total_pages' => ceil($total / $perPage),
|
||||
'showing_from' => $total > 0 ? $offset + 1 : 0,
|
||||
'showing_to' => min($offset + $perPage, $total)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle TLD active status
|
||||
*/
|
||||
public function toggleActive(int $id): bool
|
||||
{
|
||||
$sql = "UPDATE tld_registry SET is_active = NOT is_active, updated_at = NOW() WHERE id = ?";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TLDs that have RDAP servers
|
||||
*/
|
||||
public function getTldsWithRdap(): array
|
||||
{
|
||||
$sql = "SELECT * FROM tld_registry
|
||||
WHERE rdap_servers IS NOT NULL
|
||||
AND rdap_servers != '[]'
|
||||
AND is_active = 1
|
||||
ORDER BY tld ASC";
|
||||
|
||||
$stmt = $this->db->query($sql);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TLDs that have WHOIS servers
|
||||
*/
|
||||
public function getTldsWithWhois(): array
|
||||
{
|
||||
$sql = "SELECT * FROM tld_registry
|
||||
WHERE whois_server IS NOT NULL
|
||||
AND whois_server != ''
|
||||
AND is_active = 1
|
||||
ORDER BY tld ASC";
|
||||
|
||||
$stmt = $this->db->query($sql);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a custom SQL query
|
||||
*/
|
||||
public function query(string $sql): array
|
||||
{
|
||||
$stmt = $this->db->query($sql);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
}
|
||||
65
app/Models/User.php
Normal file
65
app/Models/User.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Core\Model;
|
||||
|
||||
class User extends Model
|
||||
{
|
||||
protected static string $table = 'users';
|
||||
|
||||
/**
|
||||
* Find user by username
|
||||
*/
|
||||
public function findByUsername(string $username): ?array
|
||||
{
|
||||
$stmt = $this->db->prepare("SELECT * FROM users WHERE username = ? AND is_active = 1");
|
||||
$stmt->execute([$username]);
|
||||
$result = $stmt->fetch();
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify password
|
||||
*/
|
||||
public function verifyPassword(string $password, string $hash): bool
|
||||
{
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update last login timestamp
|
||||
*/
|
||||
public function updateLastLogin(int $userId): bool
|
||||
{
|
||||
$stmt = $this->db->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
|
||||
return $stmt->execute([$userId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create user with hashed password
|
||||
*/
|
||||
public function createUser(string $username, string $password, ?string $email = null, ?string $fullName = null): int
|
||||
{
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
return $this->create([
|
||||
'username' => $username,
|
||||
'password' => $hashedPassword,
|
||||
'email' => $email,
|
||||
'full_name' => $fullName,
|
||||
'is_active' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change password
|
||||
*/
|
||||
public function changePassword(int $userId, string $newPassword): bool
|
||||
{
|
||||
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
|
||||
$stmt = $this->db->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||
return $stmt->execute([$hashedPassword, $userId]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user