Files
roa2web-service-auto/deployment/windows/scripts/Publish-And-Deploy.ps1
Marius Mutu c5e051ad80 feat: Migrate to ultrathin monolith architecture
Consolidate 3 separate applications (reports-app, data-entry-app, telegram-bot) into a unified
architecture with single backend and frontend:

Backend Changes:
- Unified FastAPI backend at backend/ with modular structure
- Modules: reports, data_entry, telegram in backend/modules/
- Centralized config.py and main.py with all routers registered
- Single worker mode (--workers 1) for Telegram bot compatibility
- Shared Oracle connection pool and JWT authentication
- Unified requirements.txt and environment configuration

Frontend Changes:
- Single Vue.js SPA with module-based routing
- Unified frontend at src/ with modules in src/modules/{reports,data-entry}/
- Shared components and stores in src/shared/
- Error boundaries for module isolation
- Dual API proxy in Vite for module communication

Infrastructure:
- New unified startup scripts: start-prod.sh, start-test.sh, start-backend.sh
- Environment templates: .env.dev.example, .env.test.example, .env.prod.example
- Updated deployment scripts for Windows IIS
- Simplified SSH tunnel management

Documentation:
- Comprehensive CLAUDE.md with architecture overview
- Module-specific docs in docs/{data-entry,telegram}/
- Architecture decision records in docs/ARCHITECTURE-DECISIONS.md
- Deployment guides consolidated in deployment/windows/docs/

This migration reduces complexity, improves maintainability, and enables easier
deployment while maintaining all existing functionality.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 23:48:14 +02:00

852 lines
30 KiB
PowerShell

<#
.SYNOPSIS
ROA2WEB - Interactive Build & Publish Console
.DESCRIPTION
Build deployment packages locally and transfer to Windows Server:
- Build locally (Frontend, Backend, Telegram Bot, or All)
- Transfer via Windows Share (LAN) or SSH/SCP (remote)
- Auto-detect best transfer method
- Interactive menu or command-line operation
- Server auto-deploys when new package detected
.PARAMETER NonInteractive
Run in non-interactive mode with specific action
.PARAMETER Action
Action to perform in non-interactive mode:
- Build: Build and publish package
- TestConnections: Test Windows Share and SSH connectivity
- Configure: Edit configuration (interactive only)
- ViewConfig: Display current configuration
.PARAMETER Component
Component to build (All, Frontend, Backend)
.PARAMETER TransferMethod
Transfer method (Auto, WindowsShare, SSH)
.PARAMETER ConfigFile
Path to configuration file (default: deploy-config.json)
.EXAMPLE
.\Publish-And-Deploy.ps1
Launch interactive menu
.EXAMPLE
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component All
Build all components and publish (auto-detect transfer method)
.EXAMPLE
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component Frontend -TransferMethod SSH
Build frontend and publish via SSH
.EXAMPLE
.\Publish-And-Deploy.ps1 -NonInteractive -Action Build -Component DataEntryApp
Build Data Entry App (frontend + backend) and publish
.NOTES
Author: ROA2WEB Team
Version: 1.1 (Added Data Entry App support)
Requires: PowerShell 5.1+, SSH key configured for remote access
#>
[CmdletBinding()]
param(
[switch]$NonInteractive,
[ValidateSet("Build", "TestConnections", "ViewConfig")]
[string]$Action = "",
[ValidateSet("All", "Frontend", "Backend")]
[string]$Component = "",
[ValidateSet("Auto", "WindowsShare", "SSH")]
[string]$TransferMethod = "",
[string]$ConfigFile = ""
)
$ErrorActionPreference = "Stop"
# =============================================================================
# CONFIGURATION LOADING
# =============================================================================
$script:ConfigFilePath = if ($ConfigFile) {
$ConfigFile
} else {
Join-Path $PSScriptRoot "deploy-config.json"
}
function Load-Configuration {
Write-Host "`nLoading configuration from: $script:ConfigFilePath" -ForegroundColor Gray
if (-not (Test-Path $script:ConfigFilePath)) {
Write-Host "[ERROR] Configuration file not found: $script:ConfigFilePath" -ForegroundColor Red
Write-Host "Creating default configuration..." -ForegroundColor Yellow
$defaultConfig = @{
server = @{
host = "10.0.20.36"
sshPort = 22122
sshUser = "Administrator"
sshKeyPath = "~/.ssh/roa2web_deploy"
windowsShare = "\\10.0.20.36\ROA2WEB-Deploy"
deployPath = "C:\ROA2WEB-Deploy"
}
build = @{
defaultComponent = "All"
outputPath = "../deploy-package"
}
transfer = @{
defaultMethod = "Auto"
scpRemotePath = "/cygdrive/c/ROA2WEB-Deploy"
retryAttempts = 3
timeout = 300
}
deployment = @{
autoDeployEnabled = $true
checkIntervalMinutes = 5
}
}
$defaultConfig | ConvertTo-Json -Depth 10 | Set-Content -Path $script:ConfigFilePath
Write-Host "Default configuration created. Please review and edit as needed." -ForegroundColor Green
}
try {
$script:Config = Get-Content -Path $script:ConfigFilePath -Raw | ConvertFrom-Json
Write-Host "[OK] Configuration loaded successfully" -ForegroundColor Green
return $true
} catch {
Write-Host "[ERROR] Failed to load configuration: $_" -ForegroundColor Red
return $false
}
}
function Save-Configuration {
try {
$script:Config | ConvertTo-Json -Depth 10 | Set-Content -Path $script:ConfigFilePath
Write-Host "[OK] Configuration saved successfully" -ForegroundColor Green
return $true
} catch {
Write-Host "[ERROR] Failed to save configuration: $_" -ForegroundColor Red
return $false
}
}
# =============================================================================
# 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 Resolve-FullPath {
param([string]$Path)
# Resolve relative paths from the parent of the scripts directory
# This matches the resolution logic in Build-ROA2WEB.ps1
$scriptDir = Split-Path -Parent $PSScriptRoot
$fullPath = Join-Path $scriptDir $Path
$fullPath = [System.IO.Path]::GetFullPath($fullPath)
return $fullPath
}
function Wait-ForKeyPress {
Write-Host "`nPress any key to continue..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}
# =============================================================================
# CONNECTION TESTING
# =============================================================================
function Test-WindowsShare {
Write-Step "Testing Windows Share access..."
$sharePath = $script:Config.server.windowsShare
Write-Info "Share: $sharePath"
try {
if (Test-Path $sharePath) {
Write-Success "Windows Share is accessible"
# Try to write test file
$testFile = Join-Path $sharePath ".test-$(Get-Date -Format 'yyyyMMddHHmmss')"
"test" | Out-File -FilePath $testFile -Force
if (Test-Path $testFile) {
Remove-Item -Path $testFile -Force
Write-Success "Write permissions verified"
return $true
} else {
Write-Warning "Share is readable but write test failed"
return $false
}
} else {
Write-Error "Windows Share is not accessible"
Write-Info "Ensure network share is configured and accessible"
return $false
}
} catch {
Write-Error "Failed to access Windows Share: $_"
return $false
}
}
function Test-SSHConnection {
Write-Step "Testing SSH connection..."
$serverHost = $script:Config.server.host
$port = $script:Config.server.sshPort
$user = $script:Config.server.sshUser
$keyPath = $script:Config.server.sshKeyPath
Write-Info "Host: ${serverHost}:${port}"
Write-Info "User: $user"
Write-Info "Key: $keyPath"
# Expand tilde in key path (support both Unix / and Windows \ styles)
if ($keyPath -like "~/*" -or $keyPath -like "~\*") {
$keyPath = $keyPath -replace "^~", $env:USERPROFILE
}
if (-not (Test-Path $keyPath)) {
Write-Error "SSH key not found: $keyPath"
return $false
}
try {
# Test SSH connection
$sshTest = ssh -i "$keyPath" -p $port -o ConnectTimeout=10 -o StrictHostKeyChecking=no "${user}@${serverHost}" "echo 'Connection successful'" 2>&1
if ($LASTEXITCODE -eq 0 -and $sshTest -like "*Connection successful*") {
Write-Success "SSH connection established"
return $true
} else {
Write-Error "SSH connection failed"
Write-Info "Output: $sshTest"
return $false
}
} catch {
Write-Error "Failed to test SSH connection: $_"
return $false
}
}
function Test-AllConnections {
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Connection Testing" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
$shareOk = Test-WindowsShare
$sshOk = Test-SSHConnection
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Test Results" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host "`n Windows Share: " -NoNewline
if ($shareOk) {
Write-Host "[OK] Available" -ForegroundColor Green
} else {
Write-Host "[X] Not Available" -ForegroundColor Red
}
Write-Host " SSH Connection: " -NoNewline
if ($sshOk) {
Write-Host "[OK] Available" -ForegroundColor Green
} else {
Write-Host "[X] Not Available" -ForegroundColor Red
}
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
return @{
WindowsShare = $shareOk
SSH = $sshOk
}
}
# =============================================================================
# BUILD & TRANSFER FUNCTIONS
# =============================================================================
function Invoke-Build {
param(
[Parameter(Mandatory)]
[ValidateSet("All", "Frontend", "Backend")]
[string]$Component
)
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Building Component: $Component" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
$buildScript = Join-Path $PSScriptRoot "Build-ROA2WEB.ps1"
if (-not (Test-Path $buildScript)) {
Write-Error "Build script not found: $buildScript"
return $false
}
try {
$outputPath = $script:Config.build.outputPath
# Resolve the output path using the same logic as Build-ROA2WEB.ps1
$resolvedOutputPath = if ([System.IO.Path]::IsPathRooted($outputPath)) {
$outputPath
} else {
Resolve-FullPath -Path $outputPath
}
Write-Step "Building deployment package..."
& $buildScript -Component $Component -OutputPath $outputPath
if ($LASTEXITCODE -ne 0) {
throw "Build script returned error code: $LASTEXITCODE"
}
if (-not (Test-Path $resolvedOutputPath)) {
throw "Build output not found: $resolvedOutputPath"
}
Write-Success "Build completed successfully"
return $true
} catch {
Write-Error "Build failed: $_"
return $false
}
}
function Copy-ViaWindowsShare {
param([string]$SourcePath)
Write-Step "Transferring via Windows Share..."
$sharePath = $script:Config.server.windowsShare
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$deployFolder = "deploy-$timestamp"
$destPath = Join-Path $sharePath $deployFolder
try {
Write-Info "Destination: $destPath"
Copy-Item -Path $SourcePath -Destination $destPath -Recurse -Force
if (Test-Path $destPath) {
Write-Success "Files transferred successfully"
Write-Success "Server will auto-deploy within $($script:Config.deployment.checkIntervalMinutes) minutes"
return $true
} else {
Write-Error "Transfer failed - destination folder not found"
return $false
}
} catch {
Write-Error "Windows Share transfer failed: $_"
return $false
}
}
function Copy-ViaSSH {
param([string]$SourcePath)
Write-Step "Transferring via SSH/SCP..."
$serverHost = $script:Config.server.host
$port = $script:Config.server.sshPort
$user = $script:Config.server.sshUser
$keyPath = $script:Config.server.sshKeyPath
$remotePath = $script:Config.transfer.scpRemotePath
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$deployFolder = "deploy-$timestamp"
# Expand tilde in key path (support both Unix / and Windows \ styles)
if ($keyPath -like "~/*" -or $keyPath -like "~\*") {
$keyPath = $keyPath -replace "^~", $env:USERPROFILE
}
try {
Write-Info "Host: ${serverHost}:${port}"
Write-Info "Destination: ${remotePath}/${deployFolder}"
# Create remote directory
Write-Info "Creating remote directory..."
$createDir = ssh -i "$keyPath" -p $port "${user}@${serverHost}" "mkdir -p '${remotePath}/${deployFolder}'" 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Failed to create remote directory: $createDir"
}
# Transfer files via SCP
Write-Info "Transferring files (this may take a few minutes)..."
$scpResult = scp -i "$keyPath" -P $port -r "${SourcePath}/*" "${user}@${serverHost}:${remotePath}/${deployFolder}/" 2>&1
if ($LASTEXITCODE -ne 0) {
throw "SCP transfer failed: $scpResult"
}
Write-Success "Files transferred successfully"
Write-Success "Server will auto-deploy within $($script:Config.deployment.checkIntervalMinutes) minutes"
return $true
} catch {
Write-Error "SSH transfer failed: $_"
return $false
}
}
function Invoke-Transfer {
param(
[Parameter(Mandatory)]
[ValidateSet("Auto", "WindowsShare", "SSH")]
[string]$Method,
[Parameter(Mandatory)]
[string]$SourcePath
)
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Transfer Method: $Method" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
if ($Method -eq "Auto") {
Write-Info "Auto-detecting best transfer method..."
# Try Windows Share first (faster for LAN)
Write-Info "Checking Windows Share availability..."
if (Test-Path $script:Config.server.windowsShare) {
Write-Success "Windows Share accessible - using Windows Share"
return Copy-ViaWindowsShare -SourcePath $SourcePath
} else {
Write-Warning "Windows Share not accessible - falling back to SSH"
return Copy-ViaSSH -SourcePath $SourcePath
}
} elseif ($Method -eq "WindowsShare") {
return Copy-ViaWindowsShare -SourcePath $SourcePath
} else {
return Copy-ViaSSH -SourcePath $SourcePath
}
}
# =============================================================================
# CONFIGURATION MANAGEMENT
# =============================================================================
function Show-CurrentConfig {
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Current Configuration" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " Server Settings:" -ForegroundColor Yellow
Write-Host " Host: $($script:Config.server.host)" -ForegroundColor Gray
Write-Host " SSH Port: $($script:Config.server.sshPort)" -ForegroundColor Gray
Write-Host " SSH User: $($script:Config.server.sshUser)" -ForegroundColor Gray
Write-Host " SSH Key: $($script:Config.server.sshKeyPath)" -ForegroundColor Gray
Write-Host " Windows Share: $($script:Config.server.windowsShare)" -ForegroundColor Gray
Write-Host " Deploy Path: $($script:Config.server.deployPath)" -ForegroundColor Gray
Write-Host ""
Write-Host " Build Settings:" -ForegroundColor Yellow
Write-Host " Default Component: $($script:Config.build.defaultComponent)" -ForegroundColor Gray
Write-Host " Output Path: $($script:Config.build.outputPath)" -ForegroundColor Gray
Write-Host ""
Write-Host " Transfer Settings:" -ForegroundColor Yellow
Write-Host " Default Method: $($script:Config.transfer.defaultMethod)" -ForegroundColor Gray
Write-Host " SCP Remote Path: $($script:Config.transfer.scpRemotePath)" -ForegroundColor Gray
Write-Host " Retry Attempts: $($script:Config.transfer.retryAttempts)" -ForegroundColor Gray
Write-Host " Timeout (sec): $($script:Config.transfer.timeout)" -ForegroundColor Gray
Write-Host ""
Write-Host " Deployment Settings:" -ForegroundColor Yellow
Write-Host " Auto-Deploy: $($script:Config.deployment.autoDeployEnabled)" -ForegroundColor Gray
Write-Host " Check Interval: $($script:Config.deployment.checkIntervalMinutes) minutes" -ForegroundColor Gray
Write-Host ""
Write-Host " Config File: $script:ConfigFilePath" -ForegroundColor Gray
Write-Host ("=" * 70) -ForegroundColor Cyan
}
function Edit-Configuration {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Configuration Editor" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " Current Settings:" -ForegroundColor Yellow
Write-Host " Server Host: $($script:Config.server.host)" -ForegroundColor Gray
Write-Host " SSH Port: $($script:Config.server.sshPort)" -ForegroundColor Gray
Write-Host " SSH User: $($script:Config.server.sshUser)" -ForegroundColor Gray
Write-Host " SSH Key: $($script:Config.server.sshKeyPath)" -ForegroundColor Gray
Write-Host " Windows Share: $($script:Config.server.windowsShare)" -ForegroundColor Gray
Write-Host " Server Deploy Path: $($script:Config.server.deployPath)" -ForegroundColor Gray
Write-Host ""
Write-Host " [1] Edit SSH Connection Settings" -ForegroundColor White
Write-Host " [2] Edit Windows Share Path" -ForegroundColor White
Write-Host " [3] Edit Server Deploy Path" -ForegroundColor White
Write-Host " [4] Edit Build Settings" -ForegroundColor White
Write-Host " [5] Edit Transfer Settings" -ForegroundColor White
Write-Host " [S] Save Configuration" -ForegroundColor Green
Write-Host " [R] Reset to Defaults" -ForegroundColor Yellow
Write-Host " [B] Back (discard changes)" -ForegroundColor Red
Write-Host ""
Write-Host ("=" * 70) -ForegroundColor Cyan
do {
Write-Host "`nYour choice: " -ForegroundColor Yellow -NoNewline
$choice = Read-Host
switch ($choice.ToUpper()) {
"1" {
Write-Host "`nEdit SSH Connection Settings:" -ForegroundColor Yellow
Write-Host "Server Host [current: $($script:Config.server.host)]: " -NoNewline
$newHost = Read-Host
if ($newHost) { $script:Config.server.host = $newHost }
Write-Host "SSH Port [current: $($script:Config.server.sshPort)]: " -NoNewline
$newPort = Read-Host
if ($newPort) { $script:Config.server.sshPort = [int]$newPort }
Write-Host "SSH User [current: $($script:Config.server.sshUser)]: " -NoNewline
$newUser = Read-Host
if ($newUser) { $script:Config.server.sshUser = $newUser }
Write-Host "SSH Key Path [current: $($script:Config.server.sshKeyPath)]: " -NoNewline
$newKey = Read-Host
if ($newKey) { $script:Config.server.sshKeyPath = $newKey }
Write-Success "SSH settings updated (not saved yet)"
return Edit-Configuration
}
"2" {
Write-Host "`nEdit Windows Share Path:" -ForegroundColor Yellow
Write-Host "Windows Share [current: $($script:Config.server.windowsShare)]: " -NoNewline
$newShare = Read-Host
if ($newShare) { $script:Config.server.windowsShare = $newShare }
Write-Success "Windows Share updated (not saved yet)"
return Edit-Configuration
}
"3" {
Write-Host "`nEdit Server Deploy Path:" -ForegroundColor Yellow
Write-Host "Deploy Path [current: $($script:Config.server.deployPath)]: " -NoNewline
$newPath = Read-Host
if ($newPath) { $script:Config.server.deployPath = $newPath }
Write-Success "Deploy path updated (not saved yet)"
return Edit-Configuration
}
"4" {
Write-Host "`nEdit Build Settings:" -ForegroundColor Yellow
Write-Host "Default Component (All/Frontend/Backend)" -ForegroundColor Gray
Write-Host "[current: $($script:Config.build.defaultComponent)]: " -NoNewline
$newComp = Read-Host
if ($newComp) { $script:Config.build.defaultComponent = $newComp }
Write-Host "Output Path [current: $($script:Config.build.outputPath)]: " -NoNewline
$newOut = Read-Host
if ($newOut) { $script:Config.build.outputPath = $newOut }
Write-Success "Build settings updated (not saved yet)"
return Edit-Configuration
}
"5" {
Write-Host "`nEdit Transfer Settings:" -ForegroundColor Yellow
Write-Host "Default Method (Auto/WindowsShare/SSH) [current: $($script:Config.transfer.defaultMethod)]: " -NoNewline
$newMethod = Read-Host
if ($newMethod) { $script:Config.transfer.defaultMethod = $newMethod }
Write-Host "SCP Remote Path [current: $($script:Config.transfer.scpRemotePath)]: " -NoNewline
$newScp = Read-Host
if ($newScp) { $script:Config.transfer.scpRemotePath = $newScp }
Write-Success "Transfer settings updated (not saved yet)"
return Edit-Configuration
}
"S" {
if (Save-Configuration) {
Write-Host "`nConfiguration saved successfully!" -ForegroundColor Green
Wait-ForKeyPress
return
}
}
"R" {
Write-Host "`nAre you sure you want to reset to defaults? [Y/N]: " -ForegroundColor Yellow -NoNewline
$confirm = Read-Host
if ($confirm.ToUpper() -eq "Y") {
Load-Configuration
Write-Success "Configuration reset to defaults"
return Edit-Configuration
}
}
"B" {
Write-Host "`nChanges discarded" -ForegroundColor Yellow
Load-Configuration # Reload from file
return
}
default {
Write-Host "Invalid choice. Please select 1-5, S, R, or B." -ForegroundColor Red
}
}
} while ($true)
}
# =============================================================================
# MENU FUNCTIONS
# =============================================================================
function Show-MainMenu {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " ROA2WEB - Interactive Build & Publish Console" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " Main Menu:" -ForegroundColor Yellow
Write-Host ""
Write-Host " [1] Build & Publish Package" -ForegroundColor White
Write-Host " (Build locally and transfer to server)" -ForegroundColor Gray
Write-Host ""
Write-Host " [2] Test Connections" -ForegroundColor White
Write-Host " (Verify Windows Share and SSH access)" -ForegroundColor Gray
Write-Host ""
Write-Host " [3] Configure Settings" -ForegroundColor White
Write-Host " (Edit deployment configuration)" -ForegroundColor Gray
Write-Host ""
Write-Host " [4] View Current Configuration" -ForegroundColor White
Write-Host " (Display active settings)" -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 "BuildPublish" }
"2" { return "TestConnections" }
"3" { return "Configure" }
"4" { return "ViewConfig" }
"Q" { return "Quit" }
default {
Write-Host "Invalid choice. Please select 1-4 or Q." -ForegroundColor Red
}
}
} while ($true)
}
function Show-ComponentMenu {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Select Component to Build" -ForegroundColor Cyan
Write-Host " Ultrathin Monolith Architecture" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " [1] All Components" -ForegroundColor White
Write-Host " (Unified Backend + Frontend)" -ForegroundColor Gray
Write-Host " Includes: Reports, Data Entry, and Telegram modules" -ForegroundColor DarkGray
Write-Host ""
Write-Host " [2] Frontend + Backend" -ForegroundColor White
Write-Host " (Vue.js SPA build + Unified FastAPI backend)" -ForegroundColor Gray
Write-Host ""
Write-Host " [3] Backend Only" -ForegroundColor White
Write-Host " (Unified FastAPI backend + shared modules)" -ForegroundColor Gray
Write-Host " All modules included - control via MODULE_*_ENABLED flags" -ForegroundColor DarkGray
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 "All" }
"2" { return "Frontend" }
"3" { return "Backend" }
"B" { return "Back" }
default {
Write-Host "Invalid choice. Please select 1-3 or B." -ForegroundColor Red
}
}
} while ($true)
}
function Show-TransferMenu {
Clear-Host
Write-Host "`n" + ("=" * 70) -ForegroundColor Cyan
Write-Host " Select Transfer Method" -ForegroundColor Cyan
Write-Host ("=" * 70) -ForegroundColor Cyan
Write-Host ""
Write-Host " [1] Auto-detect (Recommended)" -ForegroundColor White
Write-Host " Try Windows Share first, fallback to SSH" -ForegroundColor Gray
Write-Host ""
Write-Host " [2] Windows Share (LAN)" -ForegroundColor White
Write-Host " Fast transfer via network share ($($script:Config.server.windowsShare))" -ForegroundColor Gray
Write-Host ""
Write-Host " [3] SSH/SCP (Remote)" -ForegroundColor White
Write-Host " Secure transfer via SSH (port $($script:Config.server.sshPort))" -ForegroundColor Gray
Write-Host ""
Write-Host " [B] Back" -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 "Auto" }
"2" { return "WindowsShare" }
"3" { return "SSH" }
"B" { return "Back" }
default {
Write-Host "Invalid choice. Please select 1-3 or B." -ForegroundColor Red
}
}
} while ($true)
}
# =============================================================================
# ACTION HANDLERS
# =============================================================================
function Invoke-BuildAndPublish {
param(
[string]$Component = "",
[string]$TransferMethod = ""
)
# Get component if not provided
if (-not $Component) {
$Component = Show-ComponentMenu
if ($Component -eq "Back") { return }
}
# Get transfer method if not provided
if (-not $TransferMethod) {
$TransferMethod = Show-TransferMenu
if ($TransferMethod -eq "Back") { return }
}
# Build
$buildSuccess = Invoke-Build -Component $Component
if (-not $buildSuccess) {
Write-Host "`n[BUILD FAILED] Cannot proceed with transfer" -ForegroundColor Red
if (-not $NonInteractive) { Wait-ForKeyPress }
return
}
# Transfer
$outputPath = $script:Config.build.outputPath
# Resolve the output path for transfer
$resolvedOutputPath = if ([System.IO.Path]::IsPathRooted($outputPath)) {
$outputPath
} else {
Resolve-FullPath -Path $outputPath
}
$transferSuccess = Invoke-Transfer -Method $TransferMethod -SourcePath $resolvedOutputPath
if ($transferSuccess) {
Write-Host "`n" + ("=" * 70) -ForegroundColor Green
Write-Host " BUILD & PUBLISH COMPLETED SUCCESSFULLY" -ForegroundColor Green
Write-Host ("=" * 70) -ForegroundColor Green
Write-Host "`nPackage published to server: $($script:Config.server.host)" -ForegroundColor Gray
Write-Host "Server will auto-deploy within $($script:Config.deployment.checkIntervalMinutes) minutes" -ForegroundColor Gray
Write-Host ("=" * 70) -ForegroundColor Green
} else {
Write-Host "`n[TRANSFER FAILED] Package built but transfer failed" -ForegroundColor Red
}
if (-not $NonInteractive) { Wait-ForKeyPress }
}
# =============================================================================
# MAIN EXECUTION FLOW
# =============================================================================
function Main {
# Load configuration
if (-not (Load-Configuration)) {
Write-Host "`nPress any key to exit..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
exit 1
}
# Non-interactive mode
if ($NonInteractive -and $Action) {
switch ($Action) {
"Build" {
$comp = if ($Component) { $Component } else { $script:Config.build.defaultComponent }
$method = if ($TransferMethod) { $TransferMethod } else { $script:Config.transfer.defaultMethod }
Invoke-BuildAndPublish -Component $comp -TransferMethod $method
}
"TestConnections" {
Test-AllConnections | Out-Null
}
"ViewConfig" {
Show-CurrentConfig
}
}
return
}
# Interactive mode - main loop
do {
$mainChoice = Show-MainMenu
switch ($mainChoice) {
"BuildPublish" {
Invoke-BuildAndPublish
}
"TestConnections" {
Test-AllConnections | Out-Null
Wait-ForKeyPress
}
"Configure" {
Edit-Configuration
}
"ViewConfig" {
Show-CurrentConfig
Wait-ForKeyPress
}
"Quit" {
Write-Host "`nGoodbye!`n" -ForegroundColor Cyan
return
}
}
} while ($true)
}
# Run main
Main