|
| 1 | +<?php |
| 2 | + |
| 3 | +namespace Boy132\UserCreatableServers\Filament\App\Pages; |
| 4 | + |
| 5 | +use App\Exceptions\Service\Deployment\NoViableAllocationException; |
| 6 | +use App\Exceptions\Service\Deployment\NoViableNodeException; |
| 7 | +use App\Filament\Components\Forms\Fields\StartupVariable; |
| 8 | +use App\Filament\Server\Pages\Console; |
| 9 | +use App\Models\Egg; |
| 10 | +use App\Services\Servers\RandomWordService; |
| 11 | +use BackedEnum; |
| 12 | +use Boy132\UserCreatableServers\Filament\App\Widgets\UserResourceLimitsOverview; |
| 13 | +use Boy132\UserCreatableServers\Models\UserResourceLimits; |
| 14 | +use Exception; |
| 15 | +use Filament\Actions\Action; |
| 16 | +use Filament\Forms\Components\Repeater; |
| 17 | +use Filament\Forms\Components\Select; |
| 18 | +use Filament\Forms\Components\TextInput; |
| 19 | +use Filament\Forms\Concerns\InteractsWithForms; |
| 20 | +use Filament\Notifications\Notification; |
| 21 | +use Filament\Pages\Concerns\InteractsWithFormActions; |
| 22 | +use Filament\Pages\Page; |
| 23 | +use Filament\Schemas\Components\Utilities\Get; |
| 24 | +use Filament\Schemas\Components\Utilities\Set; |
| 25 | +use Filament\Schemas\Schema; |
| 26 | +use Filament\Support\Facades\FilamentView; |
| 27 | +use Illuminate\Support\Arr; |
| 28 | + |
| 29 | +/** |
| 30 | + * @property Schema $form |
| 31 | + */ |
| 32 | +class CreateServerPage extends Page |
| 33 | +{ |
| 34 | + use InteractsWithFormActions; |
| 35 | + use InteractsWithForms; |
| 36 | + |
| 37 | + protected static string|BackedEnum|null $navigationIcon = 'tabler-cube-plus'; |
| 38 | + |
| 39 | + protected static ?string $slug = 'create-server'; |
| 40 | + |
| 41 | + protected string $view = 'filament.server.pages.server-form-page'; |
| 42 | + |
| 43 | + /** @var array<string, mixed>|null */ |
| 44 | + public ?array $data = []; |
| 45 | + |
| 46 | + public function getTitle(): string |
| 47 | + { |
| 48 | + return trans('user-creatable-servers::strings.create_server'); |
| 49 | + } |
| 50 | + |
| 51 | + public static function getNavigationLabel(): string |
| 52 | + { |
| 53 | + return trans('user-creatable-servers::strings.create_server'); |
| 54 | + } |
| 55 | + |
| 56 | + public static function canAccess(): bool |
| 57 | + { |
| 58 | + return UserResourceLimits::where('user_id', auth()->user()->id)->exists(); |
| 59 | + } |
| 60 | + |
| 61 | + public function mount(): void |
| 62 | + { |
| 63 | + $this->form->fill(); |
| 64 | + } |
| 65 | + |
| 66 | + public function form(Schema $schema): Schema |
| 67 | + { |
| 68 | + /** @var UserResourceLimits $userResourceLimits */ |
| 69 | + $userResourceLimits = UserResourceLimits::where('user_id', auth()->user()->id)->firstOrFail(); |
| 70 | + |
| 71 | + return $schema |
| 72 | + ->statePath('data') |
| 73 | + ->columns(3) |
| 74 | + ->schema([ |
| 75 | + TextInput::make('name') |
| 76 | + ->label(trans('user-creatable-servers::strings.name')) |
| 77 | + ->required() |
| 78 | + ->default(fn () => (new RandomWordService())->word()) |
| 79 | + ->columnSpanFull(), |
| 80 | + Select::make('egg_id') |
| 81 | + ->label(trans('user-creatable-servers::strings.egg')) |
| 82 | + ->prefixIcon('tabler-egg') |
| 83 | + ->options(fn () => Egg::all()->mapWithKeys(fn (Egg $egg) => [$egg->id => $egg->name])) |
| 84 | + ->required() |
| 85 | + ->searchable() |
| 86 | + ->preload() |
| 87 | + ->live() |
| 88 | + ->afterStateUpdated(function ($state, Set $set) { |
| 89 | + $egg = Egg::find($state); |
| 90 | + |
| 91 | + $variables = $egg->variables ?? []; |
| 92 | + $serverVariables = collect(); |
| 93 | + foreach ($variables as $variable) { |
| 94 | + $serverVariables->add($variable->toArray()); |
| 95 | + } |
| 96 | + |
| 97 | + $set('variables', $serverVariables->sortBy(['sort'])->all()); |
| 98 | + for ($i = 0; $i < $serverVariables->count(); $i++) { |
| 99 | + $set("variables.$i.variable_value", $serverVariables[$i]['default_value']); |
| 100 | + $set("variables.$i.variable_id", $serverVariables[$i]['id']); |
| 101 | + } |
| 102 | + }) |
| 103 | + ->columnSpanFull(), |
| 104 | + TextInput::make('cpu') |
| 105 | + ->label(trans('user-creatable-servers::strings.cpu')) |
| 106 | + ->required() |
| 107 | + ->numeric() |
| 108 | + ->minValue($userResourceLimits->cpu > 0 ? 1 : 0) |
| 109 | + ->maxValue($userResourceLimits->getCpuLeft()) |
| 110 | + ->suffix('%'), |
| 111 | + TextInput::make('memory') |
| 112 | + ->label(trans('user-creatable-servers::strings.memory')) |
| 113 | + ->required() |
| 114 | + ->numeric() |
| 115 | + ->minValue($userResourceLimits->memory > 0 ? 1 : 0) |
| 116 | + ->maxValue($userResourceLimits->getMemoryLeft()) |
| 117 | + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB'), |
| 118 | + TextInput::make('disk') |
| 119 | + ->label(trans('user-creatable-servers::strings.disk')) |
| 120 | + ->required() |
| 121 | + ->numeric() |
| 122 | + ->minValue($userResourceLimits->disk > 0 ? 1 : 0) |
| 123 | + ->maxValue($userResourceLimits->getDiskLeft()) |
| 124 | + ->suffix(config('panel.use_binary_prefix') ? 'MiB' : 'MB'), |
| 125 | + Repeater::make('variables') |
| 126 | + ->label(trans('user-creatable-servers::strings.variables')) |
| 127 | + ->hidden(fn (Get $get) => !$get('egg_id')) |
| 128 | + ->grid(2) |
| 129 | + ->columnSpanFull() |
| 130 | + ->reorderable(false) |
| 131 | + ->addable(false) |
| 132 | + ->deletable(false) |
| 133 | + ->default([]) |
| 134 | + ->hidden(fn ($state) => empty($state)) |
| 135 | + ->schema([ |
| 136 | + StartupVariable::make('variable_value') |
| 137 | + ->fromForm() |
| 138 | + ->disabled(false), |
| 139 | + ]), |
| 140 | + ]); |
| 141 | + } |
| 142 | + |
| 143 | + protected function getHeaderActions(): array |
| 144 | + { |
| 145 | + return [ |
| 146 | + Action::make('save') |
| 147 | + ->hiddenLabel() |
| 148 | + ->action('save') |
| 149 | + ->keyBindings(['mod+s']) |
| 150 | + ->tooltip(trans('filament-panels::resources/pages/edit-record.form.actions.save.label')) |
| 151 | + ->icon('tabler-device-floppy'), |
| 152 | + ]; |
| 153 | + } |
| 154 | + |
| 155 | + protected function getHeaderWidgets(): array |
| 156 | + { |
| 157 | + return [ |
| 158 | + UserResourceLimitsOverview::class, |
| 159 | + ]; |
| 160 | + } |
| 161 | + |
| 162 | + public function save(): void |
| 163 | + { |
| 164 | + $data = $this->form->getState(); |
| 165 | + |
| 166 | + try { |
| 167 | + /** @var UserResourceLimits $userResourceLimits */ |
| 168 | + $userResourceLimits = UserResourceLimits::where('user_id', auth()->user()->id)->firstOrFail(); |
| 169 | + |
| 170 | + if ($server = $userResourceLimits->createServer($data['name'], $data['egg_id'], $data['cpu'], $data['memory'], $data['disk'], Arr::mapWithKeys($data['variables'], fn ($value) => [$value['env_variable'] => $value['variable_value']]))) { |
| 171 | + $redirectUrl = Console::getUrl(panel: 'server', tenant: $server); |
| 172 | + $this->redirect($redirectUrl, navigate: FilamentView::hasSpaMode($redirectUrl)); |
| 173 | + } |
| 174 | + } catch (Exception $exception) { |
| 175 | + report($exception); |
| 176 | + |
| 177 | + if ($exception instanceof NoViableNodeException) { |
| 178 | + Notification::make() |
| 179 | + ->title(trans('user-creatable-servers::strings.notifications.server_creation_failed')) |
| 180 | + ->body(trans('user-creatable-servers::strings.notifications.no_viable_node_found')) |
| 181 | + ->danger() |
| 182 | + ->send(); |
| 183 | + } elseif ($exception instanceof NoViableAllocationException) { |
| 184 | + Notification::make() |
| 185 | + ->title(trans('user-creatable-servers::strings.notifications.server_creation_failed')) |
| 186 | + ->body(trans('user-creatable-servers::strings.notifications.no_viable_allocation_found')) |
| 187 | + ->danger() |
| 188 | + ->send(); |
| 189 | + } else { |
| 190 | + Notification::make() |
| 191 | + ->title(trans('user-creatable-servers::strings.notifications.server_creation_failed')) |
| 192 | + ->body(trans('user-creatable-servers::strings.notifications.unknown_server_creation_error')) |
| 193 | + ->danger() |
| 194 | + ->send(); |
| 195 | + } |
| 196 | + } |
| 197 | + } |
| 198 | +} |
0 commit comments