true, 'detail' => 'Tools login uses Caveau client_users for tenant ' . dbnToolsClientSlug(), ]; $azure = new DbnAzureOpenAiGateway(); $missingChat = $azure->missingChatConfig(); $missingEmbedding = $azure->missingEmbeddingConfig(); $checks['azure_chat_config'] = [ 'ok' => !$missingChat, 'detail' => !$missingChat ? 'Configured' : 'Missing: ' . implode(', ', $missingChat), ]; $checks['azure_embedding_config'] = [ 'ok' => !$missingEmbedding, 'detail' => !$missingEmbedding ? 'Configured' : 'Missing: ' . implode(', ', $missingEmbedding), ]; $checks['azure_reachability'] = [ 'ok' => false, 'detail' => 'Not checked because chat config is incomplete', ]; if (!$missingChat) { $reachable = $azure->ping(8); $checks['azure_reachability'] = [ 'ok' => $reachable, 'detail' => $reachable ? 'Azure chat deployment responded' : 'Azure chat deployment did not respond', ]; } try { $db = dbnToolsDb(); $db->query('SELECT 1'); $checks['db_connectivity'] = ['ok' => true, 'detail' => 'CaveauAI admin DB reachable']; $client = dbnToolsFetchClient($db); $checks['dobetter_client'] = [ 'ok' => (bool)$client, 'detail' => $client ? 'Client id ' . $client['id'] . ' found' : 'Client slug ' . dbnToolsClientSlug() . ' not found', ]; $packageSlug = dbnToolsRequiredPackageSlug(); $package = dbnToolsFetchPackage($packageSlug, $db); $checks['family_legal_package'] = [ 'ok' => (bool)$package && !empty($package['is_active']), 'detail' => $package ? 'Package id ' . $package['id'] . ' found' : $packageSlug . ' package not found', ]; $subOk = $client && $package && dbnToolsHasActiveSubscription((int)$client['id'], (int)$package['id'], $db); $checks['family_legal_subscription'] = [ 'ok' => (bool)$subOk, 'detail' => $subOk ? 'Active subscription visible' : 'Active subscription not visible', ]; } catch (Throwable $e) { $checks['db_connectivity'] = ['ok' => false, 'detail' => $e->getMessage()]; $checks['dobetter_client'] = ['ok' => false, 'detail' => 'Not checked']; $checks['family_legal_package'] = ['ok' => false, 'detail' => 'Not checked']; $checks['family_legal_subscription'] = ['ok' => false, 'detail' => 'Not checked']; } $searchEndpoint = rtrim((string)dbnToolsEnv('DBN_AZURE_SEARCH_ENDPOINT', ''), '/'); $searchKey = (string)dbnToolsEnv('DBN_AZURE_SEARCH_KEY', ''); $searchIndex = (string)dbnToolsEnv('DBN_AZURE_SEARCH_INDEX', ''); if ($searchEndpoint && $searchKey && $searchIndex) { $countUrl = "$searchEndpoint/indexes/$searchIndex/docs/\$count?api-version=2024-05-01-preview"; $ch = curl_init($countUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8, CURLOPT_HTTPHEADER => ["api-key: $searchKey"], ]); $resp = curl_exec($ch); $code = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE); curl_close($ch); $count = is_numeric(trim((string)$resp)) ? (int)trim($resp) : null; $checks['azure_search'] = [ 'ok' => $code === 200 && $count !== null, 'detail' => $code === 200 ? "$count docs in $searchIndex" : "HTTP $code", ]; } else { $checks['azure_search'] = ['ok' => false, 'detail' => 'Azure Search env vars not configured']; } $logPath = dbnToolsMetadataLogPath(); $dir = dirname($logPath); $checks['metadata_log'] = [ 'ok' => is_dir($dir) && is_writable($dir), 'detail' => is_dir($dir) && is_writable($dir) ? 'Metadata directory is writable' : 'Metadata directory is not writable', ]; $ok = true; foreach ($checks as $check) { if (empty($check['ok'])) { $ok = false; break; } } dbnToolsRespond([ 'ok' => $ok, 'status' => $ok ? 'ok' : 'degraded', 'version' => DBN_TOOLS_VERSION, 'checks' => $checks, ], $ok ? 200 : 503);