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).
┌─────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────┘
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/)| 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 |
bwrap (bubblewrap) para el paso Python en sandbox (solo Linux):bash
sudo apt install bubblewrap # Debian/UbuntuPara 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.
# 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.
cd frontend
npm install
npm run dev # Desarrollo (http://localhost:5173)
npm run build # Producción → dist/
# 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
Un solo panel para levantar backend + frontend juntos en modo desarrollo y
detenerlos/reiniciarlos sin usar comandos sueltos:
db-studio.bat (doble clic) / db-studio.ps1 — cada proceso./db-studio.sh — no hay forma portable de abrir "una.dbstudio-logs/ (la opción [5] del menú hace tail -f deMenú (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.
[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):
python3, el módulo venv,pip, node, npm y bwrap (bubblewrap, para el sandbox del pasoapt-get disponible, lo instalasudo apt-get install..venv-python si no existe, instalarequirements.txt, y copia .env.example → .env si todavía no hay uno.env existente).APP_DB_TYPE del .env: si essudo mysql (root local vía socket, mismo mecanismo que el Paso 4 delnpm install en frontend/.dbstudio-backend/dbstudio-frontend nosudo ./systemd/install.sh (liberagit pull).sudo ./systemd/uninstall.sh y no toca nadadb-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).
Todas van en el .env en la raíz del repo. Las marcadas con * son obligatorias en producció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) |
| 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).
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).
| Variable | Default | Descripción |
|---|---|---|
PORT |
3560 |
Puerto del backend |
ENABLE_DOCS |
true |
false = deshabilita Swagger en producción |
| 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 |
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 roldenied_scopes — scopes del rol que se bloquean individualmenteScopes efectivos = (rol.scopes ∪ extra_scopes) − denied_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
| 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 |
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.
| 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) |
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.
El paso python_transform corre en un sandbox real con bwrap (mismo aislamiento que Flatpak):
resource.setrlimit, aplicados dos veces (proceso externo e interno)pandas, numpy, math, datetime, json, re, statistics, itertools, collectionsjobs.log con SANDBOX_EXEC_START/OK/FAIL/TIMEOUTEl 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).
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.
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>.
| 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
POST /oauth/token Obtener JWT (usuario + contraseña)
/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
/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/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
/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
/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
/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
/users/v1/me)GET /users/v1/me/tab-state Obtener pestañas guardadas
PUT /users/v1/me/tab-state Guardar estado de pestañas
/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)
# 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
Los logs se generan en python_api/logs/ con rotación diaria automática.
| 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 |
[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
| 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.
# 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
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.
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.
Al iniciar sesión o recargar la página → el frontend:
localStorage (sin espera de red)GET /users/v1/me/tab-state al servidor (fuente de verdad cross-device)Si el servidor tiene un estado más reciente, lo aplica sobre el localStorage
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.
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 |
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)