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>
227 lines
10 KiB
PowerShell
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
|