Menu

Tree [50311e] master /
 History

HTTPS access


File Date Author Commit
 .claude 2026-07-02 Cristian Gaitán Cristian Gaitán [3c2dbd] Agregado logs, ajuste roles, manejo de jobs, y más
 docs 1 day ago Cristian Gaitán Cristian Gaitán [50311e] muchos ajustes
 frontend 1 day ago Cristian Gaitán Cristian Gaitán [50311e] muchos ajustes
 migration 2026-07-08 Cristian Gaitán Cristian Gaitán [709600] Ajuste graficas
 nginx 4 days ago Cristian Gaitán Cristian Gaitán [e2b83e] Ajuste permisos por conexion
 python_api 1 day ago Cristian Gaitán Cristian Gaitán [50311e] muchos ajustes
 systemd 4 days ago Cristian Gaitán Cristian Gaitán [e2b83e] Ajuste permisos por conexion
 tests 1 day ago Cristian Gaitán Cristian Gaitán [50311e] muchos ajustes
 .env.example 1 day ago Cristian Gaitán Cristian Gaitán [50311e] muchos ajustes
 .gitattributes 2026-07-04 Camilo Gaitán Camilo Gaitán [8c1a70] Ajuste DB Studio para optimización
 .gitignore 4 days ago Cristian Gaitán Cristian Gaitán [e2b83e] Ajuste permisos por conexion
 .gitlab-ci.yml 2026-07-06 Cristian Gaitán Cristian Gaitán [277560] Ajuste migraciones, optimización para producción
 README.md 2026-07-11 Cristian Gaitán Cristian Gaitán [4f71ff] Ajuste instalador
 acl_users.json.example 4 days ago Cristian Gaitán Cristian Gaitán [e2b83e] Ajuste permisos por conexion
 db-studio.bat 2026-07-04 Camilo Gaitán Camilo Gaitán [8c1a70] Ajuste DB Studio para optimización
 db-studio.ps1 2026-07-04 Camilo Gaitán Camilo Gaitán [8c1a70] Ajuste DB Studio para optimización
 db-studio.sh 1 day ago Cristian Gaitán Cristian Gaitán [50311e] muchos ajustes
 hash_password.py 2026-07-01 Cristian Gaitán Cristian Gaitán [ed93fc] Nueva Versión
 requirements.txt 1 day ago Cristian Gaitán Cristian Gaitán [50311e] muchos ajustes
 run-dev-mysql.sh 2026-07-01 Cristian Gaitán Cristian Gaitán [ed93fc] Nueva Versión
 run-dev-sqlite.sh 2026-07-01 Cristian Gaitán Cristian Gaitán [ed93fc] Nueva Versión
 run-python-api.sh 2026-07-04 Camilo Gaitán Camilo Gaitán [8c1a70] Ajuste DB Studio para optimización
 sonar-project.properties 2026-07-01 Cristian Gaitán Cristian Gaitán [ed93fc] Nueva Versión

Read Me

AECSA DB Studio

Plataforma de administración de bases de datos con interfaz visual estilo DBeaver/Workbench,
motor de pipelines de datos (jobs), gráficas, reportes PDF, gestor de bloques de transformación
reutilizables y persistencia de sesión por usuario (pestañas entre dispositivos).


Tabla de contenidos

  1. Arquitectura general
  2. Componentes principales
  3. Instalación y arranque
  4. Variables de entorno
  5. Sistema de permisos (ACL)
  6. Motor de jobs y pipelines
  7. API REST
  8. Logs y monitoreo
  9. Persistencia de sesión
  10. Estructura de archivos

Arquitectura general

┌─────────────────────────────────────────────────────────────┐
  Frontend  (React 19 + Vite)                                
   Editor SQL con autocompletado por esquema (CodeMirror)   
   Canvas visual de pipelines drag & drop (@xyflow/react)   
   Dashboard, Gráficas (Recharts), Reportes PDF             
   Pestañas persistentes por usuario (cross-device)         
└────────────────────────┬────────────────────────────────────┘
                          HTTP / REST  (Authorization: Bearer <JWT>)
┌────────────────────────▼────────────────────────────────────┐
  Backend  (FastAPI + SQLAlchemy 2.0 + Python 3.11+)         
   /db/v1/*                Administración de BDs            │
│  • /jobs/v1/*              Motor de pipelines               │
│  • /charts/v1/*            Gráficas guardadas               │
│  • /reports/v1/*           Reportes PDF                     │
│  • /transform-blocks/v1/*  Bloques reutilizables            │
│  • /connections/v1/*       Conexiones dinámicas desde UI    │
│  • /users/v1/me/*          Preferencias del usuario         │
│  • /admin/users|roles/*    Gestión de usuarios y roles ACL  │
│  • /oauth/token            Autenticación JWT                │
│  • /mongo /redis /qdrant   Bases NoSQL                      │
└──────┬──────────────────────────┬───────────────────────────┘
       │                          │
┌──────▼──────┐          ┌────────▼──────────────────────────┐
│ app_state   │          │  Bases de datos administradas      │
│ (MySQL /    │          │  MySQL / PostgreSQL / SQLite /     │
│  PostgreSQL │          │  Oracle / MongoDB / Redis / Qdrant │
│  / SQLite)  │          │  (configuradas en .env o desde UI) │
│             │          └────────────────────────────────────┘
│ users_acl   │
│ roles       │
│ charts      │
│ jobs        │
│ job_runs    │
│ job_run_    │
│ steps       │
│ reports     │
│ transform_  │
│ blocks      │
│ db_         │
│ connections │
└─────────────┘

Componentes principales

Backend (python_api/)

Módulo Descripción
app/api/ Routers FastAPI — uno por recurso, con logging de auditoría
app/api/user_prefs.py GET/PUT /users/v1/me/tab-state — persistencia de pestañas cross-device
app/services/jobs_engine.py Motor de ejecución de pipelines. 20+ tipos de paso: SQL, transformación, calidad, flujo, Python en sandbox
app/services/jobs_service.py CRUD de jobs, disparo de corridas (manual/scheduler), aislamiento por usuario
app/services/transform_blocks_service.py CRUD de bloques de transformación reutilizables
app/services/charts_service.py CRUD de gráficas + ejecución de consultas via CrudService
app/services/reports_service.py Generación de PDFs con ReportLab
app/services/connections_service.py Gestión de conexiones dinámicas: sincroniza .env ↔ BD, carga al DatabaseManager
app/services/python_sandbox.py Sandbox real con bwrap: aislamiento de red/filesystem/CPU/RAM + logging de auditoría
app/core/acl.py Motor de autorización por scopes (40+ scopes, scope admin bypasea todo)
app/core/acl_users.py CRUD de usuarios ACL en la BD (users_acl)
app/core/security.py Emisión y validación de JWT (RS256 / HS256)
app/db/models_app.py Modelos ORM: UserModel (con tab_state), ChartModel, JobModel, etc.
app/db/app_state.py BD de estado propio (migraciones in-place automáticas al arrancar)
app/support/audit_logger.py Logging estructurado por categoría: ops, security, jobs, errors
app/http/middlewares/audit_log.py Middleware que registra cada request HTTP con usuario/ip/status/duración

Frontend (frontend/)

Componente Descripción
store/AppContext.jsx Estado global: auth, tabs (con persistencia cross-device), theme, scopes
api/client.js Cliente HTTP (axios): interceptores de auth, manejo de errores, funciones de API
App.jsx Shell principal: routing contextual por tipo de pestaña, sidebar condicional
components/TabsBar.jsx Barra de pestañas estilo escritorio con cierre y persistencia automática
components/StatusBar.jsx Barra de estado inferior (resultado de última operación + duración en ms)
components/SqlEditorTab.jsx Editor SQL con autocompletado de esquema via CodeMirror
components/JobCanvas.jsx Canvas visual de pipelines drag & drop (@xyflow/react)
components/JobEditorTab.jsx Editor completo de job (canvas + metadatos + scheduler)
components/ChartsTab.jsx Vista de gráficas con Recharts
components/DashboardTab.jsx Dashboard de inicio con accesos rápidos y métricas
components/TransformBlockFormModal.jsx Editor de bloques de transformación (sub-canvas)
components/ScopeBuilder.jsx Constructor visual de permisos ACL

Instalación y arranque

Requisitos

  • Python 3.11+
  • Node.js 18+
  • MySQL 8+ o PostgreSQL 14+ (para las bases administradas y/o la BD de estado de la app)
  • bwrap (bubblewrap) para el paso Python en sandbox (solo Linux):
    bash sudo apt install bubblewrap # Debian/Ubuntu

Para una instalación completa desde cero (servidor nuevo, producción con
TLS/systemd/Alembic incluidos) ver la guía paso a paso en
manual-instalacion.md. Lo de acá
abajo es el arranque rápido para desarrollo local.

Backend

# requirements.txt vive en la raíz del repo, no en python_api/
pip install -r requirements.txt

# Copiar y editar el archivo de configuración
cp .env.example .env
# Editar .env: agregar JWT_SECRET, conexiones de BD, etc.

# Arrancar (desarrollo)
./run-dev-mysql.sh        # Con MySQL como BD de estado — puerto 3560
# o
./run-dev-sqlite.sh       # Con SQLite (sin dependencias externas) — puerto 3550,
                          # para poder correrlo en paralelo con el anterior
# Swagger completo:         /docs  o  /swagger
# Swagger SQL + consultas:  /swagger-consultas
# Swagger solo API datos:   /swagger-api

También hay un panel de control multiplataforma (db-studio.bat/.ps1 en
Windows, db-studio.sh en Linux/macOS) que levanta backend + frontend juntos
y permite detenerlos/reiniciarlos sin usar comandos sueltos — ver
Panel de control.

Frontend

cd frontend
npm install
npm run dev        # Desarrollo (http://localhost:5173)
npm run build      # Producción → dist/

Primer usuario (con ENABLE_ACL=true)

# Opción A: Usar el script de hash + endpoint REST
# (hash_password.py vive en la raíz del repo, no en python_api/)
python hash_password.py MiPassword123!
# → Copiar el hash y crear el usuario via POST /admin/users

# Opción B: Importar desde el ejemplo (antes del primer arranque)
cp acl_users.json.example acl_users.json
# Al arrancar, la app migra automáticamente el JSON a la BD

Panel de control (Windows / Linux / macOS)

Un solo panel para levantar backend + frontend juntos en modo desarrollo y
detenerlos/reiniciarlos sin usar comandos sueltos:

  • Windows: db-studio.bat (doble clic) / db-studio.ps1 — cada proceso
    se abre en su propia ventana de consola, para ver sus logs en vivo.
  • Linux / macOS: ./db-studio.sh — no hay forma portable de abrir "una
    ventana nueva" en Linux, así que cada proceso corre en segundo plano con
    sus logs en .dbstudio-logs/ (la opción [5] del menú hace tail -f de
    ambos).

Menú (Windows tiene 5 opciones fijas; Linux/macOS agrega logs e instalación):

 [1] Iniciar (backend + frontend)
 [2] Detener / pausar
 [3] Reiniciar
 [4] Ver estado
 [5] Ver logs en vivo (solo Linux/macOS; Ctrl+C para volver al menú)
 [6] Instalar / actualizar / desinstalar (solo Linux/macOS — ver abajo)
 [7] Salir del panel (los procesos siguen corriendo)

"Detener" busca el proceso escuchando en el puerto del backend (PORT
del .env, 3560 por defecto) y del frontend (5173) y lo termina — funciona
sin importar si se arrancó desde este panel o manualmente, y "Ver estado"
usa el mismo mecanismo para no depender de un archivo de PIDs que pueda
quedar desactualizado. En Linux/macOS la detección usa lsof (o ss/fuser
como respaldo si lsof no está instalado) y el Detener envía primero
SIGTERM (apagado limpio) antes de forzar con SIGKILL si no responde.
Salir del panel no detiene los procesos, solo cierra el menú.

Todas las acciones del menú (iniciar/detener/reiniciar/instalar/salir)
quedan registradas con fecha, usuario y puerto en
.dbstudio-logs/control.log — la opción [5] hace tail -f de ese archivo
junto con backend.log/frontend.log, así que sirve como bitácora de
monitoreo y control de quién hizo qué y cuándo.

Opción [6] — Instalar / actualizar / desinstalar (Linux/macOS)

Automatiza de punta a punta lo que hasta ahora eran varios pasos manuales
(ver Instalación y arranque arriba y la guía
completa en manual-instalacion.md):

  1. Dependencias del sistema — verifica python3, el módulo venv,
    pip, node, npm y bwrap (bubblewrap, para el sandbox del paso
    Python de jobs); si falta algo y hay apt-get disponible, lo instala
    con sudo apt-get install.
  2. Backend — crea .venv-python si no existe, instala
    requirements.txt, y copia .env.example.env si todavía no hay uno
    (no pisa un .env existente).
  3. Base de datos del aplicativo — lee APP_DB_TYPE del .env: si es
    SQLite (vacío) no hace nada (se crea sola); si es MySQL, verifica que la
    base y el usuario configurados existan y, si no, los crea con
    sudo mysql (root local vía socket, mismo mecanismo que el Paso 4 del
    manual de instalación) — las tablas las crea Alembic solas en el primer
    arranque del backend.
  4. Frontend — corre npm install en frontend/.
  5. Servicio systemd — si dbstudio-backend/dbstudio-frontend no
    están instalados, pregunta y corre sudo ./systemd/install.sh (libera
    antes los puertos si hay procesos de modo dev corriendo). Si ya
    están instalados, en vez de reinstalar a ciegas pregunta:
  6. Actualizar → repite los pasos 1-5 (útil tras un git pull).
  7. Desinstalar → corre sudo ./systemd/uninstall.sh y no toca nada
    más (no reinstala backend/frontend).

db-studio.sh es para uso manual/interactivo (modo dev, procesos con
nohup). Para producción (arranque automático + reinicio si el proceso
muere) usa la opción [6] de arriba, o los mismos scripts a mano (ver
Systemd (backend + frontend como servicio)):

sudo ./systemd/install.sh      # instalar/actualizar
sudo ./systemd/uninstall.sh    # desinstalar

Backend y frontend escuchan en 127.0.0.1 (sin TLS) — para exponerlos con
HTTPS hace falta un reverse proxy delante. Si no hay ya uno de
infraestructura, nginx/dbstudio.conf.template trae una plantilla lista
(TLS, cabeceras X-Forwarded-*, proxy_buffering off para los endpoints SSE
de logs en tiempo real, freno de fuerza bruta adicional en /oauth/token):

sudo DBSTUDIO_FRONTEND_DOMAIN=app.midominio.com \
     DBSTUDIO_API_DOMAIN=api.midominio.com \
     DBSTUDIO_CERT_PATH=/etc/letsencrypt/live/midominio.com/fullchain.pem \
     DBSTUDIO_CERT_KEY_PATH=/etc/letsencrypt/live/midominio.com/privkey.pem \
     ./nginx/install.sh

Es opcional y no se instala solo — si ya hay un proxy manejando esto, no
correr install.sh (la plantilla sirve igual como referencia de qué rutas/
cabeceras necesita). curl -sI https://<dominio> | grep X-Dbstudio-Proxy-Template
confirma si es esta plantilla la que está respondiendo.

El esquema de la base propia del app (db_studio) se versiona con Alembic
(python_api/alembic/) — se aplica solo al arrancar (init_app_state_db()),
sin pasos manuales, incluso en una base que ya existía de antes de Alembic
(la "bautiza" contra la migración baseline sin re-ejecutar su SQL). Ver
Migraciones de esquema (Alembic).


Variables de entorno

Todas van en el .env en la raíz del repo. Las marcadas con * son obligatorias en producción.

Seguridad y autenticación

Variable Default Descripción
JWT_SECRET * Clave para firmar JWTs. Mínimo 32 caracteres aleatorios
ENABLE_ACL false true = requiere login y aplica scopes
ALLOW_RAW_SQL false true = permite exec-query sin CTE (requiere scope db:xxx:sql)
CORS_ORIGINS * Orígenes permitidos para CORS (lista separada por comas en prod)

Base de datos de estado de la app

Variable Default Descripción
APP_DB_TYPE (vacío = sqlite) mysql, postgresql u oracle. Vacío usa SQLite sin configuración adicional
APP_DB_HOST Host (y opcionalmente :puerto, ej. localhost:3306)
APP_DB_USER Usuario de conexión
APP_DB_PASSWORD Contraseña
APP_DB_NAME app_state.db Nombre de la base (o ruta del archivo si es SQLite)
APP_DB_SOCKET Socket Unix opcional en vez de APP_DB_HOST (solo MySQL local)
# Ejemplo con MySQL:
APP_DB_TYPE=mysql
APP_DB_HOST=servidor:3306
APP_DB_USER=usuario
APP_DB_PASSWORD=clave
APP_DB_NAME=db_studio

# Ejemplo con PostgreSQL:
APP_DB_TYPE=postgresql
APP_DB_HOST=servidor:5432
APP_DB_USER=usuario
APP_DB_PASSWORD=clave
APP_DB_NAME=db_studio

Las tablas se crean y migran automáticamente al arrancar (sin herramientas de migración manuales).

Servidores de BD administrados

El patrón de variables de entorno para servidores es:

DB_SERVERS=NOMBRE1,NOMBRE2          # Lista de servidores separados por coma

NOMBRE1_TYPE=mysql                  # mysql | postgresql | sqlite | oracle | mongodb | redis | qdrant
NOMBRE1_HOST=servidor:3306          # host:puerto (puerto embebido en el host)
NOMBRE1_USER=usuario
NOMBRE1_PASSWORD=contraseña

Alternativamente, las conexiones se pueden gestionar desde la UI sin reiniciar la app (módulo Conexiones, scope connections:write).

Servidor HTTP

Variable Default Descripción
PORT 3560 Puerto del backend
ENABLE_DOCS true false = deshabilita Swagger en producción

Jobs y sandbox Python

Variable Default Descripción
JOB_OUTPUTS_DIR job_outputs/ Archivos generados por pasos de exportación
JOB_UPLOADS_DIR job_uploads/ CSVs/XLSXs subidos para pasos file_input
JOB_UPLOAD_MAX_MB 50 Tamaño máximo de archivo subido
PYTHON_SANDBOX_TIMEOUT_SECONDS 30 Timeout de pared para sandbox Python
PYTHON_SANDBOX_CPU_SECONDS 10 Límite de CPU para sandbox Python
PYTHON_SANDBOX_MEMORY_MB 512 Límite de RAM para sandbox Python
PYTHON_SANDBOX_DIR job_sandbox/ Directorio de trabajo temporal del sandbox

Sistema de permisos (ACL)

Con ENABLE_ACL=false (default): cualquier JWT válido tiene acceso total.
Con ENABLE_ACL=true: cada usuario tiene un conjunto de scopes. El scope admin bypasea todos los demás.

Los usuarios pueden tener asignado un rol (conjunto de scopes reutilizable) con personalizaciones individuales:

  • extra_scopes — scopes adicionales sobre el rol
  • denied_scopes — scopes del rol que se bloquean individualmente

Scopes efectivos = (rol.scopes ∪ extra_scopes) − denied_scopes

Grammar completo de scopes

admin                               Acceso total  bypasea todo lo demás

# Bases de datos administradas
db:<bd>:read                        Leer cualquier tabla de <bd>
db:<bd>:write                       Escribir/actualizar/borrar en <bd>
db:<bd>:schema                      Crear/editar vistas, procedimientos, triggers
db:<bd>:sql                         Ejecutar SQL crudo (exec-query) y CTEs
db:<bd>:table:<tabla>:read          Lectura granular a una tabla específica
db:<bd>:table:<tabla>:write         Escritura granular a una tabla específica
db:*:read / db:*:write / etc.       Wildcard  aplica a todas las BDs

# Herramientas globales
download                            Exportar resultados como CSV/XLSX/JSON
copy                                Copiar datos de la grilla al portapapeles (UX)

# Módulos de la aplicación
charts:read / charts:write          Gráficas guardadas (definiciones)
reports:read / reports:write        Reportes PDF (definiciones + generación)
jobs:read / jobs:write              Definiciones de jobs (pipeline + cron)
jobs:execute                        Disparar corridas manuales de jobs
jobs:python                         Guardar pasos con código Python (sandbox)
transform_blocks:read / :write      Bloques de transformación reutilizables
logs:read                           Ver logs de auditoría en /admin/logs (histórico + SSE en vivo)

# Conexiones a BD (gestión dinámica desde UI)
connections:read                    Ver conexiones propias y compartidas
connections:write                   Crear/editar/borrar conexiones propias
                                     (alcance real: puede redirigir una BD ya usada
                                     por otros, ver docs/dev/acl-seguridad.md)
connections:admin                   Gestionar todas + marcar como compartidas

# NoSQL
nosql:read                          Leer datos de MongoDB/Redis/Qdrant
nosql:write                         Escribir datos en MongoDB/Redis/Qdrant
nosql:admin                         Administrar colecciones y estructuras NoSQL

Perfiles típicos

Perfil Scopes
Admin admin
Desarrollador full db:*:read write sql schema + todos los módulos + connections:admin
Analista de datos db:xxx:read sql download charts:read reports:read jobs:read jobs:execute
Operador de jobs db:xxx:read sql jobs:read jobs:write jobs:execute
Lector externo db:xxx:read download
Integración (API) jobs:read charts:read db:xxx:sql

Aislamiento por usuario

Todos los recursos de la app (jobs, gráficas, reportes, bloques, conexiones) tienen un campo
owner_username. Los usuarios sin scope admin solo ven y modifican sus propios recursos.
Los usuarios con admin ven todos.


Motor de jobs y pipelines

Tipos de paso disponibles

Categoría Tipos
Entrada sql_source, generate_rows (plantilla o tabla), file_input (CSV/XLSX)
Salida save_table, export_file (CSV/XLSX/JSON/JSON_objeto/XML), render_chart (PNG), generate_report (PDF)
Transformación filter_rows, sort_rows, select_values, calculator, group_by, pivot_table
Uniones join_rows (SQL join via pandas), union_rows
Flujo dummy, switch_case
Calidad de datos data_cleanse, data_validate
Script python_transform (sandbox bwrap — solo Linux)
Bloques transform_block (referencia a un bloque reutilizable guardado)

Encadenamiento multi-servidor

Un paso sql_source que recibe datos de un paso anterior los materializa como tabla temporal
en el servidor destino, permitiendo JOINs entre datos de distintos servidores (MySQL, PostgreSQL,
SQLite, Oracle) sin conexión directa entre ellos. Los datos viajan en memoria vía Python.

Seguridad del sandbox Python

El paso python_transform corre en un sandbox real con bwrap (mismo aislamiento que Flatpak):

  • Sin acceso a red — namespace de red separado a nivel de kernel; no evadible desde Python
  • Filesystem de solo lectura — salvo un directorio temporal propio de la corrida
  • Límites de CPU y RAM — via resource.setrlimit, aplicados dos veces (proceso externo e interno)
  • Timeout de pared — configurable, mata el proceso y todos sus hijos
  • Allowlist de importspandas, numpy, math, datetime, json, re, statistics, itertools, collections
  • Auditoría — cada ejecución queda registrada en jobs.log con SANDBOX_EXEC_START/OK/FAIL/TIMEOUT

El código del usuario recibe df (DataFrame con los datos de entrada) y pd (pandas), y debe
asignar el resultado a la variable result (DataFrame o lista de dicts).

Scheduler de corridas automáticas

Los jobs pueden tener un schedule_cron (expresión cron estándar, ej. 0 6 * * *). APScheduler
ejecuta las corridas programadas con el usuario propietario del job, heredando sus scopes — no hay
un usuario de sistema con privilegios especiales.


API REST

Autenticación

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

username=ana&password=secreto

Respuesta: {"access_token": "<JWT>", "token_type": "bearer"}

Todos los demás endpoints requieren Authorization: Bearer <token>.

Documentación interactiva

URL Contenido
/docs o /swagger Swagger completo — todos los endpoints
/swagger-consultas Solo exec-query, CTE, export + endpoints de consumo de datos
/swagger-api Solo API de consumo (para integrar con Grafana, Power BI, scripts externos)

docs/swagger/*.json y docs/api/*.yaml son snapshots exportados de estos tres endpoints (para compartir la especificación sin necesidad de tener el backend corriendo — importarlos en Postman/Insomnia, etc.). No se generan solos — si agregás o quitás un endpoint, regenerarlos con el backend corriendo:

pip install pyyaml   # una sola vez
python docs/generate_openapi_exports.py

Referencia de endpoints

Autenticación

POST   /oauth/token                             Obtener JWT (usuario + contraseña)

Bases de datos administradas (/db/v1)

GET    /db/v1/databases                         Listar BDs disponibles
GET    /db/v1/{db}/tables                       Listar tablas de una BD
GET    /db/v1/{db}/tables/{table}/rows          Leer filas con paginación
POST   /db/v1/{db}/tables/{table}/rows          Insertar fila
PUT    /db/v1/{db}/tables/{table}/rows/{pk}     Actualizar fila
DELETE /db/v1/{db}/tables/{table}/rows/{pk}     Borrar fila
POST   /db/v1/exec-query                        Ejecutar SQL crudo (requiere ALLOW_RAW_SQL)
POST   /db/v1/cte                               Ejecutar CTE (siempre disponible)
GET    /db/v1/export                            Exportar resultados como CSV/XLSX

Gráficas (/charts/v1)

GET    /charts/v1                               Listar gráficas del usuario
POST   /charts/v1                               Crear gráfica
GET    /charts/v1/{id}                          Obtener definición
PUT    /charts/v1/{id}                          Actualizar definición
DELETE /charts/v1/{id}                          Eliminar
POST   /charts/v1/{id}/data                     Ejecutar consulta(s) y devolver filas

Una gráfica puede tener una o más sources (cada una con su propia base/consulta/parámetros); si hay más de una, se ejecutan en paralelo y se combinan según config.mergeMode ("join": outer-join por el eje X con columnas namespaced src{n}_columna; "concat": se apilan las filas). El body opcional de POST /charts/v1/{id}/data ({"parameters": {...}}) sobreescribe esos parámetros en todas las fuentes — es el mismo mecanismo que usa el filtro por rango de ChartsTab (ver docs/dev/backend.md).

Jobs (/jobs/v1)

GET    /jobs/v1                                 Listar jobs del usuario
POST   /jobs/v1                                 Crear job
GET    /jobs/v1/{id}                            Obtener definición
PUT    /jobs/v1/{id}                            Actualizar
DELETE /jobs/v1/{id}                            Eliminar
POST   /jobs/v1/{id}/run                        Ejecutar manualmente
GET    /jobs/v1/{id}/runs                       Historial de corridas
GET    /jobs/v1/runs/{run_id}                   Detalle de corrida (con pasos)
GET    /jobs/v1/{id}/data/{step_id}             Datos del último paso exitoso (JSON)
GET    /jobs/v1/{id}/latest-output/{step_id}    Archivo del último paso exitoso
GET    /jobs/v1/runs/{run_id}/download/{step}   Descargar archivo de una corrida
GET    /jobs/v1/{id}/export                     Exportar job como JSON portable
POST   /jobs/v1/import                          Importar job desde JSON
POST   /jobs/v1/uploads                         Subir CSV/XLSX para paso file_input

Reportes PDF (/reports/v1)

GET    /reports/v1                              Listar reportes del usuario
POST   /reports/v1                              Crear reporte
GET    /reports/v1/{id}                         Obtener definición
PUT    /reports/v1/{id}                         Actualizar
DELETE /reports/v1/{id}                         Eliminar
POST   /reports/v1/{id}/generate                Generar y descargar PDF

Bloques de transformación (/transform-blocks/v1)

GET    /transform-blocks/v1                     Listar bloques del usuario
POST   /transform-blocks/v1                     Crear bloque
GET    /transform-blocks/v1/{id}                Obtener definición
PUT    /transform-blocks/v1/{id}                Actualizar
DELETE /transform-blocks/v1/{id}                Eliminar

Conexiones (/connections/v1)

GET    /connections/v1                          Listar conexiones visibles
POST   /connections/v1                          Crear conexión
GET    /connections/v1/{id}                     Obtener conexión
PUT    /connections/v1/{id}                     Actualizar
DELETE /connections/v1/{id}                     Eliminar
POST   /connections/v1/{id}/test                Probar conectividad

Preferencias del usuario (/users/v1/me)

GET    /users/v1/me/tab-state                   Obtener pestañas guardadas
PUT    /users/v1/me/tab-state                   Guardar estado de pestañas

Administración (/admin)

GET    /admin/users                             Listar usuarios ACL
POST   /admin/users                             Crear usuario
PUT    /admin/users/{username}                  Actualizar usuario
DELETE /admin/users/{username}                  Eliminar usuario
GET    /admin/roles                             Listar roles
POST   /admin/roles                             Crear rol
PUT    /admin/roles/{id}                        Actualizar rol
DELETE /admin/roles/{id}                        Eliminar rol
GET    /admin/logs                              Ver logs de auditoría (paginados)
GET    /admin/resources                         Ver métricas de sistema (CPU/RAM/disco)

Endpoints para integración externa (Grafana, Power BI, scripts)

# Datos normalizados del último resultado exitoso de un paso (polling periódico)
GET /jobs/v1/{job_id}/data/{step_id}
# → {"data": [{col1: val, col2: val, ...}, ...], "count": N}

# Archivo del último resultado (CSV, JSON, XML, PNG, XLSX) — sin forzar descarga
GET /jobs/v1/{job_id}/latest-output/{step_id}
# → archivo con Content-Type apropiado

# Datos en tiempo real de una gráfica guardada (si tiene varias fuentes SQL,
# se corren en paralelo y se combinan segun config.mergeMode — ver más abajo)
POST /charts/v1/{chart_id}/data
Body: {"parameters": {"fecha_inicio": "2026-01-01"}}  # opcional — sobreescribe ese parámetro en TODAS las fuentes
# → {"data": [...filas...], "meta": {"count": N}}

# Generar PDF de un reporte
POST /reports/v1/{report_id}/generate
# → archivo PDF como attachment

Logs y monitoreo

Los logs se generan en python_api/logs/ con rotación diaria automática.

Archivos de log

Archivo Contenido Retención
logs/ops.log Requests normales (< 400): consultas, exports, lecturas, estado de pestañas guardado 30 días
logs/security.log Login, logout, creación/modificación/eliminación de usuarios y roles 90 días
logs/jobs.log Ciclo de vida de jobs y sandbox Python: creación, corridas, pasos, sandbox exec 30 días
logs/errors.log Requests con status ≥ 400, excepciones inesperadas, recursos no encontrados 7 días

Formato de línea

[YYYY-MM-DD HH:MM:SS] NIVEL     dbstudio.categoria  EVENTO  clave1=valor1  clave2=valor2  ...

Ejemplos reales:

[2026-07-02 21:15:04] INFO     dbstudio.ops       REQUEST_OK        method=POST  path=/db/v1/cte  user=admin  ip=127.0.0.1  status=200  duration_ms=12
[2026-07-02 21:15:10] INFO     dbstudio.security  LOGIN_OK          username=ana  ip=192.168.1.5  status=200  duration_ms=38
[2026-07-02 21:16:00] INFO     dbstudio.jobs      RUN_START         job_id=3  job_name="Export diario"  run_id=17  trigger=schedule  user=sistema  steps=4
[2026-07-02 21:16:02] INFO     dbstudio.jobs      RUN_COMPLETE      job_id=3  job_name="Export diario"  run_id=17
[2026-07-02 21:16:05] INFO     dbstudio.jobs      SANDBOX_EXEC_START  input_rows=1200  timeout=30  cpu_limit=10  mem_limit_mb=512
[2026-07-02 21:16:06] INFO     dbstudio.jobs      SANDBOX_EXEC_OK   output_rows=1200
[2026-07-02 21:16:10] INFO     dbstudio.ops       CHART_CREATED     user=ana  chart_id=7  chart="Ventas por mes"  sources=1  chart_type=bar
[2026-07-02 21:17:00] INFO     dbstudio.ops       TAB_STATE_SAVED   user=ana  tab_count=5  active=tab-3
[2026-07-02 21:40:01] WARNING  dbstudio.security  LOGIN_FAIL        username=hacker  ip=203.0.113.42  status=401  duration_ms=250
[2026-07-02 21:41:00] WARNING  dbstudio.errors    CLIENT_ERROR      method=DELETE  path=/jobs/v1/99  user=beto  ip=10.0.0.1  status=404  duration_ms=5

Tabla de eventos auditados

Evento Archivo Nivel Descripción
LOGIN_OK security.log INFO Inicio de sesión exitoso
LOGIN_FAIL security.log WARNING Contraseña incorrecta o usuario inexistente
USER_CREATED security.log INFO Nuevo usuario creado por admin
USER_UPDATED security.log INFO Usuario modificado (password, rol, scopes)
USER_DELETED security.log INFO Usuario eliminado
JOB_CREATED jobs.log INFO Nuevo job guardado
JOB_UPDATED jobs.log INFO Pipeline o cron de job modificado
JOB_DELETED jobs.log INFO Job eliminado
RUN_START jobs.log INFO Corrida iniciada (manual o schedule)
RUN_COMPLETE jobs.log INFO Corrida completada exitosamente
RUN_FAILED jobs.log WARNING Corrida fallida
RUN_STEP_FAIL jobs.log WARNING Paso individual fallido dentro de una corrida
SANDBOX_EXEC_START jobs.log INFO Inicio de ejecución en sandbox Python
SANDBOX_EXEC_OK jobs.log INFO Sandbox completado exitosamente
SANDBOX_EXEC_FAIL jobs.log WARNING Error en código Python del sandbox
SANDBOX_EXEC_TIMEOUT jobs.log WARNING Sandbox superó el timeout
CHART_CREATED ops.log INFO Nueva gráfica guardada (incluye chart_type y cantidad de sources)
CHART_UPDATED ops.log INFO Gráfica modificada (incluye chart_type actual)
CHART_DELETED ops.log INFO Gráfica eliminada
CHART_QUERY_OK ops.log INFO Datos de gráfica ejecutados (POST /charts/v1/{id}/data) — incluye las databases de cada fuente y filas ya combinadas
CHART_NOT_FOUND errors.log WARNING Se pidió una gráfica inexistente o de otro usuario (sin scope admin)
REPORT_CREATED ops.log INFO Nuevo reporte guardado
REPORT_UPDATED ops.log INFO Reporte modificado (nombre, descripción o lista de gráficas)
REPORT_DELETED ops.log INFO Reporte eliminado
REPORT_PDF_START ops.log INFO Inicio de generación de PDF
REPORT_PDF_OK ops.log INFO PDF generado exitosamente
BLOCK_CREATED ops.log INFO Nuevo bloque de transformación guardado
BLOCK_UPDATED ops.log INFO Bloque modificado
BLOCK_DELETED ops.log INFO Bloque eliminado
TAB_STATE_SAVED ops.log INFO Estado de pestañas guardado para el usuario
REQUEST_OK ops.log INFO Request HTTP exitoso (middleware)
CLIENT_ERROR errors.log WARNING Error 4xx
SERVER_ERROR errors.log ERROR Error 5xx
CIRCUIT_BREAKER_CPU_ON / _MEM_ON / _DISK_ON ops.log WARNING CPU/memoria/disco superó el umbral — nuevas corridas de jobs rechazadas con 503
CIRCUIT_BREAKER_CPU_OFF / _MEM_OFF / _DISK_OFF ops.log INFO El recurso volvió a estar bajo el umbral
JOB_FILE_CLEANUP jobs.log INFO Limpieza diaria borró archivos viejos de job_outputs/job_uploads

Catálogo completo (incluye eventos de conexiones, NoSQL, restricción de columnas, etc.) en docs/dev/logging-monitoreo.md. Ese documento también cubre el log de control del panel db-studio (.dbstudio-logs/control.log) — quién arrancó/detuvo/reinició la app y cuándo, separado de estos logs de aplicación.

Consultas útiles sobre los logs

# Todos los logins fallidos en la última hora
grep "LOGIN_FAIL" logs/security.log | tail -50

# Corridas de job fallidas hoy
grep "RUN_FAILED\|RUN_STEP_FAIL" logs/jobs.log | grep "$(date +%Y-%m-%d)"

# Errores del servidor (5xx)
grep "SERVER_ERROR" logs/errors.log | tail -100

# Actividad de un usuario específico
grep "user=ana" logs/ops.log | tail -50

# Ejecuciones de sandbox Python con timeout
grep "SANDBOX_EXEC_TIMEOUT" logs/jobs.log

Persistencia de sesión

Las pestañas abiertas se guardan automáticamente en la base de datos por usuario, permitiendo
restaurar la sesión exactamente igual al iniciar sesión desde cualquier dispositivo o navegador.

Cómo funciona

  1. Al abrir/cerrar/cambiar pestañas → el frontend guarda el estado en localStorage (instantáneo)
    y envía un PUT /users/v1/me/tab-state al servidor con debounce de 1 segundo.

  2. Al iniciar sesión o recargar la página → el frontend:

  3. Restaura instantáneamente desde localStorage (sin espera de red)
  4. Pide GET /users/v1/me/tab-state al servidor (fuente de verdad cross-device)
  5. Si el servidor tiene un estado más reciente, lo aplica sobre el localStorage

  6. Al cambiar de dispositivo → el servidor siempre tiene el último estado guardado; el nuevo
    dispositivo recibe exactamente las pestañas que el usuario tenía abiertas.

Tipos de pestaña persistidos

Todos los tipos se restauran correctamente al volver a iniciar sesión:

Tipo Datos guardados
sql Solo el tipo (el contenido del editor se gestiona por el componente)
table Nombre de la tabla
routine Nombre y tipo (procedure/function)
ddl Nombre, tipo de objeto, tabla propietaria
manage Tipo de objeto, nombre, DDL
jobs — (lista de jobs)
job-editor jobId (o null para nuevo job) + pipeline inicial
charts — (lista de gráficas)
reports — (lista de reportes)
connections — (lista de conexiones)
users — (panel de administración)
logs — (panel de logs)
resources — (métricas de sistema)
dashboard — (pantalla de inicio)
mongodb connId, base de datos, colección
redis connId
qdrant connId

Estructura de archivos

db-studio/
├── .env                          # Variables de entorno (no commitear)
├── .env.example                  # Plantilla de configuración
├── .gitattributes                # Fuerza LF en todo el repo (ver nota de line endings)
├── run-python-api.sh             # Arranque "de producción" (lee .env, sin datos de ejemplo)
├── run-dev-mysql.sh              # Script de arranque del backend (MySQL como BD de estado)
├── run-dev-sqlite.sh             # Script de arranque del backend (SQLite)
├── db-studio.bat / db-studio.ps1 # Panel de control (Windows): iniciar/detener/reiniciar front+back
├── db-studio.sh                  # Panel de control (Linux/macOS), mismo menú
├── systemd/                      # Unidades systemd + install.sh/uninstall.sh (backend+frontend como servicio)
├── nginx/                        # Plantilla opcional de reverse proxy TLS + install.sh
├── hash_password.py              # CLI para generar hashes bcrypt
├── acl_users.json.example        # Ejemplo de usuarios ACL para migración inicial
├── migration/                    # Dump + guía para migrar a un servidor nuevo

├── python_api/                   # Backend FastAPI
   ├── requirements.txt
   ├── app/
      ├── main.py               # App factory: routers, middlewares, Swagger x3
      ├── api/                  # Routers HTTP (uno por recurso)
         ├── charts.py         # /charts/v1/*
         ├── jobs.py           # /jobs/v1/*
         ├── reports.py        # /reports/v1/*
         ├── transform_blocks.py # /transform-blocks/v1/*
         ├── connections.py    # /connections/v1/*
         ├── user_prefs.py     # /users/v1/me/* (tab-state)
         ├── admin_users.py    # /admin/users/*
         ├── admin_roles.py    # /admin/roles/*
         ├── admin_logs.py     # /admin/logs/*
         ├── admin_resources.py # /admin/resources/*
         ├── oauth.py          # /oauth/token
         ├── mongo.py          # /mongo/v1/*
         ├── redis_api.py      # /redis/v1/*
         └── qdrant_api.py     # /qdrant/v1/*
      ├── core/
         ├── acl.py            # Motor de scopes (40+ scopes documentados)
         ├── acl_users.py      # CRUD de usuarios en BD, migración desde JSON
         ├── config.py         # Settings: clase simple sobre os.getenv() (.env → Python)
         └── security.py       # JWT: emisión, validación, extracción de scopes
      ├── db/
         ├── app_state.py      # Motor SQLAlchemy del estado propio + migraciones in-place
         ├── models_app.py     # ORM: UserModel (tab_state), ChartModel, JobModel, etc.
         ├── connection.py     # Pool de conexiones a BDs administradas (DatabaseManager)
         └── validation.py     # Helper api_error() para errores HTTP uniformes
      ├── services/
         ├── jobs_engine.py    # Motor de pipelines: 20+ tipos de paso, ejecución en hilo
         ├── jobs_service.py   # CRUD + trigger + scheduler de jobs
         ├── jobs_scheduler.py # APScheduler: corridas cron automáticas
         ├── python_sandbox.py # Sandbox bwrap: aislamiento total + logging de auditoría
         ├── charts_service.py # CRUD de gráficas + ejecución via CrudService
         ├── reports_service.py # Generación de PDF con ReportLab
         ├── connections_service.py # Gestión de conexiones dinámicas
         ├── transform_blocks_service.py # CRUD de bloques reutilizables
         ├── crud_service.py   # Consultas/CRUD sobre las BDs administradas
         ├── resource_monitor.py # CPU/memoria/disco cada 2s + circuit breakers
         ├── mongo_service.py  # Operaciones MongoDB
         ├── redis_service.py  # Operaciones Redis
         └── qdrant_service.py # Operaciones Qdrant
      ├── http/
         ├── kernel.py         # Registro de middlewares y handlers de excepción
         ├── dependencies.py   # Dependencias FastAPI compartidas
         └── middlewares/
             ├── audit_log.py  # Logging de cada request HTTP (ops/security/errors)
             ├── error_handler.py # Manejo uniforme de excepciones → JSON
             ├── ip_allowlist.py  # Allowlist de IPs (opt-in)
             └── request_context.py # Context var para usuario/IP en el hilo
      ├── models/               # Pydantic request/response models
      └── support/
          ├── audit_logger.py   # Loggers por categoría con rotación diaria
          ├── crypto.py         # Cifrado de contraseñas de conexiones (XOR + JWT_SECRET)
          ├── dates.py          # Normalización de fechas GMT-5
          └── file_export.py    # Exportación CSV/XLSX/JSON/XML
   └── logs/                     # Logs generados en runtime
       ├── ops.log               # Operaciones normales
       ├── security.log          # Eventos de seguridad (90 días de retención)
       ├── jobs.log              # Ciclo de vida de jobs y sandbox
       └── errors.log            # Errores (7 días de retención)

├── frontend/                     # React 19 + Vite
   ├── package.json
   └── src/
       ├── App.jsx               # Shell principal: routing por tipo de pestaña, sidebar contextual
       ├── store/
          └── AppContext.jsx    # Estado global: auth, tabs (persistencia cross-device), theme
       ├── api/
          └── client.js         # Cliente HTTP axios: interceptores, manejo de errores, API calls
       ├── components/           # UI components
          ├── TabsBar.jsx       # Barra de pestañas con persistencia automática
          ├── StatusBar.jsx     # Barra de estado inferior con duración en ms
          ├── SqlEditorTab.jsx  # Editor SQL + autocompletado de esquema
          ├── JobCanvas.jsx     # Canvas visual drag & drop de pipelines
          ├── JobEditorTab.jsx  # Editor completo de job
          ├── ChartsTab.jsx     # Vista de gráficas con Recharts
          ├── DashboardTab.jsx  # Dashboard de inicio
          └── ...
       ├── hooks/
          └── useSqlCompletion.js # Autocompletado SQL por schema de BD
       └── utils/
           └── sqlUtils.js       # Detección de bindings y modo de query

├── job_outputs/                  # Archivos generados por pasos de exportación
├── job_uploads/                  # Archivos subidos para pasos file_input
└── job_sandbox/                  # Directorio temporal del sandbox Python (auto-limpiado)
Auth0 Logo