Ctrl K
ComunidadGuías…

scripts/new-docker-env.ps1

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
[CmdletBinding(SupportsShouldProcess)]
param(
    [Parameter(Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string]$AdvertiseIP,

    [Parameter()]
    [string]$PublicBaseURL = "",

    [Parameter()]
    [string]$PublicWebBaseURL = "",

    [Parameter()]
    [string]$AdminBindIP = "127.0.0.1",

    [Parameter()]
    [switch]$HostNetwork,

    [Parameter()]
    [switch]$BridgeNetwork,

    [Parameter()]
    [switch]$AllowInsecureDevelopmentAuth
)

$ErrorActionPreference = "Stop"

if ($HostNetwork -and $BridgeNetwork) {
    throw "HostNetwork and BridgeNetwork are mutually exclusive."
}

$parsedIP = $null
if (-not [System.Net.IPAddress]::TryParse($AdvertiseIP, [ref]$parsedIP)) {
    throw "AdvertiseIP must be an IPv4 or IPv6 address, not a DNS name."
}
$isLoopback = [System.Net.IPAddress]::IsLoopback($parsedIP)
$isIPv6 = $parsedIP.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6

$parsedAdminBindIP = $null
if (-not [System.Net.IPAddress]::TryParse($AdminBindIP, [ref]$parsedAdminBindIP)) {
    throw "AdminBindIP must be an IPv4 or IPv6 address."
}

if ([string]::IsNullOrWhiteSpace($PublicBaseURL)) {
    if (-not $isLoopback) {
        throw "PublicBaseURL is required when AdvertiseIP is not loopback."
    }
    $loopbackHost = if ($isIPv6) { "[::1]" } else { "127.0.0.1" }
    $PublicBaseURL = "http://${loopbackHost}:2401"
}
if ([string]::IsNullOrWhiteSpace($PublicWebBaseURL)) {
    $PublicWebBaseURL = $PublicBaseURL
}

function Assert-HTTPURL {
    param([string]$Name, [string]$Value)
    $uri = $null
    if (-not [Uri]::TryCreate($Value, [UriKind]::Absolute, [ref]$uri) -or
        ($uri.Scheme -ne "http" -and $uri.Scheme -ne "https") -or
        -not [string]::IsNullOrEmpty($uri.UserInfo)) {
        throw "$Name must be an absolute HTTP(S) URL without embedded credentials."
    }
}

Assert-HTTPURL -Name "PublicBaseURL" -Value $PublicBaseURL
Assert-HTTPURL -Name "PublicWebBaseURL" -Value $PublicWebBaseURL
if (-not $isLoopback -and -not $AllowInsecureDevelopmentAuth) {
    throw "Internet/LAN development-code auth requires AllowInsecureDevelopmentAuth; for production generate on loopback, then configure the webhook provider before startup."
}

$repoRoot = Split-Path -Parent $PSScriptRoot
$dockerDir = Join-Path $repoRoot "deploy\docker"
$templatePath = Join-Path $dockerDir ".env.example"
$outputPath = Join-Path $dockerDir ".env"
if (-not (Test-Path -LiteralPath $templatePath -PathType Leaf)) {
    throw "Docker environment template not found: $templatePath"
}
if (Test-Path -LiteralPath $outputPath) {
    throw "$outputPath already exists. Initialization never overwrites live credentials."
}

function New-HexSecret {
    param([int]$Bytes = 32)
    $buffer = New-Object byte[] $Bytes
    $generator = [System.Security.Cryptography.RandomNumberGenerator]::Create()
    try {
        $generator.GetBytes($buffer)
    }
    finally {
        $generator.Dispose()
    }
    return [BitConverter]::ToString($buffer).Replace("-", "").ToLowerInvariant()
}

function Get-GitValue {
    param([string[]]$GitArguments)
    try {
        $value = & git -C $repoRoot @GitArguments 2>$null
        if ($LASTEXITCODE -eq 0) { return ($value | Select-Object -First 1).Trim() }
    }
    catch {}
    return "unknown"
}

$publicBindIP = if ($isIPv6) { "::" } else { "0.0.0.0" }
$localBindIP = if ($isIPv6) { "::1" } else { "127.0.0.1" }
if ($isLoopback) {
    $publicBindIP = $parsedIP.ToString()
    $localBindIP = $parsedIP.ToString()
}
elseif (([Uri]$PublicBaseURL).Scheme -eq "http") {
    $publicLinkIP = $null
    $publicLinkHost = ([Uri]$PublicBaseURL).Host.Trim([char[]]"[]")
    if ([System.Net.IPAddress]::TryParse($publicLinkHost, [ref]$publicLinkIP) -and $publicLinkIP.Equals($parsedIP)) {
        $localBindIP = $parsedIP.ToString()
    }
}

$adminHealthIP = $parsedAdminBindIP.ToString()
if ($adminHealthIP -eq "0.0.0.0") { $adminHealthIP = "127.0.0.1" }
if ($adminHealthIP -eq "::") { $adminHealthIP = "::1" }
$publicListenHost = if ($publicBindIP.Contains(":")) { "[${publicBindIP}]" } else { $publicBindIP }
$localListenHost = if ($localBindIP.Contains(":")) { "[${localBindIP}]" } else { $localBindIP }
$serverHealthURLHost = $localListenHost
$adminListenIP = $parsedAdminBindIP.ToString()
$adminListenHost = if ($adminListenIP.Contains(":")) { "[${adminListenIP}]" } else { $adminListenIP }
$turnEnabled = (-not $isIPv6).ToString().ToLowerInvariant()
$turnAdvertiseIP = if ($isIPv6) { "127.0.0.1" } else { $parsedIP.ToString() }
$rtmpHost = if ($isIPv6) { "[$($parsedIP.ToString())]" } else { $parsedIP.ToString() }
$postgresPassword = New-HexSecret 24

$treeState = "unknown"
try {
    $treeOutput = & git -C $repoRoot status --porcelain 2>$null
    if ($LASTEXITCODE -eq 0) { $treeState = if (@($treeOutput).Count -gt 0) { "dirty" } else { "clean" } }
}
catch {}

$values = [ordered]@{
    TELESRV_BUILD_COMMIT                    = Get-GitValue @("rev-parse", "HEAD")
    TELESRV_BUILD_BRANCH                    = Get-GitValue @("rev-parse", "--abbrev-ref", "HEAD")
    TELESRV_BUILD_TREE_STATE                = $treeState
    TELESRV_BUILD_DATE                      = [DateTime]::UtcNow.ToString("o")
    POSTGRES_PASSWORD                       = $postgresPassword
    TELESRV_POSTGRES_DSN                    = "postgres://telesrv:${postgresPassword}@127.0.0.1:15432/telesrv_main?sslmode=disable"
    TELESRV_REDIS_PASSWORD                  = New-HexSecret 32
    TELESRV_ADMIN_API_TOKEN                 = New-HexSecret 32
    TELESRV_ADMIN_UI_PASSWORD               = New-HexSecret 24
    TELESRV_ADMIN_SESSION_KEY               = New-HexSecret 32
    TELESRV_TURN_SECRET                      = New-HexSecret 32
    TELESRV_OTP_WEBHOOK_SECRET              = New-HexSecret 32
    TELESRV_ALLOW_INSECURE_DEVELOPMENT_AUTH = ($isLoopback -or $AllowInsecureDevelopmentAuth).ToString().ToLowerInvariant()
    TELESRV_ADVERTISE_IP                    = $parsedIP.ToString()
    TELESRV_PUBLIC_BASE_URL                 = $PublicBaseURL
    TELESRV_PUBLIC_WEB_BASE_URL             = $PublicWebBaseURL
    TELESRV_SERVER_HOST_NETWORK             = (-not $BridgeNetwork).ToString().ToLowerInvariant()
    TELESRV_SFU_ADVERTISE_IP                = $parsedIP.ToString()
    TELESRV_TURN_ENABLE                      = $turnEnabled
    TELESRV_TURN_ADVERTISE_IP                = $turnAdvertiseIP
    TELESRV_LIVESTREAM_RTMP_URL              = "rtmp://${rtmpHost}:2400/live"
    TELESRV_PUBLIC_BIND_IP                   = $publicBindIP
    TELESRV_PUBLIC_LISTEN_HOST               = $publicListenHost
    TELESRV_LOCAL_BIND_IP                    = $localBindIP
    TELESRV_LOCAL_LISTEN_HOST                = $localListenHost
    TELESRV_SERVER_HEALTH_IP                 = $localBindIP
    TELESRV_SERVER_HEALTH_URL_HOST           = $serverHealthURLHost
    TELESRV_ADMIN_BIND_IP                    = $adminListenIP
    TELESRV_ADMIN_LISTEN_HOST                = $adminListenHost
    TELESRV_ADMIN_HEALTH_IP                  = $adminHealthIP
}

$content = [IO.File]::ReadAllText($templatePath)
foreach ($entry in $values.GetEnumerator()) {
    $pattern = "(?m)^$([Regex]::Escape($entry.Key))=.*$"
    if (-not [Regex]::IsMatch($content, $pattern)) { throw "Template is missing $($entry.Key)." }
    $replacement = ("{0}={1}" -f $entry.Key, $entry.Value).Replace('$', '$$')
    $content = [Regex]::Replace($content, $pattern, $replacement)
}

function Protect-SecretFile {
    param([string]$Path)

    if ($env:OS -eq "Windows_NT") {
        $owner = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
        $acl = New-Object System.Security.AccessControl.FileSecurity
        $acl.SetAccessRuleProtection($true, $false)
        $acl.SetOwner($owner)
        $identities = @(
            $owner,
            (New-Object System.Security.Principal.SecurityIdentifier("S-1-5-18")),
            (New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544"))
        )
        foreach ($identity in $identities) {
            $rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
                $identity,
                [System.Security.AccessControl.FileSystemRights]::FullControl,
                [System.Security.AccessControl.AccessControlType]::Allow
            )
            [void]$acl.AddAccessRule($rule)
        }
        Set-Acl -LiteralPath $Path -AclObject $acl
        return
    }

    & chmod 600 -- $Path
    if ($LASTEXITCODE -ne 0) { throw "chmod 600 failed for $Path" }
}

if ($PSCmdlet.ShouldProcess($outputPath, "write owner-only Docker deployment environment")) {
    $temporaryPath = "$outputPath.tmp.$PID"
    try {
        [IO.File]::WriteAllText($temporaryPath, $content, (New-Object Text.UTF8Encoding($false)))
        Protect-SecretFile -Path $temporaryPath
        Move-Item -LiteralPath $temporaryPath -Destination $outputPath
    }
    finally {
        if (Test-Path -LiteralPath $temporaryPath) { Remove-Item -LiteralPath $temporaryPath -Force }
    }
    Write-Host "Created $outputPath with generated deployment credentials."
}