- Auth stack: fold cert monitor into Auth Stack page as fourth tab (Certs); remove standalone cert page and top-level tab - cert_monitor.sh: write JSON status cache to State_Files/cert_status.json after each run; expose per-domain days/expiry via _CERT_DAYS/_CERT_EXPIRY globals - api/cert.php: new — serves cached cert status; falls back to configured domains as UNKN when no cache exists; POST action=run triggers live check - arrs db fallbacks: vv_arr_cleanup_stats/discovery_stats/recovery_stats now read from data/*.db files when log JSON files don't yet exist - config.php vv_conf_vars(): unescape bash \$ → $ so passwords with dollar signs read correctly from conf files - host1.conf: fill in HOST1_NPM_USER/PASS and HOST1_LLDAP_USER/PASS - Partnership adapter pattern: Unraid-specific container logic extracted to Plugin/unraid/Partnership/; platform-agnostic structure stays in Partnership/ - First-run wizard: uniform multi-step flow for all hosts; HOST2 pull moved to checklist; auto SSH keygen and API key creation on save - api/checklist.php: live setup checklist with pull_master action - Fullscreen toggle: hide Unraid header/menu; state persists via localStorage
120 lines
4.7 KiB
PHP
120 lines
4.7 KiB
PHP
<?php
|
|
// Diagnostic endpoint — tests the Unraid GraphQL API and returns raw results.
|
|
// Hit from browser: /plugins/varaverk/api/api_test.php
|
|
header('Content-Type: application/json');
|
|
require_once dirname(__DIR__) . '/include/config.php';
|
|
|
|
$hostId = vv_detect_host();
|
|
$vars = vv_conf_vars();
|
|
$key = $vars[strtoupper($hostId) . '_UNRAID_API_KEY'] ?? '';
|
|
|
|
$result = [
|
|
'host' => $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 = VV_CACHE_DIR . '/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);
|