Saltar al contenido
Anderson F. González
Volver al trabajo

Control Vehicular: de Flask a Node.js con el mismo esquema

Un sistema web que lee las facturas electrónicas de combustible de la DIAN y las concilia con los tanqueos de la flota de una distribuidora. Mi parte fue reescribir el backend de Python a Node.js, manteniendo el esquema y las rutas.

Cliente
Distribuciones Rosimar S.A.S.
Mi parte
La migración del backend y varias funciones portadas después. 7 de los 24 commits1 son míos; el sistema original y su lógica de conciliación son de otro desarrollador del equipo.
Periodo
Septiembre y octubre de 2026
Stack
Node.js 22, Express 5, MySQL con mysql2 (SQL directo), Nunjucks, node:test y supertest
Código
Privado. controlvehicular.rosimar.com tiene acceso restringido.

Por qué migrar

La aplicación original estaba en Flask. El hosting compartido de Hostinger que usa el cliente no soporta Python con frameworks, solo en un VPS. Se reescribió, con una condición: no cambiar el comportamiento. Mismo esquema MySQL, mismas rutas, mismas pantallas. La única excepción son los errores del original que se corrigieron por el camino, más abajo.

Qué se portó

  • Autenticación con bloqueo tras 5 intentos fallidos durante 15 minutos, por usuario y por IP.
  • CSRF en todo POST, también en formularios multipart, verificado antes de procesar cualquier dato.
  • El parser de las facturas electrónicas de la DIAN: ZIP con XML y PDF, con XML que usa espacios de nombres.
  • Conciliación de consumos contra facturas por período, auditoría, análisis e importación de Excel y CSV.
  • SQL directo y parametrizado, sin ORM. Las sesiones y los archivos de facturas viven en la base de datos, no en disco.
  • Un módulo que reproduce el redondeo de Python, para que los totales coincidan con los del sistema anterior.
  1. 1

    Petición

    Cabeceras de seguridad y sesión guardada en la base de datos.

  2. 2

    CSRF y permisos

    El token CSRF se verifica antes de procesar cualquier dato; el rol limita qué rutas ve cada usuario.

  3. 3

    Router

    Un router por módulo. Los roles admin, auxiliar y jefe ven cada uno su parte.

  4. 4

    SQL

    mysql2 con consultas parametrizadas, sin ORM.

  5. 5

    Plantilla

    Nunjucks, con una sintaxis casi igual a la de Jinja2 que usaba el original.

Cada módulo (autenticación, catálogos, facturas, consumos, conciliación, reportes) tiene su propio router.

Errores del original que aparecieron al reescribir

Reescribir con atención obliga a leer cada ruta. Salieron estos problemas, que se corrigieron en la migración2:

  • Un open redirect en el login.
  • La ruta de vinculación masiva aceptaba GET sin token CSRF.
  • La importación de recibos en xlsx guardaba una lista en vez de un entero.
  • Una celda mal ubicada en el export de facturas.
  • El conteo de hallazgos nuevos era falso en el análisis inicial.

Cómo se probó

Al cerrar la migración había 48 pruebas3, unitarias y de integración, con node:test y supertest contra una base MySQL real, sin mocks. Cada archivo de prueba crea y borra su propia base *_test, y el helper se niega a correr contra cualquier servidor que no sea localhost.

El código Python se conserva en legacy/, como referencia y como vía de regreso.

Límites

  • No hay integración continua. Lo que llega a main se despliega directo en Hostinger, así que las pruebas se corren a mano antes de cada push.
  • Las pruebas necesitan un contenedor de MySQL local.
  • No diseñé el producto. Lo mío fue el cambio de plataforma y el portado de funciones nuevas.

Fuentes de las cifras

  1. 1

    Autoría de los commits de fleet_control

    git shortlog de rosimar1/fleet_control (privado) · 9 de octubre de 2026

    Otro desarrollador del equipo 17 y Fabian Gonzalez (mi usuario de git) 7: 24 en total.

  2. 2

    Fallas del original corregidas en la migración

    mensaje del commit d1cc1c2 y test/integration/auth.test.js de rosimar1/fleet_control (privado) · 23 de septiembre de 2026

    Un open redirect en el login (hay una prueba: next=//evil.com se ignora) y una ruta de vinculación masiva que aceptaba GET sin token CSRF.

  3. 3

    Pruebas al cerrar la migración

    rosimar1/fleet_control, commit d1cc1c2 (privado) · 23 de septiembre de 2026

    35 llamadas a test() más 14 casos que genera el bucle de permisos de test/integration/permissions.test.js. El mensaje del commit también dice 48.