Commit Graph
16 Commits
Author SHA1 Message Date
maziggy e9b6fefe95 fix(auth): redirect to /login when JWT expires mid-session (#1698)
When the JWT expired on an open tab, the next API request hit a 401 with
  "Token has expired"; client.ts cleared the token from storage but
  AuthContext.user stayed populated from the original mount. ProtectedRoute
  only redirects when user === null, so the protected tree kept rendering
  and every subsequent request silently failed with no Authorization
  header — the UI looked like every list was empty until a manual refresh
  remounted AuthProvider.

  The 3 other setAuthToken(null) sites live inside AuthContext itself and
  already pair with setUser(null), so only the client.ts cross-module
  site needed a React-tree signal.

  - client.ts: after setAuthToken(null) on a token-invalidating 401,
    window.dispatchEvent(new CustomEvent('auth:expired')). Guarded on
    `typeof window !== 'undefined'` for SSR / test safety. Generic
    "Authentication required" 401s still don't clear the token or fire
    the event — treated as transient timing issues per the pre-existing
    comment at client.ts:155.

  - AuthContext.tsx: mount useEffect adds a window listener that calls
    setUser(null) under the mountedRef guard; cleanup removes the
    listener so unmount → remount doesn't double-bind.

  Mirrors the patch the reporter shipped on their fork (deec96d1).
2026-06-09 09:25:12 +02:00
Ben Halverson feb44a9dc1 Merge pull request #1416 from benhalverson/fix/open-in-slicer
Fix library Open in Slicer URLs for extensionless filenames
2026-05-18 11:59:34 +02:00
maziggy 57af8a1c19 feat(projects): URL field + cover photo on project cards (#1155)
Two new project fields: a free-text URL rendered as a one-click
  external-link button beside the project name on every card (opens in a
  new tab, click is e.stopPropagation()-guarded so it doesn't enter the
  project), and a cover photo that replaces the status-icon box with a
  square thumbnail.

  URL is plumbed through ProjectCreate/Update/Response/ListResponse,
  including from-template + create-template flows so it inherits between
  a project and its template. Cover photo is not inherited because the
  file would be shared on disk between source and copy.

  Schema validator rejects anything other than http:// or https://
  prefixes -- <a href> rendering would otherwise execute javascript:
  / data: / file: URLs even with React's default escaping. PATCH uses
  model_fields_set for the URL field so users can clear it by sending
  {"url": null}.

  Cover image storage: Project.cover_image_filename references a file
  Cover image storage: Project.cover_image_filename references a file
  inside the existing archives/projects/{id}/attachments/ dir, but it's
  tracked separately from the attachments JSON list so swap/delete on
  the cover doesn't perturb the user's other attachments. Three routes
  (POST/GET/DELETE /projects/{id}/cover-image) accept only .jpg/.jpeg/
  .png/.gif/.webp (no SVG -- SVG can carry script payloads), replace in
  place (prior file deleted before the new one lands so repeat uploads
  can't accumulate orphans), and self-heal when a DB reference points at
  a vanished disk file by clearing the column and 404'ing.

  GET cover-image is gated by RequireCameraStreamTokenIfAuthEnabled
  (accepts ?token=... query string) -- not the bearer-token gate -- so
  <img src> requests work in both auth-on and auth-off configurations.
  The frontend wraps getProjectCoverImageUrl with withStreamToken(),
  matching the existing pattern from getArchiveThumbnail.

  Permissions: PROJECTS_UPDATE for upload/delete/PATCH, PROJECTS_READ
  gate is implicit via the stream-token credential. Migration: 2
  idempotent ALTER TABLE projects ADD COLUMN. Localised across all 8
  UI languages.
2026-04-29 07:42:09 +02:00
Sn0rrii fdaec47378 feat(oidc): Azure Entra ID support — configurable email claim & verification + Remember Me persistent login (#1126)
feat(oidc): add Azure Entra ID support with configurable email claim resolution

Adds two new OIDC provider fields: email_claim and require_email_verified.
2026-04-25 13:32:42 +02:00
maziggy 12c01f029d Revert "feat(oidc): Azure Entra ID support — configurable email claim & verification + Remember Me persistent login (#1118)"
This reverts commit 50382006b3.
2026-04-25 11:05:32 +02:00
Sn0rrii 50382006b3 feat(oidc): Azure Entra ID support — configurable email claim & verification + Remember Me persistent login (#1118)
feat(oidc): add Azure Entra ID support with configurable email claim resolution
2026-04-25 11:02:06 +02:00
maziggy 7f11618e1e Revert "feat(oidc): Azure Entra ID support — configurable email claim & verification + Remember Me persistent login (#1103)"
This reverts commit 365c38483b.
2026-04-24 16:48:59 +02:00
Sn0rrii 365c38483b feat(oidc): Azure Entra ID support — configurable email claim & verification + Remember Me persistent login (#1103)
feat(oidc): add Azure Entra ID support with configurable email claim resolution
fix(oidc): harden email claim resolution, guards, and test coverage
2026-04-24 16:46:50 +02:00
Sn0rrii ba1c97c808 feat: Two-Factor Authentication (TOTP, Email OTP) and OIDC/SSO – full implementation with admin UI (#933)
feat: Two-Factor Authentication (TOTP, Email OTP) and OIDC/SSO – full implementation with admin UI (#933)
2026-04-13 13:24:28 +02:00
maziggy 8af0966e68 feat(printers): airduct mode + status badges + force refresh on printer card
Surface four Home Assistant-style controls on the Printers page card:

  - SD Card badge in the top status row (green / red, icon-only).
  - Enclosure Door badge in the top status row (green / yellow, icon-only).
    Detection per printer family — X1/X1C/X1E read home_flag bit 23, all
    others read top-level `stat` (hex string) bit 23 — so X1 firmware that
    does not flip stat bit 23 stops false-triggering "open". WebSocket
    status-change dedup key now includes door_open so toggling the door
    alone publishes a push, no 30s REST-poll wait.
  - Airduct Mode badge beside the speed control (cooling / heating)
    for P2S/H2D/H2C/H2S; one-click dropdown calls the existing
    set_airduct MQTT command via a new POST /printers/{id}/airduct-mode
    route.
  - Force Refresh entry in the kebab menu — calls the existing
    /printers/{id}/refresh-status endpoint to request a pushall snapshot
    without forcing a reconnect.

  Tests: door-open parsing (X1 home_flag, non-X1 stat, ignore mismatched
  source, invalid hex) and airduct route (validation, not-connected,
  success, failure).
2026-04-13 12:50:17 +02:00
maziggy f4df4393be Add spool inventory and print archive backup to GitHub backup (#870)
GitHub backup can now optionally include spool inventory (with usage
  history) and print archive metadata as JSON. Both toggles are off by
  default. No binary files (gcode/3MF) are included.
2026-04-02 09:59:00 +02:00
maziggy 85598346f7 Issue #257 Fix:
- Changed the 401 handling in request() to only clear the auth token when the error message indicates the token is actually invalid
- Previously: ANY 401 response cleared the token (could cause cascading failures)
- Now: Only clears on specific messages like "Token has expired", "Could not validate credentials", etc.
- "Authentication required" (which might be a timing issue) no longer clears the token

Closes #257
2026-02-04 14:20:12 +01:00
maziggy 583c374f01 Add Virtual Printer Proxy Mode for remote printing
Introduces a new "Proxy Mode" for the Virtual Printer that enables
remote printing from anywhere in the world without VPN, port forwarding,
or Bambu Cloud dependency.

Bambuddy acts as a TLS relay between a remote slicer (Bambu Studio/
OrcaSlicer) and the local Bambu Lab printer:

  Remote Slicer → Internet → Bambuddy Server → Local Network → Printer

The slicer connects to Bambuddy using the real printer's serial number
and access code. Bambuddy authenticates and relays all FTP (file transfer)
and MQTT (commands/status) traffic with end-to-end TLS encryption.

- No port forwarding required - printer stays safely on local network
- No VPN needed - connect from coffee shops, hotels, work, anywhere
- No Bambu Cloud dependency - fully self-hosted solution
- End-to-end TLS encryption on FTP (port 9990) and MQTT (port 8883)
- Works with Bambu Studio and OrcaSlicer
- Uses real printer credentials for authentication
- Automatic printer selection from connected printers

- Add SlicerProxyManager class for TLS relay (tcp_proxy.py)
  - TLS termination with auto-generated certificates
  - Concurrent FTP and MQTT proxy servers
  - Connection lifecycle management with proper cleanup
- Extend VirtualPrinterManager with proxy mode support
  - New 'proxy' mode alongside archive/review/queue modes
  - Target printer selection and credential management
- Add proxy configuration endpoints to settings API
- Add permission checks for proxy endpoints

- Add Proxy Mode card to Virtual Printer settings
- Target printer dropdown for proxy destination
- Real-time proxy status display (ports, target, running state)
- Full i18n support (English, German)

- Add network architecture diagram
- Add proxy mode section to README
- Add comprehensive guide to wiki
- Add prominent feature section to website

- Backend unit tests for SlicerProxyManager
- Backend unit tests for proxy mode configuration
- Frontend tests for proxy mode UI components

Closes #207 #170
2026-02-03 11:02:13 +01:00
maziggy 158fa88b7a Fix frontend API calls missing auth tokens
Several frontend components were using raw fetch() instead of the API
client, causing 401 Unauthorized errors when authentication is enabled.

Changes:
- SpoolmanSettings: Use api.getSpoolmanSettings/updateSpoolmanSettings
- ProjectsPage: Use api.importProjectFile for ZIP imports
- ProjectDetailPage: Use api.exportProjectZip for exports
- CameraPage/EmbeddedCameraViewer: Use api.getCameraStatus

Added to API client:
- getSpoolmanSettings()
- updateSpoolmanSettings()
- importProjectFile()
- exportProjectZip()
- getCameraStatus()

Added tests for auth token handling in client.test.ts
Fixed VirtualPrinterSettings test expectations to match i18n strings

Closes #231
2026-02-03 10:19:28 +01:00
maziggy 49acd8c148 Improved frontend tests 2026-01-30 13:02:57 +01:00
maziggy 4ea7f7c4aa Add GitHub profile backup feature
New feature to automatically backup K-profiles, cloud profiles, and app
settings to a GitHub repository with scheduled or on-demand execution.

Features:
- Configure GitHub repo URL and Personal Access Token
- Schedule backups hourly, daily, or weekly (background scheduler)
- Manual backup trigger with real-time progress tracking
- Skip unchanged commits (only creates commit when data changes)
- Backup history log with status and commit links
- Requires Bambu Cloud login for full profile access
- New Settings → Backup & Restore tab consolidating all backup options
- GitHub backup config included in local backup/restore (except PAT)

Backend:
- New models: GitHubBackupConfig, GitHubBackupLog
- New service: GitHubBackupService with scheduler and GitHub API client
- New routes: /github-backup/* for config, status, logs, and triggers
- Updated settings.py to include github_backup in backup/restore

Frontend:
- New GitHubBackupSettings.tsx component with auto-save
- Updated SettingsPage with Backup tab and status indicator
- Added API types and methods to client.ts

Tests:
- Backend integration tests for all GitHub backup API endpoints
- Frontend API type and endpoint tests
2026-01-27 15:01:06 +01:00