diff --git a/modules/kea/pages/edit.php b/modules/kea/pages/edit.php new file mode 100644 index 0000000..3dc3192 --- /dev/null +++ b/modules/kea/pages/edit.php @@ -0,0 +1,129 @@ +get('kea'); +$settings = modules()->settings('kea'); +$fallback = $module['db_defaults'] ?? []; +$metadataFallback = is_array($module['metadata_db_defaults'] ?? null) ? $module['metadata_db_defaults'] : []; +$metadataConfig = is_array($settings['metadata_db'] ?? null) + ? array_replace($metadataFallback, $settings['metadata_db']) + : $metadataFallback; + +$source = (string)($_GET['source'] ?? $_POST['source'] ?? 'reservation'); +$source = $source === 'lease' ? 'lease' : 'reservation'; +$id = (int)($_GET['id'] ?? $_POST['id'] ?? 0); +$error = null; +$notice = null; +$host = null; +$metadataRepo = null; + +try { + $pdo = modules()->modulePdo('kea', $fallback); + if (empty($metadataConfig['driver']) || empty($metadataConfig['dbname'])) { + throw new RuntimeException('Nexus DHCP Zusatzdatenbank ist nicht konfiguriert.'); + } + + $metadataRepo = new KeaHostMetadataRepository(Database::createFromArray($metadataConfig)); + $metadataRepo->ensureSchema(); + $repo = new KeaHostRepository($pdo, $metadataRepo); + $host = $repo->findDisplayByKey($source, $id); + if (!$host) { + throw new RuntimeException('KEA Eintrag wurde nicht gefunden.'); + } + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $metadata = [ + 'real_name' => $_POST['real_name'] ?? '', + 'device_name' => $_POST['device_name'] ?? '', + 'owner' => $_POST['owner'] ?? '', + 'location' => $_POST['location'] ?? '', + 'device_type' => $_POST['device_type'] ?? '', + 'notes' => $_POST['notes'] ?? '', + 'tags' => [], + ]; + $metadataRepo->saveForHost( + $id, + (string)($host['dhcp_identifier'] ?? ''), + (string)($host['ipv4_address'] ?? ''), + $metadata + ); + $notice = 'Zusatzdaten gespeichert.'; + $host = $repo->findDisplayByKey($source, $id) ?: $host; + } +} catch (Throwable $e) { + $error = $e->getMessage(); +} + +$metadata = is_array($host['metadata'] ?? null) ? $host['metadata'] : []; +?> +
+
+
+

KEA Eintrag bearbeiten

+

Zusatzdaten werden separat von der KEA-Datenbank gespeichert.

+
+ Zurueck +
+ + + + + +
+ +
+ + +
+
+
+ +

+

+ IP · MAC +

+
+
+ +
+ + + + + + + + + + +
+ + Abbrechen +
+
+
+ +
diff --git a/modules/kea/partials/dashboard.php b/modules/kea/partials/dashboard.php index a1fda56..423533c 100644 --- a/modules/kea/partials/dashboard.php +++ b/modules/kea/partials/dashboard.php @@ -5,81 +5,81 @@ * @var array $warnings Hinweise, falls Zusatzdaten nicht geladen werden konnten. */ ?> -
-
-

KEA DHCP Hosts

- +
+
+
+

KEA DHCP Hosts

+

Reservierungen und aktuelle Leases aus der KEA-Datenbank.

+
+ Setup
-
diff --git a/public/assets/css/app.css b/public/assets/css/app.css index d58d63c..f90cf93 100644 --- a/public/assets/css/app.css +++ b/public/assets/css/app.css @@ -756,3 +756,100 @@ a { gap: 10px; flex-wrap: wrap; } + +.kea-page { + display: grid; + gap: 16px; +} + +.kea-panel { + border: 1px solid var(--line); + border-radius: 8px; + background: color-mix(in srgb, var(--surface) 94%, transparent); + box-shadow: 0 10px 24px rgba(1, 22, 32, 0.06); + overflow: hidden; +} + +.kea-panel__head { + padding: 16px; + border-bottom: 1px solid var(--line); +} + +.kea-panel__head h3 { + margin: 8px 0 0; +} + +.kea-table-wrap { + overflow-x: auto; +} + +.kea-table { + width: 100%; + border-collapse: collapse; + min-width: 920px; +} + +.kea-table th, +.kea-table td { + padding: 13px 16px; + border-bottom: 1px solid var(--line); + text-align: left; + vertical-align: middle; +} + +.kea-table th { + color: var(--muted); + font-size: 0.78rem; + font-weight: 800; + text-transform: uppercase; +} + +.kea-table tbody tr:hover { + background: color-mix(in srgb, var(--brand-accent) 6%, transparent); +} + +.kea-table .mono { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.kea-empty { + color: var(--muted); + text-align: center; +} + +.kea-message { + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + padding: 14px 16px; +} + +.kea-message p { + margin: 6px 0 0; +} + +.kea-message--error { + border-color: color-mix(in srgb, #d92d20 60%, var(--line)); + background: color-mix(in srgb, #d92d20 10%, var(--surface)); +} + +.kea-message--warning { + border-color: color-mix(in srgb, var(--accent-orange) 60%, var(--line)); + background: color-mix(in srgb, var(--accent-orange) 12%, var(--surface)); +} + +.kea-message--success { + border-color: color-mix(in srgb, var(--accent-green) 60%, var(--line)); + background: color-mix(in srgb, var(--accent-green) 12%, var(--surface)); +} + +.kea-edit-form { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 14px; + padding: 16px; +} + +.kea-edit-form__wide { + grid-column: 1 / -1; +} diff --git a/src/Repository/KeaHostRepository.php b/src/Repository/KeaHostRepository.php index 416cea8..5b14976 100644 --- a/src/Repository/KeaHostRepository.php +++ b/src/Repository/KeaHostRepository.php @@ -158,6 +158,52 @@ final class KeaHostRepository } } + public function findDisplayByKey(string $source, int $id): ?array + { + if ($source === 'lease') { + foreach ($this->findLeases(500) as $lease) { + if ((int)($lease['host_id'] ?? 0) === $id) { + return $this->withMetadata([$lease])[0] ?? $lease; + } + } + + return null; + } + + if (!$this->tableExists('hosts')) { + return null; + } + + $macExpr = $this->hexExpression('dhcp_identifier'); + $ipExpr = $this->ipv4Expression('ipv4_address'); + $driver = $this->driver(); + $sortExpr = $driver === 'pgsql' ? 'host_id::text' : 'CAST(host_id AS CHAR)'; + $stmt = $this->pdo->prepare( + "SELECT + host_id, + {$macExpr} AS dhcp_identifier_hex, + {$ipExpr} AS ipv4_address_text, + hostname, + user_context, + 'reservation' AS source, + host_id AS sort_id, + {$sortExpr} AS sort_time + FROM hosts + WHERE host_id = :id + LIMIT 1" + ); + $stmt->bindValue(':id', $id, PDO::PARAM_INT); + $stmt->execute(); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if (!$row) { + return null; + } + + $row = $this->normalizeRow($row); + return $this->withMetadata([$row])[0] ?? $row; + } + private function isMissingTable(\PDOException $e): bool { return in_array((string)$e->getCode(), ['42P01', '42S02'], true); @@ -201,15 +247,10 @@ final class KeaHostRepository return $hosts; } - $metadataByHost = $this->metadata->findByHostIds( - array_column( - array_filter($hosts, static fn(array $host): bool => ($host['source'] ?? '') === 'reservation'), - 'host_id' - ) - ); + $metadataByHost = $this->metadata->findByHostIds(array_column($hosts, 'host_id')); foreach ($hosts as &$host) { $hostId = (int)($host['host_id'] ?? 0); - $host['metadata'] = ($host['source'] ?? '') === 'reservation' ? ($metadataByHost[$hostId] ?? []) : []; + $host['metadata'] = $metadataByHost[$hostId] ?? []; } unset($host);