327 lines
9.8 KiB
PowerShell
327 lines
9.8 KiB
PowerShell
param(
|
|
[switch]$SkipInfra,
|
|
[switch]$SkipMigrate,
|
|
[switch]$SkipApi,
|
|
[switch]$SkipWeb,
|
|
[switch]$WithGateway,
|
|
[switch]$OnlyMinio
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$root = Resolve-Path (Join-Path $PSScriptRoot '..')
|
|
$logDir = Join-Path $root 'logs'
|
|
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
|
|
|
|
function Test-CommandAvailable {
|
|
param([string]$Name)
|
|
return [bool](Get-Command $Name -ErrorAction SilentlyContinue)
|
|
}
|
|
|
|
function Test-PortOpen {
|
|
param([int]$Port)
|
|
return [bool](Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue)
|
|
}
|
|
|
|
function Test-TcpPort {
|
|
param(
|
|
[string]$HostName,
|
|
[int]$Port
|
|
)
|
|
|
|
try {
|
|
$client = [System.Net.Sockets.TcpClient]::new()
|
|
$connect = $client.BeginConnect($HostName, $Port, $null, $null)
|
|
if (-not $connect.AsyncWaitHandle.WaitOne(1000)) {
|
|
$client.Close()
|
|
return $false
|
|
}
|
|
$client.EndConnect($connect)
|
|
$client.Close()
|
|
return $true
|
|
} catch {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Start-LoggedProcess {
|
|
param(
|
|
[string]$Name,
|
|
[string]$FilePath,
|
|
[string[]]$ArgumentList,
|
|
[string]$WorkingDirectory,
|
|
[string]$OutLog,
|
|
[string]$ErrLog
|
|
)
|
|
|
|
Write-Host "Starting $Name..."
|
|
Start-Process `
|
|
-FilePath $FilePath `
|
|
-ArgumentList $ArgumentList `
|
|
-WorkingDirectory $WorkingDirectory `
|
|
-RedirectStandardOutput $OutLog `
|
|
-RedirectStandardError $ErrLog `
|
|
-WindowStyle Hidden | Out-Null
|
|
}
|
|
|
|
function Wait-ForTcpPort {
|
|
param(
|
|
[string]$Name,
|
|
[string]$HostName,
|
|
[int]$Port,
|
|
[int]$TimeoutSeconds = 20
|
|
)
|
|
|
|
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
|
while ((Get-Date) -lt $deadline) {
|
|
if (Test-TcpPort -HostName $HostName -Port $Port) {
|
|
Write-Host "$Name is listening on ${HostName}:$Port."
|
|
return $true
|
|
}
|
|
Start-Sleep -Milliseconds 500
|
|
}
|
|
|
|
Write-Host "$Name is not listening on ${HostName}:$Port after ${TimeoutSeconds}s."
|
|
return $false
|
|
}
|
|
|
|
function Find-LocalPostgresBin {
|
|
$candidates = @(
|
|
'C:\cmpp-platform-local\pgsql\bin',
|
|
'C:\cmpp-platform-local\PostgreSQL16\bin',
|
|
'C:\Program Files\PostgreSQL\16\bin'
|
|
)
|
|
|
|
foreach ($candidate in $candidates) {
|
|
if ((Test-Path (Join-Path $candidate 'pg_ctl.exe')) -and (Test-Path (Join-Path $candidate 'pg_isready.exe'))) {
|
|
return $candidate
|
|
}
|
|
}
|
|
|
|
$pgCtl = Get-Command 'pg_ctl.exe' -ErrorAction SilentlyContinue
|
|
if ($pgCtl) {
|
|
return Split-Path $pgCtl.Source -Parent
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Start-LocalPostgres {
|
|
if (Test-TcpPort -HostName '127.0.0.1' -Port 5432) {
|
|
Write-Host 'PostgreSQL is already reachable on 127.0.0.1:5432.'
|
|
return $true
|
|
}
|
|
|
|
$pgBin = Find-LocalPostgresBin
|
|
$pgData = 'C:\cmpp-platform-local\postgres-data'
|
|
if (-not $pgBin -or -not (Test-Path (Join-Path $pgData 'PG_VERSION'))) {
|
|
Write-Host 'PostgreSQL is not reachable and no local PostgreSQL data directory was found.'
|
|
Write-Host 'Expected Docker Compose or local data at C:\cmpp-platform-local\postgres-data.'
|
|
return $false
|
|
}
|
|
|
|
Write-Host "Starting local PostgreSQL from $pgData..."
|
|
& (Join-Path $pgBin 'pg_ctl.exe') -D $pgData -l (Join-Path $logDir 'postgres.log') start | Out-Host
|
|
Start-Sleep -Seconds 2
|
|
if (Test-Path (Join-Path $pgBin 'pg_isready.exe')) {
|
|
& (Join-Path $pgBin 'pg_isready.exe') -h 127.0.0.1 -p 5432 -U cmpp -d cmpp_platform | Out-Host
|
|
}
|
|
return (Wait-ForTcpPort -Name 'PostgreSQL' -HostName '127.0.0.1' -Port 5432 -TimeoutSeconds 30)
|
|
}
|
|
|
|
function Start-LocalRedis {
|
|
if (Test-TcpPort -HostName '127.0.0.1' -Port 6379) {
|
|
Write-Host 'Redis is already reachable on 127.0.0.1:6379.'
|
|
return $true
|
|
}
|
|
|
|
$redisServer = Get-Command 'redis-server' -ErrorAction SilentlyContinue
|
|
if (-not $redisServer) {
|
|
Write-Host 'Redis is not reachable and redis-server was not found in PATH.'
|
|
return $false
|
|
}
|
|
|
|
$redisDir = Join-Path $root '.local-data\redis'
|
|
New-Item -ItemType Directory -Force -Path $redisDir | Out-Null
|
|
|
|
Start-LoggedProcess `
|
|
-Name 'Redis' `
|
|
-FilePath $redisServer.Source `
|
|
-ArgumentList @('--port', '6379', '--dir', $redisDir, '--dbfilename', 'dump.rdb') `
|
|
-WorkingDirectory $root `
|
|
-OutLog (Join-Path $logDir 'redis.out.log') `
|
|
-ErrLog (Join-Path $logDir 'redis.err.log')
|
|
|
|
return (Wait-ForTcpPort -Name 'Redis' -HostName '127.0.0.1' -Port 6379 -TimeoutSeconds 15)
|
|
}
|
|
|
|
function Find-LocalMinio {
|
|
$candidates = @(
|
|
(Join-Path $root '.local-tools\minio.exe'),
|
|
'C:\cmpp-platform-local\minio.exe',
|
|
'C:\cmpp-platform-local\minio\minio.exe'
|
|
)
|
|
|
|
foreach ($candidate in $candidates) {
|
|
if (Test-Path $candidate) {
|
|
return $candidate
|
|
}
|
|
}
|
|
|
|
$minio = Get-Command 'minio.exe' -ErrorAction SilentlyContinue
|
|
if ($minio) {
|
|
return $minio.Source
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Start-LocalMinio {
|
|
if ((Test-TcpPort -HostName '127.0.0.1' -Port 9000) -and (Test-TcpPort -HostName '127.0.0.1' -Port 9001)) {
|
|
Write-Host 'MinIO is already reachable on 127.0.0.1:9000/9001.'
|
|
return $true
|
|
}
|
|
|
|
$minio = Find-LocalMinio
|
|
if (-not $minio) {
|
|
Write-Host 'MinIO is not reachable and minio.exe was not found.'
|
|
Write-Host 'Expected minio.exe at C:\cmpp-platform-local\minio.exe or in PATH.'
|
|
return $false
|
|
}
|
|
|
|
$minioData = 'C:\cmpp-platform-local\minio-data'
|
|
New-Item -ItemType Directory -Force -Path $minioData | Out-Null
|
|
if (-not $env:MINIO_ROOT_USER) {
|
|
$env:MINIO_ROOT_USER = 'cmpp_minio'
|
|
}
|
|
if (-not $env:MINIO_ROOT_PASSWORD) {
|
|
$env:MINIO_ROOT_PASSWORD = 'cmpp_minio_password'
|
|
}
|
|
|
|
Start-LoggedProcess `
|
|
-Name 'MinIO' `
|
|
-FilePath $minio `
|
|
-ArgumentList @('server', $minioData, '--address', ':9000', '--console-address', ':9001') `
|
|
-WorkingDirectory (Split-Path $minio -Parent) `
|
|
-OutLog (Join-Path $logDir 'minio.out.log') `
|
|
-ErrLog (Join-Path $logDir 'minio.err.log')
|
|
|
|
$apiReady = Wait-ForTcpPort -Name 'MinIO API' -HostName '127.0.0.1' -Port 9000 -TimeoutSeconds 20
|
|
$consoleReady = Wait-ForTcpPort -Name 'MinIO Console' -HostName '127.0.0.1' -Port 9001 -TimeoutSeconds 20
|
|
return ($apiReady -and $consoleReady)
|
|
}
|
|
|
|
function Test-Minio {
|
|
if ((Test-TcpPort -HostName '127.0.0.1' -Port 9000) -and (Test-TcpPort -HostName '127.0.0.1' -Port 9001)) {
|
|
Write-Host 'MinIO ports 9000/9001 are reachable.'
|
|
return $true
|
|
}
|
|
|
|
Write-Host 'MinIO is not fully reachable on 9000/9001. File upload smoke may be blocked.'
|
|
return $false
|
|
}
|
|
|
|
Write-Host "CMPP local service startup"
|
|
Write-Host "Workspace: $root"
|
|
|
|
$minioReady = $false
|
|
if ($OnlyMinio) {
|
|
$minioReady = Start-LocalMinio
|
|
if (-not $minioReady) {
|
|
exit 1
|
|
}
|
|
Write-Host ''
|
|
Write-Host 'MinIO startup command issued. Useful URLs:'
|
|
Write-Host ' MinIO API: http://localhost:9000'
|
|
Write-Host ' MinIO Console: http://localhost:9001'
|
|
Write-Host ''
|
|
Write-Host "Logs: $logDir"
|
|
return
|
|
}
|
|
|
|
if (-not $SkipInfra) {
|
|
if (Test-CommandAvailable 'docker') {
|
|
Write-Host "Starting PostgreSQL, Redis and MinIO via Docker Compose..."
|
|
& docker compose -f (Join-Path $root 'infra/docker-compose.yml') up -d
|
|
Wait-ForTcpPort -Name 'PostgreSQL' -HostName '127.0.0.1' -Port 5432 -TimeoutSeconds 30 | Out-Null
|
|
Wait-ForTcpPort -Name 'Redis' -HostName '127.0.0.1' -Port 6379 -TimeoutSeconds 20 | Out-Null
|
|
$minioReady = Test-Minio
|
|
} else {
|
|
Write-Host "Docker CLI is not available. Trying local PostgreSQL and Redis fallback..."
|
|
Start-LocalPostgres | Out-Null
|
|
Start-LocalRedis | Out-Null
|
|
$minioReady = Start-LocalMinio
|
|
}
|
|
} else {
|
|
$minioReady = Test-Minio
|
|
}
|
|
|
|
if (-not $minioReady) {
|
|
$localObjectRoot = Join-Path $root '.local-data\object-storage'
|
|
New-Item -ItemType Directory -Force -Path $localObjectRoot | Out-Null
|
|
$env:OBJECT_STORAGE_DRIVER = 'local'
|
|
$env:OBJECT_STORAGE_LOCAL_ROOT = $localObjectRoot
|
|
Write-Host "Object storage fallback enabled: $localObjectRoot"
|
|
}
|
|
|
|
if (-not $SkipInfra -and -not $SkipMigrate -and (Test-TcpPort -HostName '127.0.0.1' -Port 5432)) {
|
|
Write-Host 'Applying Prisma migrations...'
|
|
& npm.cmd --prefix api run prisma:migrate:deploy
|
|
}
|
|
|
|
if (-not $SkipApi) {
|
|
if (Test-PortOpen 3000) {
|
|
Write-Host "API port 3000 is already listening; leaving the existing API process untouched."
|
|
} else {
|
|
Start-LoggedProcess `
|
|
-Name 'NestJS API' `
|
|
-FilePath 'npm.cmd' `
|
|
-ArgumentList @('--prefix', 'api', 'run', 'start:dev') `
|
|
-WorkingDirectory $root `
|
|
-OutLog (Join-Path $logDir 'api-dev.out.log') `
|
|
-ErrLog (Join-Path $logDir 'api-dev.err.log')
|
|
}
|
|
}
|
|
|
|
if (-not $SkipWeb) {
|
|
if ((Test-PortOpen 5173) -or (Test-PortOpen 4173) -or (Test-PortOpen 4174)) {
|
|
Write-Host "A frontend port is already listening on 5173/4173/4174; leaving the existing web process untouched."
|
|
} else {
|
|
Start-LoggedProcess `
|
|
-Name 'frontend preview' `
|
|
-FilePath 'npm.cmd' `
|
|
-ArgumentList @('run', 'dev') `
|
|
-WorkingDirectory $root `
|
|
-OutLog (Join-Path $logDir 'frontend-dev.out.log') `
|
|
-ErrLog (Join-Path $logDir 'frontend-dev.err.log')
|
|
}
|
|
}
|
|
|
|
if ($WithGateway) {
|
|
if (-not (Test-CommandAvailable 'go')) {
|
|
Write-Host "Go CLI is not available. Skipping gateway startup."
|
|
} elseif (Test-PortOpen 8090) {
|
|
Write-Host "Gateway health port 8090 is already listening; leaving the existing gateway process untouched."
|
|
} else {
|
|
Start-LoggedProcess `
|
|
-Name 'Go gateway' `
|
|
-FilePath 'go' `
|
|
-ArgumentList @('run', './cmd/gateway') `
|
|
-WorkingDirectory (Join-Path $root 'gateway') `
|
|
-OutLog (Join-Path $logDir 'gateway.out.log') `
|
|
-ErrLog (Join-Path $logDir 'gateway.err.log')
|
|
}
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host 'Startup command issued. Useful URLs:'
|
|
Write-Host ' API: http://localhost:3000/api'
|
|
Write-Host ' Frontend: http://localhost:5173 or http://localhost:4173'
|
|
if ($minioReady) {
|
|
Write-Host ' MinIO: http://localhost:9001'
|
|
} else {
|
|
Write-Host ' MinIO: not running; using local object storage fallback when API is started by this script'
|
|
}
|
|
Write-Host ''
|
|
Write-Host "Logs: $logDir"
|