238 lines
10 KiB
PowerShell
238 lines
10 KiB
PowerShell
# Vendoo 1.36.0 – grafischer Zero-Edit-Installationsassistent
|
||
$ErrorActionPreference = 'Stop'
|
||
$VendooDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||
$SetupScript = Join-Path $VendooDir 'setup.ps1'
|
||
$EnvFile = Join-Path $VendooDir '.env'
|
||
$EnvExample = Join-Path $VendooDir '.env.example'
|
||
$ModeFile = Join-Path $VendooDir '.vendoo-install-mode'
|
||
|
||
Add-Type -AssemblyName System.Windows.Forms
|
||
Add-Type -AssemblyName System.Drawing
|
||
[System.Windows.Forms.Application]::EnableVisualStyles()
|
||
|
||
function Write-Utf8NoBom([string]$Path, [string]$Content) {
|
||
$utf8 = New-Object System.Text.UTF8Encoding($false)
|
||
[System.IO.File]::WriteAllText($Path, $Content, $utf8)
|
||
}
|
||
|
||
function Ensure-EnvFile {
|
||
if (-not (Test-Path -LiteralPath $EnvFile)) {
|
||
if (-not (Test-Path -LiteralPath $EnvExample)) { throw '.env.example fehlt.' }
|
||
Copy-Item -LiteralPath $EnvExample -Destination $EnvFile
|
||
}
|
||
}
|
||
|
||
function Get-EnvValue([string]$Key) {
|
||
if (-not (Test-Path -LiteralPath $EnvFile)) { return '' }
|
||
$line = Get-Content -LiteralPath $EnvFile | Where-Object { $_ -match ('^' + [regex]::Escape($Key) + '=') } | Select-Object -Last 1
|
||
if (-not $line) { return '' }
|
||
return ($line -replace ('^' + [regex]::Escape($Key) + '='), '').Trim()
|
||
}
|
||
|
||
function Set-EnvValue([string]$Key, [string]$Value) {
|
||
if ($Value -match "[\r\n]") { throw "Ungültiger mehrzeiliger Wert für $Key." }
|
||
Ensure-EnvFile
|
||
$content = Get-Content -LiteralPath $EnvFile -Raw
|
||
$escaped = [regex]::Escape($Key)
|
||
if ($content -match "(?m)^$escaped=") {
|
||
$content = [regex]::Replace($content, "(?m)^$escaped=.*$", "$Key=$Value")
|
||
} else {
|
||
$content = $content.TrimEnd() + "`r`n$Key=$Value`r`n"
|
||
}
|
||
Write-Utf8NoBom -Path $EnvFile -Content $content
|
||
}
|
||
|
||
function New-SecureToken {
|
||
$bytes = New-Object byte[] 32
|
||
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
|
||
try { $rng.GetBytes($bytes) } finally { $rng.Dispose() }
|
||
return (($bytes | ForEach-Object { $_.ToString('x2') }) -join '')
|
||
}
|
||
|
||
function Test-Port([string]$Text) {
|
||
$port = 0
|
||
return [int]::TryParse($Text, [ref]$port) -and $port -ge 1024 -and $port -le 65535
|
||
}
|
||
|
||
function Set-Configuration([string]$Target, [string]$Access, [int]$Port, [string]$PublicUrl, [bool]$FluxEnabled) {
|
||
Ensure-EnvFile
|
||
$token = Get-EnvValue 'VENDOO_SETUP_TOKEN'
|
||
if ([string]::IsNullOrWhiteSpace($token)) {
|
||
$token = New-SecureToken
|
||
Set-EnvValue 'VENDOO_SETUP_TOKEN' $token
|
||
}
|
||
|
||
Set-EnvValue 'PORT' ([string]$Port)
|
||
Set-EnvValue 'VENDOO_HOST_PORT' ([string]$Port)
|
||
Set-EnvValue 'VENDOO_REQUIRE_SETUP_TOKEN' 'true'
|
||
Set-EnvValue 'VENDOO_COOKIE_SAMESITE' 'lax'
|
||
Set-EnvValue 'LOCAL_IMAGE_ENABLED' ($(if ($FluxEnabled) { 'true' } else { 'false' }))
|
||
|
||
if ($Target -eq 'windows-local') {
|
||
Set-EnvValue 'VENDOO_BIND_HOST' ($(if ($Access -eq 'LAN') { '0.0.0.0' } else { '127.0.0.1' }))
|
||
Set-EnvValue 'VENDOO_PUBLISH_ADDRESS' '127.0.0.1'
|
||
Set-EnvValue 'VENDOO_COOKIE_SECURE' 'auto'
|
||
Set-EnvValue 'VENDOO_TRUST_PROXY' 'false'
|
||
Set-EnvValue 'LOCAL_IMAGE_URL' 'http://127.0.0.1:8188'
|
||
} else {
|
||
Set-EnvValue 'VENDOO_BIND_HOST' '0.0.0.0'
|
||
Set-EnvValue 'VENDOO_PUBLISH_ADDRESS' ($(if ($Access -eq 'LAN') { '0.0.0.0' } else { '127.0.0.1' }))
|
||
Set-EnvValue 'VENDOO_COOKIE_SECURE' 'auto'
|
||
Set-EnvValue 'VENDOO_TRUST_PROXY' 'false'
|
||
Set-EnvValue 'LOCAL_IMAGE_URL' 'http://host.docker.internal:8188'
|
||
}
|
||
|
||
Set-EnvValue 'PUBLIC_BASE_URL' ($(if ([string]::IsNullOrWhiteSpace($PublicUrl)) { '' } else { $PublicUrl.Trim().TrimEnd('/') }))
|
||
|
||
Write-Utf8NoBom -Path $ModeFile -Content ($Target + "`n")
|
||
try { [System.Windows.Forms.Clipboard]::SetText($token) } catch {}
|
||
return $token
|
||
}
|
||
|
||
$form = New-Object System.Windows.Forms.Form
|
||
$form.Text = 'Vendoo 1.36.0 – Installation'
|
||
$form.StartPosition = 'CenterScreen'
|
||
$form.Size = New-Object System.Drawing.Size(720, 620)
|
||
$form.MinimumSize = New-Object System.Drawing.Size(720, 620)
|
||
$form.Font = New-Object System.Drawing.Font('Segoe UI', 10)
|
||
$form.BackColor = [System.Drawing.Color]::FromArgb(246, 248, 247)
|
||
|
||
$title = New-Object System.Windows.Forms.Label
|
||
$title.Text = 'Vendoo installieren'
|
||
$title.Font = New-Object System.Drawing.Font('Segoe UI Semibold', 22)
|
||
$title.AutoSize = $true
|
||
$title.Location = New-Object System.Drawing.Point(34, 24)
|
||
$form.Controls.Add($title)
|
||
|
||
$sub = New-Object System.Windows.Forms.Label
|
||
$sub.Text = 'Alle Pflichtwerte werden geprüft und automatisch gespeichert. Es müssen keine Dateien bearbeitet werden.'
|
||
$sub.AutoSize = $false
|
||
$sub.Size = New-Object System.Drawing.Size(640, 48)
|
||
$sub.Location = New-Object System.Drawing.Point(38, 72)
|
||
$sub.ForeColor = [System.Drawing.Color]::FromArgb(70, 80, 76)
|
||
$form.Controls.Add($sub)
|
||
|
||
function Add-Label([string]$Text, [int]$Y) {
|
||
$l = New-Object System.Windows.Forms.Label
|
||
$l.Text = $Text
|
||
$l.AutoSize = $true
|
||
$l.Location = New-Object System.Drawing.Point(40, $Y)
|
||
$form.Controls.Add($l)
|
||
return $l
|
||
}
|
||
|
||
Add-Label 'Installationsart' 132 | Out-Null
|
||
$target = New-Object System.Windows.Forms.ComboBox
|
||
$target.DropDownStyle = 'DropDownList'
|
||
$target.Items.AddRange(@('Lokal auf diesem Windows-PC', 'Docker Desktop auf diesem Windows-PC'))
|
||
$target.SelectedIndex = 0
|
||
$target.Location = New-Object System.Drawing.Point(40, 158)
|
||
$target.Size = New-Object System.Drawing.Size(620, 34)
|
||
$form.Controls.Add($target)
|
||
|
||
Add-Label 'Erreichbarkeit' 208 | Out-Null
|
||
$access = New-Object System.Windows.Forms.ComboBox
|
||
$access.DropDownStyle = 'DropDownList'
|
||
$access.Items.AddRange(@('Nur auf diesem PC (empfohlen)', 'Im privaten LAN erreichbar'))
|
||
$access.SelectedIndex = 0
|
||
$access.Location = New-Object System.Drawing.Point(40, 234)
|
||
$access.Size = New-Object System.Drawing.Size(620, 34)
|
||
$form.Controls.Add($access)
|
||
|
||
Add-Label 'Port' 284 | Out-Null
|
||
$portBox = New-Object System.Windows.Forms.NumericUpDown
|
||
$portBox.Minimum = 1024
|
||
$portBox.Maximum = 65535
|
||
$portBox.Value = 8124
|
||
$portBox.Location = New-Object System.Drawing.Point(40, 310)
|
||
$portBox.Size = New-Object System.Drawing.Size(160, 34)
|
||
$form.Controls.Add($portBox)
|
||
|
||
Add-Label 'Öffentliche/LAN-Basisadresse (optional)' 358 | Out-Null
|
||
$publicUrl = New-Object System.Windows.Forms.TextBox
|
||
$publicUrl.Location = New-Object System.Drawing.Point(40, 384)
|
||
$publicUrl.Size = New-Object System.Drawing.Size(620, 34)
|
||
$form.Controls.Add($publicUrl)
|
||
|
||
$flux = New-Object System.Windows.Forms.CheckBox
|
||
$flux.Text = 'Lokales FLUX Studio aktivieren (ComfyUI bleibt ein getrennter Dienst)'
|
||
$flux.AutoSize = $true
|
||
$flux.Location = New-Object System.Drawing.Point(40, 438)
|
||
$form.Controls.Add($flux)
|
||
|
||
$hint = New-Object System.Windows.Forms.Label
|
||
$hint.Text = 'AI- und Marktplatz-Schlüssel werden später bequem unter Vendoo → Einstellungen eingetragen.'
|
||
$hint.AutoSize = $false
|
||
$hint.Size = New-Object System.Drawing.Size(620, 42)
|
||
$hint.Location = New-Object System.Drawing.Point(40, 474)
|
||
$hint.ForeColor = [System.Drawing.Color]::FromArgb(70, 80, 76)
|
||
$form.Controls.Add($hint)
|
||
|
||
$install = New-Object System.Windows.Forms.Button
|
||
$install.Text = 'Installieren'
|
||
$install.Size = New-Object System.Drawing.Size(180, 44)
|
||
$install.Location = New-Object System.Drawing.Point(480, 520)
|
||
$install.BackColor = [System.Drawing.Color]::FromArgb(32, 112, 78)
|
||
$install.ForeColor = [System.Drawing.Color]::White
|
||
$install.FlatStyle = 'Flat'
|
||
$form.Controls.Add($install)
|
||
|
||
$advanced = New-Object System.Windows.Forms.Button
|
||
$advanced.Text = 'Konsolenmenü'
|
||
$advanced.Size = New-Object System.Drawing.Size(150, 44)
|
||
$advanced.Location = New-Object System.Drawing.Point(40, 520)
|
||
$form.Controls.Add($advanced)
|
||
|
||
$cancel = New-Object System.Windows.Forms.Button
|
||
$cancel.Text = 'Abbrechen'
|
||
$cancel.Size = New-Object System.Drawing.Size(130, 44)
|
||
$cancel.Location = New-Object System.Drawing.Point(330, 520)
|
||
$form.Controls.Add($cancel)
|
||
|
||
$target.Add_SelectedIndexChanged({
|
||
if ($target.SelectedIndex -eq 1) {
|
||
$hint.Text = 'Docker Desktop muss gestartet sein. AI- und Marktplatz-Schlüssel werden später in Vendoo gepflegt.'
|
||
} else {
|
||
$hint.Text = 'AI- und Marktplatz-Schlüssel werden später bequem unter Vendoo → Einstellungen eingetragen.'
|
||
}
|
||
})
|
||
|
||
$advanced.Add_Click({
|
||
$form.Hide()
|
||
Start-Process powershell.exe -ArgumentList @('-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',('"' + $SetupScript + '"'),'menu') -Wait
|
||
$form.Close()
|
||
})
|
||
|
||
$cancel.Add_Click({ $form.Close() })
|
||
|
||
$install.Add_Click({
|
||
try {
|
||
$install.Enabled = $false
|
||
$targetMode = if ($target.SelectedIndex -eq 1) { 'docker' } else { 'windows-local' }
|
||
$accessMode = if ($access.SelectedIndex -eq 1) { 'LAN' } else { 'LOCAL' }
|
||
$url = $publicUrl.Text.Trim()
|
||
if ($url -and $url -notmatch '^https?://') { throw 'Die Basisadresse muss mit http:// oder https:// beginnen.' }
|
||
if ($accessMode -eq 'LAN' -and [string]::IsNullOrWhiteSpace($url)) {
|
||
$answer = [System.Windows.Forms.MessageBox]::Show('Für QR-Uploads ist später eine LAN-Adresse sinnvoll. Trotzdem ohne Basisadresse fortfahren?', 'LAN-Adresse fehlt', 'YesNo', 'Warning')
|
||
if ($answer -ne 'Yes') { return }
|
||
}
|
||
$token = Set-Configuration -Target $targetMode -Access $accessMode -Port ([int]$portBox.Value) -PublicUrl $url -FluxEnabled $flux.Checked
|
||
[System.Windows.Forms.MessageBox]::Show("Die Konfiguration ist gespeichert. Der Erst-Admin-Code wurde in die Zwischenablage kopiert.`r`n`r`n$token`r`n`r`nBewahre ihn bis zur ersten Anmeldung sicher auf.", 'Vendoo vorbereitet', 'OK', 'Information') | Out-Null
|
||
$form.Hide()
|
||
$command = if ($targetMode -eq 'docker') { 'install-docker' } else { 'install-local' }
|
||
$proc = Start-Process powershell.exe -ArgumentList @('-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',('"' + $SetupScript + '"'),$command) -Wait -PassThru
|
||
if ($proc.ExitCode -eq 0) {
|
||
Start-Process ("http://127.0.0.1:{0}/login.html" -f [int]$portBox.Value)
|
||
} else {
|
||
[System.Windows.Forms.MessageBox]::Show("Die Installation wurde mit Exit-Code $($proc.ExitCode) beendet. Das Konsolenfenster enthält die Details.", 'Installation nicht abgeschlossen', 'OK', 'Error') | Out-Null
|
||
}
|
||
$form.Close()
|
||
} catch {
|
||
[System.Windows.Forms.MessageBox]::Show($_.Exception.Message, 'Eingabe prüfen', 'OK', 'Warning') | Out-Null
|
||
} finally {
|
||
$install.Enabled = $true
|
||
}
|
||
})
|
||
|
||
[void]$form.ShowDialog()
|