218 lines
10 KiB
PHP
218 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Admin\Resources\Users\Schemas;
|
|
|
|
use App\Models\DnsSetting;
|
|
use Filament\Actions\Action;
|
|
use Filament\Forms\Components\DateTimePicker;
|
|
use Filament\Forms\Components\Placeholder;
|
|
use Filament\Forms\Components\TextInput;
|
|
use Filament\Forms\Components\Toggle;
|
|
use Filament\Forms\Components\Group;
|
|
use Filament\Schemas\Components\Section;
|
|
use Filament\Schemas\Schema;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\HtmlString;
|
|
use Illuminate\Support\Str;
|
|
|
|
class UserForm
|
|
{
|
|
/**
|
|
* Generate a secure password with uppercase, lowercase, numbers, and special characters
|
|
*/
|
|
public static function generateSecurePassword(int $length = 16): string
|
|
{
|
|
$uppercase = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
|
|
$lowercase = 'abcdefghjkmnpqrstuvwxyz';
|
|
$numbers = '23456789';
|
|
$special = '!@#$%^&*';
|
|
|
|
// Ensure at least one of each type
|
|
$password = $uppercase[random_int(0, strlen($uppercase) - 1)]
|
|
. $lowercase[random_int(0, strlen($lowercase) - 1)]
|
|
. $numbers[random_int(0, strlen($numbers) - 1)]
|
|
. $special[random_int(0, strlen($special) - 1)];
|
|
|
|
// Fill rest with random characters from all pools
|
|
$allChars = $uppercase . $lowercase . $numbers . $special;
|
|
for ($i = 4; $i < $length; $i++) {
|
|
$password .= $allChars[random_int(0, strlen($allChars) - 1)];
|
|
}
|
|
|
|
// Shuffle the password
|
|
return str_shuffle($password);
|
|
}
|
|
|
|
public static function configure(Schema $schema): Schema
|
|
{
|
|
return $schema
|
|
->columns(1)
|
|
->components([
|
|
Section::make(__('User Information'))
|
|
->schema([
|
|
TextInput::make('name')
|
|
->label(__('Name'))
|
|
->required()
|
|
->maxLength(255),
|
|
|
|
TextInput::make('username')
|
|
->label(__('Username'))
|
|
->required()
|
|
->maxLength(32)
|
|
->alphaNum()
|
|
->unique(ignoreRecord: true)
|
|
->rules(['regex:/^[a-z][a-z0-9_]{0,31}$/'])
|
|
->helperText(__('Lowercase letters, numbers, and underscores only. Must start with a letter.'))
|
|
->disabled(fn (string $operation): bool => $operation === 'edit'),
|
|
|
|
TextInput::make('email')
|
|
->label(__('Email address'))
|
|
->email()
|
|
->required()
|
|
->unique(ignoreRecord: true)
|
|
->maxLength(255),
|
|
|
|
TextInput::make('password')
|
|
->password()
|
|
->revealable()
|
|
->dehydrateStateUsing(fn ($state) => filled($state) ? Hash::make($state) : null)
|
|
->dehydrated(fn ($state) => filled($state))
|
|
->required(fn (string $operation): bool => $operation === 'create')
|
|
->minLength(8)
|
|
->rules([
|
|
'regex:/[a-z]/', // lowercase
|
|
'regex:/[A-Z]/', // uppercase
|
|
'regex:/[0-9]/', // number
|
|
])
|
|
->suffixActions([
|
|
Action::make('generatePassword')
|
|
->icon('heroicon-o-arrow-path')
|
|
->tooltip(__('Generate secure password'))
|
|
->action(function ($set) {
|
|
$password = self::generateSecurePassword();
|
|
$set('password', $password);
|
|
}),
|
|
Action::make('copyPassword')
|
|
->icon('heroicon-o-clipboard-document')
|
|
->tooltip(__('Copy to clipboard'))
|
|
->action(function ($state, $livewire) {
|
|
if ($state) {
|
|
$escaped = addslashes($state);
|
|
$livewire->js("navigator.clipboard.writeText('{$escaped}')");
|
|
\Filament\Notifications\Notification::make()
|
|
->title(__('Copied to clipboard'))
|
|
->success()
|
|
->duration(2000)
|
|
->send();
|
|
}
|
|
}),
|
|
])
|
|
->helperText(__('Minimum 8 characters with uppercase, lowercase, and numbers'))
|
|
->label(fn (string $operation): string => $operation === 'create' ? __('Password') : __('New Password')),
|
|
])
|
|
->columns(2),
|
|
|
|
Section::make(__('Account Settings'))
|
|
->schema([
|
|
Toggle::make('is_admin')
|
|
->label(__('Administrator'))
|
|
->helperText(__('Grant full administrative access'))
|
|
->inline(false),
|
|
|
|
Toggle::make('is_active')
|
|
->label(__('Active'))
|
|
->default(true)
|
|
->helperText(__('Inactive users cannot log in'))
|
|
->inline(false),
|
|
|
|
Toggle::make('create_linux_user')
|
|
->label(__('Create Linux User'))
|
|
->default(true)
|
|
->helperText(__('Create a system user account'))
|
|
->visibleOn('create')
|
|
->dehydrated(false)
|
|
->inline(false),
|
|
|
|
DateTimePicker::make('email_verified_at')
|
|
->label(__('Email Verified At')),
|
|
])
|
|
->columns(4),
|
|
|
|
Section::make(__('Disk Quota'))
|
|
->schema([
|
|
Placeholder::make('current_usage')
|
|
->label(__('Current Usage'))
|
|
->content(function ($record) {
|
|
if (!$record) {
|
|
return __('N/A');
|
|
}
|
|
|
|
$used = $record->disk_usage_formatted;
|
|
$quotaMb = $record->disk_quota_mb;
|
|
|
|
if (!$quotaMb || $quotaMb <= 0) {
|
|
return "{$used} (" . __('Unlimited') . ")";
|
|
}
|
|
|
|
$quota = $quotaMb >= 1024
|
|
? number_format($quotaMb / 1024, 1) . ' GB'
|
|
: $quotaMb . ' MB';
|
|
$percent = $record->disk_usage_percent;
|
|
|
|
return "{$used} / {$quota} ({$percent}%)";
|
|
})
|
|
->visibleOn('edit'),
|
|
|
|
Toggle::make('unlimited_quota')
|
|
->label(__('Unlimited Quota'))
|
|
->helperText(__('No disk space limit'))
|
|
->default(fn () => (int) DnsSetting::get('default_quota_mb', 5120) === 0)
|
|
->live()
|
|
->afterStateHydrated(function (Toggle $component, $state, $record) {
|
|
if ($record) {
|
|
$component->state(!$record->disk_quota_mb || $record->disk_quota_mb <= 0);
|
|
}
|
|
})
|
|
->afterStateUpdated(function ($state, callable $set) {
|
|
if ($state) {
|
|
$set('disk_quota_mb', 0);
|
|
} else {
|
|
$set('disk_quota_mb', (int) DnsSetting::get('default_quota_mb', 5120));
|
|
}
|
|
})
|
|
->inline(false),
|
|
|
|
TextInput::make('disk_quota_mb')
|
|
->label(__('Disk Quota'))
|
|
->numeric()
|
|
->minValue(1)
|
|
->default(fn () => (int) DnsSetting::get('default_quota_mb', 5120))
|
|
->helperText(fn ($state) => $state && $state > 0 ? number_format($state / 1024, 1) . ' GB' : null)
|
|
->suffix('MB')
|
|
->visible(fn ($get) => !$get('unlimited_quota'))
|
|
->required(fn ($get) => !$get('unlimited_quota')),
|
|
])
|
|
->description(fn () => !(bool) DnsSetting::get('quotas_enabled', false) ? __('Note: Quotas are currently disabled in Server Settings.') : null)
|
|
->columns(3),
|
|
|
|
Section::make(__('System Information'))
|
|
->schema([
|
|
Placeholder::make('home_directory_display')
|
|
->label(__('Home Directory'))
|
|
->content(fn ($record) => $record?->home_directory ?? '/home/' . __('username')),
|
|
|
|
Placeholder::make('created_at_display')
|
|
->label(__('Created'))
|
|
->content(fn ($record) => $record?->created_at?->format('M d, Y H:i') ?? __('N/A')),
|
|
|
|
Placeholder::make('updated_at_display')
|
|
->label(__('Last Updated'))
|
|
->content(fn ($record) => $record?->updated_at?->format('M d, Y H:i') ?? __('N/A')),
|
|
])
|
|
->columns(3)
|
|
->visibleOn('edit')
|
|
->collapsible(),
|
|
]);
|
|
}
|
|
}
|