2026-06-11 11:51:30 +05:30
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
|
|
|
|
|
|
$EnvFile = '.env'
|
|
|
|
|
$Registry = if ([string]::IsNullOrEmpty($env:REGISTRY)) { 'ghcr.io/dograh-hq' } else { $env:REGISTRY }
|
|
|
|
|
$EnableTelemetry = if ([string]::IsNullOrEmpty($env:ENABLE_TELEMETRY)) { 'true' } else { $env:ENABLE_TELEMETRY }
|
|
|
|
|
$Utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
|
|
|
|
|
|
|
|
|
function New-HexSecret {
|
|
|
|
|
$bytes = [byte[]]::new(32)
|
|
|
|
|
[System.Security.Cryptography.RandomNumberGenerator]::Fill($bytes)
|
|
|
|
|
return -join ($bytes | ForEach-Object { $_.ToString('x2') })
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 13:44:31 +05:30
|
|
|
function New-MinioRootUser {
|
|
|
|
|
return "dograh$((New-HexSecret).Substring(0, 12))"
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-11 11:51:30 +05:30
|
|
|
function Get-DotEnvValue {
|
|
|
|
|
param(
|
|
|
|
|
[string]$Path,
|
|
|
|
|
[string]$Key
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if (-not (Test-Path $Path)) {
|
|
|
|
|
return $null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$resolvedPath = (Resolve-Path $Path).Path
|
|
|
|
|
foreach ($line in [System.IO.File]::ReadLines($resolvedPath)) {
|
|
|
|
|
if ($line.StartsWith("$Key=")) {
|
|
|
|
|
return $line.Substring($Key.Length + 1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Set-DotEnvValue {
|
|
|
|
|
param(
|
|
|
|
|
[string]$Path,
|
|
|
|
|
[string]$Key,
|
|
|
|
|
[string]$Value
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
$lines = New-Object System.Collections.Generic.List[string]
|
|
|
|
|
$updated = $false
|
|
|
|
|
|
|
|
|
|
if (Test-Path $Path) {
|
|
|
|
|
$resolvedPath = (Resolve-Path $Path).Path
|
|
|
|
|
foreach ($line in [System.IO.File]::ReadLines($resolvedPath)) {
|
|
|
|
|
if ($line.StartsWith("$Key=")) {
|
|
|
|
|
$lines.Add("$Key=$Value")
|
|
|
|
|
$updated = $true
|
|
|
|
|
} else {
|
|
|
|
|
$lines.Add($line)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (-not $updated) {
|
|
|
|
|
$lines.Add("$Key=$Value")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[System.IO.File]::WriteAllLines((Join-Path (Get-Location) $Path), $lines, $Utf8NoBom)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 13:44:31 +05:30
|
|
|
function Get-PostgresVolumeName {
|
|
|
|
|
try {
|
|
|
|
|
$configJson = docker compose config --format json 2>$null
|
|
|
|
|
if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrEmpty($configJson)) {
|
|
|
|
|
$config = $configJson | ConvertFrom-Json
|
|
|
|
|
$volumeName = $config.volumes.postgres_data.name
|
|
|
|
|
if (-not [string]::IsNullOrEmpty($volumeName)) {
|
|
|
|
|
return $volumeName
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
# Fall back to Compose's default project-name convention below.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$projectName = if ([string]::IsNullOrEmpty($env:COMPOSE_PROJECT_NAME)) {
|
|
|
|
|
(Split-Path -Leaf (Get-Location).Path).ToLowerInvariant() -replace '[^a-z0-9_-]', ''
|
|
|
|
|
} else {
|
|
|
|
|
$env:COMPOSE_PROJECT_NAME.ToLowerInvariant() -replace '[^a-z0-9_-]', ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "${projectName}_postgres_data"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Test-DockerVolumeExists {
|
|
|
|
|
param([string]$Name)
|
|
|
|
|
|
|
|
|
|
docker volume inspect $Name *> $null
|
|
|
|
|
return $LASTEXITCODE -eq 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Wait-PostgresReady {
|
|
|
|
|
for ($attempt = 0; $attempt -lt 20; $attempt++) {
|
|
|
|
|
docker compose exec -T postgres pg_isready -U postgres *> $null
|
|
|
|
|
if ($LASTEXITCODE -eq 0) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
Start-Sleep -Seconds 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Write-Error 'Postgres did not become ready while syncing POSTGRES_PASSWORD.'
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Sync-PostgresPassword {
|
|
|
|
|
param([string]$Password)
|
|
|
|
|
|
|
|
|
|
if ([string]::IsNullOrEmpty($Password)) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$volumeName = Get-PostgresVolumeName
|
|
|
|
|
if ([string]::IsNullOrEmpty($volumeName) -or -not (Test-DockerVolumeExists $volumeName)) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Write-Host "Existing Postgres volume detected; syncing postgres password from $EnvFile."
|
|
|
|
|
$env:REGISTRY = $Registry
|
|
|
|
|
$env:ENABLE_TELEMETRY = $EnableTelemetry
|
|
|
|
|
docker compose up -d postgres
|
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
|
|
|
exit $LASTEXITCODE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Wait-PostgresReady
|
|
|
|
|
|
|
|
|
|
"ALTER USER postgres WITH PASSWORD :'dograh_password';" | docker compose exec -T postgres psql `
|
|
|
|
|
-U postgres `
|
|
|
|
|
-d postgres `
|
|
|
|
|
-v 'ON_ERROR_STOP=1' `
|
|
|
|
|
-v "dograh_password=$Password" > $null
|
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
|
|
|
Write-Error 'Failed to sync POSTGRES_PASSWORD with the existing Postgres volume.'
|
|
|
|
|
exit $LASTEXITCODE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Write-Host 'Postgres password synced.'
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-11 11:51:30 +05:30
|
|
|
if (-not (Test-Path 'docker-compose.yaml')) {
|
|
|
|
|
Write-Error 'docker-compose.yaml not found. Download it first, then re-run this script.'
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 04:41:31 -03:00
|
|
|
$envFileExisted = Test-Path $EnvFile
|
|
|
|
|
|
2026-06-11 11:51:30 +05:30
|
|
|
$existingSecret = Get-DotEnvValue -Path $EnvFile -Key 'OSS_JWT_SECRET'
|
|
|
|
|
if ([string]::IsNullOrEmpty($existingSecret)) {
|
|
|
|
|
Set-DotEnvValue -Path $EnvFile -Key 'OSS_JWT_SECRET' -Value (New-HexSecret)
|
|
|
|
|
Write-Host "Created OSS_JWT_SECRET in $EnvFile."
|
|
|
|
|
} else {
|
|
|
|
|
Write-Host "OSS_JWT_SECRET is already set in $EnvFile."
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 04:41:31 -03:00
|
|
|
$existingPostgresPassword = Get-DotEnvValue -Path $EnvFile -Key 'POSTGRES_PASSWORD'
|
|
|
|
|
if ([string]::IsNullOrEmpty($existingPostgresPassword)) {
|
|
|
|
|
if (-not $envFileExisted) {
|
|
|
|
|
Set-DotEnvValue -Path $EnvFile -Key 'POSTGRES_PASSWORD' -Value (New-HexSecret)
|
|
|
|
|
Write-Host "Created POSTGRES_PASSWORD in $EnvFile."
|
|
|
|
|
} else {
|
|
|
|
|
Write-Host "POSTGRES_PASSWORD is not set in $EnvFile; keeping the docker-compose fallback for existing local data volumes."
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Write-Host "POSTGRES_PASSWORD is already set in $EnvFile."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$existingRedisPassword = Get-DotEnvValue -Path $EnvFile -Key 'REDIS_PASSWORD'
|
|
|
|
|
if ([string]::IsNullOrEmpty($existingRedisPassword)) {
|
|
|
|
|
Set-DotEnvValue -Path $EnvFile -Key 'REDIS_PASSWORD' -Value (New-HexSecret)
|
|
|
|
|
Write-Host "Created REDIS_PASSWORD in $EnvFile."
|
|
|
|
|
} else {
|
|
|
|
|
Write-Host "REDIS_PASSWORD is already set in $EnvFile."
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 13:44:31 +05:30
|
|
|
$existingMinioRootUser = Get-DotEnvValue -Path $EnvFile -Key 'MINIO_ROOT_USER'
|
|
|
|
|
if ([string]::IsNullOrEmpty($existingMinioRootUser)) {
|
|
|
|
|
$existingMinioAccessKey = Get-DotEnvValue -Path $EnvFile -Key 'MINIO_ACCESS_KEY'
|
|
|
|
|
if ([string]::IsNullOrEmpty($existingMinioAccessKey)) {
|
|
|
|
|
Set-DotEnvValue -Path $EnvFile -Key 'MINIO_ROOT_USER' -Value (New-MinioRootUser)
|
|
|
|
|
Write-Host "Created MINIO_ROOT_USER in $EnvFile."
|
|
|
|
|
} else {
|
|
|
|
|
Set-DotEnvValue -Path $EnvFile -Key 'MINIO_ROOT_USER' -Value $existingMinioAccessKey
|
|
|
|
|
Write-Host "Created MINIO_ROOT_USER in $EnvFile from existing MINIO_ACCESS_KEY."
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Write-Host "MINIO_ROOT_USER is already set in $EnvFile."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$existingMinioRootPassword = Get-DotEnvValue -Path $EnvFile -Key 'MINIO_ROOT_PASSWORD'
|
|
|
|
|
if ([string]::IsNullOrEmpty($existingMinioRootPassword)) {
|
|
|
|
|
$existingMinioSecretKey = Get-DotEnvValue -Path $EnvFile -Key 'MINIO_SECRET_KEY'
|
|
|
|
|
if ([string]::IsNullOrEmpty($existingMinioSecretKey)) {
|
|
|
|
|
Set-DotEnvValue -Path $EnvFile -Key 'MINIO_ROOT_PASSWORD' -Value (New-HexSecret)
|
|
|
|
|
Write-Host "Created MINIO_ROOT_PASSWORD in $EnvFile."
|
|
|
|
|
} else {
|
|
|
|
|
Set-DotEnvValue -Path $EnvFile -Key 'MINIO_ROOT_PASSWORD' -Value $existingMinioSecretKey
|
|
|
|
|
Write-Host "Created MINIO_ROOT_PASSWORD in $EnvFile from existing MINIO_SECRET_KEY."
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Write-Host "MINIO_ROOT_PASSWORD is already set in $EnvFile."
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-11 11:51:30 +05:30
|
|
|
Write-Host ''
|
|
|
|
|
Write-Host "Docker registry: $Registry"
|
|
|
|
|
Write-Host "Telemetry enabled: $EnableTelemetry"
|
|
|
|
|
Write-Host ''
|
|
|
|
|
Write-Host 'This will run:'
|
|
|
|
|
Write-Host " `$env:REGISTRY = '$Registry'; `$env:ENABLE_TELEMETRY = '$EnableTelemetry'; docker compose up --pull always"
|
|
|
|
|
Write-Host ''
|
|
|
|
|
|
|
|
|
|
$answer = Read-Host 'Start Dograh now? [Y/n]'
|
|
|
|
|
if ($answer -match '^[Nn]') {
|
|
|
|
|
Write-Host 'Dograh was not started.'
|
|
|
|
|
exit 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$env:REGISTRY = $Registry
|
|
|
|
|
$env:ENABLE_TELEMETRY = $EnableTelemetry
|
2026-06-21 13:44:31 +05:30
|
|
|
Sync-PostgresPassword -Password (Get-DotEnvValue -Path $EnvFile -Key 'POSTGRES_PASSWORD')
|
2026-06-11 11:51:30 +05:30
|
|
|
docker compose up --pull always
|
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
|
|
|
exit $LASTEXITCODE
|
|
|
|
|
}
|