Despliegue
Imagen Docker
La imagen base es node:20-slim. Instala LibreOffice + fuentes DejaVu + Liberation + fontconfig (sin esto los PDFs pueden mostrar cuadros por glifos faltantes). Corre como usuario no-root.
Build local
docker build -t converter-api:local .
docker run --rm -e API_KEY=secret -p 3000:3000 converter-api:localdocker-compose
services:
converter-api:
build: .
image: converter-api:local
container_name: converter-api
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- .env
volumes:
- ./templates:/app/templates
- ./images:/app/images
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s./templates persiste las plantillas registradas. Si lo tiras, pierdes el catálogo entero (no hay backup automático). Considera montar una ruta en disco persistente o respaldar periódicamente.
./images es el contenido referenciable como path local en los renders (logos corporativos, etc.). Se gestiona fuera del API.
Variables de entorno en producción
API_KEY— genera una connode -e "console.log(require('crypto').randomBytes(32).toString('hex'))"(64 chars hex) y guárdala en tu secret manager.NODE_ENV=production— desactiva pino-pretty, salida JSON pura.LOG_LEVEL=infoen producción;warnsi tu agregador de logs cobra por volumen.LO_CONCURRENCY=1es el default seguro. Subirlo requiere validar que LibreOffice usa profile dirs separados (la librería actual sí, pero ojo con futuras versiones).
Healthcheck
El Dockerfile ya incluye HEALTHCHECK basado en curl /health. Para Kubernetes:
livenessProbe:
httpGet:
path: /health
port: 3000
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 3000
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
initialDelaySeconds: 10/health: 200 ok, 200 degraded (sin LibreOffice; el .docx sigue funcionando), 503 down (templates dir inaccesible). Si tu producto requiere PDF, configura readinessProbe que falle en degraded también.Logs
Cada request emite una línea JSON con campos requestId, method, url, statusCode, responseTime. Errores 5xx incluyen stack trace. Datos sensibles redactados:
req.headers["x-api-key"]req.headers.authorizationreq.body.data(payload del render — puede contener PII)
Persistencia
Sin DB. El estado vive 100% en filesystem en TEMPLATES_DIR (<slug>.docx y <slug>.schema.json opcional). Para restore, simplemente coloca los archivos en el directorio y reinicia el servicio (schemaService tiene cache que se invalida en eventos de cambio, pero un reinicio fresco también limpia).
Métricas / observabilidad
v1 no expone métricas Prometheus. Si lo necesitas, los hooks naturales son:
- Wrappear
renderService.renderypdfService.convertcon un counter + histogram de duración. - Counter por
error.codeen elerrorHandlermiddleware. libreoffice_availablegauge desde elpdfService.isAvailablecache.
Tests
npm test # unit + integration (PDF gated off)
RUN_PDF_TESTS=1 npm test # incluye renders reales con LibreOffice
npm run test:coverage # con thresholds (services/utils ≥ 80%)En CI, recomendado correr npm test sin LibreOffice (más rápido) y un job aparte con LibreOffice para los PDF tests.