$hostId, 'key_present' => $key !== '', 'key_prefix' => $key ? substr($key, 0, 8) . '...' : null, 'curl_available' => function_exists('curl_init'), 'allow_url_fopen'=> (bool)ini_get('allow_url_fopen'), 'debug_log' => null, 'probe' => null, 'probe_raw' => null, ]; // Show last debug log if present $debugFile = '/tmp/vv_api_debug.json'; if (file_exists($debugFile)) { $result['debug_log'] = json_decode(file_get_contents($debugFile), true); } // Run a minimal probe query if ($key) { $url = 'http://localhost/graphql'; $body = json_encode(['query' => '{ info { os { hostname } } }']); if (function_exists('curl_init')) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json', "x-api-key: {$key}"], CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_CONNECTTIMEOUT => 3, ]); $raw = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlErr = curl_error($ch); curl_close($ch); } else { $ctx = stream_context_create(['http' => [ 'method' => 'POST', 'header' => "Content-Type: application/json\r\nx-api-key: {$key}", 'content' => $body, 'timeout' => 5, 'ignore_errors' => true, ]]); $raw = @file_get_contents($url, false, $ctx); $httpCode = $raw !== false ? 200 : 0; $curlErr = ''; } $result['probe'] = [ 'url' => $url, 'http_code' => $httpCode, 'curl_err' => $curlErr ?: null, 'decoded' => json_decode((string)$raw, true), ]; $result['probe_raw'] = substr((string)$raw, 0, 1000); // ── Schema introspection — discover actual field names ──────────────────────── if ($key) { // ArrayParity and ArrayCache were separate named types in 7.2.5 — removed in 7.3 (now same as ArrayDisk) $types = ['InfoOs','InfoCpu','InfoMemory','ArrayDisk','VmDomain']; $introspectGql = '{ ' . implode(' ', array_map(fn($t) => "{$t}: __type(name: \"{$t}\") { fields { name type { name kind ofType { name kind } } } }", $types )) . ' }'; $body2 = json_encode(['query' => $introspectGql]); $ch2 = curl_init('http://localhost/graphql'); curl_setopt_array($ch2, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json', "x-api-key: {$key}"], CURLOPT_POSTFIELDS => $body2, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8, ]); $raw2 = curl_exec($ch2); curl_close($ch2); $result['schema'] = json_decode((string)$raw2, true)['data'] ?? null; // Pool drive names — what does the API actually return for cache/pool drives? $ch_pools = curl_init('http://localhost/graphql'); curl_setopt_array($ch_pools, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json', "x-api-key: {$key}"], CURLOPT_POSTFIELDS => json_encode(['query' => '{ array { caches { name device type status fsType } } }']), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8, ]); $result['pool_drives'] = json_decode((string)curl_exec($ch_pools), true)['data'] ?? null; curl_close($ch_pools); // Round 2: introspect CpuUtilization and MemoryUtilization field names $types2 = ['CpuUtilization','MemoryUtilization','TemperatureMetrics']; $gql2 = '{ ' . implode(' ', array_map(fn($t) => "{$t}: __type(name: \"{$t}\") { kind fields { name type { name kind ofType { name kind } } } }", $types2 )) . ' }'; $ch3 = curl_init('http://localhost/graphql'); curl_setopt_array($ch3, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json', "x-api-key: {$key}"], CURLOPT_POSTFIELDS => json_encode(['query' => $gql2]), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8, ]); $result['schema2'] = json_decode((string)curl_exec($ch3), true)['data'] ?? null; curl_close($ch3); } } echo json_encode($result, JSON_PRETTY_PRINT);