App web que hace lo mismo que split_liquidaciones.py: se sube el PDF mensual de
liquidaciones, se separa en un PDF por página nombrado <Sr(a)> - <Mes>.pdf, y se
descarga todo en un ZIP. Detrás de un login simple con usuario y contraseña.
| Paso | Script de Python | Esta app |
|---|---|---|
| Leer texto de cada página | PyPDF2.PdfReader |
pdfjs-dist (src/lib/pdf-text.ts) |
| Detectar nombre y mes | Sr(a): / Mes: con regex |
los mismos regex (src/lib/split.ts) |
| Escribir un PDF por página | PdfWriter |
pdf-lib |
| Empaquetar | zipfile |
jszip |
El nombre del ZIP se deduce del mes detectado en el PDF (Junio 2026 →
liquidaciones_junio_2026.zip), igual que la convención del script original.
Los nombres generados se verificaron contra las 544 páginas de los 8 PDF de ejemplo y coinciden exactamente con los que produjo el script de Python. Puedes repetir la comprobación en cualquier momento:
npm run verifypdfjs llega a su worker mediante un import() calculado, así que el rastreador de
archivos de Next no lo ve y deja pdf.worker.mjs fuera de la función. En Vercel
eso rompe /api/split con:
Setting up fake worker failed: Cannot find module '/var/task/node_modules/pdfjs-dist/legacy/build/pdf.worker.mjs'
El arreglo es el outputFileTracingIncludes de next.config.ts,
que fuerza la inclusión del worker. No borres esa entrada. Para reproducir el
problema en local sin desplegar, añade output: "standalone" al config, compila y
arranca node .next/standalone/server.js: el standalone contiene exactamente los
archivos que rastrea Next, o sea los mismos que empaqueta Vercel.
El aviso Cannot load "@napi-rs/canvas" era inocuo — pdfjs solo usa canvas para
renderizar y aquí únicamente extraemos texto — pero se silencia con
verbosity: VerbosityLevel.ERRORS en src/lib/pdf-text.ts.
- Páginas duplicadas. Si dos páginas dan el mismo nombre y mes, el script de
Python sobreescribe el archivo y mete una entrada duplicada en el ZIP; aquí la
segunda pasa a ser
Nombre - Mes (2).pdfpara no perder ninguna página. - Páginas sin datos. Se mantiene el comportamiento original (
Empleado_NN,Mes_desconocido) pero la interfaz avisa de cuántas páginas quedaron así.
npm install
cp .env.example .env.local # opcional
npm run dev # http://localhost:3000Credenciales por defecto: admin / liquidaciones2026.
npx vercel # preview
npx vercel --prodMarca este directorio (app/) como Root Directory del proyecto. No hace falta
configuración extra: Vercel detecta Next.js.
Después define las variables de entorno en Settings → Environment Variables:
| Variable | Para qué |
|---|---|
AUTH_USERNAME |
usuario del login |
AUTH_PASSWORD |
contraseña del login |
AUTH_SECRET |
clave de firma de la cookie (openssl rand -hex 32) |
Sin ellas la app arranca con los valores por defecto del código, así que cámbialas antes de exponerla.
- Subida: Vercel corta los cuerpos de petición sobre ~4,5 MB. Los PDF de ejemplo pesan ~400 KB, con mucho margen.
- Descarga: el ZIP también viaja por la función serverless, con el mismo tope de ~4,5 MB. El mayor de los ejemplos (80 páginas) genera ~3,7 MB — entra, pero un PDF bastante más grande no cabría. Si llegas a ese punto, la salida natural es mover el procesado al navegador (las tres librerías funcionan en cliente).
- Tiempo: el límite de la función está en 60 s (src/app/api/split/route.ts); separar 65 páginas tarda unos 0,3 s.
Un solo usuario hardcodeado y una cookie HttpOnly firmada con HMAC-SHA256 que
caduca a las 12 h (src/lib/auth.ts). Todo lo que no sea
/login y /api/login pasa por src/proxy.ts. Es suficiente para
restringir el acceso a un equipo pequeño, pero no es un sistema multiusuario: la
contraseña es compartida y no hay registro de accesos.