asdasd
This commit is contained in:
260
custom/apps/pihole/pages/index.php
Normal file
260
custom/apps/pihole/pages/index.php
Normal file
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once dirname(__DIR__, 3) . '/src/App/bootstrap.php';
|
||||
require_once dirname(__DIR__) . '/bootstrap.php';
|
||||
|
||||
use ModulesCore\ModuleHttp;
|
||||
|
||||
session_start();
|
||||
|
||||
$projectRoot = dirname(__DIR__, 3);
|
||||
ModuleHttp::requireDesktopAccess($projectRoot);
|
||||
$isPartial = !empty($_GET['partial']);
|
||||
|
||||
$view = trim((string) ($_GET['view'] ?? 'dashboard'));
|
||||
$allowedViews = ['dashboard', 'lists', 'queries', 'instances', 'setup'];
|
||||
if (!in_array($view, $allowedViews, true)) {
|
||||
$view = 'dashboard';
|
||||
}
|
||||
|
||||
$viewMeta = [
|
||||
'dashboard' => [
|
||||
'label' => 'Dashboard',
|
||||
'title' => 'Pi-hole Dashboard',
|
||||
'description' => 'Status, Blockings, Usage und direkte Steuerung fuer alle angebundenen Instanzen.',
|
||||
'nav_description' => 'Live-Status, Sperren und aggregierte DNS-Zahlen.',
|
||||
],
|
||||
'lists' => [
|
||||
'label' => 'Listen',
|
||||
'title' => 'Listen & Domains',
|
||||
'description' => 'Gravity-Updates, Top-Domains und neue Listen-/Domain-Eintraege.',
|
||||
'nav_description' => 'Blocklisten, Whitelist und Adlist-Pflege.',
|
||||
],
|
||||
'queries' => [
|
||||
'label' => 'Queries',
|
||||
'title' => 'Zugriffe & Blockings',
|
||||
'description' => 'Aktuelle Blockings und Top-Clients ueber alle Instanzen.',
|
||||
'nav_description' => 'Abfragen, blockierte Domains und Client-Nutzung.',
|
||||
],
|
||||
'instances' => [
|
||||
'label' => 'Instanzen',
|
||||
'title' => 'Pi-hole Instanzen',
|
||||
'description' => 'Hosts, Zugangsdaten und Primaer-Instanz administrieren.',
|
||||
'nav_description' => 'Instanzen anlegen, testen, bearbeiten und loeschen.',
|
||||
],
|
||||
'setup' => [
|
||||
'label' => 'Setup',
|
||||
'title' => 'Setup',
|
||||
'description' => 'Timeouts, API-Pfad und globale Pi-hole-Moduloptionen verwalten.',
|
||||
'nav_description' => 'Standard-Timeouts, TLS und Refresh-Regeln.',
|
||||
],
|
||||
];
|
||||
|
||||
$assetVersion = static function (string $relativePath): string {
|
||||
$fullPath = dirname(__DIR__) . '/' . ltrim($relativePath, '/');
|
||||
$mtime = is_file($fullPath) ? filemtime($fullPath) : false;
|
||||
|
||||
return $mtime === false ? '0' : (string) $mtime;
|
||||
};
|
||||
|
||||
$renderSetup = static function (): void {
|
||||
$moduleName = 'pihole';
|
||||
$manifestPath = dirname(__DIR__) . '/module.json';
|
||||
$manifestRaw = is_file($manifestPath) ? file_get_contents($manifestPath) : false;
|
||||
$manifest = is_string($manifestRaw) && trim($manifestRaw) !== '' ? json_decode($manifestRaw, true) : [];
|
||||
$fields = is_array($manifest['setup']['fields'] ?? null) ? $manifest['setup']['fields'] : [];
|
||||
$settings = modules()->settings($moduleName);
|
||||
$notice = null;
|
||||
$error = null;
|
||||
|
||||
$getValue = static function (array $source, string $path): mixed {
|
||||
$value = $source;
|
||||
foreach (explode('.', $path) as $segment) {
|
||||
if (!is_array($value) || !array_key_exists($segment, $value)) {
|
||||
return null;
|
||||
}
|
||||
$value = $value[$segment];
|
||||
}
|
||||
|
||||
return $value;
|
||||
};
|
||||
|
||||
$setValue = static function (array &$target, string $path, mixed $value): void {
|
||||
$segments = explode('.', $path);
|
||||
$cursor = &$target;
|
||||
foreach ($segments as $index => $segment) {
|
||||
if ($index === count($segments) - 1) {
|
||||
$cursor[$segment] = $value;
|
||||
return;
|
||||
}
|
||||
if (!isset($cursor[$segment]) || !is_array($cursor[$segment])) {
|
||||
$cursor[$segment] = [];
|
||||
}
|
||||
$cursor = &$cursor[$segment];
|
||||
}
|
||||
};
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && (string) ($_POST['action'] ?? '') === 'save_setup') {
|
||||
try {
|
||||
$nextSettings = $settings;
|
||||
foreach ($fields as $field) {
|
||||
if (!is_array($field)) {
|
||||
continue;
|
||||
}
|
||||
$name = trim((string) ($field['name'] ?? ''));
|
||||
$type = trim((string) ($field['type'] ?? 'text'));
|
||||
if ($name === '') {
|
||||
continue;
|
||||
}
|
||||
$raw = $_POST['settings'][$name] ?? null;
|
||||
$value = match ($type) {
|
||||
'checkbox' => $raw !== null,
|
||||
'number' => ($raw === null || $raw === '') ? null : (is_numeric((string) $raw) ? 0 + $raw : null),
|
||||
default => is_string($raw) ? trim($raw) : '',
|
||||
};
|
||||
$setValue($nextSettings, $name, $value);
|
||||
}
|
||||
modules()->saveSettings($moduleName, $nextSettings);
|
||||
$settings = modules()->settings($moduleName);
|
||||
$notice = 'Setup gespeichert.';
|
||||
} catch (\Throwable $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="pihole-content">
|
||||
<?php if ($error): ?>
|
||||
<section class="window-app-card"><div class="pihole-message pihole-message--error"><?= e($error) ?></div></section>
|
||||
<?php elseif ($notice): ?>
|
||||
<section class="window-app-card"><div class="pihole-message pihole-message--success"><?= e($notice) ?></div></section>
|
||||
<?php endif; ?>
|
||||
<section class="window-app-card">
|
||||
<div class="pihole-section-header">
|
||||
<div>
|
||||
<h3 class="pihole-section-title">Modul-Setup</h3>
|
||||
<p class="window-app-copy">Bestehende Nexus-Einstellungen werden uebernommen. Aenderungen werden lokal als Override gespeichert.</p>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="pihole-setup-form">
|
||||
<input type="hidden" name="action" value="save_setup">
|
||||
<div class="pihole-setup-grid">
|
||||
<?php foreach ($fields as $field): ?>
|
||||
<?php
|
||||
if (!is_array($field)) {
|
||||
continue;
|
||||
}
|
||||
$name = trim((string) ($field['name'] ?? ''));
|
||||
$label = trim((string) ($field['label'] ?? $name));
|
||||
$type = trim((string) ($field['type'] ?? 'text'));
|
||||
$help = trim((string) ($field['help'] ?? ''));
|
||||
$value = $getValue($settings, $name);
|
||||
?>
|
||||
<label class="pihole-field">
|
||||
<span><?= e($label) ?></span>
|
||||
<?php if ($type === 'checkbox'): ?>
|
||||
<input type="checkbox" name="settings[<?= e($name) ?>]" value="1" <?= !empty($value) ? 'checked' : '' ?>>
|
||||
<?php elseif ($type === 'number'): ?>
|
||||
<input type="number" step="any" name="settings[<?= e($name) ?>]" value="<?= e(is_scalar($value) ? (string) $value : '') ?>">
|
||||
<?php else: ?>
|
||||
<input type="<?= $type === 'password' ? 'password' : 'text' ?>" name="settings[<?= e($name) ?>]" value="<?= e(is_scalar($value) ? (string) $value : '') ?>">
|
||||
<?php endif; ?>
|
||||
<?php if ($help !== ''): ?>
|
||||
<small class="window-app-meta"><?= e($help) ?></small>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="pihole-actions">
|
||||
<button class="window-app-nav-button pihole-primary-action" type="submit">Setup speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
<?php
|
||||
};
|
||||
|
||||
$renderContent = static function () use ($view, $renderSetup, $viewMeta): void {
|
||||
$currentViewMeta = $viewMeta[$view] ?? $viewMeta['dashboard'];
|
||||
$navItems = [
|
||||
'dashboard' => '/apps/pihole/index.php?view=dashboard',
|
||||
'lists' => '/apps/pihole/index.php?view=lists',
|
||||
'queries' => '/apps/pihole/index.php?view=queries',
|
||||
'instances' => '/apps/pihole/index.php?view=instances',
|
||||
'setup' => '/apps/pihole/index.php?view=setup',
|
||||
];
|
||||
|
||||
ob_start();
|
||||
if ($view === 'setup') {
|
||||
$renderSetup();
|
||||
} else {
|
||||
require dirname(__DIR__) . '/partials/' . $view . '.php';
|
||||
}
|
||||
$innerContent = (string) ob_get_clean();
|
||||
?>
|
||||
<div class="pihole-module-shell window-app-shell">
|
||||
<div class="window-app-frame pihole-frame">
|
||||
<aside class="window-app-sidebar pihole-sidebar">
|
||||
<div class="window-app-brand pihole-brand">
|
||||
<p class="window-app-kicker">Modul</p>
|
||||
<h1>Pi-hole</h1>
|
||||
<p class="window-app-copy">Pi-hole Monitoring, Listen und Steuerung fuer mehrere Instanzen im Desktop-Standardlayout.</p>
|
||||
</div>
|
||||
<div class="window-app-nav-list pihole-nav-list">
|
||||
<?php foreach ($navItems as $navView => $href): ?>
|
||||
<?php $meta = $viewMeta[$navView] ?? null; ?>
|
||||
<?php if (!$meta): continue; endif; ?>
|
||||
<a class="window-app-nav-button pihole-nav-button<?= $view === $navView ? ' is-active' : '' ?>" href="<?= e($href) ?>">
|
||||
<strong><?= e((string) $meta['label']) ?></strong>
|
||||
<span class="window-app-meta"><?= e((string) $meta['nav_description']) ?></span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="window-app-main pihole-main">
|
||||
<div class="window-app-panel pihole-panel-shell">
|
||||
<section class="window-app-hero pihole-hero">
|
||||
<div>
|
||||
<p class="window-app-kicker">Pi-hole</p>
|
||||
<h2 class="window-app-title"><?= e((string) $currentViewMeta['title']) ?></h2>
|
||||
<p class="window-app-copy"><?= e((string) $currentViewMeta['description']) ?></p>
|
||||
</div>
|
||||
<div class="window-app-pill-row pihole-pill-row">
|
||||
<span class="window-app-pill">Desktop-App</span>
|
||||
<span class="window-app-pill">Netzwerk-Tools</span>
|
||||
<span class="window-app-pill">View: <?= e((string) $currentViewMeta['label']) ?></span>
|
||||
</div>
|
||||
</section>
|
||||
<?= $innerContent ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
};
|
||||
|
||||
ob_start();
|
||||
$renderContent();
|
||||
$pageContent = (string) ob_get_clean();
|
||||
|
||||
if ($isPartial) {
|
||||
echo $pageContent;
|
||||
return;
|
||||
}
|
||||
?><!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Pi-hole</title>
|
||||
<link rel="stylesheet" href="/assets/desktop/desktop.css">
|
||||
<link rel="stylesheet" href="/module-assets/index.php?module=pihole&path=assets/pihole.css&v=<?= htmlspecialchars($assetVersion('assets/pihole.css'), ENT_QUOTES) ?>">
|
||||
</head>
|
||||
<body>
|
||||
<?= $pageContent ?>
|
||||
|
||||
<script src="/module-assets/index.php?module=pihole&path=assets/pihole.js&v=<?= htmlspecialchars($assetVersion('assets/pihole.js'), ENT_QUOTES) ?>"></script>
|
||||
<script src="/module-assets/index.php?module=pihole&path=assets/pihole_instances.js&v=<?= htmlspecialchars($assetVersion('assets/pihole_instances.js'), ENT_QUOTES) ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user