<# .SYNOPSIS Unified Service Management for ROA2WEB Application .DESCRIPTION Manages Windows services for ROA2WEB Backend and Telegram Bot. Provides unified interface for start, stop, restart, and status operations. .PARAMETER Action Action to perform: Start, Stop, Restart, Status .PARAMETER Component Component(s) to manage: - All (default): Manage both Backend and TelegramBot - Backend: Manage only Backend service - TelegramBot: Manage only Telegram Bot service .PARAMETER Timeout Timeout in seconds for service operations (default: 30) .EXAMPLE .\Manage-ROA2WEB.ps1 -Action Start Start all services (Backend + TelegramBot) .EXAMPLE .\Manage-ROA2WEB.ps1 -Action Stop -Component Backend Stop only Backend service .EXAMPLE .\Manage-ROA2WEB.ps1 -Action Restart -Component TelegramBot Restart only Telegram Bot service .EXAMPLE .\Manage-ROA2WEB.ps1 -Action Status Show status of all services .NOTES Author: ROA2WEB Team Version: 2.0 (Unified Management Script) Requires: Administrator privileges #> [CmdletBinding()] param( [Parameter(Mandatory=$true)] [ValidateSet("Start", "Stop", "Restart", "Status")] [string]$Action, [ValidateSet("All", "Backend", "TelegramBot")] [string]$Component = "All", [int]$Timeout = 30 ) $ErrorActionPreference = "Stop" # ============================================================================= # CONFIGURATION # ============================================================================= $config = @{ Backend = @{ ServiceName = "ROA2WEB-Backend" DisplayName = "ROA2WEB Backend" HealthUrl = "http://localhost:8000/health" HealthTimeout = 5 } TelegramBot = @{ ServiceName = "ROA2WEB-TelegramBot" DisplayName = "ROA2WEB Telegram Bot" HealthUrl = "http://localhost:8002/internal/health" HealthTimeout = 10 } } # ============================================================================= # 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 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 to parse JSON if available 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 Start-ServiceComponent { param( [string]$ComponentName, [hashtable]$ComponentConfig ) Write-Step "Starting $($ComponentConfig.DisplayName)..." $service = Get-ServiceSafe -ServiceName $ComponentConfig.ServiceName if (-not $service) { Write-Error "Service '$($ComponentConfig.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 Start-Service -Name $ComponentConfig.ServiceName Write-Info "Service start command issued" # Wait for service to start $elapsed = 0 while ($service.Status -ne "Running" -and $elapsed -lt $Timeout) { Start-Sleep -Seconds 1 $service.Refresh() $elapsed++ if ($elapsed % 5 -eq 0) { Write-Info "Waiting for service to start... ($elapsed/$Timeout)" } } if ($service.Status -eq "Running") { Write-Success "Service started successfully" # Wait a bit for service to fully initialize $waitTime = if ($ComponentName -eq "TelegramBot") { 5 } else { 3 } Start-Sleep -Seconds $waitTime # Test health endpoint $health = Test-HealthEndpoint -Url $ComponentConfig.HealthUrl -TimeoutSec $ComponentConfig.HealthTimeout if ($health.Success) { Write-Success "Health check passed: $($health.Status)" # Show additional details for Telegram Bot if ($ComponentName -eq "TelegramBot" -and $health.Details) { if ($health.Details.database) { Write-Success "Database: $($health.Details.database.status)" } } } else { Write-Warning "Health check failed: $($health.Status) (service may still be starting)" if ($health.Error) { Write-Warning "Error: $($health.Error)" } } 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, [hashtable]$ComponentConfig ) Write-Step "Stopping $($ComponentConfig.DisplayName)..." $service = Get-ServiceSafe -ServiceName $ComponentConfig.ServiceName if (-not $service) { Write-Error "Service '$($ComponentConfig.ServiceName)' not found" return $false } if ($service.Status -eq "Stopped") { Write-Success "Service is already stopped" return $true } try { # Stop service Stop-Service -Name $ComponentConfig.ServiceName -Force Write-Info "Service stop command issued" # Wait for service to stop $elapsed = 0 while ($service.Status -ne "Stopped" -and $elapsed -lt $Timeout) { Start-Sleep -Seconds 1 $service.Refresh() $elapsed++ if ($elapsed % 5 -eq 0) { Write-Info "Waiting for service to stop... ($elapsed/$Timeout)" } } if ($service.Status -eq "Stopped") { Write-Success "Service stopped successfully" return $true } else { Write-Error "Service did not stop within timeout (Status: $($service.Status))" Write-Warning "You may need to force kill the process" return $false } } catch { Write-Error "Failed to stop service: $_" return $false } } function Restart-ServiceComponent { param( [string]$ComponentName, [hashtable]$ComponentConfig ) Write-Step "Restarting $($ComponentConfig.DisplayName)..." # Stop service $stopResult = Stop-ServiceComponent -ComponentName $ComponentName -ComponentConfig $ComponentConfig if (-not $stopResult) { Write-Error "Failed to stop service, aborting restart" return $false } # Wait a moment Start-Sleep -Seconds 2 # Start service $startResult = Start-ServiceComponent -ComponentName $ComponentName -ComponentConfig $ComponentConfig if ($startResult) { Write-Success "Service restarted successfully" return $true } else { Write-Error "Failed to start service after stop" return $false } } function Get-ServiceStatus { param( [string]$ComponentName, [hashtable]$ComponentConfig ) $service = Get-ServiceSafe -ServiceName $ComponentConfig.ServiceName $statusInfo = @{ Component = $ComponentConfig.DisplayName ServiceName = $ComponentConfig.ServiceName Status = "Not Installed" StartType = "N/A" Health = "Unknown" HealthDetails = $null } if ($service) { $statusInfo.Status = $service.Status $statusInfo.StartType = $service.StartType if ($service.Status -eq "Running") { # Test health endpoint $health = Test-HealthEndpoint -Url $ComponentConfig.HealthUrl -TimeoutSec $ComponentConfig.HealthTimeout $statusInfo.Health = $health.Status $statusInfo.HealthDetails = $health.Details } } return $statusInfo } function Show-ServiceStatus { param( [string]$ComponentName, [hashtable]$ComponentConfig ) $status = Get-ServiceStatus -ComponentName $ComponentName -ComponentConfig $ComponentConfig Write-Host "`n$($status.Component):" -ForegroundColor Cyan Write-Host " Service Name: $($status.ServiceName)" -ForegroundColor Gray # Color-code status $statusColor = switch ($status.Status) { "Running" { "Green" } "Stopped" { "Yellow" } "Not Installed" { "Red" } default { "Gray" } } Write-Host " Status: $($status.Status)" -ForegroundColor $statusColor Write-Host " Start Type: $($status.StartType)" -ForegroundColor Gray if ($status.Status -eq "Running") { $healthColor = switch ($status.Health) { "healthy" { "Green" } "ok" { "Green" } "unhealthy" { "Red" } "unreachable" { "Yellow" } default { "Gray" } } Write-Host " Health: $($status.Health)" -ForegroundColor $healthColor # Show additional details for Telegram Bot if ($ComponentName -eq "TelegramBot" -and $status.HealthDetails) { if ($status.HealthDetails.database) { Write-Host " Database: $($status.HealthDetails.database.status)" -ForegroundColor Gray } if ($status.HealthDetails.telegram) { Write-Host " Telegram: $($status.HealthDetails.telegram.status)" -ForegroundColor Gray } } } } # ============================================================================= # MAIN EXECUTION # ============================================================================= function Main { $banner = @" ==================================================================== ROA2WEB - Service Management (v2.0) Action: $Action | Component: $Component ==================================================================== "@ Write-Host $banner -ForegroundColor Cyan # Check administrator privileges if (-not (Test-Administrator)) { Write-Error "This script requires Administrator privileges" Write-Host "`nPlease run PowerShell as Administrator and try again." -ForegroundColor Yellow exit 1 } # Determine which components to manage $components = @() switch ($Component) { "All" { $components = @( @{ Name = "Backend"; Config = $config.Backend }, @{ Name = "TelegramBot"; Config = $config.TelegramBot } ) } "Backend" { $components = @( @{ Name = "Backend"; Config = $config.Backend } ) } "TelegramBot" { $components = @( @{ Name = "TelegramBot"; Config = $config.TelegramBot } ) } } # Execute action $allSuccess = $true switch ($Action) { "Start" { foreach ($comp in $components) { $result = Start-ServiceComponent -ComponentName $comp.Name -ComponentConfig $comp.Config if (-not $result) { $allSuccess = $false } } } "Stop" { foreach ($comp in $components) { $result = Stop-ServiceComponent -ComponentName $comp.Name -ComponentConfig $comp.Config if (-not $result) { $allSuccess = $false } } } "Restart" { foreach ($comp in $components) { $result = Restart-ServiceComponent -ComponentName $comp.Name -ComponentConfig $comp.Config if (-not $result) { $allSuccess = $false } } } "Status" { foreach ($comp in $components) { Show-ServiceStatus -ComponentName $comp.Name -ComponentConfig $comp.Config } Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan exit 0 } } # Show final status Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan if ($allSuccess) { Write-Host " OPERATION COMPLETED SUCCESSFULLY" -ForegroundColor Green Write-Host ("=" * 70) -ForegroundColor Cyan exit 0 } else { Write-Host " OPERATION COMPLETED WITH ERRORS" -ForegroundColor Red Write-Host ("=" * 70) -ForegroundColor Cyan Write-Host "`nSome services may require manual intervention." -ForegroundColor Yellow Write-Host "Check service logs for details:" -ForegroundColor Yellow Write-Host " Backend: C:\inetpub\wwwroot\roa2web\logs\" -ForegroundColor Gray Write-Host " Telegram Bot: C:\inetpub\wwwroot\roa2web\telegram-bot\logs\" -ForegroundColor Gray exit 1 } } # Run main Main