diff --git a/register/README.md b/register/README.md index 7aff20c3..9ddd5bcb 100644 --- a/register/README.md +++ b/register/README.md @@ -6,3 +6,17 @@ A simple way for users to register themselves on the panel without administrator - Self-service user registration - Seamless integration with existing login flow +- Optional maximum user cap (auto-disables new signups after limit is reached) +- Automatic default `user_resource_limits` assignment for newly registered users when `user-creatable-servers` is installed + +## Integration with User Creatable Servers + +This plugin can automatically assign default CPU, RAM, disk, and server limit values for every newly registered user. + +Configure these values in the plugin settings UI: + +- `max_users` (`0` = unlimited) +- `default_cpu` +- `default_memory` +- `default_disk` +- `default_server_limit` (`0` = unlimited) diff --git a/register/config/register.php b/register/config/register.php new file mode 100644 index 00000000..b7966b00 --- /dev/null +++ b/register/config/register.php @@ -0,0 +1,9 @@ + 0, + 'default_cpu' => 0, + 'default_memory' => 0, + 'default_disk' => 0, + 'default_server_limit' => 0, +]; diff --git a/register/lang/en/messages.php b/register/lang/en/messages.php new file mode 100644 index 00000000..d3fdd1b1 --- /dev/null +++ b/register/lang/en/messages.php @@ -0,0 +1,5 @@ + 'Registration is currently closed because the maximum number of users has been reached.', +]; diff --git a/register/plugin.json b/register/plugin.json index 43236680..3ccbb452 100644 --- a/register/plugin.json +++ b/register/plugin.json @@ -1,8 +1,8 @@ { "id": "register", "name": "Register", - "author": "Boy132", - "version": "1.0.0", + "author": "Boy132 (& TheOriginalCER06)", + "version": "1.0.1", "description": "A simple way for users to register themselves", "category": "plugin", "url": "https://github.com/pelican-dev/plugins/tree/main/register", diff --git a/register/src/Filament/Pages/Auth/Register.php b/register/src/Filament/Pages/Auth/Register.php index dd271cac..71c5b82e 100644 --- a/register/src/Filament/Pages/Auth/Register.php +++ b/register/src/Filament/Pages/Auth/Register.php @@ -3,10 +3,15 @@ namespace Boy132\Register\Filament\Pages\Auth; use App\Extensions\Captcha\CaptchaService; +use Boy132\UserCreatableServers\Models\UserResourceLimits; use Filament\Auth\Pages\Register as BaseRegister; use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Component; use Filament\Schemas\Schema; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema as SchemaFacade; +use Illuminate\Validation\ValidationException; class Register extends BaseRegister { @@ -17,6 +22,13 @@ public function boot(CaptchaService $captchaService): void $this->captchaService = $captchaService; } + public function mount(): void + { + $this->abortIfRegistrationLimitReached(); + + parent::mount(); + } + public function form(Schema $schema): Schema { $components = [ @@ -50,4 +62,63 @@ protected function getNameFormComponent(): Component ->label(trans('profile.username')) ->unique($this->getUserModel(), 'username'); } + + /** + * @param array $data + */ + protected function handleRegistration(array $data): Model + { + return DB::transaction(function () use ($data): Model { + $this->throwIfRegistrationLimitReached(true); + + $user = parent::handleRegistration($data); + + $this->createDefaultUserResourceLimits($user); + + return $user; + }); + } + + private function abortIfRegistrationLimitReached(): void + { + $maxUsers = (int) config('register.max_users', 0); + + if ($maxUsers > 0 && $this->getUserModel()::query()->count() >= $maxUsers) { + abort(403, trans('register::messages.registration_closed')); + } + } + + private function throwIfRegistrationLimitReached(bool $lockForUpdate = false): void + { + $maxUsers = (int) config('register.max_users', 0); + + $query = $this->getUserModel()::query(); + + if ($lockForUpdate) { + $query->lockForUpdate(); + } + + if ($maxUsers > 0 && $query->count() >= $maxUsers) { + throw ValidationException::withMessages([ + 'email' => trans('register::messages.registration_closed'), + ]); + } + } + + private function createDefaultUserResourceLimits(Model $user): void + { + if (!class_exists(UserResourceLimits::class) || !SchemaFacade::hasTable('user_resource_limits')) { + return; + } + + UserResourceLimits::query()->updateOrCreate( + ['user_id' => $user->getKey()], + [ + 'cpu' => (int) config('register.default_cpu', 0), + 'memory' => (int) config('register.default_memory', 0), + 'disk' => (int) config('register.default_disk', 0), + 'server_limit' => ((int) config('register.default_server_limit', 0)) ?: null, + ], + ); + } } diff --git a/register/src/RegisterPlugin.php b/register/src/RegisterPlugin.php index f012f1cf..6c29d85f 100644 --- a/register/src/RegisterPlugin.php +++ b/register/src/RegisterPlugin.php @@ -2,11 +2,17 @@ namespace Boy132\Register; +use App\Contracts\Plugins\HasPluginSettings; use Boy132\Register\Filament\Pages\Auth\Register; use Filament\Contracts\Plugin; +use Filament\Forms\Components\TextInput; +use Filament\Notifications\Notification; use Filament\Panel; +use Filament\Schemas\Components\Section; +use Illuminate\Support\Facades\Artisan; +use Throwable; -class RegisterPlugin implements Plugin +class RegisterPlugin implements HasPluginSettings, Plugin { public function getId(): string { @@ -19,4 +25,107 @@ public function register(Panel $panel): void } public function boot(Panel $panel): void {} + + public function getSettingsForm(): array + { + return [ + Section::make('Registration Limits') + ->columns(1) + ->schema([ + TextInput::make('max_users') + ->label('Maximum number of users') + ->helperText('Set to 0 for unlimited registrations.') + ->required() + ->numeric() + ->minValue(0) + ->default(fn () => config('register.max_users')), + ]), + Section::make('Default Resource Limits for New Users') + ->columns(4) + ->schema([ + TextInput::make('default_cpu') + ->label('CPU (%)') + ->required() + ->numeric() + ->minValue(0) + ->default(fn () => config('register.default_cpu')), + TextInput::make('default_memory') + ->label('RAM (MB)') + ->required() + ->numeric() + ->minValue(0) + ->default(fn () => config('register.default_memory')), + TextInput::make('default_disk') + ->label('Disk (MB)') + ->required() + ->numeric() + ->minValue(0) + ->default(fn () => config('register.default_disk')), + TextInput::make('default_server_limit') + ->label('Server limit') + ->helperText('Set to 0 for unlimited servers.') + ->required() + ->numeric() + ->minValue(0) + ->default(fn () => config('register.default_server_limit')), + ]), + ]; + } + + public function saveSettings(array $data): void + { + try { + $this->persistSettingsToConfig($data); + + Notification::make() + ->title('Settings saved') + ->success() + ->send(); + } catch (Throwable $exception) { + report($exception); + + Notification::make() + ->title('Unable to save settings') + ->body('Could not write to register/config/register.php. Check file permissions.') + ->danger() + ->send(); + } + } + + /** + * @param array $data + */ + private function persistSettingsToConfig(array $data): void + { + $configPath = plugin_path($this->getId(), 'config/register.php'); + + $config = [ + 'max_users' => max(0, (int) ($data['max_users'] ?? 0)), + 'default_cpu' => max(0, (int) ($data['default_cpu'] ?? 0)), + 'default_memory' => max(0, (int) ($data['default_memory'] ?? 0)), + 'default_disk' => max(0, (int) ($data['default_disk'] ?? 0)), + 'default_server_limit' => max(0, (int) ($data['default_server_limit'] ?? 0)), + ]; + + $content = <<<'PHP' + $value) { + config()->set("register.{$key}", $value); + } + + if (app()->configurationIsCached()) { + Artisan::call('config:clear'); + } + } }