originalQuota = $data['disk_quota_mb'] ?? null; return $data; } protected function mutateFormDataBeforeSave(array $data): array { if (! empty($data['hosting_package_id'])) { $this->selectedPackage = HostingPackage::find($data['hosting_package_id']); $data['disk_quota_mb'] = $this->selectedPackage?->disk_quota_mb; } else { $this->selectedPackage = null; $data['disk_quota_mb'] = null; } return $data; } protected function afterSave(): void { $newQuota = $this->record->disk_quota_mb; if ($newQuota !== $this->originalQuota) { // Always try to apply quota when changed try { $agent = new AgentClient; $result = $agent->quotaSet($this->record->username, (int) ($newQuota ?? 0)); if ($result['success'] ?? false) { $message = $newQuota && $newQuota > 0 ? __('Quota updated to :size GB', ['size' => number_format($newQuota / 1024, 1)]) : __('Quota removed (unlimited)'); Notification::make() ->title(__('Disk quota updated')) ->body($message) ->success() ->send(); } else { throw new Exception($result['error'] ?? __('Unknown error')); } } catch (Exception $e) { // Show warning but don't fail - quota value is saved in database Notification::make() ->title(__('Disk quota update failed')) ->body(__('Value saved but filesystem quota not applied: :error', ['error' => $e->getMessage()])) ->warning() ->send(); } } $this->syncResourceLimitsFromPackage($this->selectedPackage); } protected function syncResourceLimitsFromPackage(?HostingPackage $package): void { $cpu = $package?->cpu_limit_percent; $memory = $package?->memory_limit_mb; $io = $package?->io_limit_mb; $hasLimits = ($cpu && $cpu > 0) || ($memory && $memory > 0) || ($io && $io > 0); $limit = UserResourceLimit::where('user_id', $this->record->id)->first(); if (! $package || ! $hasLimits) { if ($limit) { $limit->fill([ 'cpu_limit_percent' => null, 'memory_limit_mb' => null, 'io_limit_mb' => null, 'is_active' => false, ])->save(); app(ResourceLimitService::class)->clear($limit); } return; } if (! $limit) { $limit = new UserResourceLimit(['user_id' => $this->record->id]); } $limit->fill([ 'cpu_limit_percent' => $cpu, 'memory_limit_mb' => $memory, 'io_limit_mb' => $io, 'is_active' => true, ])->save(); app(ResourceLimitService::class)->apply($limit); } protected function getHeaderActions(): array { return [ Actions\Action::make('loginAsUser') ->label(__('Login as User')) ->icon('heroicon-o-arrow-right-on-rectangle') ->color('info') ->visible(fn () => ! $this->record->is_admin && $this->record->is_active) ->url(fn () => route('impersonate.start', ['user' => $this->record->id]), shouldOpenInNewTab: true), Actions\DeleteAction::make() ->visible(fn () => (int) $this->record->id !== 1) ->form([ Toggle::make('remove_home') ->label(__('Delete home directory')) ->helperText(__('Warning: This will permanently delete /home/:username and all its contents!', ['username' => $this->record->username])) ->default(false), ]) ->action(function (array $data) { $removeHome = $data['remove_home'] ?? false; $username = $this->record->username; try { $linuxService = new LinuxUserService; if ($linuxService->userExists($username)) { $linuxService->deleteUser($username, $removeHome); $body = $removeHome ? __("System user ':username' has been deleted along with home directory.", ['username' => $username]) : __("System user ':username' has been deleted.", ['username' => $username]); Notification::make() ->title(__('Linux user deleted')) ->body($body) ->success() ->send(); } } catch (Exception $e) { Notification::make() ->title(__('Linux user deletion failed')) ->body($e->getMessage()) ->danger() ->send(); } // Delete from database $this->record->delete(); $this->redirect($this->getResource()::getUrl('index')); }), ]; } }