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:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user