new-site/docserver/install.ps1
justin f8cd37ac8c Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:54:22 -05:00

227 lines
10 KiB
PowerShell

# Performance West Document Conversion Worker — Windows Installation
# Run as Administrator in PowerShell on the Windows VM
#
# What this does:
# 1. Verifies Python + Microsoft Word are installed
# 2. Installs Python dependencies (pywin32, minio)
# 3. Copies docserver_worker.py + config to C:\docserver\
# 4. Creates a Task Scheduler task that starts the worker at system boot
# (runs as the installing user, "Run whether user is logged on or not"
# — Word COM works in session 0 on Server 2019 with desktop interaction)
#
# Prerequisites:
# - Windows 10/11 Pro or Windows Server 2022
# - Microsoft Word installed (only Word needed, not full Office)
# - Python 3.12+ (python.org — check "Add to PATH")
# - Outbound HTTPS to minio.performancewest.net (or wherever MinIO lives)
# - No inbound ports required — the VM connects OUT to MinIO only
#
# Usage:
# .\install.ps1 -MinioEndpoint "minio.performancewest.net" `
# -MinioPort 443 `
# -MinioSecure $true `
# -MinioAccessKey "your_access_key" `
# -MinioSecretKey "your_secret_key"
param(
[Parameter(Mandatory=$true)]
[string]$MinioEndpoint,
[int] $MinioPort = 9000,
[bool] $MinioSecure = $false,
[Parameter(Mandatory=$true)]
[string]$MinioAccessKey,
[Parameter(Mandatory=$true)]
[string]$MinioSecretKey,
[string]$MinioBucket = "performancewest",
[int] $PollInterval = 3,
[string]$AppDir = "C:\docserver"
)
$ErrorActionPreference = "Stop"
Write-Host ""
Write-Host "=== Performance West Document Conversion Worker Setup ===" -ForegroundColor Cyan
Write-Host ""
# ── 0. Admin check ────────────────────────────────────────────────────────────
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
Write-Host "ERROR: Run this script as Administrator!" -ForegroundColor Red
exit 1
}
# ── 1. Python check ───────────────────────────────────────────────────────────
Write-Host "Checking Python..." -ForegroundColor Yellow
$python = Get-Command python -ErrorAction SilentlyContinue
if (-not $python) {
Write-Host "ERROR: Python not found." -ForegroundColor Red
Write-Host " Download from https://python.org/downloads" -ForegroundColor Red
Write-Host " Install with 'Add Python to PATH' checked, then re-run." -ForegroundColor Red
exit 1
}
$pyVersion = python --version 2>&1
Write-Host " Found: $pyVersion" -ForegroundColor Green
# ── 2. Word check ─────────────────────────────────────────────────────────────
Write-Host "Checking Microsoft Word..." -ForegroundColor Yellow
try {
$word = New-Object -ComObject Word.Application
$wordVersion = $word.Version
$word.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null
Write-Host " Found: Microsoft Word $wordVersion" -ForegroundColor Green
} catch {
Write-Host "ERROR: Microsoft Word not found or COM registration broken." -ForegroundColor Red
Write-Host " Install Microsoft Word and retry." -ForegroundColor Red
exit 1
}
# ── 3. Create application directory ──────────────────────────────────────────
Write-Host "Creating $AppDir..." -ForegroundColor Yellow
New-Item -ItemType Directory -Path $AppDir -Force | Out-Null
New-Item -ItemType Directory -Path "$AppDir\logs" -Force | Out-Null
New-Item -ItemType Directory -Path "$AppDir\temp" -Force | Out-Null
Copy-Item -Path "$PSScriptRoot\docserver_worker.py" -Destination $AppDir -Force
Copy-Item -Path "$PSScriptRoot\requirements.txt" -Destination $AppDir -Force
Write-Host " Files copied." -ForegroundColor Green
# ── 4. Install Python dependencies ───────────────────────────────────────────
Write-Host "Installing Python dependencies..." -ForegroundColor Yellow
python -m pip install --upgrade pip --quiet
python -m pip install -r "$AppDir\requirements.txt" --quiet
# pywin32 post-install COM registration
$pyPrefix = python -c "import sys; print(sys.prefix)"
$postInstall = "$pyPrefix\Scripts\pywin32_postinstall.py"
if (Test-Path $postInstall) {
python $postInstall -install 2>$null
Write-Host " pywin32 COM registration done." -ForegroundColor Green
}
Write-Host " Dependencies installed." -ForegroundColor Green
# ── 5. Write environment config ──────────────────────────────────────────────
$envContent = @"
MINIO_ENDPOINT=$MinioEndpoint
MINIO_PORT=$MinioPort
MINIO_SECURE=$($MinioSecure.ToString().ToLower())
MINIO_ACCESS_KEY=$MinioAccessKey
MINIO_SECRET_KEY=$MinioSecretKey
MINIO_BUCKET=$MinioBucket
POLL_INTERVAL=$PollInterval
LOG_DIR=$AppDir\logs
TEMP_DIR=$AppDir\temp
"@
Set-Content -Path "$AppDir\docserver.env" -Value $envContent -Encoding UTF8
Write-Host " Config written to $AppDir\docserver.env" -ForegroundColor Green
# ── 6. Write startup batch script ────────────────────────────────────────────
# Reads .env, sets env vars, then starts the worker
$startScript = @'
@echo off
setlocal
cd /d C:\docserver
:: Load environment variables from docserver.env
for /f "usebackq tokens=1,* delims==" %%a in ("C:\docserver\docserver.env") do (
if not "%%a"=="" (
set "line=%%a"
if not "!line:~0,1!"=="#" set "%%a=%%b"
)
)
:: Start the worker
python C:\docserver\docserver_worker.py >> C:\docserver\logs\worker.log 2>&1
endlocal
'@
# Note: the batch script uses delayed expansion so we write it separately
$startScriptFull = @'
@echo off
setlocal enabledelayedexpansion
cd /d C:\docserver
echo [%date% %time%] Starting Performance West Docserver Worker...
for /f "usebackq tokens=1,* delims==" %%a in ("C:\docserver\docserver.env") do (
set "ln=%%a"
if not "!ln:~0,1!"=="#" (
if not "%%a"=="" set "%%a=%%b"
)
)
python C:\docserver\docserver_worker.py
echo [%date% %time%] Worker exited with code %errorlevel%.
endlocal
'@
Set-Content -Path "$AppDir\start_worker.bat" -Value $startScriptFull -Encoding ASCII
# ── 7. Register Task Scheduler task ──────────────────────────────────────────
Write-Host "Registering Task Scheduler task..." -ForegroundColor Yellow
$taskName = "PW-DocserverWorker"
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
$action = New-ScheduledTaskAction `
-Execute "cmd.exe" `
-Argument "/c `"$AppDir\start_worker.bat`"" `
-WorkingDirectory $AppDir
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit (New-TimeSpan -Hours 0) `
-RestartCount 10 `
-RestartInterval (New-TimeSpan -Minutes 1) `
-StartWhenAvailable `
-MultipleInstances IgnoreNew `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries
# Register as the current user with "Run whether user is logged on or not"
# This allows the task to start at boot without requiring an interactive login.
# Word COM works in session 0 on Windows Server 2019.
$password = Read-Host -Prompt "Enter password for $currentUser (needed for 'Run whether logged on or not')" -AsSecureString
$plainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
)
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-User $currentUser `
-Password $plainPassword `
-RunLevel Highest `
-Description "Performance West DOCX-to-PDF worker (MinIO + Word COM)" | Out-Null
Write-Host " Task '$taskName' registered (runs at boot, restarts on failure)." -ForegroundColor Green
# ── 8. Start the task now ─────────────────────────────────────────────────────
Write-Host "Starting worker task..." -ForegroundColor Yellow
Start-ScheduledTask -TaskName $taskName
Start-Sleep -Seconds 5
# ── 9. Verify ────────────────────────────────────────────────────────────────
$taskInfo = Get-ScheduledTask -TaskName $taskName
Write-Host ""
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host " Setup Complete" -ForegroundColor Green
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
Write-Host " Task state: $($taskInfo.State)"
Write-Host " App dir: $AppDir"
Write-Host " Logs: $AppDir\logs\worker.log"
Write-Host " Config: $AppDir\docserver.env"
Write-Host ""
Write-Host " MinIO endpoint: $MinioEndpoint`:$MinioPort"
Write-Host " MinIO bucket: $MinioBucket"
Write-Host " Poll interval: ${PollInterval}s"
Write-Host ""
Write-Host " The worker polls minio://$MinioBucket/to-convert/" -ForegroundColor White
Write-Host " Converted PDFs appear in minio://$MinioBucket/converted/" -ForegroundColor White
Write-Host ""
Write-Host " To verify manually, place a .docx in to-convert/ and watch" -ForegroundColor White
Write-Host " converted/ for the resulting .pdf (should appear within a few seconds)." -ForegroundColor White
Write-Host ""
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan