180 lines
5.2 KiB
PowerShell
180 lines
5.2 KiB
PowerShell
[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 подготовлены."
|