# Shared config — project DB always lives in data/postgres on port 54320 $script:LocalDbPort = 54320 $script:LocalDbUser = 'eee_admin' $script:LocalDbPassword = 'eee_password' $script:LocalDbName = 'eee_dashboard' function Get-ProjectRoot { param([string]$FromScript = $PSScriptRoot) return (Resolve-Path (Join-Path $FromScript '..')).Path } function Get-PgDataDir { param([string]$Root) return Join-Path $Root 'data\postgres' } function Get-PgCtlLogPath { param([string]$Root) return Join-Path $Root 'data\pg_ctl.log' } function Find-PgBin { $cmd = Get-Command initdb -ErrorAction SilentlyContinue if ($cmd) { return (Split-Path $cmd.Path -Parent) } $candidates = @( 'C:\Program Files\PostgreSQL\16\bin', 'C:\Program Files\PostgreSQL\15\bin', 'C:\Program Files\PostgreSQL\14\bin' ) foreach ($dir in $candidates) { if (Test-Path (Join-Path $dir 'initdb.exe')) { return $dir } } return $null } function Test-DbPortOpen { param([int]$Port = $script:LocalDbPort) $client = $null try { $client = New-Object System.Net.Sockets.TcpClient $connect = $client.BeginConnect('127.0.0.1', $Port, $null, $null) if (-not $connect.AsyncWaitHandle.WaitOne(500, $false)) { return $false } $client.EndConnect($connect) return $client.Connected } catch { return $false } finally { if ($client) { $client.Dispose() } } } function Clear-StalePostmasterLock { param([string]$PgData) $pidFile = Join-Path $PgData 'postmaster.pid' if (-not (Test-Path $pidFile)) { return } $firstLine = Get-Content $pidFile -TotalCount 1 -ErrorAction SilentlyContinue $oldPid = 0 [void][int]::TryParse($firstLine, [ref]$oldPid) $alive = $false if ($oldPid -gt 0) { $proc = Get-Process -Id $oldPid -ErrorAction SilentlyContinue if ($proc -and $proc.ProcessName -match 'postgres') { $alive = $true } } if (-not $alive) { Remove-Item $pidFile -Force -ErrorAction SilentlyContinue Write-Host ' Cleared stale postmaster.pid (folder moved or crash)' } } function Get-DatabaseUrl { return "postgresql://$($script:LocalDbUser):$($script:LocalDbPassword)@localhost:$($script:LocalDbPort)/$($script:LocalDbName)" } function Set-EnvLine { param( [string]$Content, [string]$Key, [string]$Value ) $pattern = "(?m)^$Key=" $line = "$Key=`"$Value`"" if ($Content -match $pattern) { return ($Content -replace "(?m)^$Key=.*$", $line) } return "$line`r`n$Content" } function Ensure-BackendEnv { param([string]$Root) $envPath = Join-Path $Root 'backend\.env' $examplePath = Join-Path $Root 'backend\.env.example' if (-not (Test-Path $envPath)) { Copy-Item $examplePath $envPath } $url = Get-DatabaseUrl $content = Get-Content $envPath -Raw -Encoding UTF8 $content = Set-EnvLine $content 'DATABASE_URL' $url $content = Set-EnvLine $content 'HR_DATA_PATH' '../data/seed/hr-data.json' $content = Set-EnvLine $content 'UPLOAD_DIR' '../uploads' Set-Content -Path $envPath -Value $content.TrimEnd() -Encoding UTF8 -NoNewline Add-Content -Path $envPath -Value '' -Encoding UTF8 } function Set-PostgresPort { param( [string]$PgData, [int]$Port = $script:LocalDbPort ) $auto = Join-Path $PgData 'postgresql.auto.conf' $wanted = @{ 'port' = "port = $Port" 'logging_collector' = 'logging_collector = off' } $existing = @() if (Test-Path $auto) { $existing = @(Get-Content $auto -Encoding UTF8 -ErrorAction SilentlyContinue) } foreach ($key in $wanted.Keys) { $line = $wanted[$key] $found = $false $existing = @($existing | ForEach-Object { if ($_ -match "^\s*$key\s*=") { $found = $true; $line } else { $_ } }) if (-not $found) { $existing += $line } } $utf8 = New-Object System.Text.UTF8Encoding $false [System.IO.File]::WriteAllLines($auto, $existing, $utf8) } function Test-DockerReady { if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { return $false } docker info 2>$null | Out-Null return ($LASTEXITCODE -eq 0) } function Invoke-Psql { param( [string]$PgBin, [int]$Port = $script:LocalDbPort, [string]$User = $script:LocalDbUser, [string]$Database = 'postgres', [string]$Sql ) $psql = Join-Path $PgBin 'psql.exe' & $psql -h 127.0.0.1 -p $Port -U $User -d $Database -v ON_ERROR_STOP=1 -c $Sql 2>&1 | Out-Null if ($LASTEXITCODE -ne 0) { throw "psql failed ($Sql)" } }