Browse Source

Add separate permission for AMS RFID re-read (Issue #204)

- Add new `printers:ams_rfid` permission for re-reading AMS RFID tags
- Allows granting RFID re-read access without full printer control
- Operators group includes this permission by default
- Previously used `printers:control` which grants broader access
- Permission available in Settings > Users > Group Editor

Closes #204
maziggy 3 months ago
parent
commit
ade4792eac

+ 5 - 0
CHANGELOG.md

@@ -29,6 +29,11 @@ All notable changes to Bambuddy will be documented in this file.
   - Shows username on archive cards, library files, queue items, and printer cards (while printing)
   - Works when authentication is enabled; gracefully hidden when auth is disabled
   - Database migration adds `created_by_id` columns to `print_archives`, `library_files`, and `print_queue` tables
+- **Separate AMS RFID Permission** (Issue #204):
+  - Added new `printers:ams_rfid` permission for re-reading AMS RFID tags
+  - Allows granting RFID re-read access without full printer control permissions
+  - Operators group includes this permission by default
+  - Available in Settings > Users > Group Editor as a toggleable permission
 - **Schedule Button on Archive Cards** (Issue #208):
   - Added "Schedule" button next to "Reprint" on archive cards for quick access to print scheduling
   - Previously only available in the context menu (right-click)

+ 1 - 1
backend/app/api/routes/printers.py

@@ -1689,7 +1689,7 @@ async def refresh_ams_slot(
     printer_id: int,
     ams_id: int,
     slot_id: int,
-    _=RequirePermissionIfAuthEnabled(Permission.PRINTERS_CONTROL),
+    _=RequirePermissionIfAuthEnabled(Permission.PRINTERS_AMS_RFID),
     db: AsyncSession = Depends(get_db),
 ):
     """Re-read RFID for an AMS slot (triggers filament info refresh)."""

+ 3 - 0
backend/app/core/permissions.py

@@ -21,6 +21,7 @@ class Permission(str, Enum):
     PRINTERS_DELETE = "printers:delete"
     PRINTERS_CONTROL = "printers:control"  # Start/stop/pause/resume prints
     PRINTERS_FILES = "printers:files"  # Send files to printer
+    PRINTERS_AMS_RFID = "printers:ams_rfid"  # Re-read AMS RFID tags
 
     # Archives
     ARCHIVES_READ = "archives:read"
@@ -159,6 +160,7 @@ PERMISSION_CATEGORIES = {
         Permission.PRINTERS_DELETE,
         Permission.PRINTERS_CONTROL,
         Permission.PRINTERS_FILES,
+        Permission.PRINTERS_AMS_RFID,
     ],
     "Archives": [
         Permission.ARCHIVES_READ,
@@ -305,6 +307,7 @@ DEFAULT_GROUPS = {
             Permission.PRINTERS_DELETE.value,
             Permission.PRINTERS_CONTROL.value,
             Permission.PRINTERS_FILES.value,
+            Permission.PRINTERS_AMS_RFID.value,
             # Archives - own items only
             Permission.ARCHIVES_READ.value,
             Permission.ARCHIVES_CREATE.value,

+ 1 - 1
frontend/src/api/client.ts

@@ -1727,7 +1727,7 @@ export interface ExternalLinkUpdate {
 
 // Permission type - all available permissions
 export type Permission =
-  | 'printers:read' | 'printers:create' | 'printers:update' | 'printers:delete' | 'printers:control' | 'printers:files'
+  | 'printers:read' | 'printers:create' | 'printers:update' | 'printers:delete' | 'printers:control' | 'printers:files' | 'printers:ams_rfid'
   | 'archives:read' | 'archives:create'
   | 'archives:update_own' | 'archives:update_all' | 'archives:delete_own' | 'archives:delete_all'
   | 'archives:reprint_own' | 'archives:reprint_all'

+ 8 - 8
frontend/src/pages/PrintersPage.tsx

@@ -2291,18 +2291,18 @@ function PrinterCard({
                                       <div className="absolute top-full left-0 mt-1 z-50 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-lg shadow-xl py-1 min-w-[120px]">
                                         <button
                                           className={`w-full px-3 py-1.5 text-left text-xs flex items-center gap-2 ${
-                                            hasPermission('printers:control')
+                                            hasPermission('printers:ams_rfid')
                                               ? 'text-white hover:bg-bambu-dark-tertiary'
                                               : 'text-bambu-gray/50 cursor-not-allowed'
                                           }`}
                                           onClick={(e) => {
                                             e.stopPropagation();
-                                            if (!hasPermission('printers:control')) return;
+                                            if (!hasPermission('printers:ams_rfid')) return;
                                             refreshAmsSlotMutation.mutate({ amsId: ams.id, slotId: slotIdx });
                                             setAmsSlotMenu(null);
                                           }}
-                                          disabled={isRefreshing || !hasPermission('printers:control')}
-                                          title={!hasPermission('printers:control') ? 'You do not have permission to control printers' : undefined}
+                                          disabled={isRefreshing || !hasPermission('printers:ams_rfid')}
+                                          title={!hasPermission('printers:ams_rfid') ? 'You do not have permission to re-read AMS RFID' : undefined}
                                         >
                                           <RefreshCw className={`w-3 h-3 ${isRefreshing ? 'animate-spin' : ''}`} />
                                           Re-read RFID
@@ -2480,18 +2480,18 @@ function PrinterCard({
                                   <div className="absolute top-full left-0 mt-1 z-50 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-lg shadow-xl py-1 min-w-[120px]">
                                     <button
                                       className={`w-full px-3 py-1.5 text-left text-xs flex items-center gap-2 ${
-                                        hasPermission('printers:control')
+                                        hasPermission('printers:ams_rfid')
                                           ? 'text-white hover:bg-bambu-dark-tertiary'
                                           : 'text-bambu-gray/50 cursor-not-allowed'
                                       }`}
                                       onClick={(e) => {
                                         e.stopPropagation();
-                                        if (!hasPermission('printers:control')) return;
+                                        if (!hasPermission('printers:ams_rfid')) return;
                                         refreshAmsSlotMutation.mutate({ amsId: ams.id, slotId: htSlotId });
                                         setAmsSlotMenu(null);
                                       }}
-                                      disabled={isHtRefreshing || !hasPermission('printers:control')}
-                                      title={!hasPermission('printers:control') ? 'You do not have permission to control printers' : undefined}
+                                      disabled={isHtRefreshing || !hasPermission('printers:ams_rfid')}
+                                      title={!hasPermission('printers:ams_rfid') ? 'You do not have permission to re-read AMS RFID' : undefined}
                                     >
                                       <RefreshCw className={`w-3 h-3 ${isHtRefreshing ? 'animate-spin' : ''}`} />
                                       Re-read RFID

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-1q7Yxq-H.js


+ 1 - 1
static/index.html

@@ -23,7 +23,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-D56fK0KZ.js"></script>
+    <script type="module" crossorigin src="/assets/index-1q7Yxq-H.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-CPqcJWwC.css">
   </head>
   <body>

Some files were not shown because too many files changed in this diff