0ef3da3bf5
Nuevo data_http.cpp que obtiene todos los datos del registry via HTTP POST a sqlite_api (/api/databases/registry/query). Mismas queries SQL que el data layer SQLite pero ejecutadas remotamente. cpp-httplib como cliente HTTP, nlohmann/json para parsear respuestas. CMakeLists.txt actualizado: incluye data_http.cpp, vendor/ en includes, linkea pthreads (Linux) y ws2_32 (Windows) para sockets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
203 lines
7.1 KiB
C++
203 lines
7.1 KiB
C++
#include "data_http.h"
|
|
|
|
#define CPPHTTPLIB_OPENSSL_SUPPORT 0
|
|
#include "vendor/httplib.h"
|
|
#include "vendor/nlohmann/json.hpp"
|
|
|
|
#include <cstdio>
|
|
|
|
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<int>();
|
|
if (val.is_string()) return std::atoi(val.get<std::string>().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<std::string>();
|
|
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<int>();
|
|
if (row[idx].is_string()) return std::atoi(row[idx].get<std::string>().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;
|
|
}
|