Files
roa2web-service-auto/deployment/windows/scripts/Manage-ROA2WEB.ps1
Marius Mutu 1b4e2e1f40 Refactor Windows deployment scripts: unify build and management tools
Major improvements to Windows deployment workflow:

 New Unified Scripts:
- Build-ROA2WEB.ps1: Single build script for all components (Frontend, Backend, TelegramBot)
  * Supports selective builds: -Component All|Frontend|Backend|TelegramBot
  * Replaces Build-Frontend.ps1 and Build-TelegramBot.ps1
  * Consistent output structure and better validation

- Manage-ROA2WEB.ps1: Unified service management
  * Single entry point for Start, Stop, Restart, Status actions
  * Supports -Component All|Backend|TelegramBot
  * Health checks and detailed status reporting
  * Replaces 6 separate Start/Stop/Restart scripts

🗑️ Removed Deprecated Scripts:
- Start-ROA2WEB.ps1, Stop-ROA2WEB.ps1, Restart-ROA2WEB.ps1
- Start-TelegramBot.ps1, Stop-TelegramBot.ps1, Restart-TelegramBot.ps1
(6 scripts → 1 unified Manage-ROA2WEB.ps1)

⚠️ Marked as DEPRECATED (backward compatibility):
- Build-Frontend.ps1 (use Build-ROA2WEB.ps1 -Component Frontend)
- Build-TelegramBot.ps1 (use Build-ROA2WEB.ps1 -Component TelegramBot)

🧹 Cleanup & Organization:
- Updated .gitignore: deploy-package/ and build artifacts excluded
- Removed deploy-package/ from git tracking (generated artifacts)
- Added DEPLOY_PACKAGE.md with generation instructions
- Created comprehensive scripts/README.md documentation

📝 Documentation Updates:
- Updated CLAUDE.md Windows deployment section
- Added complete script reference guide
- Migration guide from old scripts to new unified system

📊 Impact:
- 18 scripts → 11 scripts (39% reduction)
- ~10,000 LOC → ~6,500 LOC (35% reduction)
- Zero duplicate code
- Cleaner git repository (no build artifacts)
- Unified, consistent API across all operations

Migration:
./Build-Frontend.ps1        → ./Build-ROA2WEB.ps1 -Component Frontend
./Build-TelegramBot.ps1      → ./Build-ROA2WEB.ps1 -Component TelegramBot
./Start-ROA2WEB.ps1          → ./Manage-ROA2WEB.ps1 -Action Start -Component Backend
./Restart-TelegramBot.ps1    → ./Manage-ROA2WEB.ps1 -Action Restart -Component TelegramBot

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 22:50:03 +02:00

499 lines
14 KiB
PowerShell

<#
.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