mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-08-11 00:30:12 -04:00
#1558: cached-as-base push_status only forced gcode_state=IDLE while letting the real printer's live-progress fields (mc_percent, stg_cur, layer_num, ...) leak through. Bambu Studio's Send pre-flight read them as busy and refused. The cached branch now overrides the activity-field set the same way it already overrode storage indicators (#1228) and protocol fields. Same bundle ships a multi-round VP audit that found adjacent bugs in the same family: - #1558: cached branch zeroes mc_print_stage / mc_percent / mc_remaining_time / stg / stg_cur / layer_num / total_layer_num / print_error - MQTT auth: per-IP rate-limit (5/60s lockout), hmac.compare_digest, access_code redacted in DEBUG log - FTP cmd_STOR streams chunks to disk + 4 GiB cap (was buffering whole upload) - Sticky-keys allowlist extended with upgrade_state / xcam / hw_switch_state / nozzle_diameter / nozzle_type / online / ams_status - _pending_files cleanup in finally for archive / queue / dispatch handlers - _add_to_print_queue position uses MAX+1 (was hardcoded 1) - DELETE VP removes orphan PendingUpload rows + upload_dir from disk - Per-VP cert regenerates on shared-CA rotation (real signature verification, not DN match) - DHCP target-IP refresh + queue_force_color_match toggle now restart proxy VPs - Per-slicer bridge-response routing (multi-slicer cross-leak fix via sequence_id map) - Child-service readiness barrier (FTP / MQTT / Bind / SSDP) — no false is_running before sockets bind - H2D Pro O1E / O2D model codes added (experimental, needs field confirmation) - FTP passive port range widened 50000-51000; docker-compose + wiki updated - VP refresh_loop crash now unbinds raw_message_handler; tailscale catches asyncio.TimeoutError; SlicerProxyManager lifecycle hardening
345 lines
12 KiB
PowerShell
345 lines
12 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
BamBuddy Docker installation script for Windows (Docker Desktop).
|
|
|
|
.DESCRIPTION
|
|
Mirrors install/docker-install.sh for Windows. Verifies Docker Desktop is
|
|
installed and running, downloads docker-compose.yml, rewrites it for
|
|
Docker Desktop (no host networking), writes a .env, and starts the
|
|
container.
|
|
|
|
.PARAMETER InstallPath
|
|
Installation directory. Default: $env:USERPROFILE\bambuddy
|
|
|
|
.PARAMETER Port
|
|
Port to expose. Default: 8000
|
|
|
|
.PARAMETER TimeZone
|
|
IANA timezone string (e.g. Europe/Berlin). Default: derived from
|
|
Get-TimeZone or UTC.
|
|
|
|
.PARAMETER Build
|
|
Build the image from source instead of pulling the pre-built image.
|
|
|
|
.PARAMETER Yes
|
|
Non-interactive mode; accept defaults.
|
|
|
|
.EXAMPLE
|
|
Interactive install (cmd or PowerShell):
|
|
powershell -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/maziggy/bambuddy/main/install/docker-install.ps1 -OutFile docker-install.ps1; .\docker-install.ps1"
|
|
|
|
.EXAMPLE
|
|
Unattended install:
|
|
.\docker-install.ps1 -InstallPath C:\bambuddy -Port 8080 -TimeZone Europe/Berlin -Yes
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$InstallPath,
|
|
[int]$Port,
|
|
[string]$TimeZone,
|
|
[switch]$Build,
|
|
[switch]$Yes,
|
|
[switch]$Help
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
|
|
# --- Helpers --------------------------------------------------------------
|
|
|
|
function Write-Banner {
|
|
Write-Host ''
|
|
Write-Host '==========================================================' -ForegroundColor Cyan
|
|
Write-Host ' Bambuddy - Docker Installation (Windows)' -ForegroundColor Cyan
|
|
Write-Host '==========================================================' -ForegroundColor Cyan
|
|
Write-Host ''
|
|
}
|
|
|
|
function Info { param($m) Write-Host "[INFO] $m" -ForegroundColor Blue }
|
|
function Ok { param($m) Write-Host "[OK] $m" -ForegroundColor Green }
|
|
function Warn { param($m) Write-Host "[WARN] $m" -ForegroundColor Yellow }
|
|
function Fail { param($m) Write-Host "[ERR] $m" -ForegroundColor Red }
|
|
|
|
function Read-Default {
|
|
param([string]$Prompt, [string]$Default)
|
|
if ($Yes) { return $Default }
|
|
$val = Read-Host "$Prompt [$Default]"
|
|
if ([string]::IsNullOrWhiteSpace($val)) { return $Default }
|
|
return $val
|
|
}
|
|
|
|
function Read-YesNo {
|
|
param([string]$Prompt, [string]$Default = 'y')
|
|
if ($Yes) { return ($Default -eq 'y') }
|
|
$hint = if ($Default -eq 'y') { '[Y/n]' } else { '[y/N]' }
|
|
while ($true) {
|
|
$val = Read-Host "$Prompt $hint"
|
|
if ([string]::IsNullOrWhiteSpace($val)) { $val = $Default }
|
|
switch -Regex ($val.Trim().ToLower()) {
|
|
'^(y|yes)$' { return $true }
|
|
'^(n|no)$' { return $false }
|
|
default { Write-Host 'Please answer yes or no.' }
|
|
}
|
|
}
|
|
}
|
|
|
|
function Show-Help {
|
|
Get-Help $PSCommandPath -Full
|
|
exit 0
|
|
}
|
|
|
|
# --- Detection ------------------------------------------------------------
|
|
|
|
function Test-Docker {
|
|
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
|
|
Fail 'Docker is not installed.'
|
|
Write-Host ''
|
|
Write-Host ' Install Docker Desktop for Windows:' -ForegroundColor Yellow
|
|
Write-Host ' https://www.docker.com/products/docker-desktop' -ForegroundColor Cyan
|
|
Write-Host ''
|
|
Write-Host ' After install, launch Docker Desktop and re-run this script.' -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
|
|
Info 'Docker found, checking daemon...'
|
|
try {
|
|
docker info --format '{{.ServerVersion}}' *> $null
|
|
if ($LASTEXITCODE -ne 0) { throw 'docker info failed' }
|
|
} catch {
|
|
Fail 'Docker daemon is not reachable. Is Docker Desktop running?'
|
|
Write-Host ''
|
|
Write-Host ' Open Docker Desktop, wait for the whale icon to settle,' -ForegroundColor Yellow
|
|
Write-Host ' then re-run this script.' -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
Ok 'Docker daemon is running'
|
|
|
|
docker compose version *> $null
|
|
if ($LASTEXITCODE -eq 0) {
|
|
$script:DockerCompose = 'docker compose'
|
|
Ok 'Found Docker Compose v2'
|
|
} elseif (Get-Command docker-compose -ErrorAction SilentlyContinue) {
|
|
$script:DockerCompose = 'docker-compose'
|
|
Ok 'Found Docker Compose v1'
|
|
} else {
|
|
Fail 'Docker Compose not found. Install Docker Desktop 4.x+ (ships compose v2).'
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
function Get-DefaultTimeZone {
|
|
if ($TimeZone) { return $TimeZone }
|
|
try {
|
|
$tz = (Get-TimeZone).Id
|
|
# Get-TimeZone returns Windows IDs ("W. Europe Standard Time"). Docker
|
|
# expects IANA. Fall back to UTC if we can't translate.
|
|
$iana = ConvertTo-IanaTimeZone $tz
|
|
if ($iana) { return $iana }
|
|
} catch {}
|
|
return 'UTC'
|
|
}
|
|
|
|
function ConvertTo-IanaTimeZone {
|
|
param([string]$WindowsId)
|
|
# Minimal mapping of common Windows IDs to IANA. Users can override via -TimeZone.
|
|
$map = @{
|
|
'UTC' = 'UTC'
|
|
'GMT Standard Time' = 'Europe/London'
|
|
'W. Europe Standard Time' = 'Europe/Berlin'
|
|
'Central European Standard Time' = 'Europe/Warsaw'
|
|
'Romance Standard Time' = 'Europe/Paris'
|
|
'Russian Standard Time' = 'Europe/Moscow'
|
|
'Eastern Standard Time' = 'America/New_York'
|
|
'Central Standard Time' = 'America/Chicago'
|
|
'Mountain Standard Time' = 'America/Denver'
|
|
'Pacific Standard Time' = 'America/Los_Angeles'
|
|
'AUS Eastern Standard Time' = 'Australia/Sydney'
|
|
'Tokyo Standard Time' = 'Asia/Tokyo'
|
|
'China Standard Time' = 'Asia/Shanghai'
|
|
'India Standard Time' = 'Asia/Kolkata'
|
|
}
|
|
return $map[$WindowsId]
|
|
}
|
|
|
|
function Get-LanIp {
|
|
try {
|
|
$ip = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction Stop |
|
|
Where-Object {
|
|
$_.IPAddress -notmatch '^(127\.|169\.254\.)' -and
|
|
$_.PrefixOrigin -ne 'WellKnown' -and
|
|
$_.InterfaceAlias -notmatch '^(vEthernet|Loopback)'
|
|
} | Select-Object -First 1
|
|
if ($ip) { return $ip.IPAddress }
|
|
} catch {}
|
|
return '<your-ip>'
|
|
}
|
|
|
|
# --- Steps ----------------------------------------------------------------
|
|
|
|
function Initialize-InstallDir {
|
|
Info "Creating installation directory: $InstallPath"
|
|
if (-not (Test-Path $InstallPath)) {
|
|
New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null
|
|
}
|
|
Set-Location $InstallPath
|
|
Ok "Directory ready: $InstallPath"
|
|
}
|
|
|
|
function Get-ComposeFile {
|
|
Info 'Downloading docker-compose.yml...'
|
|
|
|
if ($Build) {
|
|
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
Fail 'git is required for --Build but was not found. Install Git for Windows: https://git-scm.com/download/win'
|
|
exit 1
|
|
}
|
|
if (Test-Path '.git') {
|
|
Info 'Existing repository found, updating...'
|
|
git fetch origin
|
|
git reset --hard origin/main
|
|
} else {
|
|
git clone https://github.com/maziggy/bambuddy.git .
|
|
}
|
|
} else {
|
|
Invoke-WebRequest `
|
|
-Uri 'https://raw.githubusercontent.com/maziggy/bambuddy/main/docker-compose.yml' `
|
|
-OutFile 'docker-compose.yml' `
|
|
-UseBasicParsing
|
|
}
|
|
|
|
Ok 'docker-compose.yml ready'
|
|
}
|
|
|
|
function Write-EnvFile {
|
|
Info 'Writing .env...'
|
|
$envBody = @"
|
|
# BamBuddy Docker Configuration
|
|
# Generated by docker-install.ps1 on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
|
|
|
|
PORT=$Port
|
|
TZ=$TimeZone
|
|
"@
|
|
[System.IO.File]::WriteAllText((Join-Path $InstallPath '.env'), $envBody)
|
|
Ok '.env written'
|
|
}
|
|
|
|
function Update-ComposeForDockerDesktop {
|
|
# Docker Desktop on Windows does not support network_mode: host. Comment
|
|
# it out and uncomment the port mappings. Mirrors what the bash script
|
|
# does on macOS via sed.
|
|
$path = Join-Path $InstallPath 'docker-compose.yml'
|
|
Info 'Rewriting compose for Docker Desktop (no host networking)...'
|
|
|
|
$content = [System.IO.File]::ReadAllText($path)
|
|
Copy-Item $path "$path.bak" -Force
|
|
|
|
$content = $content -replace '(?m)^(\s*)network_mode: host', '#$1network_mode: host'
|
|
$content = $content -replace '(?m)^(\s*)#ports:', '$1ports:'
|
|
$content = $content -replace '(?m)^(\s*)#(\s*)- "\$\{PORT:-8000\}:8000"', '$1$2- "$${PORT:-8000}:8000"'
|
|
|
|
[System.IO.File]::WriteAllText($path, $content)
|
|
Warn 'Printer auto-discovery (SSDP) does NOT work on Docker Desktop. Add printers manually by IP.'
|
|
Warn 'Virtual Printer ports (322, 990, 2024-2026, 3000/3002, 6000, 8883, 50000-51000) stay commented out.'
|
|
Warn 'If you plan to use a Virtual Printer, edit docker-compose.yml and uncomment the relevant `- "PORT:PORT"` lines under `ports:`.'
|
|
}
|
|
|
|
function Start-Bambuddy {
|
|
Info 'Starting Bambuddy container...'
|
|
if ($Build) {
|
|
& cmd /c "$DockerCompose up -d --build"
|
|
} else {
|
|
& cmd /c "$DockerCompose up -d"
|
|
}
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Fail 'Container start failed. Inspect logs with:'
|
|
Write-Host " cd $InstallPath" -ForegroundColor Yellow
|
|
Write-Host " $DockerCompose logs bambuddy" -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
|
|
Info 'Waiting for container to be Up...'
|
|
$attempts = 0
|
|
while ($attempts -lt 15) {
|
|
Start-Sleep -Seconds 2
|
|
$ps = & cmd /c "$DockerCompose ps" 2>&1
|
|
if ($ps -match 'Up') { Ok 'Bambuddy container is running'; return }
|
|
if ($ps -match 'Exited') {
|
|
Fail 'Container exited unexpectedly.'
|
|
Write-Host " $DockerCompose logs bambuddy" -ForegroundColor Yellow
|
|
exit 1
|
|
}
|
|
$attempts++
|
|
}
|
|
Warn "Container may still be starting. Check with: $DockerCompose ps"
|
|
}
|
|
|
|
# --- Main -----------------------------------------------------------------
|
|
|
|
if ($Help) { Show-Help }
|
|
|
|
Write-Banner
|
|
|
|
Info 'Detecting environment...'
|
|
Test-Docker
|
|
|
|
# Defaults
|
|
if (-not $InstallPath) { $InstallPath = Read-Default 'Installation directory' (Join-Path $env:USERPROFILE 'bambuddy') }
|
|
if (-not $Port) { $Port = [int](Read-Default 'Port to expose' '8000') }
|
|
if (-not $TimeZone) {
|
|
$detected = Get-DefaultTimeZone
|
|
$TimeZone = Read-Default 'Timezone (IANA)' $detected
|
|
}
|
|
if (-not $Build -and -not $Yes) {
|
|
if (Read-YesNo 'Build from source? (No = use pre-built image)' 'n') { $Build = $true }
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host 'Installation Summary' -ForegroundColor Cyan
|
|
Write-Host '-----------------------------------------'
|
|
Write-Host " Install path: $InstallPath"
|
|
Write-Host " Port: $Port"
|
|
Write-Host " Timezone: $TimeZone"
|
|
Write-Host " Build source: $Build"
|
|
Write-Host ''
|
|
|
|
if (-not (Read-YesNo 'Proceed with installation?' 'y')) {
|
|
Write-Host 'Cancelled.' -ForegroundColor Yellow
|
|
exit 0
|
|
}
|
|
|
|
Initialize-InstallDir
|
|
Get-ComposeFile
|
|
Write-EnvFile
|
|
Update-ComposeForDockerDesktop
|
|
Start-Bambuddy
|
|
|
|
$lanIp = Get-LanIp
|
|
|
|
Write-Host ''
|
|
Write-Host '==========================================================' -ForegroundColor Green
|
|
Write-Host ' Installation Complete!' -ForegroundColor Green
|
|
Write-Host '==========================================================' -ForegroundColor Green
|
|
Write-Host ''
|
|
Write-Host " Access Bambuddy: http://localhost:$Port" -ForegroundColor Cyan
|
|
Write-Host " http://${lanIp}:$Port (from other devices)" -ForegroundColor Cyan
|
|
Write-Host ''
|
|
Write-Host ' Manage container:'
|
|
Write-Host " Status: cd `"$InstallPath`"; $DockerCompose ps"
|
|
Write-Host " Logs: cd `"$InstallPath`"; $DockerCompose logs -f bambuddy"
|
|
Write-Host " Stop: cd `"$InstallPath`"; $DockerCompose down"
|
|
Write-Host " Start: cd `"$InstallPath`"; $DockerCompose up -d"
|
|
Write-Host " Restart: cd `"$InstallPath`"; $DockerCompose restart"
|
|
Write-Host ''
|
|
Write-Host ' Update Bambuddy:'
|
|
if ($Build) {
|
|
Write-Host " cd `"$InstallPath`"; git pull; $DockerCompose up -d --build"
|
|
} else {
|
|
Write-Host " cd `"$InstallPath`"; $DockerCompose pull; $DockerCompose up -d"
|
|
}
|
|
Write-Host ''
|
|
Write-Host ' Documentation: https://wiki.bambuddy.cool' -ForegroundColor Cyan
|
|
Write-Host ''
|
|
Write-Host ' Note: Printer discovery is unavailable on Docker Desktop.' -ForegroundColor Yellow
|
|
Write-Host ' Add your printers manually by IP address in the UI.' -ForegroundColor Yellow
|
|
Write-Host ''
|