Skip to content

Conversation

@Boy132
Copy link
Member

@Boy132 Boy132 commented Oct 14, 2025

Closes #1740

If an allocation is locked it can't be removed on the client area/ by normal users. When admins assign allocations to a server they are locked by default but can also be manually locked/ unlocked.

@Boy132 Boy132 self-assigned this Oct 14, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 14, 2025

📝 Walkthrough

Walkthrough

The changes introduce an allocation locking mechanism preventing users from deleting assigned allocations. A new is_locked column is added to track allocation state. Allocations are locked upon server assignment and unlocked upon dissociation. Both admin and user interfaces include lock/unlock actions with appropriate visibility controls.

Changes

Cohort / File(s) Summary
Database & Model Layer
database/migrations/2025_10_14_065517_add_is_locked_to_allocations.php, app/Models/Allocation.php
Adds is_locked boolean column with default false. Model includes property initialization, validation rules, boolean cast, and boot hook to reset is_locked to false when server_id is null.
Admin Interface
app/Filament/Admin/Resources/Servers/RelationManagers/AllocationsRelationManager.php
Adds is_locked IconColumn display. Introduces lock/unlock per-allocation actions. Updates dissociate action and bulk dissociate to clear notes, reset is_locked to false, and auto-assign allocation_id if owner has none. Record select action locks chosen allocations and auto-assigns first allocation to owner.
Server Resource Interface
app/Filament/Server/Resources/Allocations/AllocationResource.php
Adds is_locked IconColumn display. Updates DetachAction to clear notes, unlock allocation, and set server_id to null. Exposes is_locked visibility in DetachAction. Renames toolbar action from addAllocation to add_allocation.
Service Layer
app/Services/Servers/ServerCreationService.php
Updates storeAssignedAllocations flow to set is_locked = true for each allocated allocation before saving.
Localization
lang/en/admin/server.php, lang/en/server/network.php
Adds translation keys: locked, locked_helper, lock, unlock with context-specific helper text for admin and server interfaces.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AdminUI as Admin UI
    participant Model as Allocation Model
    participant DB as Database

    rect rgb(230, 245, 255)
        Note over AdminUI,Model: Allocation Assignment (Server Creation)
        AdminUI->>Model: Store allocation with server_id
        Model->>Model: Set is_locked = true
        Model->>DB: Save allocation
        DB-->>Model: Persisted
    end

    rect rgb(245, 255, 230)
        Note over User,Model: Allocation Lock/Unlock
        User->>AdminUI: Click lock/unlock action
        AdminUI->>Model: Update is_locked state
        Model->>DB: Persist change
        DB-->>AdminUI: Success
    end

    rect rgb(255, 240, 230)
        Note over User,Model: Allocation Dissociation
        User->>AdminUI: Click dissociate/detach
        AdminUI->>Model: Clear notes, set is_locked = false, set server_id = null
        Model->>DB: Persist changes
        DB-->>AdminUI: Success
        Note over Model: Auto-assign first allocation to owner if needed
    end
Loading

Pre-merge checks

✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Allow admins to "lock" allocations' clearly and concisely describes the main feature added: a locking mechanism for allocations that admins can control.
Description check ✅ Passed The description directly relates to the changeset, explaining the locking mechanism, default behavior when admins assign allocations, and manual lock/unlock capabilities.
Linked Issues check ✅ Passed The PR implements the core objective from issue #1740 by preventing users from deleting admin-assigned allocations through a locking mechanism that restricts deletion to admins.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the allocation locking feature: database migration, model updates, UI components, and service layer modifications for lock management.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Boy132 Boy132 marked this pull request as ready for review November 4, 2025 09:18
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/Filament/Admin/Resources/Servers/RelationManagers/AllocationsRelationManager.php (1)

138-144: Consider using preferred allocation update pattern for consistency.

The code uses a mass update (whereIn()->update()) to set is_locked = true after associating allocations. While this works correctly, the codebase has an established pattern for allocation updates: using whereNull('server_id'), lockForUpdate(), and direct property assignment with save() (as seen in ServerCreationService).

Based on learnings

Consider refactoring for consistency:

-                    ->after(function (array $data) {
-                        Allocation::whereIn('id', array_values(array_unique($data['recordId'])))->update(['is_locked' => true]);
+                    ->after(function (array $data) {
+                        Allocation::whereIn('id', array_values(array_unique($data['recordId'])))
+                            ->lockForUpdate()
+                            ->get()
+                            ->each(function (Allocation $allocation) {
+                                $allocation->is_locked = true;
+                                $allocation->save();
+                            });

                         if (!$this->getOwnerRecord()->allocation_id) {
                             $this->getOwnerRecord()->update(['allocation_id' => $data['recordId'][0]]);
                         }
                     }),

Note: This is optional since at this point the allocations are already associated (no race condition risk) and no critical model events need to fire for is_locked changes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21f9f25 and e3f3226.

📒 Files selected for processing (7)
  • app/Filament/Admin/Resources/Servers/RelationManagers/AllocationsRelationManager.php (2 hunks)
  • app/Filament/Server/Resources/Allocations/AllocationResource.php (2 hunks)
  • app/Models/Allocation.php (4 hunks)
  • app/Services/Servers/ServerCreationService.php (1 hunks)
  • database/migrations/2025_10_14_065517_add_is_locked_to_allocations.php (1 hunks)
  • lang/en/admin/server.php (1 hunks)
  • lang/en/server/network.php (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: lancepioch
Repo: pelican-dev/panel PR: 1595
File: app/Services/Servers/ServerCreationService.php:182-184
Timestamp: 2025-08-12T17:33:57.388Z
Learning: In the Pelican Panel codebase, when updating allocations to assign them to servers, the preferred pattern is to use whereNull('server_id'), lockForUpdate(), and direct property assignment with save() rather than mass update methods, to prevent race conditions and mass-assignment issues while ensuring model events fire properly.
📚 Learning: 2025-08-12T17:33:57.388Z
Learnt from: lancepioch
Repo: pelican-dev/panel PR: 1595
File: app/Services/Servers/ServerCreationService.php:182-184
Timestamp: 2025-08-12T17:33:57.388Z
Learning: In the Pelican Panel codebase, when updating allocations to assign them to servers, the preferred pattern is to use whereNull('server_id'), lockForUpdate(), and direct property assignment with save() rather than mass update methods, to prevent race conditions and mass-assignment issues while ensuring model events fire properly.

Applied to files:

  • app/Services/Servers/ServerCreationService.php
  • app/Models/Allocation.php
  • app/Filament/Admin/Resources/Servers/RelationManagers/AllocationsRelationManager.php
  • app/Filament/Server/Resources/Allocations/AllocationResource.php
🧬 Code graph analysis (5)
app/Services/Servers/ServerCreationService.php (1)
app/Models/Server.php (1)
  • allocation (277-280)
app/Models/Allocation.php (1)
app/Models/Server.php (1)
  • allocation (277-280)
app/Filament/Admin/Resources/Servers/RelationManagers/AllocationsRelationManager.php (2)
app/Models/Allocation.php (1)
  • Allocation (48-141)
app/Models/Server.php (1)
  • allocation (277-280)
app/Filament/Server/Resources/Allocations/AllocationResource.php (4)
app/Models/Allocation.php (3)
  • Allocation (48-141)
  • node (137-140)
  • server (129-132)
app/Models/Server.php (1)
  • allocation (277-280)
app/helpers.php (1)
  • user (127-130)
app/Models/Permission.php (1)
  • Permission (11-221)
database/migrations/2025_10_14_065517_add_is_locked_to_allocations.php (1)
app/Filament/Admin/Resources/Servers/RelationManagers/AllocationsRelationManager.php (1)
  • table (32-159)
🔇 Additional comments (15)
lang/en/server/network.php (1)

15-16: LGTM - Clear translation keys.

The translation keys appropriately describe the locking feature for end users.

lang/en/admin/server.php (1)

16-19: LGTM - Comprehensive admin translations.

The translation keys provide clear labels and context for the admin locking interface.

app/Services/Servers/ServerCreationService.php (1)

187-196: LGTM - Follows preferred allocation assignment pattern.

The implementation correctly sets is_locked = true when assigning allocations to servers, and follows the established pattern of using whereNull('server_id'), lockForUpdate(), and direct property assignment with save() to prevent race conditions and ensure model events fire properly.

Based on learnings

database/migrations/2025_10_14_065517_add_is_locked_to_allocations.php (1)

14-16: LGTM - Clean migration.

The migration properly adds the is_locked column with a sensible default of false, ensuring existing allocations remain unlocked.

app/Filament/Server/Resources/Allocations/AllocationResource.php (3)

76-80: LGTM - Clear lock status indicator.

The icon column appropriately displays the lock state with intuitive icons and helpful tooltip.


84-100: Verify admin bypass logic is intentional.

The visibility condition !$allocation->is_locked || user()?->can('update', $allocation->node) allows admins with node update permissions to delete locked allocations. This aligns with the translation "Locked allocations can only be deleted by admins", but confirm this is the intended security model.

Additionally, the detach action uses a mass update approach (Allocation::where()->update()) rather than the preferred pattern from the codebase of using lockForUpdate() with direct property assignment. While this works correctly since model events aren't critical here and is_locked is explicitly reset, consider whether consistency with the pattern used in ServerCreationService is valuable.

Based on learnings


103-103: Naming consistency improvement.

Good update to use snake_case (add_allocation) for consistency with Laravel/Filament conventions.

app/Models/Allocation.php (4)

59-61: LGTM - Proper default value initialization.

Setting the default is_locked to false in the model attributes ensures new allocations are unlocked by default.


76-76: LGTM - Validation rule added.

The boolean validation rule for is_locked is properly defined.


81-85: Excellent defensive behavior - auto-unlock on dissociation.

The updating hook automatically resets is_locked to false when server_id becomes null, ensuring locked allocations are always tied to server associations. This prevents orphaned locked allocations and provides a safety net even if explicit unlock logic is missed elsewhere.


98-98: LGTM - Boolean cast added.

Properly casts is_locked to boolean for consistent type handling.

app/Filament/Admin/Resources/Servers/RelationManagers/AllocationsRelationManager.php (4)

63-67: LGTM - Lock status display.

The icon column appropriately displays the allocation lock state in the admin interface.


74-81: LGTM - Lock/unlock actions.

The lock and unlock actions are properly implemented with appropriate visibility controls based on the current is_locked state.


84-92: LGTM - Defensive allocation_id assignment.

The dissociate after-hook properly clears the allocation state and includes good defensive logic to reassign the server's allocation_id if it becomes orphaned.

Note: The explicit setting of is_locked = false on line 86 is technically redundant since the model's booted() hook automatically resets it when server_id becomes null. However, this explicit approach is harmless and makes the intent clear.


149-157: Mass update approach is necessary here.

The bulk dissociate after-hook correctly uses a mass update to reset state for dissociated allocations. Since these allocations have already been dissociated (bypassing model events), the explicit reset of is_locked and notes is necessary. The defensive allocation_id reassignment is also a good safety measure.

@Boy132 Boy132 merged commit cec1418 into main Nov 8, 2025
25 checks passed
@Boy132 Boy132 deleted the boy132/allocation-marker branch November 8, 2025 20:54
@github-actions github-actions bot locked and limited conversation to collaborators Nov 8, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prevent users from deleting "default" allocations

3 participants