dadas
All checks were successful
Deploy / deploy-production (push) Has been skipped
Deploy / deploy-staging (push) Successful in 6s

This commit is contained in:
2026-04-27 00:48:15 +02:00
parent 329304f1aa
commit d758ed0f49

View File

@@ -9,6 +9,143 @@ require_admin();
$settings = modules()->settings($moduleName);
$notice = null;
$error = null;
$testResults = [];
$httpRequest = static function (string $method, string $url, array $headers, ?string $body, bool $verify, int $timeout): array {
$raw = '';
$httpCode = 0;
$requestError = '';
if (function_exists('curl_init')) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => $timeout,
CURLOPT_SSL_VERIFYPEER => $verify,
CURLOPT_SSL_VERIFYHOST => $verify ? 2 : 0,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
]);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$raw = (string)curl_exec($ch);
if ($raw === '' && curl_errno($ch)) {
$requestError = curl_error($ch);
}
$httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
} else {
$ctx = stream_context_create([
'http' => [
'method' => $method,
'timeout' => $timeout,
'header' => implode("\r\n", $headers),
'content' => $body ?? '',
],
'ssl' => [
'verify_peer' => $verify,
'verify_peer_name' => $verify,
],
]);
$raw = (string)@file_get_contents($url, false, $ctx);
if ($raw === '') {
$requestError = 'HTTP request failed';
} else {
$httpCode = 200;
}
}
if ($requestError !== '') {
return ['ok' => false, 'http_code' => $httpCode, 'error' => $requestError, 'raw' => $raw, 'url' => $url];
}
$decoded = json_decode($raw, true);
if (!is_array($decoded)) {
return ['ok' => false, 'http_code' => $httpCode, 'error' => 'invalid_json', 'raw' => $raw, 'url' => $url];
}
return ['ok' => true, 'http_code' => $httpCode, 'data' => $decoded, 'url' => $url];
};
$runConnectionTest = static function (array $instance, array $settings) use ($httpRequest): array {
$verifyTls = !isset($settings['verify_tls']) || $settings['verify_tls'] === '1' || $settings['verify_tls'] === 1 || $settings['verify_tls'] === true;
$timeout = (int)($settings['api_timeout_sec'] ?? 8);
if ($timeout <= 0) {
$timeout = 8;
}
$url = rtrim((string)($instance['url'] ?? ''), '/');
$password = trim((string)($instance['password'] ?? ''));
$v6AuthUrl = $url . '/api/auth';
$v6Payload = json_encode(['password' => $password], JSON_UNESCAPED_UNICODE);
$v6Auth = $httpRequest('POST', $v6AuthUrl, ['Accept: application/json', 'Content-Type: application/json'], $v6Payload, $verifyTls, $timeout);
if ($v6Auth['ok']) {
$session = (array)(($v6Auth['data']['session'] ?? []) ?: []);
$sid = trim((string)($session['sid'] ?? ''));
if ($sid !== '') {
$probe = $httpRequest('GET', $url . '/api/stats/summary', ['Accept: application/json', 'X-FTL-SID: ' . $sid], null, $verifyTls, $timeout);
if ($probe['ok']) {
return [
'ok' => true,
'status' => 'ok',
'message' => 'Verbindung OK. API v6 antwortet.',
'version' => 6,
'details' => ['auth' => $v6Auth, 'probe' => $probe],
];
}
return [
'ok' => false,
'status' => 'error',
'message' => 'v6 Auth OK, aber Stats-Endpoint antwortet nicht sauber.',
'version' => 6,
'details' => ['auth' => $v6Auth, 'probe' => $probe],
];
}
}
$legacyUrl = $url . '/admin/api.php?summaryRaw';
if ($password !== '') {
$legacyUrl .= '&auth=' . rawurlencode($password);
}
$v5Probe = $httpRequest('GET', $legacyUrl, ['Accept: application/json'], null, $verifyTls, $timeout);
if ($v5Probe['ok']) {
return [
'ok' => true,
'status' => 'ok',
'message' => 'Verbindung OK. API v5 antwortet.',
'version' => 5,
'details' => ['auth' => $v6Auth, 'probe' => $v5Probe],
];
}
$status = 'error';
$message = 'Unbekannter Fehler.';
$httpCode = (int)($v6Auth['http_code'] ?? $v5Probe['http_code'] ?? 0);
$requestError = (string)($v6Auth['error'] ?? $v5Probe['error'] ?? 'error');
if ($httpCode === 0) {
$status = 'unreachable';
$message = 'Host nicht erreichbar oder kein HTTP-Response.';
} elseif ($httpCode === 401 || $httpCode === 403) {
$status = 'auth';
$message = 'Passwort oder App-Passwort falsch oder nicht berechtigt.';
} elseif ($requestError === 'invalid_json') {
$status = 'invalid';
$message = 'API antwortet nicht mit JSON. URL pruefen.';
} else {
$message = 'API Fehler: ' . $requestError . ' (HTTP ' . $httpCode . ')';
}
return [
'ok' => false,
'status' => $status,
'message' => $message,
'details' => ['auth' => $v6Auth, 'probe' => $v5Probe],
];
};
$loadInstances = function (array $settings): array {
$normalizeSecret = static function (array $row): string {
@@ -79,6 +216,7 @@ $saveInstances = function (array $settings, array $instances): void {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$deleteId = trim((string)($_POST['delete_id'] ?? ''));
$testId = trim((string)($_POST['test_id'] ?? ''));
$currentId = trim((string)($_POST['current_id'] ?? ''));
$instanceId = trim((string)($_POST['instance_id'] ?? ''));
$name = trim((string)($_POST['name'] ?? ''));
@@ -86,7 +224,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$password = trim((string)($_POST['password'] ?? ''));
$isPrimary = isset($_POST['is_primary']);
if ($deleteId !== '') {
if ($testId !== '') {
if (isset($instances[$testId])) {
module_debug_push('pihole', [
'label' => 'connection.test.start',
'instance_id' => $testId,
'instance_name' => (string)($instances[$testId]['name'] ?? $testId),
'url' => (string)($instances[$testId]['url'] ?? ''),
]);
$result = $runConnectionTest($instances[$testId], $settings);
$testResults[$testId] = $result;
module_debug_push('pihole', [
'label' => 'connection.test.result',
'instance_id' => $testId,
'instance_name' => (string)($instances[$testId]['name'] ?? $testId),
'result' => $result,
]);
$notice = $result['message'] ?? null;
} else {
$error = 'Test-Instanz nicht gefunden.';
}
} elseif ($deleteId !== '') {
if (isset($instances[$deleteId])) {
unset($instances[$deleteId]);
$notice = 'Instanz geloescht.';
@@ -189,13 +350,16 @@ if ($primaryId === '') {
</div>
<div class="pihole-card-actions">
<button class="module-button module-button--secondary module-button--small" type="button" data-instance-edit>Bearbeiten</button>
<button class="module-button module-button--secondary module-button--small" type="button" data-instance-test>Test Verbindung</button>
<form method="post">
<input type="hidden" name="test_id" value="<?= e((string)($instance['id'] ?? '')) ?>">
<button class="module-button module-button--secondary module-button--small" type="submit" data-instance-test>Test Verbindung</button>
</form>
<form method="post" onsubmit="return confirm('Instanz wirklich loeschen?')">
<input type="hidden" name="delete_id" value="<?= e((string)($instance['id'] ?? '')) ?>">
<button class="module-button module-button--secondary module-button--small" type="submit">Loeschen</button>
</form>
</div>
<div class="pihole-test-result" data-instance-result></div>
<div class="pihole-test-result<?= !empty($testResults[$instance['id']]['status']) ? ' is-' . e((string)$testResults[$instance['id']]['status']) : '' ?>" data-instance-result><?= e((string)($testResults[$instance['id']]['message'] ?? '')) ?></div>
</div>
<?php endforeach; ?>
<?php endif; ?>