merge: quick/http-api-integration — Dashboard consume sqlite_api via HTTP, fallback SQLite
This commit is contained in:
@@ -17,6 +17,7 @@ endif()
|
|||||||
add_imgui_app(registry_dashboard
|
add_imgui_app(registry_dashboard
|
||||||
main.cpp
|
main.cpp
|
||||||
data.cpp
|
data.cpp
|
||||||
|
data_http.cpp
|
||||||
views.cpp
|
views.cpp
|
||||||
${CMAKE_SOURCE_DIR}/functions/viz/kpi_card.cpp
|
${CMAKE_SOURCE_DIR}/functions/viz/kpi_card.cpp
|
||||||
${CMAKE_SOURCE_DIR}/functions/viz/bar_chart.cpp
|
${CMAKE_SOURCE_DIR}/functions/viz/bar_chart.cpp
|
||||||
@@ -31,6 +32,18 @@ add_imgui_app(registry_dashboard
|
|||||||
|
|
||||||
target_include_directories(registry_dashboard PRIVATE
|
target_include_directories(registry_dashboard PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/vendor
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(registry_dashboard PRIVATE SQLite::SQLite3)
|
target_link_libraries(registry_dashboard PRIVATE SQLite::SQLite3)
|
||||||
|
|
||||||
|
# cpp-httplib needs threads on Linux
|
||||||
|
if(UNIX)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
target_link_libraries(registry_dashboard PRIVATE Threads::Threads)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# On Windows cross-compile, link ws2_32 for sockets
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(registry_dashboard PRIVATE ws2_32)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
name: registry_dashboard
|
name: registry_dashboard
|
||||||
lang: cpp
|
lang: cpp
|
||||||
domain: tui
|
domain: tui
|
||||||
description: "Dashboard ImGui para visualizar el estado del fn_registry: KPIs, charts, tablas, desglose por lenguaje/dominio/pureza"
|
description: "Dashboard ImGui para visualizar el estado del fn_registry. Consume datos via sqlite_api HTTP (fallback a SQLite directo). KPIs, charts, tablas, desglose por lenguaje/dominio/pureza."
|
||||||
tags: [dashboard, imgui, visualization, registry]
|
tags: [dashboard, imgui, visualization, registry, http]
|
||||||
uses_functions:
|
uses_functions:
|
||||||
- kpi_card_cpp_viz
|
- kpi_card_cpp_viz
|
||||||
- bar_chart_cpp_viz
|
- bar_chart_cpp_viz
|
||||||
@@ -17,24 +17,27 @@ uses_functions:
|
|||||||
uses_types: []
|
uses_types: []
|
||||||
framework: "imgui"
|
framework: "imgui"
|
||||||
entry_point: "main.cpp"
|
entry_point: "main.cpp"
|
||||||
dir_path: "apps/registry_dashboard"
|
dir_path: "projects/fn_monitoring/apps/registry_dashboard"
|
||||||
repo_url: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/registry_dashboard"
|
repo_url: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/registry_dashboard"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Arquitectura
|
## Arquitectura
|
||||||
|
|
||||||
Dashboard C++ que lee `registry.db` directamente via SQLite C API y visualiza el estado completo del fn_registry.
|
Dashboard C++ con dos modos de acceso a datos:
|
||||||
|
|
||||||
**Data layer** (`data.cpp`): Carga todas las stats, desgloses y listas en un solo paso desde SQLite. Soporte para reload en caliente.
|
1. **HTTP API** (primario): Conecta a `sqlite_api` via HTTP para obtener datos de registry.db. No requiere acceso al filesystem.
|
||||||
|
2. **SQLite directo** (fallback): Lee registry.db directamente si la API no esta disponible.
|
||||||
|
|
||||||
|
**Data layers:**
|
||||||
|
- `data_http.cpp`: Carga datos via HTTP POST a sqlite_api (cpp-httplib + nlohmann/json)
|
||||||
|
- `data.cpp`: Carga directa desde SQLite C API
|
||||||
|
|
||||||
**Views** (`views.cpp`): Compone funciones del registry C++ para renderizar:
|
**Views** (`views.cpp`): Compone funciones del registry C++ para renderizar:
|
||||||
- 6 KPI cards: total functions, types, apps, analysis, tested%, pure%
|
- 8 KPI cards: functions, types, apps, analysis, unit tests, proposals, tested%, pure%
|
||||||
- Bar charts: funciones por lenguaje, por dominio, actividad ultimos 30 dias
|
- Bar charts: funciones por lenguaje, por dominio
|
||||||
- Pie charts: pureza (pure/impure), kind (function/pipeline/component), tested/untested
|
- Pie charts: pureza (pure/impure), kind (function/pipeline/component)
|
||||||
- Tablas: ultimas 20 funciones, apps, analysis, tipos
|
- Tablas: ultimas 20 funciones, apps, analysis, tipos
|
||||||
|
|
||||||
**Entrada**: Paths a `registry.db` como argumentos con fallback encadenado.
|
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -48,23 +51,37 @@ cd cpp && cmake -B build/windows -S . -DCMAKE_TOOLCHAIN_FILE=toolchains/mingw-w6
|
|||||||
## Ejecucion
|
## Ejecucion
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Linux
|
# Via API (default, intenta conectar a localhost:8484)
|
||||||
./cpp/build/linux/apps/registry_dashboard/registry_dashboard /home/lucas/fn_registry/registry.db
|
./registry_dashboard
|
||||||
|
|
||||||
# Windows (PowerShell) — intenta WSL mount, luego local
|
# API explicita
|
||||||
|
./registry_dashboard --api http://192.168.1.10:8484
|
||||||
|
|
||||||
|
# Con SQLite fallback
|
||||||
|
./registry_dashboard --api http://127.0.0.1:8484 /path/to/registry.db
|
||||||
|
|
||||||
|
# Solo SQLite (sin API)
|
||||||
|
./registry_dashboard /path/to/registry.db
|
||||||
|
|
||||||
|
# Windows (PowerShell)
|
||||||
.\registry_dashboard.ps1
|
.\registry_dashboard.ps1
|
||||||
|
|
||||||
# Otra maquina — copiar .exe + .db al mismo dir
|
|
||||||
registry_dashboard.exe .\registry.db
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Dependencias vendored
|
||||||
|
|
||||||
|
| Libreria | Version | Archivo |
|
||||||
|
|----------|---------|---------|
|
||||||
|
| cpp-httplib | v0.18.3 | vendor/httplib.h |
|
||||||
|
| nlohmann/json | v3.11.3 | vendor/nlohmann/json.hpp |
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- [ ] Integrar Go runtime (CGo) para ejecutar comandos `fn` desde el GUI
|
|
||||||
- [ ] Filtros interactivos por lenguaje/dominio en sidebar
|
- [ ] Filtros interactivos por lenguaje/dominio en sidebar
|
||||||
- [ ] Busqueda FTS5 integrada
|
- [ ] Busqueda FTS5 integrada via API
|
||||||
- [ ] Detalles de funcion al hacer click en tabla
|
- [ ] Detalles de funcion al hacer click en tabla
|
||||||
|
|
||||||
## Notas
|
## Notas
|
||||||
|
|
||||||
SQLite compilado estaticamente en Windows via amalgamation vendoreada. En Linux usa libsqlite3 del sistema.
|
- Por defecto intenta conectar a sqlite_api en `http://127.0.0.1:8484`. Si falla, usa SQLite directo.
|
||||||
|
- SQLite compilado estaticamente en Windows via amalgamation vendoreada. En Linux usa libsqlite3 del sistema.
|
||||||
|
- cpp-httplib usa sockets nativos (no OpenSSL) — solo HTTP, no HTTPS.
|
||||||
|
|||||||
+202
@@ -0,0 +1,202 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Load all registry data via sqlite_api HTTP endpoint.
|
||||||
|
// api_url should be like "http://127.0.0.1:8484".
|
||||||
|
// Returns true on success.
|
||||||
|
bool load_registry_data_http(const std::string& api_url, RegistryData& out);
|
||||||
@@ -2,22 +2,41 @@
|
|||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "core/fullscreen_window.h"
|
#include "core/fullscreen_window.h"
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
#include "data_http.h"
|
||||||
#include "views.h"
|
#include "views.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
static RegistryData g_data;
|
static RegistryData g_data;
|
||||||
static std::string g_db_path;
|
static std::string g_db_path;
|
||||||
|
static std::string g_api_url;
|
||||||
static bool g_loaded = false;
|
static bool g_loaded = false;
|
||||||
|
static bool g_using_http = false;
|
||||||
|
|
||||||
static void reload_data() {
|
static void reload_data() {
|
||||||
g_data = RegistryData{};
|
g_data = RegistryData{};
|
||||||
g_loaded = load_registry_data(g_db_path.c_str(), g_data);
|
|
||||||
if (!g_loaded) {
|
// Try HTTP API first
|
||||||
fprintf(stderr, "Failed to load registry data from: %s\n", g_db_path.c_str());
|
if (!g_api_url.empty()) {
|
||||||
|
g_loaded = load_registry_data_http(g_api_url, g_data);
|
||||||
|
if (g_loaded) {
|
||||||
|
g_using_http = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "HTTP API failed, falling back to SQLite\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to direct SQLite
|
||||||
|
g_using_http = false;
|
||||||
|
if (!g_db_path.empty()) {
|
||||||
|
g_loaded = load_registry_data(g_db_path.c_str(), g_data);
|
||||||
|
if (!g_loaded) {
|
||||||
|
fprintf(stderr, "Failed to load registry data from: %s\n", g_db_path.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,11 +49,17 @@ static void render() {
|
|||||||
if (!g_loaded) {
|
if (!g_loaded) {
|
||||||
fullscreen_window_begin("##error");
|
fullscreen_window_begin("##error");
|
||||||
ImGui::TextColored(ImVec4(1, 0.3f, 0.3f, 1),
|
ImGui::TextColored(ImVec4(1, 0.3f, 0.3f, 1),
|
||||||
"Could not open registry.db");
|
"Could not load registry data");
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Text("Tried: %s", g_db_path.c_str());
|
if (!g_api_url.empty())
|
||||||
|
ImGui::Text("API: %s (unreachable)", g_api_url.c_str());
|
||||||
|
if (!g_db_path.empty())
|
||||||
|
ImGui::Text("DB: %s", g_db_path.c_str());
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::TextWrapped("Usage: registry_dashboard <path1> [path2] [path3] ...");
|
ImGui::TextWrapped(
|
||||||
|
"Usage: registry_dashboard [--api URL] [db_path ...]\n"
|
||||||
|
" --api URL Connect to sqlite_api (default: http://127.0.0.1:8484)\n"
|
||||||
|
" db_path Direct SQLite path(s) as fallback");
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
if (ImGui::Button("Retry")) {
|
if (ImGui::Button("Retry")) {
|
||||||
reload_data();
|
reload_data();
|
||||||
@@ -47,26 +72,46 @@ static void render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
if (argc < 2) {
|
// Parse --api flag
|
||||||
fprintf(stderr, "Usage: registry_dashboard <db_path> [fallback_path ...]\n");
|
std::vector<std::string> db_candidates;
|
||||||
fprintf(stderr, " Tries each path in order until one opens successfully.\n");
|
bool api_explicit = false;
|
||||||
return 1;
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--api") == 0 && i + 1 < argc) {
|
||||||
|
g_api_url = argv[++i];
|
||||||
|
api_explicit = true;
|
||||||
|
} else if (strncmp(argv[i], "--api=", 6) == 0) {
|
||||||
|
g_api_url = argv[i] + 6;
|
||||||
|
api_explicit = true;
|
||||||
|
} else {
|
||||||
|
db_candidates.push_back(argv[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try each argument in order
|
// Default: try localhost API if no --api given
|
||||||
for (int i = 1; i < argc; i++) {
|
if (!api_explicit) {
|
||||||
std::string candidate = argv[i];
|
g_api_url = "http://127.0.0.1:8484";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve SQLite fallback path
|
||||||
|
for (auto& candidate : db_candidates) {
|
||||||
if (std::ifstream(candidate).good()) {
|
if (std::ifstream(candidate).good()) {
|
||||||
g_db_path = candidate;
|
g_db_path = candidate;
|
||||||
fprintf(stdout, "Using: %s\n", g_db_path.c_str());
|
fprintf(stdout, "SQLite fallback: %s\n", g_db_path.c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "Not found: %s\n", candidate.c_str());
|
fprintf(stderr, "Not found: %s\n", candidate.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_db_path.empty()) {
|
if (g_api_url.empty() && g_db_path.empty() && db_candidates.empty()) {
|
||||||
// None found, use last arg so error screen shows something useful
|
fprintf(stderr, "Usage: registry_dashboard [--api URL] [db_path ...]\n");
|
||||||
g_db_path = argv[argc - 1];
|
fprintf(stderr, " --api URL Connect to sqlite_api (default: http://127.0.0.1:8484)\n");
|
||||||
|
fprintf(stderr, " db_path Direct SQLite path(s) as fallback\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_db_path.empty() && !db_candidates.empty()) {
|
||||||
|
g_db_path = db_candidates.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
reload_data();
|
reload_data();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
$exe = Join-Path $PSScriptRoot "registry_dashboard.exe"
|
$exe = Join-Path $PSScriptRoot "registry_dashboard.exe"
|
||||||
|
|
||||||
|
# Try API first (default), with SQLite fallback via WSL mount
|
||||||
& $exe `
|
& $exe `
|
||||||
"\\wsl.localhost\Ubuntu-22.04\home\lucas\fn_registry\registry.db" `
|
--api "http://127.0.0.1:8484" `
|
||||||
|
"\\wsl.localhost\Ubuntu-22.04\home\lucas\fn_registry\registry.db"
|
||||||
|
|||||||
Vendored
+10255
File diff suppressed because it is too large
Load Diff
Vendored
+24765
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user