feat: [US-004] Add SSH tunnel auto-start for Windows services
- Add ssh-tunnel.ps1: Windows SSH tunnel manager (equivalent to ssh-tunnel.sh) - Supports password auth via plink.exe (PuTTY) - Supports ssh_hostkey for non-interactive batch mode - Commands: start, stop, restart, status - Add start-backend-service.ps1: NSSM service wrapper - Starts SSH tunnels before uvicorn - Waits for tunnel ports to be accessible (30s timeout) - Configured by Install-ROA2WEB.ps1 - Add start.ps1: Windows equivalent of start.sh - Orchestrates SSH tunnel + backend + frontend startup - Add backend/shared/ssh_tunnel_manager.py: Python monitoring - Background asyncio task monitors tunnel health every 30s - Auto-restarts tunnels after 2 consecutive failures - Exposes status to /health endpoint - Update ROA2WEB-Console.ps1: - Add Deploy-Scripts function - Update Update-ServiceToUseVenv to use wrapper script - Fix PowerShell reserved variable ($PID -> $tunnelPid) - Fix script path detection (scripts/ vs deployment/windows/scripts/) - Update README.md with ssh_hostkey documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -215,7 +215,15 @@ function Initialize-Venv {
|
||||
function Update-ServiceToUseVenv {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Updates NSSM service to use venv Python
|
||||
Updates NSSM service to use wrapper script for SSH tunnel auto-start
|
||||
|
||||
.DESCRIPTION
|
||||
Configures the service to use start-backend-service.ps1 wrapper which:
|
||||
1. Starts SSH tunnels before backend
|
||||
2. Waits for tunnel ports to be accessible
|
||||
3. Starts uvicorn with correct settings
|
||||
|
||||
This ensures SSH tunnels are always running when the backend starts.
|
||||
#>
|
||||
$venvPython = Join-Path $Config.VenvPath "Scripts\python.exe"
|
||||
|
||||
@@ -231,24 +239,60 @@ function Update-ServiceToUseVenv {
|
||||
return $false
|
||||
}
|
||||
|
||||
# Find wrapper script
|
||||
$wrapperScript = Join-Path $Config.InstallRoot "scripts\start-backend-service.ps1"
|
||||
if (-not (Test-Path $wrapperScript)) {
|
||||
# Fallback: try deployment location
|
||||
$wrapperScript = Join-Path $PSScriptRoot "start-backend-service.ps1"
|
||||
}
|
||||
|
||||
$useWrapper = Test-Path $wrapperScript
|
||||
|
||||
try {
|
||||
# Get current application path
|
||||
# Get current application
|
||||
$currentApp = & nssm get $Config.ServiceName Application 2>&1
|
||||
|
||||
if ($currentApp -eq $venvPython) {
|
||||
Write-Success "Service already configured to use venv Python"
|
||||
return $true
|
||||
if ($useWrapper) {
|
||||
# Check if already using wrapper
|
||||
if ($currentApp -like "*powershell*") {
|
||||
$currentArgs = & nssm get $Config.ServiceName AppParameters 2>&1
|
||||
if ($currentArgs -like "*start-backend-service.ps1*") {
|
||||
Write-Success "Service already configured to use wrapper script"
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
Write-Step "Updating service to use wrapper script (SSH tunnel auto-start)..."
|
||||
|
||||
# Stop service first
|
||||
Stop-ROAService | Out-Null
|
||||
|
||||
# Update service to use PowerShell wrapper
|
||||
& nssm set $Config.ServiceName Application "powershell.exe"
|
||||
& nssm set $Config.ServiceName AppParameters "-ExecutionPolicy Bypass -File `"$wrapperScript`""
|
||||
|
||||
Write-Success "Service updated to use wrapper: $wrapperScript"
|
||||
Write-Info "SSH tunnels will auto-start when service starts"
|
||||
} else {
|
||||
# Fallback: use venv Python directly (old behavior)
|
||||
if ($currentApp -eq $venvPython) {
|
||||
Write-Success "Service already configured to use venv Python"
|
||||
return $true
|
||||
}
|
||||
|
||||
Write-Step "Updating service to use venv Python (no wrapper available)..."
|
||||
|
||||
# Stop service first
|
||||
Stop-ROAService | Out-Null
|
||||
|
||||
# Update service application
|
||||
& nssm set $Config.ServiceName Application $venvPython
|
||||
& nssm set $Config.ServiceName AppParameters "-m uvicorn main:app --host 127.0.0.1 --port $($Config.ServicePort) --workers 1"
|
||||
|
||||
Write-Success "Service updated to use: $venvPython"
|
||||
Write-Warning "Wrapper script not found - SSH tunnels must be started manually"
|
||||
}
|
||||
|
||||
Write-Step "Updating service to use venv Python..."
|
||||
|
||||
# Stop service first
|
||||
Stop-ROAService | Out-Null
|
||||
|
||||
# Update service application
|
||||
& nssm set $Config.ServiceName Application $venvPython
|
||||
Write-Success "Service updated to use: $venvPython"
|
||||
|
||||
return $true
|
||||
} catch {
|
||||
Write-Error "Failed to update service: $_"
|
||||
@@ -777,6 +821,85 @@ function New-Backup {
|
||||
# DEPLOYMENT FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
function Deploy-Scripts {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Deploys PowerShell scripts to installation directory
|
||||
|
||||
.DESCRIPTION
|
||||
Copies deployment scripts from package to install root's scripts folder.
|
||||
This includes the wrapper script needed for SSH tunnel auto-start.
|
||||
#>
|
||||
param([string]$SourcePath)
|
||||
|
||||
Write-Step "Deploying scripts..."
|
||||
|
||||
$sourceScripts = Join-Path $SourcePath "scripts"
|
||||
$destScripts = Join-Path $Config.InstallRoot "scripts"
|
||||
|
||||
# Scripts to deploy (essential for operation)
|
||||
$requiredScripts = @(
|
||||
"ssh-tunnel.ps1",
|
||||
"start-backend-service.ps1",
|
||||
"ROA2WEB-Console.ps1"
|
||||
)
|
||||
|
||||
try {
|
||||
# Create scripts directory if needed
|
||||
if (-not (Test-Path $destScripts)) {
|
||||
New-Item -ItemType Directory -Path $destScripts -Force | Out-Null
|
||||
Write-Info "Created scripts directory: $destScripts"
|
||||
}
|
||||
|
||||
$deployedCount = 0
|
||||
|
||||
# Copy scripts from package
|
||||
if (Test-Path $sourceScripts) {
|
||||
foreach ($script in $requiredScripts) {
|
||||
$srcFile = Join-Path $sourceScripts $script
|
||||
$destFile = Join-Path $destScripts $script
|
||||
|
||||
if (Test-Path $srcFile) {
|
||||
Copy-Item -Path $srcFile -Destination $destFile -Force
|
||||
Write-Info "Deployed: $script"
|
||||
$deployedCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Also copy from current script location (fallback)
|
||||
foreach ($script in $requiredScripts) {
|
||||
$srcFile = Join-Path $PSScriptRoot $script
|
||||
$destFile = Join-Path $destScripts $script
|
||||
|
||||
if ((Test-Path $srcFile) -and (-not (Test-Path $destFile))) {
|
||||
Copy-Item -Path $srcFile -Destination $destFile -Force
|
||||
Write-Info "Deployed (from PSScriptRoot): $script"
|
||||
$deployedCount++
|
||||
}
|
||||
}
|
||||
|
||||
if ($deployedCount -gt 0) {
|
||||
Write-Success "Scripts deployed ($deployedCount files)"
|
||||
} else {
|
||||
Write-Warning "No scripts to deploy"
|
||||
}
|
||||
|
||||
# Verify essential wrapper script
|
||||
$wrapperPath = Join-Path $destScripts "start-backend-service.ps1"
|
||||
if (Test-Path $wrapperPath) {
|
||||
Write-Success "Service wrapper script ready: $wrapperPath"
|
||||
} else {
|
||||
Write-Warning "Service wrapper script not found - SSH tunnel auto-start may not work"
|
||||
}
|
||||
|
||||
return $true
|
||||
} catch {
|
||||
Write-Error "Scripts deployment failed: $_"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Deploy-Backend {
|
||||
param([string]$SourcePath)
|
||||
|
||||
@@ -1019,6 +1142,9 @@ function Deploy-All {
|
||||
Write-Warning "Backup failed, but continuing with deployment"
|
||||
}
|
||||
|
||||
# Deploy scripts first (needed for service wrapper)
|
||||
$scriptsOk = Deploy-Scripts -SourcePath $SourcePath
|
||||
|
||||
# Deploy backend (includes service restart)
|
||||
$backendOk = Deploy-Backend -SourcePath $SourcePath
|
||||
|
||||
@@ -1028,6 +1154,12 @@ function Deploy-All {
|
||||
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
||||
Write-Host " DEPLOYMENT SUMMARY" -ForegroundColor Cyan
|
||||
Write-Host ("=" * 70) -ForegroundColor Cyan
|
||||
Write-Host " Scripts: " -NoNewline
|
||||
if ($scriptsOk) {
|
||||
Write-Host "[OK] Success" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "[X] Failed" -ForegroundColor Red
|
||||
}
|
||||
Write-Host " Backend: " -NoNewline
|
||||
if ($backendOk) {
|
||||
Write-Host "[OK] Success" -ForegroundColor Green
|
||||
@@ -1042,7 +1174,7 @@ function Deploy-All {
|
||||
}
|
||||
Write-Host ("=" * 70) -ForegroundColor Cyan
|
||||
|
||||
return ($backendOk -and $frontendOk)
|
||||
return ($scriptsOk -and $backendOk -and $frontendOk)
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user