Harden custom streamdeck startup

Require the project virtual environment and wait for backend readiness before launching the overlay.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-12 21:48:33 +02:00
parent 58fe9548a6
commit 9ba6e3ec0a
+87 -15
View File
@@ -8,6 +8,7 @@ $repoRoot = Split-Path -Parent $PSScriptRoot
$overlayDir = Join-Path $repoRoot "overlay"
$dataDir = Join-Path $repoRoot "data"
$launcherLog = Join-Path $dataDir "startup-launcher.log"
$backendUrl = "http://127.0.0.1:8000/api/state"
if (-not (Test-Path $dataDir)) {
New-Item -ItemType Directory -Path $dataDir | Out-Null
@@ -21,16 +22,11 @@ function Write-LauncherLog {
function Resolve-PythonPath {
$venvPython = Join-Path $repoRoot ".venv\Scripts\python.exe"
if (Test-Path $venvPython) {
return $venvPython
if (-not (Test-Path $venvPython)) {
throw "Project Python environment not found: $venvPython. Run: py -3.14 -m venv .venv"
}
$pythonCmd = Get-Command python -ErrorAction SilentlyContinue
if ($null -ne $pythonCmd) {
return $pythonCmd.Source
}
throw "Could not find Python. Install Python or create .venv first."
return (Resolve-Path $venvPython).Path
}
function Resolve-NpmPath {
@@ -42,6 +38,18 @@ function Resolve-NpmPath {
throw "Could not find npm.cmd. Install Node.js/npm first."
}
function Test-PythonRuntime {
param([string]$PythonPath)
$output = & $PythonPath -c "import uvicorn, backend.main; print('backend imports OK')" 2>&1
if ($LASTEXITCODE -ne 0) {
$details = ($output | Out-String).Trim()
throw "Backend Python runtime validation failed: $details"
}
Write-LauncherLog "Python runtime verified: $PythonPath"
}
function Start-HiddenProcess {
param(
[string]$Name,
@@ -54,18 +62,54 @@ function Start-HiddenProcess {
if ($DryRun) {
Write-LauncherLog "DRY RUN -> $Name | $FilePath $($Arguments -join ' ')"
return
return $null
}
Start-Process `
$process = Start-Process `
-FilePath $FilePath `
-ArgumentList $Arguments `
-WorkingDirectory $WorkingDirectory `
-WindowStyle Hidden `
-RedirectStandardOutput $StdOutPath `
-RedirectStandardError $StdErrPath | Out-Null
-RedirectStandardError $StdErrPath `
-PassThru
Write-LauncherLog "Started $Name."
Write-LauncherLog "Started $Name process (PID $($process.Id))."
return $process
}
function Test-BackendReady {
try {
$response = Invoke-WebRequest -UseBasicParsing -Uri $backendUrl -Method Get -TimeoutSec 2 -ErrorAction Stop
return $response.StatusCode -eq 200
} catch {
return $false
}
}
function Wait-ForBackendReady {
param(
[System.Diagnostics.Process]$Process,
[int]$TimeoutSeconds = 30
)
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
do {
if (Test-BackendReady) {
return $true
}
if ($null -ne $Process) {
$Process.Refresh()
if ($Process.HasExited) {
return $false
}
}
Start-Sleep -Milliseconds 500
} while ((Get-Date) -lt $deadline)
return $false
}
function Is-BackendRunning {
@@ -90,23 +134,51 @@ function Is-OverlayRunning {
try {
$pythonPath = Resolve-PythonPath
$npmPath = Resolve-NpmPath
Write-LauncherLog "Launcher using Python: $pythonPath"
if (-not $DryRun) {
Test-PythonRuntime $pythonPath
}
if (-not (Test-Path (Join-Path $repoRoot "frontend\dist\index.html"))) {
Write-LauncherLog "Warning: frontend/dist is missing. Build frontend for production view."
}
if (Is-BackendRunning) {
Write-LauncherLog "Backend already running. Skipping backend launch."
$backendReady = Test-BackendReady
if ($backendReady) {
Write-LauncherLog "Backend already ready at $backendUrl. Skipping backend launch."
} elseif (Is-BackendRunning) {
Write-LauncherLog "Backend process exists but is not ready. Waiting for $backendUrl."
if (-not $DryRun -and -not (Wait-ForBackendReady -TimeoutSeconds 30)) {
throw "Existing backend process did not become ready at $backendUrl. See data/backend.stderr.log."
}
$backendReady = $true
Write-LauncherLog "Backend ready at $backendUrl."
} else {
$backendOut = Join-Path $dataDir "backend.stdout.log"
$backendErr = Join-Path $dataDir "backend.stderr.log"
Start-HiddenProcess `
$backendProcess = Start-HiddenProcess `
-Name "backend" `
-FilePath $pythonPath `
-Arguments @("-m", "uvicorn", "backend.main:app", "--host", "127.0.0.1", "--port", "8000") `
-WorkingDirectory $repoRoot `
-StdOutPath $backendOut `
-StdErrPath $backendErr
if (-not $DryRun -and -not (Wait-ForBackendReady -Process $backendProcess -TimeoutSeconds 30)) {
$stderrTail = if (Test-Path $backendErr) { (Get-Content $backendErr -Tail 10 | Out-String).Trim() } else { "No backend stderr log was created." }
Write-LauncherLog "Backend failed readiness check. stderr tail: $stderrTail"
throw "Backend did not become ready at $backendUrl."
}
$backendReady = $true
if (-not $DryRun) {
Write-LauncherLog "Backend ready at $backendUrl."
}
}
if (-not $backendReady -and -not $DryRun) {
throw "Overlay launch skipped because the backend is not ready."
}
if (Is-OverlayRunning) {