282 lines
15 KiB
Markdown
282 lines
15 KiB
Markdown
---
|
|
name: fn_monitoring
|
|
description: "Monitoreo y visualizacion del estado del fn_registry. API HTTP read-only sobre las bases de datos SQLite y dashboard ImGui que consume la API."
|
|
tags: [monitoring, api, dashboard, sqlite, visualization]
|
|
repo_url: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/fn_monitoring"
|
|
---
|
|
|
|
## Apps
|
|
|
|
| App | Lang | Descripcion |
|
|
|-----|------|-------------|
|
|
| [sqlite_api](apps/sqlite_api/app.md) | Go | API REST HTTP read-only sobre `registry.db` y todas las `operations.db`. Puerto `8484`. |
|
|
| [registry_dashboard](apps/registry_dashboard/app.md) | C++ / ImGui | Dashboard con KPIs, charts y tablas del registry. Consume `sqlite_api` (HTTP) con fallback a SQLite directo. |
|
|
|
|
Cada `app.md` es la referencia canonica del binario — endpoints completos, flags, dependencias. Este documento cubre **como operar el proyecto como un todo**: arranque, service, flujo de datos, troubleshooting.
|
|
|
|
---
|
|
|
|
## Arquitectura
|
|
|
|
```
|
|
registry.db (raiz)
|
|
apps/*/operations.db
|
|
projects/*/apps/*/operations.db
|
|
│ (read-only, mode=ro)
|
|
▼
|
|
┌──────────────────────────────────────────────┐
|
|
│ sqlite_api (Go net/http, :8484) │
|
|
│ /health │
|
|
│ /api/databases │
|
|
│ /api/databases/:db/tables │
|
|
│ /api/databases/:db/schema │
|
|
│ /api/databases/:db/query (POST, SELECT) │
|
|
│ /api/databases/:db/fts │
|
|
└──────────────────────────────────────────────┘
|
|
▲
|
|
│ HTTP GET/POST
|
|
│ (cpp-httplib + nlohmann/json)
|
|
│
|
|
┌──────────────────────────────────────────────┐
|
|
│ registry_dashboard (C++ / ImGui + ImPlot) │
|
|
│ main.cpp → reload_data() │
|
|
│ data_http.cpp (primario, HTTP) │
|
|
│ data.cpp (fallback, SQLite C API) │
|
|
│ views.cpp → KPI row, charts, tables │
|
|
└──────────────────────────────────────────────┘
|
|
```
|
|
|
|
**Separacion de responsabilidades:**
|
|
|
|
- `sqlite_api` **no conoce el dashboard**. Es una API generica: expone cualquier DB SQLite de `fn_registry/` read-only con FTS5.
|
|
- `registry_dashboard` **no conoce la estructura de registry.db directamente**, solo a traves del JSON que devuelve la API. El modo SQLite directo es fallback para entornos sin red.
|
|
|
|
**Puerto `8484`** — elegido para no colisionar con Metabase (3000), Jupyter (8888) ni deploy_server (9090).
|
|
|
|
---
|
|
|
|
## Servicio sqlite_api
|
|
|
|
### Modos de arranque
|
|
|
|
| Modo | Comando | Cuando usarlo |
|
|
|------|---------|---------------|
|
|
| Dev (foreground, `go run`) | `cd projects/fn_monitoring/apps/sqlite_api && go run -tags fts5 .` | Iteracion rapida, ver logs en la terminal |
|
|
| Dev (background) | `./start.sh` (dentro de `apps/sqlite_api/`) | Probar el dashboard rapido sin systemd. Escribe PID en `sqlite_api.pid` y log en `sqlite_api.log` |
|
|
| Production (systemd) | `sudo systemctl start sqlite_api` | Arranque en boot, restart on failure, logs en journal |
|
|
|
|
### Variables de entorno
|
|
|
|
| Var | Valor | Proposito |
|
|
|-----|-------|-----------|
|
|
| `FN_REGISTRY_ROOT` | ruta absoluta a la raiz del registry | Evita que el binario busque `registry.db` subiendo por el cwd. Obligatoria bajo systemd. |
|
|
|
|
### Instalar como servicio systemd (local)
|
|
|
|
Usar el pipeline del registry `install_systemd_service_bash_pipelines`:
|
|
|
|
```bash
|
|
cd /home/lucas/fn_registry
|
|
|
|
# 1. Build del binario
|
|
CGO_ENABLED=1 go build -tags fts5 \
|
|
-o projects/fn_monitoring/apps/sqlite_api/sqlite_api \
|
|
./projects/fn_monitoring/apps/sqlite_api/
|
|
|
|
# 2. Instalar unit + enable + start (requiere sudo sin password para systemctl)
|
|
source bash/functions/pipelines/install_systemd_service.sh
|
|
install_systemd_service \
|
|
--name sqlite_api \
|
|
--exec "$(pwd)/projects/fn_monitoring/apps/sqlite_api/sqlite_api" \
|
|
--workdir "$(pwd)" \
|
|
--env "FN_REGISTRY_ROOT=$(pwd)" \
|
|
--description "fn_registry SQLite HTTP API" \
|
|
--after network.target \
|
|
--restart on-failure
|
|
```
|
|
|
|
### Operacion
|
|
|
|
```bash
|
|
sudo systemctl status sqlite_api # estado + ultimas lineas del journal
|
|
sudo systemctl restart sqlite_api # tras rebuild del binario
|
|
sudo systemctl stop sqlite_api # parar
|
|
journalctl -u sqlite_api -f # logs en vivo
|
|
curl http://127.0.0.1:8484/health # health check
|
|
```
|
|
|
|
### Redeploy tras cambios en el codigo Go
|
|
|
|
```bash
|
|
cd /home/lucas/fn_registry
|
|
CGO_ENABLED=1 go build -tags fts5 \
|
|
-o projects/fn_monitoring/apps/sqlite_api/sqlite_api \
|
|
./projects/fn_monitoring/apps/sqlite_api/
|
|
sudo systemctl restart sqlite_api
|
|
```
|
|
|
|
No hace falta reinstalar el unit — solo recompilar y reiniciar.
|
|
|
|
---
|
|
|
|
## Dashboard registry_dashboard
|
|
|
|
### Build
|
|
|
|
```bash
|
|
cd cpp
|
|
cmake -B build/linux -S .
|
|
cmake --build build/linux --target registry_dashboard -j$(nproc)
|
|
```
|
|
|
|
El binario queda en `cpp/build/linux/registry_dashboard` (o `projects/fn_monitoring/apps/registry_dashboard/registry_dashboard.exe` en Windows).
|
|
|
|
### Ejecucion
|
|
|
|
```bash
|
|
# Modo API (por defecto, intenta localhost:8484)
|
|
./registry_dashboard
|
|
|
|
# API remoto
|
|
./registry_dashboard --api http://192.168.1.10:8484
|
|
|
|
# API + fallback SQLite
|
|
./registry_dashboard --api http://127.0.0.1:8484 /home/lucas/fn_registry/registry.db
|
|
|
|
# Solo SQLite (sin API)
|
|
./registry_dashboard /home/lucas/fn_registry/registry.db
|
|
```
|
|
|
|
La UI muestra en la cabecera de donde vienen los datos (HTTP vs SQLite). `F5` recarga.
|
|
|
|
### Flujo de datos
|
|
|
|
1. `main.cpp::reload_data()` intenta HTTP primero via `load_registry_data_http()`.
|
|
2. Si la API responde `200` y el JSON parsea, los datos pueblan `RegistryData`.
|
|
3. Si falla la API (timeout, 5xx, JSON invalido) y hay `--db`, cae a `load_registry_data()` (SQLite directo).
|
|
4. Si ninguno funciona, la UI muestra un mensaje de error y no hay reintento automatico — hay que pulsar reload.
|
|
|
|
### Vistas
|
|
|
|
| Seccion | Datos | Query subyacente |
|
|
|---------|-------|------------------|
|
|
| KPI row (8 cards) | totales y porcentajes | `SELECT COUNT(*)` sobre functions, types, apps, analysis, unit_tests, proposals + agregados tested/pure |
|
|
| Charts (bar + pie) | funciones por lang/domain, reparto pure/impure, kind | `GROUP BY lang`, `GROUP BY domain`, `GROUP BY purity`, `GROUP BY kind` |
|
|
| Tablas | ultimas 20 functions, apps, analysis, types | `ORDER BY updated_at DESC LIMIT 20` |
|
|
|
|
Detalle de composicion de componentes viz en `apps/registry_dashboard/app.md`.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
| Sintoma | Causa probable | Verificacion / Fix |
|
|
|---------|----------------|--------------------|
|
|
| Dashboard dice "HTTP API failed, falling back to SQLite" | `sqlite_api` no esta corriendo | `curl http://127.0.0.1:8484/health` — si falla, `systemctl status sqlite_api` o `./start.sh` |
|
|
| `sqlite_api` arranca y muere inmediatamente | No encuentra `registry.db` | Exportar `FN_REGISTRY_ROOT=/home/lucas/fn_registry` o correr desde la raiz del registry |
|
|
| `systemctl start sqlite_api` pide password | Falta sudoers para systemctl | Ver `.claude/rules/deploy.md` — el usuario necesita `NOPASSWD` para `systemctl`, `mv` a `/etc/systemd/system/` |
|
|
| Dashboard abre pero todas las cifras son 0 | API conecta pero devuelve DB vacia | `curl -X POST http://127.0.0.1:8484/api/databases/registry/query -d '{"sql":"SELECT COUNT(*) FROM functions"}'` |
|
|
| API responde lento / timeout | Query pesada sobre FTS5 | Timeout hardcoded a 5s en `handlers.go`. Revisar la query en journal. |
|
|
| Bind rechazado (`address already in use`) | Otro proceso en `8484` | `ss -tlnp | grep 8484` — matar el huerfano o cambiar `--bind` |
|
|
|
|
### Logs
|
|
|
|
```bash
|
|
# systemd
|
|
journalctl -u sqlite_api -n 100 --no-pager
|
|
journalctl -u sqlite_api -f
|
|
|
|
# start.sh
|
|
tail -f projects/fn_monitoring/apps/sqlite_api/sqlite_api.log
|
|
```
|
|
|
|
---
|
|
|
|
## Como extender
|
|
|
|
### Anadir un endpoint a sqlite_api
|
|
|
|
1. Registrar la ruta en `Server.Routes()` (`handlers.go`).
|
|
2. Handler lee `r.URL.Path` / `r.Body`, delega en `DBPool` para resolver la DB, ejecuta SQL read-only.
|
|
3. Test en `handlers_test.go` (patron: tabla de casos HTTP).
|
|
4. Rebuild + `systemctl restart sqlite_api`.
|
|
5. Documentar en `apps/sqlite_api/app.md` (tabla de endpoints).
|
|
|
|
### Anadir una vista al dashboard
|
|
|
|
1. Nuevo campo en `RegistryData` (`data.h`) + su equivalente en la respuesta JSON.
|
|
2. Parseo en `data_http.cpp` y carga SQL en `data.cpp` (ambos paths, para mantener el fallback).
|
|
3. Renderizado en `views.cpp` usando componentes del dominio `viz` (`kpi_card`, `bar_chart`, etc.) — ver regla `frontend_theming` analoga para C++: usar primitivos del registry antes que ImGui crudo.
|
|
4. Rebuild con CMake.
|
|
|
|
### Anadir una DB nueva
|
|
|
|
La descubre automaticamente `DiscoverDatabases()` escaneando `apps/*/operations.db` y `projects/*/apps/*/operations.db`. No hay que registrar nada — al reiniciar `sqlite_api` aparecen con alias `ops:{app_name}`.
|
|
|
|
---
|
|
|
|
## Deploy en otros PCs
|
|
|
|
Este proyecto se instala identico en cualquier maquina con el registry clonado:
|
|
|
|
1. `fn sync` para traer los metadatos del proyecto.
|
|
2. Build + systemd install (seccion "Instalar como servicio systemd" arriba).
|
|
3. Build del dashboard.
|
|
|
|
Los datos son los `.db` locales — cada PC ve su propio estado del registry y sus propias `operations.db`. No hay sincronizacion remota de datos en este servicio: para eso existe `fn sync` contra `registry_api` (proyecto diferente, ver memoria `project_registry_api`).
|
|
|
|
---
|
|
|
|
## Estado actual
|
|
|
|
### Fase — projects view + mutaciones desde el dashboard `[done 2026-04-25]`
|
|
|
|
El dashboard pasa de read-only a manipular el registry via la API. Ampliacion en tres patas:
|
|
|
|
**Backend (`sqlite_api`)** — endpoints nuevos en `handlers_projects.go` y `handlers_mutations.go`:
|
|
|
|
| Metodo | Path | Que hace |
|
|
|---|---|---|
|
|
| `GET` | `/api/projects` | Lista con conteos `apps_count` / `analyses_count` / `vaults_count` por proyecto + bloque `orphans` (entidades con `project_id` vacio). |
|
|
| `GET` | `/api/projects/{id}` | Detalle: apps[], analyses[], vaults[]. Acepta `id="orphans"` para devolver las huerfanas. |
|
|
| `POST` | `/api/reindex` | Ejecuta `fn index` desde `registryRoot`, devuelve `{ok, output}`. |
|
|
| `POST` | `/api/add/app` | Body `{name, lang, domain, project, description}` → crea `apps/{name}/` o `projects/{p}/apps/{name}/` con `app.md` minimo + `fn index`. |
|
|
| `POST` | `/api/add/analysis` | Body `{name, project, packages[], description}` → invoca `fn run init_jupyter_analysis [--project p] name pkg1 pkg2 ...`. |
|
|
| `POST` | `/api/add/vault` | Body `{name, project, path, description}` → crea dir o symlink en `projects/{p}/vaults/` + entry append en `vault.yaml`. |
|
|
|
|
`Server.registryRoot` se inyecta en `NewServer(pool, root)` (rebajado de `findRegistryRoot()` en `main.go`). Helpers `runFN()` y `runShell()` ejecutan con `cmd.Dir = registryRoot` y `FN_REGISTRY_ROOT` en el env.
|
|
|
|
**Dashboard (`registry_dashboard`)** — actions bar + tab Projects + modal Add:
|
|
|
|
- Toolbar nueva en el header (`fn_ui::toolbar`): boton `Reindex` (Primary) → dispara `http_post_reindex` via `process_runner`; boton `+ Add` → abre `modal_dialog`; boton `Reload`; `toast_inbox_button` con badge.
|
|
- Modal Add con `select` para kind (App / Analysis / Vault), `select` de proyecto (obligatorio para Vault, opcional para resto), `text_input` Name + Description y campos especificos por kind (lang/domain para App, packages CSV para Analysis, abs path para Vault). Submit dispara el endpoint correspondiente via `process_runner`. Toast al completar + reload automatico.
|
|
- Tab Projects con dos columnas: `tree_view` izquierda (proyectos + entrada "(orphans)" cuando hay entidades huerfanas), detalle derecha con tabs internas Apps / Analysis / Vaults. Click en un proyecto dispara `load_project_detail_http`.
|
|
|
|
**Datos en `RegistryData`**: nuevos `projects[]`, `orphan_apps`, `orphan_analyses`, `orphan_vaults`. Tipos nuevos `ProjectRow`, `VaultRow`, `ProjectDetail`. `load_registry_data_http` llama a `load_projects_http` al final como best-effort (no fatal si falla).
|
|
|
|
### Bug fix — vibracion al redimensionar `[done 2026-04-25]`
|
|
|
|
Dos fuentes de "vibracion" durante drag-resize de la ventana:
|
|
|
|
- `fullscreen_window_cpp_core` v0.2: anadido `NoScrollbar | NoScrollWithMouse`. Sin esto, si el contenido excedia por 1-2px aparecia un scrollbar fugaz que reducia el ancho ~14px y reflowaba todo.
|
|
- `views.cpp::draw_dashboard`: altura de charts pasa de `GetContentRegionAvail().y * 0.35` a constante 260 px. La proporcion relativa propagaba el resize a todos los plots.
|
|
- `kpi_card_cpp_viz` v1.2: altura fija 78 px (antes 108) + scale 1.4x (antes 1.8) + padding sm + `NoScrollbar`. El `AutoResizeY` con 8 cards generaba lag perceptible al redimensionar.
|
|
|
|
### Bug fix — HTTP POST timeout en thread de background `[done 2026-04-25]`
|
|
|
|
`http_client.cpp::request()` pasaba `struct timeval` a `setsockopt(SO_RCVTIMEO)` en Windows, donde MSDN especifica `DWORD` ms. Resultado: timeout efectivo de **5 ms** en lugar de **5 s**. Se nota especialmente en POST desde threads (background runners) porque la latencia de scheduling puede pasar de 5 ms. Fix: rama `_WIN32` con `DWORD timeout_ms`. Tambien `wsa_init` envuelto en `std::call_once` para evitar race entre main thread + runners. Mensajes de error formateados con ASCII (em dash U+2014 falla render con la fuente default).
|
|
|
|
### Tooling sibling — primitives_gallery `[done 2026-04-25]`
|
|
|
|
Nueva app dev en `cpp/apps/primitives_gallery/` (no es app del registry, vive en el source tree). Catalogo visual interactivo de los 19 primitivos UI de `cpp/functions/{core,viz}` con sidebar + panel + snippet por demo. Doble rol: smoke test visual al modificar tokens/componentes y build gate (esta en el CMake principal — si un primitivo rompe API la gallery no compila).
|
|
|
|
Demo destacada: `graph_viewport` con sliders de Nodes (100-20 000), Clusters (2-16) y los tres parametros de `ForceLayoutConfig` (Repulsion / Attraction / Gravity) aplicados en vivo. Util tambien como benchmark de rendimiento del stack `graph_renderer` + `graph_force_layout` + `graph_spatial_hash`.
|
|
|
|
`README.md` propio en `cpp/apps/primitives_gallery/README.md`.
|
|
|
|
### Lo siguiente que pega
|
|
|
|
- Tests unitarios de logica pura (Phase A del plan de tests): vendoreado de `doctest`, ~6 tests para `label_stride`, `slice_at`, `process_runner` transitions, `toast` queue, `tokens` sanity, `parse_url`. Cierra el ciclo gallery (visual) + ctest (logica).
|
|
- Para que algunos tests sean posibles hace falta exponer funciones internas de `bar_chart.cpp` y `pie_chart.cpp` (actualmente en namespace anonimo).
|
|
- `loginctl enable-linger lucas` para que el `sqlite_api.service` (user-level systemd) sobreviva al logout. Requiere sudo una vez. Decision pendiente del usuario.
|