feat: API Gateway (Express 5 + Prisma + Monday) y conexion de apps al API

- api-gateway: servicio Monday (retry 429/complejidad, paginacion cursor,
  column IDs por titulo), endpoints unidades/cotizaciones/clientes/planes,
  webhook con handshake challenge, Prisma SQLite dev, 55 tests
- cotizador: inventario desde /api/unidades con fallback a seed
- alta-clientes: POST /api/clientes + upload multipart de documentos
- cobranza: persistencia via API con respaldo offline en localStorage
- .env.example con board IDs del workspace Frasa-CAS
This commit is contained in:
2026-08-21 23:39:17 +00:00
parent ecd4edac02
commit 0546f35d2a
48 changed files with 7129 additions and 66 deletions

View File

@@ -0,0 +1,23 @@
import type { NextFunction, Request, Response } from 'express'
import { ZodError } from 'zod'
import { MulterError } from 'multer'
import { AppError } from '../utils/errors.js'
/** Error handler central: zod → 400 con issues, AppError → su status, resto → 500 */
export function errorHandler(err: unknown, req: Request, res: Response, _next: NextFunction): void {
if (err instanceof ZodError) {
res.status(400).json({ error: 'Validación fallida', issues: err.issues })
return
}
if (err instanceof MulterError) {
res.status(400).json({ error: `Error de carga de archivos: ${err.message}` })
return
}
if (err instanceof AppError) {
res.status(err.status).json({ error: err.message })
return
}
// eslint-disable-next-line no-console
console.error(`[500] ${req.method} ${req.originalUrl}:`, err)
res.status(500).json({ error: 'Error interno del servidor' })
}

View File

@@ -0,0 +1,11 @@
import type { NextFunction, Request, Response } from 'express'
/** Logger de requests simple: método, ruta, status y duración */
export function requestLogger(req: Request, res: Response, next: NextFunction): void {
const inicio = Date.now()
res.on('finish', () => {
// eslint-disable-next-line no-console
console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${Date.now() - inicio}ms`)
})
next()
}