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>
852 lines
30 KiB
PowerShell
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
|