Files
devolucion_proveedores/apps/api/src/web-estatico.ts
T
georgeandClaude Sonnet 5.5 287b187c14 Aplicación web de Devolución a Proveedores para SAP Business One
Reemplaza al addon .NET Framework (SolucionLibros). Genera Devoluciones de mercancías
enlazadas a sus Entradas por compra a partir de un listado pegado desde Excel.

- libs/shared (@dp/shared): modelos y lógica de dominio pura (FIFO, consolidación por
  proveedor, parseo del portapapeles), portada del addon.
- apps/api (NestJS): cliente del Service Layer con cola por sesión, login con la sesión de
  SAP, metadatos (UDT/UDF/UDO) y botón Reconstruir, catálogos, planificaciones (pegar,
  procesar, reprocesar, marcar líneas, crear Devoluciones, cerrar), listado paginado con
  filtros y descarga de PDF de la Guía de despacho desde BES. Sirve también la web
  compilada, con HTTPS opcional.
- apps/web (Angular): login, Configuración → Metadatos y Planificaciones (listado,
  nueva, detalle).
- deploy/windows y scripts/publicar.mjs: despliegue como servicio de Windows con WinSW.

Verificado contra el Service Layer de SQL Server. Sin probar aún: SAP HANA y la
instalación del servicio en Windows (ver CLAUDE.md, sección "Sin verificar").

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 08:09:07 -05:00

42 lines
1.8 KiB
TypeScript

import { extname, join } from 'node:path';
import type { NestExpressApplication } from '@nestjs/platform-express';
import type { NextFunction, Request, Response } from 'express';
/** Nombres con hash de Angular (main-HFVHQMEH.js, styles-RS7YTGMB.css): no cambian, se pueden cachear un año. */
const CON_HASH = /-[A-Z0-9]{8}\.(js|css)$/;
/**
* Cabeceras de seguridad básicas. HSTS solo si la API atiende por HTTPS: con HTTP obligaría al
* navegador a usar un HTTPS que no existe.
*/
export function cabecerasSeguridad(https: boolean) {
return (_req: Request, res: Response, next: NextFunction): void => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Referrer-Policy', 'same-origin');
if (https) res.setHeader('Strict-Transport-Security', 'max-age=31536000');
next();
};
}
/**
* La API sirve también la web compilada, así que todo queda en el mismo origen (sin CORS y con la
* cookie de sesión SameSite=strict). Las rutas de Angular (sin extensión y fuera de /api) devuelven
* index.html; un archivo que no existe responde 404 en lugar de index.html.
*/
export function servirWeb(app: NestExpressApplication, carpeta: string): void {
app.useStaticAssets(carpeta, {
index: false,
setHeaders: (res, ruta) => {
res.setHeader('Cache-Control', CON_HASH.test(ruta) ? 'public, max-age=31536000, immutable' : 'no-cache');
},
});
app.use((req: Request, res: Response, next: NextFunction) => {
const esRutaDeLaApp = (req.method === 'GET' || req.method === 'HEAD') && !req.path.startsWith('/api') && extname(req.path) === '';
if (!esRutaDeLaApp) return next();
res.setHeader('Cache-Control', 'no-cache');
res.sendFile(join(carpeta, 'index.html'), (error) => error && next(error));
});
}