Back to 0fee
0fee

Migración a SQLAdmin: del admin SolidJS al backend integrado

Cómo migramos el panel de administración de 0fee.dev de un frontend SolidJS personalizado a SQLAdmin con 16 modelos y 12 vistas. Por Juste A. Gnimavo.

Juste A. Gnimavo (Thales) & Claude | March 27, 2026 3 min 0fee
EN/ FR/ ES
sqladminmigrationbackendormadmin-panel

El panel de administración personalizado que construimos en la sesión 020 funcionaba. Tenía rutas UUID, autenticación JWT y jerarquía de roles. Pero también tenía un problema: era una aplicación frontend completamente separada que necesitaba mantenimiento junto con el panel SolidJS principal y el sitio de marketing. Tres aplicaciones frontend para una plataforma eran dos de más.

En sesiones posteriores, tomamos la decisión de retirar el admin SolidJS personalizado y reemplazarlo con SQLAdmin -- una biblioteca Python que genera una interfaz de administración directamente desde modelos ORM de SQLAlchemy. La migración tocó 12 archivos de modelos ORM, creó 16 modelos SQLAlchemy, construyó 12 vistas de administración y eliminó miles de líneas de código frontend personalizado.

¿Por qué SQLAdmin?

CaracterísticaAdmin SolidJS personalizadoSQLAdmin
Carga de mantenimientoAlta (app separada)Baja (auto-generado desde modelos)
Pipeline de buildVite + SolidJSNinguno (renderizado en servidor)
Soporte de nuevo modeloEscribir frontend + APIAñadir una clase de vista admin
Búsqueda/filtroImplementación personalizadaIncorporado
DespliegueServicio separadoMismo proceso del backend

La compensación fue clara. Perdimos control de diseño píxel por píxel, pero ganamos cero sobrecarga de mantenimiento para operaciones CRUD de admin. Para una herramienta interna usada por una persona (Thales), esa compensación fue fácil de hacer.

Los 16 modelos SQLAlchemy

La migración requirió convertir cada tabla de base de datos a un modelo ORM de SQLAlchemy apropiado: User, App, Transaction, Provider, AppProvider, PaymentMethod, Webhook, WebhookDelivery, Invoice, Coupon, CouponUsage, ApiKey, FeatureRequest, FeatureUpvote, FeatureComment y AuditLog.

El problema del nombre de columna reservado: metadata a meta

metadata es un atributo reservado en la base declarativa de SQLAlchemy. Definir una columna con ese nombre oculta el atributo interno y causa errores crípticos. El renombrado a meta se propagó a través de cada modelo, endpoint API, SDK y la migración de base de datos misma.

Las 12 vistas de administración

Las transacciones son solo lectura (can_create = False, can_edit = False, can_delete = False) -- en una plataforma de pagos, nunca modificas un registro de transacción después de la creación. Esto es un principio fundamental de auditoría financiera: el libro mayor es solo de adición.

SQLAdmin usa sesiones basadas en cookies para autenticación. El panel ahora vive en /thales -- una ruta significativa para Thales (el CEO y único admin) mientras sigue siendo no obvia para escáneres automatizados.

La corrección de compatibilidad de column_filters

Encontramos errores 500 al usar filtros de columna en SQLAdmin. La corrección requirió especificar filtros como cadenas en lugar de atributos del modelo.

Lo que eliminó la migración

~3.000 líneas de código admin SolidJS, 7 endpoints API personalizados, un pipeline de build separado, configuración de despliegue específica del admin y enrutamiento frontend para admin.

Lecciones de la migración

Empieza con SQLAdmin si eres un equipo pequeño. El nombre de columna metadata te morderá. Las vistas de solo lectura son una funcionalidad, no una limitación. Las sesiones de cookies están bien para configuraciones de un solo admin.


Este artículo es parte de la serie "Cómo construimos 0fee.dev". 0fee.dev es un orquestador de pagos que cubre más de 53 proveedores en más de 200 países, construido por Juste A. GNIMAVO y Claude desde Abiyán sin ingenieros humanos. Sigue la serie para conocer la historia completa de la construcción.

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles

Thales & Claude deblo

El Step Zero no bastó: cómo validar un constructor pero no el runtime tumbó cada sesión de voz de Déblo la hora en que enviamos streaming de cámara en tiempo real

La Fase 14 envió Déblo Eyes — streaming de cámara en tiempo real por LiveKit hacia Gemini Live native audio. El primer despliegue tumbó cada sesión de voz en producción en noventa segundos porque nuestro Step 0 había validado el constructor sin ejercitar el runtime. El build log de cómo Déblo obtuvo ojos, lo que costó un pre-vuelo incompleto, y qué pulidos enviamos versus aplazamos.

33 min May 20, 2026
debloclaude-opus-4.7claude-codegemini-live +25
Thales & Claude deblo

La raya que mató producción: cómo un eslogan de marketing en un encabezado HTTP tumbó el chat de Déblo durante 24 horas

Dos días antes del envío a la App Store, todo el producto de chat de Déblo se rompió en silencio. Sin spinner, sin toast, sin error en la UI — solo aire muerto. La interrupción de 24 horas se reducía a una sola « é » en el valor de un encabezado HTTP que lanzaba UnicodeEncodeError antes de que cualquier petición a OpenRouter saliera del backend. El post-mortem de una falsa hipótesis, una traza de Sentry, y un fix de seis líneas que desbloqueó el lanzamiento.

29 min May 19, 2026
debloclaude-opus-4.7claude-codeincident +19
Thales & Claude deblo

Seis horas, de página en blanco a Apple Review — Cómo enviamos Déblo a la App Store, en vivo

Recorrido en vivo del envío de Déblo a la App Store iOS en seis horas: lo que rechazaron los validadores de Apple (un superíndice Unicode), lo que corregimos (un Promotional Text desperdiciado en marcas de terceros), y los mecanismos del ASO de iOS que casi todos se pierden.

30 min May 13, 2026
debloclaude-opus-4.7claude-codeapp-store +16