[CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$PsqlPath, [Parameter(Mandatory = $true)] [string]$AdminUser, [Parameter(Mandatory = $true)] [string]$AdminPassword, [Parameter(Mandatory = $true)] [ValidateRange(1, 65535)] [int]$PostgreSqlPort, [Parameter(Mandatory = $true)] [string]$DatabaseName, [Parameter(Mandatory = $true)] [string]$DatabaseUser, [Parameter(Mandatory = $true)] [string]$DatabasePassword, [Parameter(Mandatory = $true)] [ValidateRange(1, 65535)] [int]$ApplicationPort, [Parameter(Mandatory = $true)] [string]$ConfigPath ) $ErrorActionPreference = 'Stop' trap { $logDirectory = Split-Path -Parent $ConfigPath New-Item -ItemType Directory -Path $logDirectory -Force -ErrorAction SilentlyContinue | Out-Null $_ | Out-String | Set-Content -LiteralPath (Join-Path $logDirectory 'install-error.log') -Encoding UTF8 $env:PGPASSWORD = $null exit 1 } function Assert-SafeIdentifier { param([string]$Value, [string]$Label) if ($Value -notmatch '^[A-Za-z_][A-Za-z0-9_]*$') { throw "$Label содержит недопустимые символы." } } function Quote-Identifier { param([string]$Value) return '"' + $Value.Replace('"', '""') + '"' } function Quote-Literal { param([string]$Value) return "'" + $Value.Replace("'", "''") + "'" } function Invoke-Psql { param( [string]$Database, [string]$Sql, [switch]$Capture ) $arguments = @( '--host', '127.0.0.1', '--port', [string]$PostgreSqlPort, '--username', $AdminUser, '--dbname', $Database, '--no-password', '--set', 'ON_ERROR_STOP=1', '--tuples-only', '--no-align', '--command', $Sql ) if ($Capture) { $output = & $PsqlPath @arguments 2>&1 if ($LASTEXITCODE -ne 0) { throw ($output -join [Environment]::NewLine) } return ($output -join [Environment]::NewLine).Trim() } & $PsqlPath @arguments if ($LASTEXITCODE -ne 0) { throw "psql завершился с кодом $LASTEXITCODE." } } if (-not (Test-Path -LiteralPath $PsqlPath -PathType Leaf)) { throw "Не найден psql.exe: $PsqlPath" } Assert-SafeIdentifier -Value $AdminUser -Label 'Логин администратора PostgreSQL' Assert-SafeIdentifier -Value $DatabaseName -Label 'Имя базы данных' Assert-SafeIdentifier -Value $DatabaseUser -Label 'Логин пользователя базы данных' $env:PGPASSWORD = $AdminPassword $connected = $false $lastConnectionError = $null for ($attempt = 1; $attempt -le 45; $attempt++) { try { Invoke-Psql -Database 'postgres' -Sql 'SELECT 1;' | Out-Null $connected = $true break } catch { $lastConnectionError = $_ Start-Sleep -Seconds 2 } } if (-not $connected) { throw "Не удалось подключиться к PostgreSQL на порту $PostgreSqlPort. $lastConnectionError" } $databaseIdentifier = Quote-Identifier $DatabaseName $databaseLiteral = Quote-Literal $DatabaseName $userIdentifier = Quote-Identifier $DatabaseUser $userLiteral = Quote-Literal $DatabaseUser $passwordLiteral = Quote-Literal $DatabasePassword $roleExists = Invoke-Psql -Database 'postgres' -Sql "SELECT 1 FROM pg_roles WHERE rolname = $userLiteral;" -Capture if ($roleExists -eq '1') { Invoke-Psql -Database 'postgres' -Sql "ALTER ROLE $userIdentifier WITH LOGIN PASSWORD $passwordLiteral;" } else { Invoke-Psql -Database 'postgres' -Sql "CREATE ROLE $userIdentifier WITH LOGIN PASSWORD $passwordLiteral;" } $databaseExists = Invoke-Psql -Database 'postgres' -Sql "SELECT 1 FROM pg_database WHERE datname = $databaseLiteral;" -Capture if ($databaseExists -ne '1') { Invoke-Psql -Database 'postgres' -Sql "CREATE DATABASE $databaseIdentifier OWNER $userIdentifier ENCODING 'UTF8';" } else { Invoke-Psql -Database 'postgres' -Sql "ALTER DATABASE $databaseIdentifier OWNER TO $userIdentifier;" } Invoke-Psql -Database 'postgres' -Sql "GRANT ALL PRIVILEGES ON DATABASE $databaseIdentifier TO $userIdentifier;" $env:PGPASSWORD = $DatabasePassword $testArguments = @( '--host', '127.0.0.1', '--port', [string]$PostgreSqlPort, '--username', $DatabaseUser, '--dbname', $DatabaseName, '--no-password', '--set', 'ON_ERROR_STOP=1', '--command', 'SELECT 1;' ) & $PsqlPath @testArguments | Out-Null if ($LASTEXITCODE -ne 0) { throw 'Не удалось проверить подключение с учётными данными приложения.' } $configDirectory = Split-Path -Parent $ConfigPath New-Item -ItemType Directory -Path $configDirectory -Force | Out-Null $config = [ordered]@{ database = [ordered]@{ host = '127.0.0.1' port = $PostgreSqlPort name = $DatabaseName user = $DatabaseUser password = $DatabasePassword } application = [ordered]@{ host = '0.0.0.0' port = $ApplicationPort } } $config | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $ConfigPath -Encoding UTF8 $env:PGPASSWORD = $null Write-Host "База данных $DatabaseName и конфигурация Inventera подготовлены."