<# .SYNOPSIS Instala (o reinstala) el demonio descarga_factura_transporte como servicio de Windows con NSSM. .DESCRIPTION Ejecutar en PowerShell COMO ADMINISTRADOR, desde cualquier carpeta. Ver docs/despliegue_windows.md. No arranca el servicio: primero revisa el .env y luego ejecuta nssm start . .PARAMETER Cuenta Cuenta con la que corre el servicio, en formato DOMINIO\usuario. Debe tener acceso de lectura/escritura a la ruta de red de RUTA_RED_FACTURACION: la cuenta LocalSystem (por defecto de los servicios) NO ve recursos de red. .PARAMETER Clave Contraseña de la cuenta (SecureString). Si se omite, se pide por consola. .EXAMPLE .\deploy\instalar_servicio.ps1 -Cuenta "ABERIO\svc_facturas" #> param( [string]$Nombre = "descarga_factura_transporte", [string]$Cuenta = "", [securestring]$Clave, [string]$Nssm = "nssm", [string]$Python = "" ) $ErrorActionPreference = "Stop" # --- Rutas (la carpeta del proyecto es la que contiene deploy\) --------------------------------------------- $Proyecto = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path if (-not $Python) { $Python = Join-Path $Proyecto "venv\Scripts\python.exe" } # --- Comprobaciones previas ---------------------------------------------------------------------------------- if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator)) { throw "Ejecute este script como administrador." } if (-not (Get-Command $Nssm -ErrorAction SilentlyContinue)) { throw "No se encontró nssm. Descárguelo de https://nssm.cc y agréguelo al PATH, o use -Nssm 'C:\ruta\nssm.exe'." } if (-not (Test-Path $Python)) { throw "No existe $Python. Cree el entorno virtual: py -3 -m venv venv ; venv\Scripts\pip install -r requirements.txt" } if (-not (Test-Path (Join-Path $Proyecto ".env"))) { throw "Falta $Proyecto\.env. Copie .env.example a .env y complételo (ver docs/configurar_msal.md)." } if (-not $Cuenta) { Write-Warning "Sin -Cuenta el servicio corre como LocalSystem y NO podrá escribir en la ruta de red de facturación electrónica." } # --- Instalación ---------------------------------------------------------------------------------------------- if (Get-Service -Name $Nombre -ErrorAction SilentlyContinue) { Write-Host "El servicio '$Nombre' ya existe: se detiene y se reconfigura." & $Nssm stop $Nombre confirm | Out-Null } else { & $Nssm install $Nombre $Python "main.py" | Out-Null } $LogsNssm = Join-Path $Proyecto "logs" New-Item -ItemType Directory -Force -Path $LogsNssm | Out-Null & $Nssm set $Nombre Application $Python & $Nssm set $Nombre AppParameters "main.py" & $Nssm set $Nombre AppDirectory $Proyecto # el código no depende del cwd, pero el .env y los logs sí cuentan con él & $Nssm set $Nombre DisplayName "Descarga de facturas de transporte" & $Nssm set $Nombre Description "Lee el buzón, valida las facturas contra SAP B1, crea el draft y responde al proveedor." & $Nssm set $Nombre Start SERVICE_AUTO_START & $Nssm set $Nombre AppEnvironmentExtra "PYTHONUTF8=1" "PYTHONIOENCODING=utf-8" "PYTHONUNBUFFERED=1" # emojis del log en la consola de Windows # Salida estándar y de error del proceso (el log de la aplicación sigue en logs\descarga_factura_transporte.log) & $Nssm set $Nombre AppStdout (Join-Path $LogsNssm "servicio.out.log") & $Nssm set $Nombre AppStderr (Join-Path $LogsNssm "servicio.err.log") & $Nssm set $Nombre AppRotateFiles 1 & $Nssm set $Nombre AppRotateOnline 1 & $Nssm set $Nombre AppRotateBytes 10485760 # 10 MB # Reinicio automático si el proceso muere; espera 30 s para no entrar en un bucle rápido & $Nssm set $Nombre AppExit Default Restart & $Nssm set $Nombre AppRestartDelay 30000 & $Nssm set $Nombre AppThrottle 10000 # Parada limpia: Ctrl+C (el demonio lo atiende con KeyboardInterrupt) y 20 s de gracia antes de forzar & $Nssm set $Nombre AppStopMethodSkip 0 & $Nssm set $Nombre AppStopMethodConsole 20000 & $Nssm set $Nombre AppStopMethodWindow 0 & $Nssm set $Nombre AppStopMethodThreads 0 if ($Cuenta) { if (-not $Clave) { $Clave = Read-Host "Contraseña de $Cuenta" -AsSecureString } $Texto = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Clave)) & $Nssm set $Nombre ObjectName $Cuenta $Texto Remove-Variable Texto } Write-Host "" Write-Host "Servicio '$Nombre' instalado. Pasos siguientes:" Write-Host " 1. Revise $Proyecto\.env (NOTIFICACION_MODO, NOTIFICACION_EQUIPO, LOGO_URL)." Write-Host " 2. nssm start $Nombre" Write-Host " 3. Get-Content '$LogsNssm\descarga_factura_transporte.log' -Wait -Tail 30"