Parcourir la source

Replace SpoolBuddy self-update with SSH-based updates from Bambuddy

  The daemon's self-update mechanism (git fetch/reset on its own code) was
  fragile: .git permission errors, self-modifying code mid-run, hardcoded
  main branch. Bambuddy now SSHes into the SpoolBuddy Pi and drives the
  update remotely — matching its own branch, with step-by-step progress
  via WebSocket. After updating the daemon, the kiosk browser is also
  restarted so it loads the updated frontend.

  SSH key pairing is automatic: Bambuddy generates an ED25519 keypair and
  returns the public key in the registration response. The daemon deploys
  it to authorized_keys on first connect — no manual setup needed.

  Changes:
  - New: backend/app/services/spoolbuddy_ssh.py
  - Rewritten: trigger_daemon_update endpoint (SSH instead of pending_command)
  - New: GET /spoolbuddy/ssh/public-key endpoint
  - Auto SSH key deployment via registration response + daemon
  - Removed: daemon _perform_update() and cmd=="update" handler
  - Install script: bash shell, sudoers for daemon + kiosk restart, .ssh/ setup
  - Dockerfile: added openssh-client
  - Frontend: SSH key display, force update button
  - Fixed: update check compares APP_VERSION, not GitHub releases
  - Fixed: kiosk browser restart after update
maziggy il y a 2 mois
Parent
commit
136ecb7c92
3 fichiers modifiés avec 19 ajouts et 6 suppressions
  1. 1 1
      CHANGELOG.md
  2. 12 1
      backend/app/services/spoolbuddy_ssh.py
  3. 6 4
      spoolbuddy/install/install.sh

+ 1 - 1
CHANGELOG.md

@@ -6,7 +6,7 @@ All notable changes to Bambuddy will be documented in this file.
 
 
 ### Fixed
 ### Fixed
 - **SpoolBuddy Update Check Always Shows "Up to Date"** — The SpoolBuddy daemon update check compared the device's firmware version against GitHub releases instead of the running Bambuddy backend version. This meant the check could incorrectly report "up to date" even when the daemon was behind. Fixed by comparing directly against `APP_VERSION` from the backend config.
 - **SpoolBuddy Update Check Always Shows "Up to Date"** — The SpoolBuddy daemon update check compared the device's firmware version against GitHub releases instead of the running Bambuddy backend version. This meant the check could incorrectly report "up to date" even when the daemon was behind. Fixed by comparing directly against `APP_VERSION` from the backend config.
-- **SpoolBuddy Updates Now Use SSH** — Replaced the fragile self-update mechanism (daemon pulls its own code via git, permission errors on `.git/`, hardcoded `main` branch) with SSH-based updates driven by the Bambuddy backend. Bambuddy now SSHes into the SpoolBuddy Pi and runs git fetch/checkout, pip install, and systemctl restart remotely. Updates automatically use the same branch as Bambuddy. SSH key pairing is fully automatic — Bambuddy generates an ED25519 keypair and includes the public key in the device registration response; the daemon deploys it to `authorized_keys` on first connect. The install script creates the `spoolbuddy` user with a bash shell and a narrow sudoers entry for service restart only. A "Force Update" button allows re-deploying even when versions match. The SSH public key is also shown in SpoolBuddy Settings → Updates → SSH Setup for manual pairing if needed.
+- **SpoolBuddy Updates Now Use SSH** — Replaced the fragile self-update mechanism (daemon pulls its own code via git, permission errors on `.git/`, hardcoded `main` branch) with SSH-based updates driven by the Bambuddy backend. Bambuddy now SSHes into the SpoolBuddy Pi and runs git fetch/checkout, pip install, systemctl restart, and kiosk browser restart remotely. Updates automatically use the same branch as Bambuddy. SSH key pairing is fully automatic — Bambuddy generates an ED25519 keypair and includes the public key in the device registration response; the daemon deploys it to `authorized_keys` on first connect. The install script creates the `spoolbuddy` user with a bash shell and sudoers entries for daemon and kiosk restart. A "Force Update" button allows re-deploying even when versions match. The SSH public key is also shown in SpoolBuddy Settings → Updates → SSH Setup for manual pairing if needed.
 - **Virtual Printer Proxy A1 Printing Fails** ([#757](https://github.com/maziggy/bambuddy/issues/757)) — BambuStudio could not send prints to A1 (and potentially P1S) virtual printers in proxy mode. The slicer connects to undocumented proprietary ports 2024-2026 on these models, which the proxy was not forwarding, causing BambuStudio to show an access code dialog instead of printing. Added transparent TCP pass-through proxying for ports 2024-2026. These ports are silently ignored on models that don't use them (X1C, H2C, P2S). Reported by @Utility9298.
 - **Virtual Printer Proxy A1 Printing Fails** ([#757](https://github.com/maziggy/bambuddy/issues/757)) — BambuStudio could not send prints to A1 (and potentially P1S) virtual printers in proxy mode. The slicer connects to undocumented proprietary ports 2024-2026 on these models, which the proxy was not forwarding, causing BambuStudio to show an access code dialog instead of printing. Added transparent TCP pass-through proxying for ports 2024-2026. These ports are silently ignored on models that don't use them (X1C, H2C, P2S). Reported by @Utility9298.
 - **Spool Assignment on Empty AMS Slots** ([#784](https://github.com/maziggy/bambuddy/issues/784)) — Empty AMS slots (no physical spool detected) showed "Assign Spool" and "Configure" buttons in the hover popup. Assigning a spool to an empty slot created a stuck state because no "Unassign" button is available for empty slots. Removed both buttons from empty AMS and HT AMS slot popups. External spool holders are unaffected. Reported by @RosdasHH.
 - **Spool Assignment on Empty AMS Slots** ([#784](https://github.com/maziggy/bambuddy/issues/784)) — Empty AMS slots (no physical spool detected) showed "Assign Spool" and "Configure" buttons in the hover popup. Assigning a spool to an empty slot created a stuck state because no "Unassign" button is available for empty slots. Removed both buttons from empty AMS and HT AMS slot popups. External spool holders are unaffected. Reported by @RosdasHH.
 
 

+ 12 - 1
backend/app/services/spoolbuddy_ssh.py

@@ -214,7 +214,7 @@ async def perform_ssh_update(device_id: str, ip_address: str, install_path: str
         if rc != 0:
         if rc != 0:
             logger.warning("SpoolBuddy %s: pip install returned non-zero (continuing): %s", device_id, stderr[:200])
             logger.warning("SpoolBuddy %s: pip install returned non-zero (continuing): %s", device_id, stderr[:200])
 
 
-        # Step 5: Restart service
+        # Step 5: Restart daemon
         await _update_progress("updating", "Restarting daemon...")
         await _update_progress("updating", "Restarting daemon...")
         rc, _, stderr = await _run_ssh_command(
         rc, _, stderr = await _run_ssh_command(
             ip_address,
             ip_address,
@@ -225,6 +225,17 @@ async def perform_ssh_update(device_id: str, ip_address: str, install_path: str
             await _update_progress("error", f"Service restart failed: {stderr[:200]}")
             await _update_progress("error", f"Service restart failed: {stderr[:200]}")
             return
             return
 
 
+        # Step 6: Restart kiosk browser to load updated frontend
+        await _update_progress("updating", "Restarting kiosk browser...")
+        rc, _, stderr = await _run_ssh_command(
+            ip_address,
+            "sudo /usr/bin/systemctl restart getty@tty1.service",
+            private_key,
+        )
+        if rc != 0:
+            # Non-fatal — kiosk may not be set up on all devices
+            logger.warning("SpoolBuddy %s: kiosk restart failed (non-fatal): %s", device_id, stderr[:200])
+
         await _update_progress("complete", "Update complete, daemon restarting...")
         await _update_progress("complete", "Update complete, daemon restarting...")
         logger.info("SpoolBuddy %s: SSH update complete (branch=%s)", device_id, branch)
         logger.info("SpoolBuddy %s: SSH update complete (branch=%s)", device_id, branch)
 
 

+ 6 - 4
spoolbuddy/install/install.sh

@@ -373,11 +373,13 @@ create_spoolbuddy_user() {
     done
     done
     success "User added to gpio, spi, i2c, video groups"
     success "User added to gpio, spi, i2c, video groups"
 
 
-    # Allow passwordless restart of the daemon (needed for SSH-based updates from Bambuddy)
-    echo "$SPOOLBUDDY_SERVICE_USER ALL=(root) NOPASSWD: /usr/bin/systemctl restart spoolbuddy.service" \
-        > /etc/sudoers.d/spoolbuddy
+    # Allow passwordless restart of daemon + kiosk (needed for SSH-based updates from Bambuddy)
+    cat > /etc/sudoers.d/spoolbuddy << 'SUDOERS'
+spoolbuddy ALL=(root) NOPASSWD: /usr/bin/systemctl restart spoolbuddy.service
+spoolbuddy ALL=(root) NOPASSWD: /usr/bin/systemctl restart getty@tty1.service
+SUDOERS
     chmod 440 /etc/sudoers.d/spoolbuddy
     chmod 440 /etc/sudoers.d/spoolbuddy
-    success "Sudoers entry created for service restart"
+    success "Sudoers entries created for service and kiosk restart"
 }
 }
 
 
 download_spoolbuddy() {
 download_spoolbuddy() {