Browse Source

Fix native install misdetected as Docker in LXC containers

  The _is_docker_environment() fallback assumed Docker when .git/ was
  absent, which is also true for native installs in Proxmox LXC
  containers. Replace the .git/ fallback with a check of
  /run/systemd/container (only matches docker/podman/oci, not lxc).
maziggy 2 months ago
parent
commit
a077fd138d
2 changed files with 11 additions and 2 deletions
  1. 1 0
      CHANGELOG.md
  2. 10 2
      backend/app/api/routes/updates.py

+ 1 - 0
CHANGELOG.md

@@ -34,6 +34,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Token-Based Auth for Media Endpoints** — Camera streams, snapshots, thumbnails, timelapse videos, photos, QR codes, and cover images served via `<img>`/`<video>` tags now require a stream token query parameter (`?token=xxx`) when authentication is enabled. Previously these endpoints were unauthenticated because browser media elements cannot send `Authorization` headers. The frontend obtains a 60-minute reusable token via `POST /printers/camera/stream-token` (requires `CAMERA_VIEW` permission) and automatically appends it to all media URLs. Affects endpoints in camera, archives, library, printers, print-log, and external-links routes. When auth is disabled (default for local installs), behavior is unchanged — no token required. Reported by Sacha Vaudey via security email.
 
 ### Fixed
+- **Native Install Misdetected as Docker in LXC Containers** — The update check falsely identified native installs as Docker when running inside Proxmox LXC containers. The detection logic used `.git/` directory absence as a Docker fallback, but LXC containers may also lack `.git/` depending on how the install was deployed. Replaced the `.git/` fallback with a proper check of `/run/systemd/container` which only matches Docker/Podman/OCI runtimes, not LXC. Native installs in LXC containers now correctly show the in-app update button instead of Docker Compose instructions.
 - **Print Fails on Files With Spaces in Name** ([#824](https://github.com/maziggy/bambuddy/issues/824)) — Printing files with spaces in their filename (e.g. "Junktion Box PRO 90.3mf") caused the printer to silently ignore the print command and remain IDLE. The FTP upload succeeded, but the MQTT print command's `url` field (`ftp://file name.3mf`) contained unencoded spaces that the firmware couldn't parse. Fixed by replacing spaces with underscores in the remote filename before upload. Reported by @benjamdev.
 - **SpoolBuddy Low Filament Warning Missing Slot Number** — The status bar low filament warning showed "AMS B" instead of the specific slot like "B2". Now uses `formatSlotLabel` to display the full slot label (e.g. "Low Filament: PLA (B2) - 4% remaining").
 - **SpoolBuddy Read Tag Diagnostic Fails on NTAG Tags** — The `read_tag.py` diagnostic script had five issues preventing NTAG reads: (1) SAK `0x04` (MIFARE Ultralight family) was rejected as "unsupported tag type" — now accepts both `0x00` and `0x04`. (2) `ntag_read_pages` had TX CRC off (should be on per NTAG spec), no Crypto1 clear, and no IDLE→TRANSCEIVE state reset. (3) The PN5180 enters an unrecoverable state after an NTAG READ command — added full GPIO hardware reset between each 4-page batch. (4) Reading past the end of smaller tags (MIFARE Ultralight has 16 pages vs NTAG's 44+) caused a hard failure — now returns partial data gracefully. (5) `ntag_write_page`/`ntag_write_pages` had the same stale CRC/state issues plus unreliable ACK checking and post-write verification — synced with daemon.

+ 10 - 2
backend/app/api/routes/updates.py

@@ -42,8 +42,16 @@ def _is_docker_environment() -> bool:
                 return True
     except (FileNotFoundError, PermissionError):
         pass  # cgroup file unavailable; continue with other detection methods
-    git_dir = settings.base_dir / ".git"
-    return not git_dir.exists()
+    # Check container runtime hint (systemd sets this for Docker/podman,
+    # but NOT for LXC/LXD — avoids false positives on Proxmox containers)
+    try:
+        with open("/run/systemd/container") as f:
+            runtime = f.read().strip()
+            if runtime in ("docker", "podman", "oci"):
+                return True
+    except (FileNotFoundError, PermissionError):
+        pass
+    return False
 
 
 def _find_executable(name: str) -> str | None: