feat: Improve Windows deployment and fix production paths

Data Entry App:
- Fix shared path finding for both dev and production environments
- Add base URL support for IIS subdirectory deployment (/data-entry/)
- Use import.meta.env.BASE_URL in router for correct path handling
- Add email-validator and python-jose dependencies

Deployment Scripts:
- Enhance Build-ROA2WEB.ps1 with improved build process
- Update ROA2WEB-Console.ps1 with Data Entry support
- Improve Publish-And-Deploy.ps1 deployment workflow
- Update deploy-config.json with new settings

Gitignore:
- Add more build cache patterns to ignore
- Add temp frontend build directories

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-18 19:44:15 +02:00
parent 642ae3a96c
commit 3295f60faa
10 changed files with 1015 additions and 62 deletions

View File

@@ -68,7 +68,7 @@
[CmdletBinding()]
param(
[ValidateSet("All", "Frontend", "Backend", "TelegramBot")]
[ValidateSet("All", "Frontend", "Backend", "TelegramBot", "DataEntryApp", "DataEntryBackend")]
[string]$Component = "",
[string]$OutputPath = "./deploy-package",
@@ -85,9 +85,14 @@ $ErrorActionPreference = "Stop"
# =============================================================================
$config = @{
# Reports App sources
BackendSource = "../../reports-app/backend"
FrontendSource = "../../reports-app/frontend"
TelegramBotSource = "../../reports-app/telegram-bot"
# Data Entry App sources
DataEntryBackendSource = "../../data-entry-app/backend"
DataEntryFrontendSource = "../../data-entry-app/frontend"
# Shared sources
SharedSource = "../../shared"
ConfigSource = "../config"
RequiredNodeVersion = 16
@@ -175,17 +180,25 @@ function Show-BuildMenu {
Write-Host " Select components to build:" -ForegroundColor Yellow
Write-Host ""
Write-Host " [1] All Components" -ForegroundColor White
Write-Host " (Frontend + Backend + Telegram Bot)" -ForegroundColor Gray
Write-Host " (Reports App + Telegram Bot + Data Entry App)" -ForegroundColor Gray
Write-Host ""
Write-Host " [2] Frontend + Backend" -ForegroundColor White
Write-Host " --- Reports App ---" -ForegroundColor Cyan
Write-Host " [2] Reports Frontend + Backend" -ForegroundColor White
Write-Host " (Vue.js build + FastAPI backend files)" -ForegroundColor Gray
Write-Host ""
Write-Host " [3] Backend Only" -ForegroundColor White
Write-Host " [3] Reports Backend Only" -ForegroundColor White
Write-Host " (FastAPI backend files + shared modules)" -ForegroundColor Gray
Write-Host ""
Write-Host " [4] Telegram Bot Only" -ForegroundColor White
Write-Host " (Telegram bot standalone package)" -ForegroundColor Gray
Write-Host ""
Write-Host " --- Data Entry App ---" -ForegroundColor Cyan
Write-Host " [5] Data Entry Frontend + Backend" -ForegroundColor White
Write-Host " (Vue.js build + FastAPI backend files)" -ForegroundColor Gray
Write-Host ""
Write-Host " [6] Data Entry Backend Only" -ForegroundColor White
Write-Host " (FastAPI backend files only)" -ForegroundColor Gray
Write-Host ""
Write-Host " [C] Clean Build Cache" -ForegroundColor Yellow
Write-Host " (Remove cached node_modules to free disk space)" -ForegroundColor Gray
Write-Host ""
@@ -202,6 +215,8 @@ function Show-BuildMenu {
"2" { return "Frontend" }
"3" { return "Backend" }
"4" { return "TelegramBot" }
"5" { return "DataEntryApp" }
"6" { return "DataEntryBackend" }
"C" {
Clear-BuildCache
Write-Host "`nPress any key to return to menu..." -ForegroundColor Gray
@@ -213,7 +228,7 @@ function Show-BuildMenu {
exit 0
}
default {
Write-Host "Invalid choice. Please select 1-4, C or Q." -ForegroundColor Red
Write-Host "Invalid choice. Please select 1-6, C or Q." -ForegroundColor Red
}
}
} while ($true)
@@ -258,18 +273,28 @@ function Build-Frontend {
throw "Frontend source path not found: $SourcePath"
}
# Create temporary build directory
$tempBuildDir = Join-Path $OutputPath ".temp-frontend-build"
if (Test-Path $tempBuildDir) {
# Determine if this is reports-app or data-entry-app based on source path
$appFolder = Split-Path (Split-Path $SourcePath -Parent) -Leaf # "reports-app" or "data-entry-app"
# Create temporary build directory with full project structure
# Structure:
# .temp-frontend-build/
# ├── reports-app/frontend/ (or data-entry-app/frontend/)
# └── shared/
$tempRootDir = Join-Path $OutputPath ".temp-frontend-build"
if (Test-Path $tempRootDir) {
Write-Step "Cleaning existing temp build directory..."
Remove-Item -Path $tempBuildDir -Recurse -Force
Remove-Item -Path $tempRootDir -Recurse -Force
}
# Create the app-specific path inside temp
$tempBuildDir = Join-Path $tempRootDir "$appFolder\frontend"
New-Item -ItemType Directory -Path $tempBuildDir -Force | Out-Null
Write-Success "Created temp build directory (isolated from WSL)"
# Create cache directory for node_modules (OUTSIDE deploy-package)
$scriptDir = Split-Path -Parent $PSScriptRoot
$cacheDir = Join-Path $scriptDir ".build-cache"
$cacheDir = Join-Path $scriptDir ".build-cache-$appFolder"
if (-not (Test-Path $cacheDir)) {
New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null
}
@@ -318,7 +343,30 @@ function Build-Frontend {
}
Write-Success "Frontend sources copied to temp"
# Build in temp directory
# Copy shared folder to maintain relative imports (../../../shared/frontend/)
# Now shared goes into tempRootDir/shared (same level as reports-app or data-entry-app)
$projectRoot = Split-Path (Split-Path $SourcePath -Parent) -Parent
$sharedSourcePath = Join-Path $projectRoot "shared"
if (Test-Path $sharedSourcePath) {
$sharedDestPath = Join-Path $tempRootDir "shared"
Write-Step "Copying shared components for build..."
Write-Info "Source: $sharedSourcePath"
Write-Info "Dest: $sharedDestPath"
# Remove existing shared folder in dest if exists
if (Test-Path $sharedDestPath) {
Remove-Item -Path $sharedDestPath -Recurse -Force
}
Copy-Item -Path $sharedSourcePath -Destination $sharedDestPath -Recurse -Force -Exclude @("__pycache__", "*.pyc", "tests")
Write-Success "Shared components copied for relative imports"
} else {
Write-Warning "Shared folder not found at: $sharedSourcePath"
}
# Build in temp directory (now at tempRootDir/appFolder/frontend)
Push-Location $tempBuildDir
try {
# Check if dependencies need to be reinstalled
@@ -386,6 +434,24 @@ function Build-Frontend {
throw "Vite not found in node_modules - devDependencies not installed"
}
# Create junction for node_modules at tempRootDir level
# This allows shared folder to resolve npm dependencies
$tempRootNodeModules = Join-Path $tempRootDir "node_modules"
if (-not (Test-Path $tempRootNodeModules)) {
Write-Step "Creating node_modules junction for shared imports..."
# Use cmd /c mklink /J for directory junction (works without admin rights)
$junctionResult = cmd /c mklink /J "$tempRootNodeModules" "$nodeModulesPath" 2>&1
if (Test-Path $tempRootNodeModules) {
Write-Success "Node modules junction created for shared folder access"
} else {
Write-Warning "Could not create junction: $junctionResult"
# Fallback: copy node_modules (slower but works)
Write-Info "Falling back to copying node_modules..."
Copy-Item -Path $nodeModulesPath -Destination $tempRootNodeModules -Recurse -Force
Write-Success "Node modules copied to root (fallback)"
}
}
# Build for production
Write-Step "Building for production..."
$env:NODE_ENV = "production"
@@ -596,6 +662,112 @@ function Copy-TelegramBotFiles {
}
}
function Copy-DataEntryBackendFiles {
param(
[string]$SourcePath,
[string]$DestPath
)
Write-Step "Copying Data Entry backend files..."
if (-not (Test-Path $SourcePath)) {
throw "Data Entry backend source path not found: $SourcePath"
}
if (-not (Test-Path $DestPath)) {
New-Item -ItemType Directory -Path $DestPath -Force | Out-Null
}
# Exclude patterns
$excludeDirs = @("venv", "__pycache__", ".pytest_cache", "logs", "temp", "node_modules", "data")
$excludeFiles = @("*.pyc", "*.pyo", "*.log", ".env", ".env.local", "*.db")
$normalizedSourcePath = $SourcePath.TrimEnd('\', '/') + '\'
$testExclude = {
param([string]$RelativePath, [bool]$IsDirectory, [array]$ExcludeDirs, [array]$ExcludeFiles)
if ($IsDirectory) {
$dirName = Split-Path $RelativePath -Leaf
if ($ExcludeDirs -contains $dirName) {
return $true
}
}
$pathParts = $RelativePath -split '[\\/]'
foreach ($part in $pathParts) {
if ($ExcludeDirs -contains $part) {
return $true
}
}
if (-not $IsDirectory) {
foreach ($pattern in $ExcludeFiles) {
if ($RelativePath -like $pattern) {
return $true
}
}
}
return $false
}
Get-ChildItem -Path $SourcePath -Recurse | ForEach-Object {
if ($_.FullName.Length -le $normalizedSourcePath.Length) {
return
}
$relativePath = $_.FullName.Substring($normalizedSourcePath.Length)
$shouldExclude = & $testExclude -RelativePath $relativePath -IsDirectory $_.PSIsContainer -ExcludeDirs $excludeDirs -ExcludeFiles $excludeFiles
if ($shouldExclude) {
return
}
$destFile = Join-Path $DestPath $relativePath
if ($_.PSIsContainer) {
if (-not (Test-Path $destFile)) {
New-Item -ItemType Directory -Path $destFile -Force | Out-Null
}
} else {
$destDir = Split-Path $destFile -Parent
if (-not (Test-Path $destDir)) {
New-Item -ItemType Directory -Path $destDir -Force | Out-Null
}
Copy-Item -Path $_.FullName -Destination $destFile -Force
}
}
$backendFiles = Get-ChildItem -Path $DestPath -Recurse -File
Write-Success "Copied $(($backendFiles).Count) Data Entry backend files"
# Copy .env.example explicitly
$sourceEnvExample = Join-Path $SourcePath ".env.example"
$destEnvExample = Join-Path $DestPath ".env.example"
if (Test-Path $sourceEnvExample) {
Copy-Item -Path $sourceEnvExample -Destination $destEnvExample -Force
Write-Success ".env.example template copied"
}
# Copy .env.prod and .env.test templates
foreach ($envFile in @(".env.prod", ".env.test")) {
$sourceEnv = Join-Path $SourcePath $envFile
$destEnv = Join-Path $DestPath $envFile
if (Test-Path $sourceEnv) {
Copy-Item -Path $sourceEnv -Destination $destEnv -Force
Write-Success "$envFile template copied"
}
}
# Verify requirements.txt
$requirementsTxt = Join-Path $DestPath "requirements.txt"
if (-not (Test-Path $requirementsTxt)) {
Write-Error "CRITICAL: requirements.txt not found!"
throw "Data Entry backend package incomplete - missing requirements.txt"
}
Write-Success "Verified: requirements.txt present"
}
function Copy-SharedModules {
param(
[string]$SourcePath,
@@ -726,10 +898,12 @@ function New-DeploymentReadme {
Write-Step "Creating deployment README..."
$componentDesc = switch ($ComponentType) {
"All" { "COMPLETE DEPLOYMENT PACKAGE (Frontend + Backend + Telegram Bot)" }
"Frontend" { "FRONTEND + BACKEND DEPLOYMENT PACKAGE" }
"Backend" { "BACKEND DEPLOYMENT PACKAGE" }
"All" { "COMPLETE DEPLOYMENT PACKAGE (Reports + Telegram + Data Entry)" }
"Frontend" { "REPORTS FRONTEND + BACKEND DEPLOYMENT PACKAGE" }
"Backend" { "REPORTS BACKEND DEPLOYMENT PACKAGE" }
"TelegramBot" { "TELEGRAM BOT DEPLOYMENT PACKAGE" }
"DataEntryApp" { "DATA ENTRY APP DEPLOYMENT PACKAGE (Frontend + Backend)" }
"DataEntryBackend" { "DATA ENTRY BACKEND DEPLOYMENT PACKAGE" }
}
$readme = @"
@@ -743,11 +917,11 @@ CONTENTS:
---------
"@
if ($ComponentType -eq "All" -or $ComponentType -eq "Frontend") {
if ($ComponentType -eq "All" -or $ComponentType -eq "Frontend" -or $ComponentType -eq "Backend") {
$readme += @"
backend/ FastAPI backend application (Python)
frontend/ Vue.js static files (production build)
backend/ Reports App - FastAPI backend (Python, port 8000)
frontend/ Reports App - Vue.js static files (production build)
shared/ Shared Python modules (auth, database, utils)
config/ Configuration templates (.env, web.config)
"@
@@ -756,7 +930,20 @@ CONTENTS:
if ($ComponentType -eq "All" -or $ComponentType -eq "TelegramBot") {
$readme += @"
telegram-bot/ Telegram bot application
telegram-bot/ Telegram bot application (port 8002)
"@
}
if ($ComponentType -eq "All" -or $ComponentType -eq "DataEntryApp" -or $ComponentType -eq "DataEntryBackend") {
$readme += @"
data-entry-backend/ Data Entry App - FastAPI backend (Python, port 8003)
"@
}
if ($ComponentType -eq "All" -or $ComponentType -eq "DataEntryApp") {
$readme += @"
data-entry-frontend/ Data Entry App - Vue.js static files (production build)
"@
}
@@ -922,15 +1109,15 @@ function New-DeploymentPackage {
# Build based on component type
switch ($ComponentType) {
"All" {
# Frontend
# Reports Frontend
$frontendDistPath = Build-Frontend -SourcePath $Paths.FrontendSource -OutputPath $OutputPath
$frontendDest = Join-Path $OutputPath "frontend"
New-Item -ItemType Directory -Path $frontendDest -Force | Out-Null
Write-Step "Copying frontend files..."
Write-Step "Copying Reports frontend files..."
Copy-Item -Path "$frontendDistPath\*" -Destination $frontendDest -Recurse -Force
Write-Success "Frontend files copied"
Write-Success "Reports frontend files copied"
# Backend
# Reports Backend
$backendDest = Join-Path $OutputPath "backend"
Copy-BackendFiles -SourcePath $Paths.BackendSource -DestPath $backendDest
@@ -945,6 +1132,18 @@ function New-DeploymentPackage {
# Telegram Bot
$telegramDest = Join-Path $OutputPath "telegram-bot"
Copy-TelegramBotFiles -SourcePath $Paths.TelegramBotSource -DestPath $telegramDest
# Data Entry Frontend
$dataEntryFrontendDistPath = Build-Frontend -SourcePath $Paths.DataEntryFrontendSource -OutputPath $OutputPath
$dataEntryFrontendDest = Join-Path $OutputPath "data-entry-frontend"
New-Item -ItemType Directory -Path $dataEntryFrontendDest -Force | Out-Null
Write-Step "Copying Data Entry frontend files..."
Copy-Item -Path "$dataEntryFrontendDistPath\*" -Destination $dataEntryFrontendDest -Recurse -Force
Write-Success "Data Entry frontend files copied"
# Data Entry Backend
$dataEntryBackendDest = Join-Path $OutputPath "data-entry-backend"
Copy-DataEntryBackendFiles -SourcePath $Paths.DataEntryBackendSource -DestPath $dataEntryBackendDest
}
"Frontend" {
@@ -988,10 +1187,46 @@ function New-DeploymentPackage {
$telegramDest = Join-Path $OutputPath "telegram-bot"
Copy-TelegramBotFiles -SourcePath $Paths.TelegramBotSource -DestPath $telegramDest
}
"DataEntryApp" {
# Data Entry Frontend build
$dataEntryFrontendDistPath = Build-Frontend -SourcePath $Paths.DataEntryFrontendSource -OutputPath $OutputPath
$dataEntryFrontendDest = Join-Path $OutputPath "data-entry-frontend"
New-Item -ItemType Directory -Path $dataEntryFrontendDest -Force | Out-Null
Write-Step "Copying Data Entry frontend files..."
Copy-Item -Path "$dataEntryFrontendDistPath\*" -Destination $dataEntryFrontendDest -Recurse -Force
Write-Success "Data Entry frontend files copied"
# Data Entry Backend
$dataEntryBackendDest = Join-Path $OutputPath "data-entry-backend"
Copy-DataEntryBackendFiles -SourcePath $Paths.DataEntryBackendSource -DestPath $dataEntryBackendDest
# Shared modules
$sharedDest = Join-Path $OutputPath "shared"
Copy-SharedModules -SourcePath $Paths.SharedSource -DestPath $sharedDest
# Config templates
$configDest = Join-Path $OutputPath "config"
Copy-ConfigTemplates -SourcePath $Paths.ConfigSource -DestPath $configDest
}
"DataEntryBackend" {
# Data Entry Backend only
$dataEntryBackendDest = Join-Path $OutputPath "data-entry-backend"
Copy-DataEntryBackendFiles -SourcePath $Paths.DataEntryBackendSource -DestPath $dataEntryBackendDest
# Shared modules
$sharedDest = Join-Path $OutputPath "shared"
Copy-SharedModules -SourcePath $Paths.SharedSource -DestPath $sharedDest
# Config templates
$configDest = Join-Path $OutputPath "config"
Copy-ConfigTemplates -SourcePath $Paths.ConfigSource -DestPath $configDest
}
}
# Cleanup temporary directories
if ($ComponentType -eq "All" -or $ComponentType -eq "Frontend") {
if ($ComponentType -eq "All" -or $ComponentType -eq "Frontend" -or $ComponentType -eq "DataEntryApp") {
Remove-TempDirectories -OutputPath $OutputPath
}
@@ -1041,9 +1276,14 @@ function Main {
try {
# Resolve paths
$paths = @{
# Reports App sources
BackendSource = Resolve-FullPath -Path $config.BackendSource
FrontendSource = Resolve-FullPath -Path $config.FrontendSource
TelegramBotSource = Resolve-FullPath -Path $config.TelegramBotSource
# Data Entry App sources
DataEntryBackendSource = Resolve-FullPath -Path $config.DataEntryBackendSource
DataEntryFrontendSource = Resolve-FullPath -Path $config.DataEntryFrontendSource
# Shared sources
SharedSource = Resolve-FullPath -Path $config.SharedSource
ConfigSource = Resolve-FullPath -Path $config.ConfigSource
}
@@ -1055,7 +1295,7 @@ function Main {
Write-Host " Output Path: $outputFullPath"
# Validate Node.js for frontend builds
if ($Component -eq "All" -or $Component -eq "Frontend") {
if ($Component -eq "All" -or $Component -eq "Frontend" -or $Component -eq "DataEntryApp") {
Test-NodeJS
}