diff --git a/modules/pihole/module.json b/modules/pihole/module.json index 6d5409b..f125f8a 100644 --- a/modules/pihole/module.json +++ b/modules/pihole/module.json @@ -4,6 +4,20 @@ "description": "Pi-hole Monitoring, Listen und Steuerung fuer mehrere Instanzen.", "setup": { "fields": [ + { + "name": "api_timeout_sec", + "label": "API Timeout Standard (Sekunden)", + "type": "number", + "help": "Timeout fuer normale Pi-hole API-Abfragen wie Dashboard und Queries. Standard: 8 Sekunden.", + "required": false + }, + { + "name": "action_timeout_sec", + "label": "API Timeout Aktionen (Sekunden)", + "type": "number", + "help": "Timeout fuer laengere Aktionen wie Listen- oder Pi-hole-Updates. Standard: 120 Sekunden.", + "required": false + }, { "name": "dashboard_refresh_sec", "label": "Refresh Dashboard (Sekunden)", diff --git a/modules/pihole/pages/api.php b/modules/pihole/pages/api.php index 4a08703..a55b8e6 100644 --- a/modules/pihole/pages/api.php +++ b/modules/pihole/pages/api.php @@ -5,6 +5,7 @@ header('Content-Type: application/json; charset=utf-8'); $action = (string)($_GET['action'] ?? ''); $instances = module_fn('pihole', 'instances'); $listsPrimaryOnly = module_fn('pihole', 'lists_primary_only'); +$moduleSettings = modules()->settings('pihole'); $body = file_get_contents('php://input'); $payload = []; @@ -193,7 +194,7 @@ $httpRequest = function (string $method, string $url, array $headers, ?string $b return ['ok' => true, 'data' => $data, 'http_code' => $httpCode, 'url' => $url]; }; -$v5Request = function (array $instance, array $params) use ($normalizeApiPath, $httpRequest): array { +$v5Request = function (array $instance, array $params, ?int $timeoutOverride = null) use ($normalizeApiPath, $httpRequest): array { if (!empty($instance['password']) && !isset($params['auth'])) { $params['auth'] = $instance['password']; } @@ -202,7 +203,7 @@ $v5Request = function (array $instance, array $params) use ($normalizeApiPath, $ $qs = http_build_query($params, '', '&', PHP_QUERY_RFC3986); $full = $qs !== '' ? $url . '?' . $qs : $url; - $timeout = (int)($instance['timeout'] ?? 8); + $timeout = $timeoutOverride ?? (int)($instance['timeout'] ?? 8); if ($timeout <= 0) { $timeout = 8; } @@ -269,11 +270,11 @@ $v6Auth = function (array $instance) use ($httpRequest, $readSessionCache, $writ return $res; }; -$v6Request = function (array $instance, string $path, string $method, array $payload, string $sid) use ($httpRequest, $clearSessionCache, $touchSessionCache): array { +$v6Request = function (array $instance, string $path, string $method, array $payload, string $sid, ?int $timeoutOverride = null) use ($httpRequest, $clearSessionCache, $touchSessionCache): array { $base = rtrim((string)$instance['url'], '/'); $path = ltrim($path, '/'); $url = $base . '/api/' . $path; - $timeout = (int)($instance['timeout'] ?? 8); + $timeout = $timeoutOverride ?? (int)($instance['timeout'] ?? 8); if ($timeout <= 0) { $timeout = 8; } @@ -306,10 +307,10 @@ $v6Request = function (array $instance, string $path, string $method, array $pay return $result; }; -$v6RequestAny = function (array $instance, array $paths, string $method, array $payload, string $sid) use ($v6Request): array { +$v6RequestAny = function (array $instance, array $paths, string $method, array $payload, string $sid, ?int $timeoutOverride = null) use ($v6Request): array { $last = ['ok' => false, 'error' => 'no_path', 'http_code' => 0, 'url' => '']; foreach ($paths as $path) { - $result = $v6Request($instance, (string)$path, $method, $payload, $sid); + $result = $v6Request($instance, (string)$path, $method, $payload, $sid, $timeoutOverride); if (($result['ok'] ?? false) === true) { return $result; } @@ -405,6 +406,11 @@ $resolvePrimaryId = function () use ($instances): ?string { return $first !== null ? (string)$first : null; }; +$resolveActionTimeout = static function () use ($moduleSettings): int { + $timeout = (int)($moduleSettings['action_timeout_sec'] ?? 120); + return $timeout > 0 ? $timeout : 120; +}; + $pickInstances = function (string $target) use ($instances, $resolvePrimaryId): array { if ($target === 'all') { return $instances; @@ -1024,6 +1030,7 @@ if ($action === 'enable') { if ($action === 'gravity') { require_admin(); $debugPush('gravity.request', ['payload' => $payload]); + $actionTimeout = $resolveActionTimeout(); $target = $listsPrimaryOnly ? 'primary' : (string)($payload['instance'] ?? 'primary'); $targets = $pickInstances($target); if (!$targets) { @@ -1034,9 +1041,9 @@ if ($action === 'gravity') { $apiInfo = $detectApi($instance); if ($apiInfo['version'] === 6) { $sid = (string)($apiInfo['sid'] ?? ''); - $result = $v6Request($instance, 'action/gravity', 'POST', [], $sid); + $result = $v6Request($instance, 'action/gravity', 'POST', [], $sid, $actionTimeout); } elseif ($apiInfo['version'] === 5) { - $result = $v5Request($instance, ['updateGravity' => 1]); + $result = $v5Request($instance, ['updateGravity' => 1], $actionTimeout); } else { $result = ['ok' => false, 'error' => 'unknown_api', 'http_code' => 0, 'url' => '']; } @@ -1112,6 +1119,7 @@ if ($action === 'adlist_add') { if ($action === 'update') { require_admin(); $debugPush('update.request', ['payload' => $payload]); + $actionTimeout = $resolveActionTimeout(); $target = (string)($payload['instance'] ?? 'primary'); $targets = $pickInstances($target); if (!$targets) { @@ -1124,7 +1132,7 @@ if ($action === 'update') { if ($apiInfo['version'] === 6) { $result = ['ok' => false, 'error' => 'not_supported_v6', 'http_code' => 400, 'url' => '']; } elseif ($apiInfo['version'] === 5) { - $result = $v5Request($instance, ['update' => 1]); + $result = $v5Request($instance, ['update' => 1], $actionTimeout); } else { $result = ['ok' => false, 'error' => 'unknown_api', 'http_code' => 0, 'url' => '']; }