# Despliegue en Windows La aplicación se ejecuta como **un solo servicio de Windows**: la API (NestJS) sirve también la web compilada (Angular), por HTTPS y en un único puerto. El servicio lo administra **WinSW**. ``` Navegador ──HTTPS :443──▶ servicio "DevolucionProveedores" (node dist\main.js) ├─ sirve la web (Angular compilado) ├─ /api ──▶ Service Layer de SAP B1 (:50000) └─ /api/…/pdf ──▶ web service de BES (HTTPS) ``` No hay base de datos propia ni otros servicios: todo se guarda en SAP. Como las sesiones viven en la memoria de la API, **reiniciar el servicio cierra la sesión de todos los usuarios**. > **Estado de esta guía.** La carpeta publicada, la API con HTTPS, la web servida por la API, la > redirección HTTP→HTTPS y los logs sin colores se probaron en un Mac. Los scripts de PowerShell y > WinSW **no se han ejecutado todavía en Windows** (se comprobó su sintaxis y la generación del XML). > Tampoco se ha probado contra HANA. Haga la primera instalación en un servidor de pruebas. ## 1. Requisitos del servidor | Qué | Detalle | |---|---| | Windows | Windows Server 2016 o superior. | | .NET Framework | 4.6.1 o superior (lo necesita WinSW; viene con Windows Server 2016+). | | Node.js | **22 LTS** o superior, instalado para todos los usuarios (`node -v` en una consola nueva). | | Red | Acceso al Service Layer de SAP (puerto 50000) y al web service de BES (HTTPS de salida). | | WinSW | `WinSW-x64.exe` de (rama 2.x). Verifique el hash que publica el proyecto. | | Certificado | Un `.pfx` (o `.key` + `.crt`) para el nombre con que los usuarios abrirán la página. | | Permisos | Una consola de PowerShell **como administrador**. | ## 2. Publicar (en su equipo de desarrollo o en el servidor) Desde la raíz del proyecto, con Node 22: ```bash npm install npm run publicar ``` Genera `release\devolucion-proveedores\` con la API y la web compiladas y **solo las dependencias de producción** (unos 55 MB). Si ya compiló (`npm run build`), use `npm run publicar -- --sin-build`. Las dependencias de la API son JavaScript puro, así que se puede publicar desde un Mac o Linux y ejecutar en Windows. ## 3. Copiar al servidor Copie toda la carpeta `devolucion-proveedores` a, por ejemplo, `C:\apps\devolucion-proveedores`: ``` C:\apps\devolucion-proveedores\ ├── app\ la aplicación (no se edita, salvo el .env) │ └── apps\api\.env ← se crea en el paso 4 ├── certificados\ ← el certificado HTTPS (paso 5) ├── logs\ ← los logs del servicio ├── instalar-servicio.ps1 ├── desinstalar-servicio.ps1 ├── devolucion-api.xml.plantilla └── DEPLOY.md ``` ## 4. Configurar el `.env` ```powershell cd C:\apps\devolucion-proveedores\app\apps\api Copy-Item .env.example .env notepad .env ``` Ejemplo para producción (HANA, HTTPS en el puerto 443 y redirección desde el 80): ```ini PORT=443 SL_URL=https://servidor-hana:50000/b1s/v1 SL_COMPANY_DB=NombreDeLaSociedad SL_REJECT_UNAUTHORIZED=false DB_TYPE=HANA COOKIE_SECURE=true HTTPS_PFX_PATH=C:\apps\devolucion-proveedores\certificados\sitio.pfx HTTPS_PFX_PASSPHRASE=la-clave-del-pfx HTTP_REDIRECT_PORT=80 ``` - **El puerto es el que ponga en `PORT`**: 443 es solo el ejemplo (con él la URL no lleva `:puerto`). Puede usar cualquiera que esté libre, por ejemplo `PORT=8443`; entonces la URL es `https://servidor:8443`. WinSW no influye: solo arranca el servicio. - `HTTP_REDIRECT_PORT` es opcional. Solo es útil con el 80 (la gente escribe `http://servidor` sin puerto); con otro puerto solo redirige a quien escriba `http://servidor:`. Déjelo vacío si no lo necesita. - **No** se configura el usuario ni la contraseña de SAP: cada persona los escribe en el login. - `WEB_ORIGIN` no hace falta: la web y la API comparten origen. - Con `.key`/`.crt` en vez de `.pfx`: `HTTPS_KEY_PATH` y `HTTPS_CERT_PATH` (no mezcle ambas formas). - Si deja el `.env` con la clave del `.pfx`, restrinja quién puede leerlo: ```powershell icacls C:\apps\devolucion-proveedores\app\apps\api\.env /inheritance:r /grant:r "Administrators:F" "SYSTEM:F" ``` (Si el servicio correrá con una cuenta propia, agréguela con `/grant:r "EMPRESA\svc-devolucion:R"`.) ## 5. Certificado HTTPS Lo normal es un certificado de la CA de la empresa, exportado como `.pfx` a `certificados\`. Para una **prueba** puede crear uno autofirmado (los navegadores mostrarán una advertencia): ```powershell $c = New-SelfSignedCertificate -DnsName 'devolucion.empresa.local' -CertStoreLocation Cert:\LocalMachine\My -NotAfter (Get-Date).AddYears(2) Export-PfxCertificate -Cert $c -FilePath C:\apps\devolucion-proveedores\certificados\sitio.pfx -Password (Read-Host -AsSecureString 'Clave del pfx') ``` HTTPS es obligatorio: el botón *Pegar desde el portapapeles* solo funciona con `localhost` o HTTPS, y la cookie de sesión se marca `Secure`. ## 6. Abrir el firewall Abra **el puerto que puso en `PORT`** (y el de `HTTP_REDIRECT_PORT`, si lo usa). Con los valores del ejemplo (443 y 80): ```powershell New-NetFirewallRule -DisplayName 'Devolucion Proveedores HTTPS' -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow New-NetFirewallRule -DisplayName 'Devolucion Proveedores HTTP (redireccion)' -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow ``` ## 7. Instalar el servicio ```powershell cd C:\apps\devolucion-proveedores .\instalar-servicio.ps1 -WinSW C:\descargas\WinSW-x64.exe ``` Opciones: | Parámetro | Para qué | |---|---| | `-Cuenta 'EMPRESA\svc-devolucion'` | Ejecutar con una cuenta propia en vez de `LocalSystem`. Pide la contraseña y no la guarda en ningún archivo. Esa cuenta necesita el derecho *Iniciar sesión como servicio* y lectura sobre la carpeta. | | `-UsarCertificadosDelSistema` | Agrega `NODE_OPTIONS=--use-system-ca`. Úselo si Fortinet (u otro firewall) inspecciona el HTTPS de salida con un certificado propio y la descarga de PDF falla por certificado. | | `-NombreServicio` | Cambia el nombre del servicio (por defecto `DevolucionProveedores`). | El script verifica Node 22, los archivos y el `.env`; genera `servicio\devolucion-api.xml` y `servicio\devolucion-api.exe`; instala el servicio (arranque automático y reinicio si falla) y lo inicia. ## 8. Verificar ```powershell Get-Service DevolucionProveedores Invoke-WebRequest https://devolucion.empresa.local/api/health -UseBasicParsing Get-Content C:\apps\devolucion-proveedores\logs\*.out.log -Tail 30 ``` Debe verse `{"status":"ok"}` y, en el log, la línea `Escuchando en https://0.0.0.0:443`. Después abra la dirección en un navegador, inicie sesión con un usuario de SAP y revise **Configuración → Metadatos**. En una base nueva pulse **Reconstruir** para crear las tablas, campos y el objeto de usuario. ## 9. Logs WinSW guarda lo que la API escribe por consola en `logs\` (`*.out.log` y `*.err.log`), rotado por tamaño: 10 MB y 8 archivos. La aplicación no escribe logs por su cuenta. - Los errores 4xx/5xx de la API, los fallos y las peticiones lentas al Service Layer, y los eventos de negocio (planificación creada, PDF descargado) quedan registrados. Las peticiones correctas no. - Para seguirlo en vivo: `Get-Content C:\apps\devolucion-proveedores\logs\*.out.log -Wait -Tail 50`. - La contraseña del web service DTE (`@DTE_CONFC`) y las de los usuarios **nunca** se registran. ## 10. Actualizar a una versión nueva 1. Publique la versión nueva (paso 2) y copie la carpeta al servidor, por ejemplo a `C:\nueva\`. 2. Elija una hora en que haya pocos usuarios: **se perderán las sesiones**. 3. Ejecute: ```powershell Stop-Service DevolucionProveedores # Respaldo opcional de la versión actual Copy-Item C:\apps\devolucion-proveedores\app C:\apps\respaldo-app -Recurse # Copia la nueva sin tocar el .env robocopy C:\nueva\devolucion-proveedores\app C:\apps\devolucion-proveedores\app /MIR /XF .env Start-Service DevolucionProveedores ``` `/MIR` borra de `app\` lo que ya no exista en la versión nueva; `/XF .env` conserva su configuración. Los certificados, los logs y el servicio no cambian. ## 11. Desinstalar ```powershell cd C:\apps\devolucion-proveedores .\desinstalar-servicio.ps1 ``` Detiene y elimina el servicio. No borra archivos ni logs. ## 12. Seguridad - **No publique la aplicación en Internet.** El login es el de SAP B1; manténgala en la red interna o detrás de una VPN. - Use un certificado válido de la CA de la empresa; el servicio envía `Strict-Transport-Security`. - Dele al servicio la cuenta con menos permisos posible: solo lectura sobre su carpeta y escritura en `logs\`. - Restrinja la lectura del `.env` si contiene la clave del `.pfx` (paso 4). ## 13. Problemas frecuentes | Síntoma | Causa probable | |---|---| | El servicio se inicia y se detiene | Mire `logs\*.err.log`. Suele ser una ruta mal escrita en el `.env` (`No se pudo leer HTTPS_PFX_PATH (…)`) o el puerto ocupado (`EADDRINUSE`). | | `WEB_DIST_DIR no contiene un index.html` | Se copió la carpeta `app` incompleta: falta `app\apps\web\dist\web\browser`. | | Puerto 443 o 80 ocupado | IIS u otro programa lo usa. Cambie `PORT` / `HTTP_REDIRECT_PORT` en el `.env` y las reglas del firewall. | | El navegador avisa que el sitio no es seguro | Certificado autofirmado, o el nombre de la URL no coincide con el del certificado. | | Entra al login pero vuelve a pedirlo | Con `COOKIE_SECURE=true` la cookie solo se guarda por HTTPS: abra la página por `https://`. | | `EHOSTUNREACH` o no conecta con SAP | Red o firewall hacia el Service Layer; reinicie el servicio tras un cambio de red. | | La descarga de PDF falla por certificado | Fortinet inspecciona el HTTPS de salida: reinstale con `-UsarCertificadosDelSistema`, o importe el certificado raíz de Fortinet en Windows. | | Cada acción tarda decenas de segundos | Mire el log: los avisos `Petición lenta al Service Layer` indican qué llamada y cuánto tardó. | | `.\instalar-servicio.ps1` dice que no se puede ejecutar scripts | `Set-ExecutionPolicy -Scope Process Bypass` en esa consola y vuelva a intentarlo. | ## Antes de pasar a producción (HANA) El sistema se probó contra SQL Server (`SBOLibros`). Con HANA: 1. Pruebe primero con una **copia** de la sociedad: `DB_TYPE=HANA`, **Reconstruir** y una ejecución completa. 2. Compare los resultados del FIFO con los del addon .NET para el mismo listado. 3. Confirme que el Service Layer de HANA expone `/b1s/v1` (el paginado usa `$inlinecount`).