## Bug Fix ### Problem IIS configuration failed with error: ``` [INSTALLATION FAILED] Cannot validate argument on parameter 'Name'. The argument is null. Provide a valid value for the argument, and then try running the command again. at Install-BackendFirstTime, line 643 ``` The error occurred at line 643: ```powershell Remove-WebAppPool -Name $Config.IISAppPoolName -ErrorAction SilentlyContinue ``` ### Root Cause Global configuration was missing IIS-related properties used in the IIS setup section: - `$Config.IISAppPoolName` - used for Application Pool name (line 643, 646, 647) - `$Config.IISSiteName` - used for IIS Site name (line 651, 653, 657) - `$Config.IISAppName` - used for Web Application name (line 651, 653, 656) When the script tried to use these null values, PowerShell validation failed. ### Solution Added IIS configuration properties to `$script:Config` hashtable (lines 81-84): ```powershell # IIS Configuration IISSiteName = "Default Web Site" IISAppName = "roa2web" IISAppPoolName = "ROA2WEB-AppPool" ``` ### Impact - IIS Application Pool creation now works correctly - Web Application is created under "Default Web Site" with path "/roa2web" - Frontend files are served through IIS with proper app pool isolation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1752 lines
63 KiB
PowerShell
1752 lines
63 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
ROA2WEB - Unified Deployment & Management Console for Windows Server
|
|
|
|
.DESCRIPTION
|
|
Interactive console for deploying and managing ROA2WEB application:
|
|
- Deploy Backend + Frontend
|
|
- Deploy Telegram Bot
|
|
- Deploy all components
|
|
- Start/Stop/Restart services
|
|
- Check service status and health
|
|
- Backup and rollback support
|
|
|
|
.PARAMETER NonInteractive
|
|
Run in non-interactive mode with specific action
|
|
|
|
.PARAMETER Action
|
|
Action to perform in non-interactive mode:
|
|
- DeployBackend: Deploy Backend + Frontend
|
|
- DeployTelegramBot: Deploy Telegram Bot only
|
|
- DeployAll: Deploy all components
|
|
- StartAll: Start all services
|
|
- StopAll: Stop all services
|
|
- RestartAll: Restart all services
|
|
- Status: Show status
|
|
|
|
.EXAMPLE
|
|
.\ROA2WEB-Console.ps1
|
|
Launch interactive menu
|
|
|
|
.EXAMPLE
|
|
.\ROA2WEB-Console.ps1 -NonInteractive -Action DeployAll
|
|
Deploy all components without menu
|
|
|
|
.NOTES
|
|
Author: ROA2WEB Team
|
|
Version: 2.0 (Unified Console)
|
|
Requires: Administrator privileges, PowerShell 5.1+
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[switch]$NonInteractive,
|
|
|
|
[ValidateSet("DeployBackend", "DeployTelegramBot", "DeployAll",
|
|
"StartAll", "StopAll", "RestartAll", "Status")]
|
|
[string]$Action = ""
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
# =============================================================================
|
|
# GLOBAL CONFIGURATION
|
|
# =============================================================================
|
|
|
|
# Auto-detect source path: if running from scripts/ subdirectory, use parent
|
|
$detectedSourcePath = if ((Split-Path $PSScriptRoot -Leaf) -eq "scripts") {
|
|
Split-Path $PSScriptRoot -Parent
|
|
} else {
|
|
$PSScriptRoot
|
|
}
|
|
|
|
$script:Config = @{
|
|
# General
|
|
InstallPath = "C:\inetpub\wwwroot\roa2web"
|
|
SourcePath = $detectedSourcePath
|
|
BackupPath = "C:\inetpub\wwwroot\roa2web\backups"
|
|
|
|
# Backend
|
|
BackendPath = "C:\inetpub\wwwroot\roa2web\backend"
|
|
BackendServiceName = "ROA2WEB-Backend"
|
|
BackendServiceDisplayName = "ROA2WEB Backend API"
|
|
BackendServiceDescription = "FastAPI backend service for ROA2WEB application"
|
|
BackendPort = 8000
|
|
BackendHealthUrl = "http://localhost:8000/health"
|
|
BackendHealthTimeout = 5
|
|
|
|
# Frontend
|
|
FrontendPath = "C:\inetpub\wwwroot\roa2web\frontend"
|
|
|
|
# IIS Configuration
|
|
IISSiteName = "Default Web Site"
|
|
IISAppName = "roa2web"
|
|
IISAppPoolName = "ROA2WEB-AppPool"
|
|
|
|
# Telegram Bot
|
|
TelegramBotPath = "C:\inetpub\wwwroot\roa2web\telegram-bot"
|
|
TelegramBotServiceName = "ROA2WEB-TelegramBot"
|
|
TelegramBotServiceDisplayName = "ROA2WEB Telegram Bot"
|
|
TelegramBotServiceDescription = "Telegram bot frontend for ROA2WEB application"
|
|
TelegramBotPort = 8002
|
|
TelegramBotHealthUrl = "http://localhost:8002/internal/health"
|
|
TelegramBotHealthTimeout = 10
|
|
|
|
# Logs
|
|
LogsPath = "C:\inetpub\wwwroot\roa2web\logs"
|
|
TelegramBotLogsPath = "C:\inetpub\wwwroot\roa2web\telegram-bot\logs"
|
|
|
|
# Timeouts
|
|
ServiceTimeout = 30
|
|
}
|
|
|
|
# =============================================================================
|
|
# HELPER FUNCTIONS
|
|
# =============================================================================
|
|
|
|
function Write-Step {
|
|
param([string]$Message)
|
|
Write-Host "`n[*] $Message" -ForegroundColor Cyan
|
|
}
|
|
|
|
function Write-Success {
|
|
param([string]$Message)
|
|
Write-Host " [OK] $Message" -ForegroundColor Green
|
|
}
|
|
|
|
function Write-Error {
|
|
param([string]$Message)
|
|
Write-Host " [ERROR] $Message" -ForegroundColor Red
|
|
}
|
|
|
|
function Write-Warning {
|
|
param([string]$Message)
|
|
Write-Host " [WARN] $Message" -ForegroundColor Yellow
|
|
}
|
|
|
|
function Write-Info {
|
|
param([string]$Message)
|
|
Write-Host " [*] $Message" -ForegroundColor Yellow
|
|
}
|
|
|
|
function Test-Administrator {
|
|
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
|
|
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 {
|
|
return Get-Service -Name $ServiceName -ErrorAction Stop
|
|
} catch {
|
|
return $null
|
|
}
|
|
}
|
|
|
|
function Test-HealthEndpoint {
|
|
param(
|
|
[string]$Url,
|
|
[int]$TimeoutSec
|
|
)
|
|
|
|
try {
|
|
$response = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec $TimeoutSec -ErrorAction Stop
|
|
|
|
if ($response.StatusCode -eq 200) {
|
|
try {
|
|
$content = $response.Content | ConvertFrom-Json
|
|
return @{
|
|
Success = $true
|
|
Status = $content.status
|
|
Details = $content
|
|
}
|
|
} catch {
|
|
return @{
|
|
Success = $true
|
|
Status = "healthy"
|
|
Details = $null
|
|
}
|
|
}
|
|
}
|
|
|
|
return @{
|
|
Success = $false
|
|
Status = "unhealthy"
|
|
Details = $null
|
|
}
|
|
} catch {
|
|
return @{
|
|
Success = $false
|
|
Status = "unreachable"
|
|
Details = $null
|
|
Error = $_.Exception.Message
|
|
}
|
|
}
|
|
}
|
|
|
|
function Wait-ForKeyPress {
|
|
Write-Host "`nPress any key to continue..." -ForegroundColor Gray
|
|
$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"
|
|
|
|
# Define paths (BEFORE installing dependencies)
|
|
$pipPath = Join-Path $venvPath "Scripts\pip.exe"
|
|
$pythonPath = Join-Path $venvPath "Scripts\python.exe"
|
|
$requirementsPath = Join-Path $Config.BackendPath "requirements.txt"
|
|
|
|
# Install dependencies
|
|
Write-Step "Installing Python dependencies..."
|
|
|
|
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"
|
|
|
|
# Define paths (BEFORE installing dependencies)
|
|
$pipPath = Join-Path $venvPath "Scripts\pip.exe"
|
|
$pythonPath = Join-Path $venvPath "Scripts\python.exe"
|
|
$requirementsPath = Join-Path $Config.TelegramBotPath "requirements.txt"
|
|
|
|
# Install dependencies
|
|
Write-Step "Installing Python dependencies..."
|
|
|
|
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
|
|
# =============================================================================
|
|
|
|
function Show-MainMenu {
|
|
Clear-Host
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " ROA2WEB - Unified Deployment & Management Console" -ForegroundColor Cyan
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host ""
|
|
Write-Host " Main Menu:" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host " [1] Deploy Components" -ForegroundColor White
|
|
Write-Host " (Update application files and configurations)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host " [2] Manage Services" -ForegroundColor White
|
|
Write-Host " (Start, stop, restart Backend and Telegram Bot)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host " [3] Check Status" -ForegroundColor White
|
|
Write-Host " (View service status and health checks)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host " [Q] Quit" -ForegroundColor Red
|
|
Write-Host ""
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
|
|
do {
|
|
Write-Host "`nYour choice: " -ForegroundColor Yellow -NoNewline
|
|
$choice = Read-Host
|
|
|
|
switch ($choice.ToUpper()) {
|
|
"1" { return "Deploy" }
|
|
"2" { return "Manage" }
|
|
"3" { return "Status" }
|
|
"Q" { return "Quit" }
|
|
default {
|
|
Write-Host "Invalid choice. Please select 1-3 or Q." -ForegroundColor Red
|
|
}
|
|
}
|
|
} while ($true)
|
|
}
|
|
|
|
function Show-DeployMenu {
|
|
Clear-Host
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " Deploy Components" -ForegroundColor Cyan
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host ""
|
|
Write-Host " Select what to deploy:" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host " [1] Backend + Frontend" -ForegroundColor White
|
|
Write-Host " (FastAPI backend + Vue.js frontend files)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host " [2] Telegram Bot" -ForegroundColor White
|
|
Write-Host " (Telegram bot application only)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host " [3] All Components" -ForegroundColor White
|
|
Write-Host " (Backend + Frontend + Telegram Bot)" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host " [B] Back to Main Menu" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
|
|
do {
|
|
Write-Host "`nYour choice: " -ForegroundColor Yellow -NoNewline
|
|
$choice = Read-Host
|
|
|
|
switch ($choice.ToUpper()) {
|
|
"1" { return "Backend" }
|
|
"2" { return "TelegramBot" }
|
|
"3" { return "All" }
|
|
"B" { return "Back" }
|
|
default {
|
|
Write-Host "Invalid choice. Please select 1-3 or B." -ForegroundColor Red
|
|
}
|
|
}
|
|
} while ($true)
|
|
}
|
|
|
|
function Show-ManageMenu {
|
|
Clear-Host
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " Manage Services" -ForegroundColor Cyan
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host ""
|
|
Write-Host " Quick Actions:" -ForegroundColor Yellow
|
|
Write-Host " [1] Start All Services" -ForegroundColor Green
|
|
Write-Host " [2] Stop All Services" -ForegroundColor Red
|
|
Write-Host " [3] Restart All Services" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host " Backend Service:" -ForegroundColor Yellow
|
|
Write-Host " [4] Start Backend" -ForegroundColor Green
|
|
Write-Host " [5] Stop Backend" -ForegroundColor Red
|
|
Write-Host " [6] Restart Backend" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host " Telegram Bot Service:" -ForegroundColor Yellow
|
|
Write-Host " [7] Start Telegram Bot" -ForegroundColor Green
|
|
Write-Host " [8] Stop Telegram Bot" -ForegroundColor Red
|
|
Write-Host " [9] Restart Telegram Bot" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host " [B] Back to Main Menu" -ForegroundColor Gray
|
|
Write-Host ""
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
|
|
do {
|
|
Write-Host "`nYour choice: " -ForegroundColor Yellow -NoNewline
|
|
$choice = Read-Host
|
|
|
|
switch ($choice.ToUpper()) {
|
|
"1" { return @{ Action = "Start"; Component = "All" } }
|
|
"2" { return @{ Action = "Stop"; Component = "All" } }
|
|
"3" { return @{ Action = "Restart"; Component = "All" } }
|
|
"4" { return @{ Action = "Start"; Component = "Backend" } }
|
|
"5" { return @{ Action = "Stop"; Component = "Backend" } }
|
|
"6" { return @{ Action = "Restart"; Component = "Backend" } }
|
|
"7" { return @{ Action = "Start"; Component = "TelegramBot" } }
|
|
"8" { return @{ Action = "Stop"; Component = "TelegramBot" } }
|
|
"9" { return @{ Action = "Restart"; Component = "TelegramBot" } }
|
|
"B" { return @{ Action = "Back"; Component = "" } }
|
|
default {
|
|
Write-Host "Invalid choice. Please select 1-9 or B." -ForegroundColor Red
|
|
}
|
|
}
|
|
} while ($true)
|
|
}
|
|
|
|
# =============================================================================
|
|
# SERVICE MANAGEMENT FUNCTIONS
|
|
# =============================================================================
|
|
|
|
function Start-ServiceComponent {
|
|
param(
|
|
[string]$ComponentName,
|
|
[string]$ServiceName,
|
|
[string]$HealthUrl,
|
|
[int]$HealthTimeout
|
|
)
|
|
|
|
Write-Step "Starting $ComponentName..."
|
|
|
|
$service = Get-ServiceSafe -ServiceName $ServiceName
|
|
|
|
if (-not $service) {
|
|
Write-Error "Service '$ServiceName' not found"
|
|
Write-Warning "Run Install script first to create the service"
|
|
return $false
|
|
}
|
|
|
|
if ($service.Status -eq "Running") {
|
|
Write-Success "Service is already running"
|
|
return $true
|
|
}
|
|
|
|
try {
|
|
Start-Service -Name $ServiceName
|
|
Write-Info "Service start command issued"
|
|
|
|
$elapsed = 0
|
|
while ($service.Status -ne "Running" -and $elapsed -lt $Config.ServiceTimeout) {
|
|
Start-Sleep -Seconds 1
|
|
$service.Refresh()
|
|
$elapsed++
|
|
if ($elapsed % 5 -eq 0) {
|
|
Write-Info "Waiting for service to start... ($elapsed/$($Config.ServiceTimeout))"
|
|
}
|
|
}
|
|
|
|
if ($service.Status -eq "Running") {
|
|
Write-Success "Service started successfully"
|
|
|
|
# Wait for initialization
|
|
$waitTime = if ($ComponentName -eq "Telegram Bot") { 5 } else { 3 }
|
|
Start-Sleep -Seconds $waitTime
|
|
|
|
# Test health endpoint
|
|
$health = Test-HealthEndpoint -Url $HealthUrl -TimeoutSec $HealthTimeout
|
|
|
|
if ($health.Success) {
|
|
Write-Success "Health check passed: $($health.Status)"
|
|
} else {
|
|
Write-Warning "Health check failed: $($health.Status) (service may still be starting)"
|
|
}
|
|
|
|
return $true
|
|
} else {
|
|
Write-Error "Service failed to start (Status: $($service.Status))"
|
|
return $false
|
|
}
|
|
} catch {
|
|
Write-Error "Failed to start service: $_"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Stop-ServiceComponent {
|
|
param(
|
|
[string]$ComponentName,
|
|
[string]$ServiceName
|
|
)
|
|
|
|
Write-Step "Stopping $ComponentName..."
|
|
|
|
$service = Get-ServiceSafe -ServiceName $ServiceName
|
|
|
|
if (-not $service) {
|
|
Write-Error "Service '$ServiceName' not found"
|
|
return $false
|
|
}
|
|
|
|
if ($service.Status -eq "Stopped") {
|
|
Write-Success "Service is already stopped"
|
|
return $true
|
|
}
|
|
|
|
try {
|
|
Stop-Service -Name $ServiceName -Force
|
|
Write-Info "Service stop command issued"
|
|
|
|
$elapsed = 0
|
|
while ($service.Status -ne "Stopped" -and $elapsed -lt $Config.ServiceTimeout) {
|
|
Start-Sleep -Seconds 1
|
|
$service.Refresh()
|
|
$elapsed++
|
|
if ($elapsed % 5 -eq 0) {
|
|
Write-Info "Waiting for service to stop... ($elapsed/$($Config.ServiceTimeout))"
|
|
}
|
|
}
|
|
|
|
if ($service.Status -eq "Stopped") {
|
|
Write-Success "Service stopped successfully"
|
|
return $true
|
|
} else {
|
|
Write-Error "Service did not stop within timeout (Status: $($service.Status))"
|
|
return $false
|
|
}
|
|
} catch {
|
|
Write-Error "Failed to stop service: $_"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Restart-ServiceComponent {
|
|
param(
|
|
[string]$ComponentName,
|
|
[string]$ServiceName,
|
|
[string]$HealthUrl,
|
|
[int]$HealthTimeout
|
|
)
|
|
|
|
Write-Step "Restarting $ComponentName..."
|
|
|
|
$stopResult = Stop-ServiceComponent -ComponentName $ComponentName -ServiceName $ServiceName
|
|
|
|
if (-not $stopResult) {
|
|
Write-Error "Failed to stop service, aborting restart"
|
|
return $false
|
|
}
|
|
|
|
Start-Sleep -Seconds 2
|
|
|
|
$startResult = Start-ServiceComponent -ComponentName $ComponentName -ServiceName $ServiceName -HealthUrl $HealthUrl -HealthTimeout $HealthTimeout
|
|
|
|
if ($startResult) {
|
|
Write-Success "Service restarted successfully"
|
|
return $true
|
|
} else {
|
|
Write-Error "Failed to start service after stop"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Show-ServiceStatus {
|
|
param(
|
|
[string]$ComponentName,
|
|
[string]$ServiceName,
|
|
[string]$HealthUrl,
|
|
[int]$HealthTimeout
|
|
)
|
|
|
|
$service = Get-ServiceSafe -ServiceName $ServiceName
|
|
|
|
Write-Host "`n${ComponentName}:" -ForegroundColor Cyan
|
|
Write-Host " Service Name: $ServiceName" -ForegroundColor Gray
|
|
|
|
if ($service) {
|
|
$statusColor = switch ($service.Status) {
|
|
"Running" { "Green" }
|
|
"Stopped" { "Yellow" }
|
|
default { "Gray" }
|
|
}
|
|
Write-Host " Status: $($service.Status)" -ForegroundColor $statusColor
|
|
Write-Host " Start Type: $($service.StartType)" -ForegroundColor Gray
|
|
|
|
if ($service.Status -eq "Running") {
|
|
$health = Test-HealthEndpoint -Url $HealthUrl -TimeoutSec $HealthTimeout
|
|
$healthColor = switch ($health.Status) {
|
|
"healthy" { "Green" }
|
|
"ok" { "Green" }
|
|
"unhealthy" { "Red" }
|
|
"unreachable" { "Yellow" }
|
|
default { "Gray" }
|
|
}
|
|
Write-Host " Health: $($health.Status)" -ForegroundColor $healthColor
|
|
}
|
|
} else {
|
|
Write-Host " Status: Not Installed" -ForegroundColor Red
|
|
}
|
|
}
|
|
|
|
# =============================================================================
|
|
# BACKUP FUNCTIONS
|
|
# =============================================================================
|
|
|
|
function New-BackupDirectory {
|
|
if (-not (Test-Path $Config.BackupPath)) {
|
|
New-Item -ItemType Directory -Path $Config.BackupPath -Force | Out-Null
|
|
}
|
|
}
|
|
|
|
function Backup-CurrentDeployment {
|
|
param(
|
|
[string]$Component # "Backend", "TelegramBot", or "All"
|
|
)
|
|
|
|
Write-Step "Creating backup of current deployment..."
|
|
|
|
New-BackupDirectory
|
|
|
|
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
|
$backupName = "backup-$Component-$timestamp"
|
|
$backupFullPath = Join-Path $Config.BackupPath $backupName
|
|
|
|
try {
|
|
New-Item -ItemType Directory -Path $backupFullPath -Force | Out-Null
|
|
|
|
switch ($Component) {
|
|
"Backend" {
|
|
# Backup backend
|
|
if (Test-Path $Config.BackendPath) {
|
|
$backupBackendPath = Join-Path $backupFullPath "backend"
|
|
Copy-Item -Path $Config.BackendPath -Destination $backupBackendPath -Recurse -Force -ErrorAction SilentlyContinue
|
|
Write-Success "Backend backed up"
|
|
}
|
|
|
|
# Backup frontend
|
|
if (Test-Path $Config.FrontendPath) {
|
|
$backupFrontendPath = Join-Path $backupFullPath "frontend"
|
|
Copy-Item -Path $Config.FrontendPath -Destination $backupFrontendPath -Recurse -Force -ErrorAction SilentlyContinue
|
|
Write-Success "Frontend backed up"
|
|
}
|
|
}
|
|
|
|
"TelegramBot" {
|
|
# Backup app directory
|
|
$appPath = Join-Path $Config.TelegramBotPath "app"
|
|
if (Test-Path $appPath) {
|
|
$backupAppPath = Join-Path $backupFullPath "app"
|
|
Copy-Item -Path $appPath -Destination $backupAppPath -Recurse -Force
|
|
Write-Success "App files backed up"
|
|
}
|
|
|
|
# Backup requirements.txt
|
|
$reqFile = Join-Path $Config.TelegramBotPath "requirements.txt"
|
|
if (Test-Path $reqFile) {
|
|
Copy-Item -Path $reqFile -Destination (Join-Path $backupFullPath "requirements.txt") -Force
|
|
Write-Success "Requirements file backed up"
|
|
}
|
|
|
|
# Backup .env
|
|
$envFile = Join-Path $Config.TelegramBotPath ".env"
|
|
if (Test-Path $envFile) {
|
|
Copy-Item -Path $envFile -Destination (Join-Path $backupFullPath ".env") -Force
|
|
Write-Success ".env file backed up"
|
|
}
|
|
|
|
# Backup database
|
|
$dbFile = Join-Path $Config.TelegramBotPath "data\telegram_bot.db"
|
|
if (Test-Path $dbFile) {
|
|
$backupDataPath = Join-Path $backupFullPath "data"
|
|
New-Item -ItemType Directory -Path $backupDataPath -Force | Out-Null
|
|
Copy-Item -Path $dbFile -Destination (Join-Path $backupDataPath "telegram_bot.db") -Force
|
|
Write-Success "Database backed up"
|
|
}
|
|
}
|
|
|
|
"All" {
|
|
# Backup both
|
|
Backup-CurrentDeployment -Component "Backend"
|
|
Backup-CurrentDeployment -Component "TelegramBot"
|
|
return $backupFullPath
|
|
}
|
|
}
|
|
|
|
Write-Success "Backup created at: $backupFullPath"
|
|
|
|
# Clean old backups (keep last 10)
|
|
$allBackups = Get-ChildItem -Path $Config.BackupPath -Directory |
|
|
Where-Object { $_.Name -like "backup-*" } |
|
|
Sort-Object Name -Descending
|
|
|
|
if ($allBackups.Count -gt 10) {
|
|
$oldBackups = $allBackups | Select-Object -Skip 10
|
|
foreach ($oldBackup in $oldBackups) {
|
|
Remove-Item -Path $oldBackup.FullName -Recurse -Force
|
|
Write-Success "Cleaned old backup: $($oldBackup.Name)"
|
|
}
|
|
}
|
|
|
|
return $backupFullPath
|
|
} catch {
|
|
Write-Error "Backup failed: $_"
|
|
throw
|
|
}
|
|
}
|
|
|
|
# =============================================================================
|
|
# DEPLOYMENT FUNCTIONS
|
|
# =============================================================================
|
|
|
|
function Update-BackendFiles {
|
|
Write-Step "Updating backend files..."
|
|
|
|
$sourceBackend = Join-Path $Config.SourcePath "backend"
|
|
|
|
if (-not (Test-Path $sourceBackend)) {
|
|
throw "Source backend path not found: $sourceBackend"
|
|
}
|
|
|
|
try {
|
|
# Copy files except .env and logs
|
|
$excludeFiles = @("*.env", "*.log", "*.pyc", "__pycache__")
|
|
|
|
Get-ChildItem -Path $sourceBackend -Recurse -File | ForEach-Object {
|
|
$relativePath = $_.FullName.Substring($sourceBackend.Length)
|
|
$destPath = Join-Path $Config.BackendPath $relativePath
|
|
|
|
$skip = $false
|
|
foreach ($pattern in $excludeFiles) {
|
|
if ($_.Name -like $pattern -or $_.Directory.Name -eq "__pycache__") {
|
|
$skip = $true
|
|
break
|
|
}
|
|
}
|
|
|
|
if (-not $skip) {
|
|
$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 updated"
|
|
|
|
# Copy .env.example explicitly (excluded from recursive copy)
|
|
$sourceEnvExample = Join-Path $sourceBackend ".env.example"
|
|
$destEnvExample = Join-Path $Config.BackendPath ".env.example"
|
|
if (Test-Path $sourceEnvExample) {
|
|
Copy-Item -Path $sourceEnvExample -Destination $destEnvExample -Force
|
|
Write-Success ".env.example template updated"
|
|
}
|
|
|
|
# Check if requirements.txt changed
|
|
$sourceReq = Join-Path $sourceBackend "requirements.txt"
|
|
$destReq = Join-Path $Config.BackendPath "requirements.txt"
|
|
|
|
if (Test-Path $sourceReq) {
|
|
$sourceHash = (Get-FileHash $sourceReq).Hash
|
|
$destHash = if (Test-Path $destReq) { (Get-FileHash $destReq).Hash } else { "" }
|
|
|
|
if ($sourceHash -ne $destHash) {
|
|
Write-Step "Requirements changed, updating Python dependencies..."
|
|
Copy-Item -Path $sourceReq -Destination $destReq -Force
|
|
|
|
# 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 updated"
|
|
} else {
|
|
throw "pip install failed with exit code: $LASTEXITCODE"
|
|
}
|
|
} else {
|
|
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"
|
|
}
|
|
}
|
|
} catch {
|
|
Write-Error "Failed to update backend files: $_"
|
|
throw
|
|
}
|
|
}
|
|
|
|
function Update-FrontendFiles {
|
|
Write-Step "Updating frontend files..."
|
|
|
|
$sourceFrontend = Join-Path $Config.SourcePath "frontend"
|
|
|
|
if (-not (Test-Path $sourceFrontend)) {
|
|
throw "Source frontend path not found: $sourceFrontend"
|
|
}
|
|
|
|
try {
|
|
# Remove old frontend files (except web.config)
|
|
if (Test-Path $Config.FrontendPath) {
|
|
Get-ChildItem -Path $Config.FrontendPath -Exclude "web.config" | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
# Copy new frontend files
|
|
Copy-Item -Path "$sourceFrontend\*" -Destination $Config.FrontendPath -Recurse -Force
|
|
|
|
Write-Success "Frontend files updated"
|
|
} catch {
|
|
Write-Error "Failed to update frontend files: $_"
|
|
throw
|
|
}
|
|
}
|
|
|
|
function Update-TelegramBotFiles {
|
|
Write-Step "Updating Telegram bot files..."
|
|
|
|
$sourceApp = Join-Path $Config.SourcePath "app"
|
|
|
|
if (-not (Test-Path $sourceApp)) {
|
|
throw "Source app directory not found: $sourceApp"
|
|
}
|
|
|
|
try {
|
|
# Remove old app directory
|
|
$destApp = Join-Path $Config.TelegramBotPath "app"
|
|
if (Test-Path $destApp) {
|
|
Remove-Item -Path $destApp -Recurse -Force
|
|
Write-Success "Removed old app directory"
|
|
}
|
|
|
|
# Copy new app files
|
|
Copy-Item -Path $sourceApp -Destination $destApp -Recurse -Force
|
|
Write-Success "Application files updated"
|
|
|
|
# Update requirements.txt if changed
|
|
$sourceReq = Join-Path $Config.SourcePath "requirements.txt"
|
|
$destReq = Join-Path $Config.TelegramBotPath "requirements.txt"
|
|
|
|
if (Test-Path $sourceReq) {
|
|
$sourceHash = (Get-FileHash $sourceReq -Algorithm SHA256).Hash
|
|
$destHash = if (Test-Path $destReq) {
|
|
(Get-FileHash $destReq -Algorithm SHA256).Hash
|
|
} else {
|
|
""
|
|
}
|
|
|
|
if ($sourceHash -ne $destHash) {
|
|
Write-Step "Requirements changed, updating Python dependencies..."
|
|
Copy-Item -Path $sourceReq -Destination $destReq -Force
|
|
|
|
# Use virtual environment pip
|
|
$venvPath = Join-Path $Config.TelegramBotPath "venv"
|
|
$pipPath = Join-Path $venvPath "Scripts\pip.exe"
|
|
|
|
if (Test-Path $pipPath) {
|
|
& $pipPath install -r $destReq --upgrade
|
|
Write-Success "Python dependencies updated"
|
|
} else {
|
|
Write-Warning "Virtual environment not found, skipping dependency update"
|
|
}
|
|
} else {
|
|
Write-Success "Python dependencies unchanged"
|
|
}
|
|
}
|
|
|
|
# Copy .env.example template (always update to keep in sync)
|
|
$sourceEnvExample = Join-Path $Config.SourcePath ".env.example"
|
|
$destEnvExample = Join-Path $Config.TelegramBotPath ".env.example"
|
|
if (Test-Path $sourceEnvExample) {
|
|
Copy-Item -Path $sourceEnvExample -Destination $destEnvExample -Force
|
|
Write-Success ".env.example template updated"
|
|
}
|
|
|
|
# Preserve .env file (or create from .env.example if missing)
|
|
$envFile = Join-Path $Config.TelegramBotPath ".env"
|
|
if (-not (Test-Path $envFile)) {
|
|
if (Test-Path $sourceEnvExample) {
|
|
Copy-Item -Path $sourceEnvExample -Destination $envFile -Force
|
|
Write-Warning "Created .env from .env.example - PLEASE CONFIGURE"
|
|
}
|
|
} else {
|
|
Write-Success ".env file preserved"
|
|
}
|
|
|
|
} catch {
|
|
Write-Error "Failed to update Telegram bot files: $_"
|
|
throw
|
|
}
|
|
}
|
|
|
|
function Deploy-Backend {
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " DEPLOYING BACKEND + FRONTEND" -ForegroundColor Cyan
|
|
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"
|
|
|
|
# Stop backend service
|
|
$stopped = Stop-ServiceComponent -ComponentName "Backend" -ServiceName $Config.BackendServiceName
|
|
if (-not $stopped) {
|
|
throw "Failed to stop backend service"
|
|
}
|
|
|
|
# Update files
|
|
Update-BackendFiles
|
|
Update-FrontendFiles
|
|
|
|
# Start backend service
|
|
$started = Start-ServiceComponent -ComponentName "Backend" `
|
|
-ServiceName $Config.BackendServiceName `
|
|
-HealthUrl $Config.BackendHealthUrl `
|
|
-HealthTimeout $Config.BackendHealthTimeout
|
|
|
|
if ($started) {
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Green
|
|
Write-Host " BACKEND + FRONTEND DEPLOYED SUCCESSFULLY" -ForegroundColor Green
|
|
Write-Host ("=" * 70) -ForegroundColor Green
|
|
Write-Host " Backup: $backupPath" -ForegroundColor Gray
|
|
return $true
|
|
} else {
|
|
throw "Failed to start backend service after deployment"
|
|
}
|
|
|
|
} catch {
|
|
Write-Host "`n[DEPLOYMENT FAILED] $_" -ForegroundColor Red
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Deploy-TelegramBot {
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " DEPLOYING TELEGRAM BOT" -ForegroundColor Cyan
|
|
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"
|
|
|
|
# Stop telegram bot service
|
|
$stopped = Stop-ServiceComponent -ComponentName "Telegram Bot" -ServiceName $Config.TelegramBotServiceName
|
|
if (-not $stopped) {
|
|
throw "Failed to stop Telegram bot service"
|
|
}
|
|
|
|
# Update files
|
|
# Temporarily change source path for telegram bot
|
|
$originalSourcePath = $Config.SourcePath
|
|
$Config.SourcePath = Join-Path $originalSourcePath "telegram-bot"
|
|
|
|
Update-TelegramBotFiles
|
|
|
|
# Restore original source path
|
|
$Config.SourcePath = $originalSourcePath
|
|
|
|
# Start telegram bot service
|
|
$started = Start-ServiceComponent -ComponentName "Telegram Bot" `
|
|
-ServiceName $Config.TelegramBotServiceName `
|
|
-HealthUrl $Config.TelegramBotHealthUrl `
|
|
-HealthTimeout $Config.TelegramBotHealthTimeout
|
|
|
|
if ($started) {
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Green
|
|
Write-Host " TELEGRAM BOT DEPLOYED SUCCESSFULLY" -ForegroundColor Green
|
|
Write-Host ("=" * 70) -ForegroundColor Green
|
|
Write-Host " Backup: $backupPath" -ForegroundColor Gray
|
|
return $true
|
|
} else {
|
|
throw "Failed to start Telegram bot service after deployment"
|
|
}
|
|
|
|
} catch {
|
|
Write-Host "`n[DEPLOYMENT FAILED] $_" -ForegroundColor Red
|
|
return $false
|
|
}
|
|
}
|
|
|
|
# =============================================================================
|
|
# MAIN EXECUTION FLOW
|
|
# =============================================================================
|
|
|
|
function Execute-ManageAction {
|
|
param(
|
|
[string]$Action,
|
|
[string]$Component
|
|
)
|
|
|
|
$allSuccess = $true
|
|
|
|
$components = @()
|
|
switch ($Component) {
|
|
"All" {
|
|
$components = @(
|
|
@{ Name = "Backend"; ServiceName = $Config.BackendServiceName;
|
|
HealthUrl = $Config.BackendHealthUrl; HealthTimeout = $Config.BackendHealthTimeout },
|
|
@{ Name = "Telegram Bot"; ServiceName = $Config.TelegramBotServiceName;
|
|
HealthUrl = $Config.TelegramBotHealthUrl; HealthTimeout = $Config.TelegramBotHealthTimeout }
|
|
)
|
|
}
|
|
"Backend" {
|
|
$components = @(
|
|
@{ Name = "Backend"; ServiceName = $Config.BackendServiceName;
|
|
HealthUrl = $Config.BackendHealthUrl; HealthTimeout = $Config.BackendHealthTimeout }
|
|
)
|
|
}
|
|
"TelegramBot" {
|
|
$components = @(
|
|
@{ Name = "Telegram Bot"; ServiceName = $Config.TelegramBotServiceName;
|
|
HealthUrl = $Config.TelegramBotHealthUrl; HealthTimeout = $Config.TelegramBotHealthTimeout }
|
|
)
|
|
}
|
|
}
|
|
|
|
foreach ($comp in $components) {
|
|
$result = switch ($Action) {
|
|
"Start" {
|
|
Start-ServiceComponent -ComponentName $comp.Name -ServiceName $comp.ServiceName `
|
|
-HealthUrl $comp.HealthUrl -HealthTimeout $comp.HealthTimeout
|
|
}
|
|
"Stop" {
|
|
Stop-ServiceComponent -ComponentName $comp.Name -ServiceName $comp.ServiceName
|
|
}
|
|
"Restart" {
|
|
Restart-ServiceComponent -ComponentName $comp.Name -ServiceName $comp.ServiceName `
|
|
-HealthUrl $comp.HealthUrl -HealthTimeout $comp.HealthTimeout
|
|
}
|
|
}
|
|
|
|
if (-not $result) {
|
|
$allSuccess = $false
|
|
}
|
|
}
|
|
|
|
if ($allSuccess) {
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " OPERATION COMPLETED SUCCESSFULLY" -ForegroundColor Green
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
} else {
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " OPERATION COMPLETED WITH ERRORS" -ForegroundColor Red
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
}
|
|
}
|
|
|
|
function Execute-DeployAction {
|
|
param([string]$Component)
|
|
|
|
$success = switch ($Component) {
|
|
"Backend" { Deploy-Backend }
|
|
"TelegramBot" { Deploy-TelegramBot }
|
|
"All" {
|
|
$backendOk = Deploy-Backend
|
|
$telegramOk = Deploy-TelegramBot
|
|
$backendOk -and $telegramOk
|
|
}
|
|
}
|
|
|
|
if ($success) {
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " DEPLOYMENT COMPLETED" -ForegroundColor Green
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
} else {
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " DEPLOYMENT FAILED" -ForegroundColor Red
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
}
|
|
}
|
|
|
|
function Show-AllStatus {
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " ROA2WEB - System Status" -ForegroundColor Cyan
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
|
|
Show-ServiceStatus -ComponentName "Backend" `
|
|
-ServiceName $Config.BackendServiceName `
|
|
-HealthUrl $Config.BackendHealthUrl `
|
|
-HealthTimeout $Config.BackendHealthTimeout
|
|
|
|
Show-ServiceStatus -ComponentName "Telegram Bot" `
|
|
-ServiceName $Config.TelegramBotServiceName `
|
|
-HealthUrl $Config.TelegramBotHealthUrl `
|
|
-HealthTimeout $Config.TelegramBotHealthTimeout
|
|
|
|
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
|
|
}
|
|
|
|
function Main {
|
|
# Check administrator privileges
|
|
if (-not (Test-Administrator)) {
|
|
Write-Host "`n[ERROR] This script requires Administrator privileges" -ForegroundColor Red
|
|
Write-Host "Please run PowerShell as Administrator and try again.`n" -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
|
|
# Non-interactive mode
|
|
if ($NonInteractive -and $Action) {
|
|
switch ($Action) {
|
|
"DeployBackend" { Execute-DeployAction -Component "Backend" }
|
|
"DeployTelegramBot" { Execute-DeployAction -Component "TelegramBot" }
|
|
"DeployAll" { Execute-DeployAction -Component "All" }
|
|
"StartAll" { Execute-ManageAction -Action "Start" -Component "All" }
|
|
"StopAll" { Execute-ManageAction -Action "Stop" -Component "All" }
|
|
"RestartAll" { Execute-ManageAction -Action "Restart" -Component "All" }
|
|
"Status" { Show-AllStatus }
|
|
}
|
|
return
|
|
}
|
|
|
|
# Interactive mode - main loop
|
|
do {
|
|
$mainChoice = Show-MainMenu
|
|
|
|
switch ($mainChoice) {
|
|
"Deploy" {
|
|
do {
|
|
$deployChoice = Show-DeployMenu
|
|
|
|
if ($deployChoice -ne "Back") {
|
|
Execute-DeployAction -Component $deployChoice
|
|
Wait-ForKeyPress
|
|
}
|
|
} while ($deployChoice -ne "Back")
|
|
}
|
|
|
|
"Manage" {
|
|
do {
|
|
$manageChoice = Show-ManageMenu
|
|
|
|
if ($manageChoice.Action -ne "Back") {
|
|
Execute-ManageAction -Action $manageChoice.Action -Component $manageChoice.Component
|
|
Wait-ForKeyPress
|
|
}
|
|
} while ($manageChoice.Action -ne "Back")
|
|
}
|
|
|
|
"Status" {
|
|
Show-AllStatus
|
|
Wait-ForKeyPress
|
|
}
|
|
|
|
"Quit" {
|
|
Write-Host "`nGoodbye!`n" -ForegroundColor Cyan
|
|
return
|
|
}
|
|
}
|
|
} while ($true)
|
|
}
|
|
|
|
# Run main
|
|
Main
|