diff --git a/deployment/windows/scripts/ROA2WEB-Console.ps1 b/deployment/windows/scripts/ROA2WEB-Console.ps1 index 707d3df..f0df456 100644 --- a/deployment/windows/scripts/ROA2WEB-Console.ps1 +++ b/deployment/windows/scripts/ROA2WEB-Console.ps1 @@ -124,6 +124,73 @@ function Test-Administrator { return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } +function Test-CommandExists { + param([string]$Command) + try { + if (Get-Command $Command -ErrorAction Stop) { + return $true + } + } catch { + return $false + } +} + +# ============================================================================= +# DETECTION FUNCTIONS - Check if first install or update needed +# ============================================================================= + +function Test-BackendInstalled { + Write-Step "Checking if Backend is installed..." + + $venvPath = Join-Path $Config.BackendPath "venv" + $venvExists = Test-Path $venvPath + $service = Get-ServiceSafe -ServiceName $Config.BackendServiceName + $serviceExists = $null -ne $service + + $requirementsPath = Join-Path $Config.BackendPath "requirements.txt" + $hasRequirements = Test-Path $requirementsPath + + if ($venvExists -and $serviceExists -and $hasRequirements) { + Write-Success "Backend is installed (venv + service exist)" + return $true + } else { + Write-Warning "Backend NOT installed (missing: $( + @( + if (-not $venvExists) { 'venv' } + if (-not $serviceExists) { 'service' } + if (-not $hasRequirements) { 'requirements.txt' } + ) -join ', ' + ))" + return $false + } +} + +function Test-TelegramBotInstalled { + Write-Step "Checking if Telegram Bot is installed..." + + $venvPath = Join-Path $Config.TelegramBotPath "venv" + $venvExists = Test-Path $venvPath + $service = Get-ServiceSafe -ServiceName $Config.TelegramBotServiceName + $serviceExists = $null -ne $service + + $appPath = Join-Path $Config.TelegramBotPath "app" + $hasApp = Test-Path $appPath + + if ($venvExists -and $serviceExists -and $hasApp) { + Write-Success "Telegram Bot is installed (venv + service exist)" + return $true + } else { + Write-Warning "Telegram Bot NOT installed (missing: $( + @( + if (-not $venvExists) { 'venv' } + if (-not $serviceExists) { 'service' } + if (-not $hasApp) { 'app files' } + ) -join ', ' + ))" + return $false + } +} + function Get-ServiceSafe { param([string]$ServiceName) try { @@ -179,6 +246,595 @@ function Wait-ForKeyPress { $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") } +# ============================================================================= +# PREREQUISITE INSTALLATION FUNCTIONS +# ============================================================================= + +function Install-Chocolatey { + Write-Step "Checking Chocolatey package manager..." + + if (Test-CommandExists "choco") { + Write-Success "Chocolatey already installed" + return $true + } + + Write-Info "Installing Chocolatey..." + 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") + + Write-Success "Chocolatey installed successfully" + return $true + } catch { + Write-Warning "Failed to install Chocolatey: $_" + Write-Warning "Some automated installations may not be available" + return $false + } +} + +function Install-Python { + Write-Step "Checking Python installation..." + + # Check if Python is already installed + try { + $pythonCmd = Get-Command python -ErrorAction Stop + $pythonVersionOutput = & python --version 2>&1 + if ($pythonVersionOutput -match "Python (\d+\.\d+\.\d+)") { + $installedVersion = $matches[1] + $versionParts = $installedVersion -split '\.' + $major = [int]$versionParts[0] + $minor = [int]$versionParts[1] + + if ($major -ge 3 -and $minor -ge 11) { + Write-Success "Python $installedVersion already installed" + return $true + } else { + Write-Warning "Python $installedVersion found, but 3.11+ required" + } + } + } catch { + Write-Info "Python not found, will attempt installation..." + } + + # Try to install via Chocolatey + if (Test-CommandExists "choco") { + try { + Write-Step "Installing Python 3.11..." + choco install python --version=3.11.9 -y --force + + # Refresh environment + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + + Write-Success "Python installed successfully" + return $true + } catch { + Write-Error "Failed to install Python via Chocolatey: $_" + return $false + } + } else { + Write-Error "Chocolatey not available, cannot auto-install Python" + Write-Warning "Please download Python manually from: https://www.python.org/downloads/" + return $false + } +} + +function Install-NSSM { + Write-Step "Checking NSSM (service manager)..." + + if (Test-Path "C:\nssm\nssm.exe") { + Write-Success "NSSM already installed" + return $true + } + + # Try Chocolatey first + if (Test-CommandExists "choco") { + try { + choco install nssm -y + Write-Success "NSSM installed via Chocolatey" + return $true + } catch { + Write-Warning "Chocolatey installation failed, trying direct download..." + } + } + + # Direct download fallback + try { + $nssmUrl = "https://nssm.cc/release/nssm-2.24.zip" + $nssmZip = "$env:TEMP\nssm.zip" + $nssmExtract = "$env:TEMP\nssm" + + Write-Step "Downloading NSSM..." + Invoke-WebRequest -Uri $nssmUrl -OutFile $nssmZip + + Write-Step "Extracting NSSM..." + Expand-Archive -Path $nssmZip -DestinationPath $nssmExtract -Force + + # Copy nssm.exe to C:\nssm + New-Item -ItemType Directory -Path "C:\nssm" -Force | Out-Null + Copy-Item -Path "$nssmExtract\nssm-2.24\win64\nssm.exe" -Destination "C:\nssm\nssm.exe" -Force + + # Add to PATH + $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") + if ($currentPath -notlike "*C:\nssm*") { + [Environment]::SetEnvironmentVariable("Path", "$currentPath;C:\nssm", "Machine") + $env:Path += ";C:\nssm" + } + + # Cleanup + Remove-Item $nssmZip -Force + Remove-Item $nssmExtract -Recurse -Force + + Write-Success "NSSM installed successfully" + return $true + } catch { + Write-Error "Failed to install NSSM: $_" + return $false + } +} + +function Install-IISModules { + Write-Step "Checking IIS installation..." + + # Detect OS type (Server vs Desktop) + $osInfo = Get-CimInstance -ClassName Win32_OperatingSystem + $isServer = $osInfo.ProductType -eq 3 + + # Check if IIS is installed + $iisInstalled = $false + + if ($isServer) { + $iisFeature = Get-WindowsFeature -Name Web-Server -ErrorAction SilentlyContinue + $iisInstalled = $iisFeature -and $iisFeature.InstallState -eq "Installed" + + if (-not $iisInstalled) { + Write-Warning "IIS not installed on Windows Server" + Write-Host " Install with: Install-WindowsFeature -Name Web-Server -IncludeManagementTools" -ForegroundColor Yellow + return $false + } + } else { + $iisFeature = Get-WindowsOptionalFeature -Online -FeatureName IIS-WebServer -ErrorAction SilentlyContinue + $iisInstalled = $iisFeature -and $iisFeature.State -eq "Enabled" + + if (-not $iisInstalled) { + Write-Warning "IIS not installed on Windows Desktop" + Write-Host " Enable via: Control Panel -> Windows Features -> Internet Information Services" -ForegroundColor Yellow + return $false + } + } + + Write-Success "IIS is installed ($($osInfo.Caption))" + + # Install URL Rewrite Module + Write-Step "Checking IIS URL Rewrite Module..." + $urlRewriteInstalled = Get-WebConfiguration -Filter "/system.webServer/rewrite" -PSPath "IIS:\" -ErrorAction SilentlyContinue + + if (-not $urlRewriteInstalled) { + try { + Write-Info "Installing URL Rewrite Module..." + $urlRewriteUrl = "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_en-US.msi" + $urlRewritePath = "$env:TEMP\rewrite_amd64.msi" + + Invoke-WebRequest -Uri $urlRewriteUrl -OutFile $urlRewritePath + Start-Process msiexec.exe -ArgumentList "/i", $urlRewritePath, "/quiet", "/norestart" -Wait + Remove-Item $urlRewritePath -Force + + Write-Success "URL Rewrite Module installed" + } catch { + Write-Warning "Failed to install URL Rewrite: $_" + Write-Warning "Download manually from: https://www.iis.net/downloads/microsoft/url-rewrite" + } + } else { + Write-Success "URL Rewrite Module already installed" + } + + return $true +} + +# ============================================================================= +# FIRST-TIME INSTALLATION FUNCTIONS +# ============================================================================= + +function Install-BackendFirstTime { + Write-Host "`n" + ("=" * 70) -ForegroundColor Yellow + Write-Host " FIRST-TIME INSTALLATION: BACKEND + FRONTEND" -ForegroundColor Yellow + Write-Host ("=" * 70) -ForegroundColor Yellow + + try { + # Install prerequisites + Write-Step "Installing prerequisites..." + Install-Chocolatey | Out-Null + + if (-not (Install-Python)) { + throw "Python installation failed" + } + + if (-not (Install-NSSM)) { + throw "NSSM installation failed" + } + + # Create directory structure + Write-Step "Creating directory structure..." + $directories = @( + $Config.InstallPath, + $Config.BackendPath, + $Config.FrontendPath, + $Config.LogsPath, + (Join-Path $Config.BackendPath "logs"), + (Join-Path $Config.BackendPath "temp") + ) + + foreach ($dir in $directories) { + if (-not (Test-Path $dir)) { + New-Item -ItemType Directory -Path $dir -Force | Out-Null + Write-Success "Created: $dir" + } else { + Write-Success "Already exists: $dir" + } + } + + # Set permissions for IIS + try { + $acl = Get-Acl $Config.InstallPath + $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule("IIS_IUSRS", "ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow") + $acl.SetAccessRule($accessRule) + Set-Acl -Path $Config.InstallPath -AclObject $acl + Write-Success "Permissions set for IIS_IUSRS" + } catch { + Write-Warning "Could not set permissions: $_" + } + + # Copy backend files from source + Write-Step "Copying backend files..." + $sourceBackend = Join-Path $Config.SourcePath "backend" + + if (-not (Test-Path $sourceBackend)) { + throw "Source backend path not found: $sourceBackend" + } + + # Copy backend files (exclude venv, __pycache__, .env) + $excludeDirs = @("venv", "__pycache__", ".pytest_cache", "logs", "temp", "node_modules") + $excludeFiles = @("*.pyc", "*.pyo", "*.log", ".env", ".env.local") + + Get-ChildItem -Path $sourceBackend -Recurse | ForEach-Object { + $relativePath = $_.FullName.Substring($sourceBackend.Length).TrimStart('\', '/') + + # Check if excluded + $inExcludedDir = $false + foreach ($excludeDir in $excludeDirs) { + if ($relativePath -match "^$excludeDir" -or $relativePath -match "[\\/]$excludeDir[\\/]") { + $inExcludedDir = $true + break + } + } + if ($inExcludedDir) { return } + + $isExcludedFile = $false + foreach ($pattern in $excludeFiles) { + if ($_.Name -like $pattern) { + $isExcludedFile = $true + break + } + } + if ($isExcludedFile) { return } + + $destPath = Join-Path $Config.BackendPath $relativePath + + if ($_.PSIsContainer) { + if (-not (Test-Path $destPath)) { + New-Item -ItemType Directory -Path $destPath -Force | Out-Null + } + } else { + $destDir = Split-Path $destPath -Parent + if (-not (Test-Path $destDir)) { + New-Item -ItemType Directory -Path $destDir -Force | Out-Null + } + Copy-Item -Path $_.FullName -Destination $destPath -Force + } + } + Write-Success "Backend files copied" + + # Copy .env.example + $sourceEnvExample = Join-Path $sourceBackend ".env.example" + if (Test-Path $sourceEnvExample) { + $destEnvExample = Join-Path $Config.BackendPath ".env.example" + Copy-Item -Path $sourceEnvExample -Destination $destEnvExample -Force + Write-Success ".env.example template copied" + } + + # Copy shared modules + $sourceShared = Join-Path $Config.SourcePath "shared" + if (Test-Path $sourceShared) { + $destShared = Join-Path (Split-Path $Config.BackendPath -Parent) "shared" + Copy-Item -Path $sourceShared -Destination $destShared -Recurse -Force -Exclude @("__pycache__", "*.pyc") + Write-Success "Shared modules copied" + } + + # Copy frontend files + Write-Step "Copying frontend files..." + $sourceFrontend = Join-Path $Config.SourcePath "frontend" + + if (Test-Path $sourceFrontend) { + Copy-Item -Path "$sourceFrontend\*" -Destination $Config.FrontendPath -Recurse -Force + Write-Success "Frontend files copied" + } else { + Write-Warning "Frontend source not found: $sourceFrontend" + } + + # Create virtual environment + Write-Step "Creating Python virtual environment..." + $venvPath = Join-Path $Config.BackendPath "venv" + & python -m venv $venvPath + Write-Success "Virtual environment created at: $venvPath" + + # Install dependencies + Write-Step "Installing Python dependencies..." + $pipPath = Join-Path $venvPath "Scripts\pip.exe" + $pythonPath = Join-Path $venvPath "Scripts\python.exe" + $requirementsPath = Join-Path $Config.BackendPath "requirements.txt" + + if (Test-Path $requirementsPath) { + Write-Info "Upgrading pip..." + & $pythonPath -m pip install --upgrade pip | Out-Default + + Write-Info "Installing dependencies from requirements.txt..." + & $pipPath install -r $requirementsPath | Out-Default + Write-Success "Python dependencies installed" + } else { + Write-Warning "requirements.txt not found at: $requirementsPath" + } + + # Create Windows Service + Write-Step "Creating Windows Service for backend..." + + # Remove existing service if present + $oldErrorAction = $ErrorActionPreference + $ErrorActionPreference = "SilentlyContinue" + $nssmOutput = & nssm status $Config.BackendServiceName 2>&1 + $serviceExists = $LASTEXITCODE -eq 0 + $ErrorActionPreference = $oldErrorAction + + if ($serviceExists) { + Write-Info "Removing existing service..." + & nssm stop $Config.BackendServiceName 2>&1 | Out-Null + Start-Sleep -Seconds 2 + & nssm remove $Config.BackendServiceName confirm 2>&1 | Out-Null + Start-Sleep -Seconds 2 + } + + # Create service with venv Python + & nssm install $Config.BackendServiceName $pythonPath "-m" "uvicorn" "app.main:app" "--host" "127.0.0.1" "--port" $Config.BackendPort.ToString() "--workers" "4" + & nssm set $Config.BackendServiceName DisplayName $Config.BackendServiceDisplayName + & nssm set $Config.BackendServiceName Description $Config.BackendServiceDescription + & nssm set $Config.BackendServiceName Start SERVICE_AUTO_START + & nssm set $Config.BackendServiceName AppDirectory $Config.BackendPath + & nssm set $Config.BackendServiceName AppEnvironmentExtra "PYTHONPATH=$($Config.InstallPath)" + + # Set logging + $stdoutLog = Join-Path $Config.LogsPath "backend-stdout.log" + $stderrLog = Join-Path $Config.LogsPath "backend-stderr.log" + & nssm set $Config.BackendServiceName AppStdout $stdoutLog + & nssm set $Config.BackendServiceName AppStderr $stderrLog + & nssm set $Config.BackendServiceName AppStdoutCreationDisposition 4 + & nssm set $Config.BackendServiceName AppStderrCreationDisposition 4 + & nssm set $Config.BackendServiceName AppExit Default Restart + & nssm set $Config.BackendServiceName AppRestartDelay 5000 + + Write-Success "Windows Service created: $($Config.BackendServiceName)" + + # Configure IIS + if (Install-IISModules) { + Write-Step "Configuring IIS..." + Import-Module WebAdministration -ErrorAction Stop + + # Create Application Pool + if (Test-Path "IIS:\AppPools\$($Config.IISAppPoolName)") { + Remove-WebAppPool -Name $Config.IISAppPoolName -ErrorAction SilentlyContinue + } + + New-WebAppPool -Name $Config.IISAppPoolName -Force | Out-Null + Set-ItemProperty -Path "IIS:\AppPools\$($Config.IISAppPoolName)" -Name "managedRuntimeVersion" -Value "" + Write-Success "Application Pool created: $($Config.IISAppPoolName)" + + # Create/update application + $existingApp = Get-WebApplication -Name $Config.IISAppName -Site $Config.IISSiteName -ErrorAction SilentlyContinue + if ($existingApp) { + Remove-WebApplication -Name $Config.IISAppName -Site $Config.IISSiteName -ErrorAction SilentlyContinue + } + + New-WebApplication -Name $Config.IISAppName ` + -Site $Config.IISSiteName ` + -PhysicalPath $Config.FrontendPath ` + -ApplicationPool $Config.IISAppPoolName ` + -Force | Out-Null + + Write-Success "IIS Application created: /$($Config.IISAppName)" + + # Copy web.config + $webConfigSource = Join-Path (Split-Path $Config.SourcePath -Parent) "config\web.config" + $webConfigDest = Join-Path $Config.FrontendPath "web.config" + if (Test-Path $webConfigSource) { + Copy-Item -Path $webConfigSource -Destination $webConfigDest -Force + Write-Success "web.config copied" + } + } else { + Write-Warning "IIS modules not installed, skipping IIS configuration" + } + + Write-Host "`n" + ("=" * 70) -ForegroundColor Green + Write-Host " BACKEND + FRONTEND INSTALLATION COMPLETED" -ForegroundColor Green + Write-Host ("=" * 70) -ForegroundColor Green + Write-Host "`nIMPORTANT: Configure .env file before starting service" -ForegroundColor Yellow + Write-Host "Location: $($Config.BackendPath)\.env" -ForegroundColor Yellow + + return $true + } catch { + Write-Host "`n[INSTALLATION FAILED] $_" -ForegroundColor Red + Write-Host $_.ScriptStackTrace -ForegroundColor Red + return $false + } +} + +function Install-TelegramBotFirstTime { + Write-Host "`n" + ("=" * 70) -ForegroundColor Yellow + Write-Host " FIRST-TIME INSTALLATION: TELEGRAM BOT" -ForegroundColor Yellow + Write-Host ("=" * 70) -ForegroundColor Yellow + + try { + # Install prerequisites + Write-Step "Installing prerequisites..." + Install-Chocolatey | Out-Null + + if (-not (Install-Python)) { + throw "Python installation failed" + } + + if (-not (Install-NSSM)) { + throw "NSSM installation failed" + } + + # Create directory structure + Write-Step "Creating directory structure..." + $directories = @( + $Config.TelegramBotPath, + (Join-Path $Config.TelegramBotPath "app"), + (Join-Path $Config.TelegramBotPath "data"), + $Config.TelegramBotLogsPath, + (Join-Path $Config.TelegramBotPath "temp"), + (Join-Path $Config.TelegramBotPath "backups") + ) + + foreach ($dir in $directories) { + if (-not (Test-Path $dir)) { + New-Item -ItemType Directory -Path $dir -Force | Out-Null + Write-Success "Created: $dir" + } else { + Write-Success "Already exists: $dir" + } + } + + # Set permissions + try { + $acl = Get-Acl $Config.TelegramBotPath + $systemRule = New-Object System.Security.AccessControl.FileSystemAccessRule("SYSTEM", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow") + $acl.SetAccessRule($systemRule) + Set-Acl -Path $Config.TelegramBotPath -AclObject $acl + Write-Success "Permissions set for SYSTEM" + } catch { + Write-Warning "Could not set permissions: $_" + } + + # Copy telegram bot files + Write-Step "Copying Telegram bot files..." + $sourceTelegramBot = Join-Path $Config.SourcePath "telegram-bot" + + if (-not (Test-Path $sourceTelegramBot)) { + throw "Source telegram-bot path not found: $sourceTelegramBot" + } + + # Copy app directory + $sourceApp = Join-Path $sourceTelegramBot "app" + $destApp = Join-Path $Config.TelegramBotPath "app" + + if (Test-Path $destApp) { + Remove-Item -Path $destApp -Recurse -Force + } + + Copy-Item -Path $sourceApp -Destination $destApp -Recurse -Force + Write-Success "Application files copied" + + # Copy requirements.txt + $sourceReq = Join-Path $sourceTelegramBot "requirements.txt" + if (Test-Path $sourceReq) { + $destReq = Join-Path $Config.TelegramBotPath "requirements.txt" + Copy-Item -Path $sourceReq -Destination $destReq -Force + Write-Success "requirements.txt copied" + } + + # Copy .env.example + $sourceEnvExample = Join-Path $sourceTelegramBot ".env.example" + if (Test-Path $sourceEnvExample) { + $destEnvExample = Join-Path $Config.TelegramBotPath ".env.example" + Copy-Item -Path $sourceEnvExample -Destination $destEnvExample -Force + Write-Success ".env.example template copied" + } + + # Create virtual environment + Write-Step "Creating Python virtual environment..." + $venvPath = Join-Path $Config.TelegramBotPath "venv" + & python -m venv $venvPath + Write-Success "Virtual environment created" + + # Install dependencies + Write-Step "Installing Python dependencies..." + $pipPath = Join-Path $venvPath "Scripts\pip.exe" + $pythonPath = Join-Path $venvPath "Scripts\python.exe" + $requirementsPath = Join-Path $Config.TelegramBotPath "requirements.txt" + + if (Test-Path $requirementsPath) { + Write-Info "Upgrading pip..." + & $pythonPath -m pip install --upgrade pip | Out-Default + + Write-Info "Installing dependencies..." + & $pipPath install -r $requirementsPath | Out-Default + Write-Success "Python dependencies installed" + } + + # Create Windows Service + Write-Step "Creating Windows Service for Telegram bot..." + + # Remove existing service if present + $oldErrorAction = $ErrorActionPreference + $ErrorActionPreference = "SilentlyContinue" + $nssmOutput = & nssm status $Config.TelegramBotServiceName 2>&1 + $serviceExists = $LASTEXITCODE -eq 0 + $ErrorActionPreference = $oldErrorAction + + if ($serviceExists) { + Write-Info "Removing existing service..." + & nssm stop $Config.TelegramBotServiceName 2>&1 | Out-Null + Start-Sleep -Seconds 2 + & nssm remove $Config.TelegramBotServiceName confirm 2>&1 | Out-Null + Start-Sleep -Seconds 2 + } + + # Create service + & nssm install $Config.TelegramBotServiceName $pythonPath "-m" "app.main" + & nssm set $Config.TelegramBotServiceName DisplayName $Config.TelegramBotServiceDisplayName + & nssm set $Config.TelegramBotServiceName Description $Config.TelegramBotServiceDescription + & nssm set $Config.TelegramBotServiceName Start SERVICE_AUTO_START + & nssm set $Config.TelegramBotServiceName AppDirectory $Config.TelegramBotPath + & nssm set $Config.TelegramBotServiceName AppEnvironmentExtra "PYTHONPATH=$($Config.TelegramBotPath)" + + # Set logging + $stdoutLog = Join-Path $Config.TelegramBotLogsPath "stdout.log" + $stderrLog = Join-Path $Config.TelegramBotLogsPath "stderr.log" + & nssm set $Config.TelegramBotServiceName AppStdout $stdoutLog + & nssm set $Config.TelegramBotServiceName AppStderr $stderrLog + & nssm set $Config.TelegramBotServiceName AppStdoutCreationDisposition 4 + & nssm set $Config.TelegramBotServiceName AppStderrCreationDisposition 4 + & nssm set $Config.TelegramBotServiceName AppExit Default Restart + & nssm set $Config.TelegramBotServiceName AppRestartDelay 5000 + + Write-Success "Windows Service created: $($Config.TelegramBotServiceName)" + + Write-Host "`n" + ("=" * 70) -ForegroundColor Green + Write-Host " TELEGRAM BOT INSTALLATION COMPLETED" -ForegroundColor Green + Write-Host ("=" * 70) -ForegroundColor Green + Write-Host "`nIMPORTANT: Configure .env file before starting service" -ForegroundColor Yellow + Write-Host "Location: $($Config.TelegramBotPath)\.env" -ForegroundColor Yellow + + return $true + } catch { + Write-Host "`n[INSTALLATION FAILED] $_" -ForegroundColor Red + Write-Host $_.ScriptStackTrace -ForegroundColor Red + return $false + } +} + # ============================================================================= # MENU FUNCTIONS # ============================================================================= @@ -653,16 +1309,21 @@ function Update-BackendFiles { Write-Step "Requirements changed, updating Python dependencies..." Copy-Item -Path $sourceReq -Destination $destReq -Force - $pythonCmd = Get-Command python -ErrorAction SilentlyContinue - if ($pythonCmd) { - & python -m pip install -r $destReq --upgrade + # Use virtual environment pip (same pattern as Telegram Bot) + $venvPath = Join-Path $Config.BackendPath "venv" + $pipPath = Join-Path $venvPath "Scripts\pip.exe" + + if (Test-Path $pipPath) { + & $pipPath install -r $destReq --upgrade if ($LASTEXITCODE -eq 0) { - Write-Success "Python dependencies installed successfully" + Write-Success "Python dependencies updated" } else { throw "pip install failed with exit code: $LASTEXITCODE" } } else { - throw "Python not found in PATH" + Write-Warning "Virtual environment not found at: $venvPath" + Write-Warning "Run Install-ROA2WEB.ps1 first to create virtual environment" + throw "Backend virtual environment not found" } } else { Write-Success "Python dependencies unchanged" @@ -782,6 +1443,24 @@ function Deploy-Backend { Write-Host ("=" * 70) -ForegroundColor Cyan try { + # Auto-detect: First-time install or update? + $isInstalled = Test-BackendInstalled + + if (-not $isInstalled) { + Write-Host "" + Write-Warning "Backend NOT detected - performing FIRST-TIME INSTALLATION" + Write-Host "" + Start-Sleep -Seconds 2 + + # Route to first-time installation + return Install-BackendFirstTime + } + + # Backend is already installed - proceed with UPDATE + Write-Host "" + Write-Info "Backend detected - performing UPDATE" + Write-Host "" + # Create backup $backupPath = Backup-CurrentDeployment -Component "Backend" @@ -823,6 +1502,24 @@ function Deploy-TelegramBot { Write-Host ("=" * 70) -ForegroundColor Cyan try { + # Auto-detect: First-time install or update? + $isInstalled = Test-TelegramBotInstalled + + if (-not $isInstalled) { + Write-Host "" + Write-Warning "Telegram Bot NOT detected - performing FIRST-TIME INSTALLATION" + Write-Host "" + Start-Sleep -Seconds 2 + + # Route to first-time installation + return Install-TelegramBotFirstTime + } + + # Telegram Bot is already installed - proceed with UPDATE + Write-Host "" + Write-Info "Telegram Bot detected - performing UPDATE" + Write-Host "" + # Create backup $backupPath = Backup-CurrentDeployment -Component "TelegramBot"