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 afterCreate(): void { $createLinuxUser = $this->data['create_linux_user'] ?? true; if ($createLinuxUser) { try { $linuxService = new LinuxUserService; // Get the plain password before it was hashed $password = $this->data['sftp_password'] ?? null; $linuxService->createUser($this->record, $password); Notification::make() ->title(__('Linux user created')) ->body(__("System user ':username' has been created.", ['username' => $this->record->username])) ->success() ->send(); // Apply disk quota if enabled $this->applyDiskQuota(); // Apply resource limits from package $this->syncResourceLimitsFromPackage($this->selectedPackage, true); } catch (Exception $e) { Notification::make() ->title(__('Linux user creation failed')) ->body($e->getMessage()) ->danger() ->send(); } } else { // Store resource limits even if the Linux user was not created yet $this->syncResourceLimitsFromPackage($this->selectedPackage, false); } if (! $this->record->hosting_package_id) { Notification::make() ->title(__('No hosting package selected')) ->body(__('This user has unlimited resource limits.')) ->warning() ->send(); } } protected function applyDiskQuota(): void { $quotaMb = $this->record->disk_quota_mb; if (! $quotaMb || $quotaMb <= 0) { return; } // Always try to apply quota when set try { $agent = new AgentClient; $result = $agent->quotaSet($this->record->username, (int) $quotaMb); if ($result['success'] ?? false) { Notification::make() ->title(__('Disk quota applied')) ->body(__("Quota of :quota GB set for ':username'.", ['quota' => number_format($quotaMb / 1024, 1), 'username' => $this->record->username])) ->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 failed')) ->body(__('Value saved but filesystem quota not applied: :error', ['error' => $e->getMessage()])) ->warning() ->send(); } } protected function syncResourceLimitsFromPackage(?HostingPackage $package, bool $apply): void { if (! $this->record) { return; } $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(); if ($apply) { 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(); if ($apply) { app(ResourceLimitService::class)->apply($limit); } } }