Commit Graph
3 Commits
Author SHA1 Message Date
maziggy 45921b7a56 Limit groups to selected printers (issue #1727)
A group can now be limited to a set of printers. Its members see and
control only those printers. Every other printer answers 404, as if it
didn't exist.

- Groups gain restrict_printers and a group_printers table (migration
  for SQLite and Postgres). A user's printers are the union of their
  limited groups. Groups without the flag don't limit anything, a user
  in no limited group keeps every printer, and admins see all of them.
- core/printer_scope.py holds the scope. RequestPrinterScope and
  RequirePrinterPermissionIfAuthEnabled apply it to routes: printer
  routes, camera, queue and batches, archives, projects, stats, print
  log, pipeline runs, inventory and Spoolman assignments, maintenance,
  smart plugs, scheduled drying, firmware and Obico status.
- API keys, camera stream, Cam Wall, overlay and WebSocket tokens carry
  the printers of whoever created them. WebSocket broadcasts are
  filtered per connection, and the filtering fails closed.
- Scheduler: "Any <model>" jobs stay on their owner's printers. A job
  pinned to a printer its owner lost waits with a reason. Callers with
  no user identity and limited printers must queue to a specific printer.
- Group editor: new Printer access section, translated into all 15
  locales. Saving a system group no longer resends unchanged
  permissions, which the backend refused.
2026-10-01 14:47:40 +02:00
maziggy 8b5d8e3170 Add layers, job id, HMS faults and serial to the API-key printer status (#2919) 2026-09-30 11:45:31 +02:00
maziggy c9bc5eb4e9 fix(webhook): #1584 PrinterState dataclass attribute access in status / stop / cancel
webhook.py treated printer_manager.get_status() return as a dict and
  called .get(...) on it. The return is a PrinterState dataclass
  (backend/app/services/bambu_mqtt.py), so the call raised AttributeError
  and Starlette surfaced it as a generic 500 for every printer with a
  status row. Non-existent printers correctly returned 404 because the
  early "Printer not found" branch fired before the crash.

  Reporter's repro matched exactly: id 1 (existing printer) returned 500,
  id 2 and id 3 (no row) returned 404. Verified end-to-end against a live
  PG-backed instance with the reporter's key shape — same 500 before the
  patch, 200 with the correct payload after.

  8 crash sites across 3 routes:
    - webhook_get_printer_status   GET  /printer/{id}/status      5 sites
    - webhook_stop_print           POST /printer/{id}/stop        2 sites
    - webhook_cancel_print         POST /printer/{id}/cancel      2 sites

  Every status.get("X", default) replaced with status.X if status else
  default. Pydantic response schema unchanged; PrinterState's dataclass
  defaults cleanly cover the "registered but never connected" branch so
  the status route now returns 200 with connected=false, state=null
  rather than crashing.
2026-06-02 12:57:51 +02:00