From 018a74447576d48c47c838c89db70e784fd6b80f Mon Sep 17 00:00:00 2001 From: maziggy Date: Mon, 2 Feb 2026 07:39:58 +0100 Subject: [PATCH] Location filter for queue and auth fixes (Issue #220) Features: - Add location filter for "Any {Model}" queue assignments - Queue items can target a specific location (e.g., "Any X1C in Workshop") - Location dropdown filter on Queue page to view jobs by location - Scheduler considers location when assigning model-based jobs Closes #220 --- .gitignore | 3 + CHANGELOG.md | 7 + README.md | 2 +- backend/app/api/routes/print_queue.py | 2 + backend/app/core/auth.py | 8 +- backend/app/core/database.py | 6 + backend/app/main.py | 29 ++- backend/app/models/print_queue.py | 3 + backend/app/schemas/print_queue.py | 3 + backend/app/services/print_scheduler.py | 17 +- backend/tests/integration/test_auth_api.py | 85 +++++++ .../tests/integration/test_print_queue_api.py | 213 +++++++++++++++++ frontend/src/api/client.ts | 3 + .../components/PrintModal/PrinterSelector.tsx | 74 +++++- frontend/src/components/PrintModal/index.tsx | 13 ++ frontend/src/components/PrintModal/types.ts | 4 + frontend/src/pages/PrintersPage.tsx | 14 +- frontend/src/pages/QueuePage.tsx | 69 +++++- frontend/vitest.config.ts | 7 + icons/5f21bc794a4e4521b72c6564029ed5d9.svg | 1 - .../{index-1q7Yxq-H.js => index-D-vJDFzo.js} | 218 +++++++++--------- static/index.html | 2 +- 22 files changed, 652 insertions(+), 131 deletions(-) delete mode 100644 icons/5f21bc794a4e4521b72c6564029ed5d9.svg rename static/assets/{index-1q7Yxq-H.js => index-D-vJDFzo.js} (56%) diff --git a/.gitignore b/.gitignore index 57914d1aa..8a840e6be 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,6 @@ firmware/ node_modules/ data/ + +# JWT secret file (should be in data dir, but protect project root too) +.jwt_secret diff --git a/CHANGELOG.md b/CHANGELOG.md index 80d4847ae..ca9659c38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to Bambuddy will be documented in this file. ## [0.1.7b] - Not released ### Enhancements +- **Location Filter for Queue** (Issue #220): + - Filter queue jobs by printer location in the Queue page + - "Any {Model}" queue assignments can now specify a target location (e.g., "Any X1C in Workshop") + - Location filter dropdown shows all unique locations from printers and queue items + - Location is saved with queue items and displayed in the queue list - **Ownership-Based Permissions** (Issue #205): - Users can now only update/delete their own items unless they have elevated permissions - Update/delete permissions split into `*_own` and `*_all` variants: @@ -51,6 +56,8 @@ All notable changes to Bambuddy will be documented in this file. - Removed ~2000 lines of legacy JSON-based backup/restore code ### Fixes +- **JWT secret key not persistent across restarts** - Fixed JWT secret key generation to properly use data directory, ensuring tokens remain valid across container restarts +- **Images/thumbnails returning 401 when auth enabled** - Fixed auth middleware to allow public access to image/media endpoints (thumbnails, photos, QR codes, timelapses, camera streams) since browser elements like `` don't send Authorization headers - **Library thumbnails missing after restore** - Fixed library files using absolute paths that break after restore on different systems: - Library now stores relative paths in database for portability - Automatic migration converts existing absolute paths to relative on startup diff --git a/README.md b/README.md index f305c2971..912ea921c 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ ### ⏰ Scheduling & Automation - Print queue with drag-and-drop - Multi-printer selection (send to multiple printers at once) -- Model-based queue assignment (send to "any X1C" for load balancing) +- Model-based queue assignment (send to "any X1C" for load balancing) with location filtering - Filament validation (only assign to printers with required filaments) - Per-printer AMS mapping (individual slot configuration for print farms) - Scheduled prints (date/time) diff --git a/backend/app/api/routes/print_queue.py b/backend/app/api/routes/print_queue.py index d4109a5db..ff8f8c4c1 100644 --- a/backend/app/api/routes/print_queue.py +++ b/backend/app/api/routes/print_queue.py @@ -121,6 +121,7 @@ def _enrich_response(item: PrintQueueItem) -> PrintQueueItemResponse: "id": item.id, "printer_id": item.printer_id, "target_model": item.target_model, + "target_location": item.target_location, "required_filament_types": required_filament_types_parsed, "waiting_reason": item.waiting_reason, "archive_id": item.archive_id, @@ -289,6 +290,7 @@ async def add_to_queue( item = PrintQueueItem( printer_id=data.printer_id, target_model=target_model_norm, + target_location=data.target_location, required_filament_types=required_filament_types, archive_id=data.archive_id, library_file_id=data.library_file_id, diff --git a/backend/app/core/auth.py b/backend/app/core/auth.py index 4716b5c11..2dc5ec4e9 100644 --- a/backend/app/core/auth.py +++ b/backend/app/core/auth.py @@ -48,7 +48,13 @@ def _get_jwt_secret() -> str: return env_secret # 2. Check for secret file in data directory - data_dir = Path(os.environ.get("BAMBUDDY_DATA_DIR", "/app/data")) + # Use DATA_DIR env var (same as rest of app), fallback to data/ subdirectory + data_dir_env = os.environ.get("DATA_DIR") + if data_dir_env: + data_dir = Path(data_dir_env) + else: + # Fallback to data/ subdirectory under project root (not project root itself!) + data_dir = Path(__file__).parent.parent.parent.parent / "data" secret_file = data_dir / ".jwt_secret" if secret_file.exists(): diff --git a/backend/app/core/database.py b/backend/app/core/database.py index 8635bf2f5..6c97bf120 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -1044,6 +1044,12 @@ async def run_migrations(conn): except Exception: pass + # Migration: Add target_location column to print_queue for location-based filtering (Issue #220) + try: + await conn.execute(text("ALTER TABLE print_queue ADD COLUMN target_location VARCHAR(100)")) + except Exception: + pass + # Migration: Convert absolute paths to relative paths in library_files table # This ensures backup/restore portability across different installations try: diff --git a/backend/app/main.py b/backend/app/main.py index 9c7bb34ae..32d3279e7 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -2543,11 +2543,14 @@ app = FastAPI( # ============================================================================= # Public routes that don't require authentication even when auth is enabled PUBLIC_API_ROUTES = { - # Auth routes needed before login + # Auth routes needed before/during login "/api/v1/auth/status", "/api/v1/auth/login", + "/api/v1/auth/setup", # Needed for initial setup and recovery # Version check for updates (no sensitive data) "/api/v1/updates/version", + # Metrics endpoint handles its own prometheus_token authentication + "/api/v1/metrics", } # Route prefixes that are public (for routes with dynamic segments) @@ -2556,6 +2559,25 @@ PUBLIC_API_PREFIXES = [ "/api/v1/ws", ] +# Route patterns that are public (read-only display data) +# These are checked with "in path" - needed because browsers load images/videos +# via and