#include "data_http.h" #define CPPHTTPLIB_OPENSSL_SUPPORT 0 #include "vendor/httplib.h" #include "vendor/nlohmann/json.hpp" #include using json = nlohmann::json; // POST a SQL query to the API and return parsed JSON, or null on failure. static json api_query(httplib::Client& cli, const char* sql) { json body; body["sql"] = sql; auto res = cli.Post("/api/databases/registry/query", body.dump(), "application/json"); if (!res || res->status != 200) { if (res) fprintf(stderr, "[http] query error %d: %s\n", res->status, res->body.c_str()); else fprintf(stderr, "[http] connection failed for query: %s\n", sql); return nullptr; } return json::parse(res->body, nullptr, false); } // Extract first int from a single-row, single-column result. static int extract_int(const json& j) { if (j.is_null() || !j.contains("rows") || j["rows"].empty()) return 0; auto& val = j["rows"][0][0]; if (val.is_number()) return val.get(); if (val.is_string()) return std::atoi(val.get().c_str()); return 0; } // Extract string from a row at given column index. static std::string extract_str(const json& row, size_t idx) { if (idx >= row.size() || row[idx].is_null()) return ""; if (row[idx].is_string()) return row[idx].get(); return row[idx].dump(); } // Extract int from a row at given column index. static int extract_row_int(const json& row, size_t idx) { if (idx >= row.size() || row[idx].is_null()) return 0; if (row[idx].is_number()) return row[idx].get(); if (row[idx].is_string()) return std::atoi(row[idx].get().c_str()); return 0; } bool load_registry_data_http(const std::string& api_url, RegistryData& out) { httplib::Client cli(api_url); cli.set_connection_timeout(2); cli.set_read_timeout(5); // Health check first auto health = cli.Get("/health"); if (!health || health->status != 200) { fprintf(stderr, "[http] sqlite_api not reachable at %s\n", api_url.c_str()); return false; } fprintf(stdout, "[http] Connected to sqlite_api at %s\n", api_url.c_str()); // --- Counts --- auto j = api_query(cli, "SELECT COUNT(*) FROM functions"); out.stats.total_functions = extract_int(j); j = api_query(cli, "SELECT COUNT(*) FROM types"); out.stats.total_types = extract_int(j); j = api_query(cli, "SELECT COUNT(*) FROM apps"); out.stats.total_apps = extract_int(j); j = api_query(cli, "SELECT COUNT(*) FROM analysis"); out.stats.total_analysis = extract_int(j); j = api_query(cli, "SELECT COUNT(*) FROM unit_tests"); out.stats.total_unit_tests = extract_int(j); j = api_query(cli, "SELECT COUNT(*) FROM proposals"); out.stats.total_proposals = extract_int(j); j = api_query(cli, "SELECT COUNT(*) FROM functions WHERE tested = 1"); out.stats.tested_functions = extract_int(j); j = api_query(cli, "SELECT COUNT(*) FROM functions WHERE purity = 'pure'"); out.stats.pure_functions = extract_int(j); j = api_query(cli, "SELECT COUNT(*) FROM functions WHERE purity = 'impure'"); out.stats.impure_functions = extract_int(j); // --- By language --- out.by_lang.clear(); j = api_query(cli, "SELECT lang, COUNT(*) as cnt FROM functions GROUP BY lang ORDER BY cnt DESC"); if (!j.is_null() && j.contains("rows")) { for (auto& row : j["rows"]) { out.by_lang.push_back({extract_str(row, 0), extract_row_int(row, 1)}); } } // --- By domain --- out.by_domain.clear(); j = api_query(cli, "SELECT domain, COUNT(*) as cnt FROM functions GROUP BY domain ORDER BY cnt DESC"); if (!j.is_null() && j.contains("rows")) { for (auto& row : j["rows"]) { out.by_domain.push_back({extract_str(row, 0), extract_row_int(row, 1)}); } } // --- By kind --- out.by_kind.clear(); j = api_query(cli, "SELECT kind, COUNT(*) as cnt FROM functions GROUP BY kind ORDER BY cnt DESC"); if (!j.is_null() && j.contains("rows")) { for (auto& row : j["rows"]) { out.by_kind.push_back({extract_str(row, 0), extract_row_int(row, 1)}); } } // --- By date (last 30 days) --- out.by_date.clear(); j = api_query(cli, "SELECT date(created_at) as d, COUNT(*) as cnt FROM functions " "WHERE created_at >= date('now', '-30 days') GROUP BY d ORDER BY d"); if (!j.is_null() && j.contains("rows")) { for (auto& row : j["rows"]) { out.by_date.push_back({extract_str(row, 0), extract_row_int(row, 1)}); } } // --- Recent functions (last 20) --- out.recent_funcs.clear(); j = api_query(cli, "SELECT id, name, lang, domain, kind, purity, description, created_at, tested " "FROM functions ORDER BY created_at DESC LIMIT 20"); if (!j.is_null() && j.contains("rows")) { for (auto& row : j["rows"]) { FunctionRow r; r.id = extract_str(row, 0); r.name = extract_str(row, 1); r.lang = extract_str(row, 2); r.domain = extract_str(row, 3); r.kind = extract_str(row, 4); r.purity = extract_str(row, 5); r.description = extract_str(row, 6); r.created_at = extract_str(row, 7); r.tested = extract_row_int(row, 8) != 0; out.recent_funcs.push_back(std::move(r)); } } // --- Apps --- out.apps.clear(); j = api_query(cli, "SELECT id, name, lang, domain, description, framework FROM apps ORDER BY name"); if (!j.is_null() && j.contains("rows")) { for (auto& row : j["rows"]) { AppRow r; r.id = extract_str(row, 0); r.name = extract_str(row, 1); r.lang = extract_str(row, 2); r.domain = extract_str(row, 3); r.description = extract_str(row, 4); r.framework = extract_str(row, 5); out.apps.push_back(std::move(r)); } } // --- Analysis --- out.analyses.clear(); j = api_query(cli, "SELECT id, name, domain, description FROM analysis ORDER BY name"); if (!j.is_null() && j.contains("rows")) { for (auto& row : j["rows"]) { AnalysisRow r; r.id = extract_str(row, 0); r.name = extract_str(row, 1); r.domain = extract_str(row, 2); r.description = extract_str(row, 3); out.analyses.push_back(std::move(r)); } } // --- Types --- out.types.clear(); j = api_query(cli, "SELECT id, name, lang, domain, algebraic, description FROM types ORDER BY name"); if (!j.is_null() && j.contains("rows")) { for (auto& row : j["rows"]) { TypeRow r; r.id = extract_str(row, 0); r.name = extract_str(row, 1); r.lang = extract_str(row, 2); r.domain = extract_str(row, 3); r.algebraic = extract_str(row, 4); r.description = extract_str(row, 5); out.types.push_back(std::move(r)); } } out.prepare_chart_data(); return true; }