Files
descarga_factura_transporte/deploy/instalar_servicio.ps1
T
GeorgeandClaude Sonnet 5.5 ca1550bced versión 1.2.0: despliegue como servicio de Windows con NSSM
- deploy/: scripts de PowerShell para instalar el servicio (cuenta de servicio
  con acceso a la ruta de red, reinicio automático, parada con Ctrl+C,
  rotación de la salida, PYTHONUTF8), actualizar a una versión etiquetada o
  volver a una anterior, y desinstalar.
- docs/despliegue_windows.md: requisitos, instalación, operación, rollback y
  problemas frecuentes.
- tools/verificar_conexiones.py: comprueba .env, Graph, SAP, HANA y la ruta de
  red sin modificar nada.
- CHANGELOG.md, README e inventario; pyproject.toml pasa a 1.2.0.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 12:08:38 -05:00

100 lines
4.7 KiB
PowerShell

<#
.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 <nombre>.
.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"