feat: Add Linux deployment scripts and server logs view

- Add deployment/linux/ with deploy.sh for deploying from Claude-Agent LXC to Windows server
- Add ServerLogsView.vue for viewing server logs from frontend
- Add shared/routes/system.py for system health endpoints
- Update CLAUDE.md with quick deploy instructions
- Improve Windows deployment scripts (ROA2WEB-Console.ps1)
- Fix OCR service validation and worker pool improvements
- Update environment config examples
- Various script permission and startup fixes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-04 00:26:36 +00:00
parent 495790411f
commit 02a8c8682c
39 changed files with 1939 additions and 80 deletions

View File

@@ -301,6 +301,10 @@ function Invoke-Deployment {
# Capture exit code IMMEDIATELY (before any other command that might reset it)
$exitCode = $LASTEXITCODE
# Run OCR dependency check with auto-install
Write-Log -Message "Checking and installing OCR dependencies..." -Level "INFO"
& $consoleScript -NonInteractive -Action InstallOCR 2>&1 | ForEach-Object { Write-Log -Message $_ -Level "INFO" }
Pop-Location
# Check if exit code indicates success (0 = success)

View File

@@ -72,6 +72,8 @@ $script:Config = @{
FrontendPath = Join-Path $InstallPath "frontend"
LogsPath = Join-Path $InstallPath "logs"
TempPath = Join-Path $InstallPath "temp"
# IMPORTANT: venv is OUTSIDE InstallPath to survive deployments!
VenvPath = "C:\inetpub\wwwroot\roa2web-venv"
PythonVersion = $PythonVersion
ServicePort = $ServicePort
IISSiteName = $IISSiteName
@@ -302,24 +304,46 @@ function New-DirectoryStructure {
}
function Install-PythonDependencies {
Write-Step "Installing Python dependencies..."
Write-Step "Setting up Python virtual environment..."
$requirementsPath = Join-Path $Config.BackendPath "requirements.txt"
$venvPath = $Config.VenvPath
$venvPython = Join-Path $venvPath "Scripts\python.exe"
$venvPip = Join-Path $venvPath "Scripts\pip.exe"
# Create venv if it doesn't exist
if (-not (Test-Path $venvPython)) {
Write-Step "Creating virtual environment at $venvPath..."
try {
& python -m venv $venvPath
Write-Success "Virtual environment created"
} catch {
throw "Failed to create virtual environment: $_"
}
} else {
Write-Success "Virtual environment already exists"
}
# Upgrade pip in venv
Write-Step "Upgrading pip in virtual environment..."
try {
& $venvPython -m pip install --upgrade pip
Write-Success "Pip upgraded"
} catch {
Write-Warning "Could not upgrade pip: $_"
}
# Install dependencies
if (-not (Test-Path $requirementsPath)) {
Write-Warning "requirements.txt not found at $requirementsPath"
Write-Warning "Please copy backend files first, then run this script again"
return
}
Write-Step "Installing Python dependencies in virtual environment..."
try {
# Upgrade pip first
& python -m pip install --upgrade pip
# Install dependencies
& python -m pip install -r $requirementsPath
Write-Success "Python dependencies installed successfully"
& $venvPip install -r $requirementsPath
Write-Success "Python dependencies installed successfully in venv"
} catch {
throw "Failed to install Python dependencies: $_"
}
@@ -359,16 +383,21 @@ function New-WindowsService {
Write-Success "Existing service removed"
}
# Get Python path
$pythonPath = (Get-Command python).Source
# Get Python path from venv
$venvPython = Join-Path $Config.VenvPath "Scripts\python.exe"
if (-not (Test-Path $venvPython)) {
throw "Virtual environment Python not found at $venvPython. Run Install-PythonDependencies first."
}
$uvicornModule = "uvicorn"
$appModule = "main:app"
# NSSM service creation
try {
# Install service
# Install service using venv Python
# NOTE: Using --workers 1 because Telegram bot requires single instance (polling conflict)
& nssm install $Config.ServiceName $pythonPath "-m" $uvicornModule $appModule "--host" "127.0.0.1" "--port" $Config.ServicePort.ToString() "--workers" "1"
& nssm install $Config.ServiceName $venvPython "-m" $uvicornModule $appModule "--host" "127.0.0.1" "--port" $Config.ServicePort.ToString() "--workers" "1"
# Set service configuration
& nssm set $Config.ServiceName DisplayName $Config.ServiceDisplayName
@@ -530,6 +559,7 @@ function Show-Summary {
Write-Host "`nInstallation Details:" -ForegroundColor Yellow
Write-Host " Install Path: $($Config.InstallPath)"
Write-Host " Backend Path: $($Config.BackendPath)"
Write-Host " Virtual Env: $($Config.VenvPath)"
Write-Host " Frontend Path: $($Config.FrontendPath)"
Write-Host " Service Name: $($Config.ServiceName)"
Write-Host " Service Port: $($Config.ServicePort)"

View File

@@ -54,7 +54,7 @@ param(
[ValidateSet("DeployBackend", "DeployFrontend", "DeployAll",
"StartService", "StopService", "RestartService",
"Status", "ViewLogs")]
"Status", "ViewLogs", "CheckOCR", "InstallOCR")]
[string]$Action = "",
[string]$PackagePath = ""
@@ -84,6 +84,8 @@ $script:Config = @{
# Installation Paths
InstallRoot = "C:\inetpub\wwwroot\roa2web"
BackendPath = "C:\inetpub\wwwroot\roa2web\backend"
# IMPORTANT: venv is OUTSIDE roa2web to survive deployments!
VenvPath = "C:\inetpub\wwwroot\roa2web-venv"
FrontendPath = "C:\inetpub\wwwroot\roa2web\frontend"
SharedPath = "C:\inetpub\wwwroot\roa2web\shared"
ConfigPath = "C:\inetpub\wwwroot\roa2web\config"
@@ -132,6 +134,128 @@ function Write-Info {
Write-Host " [*] $Message" -ForegroundColor Gray
}
function Get-PythonPaths {
<#
.SYNOPSIS
Returns Python and pip paths, preferring venv if available
#>
$venvPython = Join-Path $Config.VenvPath "Scripts\python.exe"
$venvPip = Join-Path $Config.VenvPath "Scripts\pip.exe"
if (Test-Path $venvPython) {
return @{
Python = $venvPython
Pip = $venvPip
IsVenv = $true
}
} else {
# Fallback to global Python
$globalPython = (Get-Command python -ErrorAction SilentlyContinue).Source
$globalPip = (Get-Command pip -ErrorAction SilentlyContinue).Source
return @{
Python = $globalPython
Pip = $globalPip
IsVenv = $false
}
}
}
function Initialize-Venv {
<#
.SYNOPSIS
Creates virtual environment at external location if it doesn't exist.
External location (C:\inetpub\wwwroot\roa2web-venv) survives deployments.
#>
$venvPath = $Config.VenvPath
$venvPython = Join-Path $venvPath "Scripts\python.exe"
$venvPip = Join-Path $venvPath "Scripts\pip.exe"
# If venv exists and is valid (pip works), we're good
if (Test-Path $venvPython) {
# Verify pip is functional (not broken by move)
try {
$pipTest = & $venvPip --version 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Success "Virtual environment already exists at $venvPath"
return $true
} else {
Write-Warning "Venv exists but pip is broken, recreating..."
Remove-Item -Path $venvPath -Recurse -Force -ErrorAction SilentlyContinue
}
} catch {
Write-Warning "Venv exists but pip test failed, recreating..."
Remove-Item -Path $venvPath -Recurse -Force -ErrorAction SilentlyContinue
}
}
Write-Step "Creating virtual environment at external location..."
Write-Info "Path: $venvPath (survives deployments)"
try {
$globalPython = (Get-Command python -ErrorAction Stop).Source
& $globalPython -m venv $venvPath
if (Test-Path $venvPython) {
Write-Success "Virtual environment created"
# Upgrade pip
Write-Step "Upgrading pip in venv..."
& $venvPython -m pip install --upgrade pip 2>&1 | Out-Null
Write-Success "Pip upgraded"
return $true
} else {
Write-Error "Failed to create virtual environment"
return $false
}
} catch {
Write-Error "Failed to create venv: $_"
return $false
}
}
function Update-ServiceToUseVenv {
<#
.SYNOPSIS
Updates NSSM service to use venv Python
#>
$venvPython = Join-Path $Config.VenvPath "Scripts\python.exe"
if (-not (Test-Path $venvPython)) {
Write-Warning "Venv Python not found: $venvPython"
return $false
}
# Check if nssm is available
$nssmPath = Get-Command nssm -ErrorAction SilentlyContinue
if (-not $nssmPath) {
Write-Warning "NSSM not found in PATH"
return $false
}
try {
# Get current application path
$currentApp = & nssm get $Config.ServiceName Application 2>&1
if ($currentApp -eq $venvPython) {
Write-Success "Service already configured to use venv Python"
return $true
}
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: $_"
return $false
}
}
function Wait-ForKeyPress {
Write-Host "`nPress any key to continue..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
@@ -304,6 +428,294 @@ function Test-ServiceHealth {
}
}
# =============================================================================
# OCR DEPENDENCY CHECK
# =============================================================================
function Test-OCRDependencies {
param(
[switch]$AutoInstall,
[switch]$Silent
)
if (-not $Silent) {
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " OCR Dependencies Check$(if ($AutoInstall) { ' (Auto-Install Enabled)' })" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
}
$allOk = $true
# Get Python paths (prefer venv)
$pyPaths = Get-PythonPaths
# Check Python/venv
if (-not $Silent) { Write-Step "Checking Python installation..." }
if ($pyPaths.IsVenv) {
if (-not $Silent) {
$venvPythonVersion = & $pyPaths.Python --version 2>&1
Write-Success "Virtual environment: $($Config.VenvPath)"
Write-Success "Python (venv): $venvPythonVersion"
}
# Ensure service uses venv Python
if ($AutoInstall) {
# Install requirements.txt if exists
$requirementsFile = Join-Path $Config.BackendPath "requirements.txt"
if (Test-Path $requirementsFile) {
if (-not $Silent) { Write-Step "Installing base requirements in venv..." }
try {
& $pyPaths.Pip install -r $requirementsFile 2>&1 | Out-Null
if (-not $Silent) { Write-Success "Base requirements installed" }
} catch {
if (-not $Silent) { Write-Warning "Failed to install requirements: $_" }
}
}
if (-not $Silent) { Write-Step "Ensuring service uses venv Python..." }
$serviceUpdated = Update-ServiceToUseVenv
if ($serviceUpdated -and -not $Silent) {
Write-Success "Service configured to use venv"
}
}
} else {
# No venv - create one if AutoInstall
if ($AutoInstall) {
if (-not $Silent) { Write-Warning "Virtual environment not found - creating..." }
$venvCreated = Initialize-Venv
if ($venvCreated) {
# Refresh paths
$pyPaths = Get-PythonPaths
# Update service to use venv Python
if (-not $Silent) { Write-Step "Updating service to use virtual environment..." }
Update-ServiceToUseVenv | Out-Null
} else {
if (-not $Silent) { Write-Error "Could not create virtual environment" }
return $false
}
} else {
# Check global Python
if ($pyPaths.Python) {
$pythonVersion = cmd /c "python --version 2>&1"
if (-not $Silent) {
Write-Warning "Using global Python (venv recommended)"
Write-Success "Python: $pythonVersion"
}
} else {
if (-not $Silent) { Write-Error "Python not found" }
return $false
}
}
}
# Determine pip executable to use
$pipExe = if ($pyPaths.Pip -and (Test-Path $pyPaths.Pip)) { "`"$($pyPaths.Pip)`"" } else { "pip" }
if (-not $Silent -and $pyPaths.IsVenv) { Write-Info "Using pip from venv: $pipExe" }
# Check and optionally install Python packages
if (-not $Silent) { Write-Step "Checking Python OCR packages..." }
$packages = @(
@{ Name = "torch"; Package = "torch"; Required = $true; Description = "PyTorch (for docTR)" },
@{ Name = "torchvision"; Package = "torchvision"; Required = $true; Description = "TorchVision (for docTR)" },
@{ Name = "python-doctr"; Package = "python-doctr"; Required = $true; Description = "docTR OCR engine" },
@{ Name = "pytesseract"; Package = "pytesseract"; Required = $true; Description = "Tesseract Python wrapper" },
@{ Name = "paddleocr"; Package = "paddleocr"; Required = $true; Description = "PaddleOCR engine" }
)
foreach ($pkg in $packages) {
# Use venv pip to check packages
$pipOutput = cmd /c "$pipExe show $($pkg.Package) 2>&1"
$isInstalled = $pipOutput -match "Version:"
if ($isInstalled) {
$versionLine = $pipOutput | Where-Object { $_ -match "^Version:" }
$version = if ($versionLine) { ($versionLine -split ":")[1].Trim() } else { "unknown" }
if (-not $Silent) { Write-Success "$($pkg.Package): $version" }
} else {
if ($pkg.Required) {
if ($AutoInstall) {
if (-not $Silent) { Write-Warning "$($pkg.Package): NOT INSTALLED - Installing..." }
try {
# Use venv pip to install
$installCmd = "$pipExe install `"$($pkg.Name)`""
if (-not $Silent) { Write-Info " Running: $installCmd" }
$installResult = Invoke-Expression "cmd /c $installCmd 2>&1"
# Verify installation
$verifyOutput = cmd /c "$pipExe show $($pkg.Package) 2>&1"
if ($verifyOutput -match "Version:") {
if (-not $Silent) { Write-Success "$($pkg.Package): Installed successfully" }
} else {
if (-not $Silent) {
Write-Error "$($pkg.Package): Installation FAILED"
# Show last few lines of pip output for debugging
$errorLines = ($installResult | Select-Object -Last 5) -join "`n"
if ($errorLines) {
Write-Host " Pip output:" -ForegroundColor Gray
Write-Host " $errorLines" -ForegroundColor Gray
}
Write-Info " Try manually: $pipExe install `"$($pkg.Name)`""
}
$allOk = $false
}
} catch {
if (-not $Silent) { Write-Error "$($pkg.Package): Installation error - $_" }
$allOk = $false
}
} else {
if (-not $Silent) {
Write-Error "$($pkg.Package): NOT INSTALLED - $($pkg.Description)"
Write-Info " Install with: pip install $($pkg.Name)"
}
$allOk = $false
}
} else {
if (-not $Silent) { Write-Warning "$($pkg.Package): Not installed (optional)" }
}
}
}
# Check external tools
if (-not $Silent) { Write-Step "Checking external OCR tools..." }
# Check for Chocolatey (used for auto-install)
$chocoAvailable = $null -ne (Get-Command choco -ErrorAction SilentlyContinue)
# Install Chocolatey if needed and AutoInstall is enabled
if ($AutoInstall -and -not $chocoAvailable) {
if (-not $Silent) { Write-Warning "Chocolatey: NOT FOUND - Installing..." }
try {
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Refresh environment
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
$chocoAvailable = $null -ne (Get-Command choco -ErrorAction SilentlyContinue)
if ($chocoAvailable) {
if (-not $Silent) { Write-Success "Chocolatey: Installed successfully" }
} else {
if (-not $Silent) { Write-Warning "Chocolatey: Installed but not in PATH - restart PowerShell" }
}
} catch {
if (-not $Silent) { Write-Error "Chocolatey: Installation failed - $_" }
}
}
# Tesseract
$tesseractPath = Get-Command tesseract -ErrorAction SilentlyContinue
if ($tesseractPath) {
$tessVersion = cmd /c "tesseract --version 2>&1" | Select-Object -First 1
if (-not $Silent) {
Write-Success "Tesseract: $tessVersion"
Write-Info " Path: $($tesseractPath.Source)"
}
} else {
if ($AutoInstall) {
if ($chocoAvailable) {
if (-not $Silent) { Write-Warning "Tesseract: NOT FOUND - Installing via Chocolatey..." }
try {
$result = cmd /c "choco install tesseract -y 2>&1"
# Refresh PATH
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
$tesseractPath = Get-Command tesseract -ErrorAction SilentlyContinue
if ($tesseractPath) {
if (-not $Silent) { Write-Success "Tesseract: Installed successfully" }
} else {
if (-not $Silent) { Write-Error "Tesseract: Installation completed but not in PATH - restart PowerShell" }
$allOk = $false
}
} catch {
if (-not $Silent) { Write-Error "Tesseract: Chocolatey install failed - $_" }
$allOk = $false
}
} else {
if (-not $Silent) {
Write-Error "Tesseract: NOT FOUND - Chocolatey not available for auto-install"
Write-Info " Install Chocolatey first: https://chocolatey.org/install"
Write-Info " Then run: choco install tesseract -y"
}
$allOk = $false
}
} else {
if (-not $Silent) {
Write-Error "Tesseract: NOT FOUND in PATH"
Write-Info " Install with: choco install tesseract -y"
Write-Info " Or download from: https://github.com/UB-Mannheim/tesseract/wiki"
}
$allOk = $false
}
}
# Poppler (for PDF support)
$popplerPath = Get-Command pdftoppm -ErrorAction SilentlyContinue
if ($popplerPath) {
if (-not $Silent) {
Write-Success "Poppler: Found (pdftoppm)"
Write-Info " Path: $($popplerPath.Source)"
}
} else {
if ($AutoInstall -and $chocoAvailable) {
if (-not $Silent) { Write-Warning "Poppler: NOT FOUND - Installing via Chocolatey..." }
try {
$result = cmd /c "choco install poppler -y 2>&1"
# Refresh PATH
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
$popplerPath = Get-Command pdftoppm -ErrorAction SilentlyContinue
if ($popplerPath) {
if (-not $Silent) { Write-Success "Poppler: Installed successfully" }
} else {
if (-not $Silent) { Write-Warning "Poppler: Installation completed but not in PATH - restart PowerShell" }
}
} catch {
if (-not $Silent) { Write-Warning "Poppler: Chocolatey install failed - $_" }
}
} else {
if (-not $Silent) {
Write-Warning "Poppler: NOT FOUND in PATH (required for PDF OCR)"
Write-Info " Install with: choco install poppler -y"
}
}
}
# Check .env OCR settings
if (-not $Silent) {
Write-Step "Checking OCR configuration in .env..."
$envPath = Join-Path $Config.BackendPath ".env"
if (Test-Path $envPath) {
$envContent = Get-Content $envPath -Raw
$ocrSettings = @(
"OCR_ENABLE_PADDLEOCR",
"OCR_ENABLE_TESSERACT",
"OCR_DEFAULT_ENGINE",
"OCR_WORKERS"
)
foreach ($setting in $ocrSettings) {
if ($envContent -match "$setting\s*=\s*(.+)") {
Write-Info " $setting = $($Matches[1].Trim())"
} else {
Write-Warning " ${setting}: NOT CONFIGURED"
}
}
} else {
Write-Warning ".env file not found at: $envPath"
}
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
if ($allOk) {
Write-Host " Result: All required OCR dependencies are installed" -ForegroundColor Green
} else {
Write-Host " Result: Some required dependencies are MISSING" -ForegroundColor Red
Write-Host " Run with -AutoInstall to install missing packages" -ForegroundColor Yellow
}
Write-Host ("=" * 70) -ForegroundColor Cyan
}
return $allOk
}
# =============================================================================
# BACKUP FUNCTIONS
# =============================================================================
@@ -400,6 +812,23 @@ function Deploy-Backend {
Write-Info "Preserving .env file"
}
# Preserve data directory (contains SQLite databases with production data!)
$dataDir = Join-Path $Config.BackendPath "data"
$dataTempPath = Join-Path $env:TEMP "roa2web-data-backup-$(Get-Date -Format 'yyyyMMddHHmmss')"
$dataBackup = $null
if (Test-Path $dataDir) {
Write-Info "Preserving data directory (SQLite databases, uploads, cache)"
Copy-Item -Path $dataDir -Destination $dataTempPath -Recurse -Force
$dataBackup = $dataTempPath
}
# Delete old venv inside backend if it exists (it has hardcoded paths and can't be moved)
$oldVenvPath = Join-Path $Config.BackendPath "venv"
if (Test-Path $oldVenvPath) {
Write-Info "Removing old venv from backend directory (will use external venv)"
Remove-Item -Path $oldVenvPath -Recurse -Force -ErrorAction SilentlyContinue
}
Remove-Item -Path $Config.BackendPath -Recurse -Force
Copy-Item -Path $sourceBe -Destination $Config.BackendPath -Recurse -Force
@@ -408,6 +837,18 @@ function Deploy-Backend {
Set-Content -Path $envFile -Value $envBackup -Force
Write-Success ".env file restored"
}
# Restore data directory
if ($dataBackup -and (Test-Path $dataBackup)) {
# Remove the empty data dir from package and restore the preserved one
$newDataDir = Join-Path $Config.BackendPath "data"
if (Test-Path $newDataDir) {
Remove-Item -Path $newDataDir -Recurse -Force
}
Copy-Item -Path $dataBackup -Destination $newDataDir -Recurse -Force
Remove-Item -Path $dataBackup -Recurse -Force -ErrorAction SilentlyContinue
Write-Success "Data directory restored (SQLite databases preserved)"
}
} else {
Copy-Item -Path $sourceBe -Destination $Config.BackendPath -Recurse -Force
}
@@ -423,10 +864,81 @@ function Deploy-Backend {
Write-Success "Shared modules deployed"
}
# Setup virtual environment
Write-Step "Setting up Python virtual environment..."
$venvCreated = Initialize-Venv
if (-not $venvCreated) {
Write-Warning "Could not create/verify virtual environment"
}
# Install requirements.txt
$requirementsFile = Join-Path $Config.BackendPath "requirements.txt"
if (Test-Path $requirementsFile) {
$pyPaths = Get-PythonPaths
if ($pyPaths.IsVenv) {
Write-Step "Installing Python dependencies in venv..."
$pipExe = $pyPaths.Pip
# Verify pip is functional before installing
$pipVersion = & $pipExe --version 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Error "Pip is not functional: $pipVersion"
Write-Info "Try: Remove-Item -Recurse $($Config.VenvPath); then redeploy"
} else {
Write-Info "Using pip: $pipVersion"
# Install requirements (ignore warnings, check exit code only)
$oldErrorAction = $ErrorActionPreference
$ErrorActionPreference = "Continue"
$pipOutput = & $pipExe install -r $requirementsFile 2>&1
$pipExitCode = $LASTEXITCODE
$ErrorActionPreference = $oldErrorAction
# Log warnings but don't fail on them
$pipOutput | ForEach-Object {
if ($_ -match "WARNING:") {
Write-Warning $_
}
}
if ($pipExitCode -eq 0) {
# Verify uvicorn installed (critical dependency)
$uvicornCheck = & $pipExe show uvicorn 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Success "Python dependencies installed"
} else {
Write-Error "Dependencies install failed - uvicorn not found"
Write-Info "Manual fix: $pipExe install -r $requirementsFile"
}
} else {
Write-Error "Pip install failed with exit code $pipExitCode"
Write-Info "Manual fix: $pipExe install -r $requirementsFile"
}
}
} else {
Write-Warning "No venv found - skipping requirements.txt installation"
}
}
# Update service to use venv Python
$pyPaths = Get-PythonPaths
if ($pyPaths.IsVenv) {
Update-ServiceToUseVenv | Out-Null
}
# Start service
Start-Sleep -Seconds 2
if (Start-ROAService) {
Write-Success "Backend deployment completed successfully"
# Check and auto-install OCR dependencies after deployment
Write-Step "Checking and installing OCR dependencies..."
$ocrOk = Test-OCRDependencies -AutoInstall
if (-not $ocrOk) {
Write-Warning "Some OCR dependencies could not be installed automatically"
Write-Info "Manual installation may be required for external tools (Tesseract, Poppler)"
}
return $true
} else {
Write-Warning "Backend deployed but service start failed"
@@ -670,6 +1182,7 @@ function Show-MainMenu {
Write-Host " === Monitoring ===" -ForegroundColor Cyan
Write-Host " [7] View Status" -ForegroundColor White
Write-Host " [8] View Logs" -ForegroundColor White
Write-Host " [9] Check/Install OCR Dependencies" -ForegroundColor White
Write-Host ""
Write-Host " [Q] Quit" -ForegroundColor Red
Write-Host ""
@@ -738,9 +1251,23 @@ function Show-MainMenu {
Wait-ForKeyPress
return "Continue"
}
"9" {
$ocrOk = Test-OCRDependencies
if (-not $ocrOk) {
Write-Host ""
Write-Host " Install missing dependencies? (Y/N): " -ForegroundColor Yellow -NoNewline
$installChoice = Read-Host
if ($installChoice -eq "Y" -or $installChoice -eq "y") {
Write-Host ""
Test-OCRDependencies -AutoInstall | Out-Null
}
}
Wait-ForKeyPress
return "Continue"
}
"Q" { return "Quit" }
default {
Write-Host "Invalid choice. Please select 1-8 or Q." -ForegroundColor Red
Write-Host "Invalid choice. Please select 1-9 or Q." -ForegroundColor Red
}
}
} while ($true)
@@ -806,6 +1333,14 @@ function Main {
Show-Logs
exit 0
}
"CheckOCR" {
$success = Test-OCRDependencies
exit $(if ($success) { 0 } else { 1 })
}
"InstallOCR" {
$success = Test-OCRDependencies -AutoInstall
exit $(if ($success) { 0 } else { 1 })
}
}
}