New desktop
This commit is contained in:
12
temp/nexus-module-import/modules/kea/design.json
Normal file
12
temp/nexus-module-import/modules/kea/design.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"eyebrow": "Modul",
|
||||
"title": "KEA DHCP",
|
||||
"description": "Verwaltung von KEA DHCP Hosts und Reservierungen.",
|
||||
"actions": [
|
||||
{ "label": "Setup", "href": "/modules/setup/kea", "variant": "secondary" }
|
||||
],
|
||||
"tabs": [
|
||||
{ "label": "Hosts", "href": "/module/kea", "match_prefixes": ["/module/kea", "/module/kea/edit"] },
|
||||
{ "label": "Gruppen", "href": "/module/kea/groups", "match_prefixes": ["/module/kea/groups"] }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Database;
|
||||
use App\ModuleMigrationContext;
|
||||
use App\Repository\KeaHostMetadataRepository;
|
||||
|
||||
return new class {
|
||||
public function up(ModuleMigrationContext $context): void
|
||||
{
|
||||
$settings = $context->settings();
|
||||
$fallback = is_array($context->module['metadata_db_defaults'] ?? null)
|
||||
? $context->module['metadata_db_defaults']
|
||||
: [];
|
||||
$config = is_array($settings['metadata_db'] ?? null)
|
||||
? array_replace($fallback, $settings['metadata_db'])
|
||||
: $fallback;
|
||||
|
||||
if (empty($config['driver']) || empty($config['dbname'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$repo = new KeaHostMetadataRepository(Database::createFromArray($config));
|
||||
$repo->ensureSchema();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Database;
|
||||
use App\ModuleMigrationContext;
|
||||
use App\Repository\KeaHostMetadataRepository;
|
||||
|
||||
return new class {
|
||||
public function up(ModuleMigrationContext $context): void
|
||||
{
|
||||
$settings = $context->settings();
|
||||
$fallback = is_array($context->module['metadata_db_defaults'] ?? null)
|
||||
? $context->module['metadata_db_defaults']
|
||||
: [];
|
||||
$config = is_array($settings['metadata_db'] ?? null)
|
||||
? array_replace($fallback, $settings['metadata_db'])
|
||||
: $fallback;
|
||||
|
||||
if (empty($config['driver']) || empty($config['dbname'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$repo = new KeaHostMetadataRepository(Database::createFromArray($config));
|
||||
$repo->ensureSchema();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Database;
|
||||
use App\ModuleMigrationContext;
|
||||
use App\Repository\KeaHostMetadataRepository;
|
||||
|
||||
return new class {
|
||||
public function up(ModuleMigrationContext $context): void
|
||||
{
|
||||
$settings = $context->settings();
|
||||
$fallback = is_array($context->module['metadata_db_defaults'] ?? null)
|
||||
? $context->module['metadata_db_defaults']
|
||||
: [];
|
||||
$config = is_array($settings['metadata_db'] ?? null)
|
||||
? array_replace($fallback, $settings['metadata_db'])
|
||||
: $fallback;
|
||||
|
||||
if (empty($config['driver']) || empty($config['dbname'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$repo = new KeaHostMetadataRepository(Database::createFromArray($config));
|
||||
$repo->ensureSchema();
|
||||
}
|
||||
};
|
||||
46
temp/nexus-module-import/modules/kea/module.json
Normal file
46
temp/nexus-module-import/modules/kea/module.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"title": "KEA DHCP",
|
||||
"version": "1.2.0",
|
||||
"schema_version": 3,
|
||||
"description": "Verwaltung von KEA DHCP Hosts und Reservierungen.",
|
||||
"setup": {
|
||||
"fields": [
|
||||
{ "name": "db.driver", "label": "KEA DB Driver", "type": "text", "required": true, "help": "Standard-KEA-Datenbank, die auch vom KEA-Dienst selbst genutzt wird." },
|
||||
{ "name": "db.host", "label": "KEA DB Host", "type": "text", "required": true },
|
||||
{ "name": "db.port", "label": "KEA DB Port", "type": "number", "required": true },
|
||||
{ "name": "db.dbname", "label": "KEA DB Name", "type": "text", "required": true },
|
||||
{ "name": "db.schema", "label": "KEA DB Schema", "type": "text", "required": false },
|
||||
{ "name": "db.user", "label": "KEA DB User", "type": "text", "required": true },
|
||||
{ "name": "db.password", "label": "KEA DB Passwort", "type": "password", "required": true },
|
||||
{ "name": "metadata_db.driver", "label": "Nexus DHCP DB Driver", "type": "text", "required": true, "help": "Separate Datenbank fuer Nexus-eigene DHCP-Zusatzinfos, nicht fuer KEA-Standardtabellen." },
|
||||
{ "name": "metadata_db.host", "label": "Nexus DHCP DB Host", "type": "text", "required": true },
|
||||
{ "name": "metadata_db.port", "label": "Nexus DHCP DB Port", "type": "number", "required": true },
|
||||
{ "name": "metadata_db.dbname", "label": "Nexus DHCP DB Name", "type": "text", "required": true },
|
||||
{ "name": "metadata_db.schema", "label": "Nexus DHCP DB Schema", "type": "text", "required": false },
|
||||
{ "name": "metadata_db.user", "label": "Nexus DHCP DB User", "type": "text", "required": true },
|
||||
{ "name": "metadata_db.password", "label": "Nexus DHCP DB Passwort", "type": "password", "required": true },
|
||||
{ "name": "kea_db_version", "label": "KEA DB Version", "type": "text", "required": false },
|
||||
{ "name": "kea_init_script", "label": "KEA Init Script", "type": "text", "required": false },
|
||||
{ "name": "kea_init_cmd", "label": "KEA Init Command", "type": "text", "required": false },
|
||||
{ "name": "kea_auto_init", "label": "KEA Auto-Init", "type": "checkbox", "required": false }
|
||||
]
|
||||
},
|
||||
"db_defaults": {
|
||||
"driver": "pgsql",
|
||||
"host": "localhost",
|
||||
"port": 5432,
|
||||
"dbname": "",
|
||||
"schema": "public",
|
||||
"user": "",
|
||||
"password": ""
|
||||
},
|
||||
"metadata_db_defaults": {
|
||||
"driver": "mysql",
|
||||
"host": "192.168.178.10",
|
||||
"port": 3306,
|
||||
"dbname": "",
|
||||
"schema": "",
|
||||
"user": "",
|
||||
"password": ""
|
||||
}
|
||||
}
|
||||
91
temp/nexus-module-import/modules/kea/pages/data.php
Normal file
91
temp/nexus-module-import/modules/kea/pages/data.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
use App\Database;
|
||||
use App\Repository\KeaHostMetadataRepository;
|
||||
use App\Repository\KeaHostRepository;
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-store');
|
||||
|
||||
$module = modules()->get('kea');
|
||||
$fallback = $module['db_defaults'] ?? [];
|
||||
$settings = modules()->settings('kea');
|
||||
$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;
|
||||
|
||||
try {
|
||||
$metadataRepo = null;
|
||||
$pdo = modules()->modulePdo('kea', $fallback);
|
||||
if (!empty($metadataConfig['driver']) && !empty($metadataConfig['dbname'])) {
|
||||
try {
|
||||
$metadataRepo = new KeaHostMetadataRepository(Database::createFromArray($metadataConfig));
|
||||
$metadataRepo->ensureSchema();
|
||||
} catch (\Throwable) {
|
||||
$metadataRepo = null;
|
||||
}
|
||||
}
|
||||
|
||||
$repo = new KeaHostRepository($pdo, $metadataRepo);
|
||||
$hosts = $repo->findAll(200);
|
||||
$stats = [
|
||||
'total' => $repo->countReservations() + $repo->countLeases(),
|
||||
'reservations' => $repo->countReservations(),
|
||||
'leases' => $repo->countLeases(),
|
||||
'groups' => [],
|
||||
'free_ips' => [],
|
||||
];
|
||||
|
||||
foreach ($hosts as $host) {
|
||||
$group = trim((string)($host['metadata']['group_name'] ?? ''));
|
||||
if ($group !== '') {
|
||||
$stats['groups'][$group] = ($stats['groups'][$group] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($metadataRepo !== null) {
|
||||
$stats['free_ips'] = array_map(
|
||||
static fn(array $ips): int => count($ips),
|
||||
$metadataRepo->availableIpsByGroup(
|
||||
array_merge($repo->usedIpAddresses(), $metadataRepo->desiredIps()),
|
||||
4096
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$rows = array_map(static function (array $host): array {
|
||||
return [
|
||||
'source' => (string)($host['source'] ?? 'reservation'),
|
||||
'host_id' => (string)($host['host_id'] ?? '0'),
|
||||
'display_name' => (string)($host['metadata']['device_name'] ?? $host['metadata']['real_name'] ?? $host['display_name'] ?? $host['hostname'] ?? 'Unbekannt'),
|
||||
'ipv4_address' => (string)($host['ipv4_address'] ?? ''),
|
||||
'dhcp_identifier' => (string)($host['dhcp_identifier'] ?? ''),
|
||||
'last_seen_at' => (string)($host['last_seen_at'] ?? '-'),
|
||||
'lease_expires_at' => (string)($host['lease_expires_at'] ?? '-'),
|
||||
'real_name' => (string)($host['metadata']['real_name'] ?? '-'),
|
||||
'location' => (string)($host['metadata']['location'] ?? '-'),
|
||||
'group_name' => (string)($host['metadata']['group_name'] ?? '-'),
|
||||
];
|
||||
}, $hosts);
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'stats' => [
|
||||
'total' => (int)$stats['total'],
|
||||
'reservations' => (int)$stats['reservations'],
|
||||
'leases' => (int)$stats['leases'],
|
||||
'groups' => count($stats['groups']),
|
||||
'free_ips' => array_sum($stats['free_ips']),
|
||||
],
|
||||
'rows' => $rows,
|
||||
'updated_at' => date('H:i:s'),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
} catch (\Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'ok' => false,
|
||||
'error' => $e->getMessage(),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
exit;
|
||||
266
temp/nexus-module-import/modules/kea/pages/edit.php
Normal file
266
temp/nexus-module-import/modules/kea/pages/edit.php
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
use App\Database;
|
||||
use App\Repository\KeaHostMetadataRepository;
|
||||
use App\Repository\KeaHostRepository;
|
||||
|
||||
$module = modules()->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;
|
||||
$groups = [];
|
||||
$availableIpsByGroup = [];
|
||||
$checks = [];
|
||||
$hostKey = '';
|
||||
|
||||
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();
|
||||
$groups = $metadataRepo->listGroups();
|
||||
$repo = new KeaHostRepository($pdo, $metadataRepo);
|
||||
$host = $repo->findDisplayByKey($source, $id);
|
||||
if (!$host) {
|
||||
throw new RuntimeException('KEA Eintrag wurde nicht gefunden.');
|
||||
}
|
||||
$hostKey = $source . ':' . (string)($host['host_id'] ?? $id);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = (string)($_POST['action'] ?? 'save_metadata');
|
||||
if ($action === 'dns_lookup') {
|
||||
$ip = (string)($host['ipv4_address'] ?? '');
|
||||
$hostname = trim((string)($host['metadata']['device_name'] ?? $host['hostname'] ?? ''));
|
||||
$reverse = $ip !== '' ? gethostbyaddr($ip) : '';
|
||||
$forward = $hostname !== '' ? gethostbyname($hostname) : '';
|
||||
$success = ($reverse !== '' && $reverse !== $ip) || ($forward !== '' && $forward !== $hostname);
|
||||
|
||||
$metadataRepo->saveCheck($hostKey, 'dns', $success ? 'success' : 'warning', [
|
||||
'ip' => $ip,
|
||||
'hostname' => $hostname,
|
||||
'reverse' => $reverse,
|
||||
'forward' => $forward,
|
||||
]);
|
||||
$notice = $success ? 'DNS-Pruefung gespeichert.' : 'DNS-Pruefung gespeichert, aber kein eindeutiger Name gefunden.';
|
||||
} else {
|
||||
$metadata = [
|
||||
'real_name' => $_POST['real_name'] ?? '',
|
||||
'device_name' => $_POST['device_name'] ?? '',
|
||||
'owner' => $_POST['owner'] ?? '',
|
||||
'location' => $_POST['location'] ?? '',
|
||||
'device_type' => $_POST['device_type'] ?? '',
|
||||
'group_name' => $_POST['group_name'] ?? '',
|
||||
'desired_ip' => $_POST['desired_ip'] ?? '',
|
||||
'notes' => $_POST['notes'] ?? '',
|
||||
'tags' => [],
|
||||
];
|
||||
$desiredIp = trim((string)$metadata['desired_ip']);
|
||||
if ($desiredIp !== '') {
|
||||
$newHostId = $repo->reserveDisplayEntry($host, $desiredIp, $metadata);
|
||||
$source = 'reservation';
|
||||
$id = $newHostId;
|
||||
$notice = 'Zusatzdaten gespeichert und KEA-Reservierung gesetzt.';
|
||||
} else {
|
||||
$metadataRepo->saveForHost(
|
||||
$id,
|
||||
(string)($host['dhcp_identifier'] ?? ''),
|
||||
(string)($host['ipv4_address'] ?? ''),
|
||||
$metadata
|
||||
);
|
||||
$notice = 'Zusatzdaten gespeichert.';
|
||||
}
|
||||
}
|
||||
$host = $repo->findDisplayByKey($source, $id) ?: $host;
|
||||
$hostKey = $source . ':' . (string)($host['host_id'] ?? $id);
|
||||
}
|
||||
|
||||
$usedIps = array_diff(
|
||||
array_merge($repo->usedIpAddresses(), $metadataRepo->desiredIps()),
|
||||
[(string)($host['ipv4_address'] ?? ''), (string)($host['metadata']['desired_ip'] ?? '')]
|
||||
);
|
||||
$availableIpsByGroup = $metadataRepo->availableIpsByGroup($usedIps);
|
||||
$checks = $metadataRepo->latestChecks([$hostKey])[$hostKey] ?? [];
|
||||
} catch (Throwable $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
$metadata = is_array($host['metadata'] ?? null) ? $host['metadata'] : [];
|
||||
$selectedGroup = (string)($metadata['group_name'] ?? '');
|
||||
$selectedIp = (string)($metadata['desired_ip'] ?? '');
|
||||
?>
|
||||
<?= module_shell_header('kea', [
|
||||
'title' => 'KEA Eintrag bearbeiten',
|
||||
]) ?>
|
||||
<div class="module-flow kea-page">
|
||||
<section class="module-box">
|
||||
<div class="module-box-head">
|
||||
<div>
|
||||
<h2 class="module-box-title">KEA Eintrag bearbeiten</h2>
|
||||
<p>Zusatzdaten werden separat von der KEA-Datenbank gespeichert.</p>
|
||||
</div>
|
||||
<div class="setup-actions">
|
||||
<a class="module-button module-button--secondary" href="/module/kea/groups">Gruppen verwalten</a>
|
||||
<a class="module-button module-button--secondary" href="/module/kea">Zurueck</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="kea-message kea-message--error" role="alert">
|
||||
<strong>Fehler</strong>
|
||||
<p><?= e($error) ?></p>
|
||||
</div>
|
||||
<?php elseif ($host): ?>
|
||||
<?php if ($notice): ?>
|
||||
<div class="kea-message kea-message--success">
|
||||
<?= e($notice) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="module-box kea-panel">
|
||||
<div class="module-box-head kea-panel__head">
|
||||
<div>
|
||||
<span class="pill"><?= ($source === 'lease') ? 'Lease' : 'Reservierung' ?></span>
|
||||
<h2 class="module-box-title"><?= e((string)($host['hostname'] ?: 'Unbekannt')) ?></h2>
|
||||
<p class="muted">
|
||||
IP <?= e((string)($host['ipv4_address'] ?? '')) ?> · MAC <?= e((string)($host['dhcp_identifier'] ?? '')) ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" class="kea-edit-form">
|
||||
<input type="hidden" name="action" value="save_metadata">
|
||||
<input type="hidden" name="source" value="<?= e($source) ?>">
|
||||
<input type="hidden" name="id" value="<?= e((string)$id) ?>">
|
||||
|
||||
<label class="setup-field">
|
||||
<span>Echter Name</span>
|
||||
<input type="text" name="real_name" value="<?= e((string)($metadata['real_name'] ?? '')) ?>">
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>Gerätename</span>
|
||||
<input type="text" name="device_name" value="<?= e((string)($metadata['device_name'] ?? '')) ?>">
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>Besitzer</span>
|
||||
<input type="text" name="owner" value="<?= e((string)($metadata['owner'] ?? '')) ?>">
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>Standort</span>
|
||||
<input type="text" name="location" value="<?= e((string)($metadata['location'] ?? '')) ?>">
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>Gerätetyp</span>
|
||||
<input type="text" name="device_type" value="<?= e((string)($metadata['device_type'] ?? '')) ?>">
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>Gruppe</span>
|
||||
<select name="group_name" data-kea-group-select>
|
||||
<option value="">Bitte waehlen</option>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<option value="<?= e($group) ?>" <?= $selectedGroup === $group ? 'selected' : '' ?>><?= e($group) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php if ($groups === []): ?>
|
||||
<small class="muted">Noch keine Gruppen definiert. Oeffne zuerst Gruppen verwalten.</small>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>Feste IP</span>
|
||||
<select name="desired_ip" data-kea-ip-select data-selected-ip="<?= e($selectedIp) ?>">
|
||||
<option value="">Erst Gruppe waehlen</option>
|
||||
</select>
|
||||
<small class="muted">Es werden nur freie IPs aus dem IP-Bereich der gewaehlten Gruppe angeboten.</small>
|
||||
</label>
|
||||
<label class="setup-field kea-edit-form__wide">
|
||||
<span>Notizen</span>
|
||||
<textarea name="notes" rows="4"><?= e((string)($metadata['notes'] ?? '')) ?></textarea>
|
||||
</label>
|
||||
|
||||
<div class="setup-actions kea-edit-form__wide">
|
||||
<button class="cta-button" type="submit">Speichern</button>
|
||||
<a class="nav-link" href="/module/kea">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="module-box kea-panel">
|
||||
<div class="module-box-head kea-panel__head">
|
||||
<div>
|
||||
<span class="pill">Pruefungen</span>
|
||||
<h2 class="module-box-title">Gerätechecks</h2>
|
||||
<p class="muted">Pruefergebnisse werden in der Nexus-DHCP-Datenbank gespeichert und koennen spaeter fuer Reports genutzt werden.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kea-check-grid">
|
||||
<div class="kea-check-card">
|
||||
<h4>DNS / Hostname</h4>
|
||||
<?php $dnsCheck = $checks['dns'] ?? null; ?>
|
||||
<?php if ($dnsCheck): ?>
|
||||
<?php $dnsResult = json_decode((string)($dnsCheck['result_json'] ?? '{}'), true) ?: []; ?>
|
||||
<p class="muted">Status: <?= e((string)($dnsCheck['status'] ?? '')) ?> · <?= e((string)($dnsCheck['checked_at'] ?? '')) ?></p>
|
||||
<p class="mono">Reverse: <?= e((string)($dnsResult['reverse'] ?? '-')) ?></p>
|
||||
<p class="mono">Forward: <?= e((string)($dnsResult['forward'] ?? '-')) ?></p>
|
||||
<?php else: ?>
|
||||
<p class="muted">Noch keine DNS-Pruefung gespeichert.</p>
|
||||
<?php endif; ?>
|
||||
<form method="post" class="setup-actions">
|
||||
<input type="hidden" name="action" value="dns_lookup">
|
||||
<input type="hidden" name="source" value="<?= e($source) ?>">
|
||||
<input type="hidden" name="id" value="<?= e((string)$id) ?>">
|
||||
<button class="nav-link" type="submit">DNS jetzt pruefen</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="kea-check-card">
|
||||
<h4>Login-Erkennung</h4>
|
||||
<p class="muted">Vorbereitet fuer spaetere HTTP/Port-Erkennung. Noch nicht automatisch aktiv, damit keine ungewollten Scans laufen.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
(() => {
|
||||
const ipsByGroup = <?= json_encode($availableIpsByGroup, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
|
||||
const groupSelect = document.querySelector('[data-kea-group-select]');
|
||||
const ipSelect = document.querySelector('[data-kea-ip-select]');
|
||||
if (!groupSelect || !ipSelect) {
|
||||
return;
|
||||
}
|
||||
const selectedIp = ipSelect.dataset.selectedIp || '';
|
||||
const renderIps = () => {
|
||||
const ips = ipsByGroup[groupSelect.value] || [];
|
||||
ipSelect.innerHTML = '';
|
||||
const empty = document.createElement('option');
|
||||
empty.value = '';
|
||||
empty.textContent = groupSelect.value ? 'Keine feste IP' : 'Erst Gruppe waehlen';
|
||||
ipSelect.appendChild(empty);
|
||||
if (selectedIp && !ips.includes(selectedIp)) {
|
||||
ips.unshift(selectedIp);
|
||||
}
|
||||
for (const ip of ips) {
|
||||
const option = document.createElement('option');
|
||||
option.value = ip;
|
||||
option.textContent = ip;
|
||||
option.selected = ip === selectedIp;
|
||||
ipSelect.appendChild(option);
|
||||
}
|
||||
};
|
||||
groupSelect.addEventListener('change', renderIps);
|
||||
renderIps();
|
||||
})();
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?= module_shell_footer() ?>
|
||||
227
temp/nexus-module-import/modules/kea/pages/groups.php
Normal file
227
temp/nexus-module-import/modules/kea/pages/groups.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
use App\Database;
|
||||
use App\Repository\KeaHostMetadataRepository;
|
||||
use App\Repository\KeaHostRepository;
|
||||
|
||||
$module = modules()->get('kea');
|
||||
$settings = modules()->settings('kea');
|
||||
$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;
|
||||
$fallback = $module['db_defaults'] ?? [];
|
||||
$error = null;
|
||||
$notice = null;
|
||||
$groups = [];
|
||||
$availableIpsByGroup = [];
|
||||
$usedIps = [];
|
||||
|
||||
try {
|
||||
if (empty($metadataConfig['driver']) || empty($metadataConfig['dbname'])) {
|
||||
throw new RuntimeException('Nexus DHCP Zusatzdatenbank ist nicht konfiguriert.');
|
||||
}
|
||||
|
||||
$metadataRepo = new KeaHostMetadataRepository(Database::createFromArray($metadataConfig));
|
||||
$metadataRepo->ensureSchema();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = (string)($_POST['action'] ?? '');
|
||||
if ($action === 'save_group') {
|
||||
$metadataRepo->saveGroup(
|
||||
(string)($_POST['name'] ?? ''),
|
||||
(string)($_POST['description'] ?? ''),
|
||||
(string)($_POST['parent_name'] ?? '')
|
||||
);
|
||||
$notice = 'Gruppe gespeichert.';
|
||||
} elseif ($action === 'add_range') {
|
||||
$metadataRepo->addRange(
|
||||
(string)($_POST['group_name'] ?? ''),
|
||||
(string)($_POST['start_ip'] ?? ''),
|
||||
(string)($_POST['end_ip'] ?? '')
|
||||
);
|
||||
$notice = 'IP-Bereich gespeichert.';
|
||||
}
|
||||
}
|
||||
|
||||
$groups = $metadataRepo->listGroupsWithRanges();
|
||||
$keaRepo = new KeaHostRepository(modules()->modulePdo('kea', $fallback), $metadataRepo);
|
||||
$usedIps = array_merge($keaRepo->usedIpAddresses(), $metadataRepo->desiredIps());
|
||||
$availableIpsByGroup = $metadataRepo->availableIpsByGroup($usedIps, 4096);
|
||||
} catch (Throwable $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
|
||||
$usedIpLookup = array_flip(array_filter(array_map('strval', $usedIps)));
|
||||
$matrixForGroup = static function (array $group) use ($usedIpLookup): array {
|
||||
$dots = [];
|
||||
foreach (($group['ranges'] ?? []) as $range) {
|
||||
$start = ip2long((string)($range['start_ip'] ?? ''));
|
||||
$end = ip2long((string)($range['end_ip'] ?? ''));
|
||||
if ($start === false || $end === false) {
|
||||
continue;
|
||||
}
|
||||
for ($ip = $start; $ip <= $end && count($dots) < 512; $ip++) {
|
||||
$address = long2ip($ip);
|
||||
if ($address === false) {
|
||||
continue;
|
||||
}
|
||||
$dots[] = [
|
||||
'ip' => $address,
|
||||
'used' => isset($usedIpLookup[$address]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $dots;
|
||||
};
|
||||
?>
|
||||
<?= module_shell_header('kea', [
|
||||
'title' => 'KEA Gruppen',
|
||||
]) ?>
|
||||
<div class="module-flow kea-page">
|
||||
<section class="module-box">
|
||||
<div class="module-box-head">
|
||||
<div>
|
||||
<h2 class="module-box-title">KEA Gruppen</h2>
|
||||
<p>Gruppen und IP-Bereiche fuer DHCP-Reservierungen.</p>
|
||||
</div>
|
||||
<a class="module-button module-button--secondary" href="/module/kea">Zurueck</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="kea-message kea-message--error" role="alert">
|
||||
<strong>Fehler</strong>
|
||||
<p><?= e($error) ?></p>
|
||||
</div>
|
||||
<?php elseif ($notice): ?>
|
||||
<div class="kea-message kea-message--success"><?= e($notice) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="module-box kea-panel">
|
||||
<div class="module-box-head kea-panel__head">
|
||||
<div>
|
||||
<span class="pill">Gruppe</span>
|
||||
<h2 class="module-box-title">Gruppe anlegen</h2>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="kea-edit-form">
|
||||
<input type="hidden" name="action" value="save_group">
|
||||
<label class="setup-field">
|
||||
<span>Name</span>
|
||||
<input type="text" name="name" required>
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>Beschreibung</span>
|
||||
<input type="text" name="description">
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>Uebergeordnete Gruppe</span>
|
||||
<select name="parent_name">
|
||||
<option value="">Keine</option>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<option value="<?= e((string)$group['name']) ?>"><?= e((string)$group['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<div class="setup-actions kea-edit-form__wide">
|
||||
<button class="cta-button" type="submit">Gruppe speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="module-box kea-panel">
|
||||
<div class="module-box-head kea-panel__head">
|
||||
<div>
|
||||
<span class="pill">IP-Bereich</span>
|
||||
<h2 class="module-box-title">Bereich zuweisen</h2>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="kea-edit-form">
|
||||
<input type="hidden" name="action" value="add_range">
|
||||
<label class="setup-field">
|
||||
<span>Gruppe</span>
|
||||
<select name="group_name" required>
|
||||
<option value="">Bitte waehlen</option>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<option value="<?= e((string)$group['name']) ?>"><?= e((string)$group['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>Start-IP</span>
|
||||
<input type="text" name="start_ip" placeholder="192.168.178.50" required>
|
||||
</label>
|
||||
<label class="setup-field">
|
||||
<span>End-IP</span>
|
||||
<input type="text" name="end_ip" placeholder="192.168.178.99" required>
|
||||
</label>
|
||||
<div class="setup-actions kea-edit-form__wide">
|
||||
<button class="cta-button" type="submit">Bereich speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="module-box-table kea-panel">
|
||||
<div class="module-box-head kea-panel__head">
|
||||
<div>
|
||||
<span class="pill">Uebersicht</span>
|
||||
<h2 class="module-box-title">Gruppen und freie IPs</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kea-table-wrap">
|
||||
<table class="kea-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gruppe</th>
|
||||
<th>Untergruppe von</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Bereiche</th>
|
||||
<th>Freie IPs</th>
|
||||
<th>Matrix</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if ($groups === []): ?>
|
||||
<tr><td colspan="6" class="kea-empty">Noch keine Gruppen definiert.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<?php $available = $availableIpsByGroup[(string)$group['name']] ?? []; ?>
|
||||
<tr>
|
||||
<td><?= e((string)$group['name']) ?></td>
|
||||
<td><?= e((string)($group['parent_name'] ?: '-')) ?></td>
|
||||
<td><?= e((string)($group['description'] ?? '-')) ?></td>
|
||||
<td>
|
||||
<?php if (($group['ranges'] ?? []) === []): ?>
|
||||
<span class="muted">Kein Bereich</span>
|
||||
<?php else: ?>
|
||||
<?php foreach ($group['ranges'] as $range): ?>
|
||||
<div class="mono"><?= e((string)$range['start_ip']) ?> - <?= e((string)$range['end_ip']) ?></div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= e((string)count($available)) ?></td>
|
||||
<td>
|
||||
<?php $matrix = $matrixForGroup($group); ?>
|
||||
<?php if ($matrix === []): ?>
|
||||
<span class="muted">Kein Bereich</span>
|
||||
<?php else: ?>
|
||||
<div class="ip-matrix" aria-label="IP Matrix fuer <?= e((string)$group['name']) ?>">
|
||||
<?php foreach ($matrix as $dot): ?>
|
||||
<span
|
||||
class="ip-dot <?= $dot['used'] ? 'is-used' : 'is-free' ?>"
|
||||
title="<?= e((string)$dot['ip']) ?> · <?= $dot['used'] ? 'belegt' : 'frei' ?>"
|
||||
></span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<?= module_shell_footer() ?>
|
||||
62
temp/nexus-module-import/modules/kea/pages/index.php
Normal file
62
temp/nexus-module-import/modules/kea/pages/index.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
use App\Database;
|
||||
use App\Repository\KeaHostRepository;
|
||||
use App\Repository\KeaHostMetadataRepository;
|
||||
|
||||
$module = modules()->get('kea');
|
||||
$fallback = $module['db_defaults'] ?? [];
|
||||
|
||||
$settings = modules()->settings('kea');
|
||||
$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;
|
||||
$metadataRepo = null;
|
||||
$hosts = [];
|
||||
$error = null;
|
||||
$warnings = [];
|
||||
$stats = [
|
||||
'total' => 0,
|
||||
'reservations' => 0,
|
||||
'leases' => 0,
|
||||
'groups' => [],
|
||||
'free_ips' => [],
|
||||
];
|
||||
|
||||
try {
|
||||
$pdo = modules()->modulePdo('kea', $fallback);
|
||||
if (!empty($metadataConfig['driver']) && !empty($metadataConfig['dbname'])) {
|
||||
try {
|
||||
$metadataRepo = new KeaHostMetadataRepository(Database::createFromArray($metadataConfig));
|
||||
$metadataRepo->ensureSchema();
|
||||
} catch (\Throwable $e) {
|
||||
$warnings[] = 'Nexus DHCP Zusatzdatenbank nicht verfuegbar: ' . $e->getMessage();
|
||||
$metadataRepo = null;
|
||||
}
|
||||
}
|
||||
|
||||
$repo = new KeaHostRepository($pdo, $metadataRepo);
|
||||
$hosts = $repo->findAll(200);
|
||||
$stats['reservations'] = $repo->countReservations();
|
||||
$stats['leases'] = $repo->countLeases();
|
||||
$stats['total'] = $stats['reservations'] + $stats['leases'];
|
||||
foreach ($hosts as $host) {
|
||||
$group = trim((string)($host['metadata']['group_name'] ?? ''));
|
||||
if ($group !== '') {
|
||||
$stats['groups'][$group] = ($stats['groups'][$group] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
if ($metadataRepo !== null) {
|
||||
$stats['free_ips'] = array_map(
|
||||
static fn(array $ips): int => count($ips),
|
||||
$metadataRepo->availableIpsByGroup(
|
||||
array_merge($repo->usedIpAddresses(), $metadataRepo->desiredIps()),
|
||||
4096
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$error = "Datenbankfehler: " . $e->getMessage();
|
||||
}
|
||||
|
||||
module_tpl('kea', 'dashboard', compact('hosts', 'error', 'warnings', 'stats'));
|
||||
231
temp/nexus-module-import/modules/kea/partials/dashboard.php
Normal file
231
temp/nexus-module-import/modules/kea/partials/dashboard.php
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
/**
|
||||
* @var array $hosts Die Liste der KEA-Hosts.
|
||||
* @var string|null $error Eine Fehlermeldung, falls vorhanden.
|
||||
* @var array $warnings Hinweise, falls Zusatzdaten nicht geladen werden konnten.
|
||||
* @var array $stats Kennzahlen fuer die Uebersicht.
|
||||
*/
|
||||
?>
|
||||
<?= module_shell_header('kea', [
|
||||
'title' => 'KEA DHCP Hosts',
|
||||
'description' => 'Reservierungen und aktuelle Leases aus der KEA-Datenbank.',
|
||||
]) ?>
|
||||
<div class="module-flow kea-page">
|
||||
<section class="module-box">
|
||||
<div class="module-box-head">
|
||||
<div>
|
||||
<h2 class="module-box-title">KEA DHCP Hosts</h2>
|
||||
<p>Reservierungen und aktuelle Leases aus der KEA-Datenbank.</p>
|
||||
<p class="muted kea-refresh-state" data-kea-refresh-state>
|
||||
Automatische Aktualisierung alle 5 Sekunden.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="kea-message kea-message--error" role="alert">
|
||||
<strong>Fehler</strong>
|
||||
<p><?= e($error) ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach (($warnings ?? []) as $warning): ?>
|
||||
<div class="kea-message kea-message--warning" role="alert">
|
||||
<p><?= e((string)$warning) ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<div class="module-box-grid module-box-grid--stats stats">
|
||||
<section class="module-box-soft stat-card">
|
||||
<span class="stat-label">Einträge</span>
|
||||
<span class="stat-value" data-kea-stat="total"><?= e((string)($stats['total'] ?? 0)) ?></span>
|
||||
</section>
|
||||
<section class="module-box-soft stat-card">
|
||||
<span class="stat-label">Reservierungen</span>
|
||||
<span class="stat-value" data-kea-stat="reservations"><?= e((string)($stats['reservations'] ?? 0)) ?></span>
|
||||
</section>
|
||||
<section class="module-box-soft stat-card">
|
||||
<span class="stat-label">Leases</span>
|
||||
<span class="stat-value" data-kea-stat="leases"><?= e((string)($stats['leases'] ?? 0)) ?></span>
|
||||
</section>
|
||||
<section class="module-box-soft stat-card">
|
||||
<span class="stat-label">Gruppen</span>
|
||||
<span class="stat-value" data-kea-stat="groups"><?= e((string)count($stats['groups'] ?? [])) ?></span>
|
||||
</section>
|
||||
<section class="module-box-soft stat-card">
|
||||
<span class="stat-label">Freie Gruppen-IPs</span>
|
||||
<span class="stat-value" data-kea-stat="free_ips"><?= e((string)array_sum($stats['free_ips'] ?? [])) ?></span>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="module-box-table kea-panel">
|
||||
<div class="module-box-head kea-panel__head">
|
||||
<div>
|
||||
<span class="pill">Inventar</span>
|
||||
<h2 class="module-box-title">Registrierte Geräte</h2>
|
||||
<p class="muted">Zusatzdaten werden in der separaten Nexus-DHCP-Datenbank gespeichert.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kea-table-wrap">
|
||||
<table class="kea-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Quelle</th>
|
||||
<th>Hostname</th>
|
||||
<th>IP Adresse</th>
|
||||
<th>MAC Adresse</th>
|
||||
<th>Zuletzt gesehen</th>
|
||||
<th>Lease bis</th>
|
||||
<th>Echter Name</th>
|
||||
<th>Standort</th>
|
||||
<th>Gruppe</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-kea-host-rows>
|
||||
<?php if (empty($hosts)): ?>
|
||||
<tr>
|
||||
<td colspan="10" class="kea-empty">
|
||||
Keine Reservierungen oder aktiven Leases gefunden.
|
||||
</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($hosts as $host): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="pill"><?= ($host['source'] ?? '') === 'lease' ? 'Lease' : 'Reservierung' ?></span>
|
||||
</td>
|
||||
<td>
|
||||
<?= e((string)($host['metadata']['device_name'] ?? $host['metadata']['real_name'] ?? $host['display_name'] ?? $host['hostname'] ?? 'Unbekannt')) ?>
|
||||
</td>
|
||||
<td class="mono">
|
||||
<?= e($host['ipv4_address']) ?>
|
||||
</td>
|
||||
<td class="mono">
|
||||
<?= e($host['dhcp_identifier']) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= e((string)($host['last_seen_at'] ?? '-')) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= e((string)($host['lease_expires_at'] ?? '-')) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= e((string)($host['metadata']['real_name'] ?? '-')) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= e((string)($host['metadata']['location'] ?? '-')) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= e((string)($host['metadata']['group_name'] ?? '-')) ?>
|
||||
</td>
|
||||
<td>
|
||||
<a class="nav-link" href="/module/kea/edit?source=<?= e((string)($host['source'] ?? 'reservation')) ?>&id=<?= e((string)($host['host_id'] ?? '0')) ?>">
|
||||
Bearbeiten
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<script>
|
||||
(() => {
|
||||
const rowsTarget = document.querySelector('[data-kea-host-rows]');
|
||||
const stateTarget = document.querySelector('[data-kea-refresh-state]');
|
||||
if (!rowsTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
const setText = (selector, value) => {
|
||||
const target = document.querySelector(selector);
|
||||
if (target) {
|
||||
target.textContent = String(value ?? '0');
|
||||
}
|
||||
};
|
||||
|
||||
const cell = (value, className = '') => {
|
||||
const td = document.createElement('td');
|
||||
if (className) {
|
||||
td.className = className;
|
||||
}
|
||||
td.textContent = value || '-';
|
||||
return td;
|
||||
};
|
||||
|
||||
const renderRows = (rows) => {
|
||||
rowsTarget.textContent = '';
|
||||
if (!Array.isArray(rows) || rows.length === 0) {
|
||||
const tr = document.createElement('tr');
|
||||
const td = cell('Keine Reservierungen oder aktiven Leases gefunden.', 'kea-empty');
|
||||
td.colSpan = 10;
|
||||
tr.appendChild(td);
|
||||
rowsTarget.appendChild(tr);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const row of rows) {
|
||||
const tr = document.createElement('tr');
|
||||
const source = document.createElement('td');
|
||||
const pill = document.createElement('span');
|
||||
pill.className = 'pill';
|
||||
pill.textContent = row.source === 'lease' ? 'Lease' : 'Reservierung';
|
||||
source.appendChild(pill);
|
||||
tr.appendChild(source);
|
||||
|
||||
tr.appendChild(cell(row.display_name));
|
||||
tr.appendChild(cell(row.ipv4_address, 'mono'));
|
||||
tr.appendChild(cell(row.dhcp_identifier, 'mono'));
|
||||
tr.appendChild(cell(row.last_seen_at));
|
||||
tr.appendChild(cell(row.lease_expires_at));
|
||||
tr.appendChild(cell(row.real_name));
|
||||
tr.appendChild(cell(row.location));
|
||||
tr.appendChild(cell(row.group_name));
|
||||
|
||||
const action = document.createElement('td');
|
||||
const link = document.createElement('a');
|
||||
link.className = 'nav-link';
|
||||
link.href = `/module/kea/edit?source=${encodeURIComponent(row.source || 'reservation')}&id=${encodeURIComponent(row.host_id || '0')}`;
|
||||
link.textContent = 'Bearbeiten';
|
||||
action.appendChild(link);
|
||||
tr.appendChild(action);
|
||||
|
||||
rowsTarget.appendChild(tr);
|
||||
}
|
||||
};
|
||||
|
||||
const refresh = async () => {
|
||||
try {
|
||||
const response = await fetch('/module/kea/data', {
|
||||
headers: {Accept: 'application/json'},
|
||||
cache: 'no-store',
|
||||
});
|
||||
const payload = await response.json();
|
||||
if (!response.ok || !payload.ok) {
|
||||
throw new Error(payload.error || 'Aktualisierung fehlgeschlagen.');
|
||||
}
|
||||
|
||||
setText('[data-kea-stat="total"]', payload.stats?.total);
|
||||
setText('[data-kea-stat="reservations"]', payload.stats?.reservations);
|
||||
setText('[data-kea-stat="leases"]', payload.stats?.leases);
|
||||
setText('[data-kea-stat="groups"]', payload.stats?.groups);
|
||||
setText('[data-kea-stat="free_ips"]', payload.stats?.free_ips);
|
||||
renderRows(payload.rows);
|
||||
if (stateTarget) {
|
||||
stateTarget.textContent = `Automatische Aktualisierung aktiv. Zuletzt aktualisiert: ${payload.updated_at || '-'}`;
|
||||
}
|
||||
} catch (error) {
|
||||
if (stateTarget) {
|
||||
stateTarget.textContent = `Automatische Aktualisierung fehlgeschlagen: ${error.message}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.setInterval(refresh, 5000);
|
||||
})();
|
||||
</script>
|
||||
<?= module_shell_footer() ?>
|
||||
Reference in New Issue
Block a user