diff --git a/cp/app/Controllers/DomainsController.php b/cp/app/Controllers/DomainsController.php index 7f3ebf8..e26c7e5 100644 --- a/cp/app/Controllers/DomainsController.php +++ b/cp/app/Controllers/DomainsController.php @@ -1056,7 +1056,61 @@ class DomainsController extends Controller } } - + + public function historyDomain(Request $request, Response $response, $args) + { + $db = $this->container->get('db'); + $db_audit = $this->container->get('db_audit'); + // Get the current URI + $uri = $request->getUri()->getPath(); + + if ($args) { + $args = strtolower(trim($args)); + + if (!preg_match('/^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)*[a-z0-9]([-a-z0-9]*[a-z0-9])?$/', $args)) { + $this->container->get('flash')->addMessage('error', 'Invalid domain name format'); + return $response->withHeader('Location', '/domains')->withStatus(302); + } + + try { + $exists = $db_audit->selectValue('SELECT 1 FROM domain LIMIT 1'); + } catch (\PDOException $e) { + throw new \RuntimeException('Audit table is empty or not configured'); + } + + $domain = $db->selectRow('SELECT id,name FROM domain WHERE name = ?', + [ $args ]); + + if ($domain) { + $history = $db_audit->select( + 'SELECT * FROM domain WHERE name = ? ORDER BY audit_timestamp DESC, audit_rownum ASC', + [$args] + ); + + if (strpos($domain['name'], 'xn--') === 0) { + $domain['name_o'] = $domain['name']; + $domain['name'] = idn_to_utf8($domain['name'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46); + } else { + $domain['name_o'] = $domain['name']; + } + + return view($response,'admin/domains/historyDomain.twig', [ + 'domain' => $domain, + 'history' => $history, + 'currentUri' => $uri + ]); + } else { + // Domain does not exist, redirect to the domains view + return $response->withHeader('Location', '/domains')->withStatus(302); + } + + } else { + // Redirect to the domains view + return $response->withHeader('Location', '/domains')->withStatus(302); + } + + } + public function updateDomain(Request $request, Response $response, $args) { $db = $this->container->get('db'); diff --git a/cp/bootstrap/app.php b/cp/bootstrap/app.php index ea1d936..11d85c2 100644 --- a/cp/bootstrap/app.php +++ b/cp/bootstrap/app.php @@ -66,6 +66,14 @@ $container->set('pdo', function () use ($pdo) { return $pdo; }); +$container->set('db_audit', function () use ($db_audit) { + return $db_audit; +}); + +$container->set('pdo_audit', function () use ($pdo_audit) { + return $pdo_audit; +}); + $container->set('auth', function() { //$responseFactory = new \Nyholm\Psr7\Factory\Psr17Factory(); //$response = $responseFactory->createResponse(); diff --git a/cp/bootstrap/database.php b/cp/bootstrap/database.php index fb7aaf4..0922563 100644 --- a/cp/bootstrap/database.php +++ b/cp/bootstrap/database.php @@ -32,4 +32,28 @@ try { } catch (PDOException $e) { $log->alert("Database connection failed: " . $e->getMessage(), ['driver' => $defaultDriver]); +} + +// Audit DB (optional) +try { + $auditDriver = match ($defaultDriver) { + 'mysql' => "{$config['mysql']['driver']}:dbname=registryAudit;host={$config['mysql']['host']};charset={$config['mysql']['charset']}", + 'sqlite' => "{$config['sqlite']['driver']}:{$config['sqlite']['audit_path']}", // assumes audit_path is set for SQLite + 'pgsql' => "{$config['pgsql']['driver']}:dbname=registryAudit;host={$config['pgsql']['host']}", + default => throw new \RuntimeException('Unsupported database driver for audit'), + }; + + if (str_starts_with($auditDriver, "sqlite")) { + $pdo_audit = new \PDO($auditDriver); + } else { + $pdo_audit = new \PDO( + $auditDriver, + $config[$defaultDriver]['username'], + $config[$defaultDriver]['password'] + ); + } + + $db_audit = PdoDatabase::fromPdo($pdo_audit); +} catch (PDOException $e) { + $log->alert("Audit database connection failed: " . $e->getMessage(), ['driver' => 'audit']); } \ No newline at end of file diff --git a/cp/resources/views/admin/domains/historyDomain.twig b/cp/resources/views/admin/domains/historyDomain.twig new file mode 100644 index 0000000..9ba7f40 --- /dev/null +++ b/cp/resources/views/admin/domains/historyDomain.twig @@ -0,0 +1,125 @@ +{% extends "layouts/app.twig" %} + +{% block title %}{{ __('Domain History') }}{% endblock %} + +{% block content %} +
+ + + +
+
+
+
+
+

+ {{ __('Domain') }} {{ domain.name }} +

+
+
+
+ + + + + + + + + + + + + + {% if history|length == 0 %} + + + + {% else %} + {% set max = history|length %} + {% for i in 0..max-1 %} + {% set entry = history[i] %} + {% if entry.audit_statement == 'UPDATE' and entry.audit_type == 'OLD' %} + {% set old = entry %} + {% set new = history[i + 1] is defined and history[i + 1].audit_type == 'NEW' ? history[i + 1] : {} %} + {% for key in old|keys %} + {% if old[key] != new[key] and key not in ['audit_timestamp','audit_statement','audit_type','audit_uuid','audit_rownum','audit_user','audit_ses_id','audit_usr_id'] %} + + + + + + + + + + {% endif %} + {% endfor %} + {% elseif entry.audit_statement == 'INSERT' %} + + + + + + + + {% elseif entry.audit_statement == 'DELETE' %} + + + + + + + + {% endif %} + {% endfor %} + {% endif %} + +
{{ __('Timestamp') }}{{ __('Action') }}{{ __('User') }}{{ __('Session') }}{{ __('Changed Field') }}{{ __('Old Value') }}{{ __('New Value') }}
{{ __('No audit history available for this domain.') }}
{{ old.audit_timestamp }}{{ old.audit_statement }}{{ old.audit_usr_id|default('–') }}{{ old.audit_ses_id|default('–') }}{{ key }}{{ old[key]|default('–') }}{{ new[key]|default('–') }}
{{ entry.audit_timestamp }}{{ entry.audit_statement }}{{ entry.audit_usr_id|default('–') }}{{ entry.audit_ses_id|default('–') }}{{ __('New domain inserted.') }}
{{ entry.audit_timestamp }}{{ entry.audit_statement }}{{ entry.audit_usr_id|default('–') }}{{ entry.audit_ses_id|default('–') }}{{ __('Domain was deleted.') }}
+
+
+
+
+
+
+ {% include 'partials/footer.twig' %} +
+{% endblock %} \ No newline at end of file diff --git a/cp/resources/views/admin/domains/viewDomain.twig b/cp/resources/views/admin/domains/viewDomain.twig index cf83e03..88a611e 100644 --- a/cp/resources/views/admin/domains/viewDomain.twig +++ b/cp/resources/views/admin/domains/viewDomain.twig @@ -29,6 +29,13 @@
+ + + {{ __('Domain History') }} + + + + {{ __('Renew Domain') }} diff --git a/cp/routes/web.php b/cp/routes/web.php index c2e2d1f..18a27c0 100644 --- a/cp/routes/web.php +++ b/cp/routes/web.php @@ -48,6 +48,7 @@ $app->group('', function ($route) { $route->map(['GET', 'POST'], '/domain/check', DomainsController::class . ':checkDomain')->setName('checkDomain'); $route->map(['GET', 'POST'], '/domain/create', DomainsController::class . ':createDomain')->setName('createDomain'); $route->get('/domain/view/{domain}', DomainsController::class . ':viewDomain')->setName('viewDomain'); + $route->get('/domain/history/{domain}', DomainsController::class . ':historyDomain')->setName('historyDomain'); $route->get('/domain/update/{domain}', DomainsController::class . ':updateDomain')->setName('updateDomain'); $route->post('/domain/update', DomainsController::class . ':updateDomainProcess')->setName('updateDomainProcess'); $route->post('/domain/deletesecdns', DomainsController::class . ':domainDeleteSecdns')->setName('domainDeleteSecdns');