Files
nexus/modules/pihole/pages/instances.php
2026-03-09 02:24:29 +01:00

225 lines
8.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
$moduleName = 'pihole';
$assets = app()->assets();
$assets->addStyle('/module/pihole/asset?file=pihole.css');
$assets->addScript('/module/pihole/asset?file=pihole_instances.js', 'footer', true);
require_admin();
$settings = modules()->settings($moduleName);
$notice = null;
$error = null;
$loadInstances = function (array $settings): array {
$instances = [];
$rawJson = trim((string)($settings['instances_json'] ?? ''));
if ($rawJson !== '') {
$decoded = json_decode($rawJson, true);
if (is_array($decoded)) {
foreach ($decoded as $row) {
if (!is_array($row)) {
continue;
}
$id = trim((string)($row['id'] ?? ''));
$url = trim((string)($row['url'] ?? ''));
if ($id === '' || $url === '') {
continue;
}
$instances[$id] = [
'id' => $id,
'name' => trim((string)($row['name'] ?? '')) ?: $id,
'url' => $url,
'token' => trim((string)($row['token'] ?? '')),
'is_primary' => !empty($row['is_primary']),
];
}
}
}
if (!$instances) {
foreach (['primary', 'secondary'] as $key) {
$url = trim((string)($settings[$key . '_url'] ?? ''));
if ($url === '') {
continue;
}
$instances[$key] = [
'id' => $key,
'name' => trim((string)($settings[$key . '_name'] ?? '')) ?: ($key === 'primary' ? 'Primaer' : 'Sekundaer'),
'url' => $url,
'token' => trim((string)($settings[$key . '_token'] ?? '')),
'is_primary' => $key === 'primary',
];
}
}
return $instances;
};
$instances = $loadInstances($settings);
$sanitizeId = function (string $id): string {
$id = preg_replace('/[^a-zA-Z0-9_-]/', '', $id);
return trim((string)$id);
};
$saveInstances = function (array $settings, array $instances): void {
$payload = $settings;
$payload['instances_json'] = json_encode(array_values($instances), JSON_UNESCAPED_UNICODE);
modules()->saveSettings('pihole', $payload);
};
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$deleteId = trim((string)($_POST['delete_id'] ?? ''));
$currentId = trim((string)($_POST['current_id'] ?? ''));
$instanceId = trim((string)($_POST['instance_id'] ?? ''));
$name = trim((string)($_POST['name'] ?? ''));
$url = trim((string)($_POST['url'] ?? ''));
$token = trim((string)($_POST['token'] ?? ''));
$isPrimary = isset($_POST['is_primary']);
if ($deleteId !== '') {
if (isset($instances[$deleteId])) {
unset($instances[$deleteId]);
$notice = 'Instanz geloescht.';
}
} else {
$instanceId = $sanitizeId($instanceId);
if ($instanceId === '' || $url === '') {
$error = 'Bitte ID und URL angeben.';
} else {
$existingToken = '';
if ($currentId !== '' && isset($instances[$currentId])) {
$existingToken = (string)($instances[$currentId]['token'] ?? '');
}
$tokenToStore = $token !== '' ? $token : $existingToken;
if ($currentId !== '' && $currentId !== $instanceId) {
unset($instances[$currentId]);
}
$instances[$instanceId] = [
'id' => $instanceId,
'name' => $name !== '' ? $name : $instanceId,
'url' => $url,
'token' => $tokenToStore,
'is_primary' => $isPrimary,
];
if ($isPrimary) {
foreach ($instances as $id => &$row) {
$row['is_primary'] = ($id === $instanceId);
}
unset($row);
$settings['primary_id'] = $instanceId;
}
$notice = $currentId !== '' ? 'Instanz aktualisiert.' : 'Instanz gespeichert.';
}
}
if (!$error) {
$saveInstances($settings, $instances);
$settings = modules()->settings($moduleName);
$instances = $loadInstances($settings);
}
}
$primaryId = trim((string)($settings['primary_id'] ?? ''));
if ($primaryId === '') {
foreach ($instances as $id => $row) {
if (!empty($row['is_primary'])) {
$primaryId = $id;
break;
}
}
}
?>
<div class="card">
<div class="pill">Pi-hole</div>
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; margin-top:.75rem;">
<h1 style="margin:0;">Instanzen</h1>
<div style="display:flex; gap:10px; flex-wrap:wrap;">
<button class="cta-button" type="button" data-instance-new>+ Neue Instanz</button>
</div>
</div>
<p class="muted">Pi-hole Instanzen hinzufuegen, bearbeiten und loeschen.</p>
<?php if ($error): ?>
<div class="card notice-card" style="margin-top:1rem; border-color:#ffb4a8; background:#fff5f3; color:#7a2114;">
<?= e($error) ?>
</div>
<?php elseif ($notice): ?>
<div class="card notice-card" style="margin-top:1rem; border-color:var(--accent-2);">
<?= e($notice) ?>
</div>
<?php endif; ?>
<div class="pihole-instance-grid" style="margin-top:1rem;">
<?php if (!$instances): ?>
<div class="card" style="padding:16px;">Keine Instanzen vorhanden.</div>
<?php else: ?>
<?php foreach ($instances as $instance): ?>
<div class="card pihole-instance-card"
data-instance-id="<?= e((string)$instance['id']) ?>"
data-name="<?= e((string)($instance['name'] ?? '')) ?>"
data-url="<?= e((string)($instance['url'] ?? '')) ?>"
data-primary="<?= !empty($instance['is_primary']) ? '1' : '0' ?>">
<div class="pihole-instance-header">
<div>
<strong><?= e((string)($instance['name'] ?? '')) ?></strong>
<div class="muted">ID: <?= e((string)($instance['id'] ?? '')) ?></div>
<div class="muted">URL: <?= e((string)($instance['url'] ?? '')) ?></div>
</div>
<?php if (!empty($instance['is_primary']) || $instance['id'] === $primaryId): ?>
<span class="pihole-status">Primaer</span>
<?php endif; ?>
</div>
<div class="pihole-card-actions">
<button class="nav-link" type="button" data-instance-edit>Bearbeiten</button>
<button class="nav-link" type="button" data-instance-test>Test Verbindung</button>
<form method="post" onsubmit="return confirm('Instanz wirklich loeschen?')">
<input type="hidden" name="delete_id" value="<?= e((string)($instance['id'] ?? '')) ?>">
<button class="nav-link" type="submit">Loeschen</button>
</form>
</div>
<div class="pihole-test-result" data-instance-result></div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<div class="modal" data-instance-modal aria-hidden="true">
<div class="modal-card">
<div class="modal-header">
<strong data-instance-modal-title>Neue Instanz</strong>
<button class="icon-button" type="button" data-instance-close>×</button>
</div>
<form method="post" class="form-grid" style="margin-top:.75rem;" data-instance-form>
<input type="hidden" name="current_id" value="">
<label class="form-field">
<span class="muted">ID</span>
<input type="text" name="instance_id" placeholder="z.B. pihole-main" required>
</label>
<label class="form-field">
<span class="muted">Name</span>
<input type="text" name="name" placeholder="z.B. Pi-hole Main" required>
</label>
<label class="form-field">
<span class="muted">URL</span>
<input type="text" name="url" placeholder="http://pi-hole.local" required>
</label>
<label class="form-field">
<span class="muted">API Token (leer lassen = unveraendert)</span>
<input type="password" name="token" placeholder="Token" autocomplete="new-password">
</label>
<label class="form-field" style="align-items:center;">
<span class="muted">Als Primaer verwenden</span>
<input type="checkbox" name="is_primary" value="1">
</label>
<div style="display:flex; gap:10px; flex-wrap:wrap;">
<button class="cta-button" type="submit" data-instance-submit>Speichern</button>
<button class="nav-link" type="button" data-instance-cancel>Zuruecksetzen</button>
</div>
</form>
</div>
</div>