diff --git a/app/Filament/Pages/ApiTokens.php b/app/Filament/Pages/ApiTokens.php deleted file mode 100644 index 90ef1eb6c..000000000 --- a/app/Filament/Pages/ApiTokens.php +++ /dev/null @@ -1,134 +0,0 @@ -state([ - 'token' => $this->token, - ]) - ->schema([ - Section::make() - ->columns(1) - ->schema([ - TextEntry::make('token') - ->label('API Token') - ->formatStateUsing(fn (string $state) => explode('|', $state)[1]) - ->helperText('Copy and save the token above, this token will not be shown again!') - ->color('gray') - ->copyable() - ->copyableState(fn (string $state) => explode('|', $state)[1]) - ->fontFamily(FontFamily::Mono), - ]), - ]); - } - - public function table(Table $table): Table - { - return $table - ->relationship(fn (): MorphMany => Auth::user()->tokens()) - ->headerActions([ - Action::make('createToken') - ->form([ - TextInput::make('token_name') - ->label('Name') - ->maxLength('100') - ->required() - ->autocomplete(false), - CheckboxList::make('abilities') - ->options([ - 'results:read' => 'Read results', - 'speedtests:run' => 'Run speedtest', - 'ookla:list-servers' => 'List servers', - ]) - ->descriptions([ - 'results:read' => 'Allow this token to read results.', - 'speedtests:run' => 'Allow this token to run speedtests.', - 'ookla:list-servers' => 'Allow this token to list server.', - ]) - ->bulkToggleable(), - DateTimePicker::make('token_expires_at') - ->label('Expires at') - ->nullable() - ->helperText('Leave empty for no expiration'), - ]) - ->action(function (array $data) { - $token = Auth::user()->createToken( - name: $data['token_name'], - abilities: $data['abilities'], - expiresAt: $data['token_expires_at'] ? Carbon::parse($data['token_expires_at']) : null, - ); - - $this->token = $token->plainTextToken; - }) - ->label('Create API Token') - ->modal('createToken') - ->modalWidth(MaxWidth::ExtraLarge), - ]) - ->columns([ - TextColumn::make('name') - ->searchable(), - TextColumn::make('abilities') - ->badge(), - TextColumn::make('created_at') - ->alignEnd() - ->dateTime() - ->sortable(), - TextColumn::make('last_used_at') - ->alignEnd() - ->dateTime() - ->sortable(), - TextColumn::make('expires_at') - ->alignEnd() - ->dateTime() - ->sortable(), - ]) - ->actions([ - ActionGroup::make([ - DeleteAction::make(), - ]), - ]) - ->bulkActions([ - // ... - ]); - } -} diff --git a/app/Filament/Resources/ApiTokenResource.php b/app/Filament/Resources/ApiTokenResource.php new file mode 100644 index 000000000..f5872b048 --- /dev/null +++ b/app/Filament/Resources/ApiTokenResource.php @@ -0,0 +1,158 @@ +schema([ + TextInput::make('name') + ->label('Name') + ->unique(ignoreRecord: true) + ->maxLength(100) + ->required(), + CheckboxList::make('abilities') + ->label('Abilities') + ->options([ + 'results:read' => 'Read results', + 'speedtests:run' => 'Run speedtest', + 'ookla:list-servers' => 'List servers', + ]) + ->required() + ->bulkToggleable() + ->descriptions([ + 'results:read' => 'Allow this token to read results.', + 'speedtests:run' => 'Allow this token to run speedtests.', + 'ookla:list-servers' => 'Allow this token to list servers.', + ]), + DateTimePicker::make('expires_at') + ->label('Expires at') + ->nullable() + ->native(false) + ->helperText('Leave empty for no expiration'), + ]) + ->columns([ + 'lg' => 1, + ]), + ]; + } + + public static function form(Form $form): Form + { + return $form->schema(static::getTokenFormSchema()); + } + + public static function table(Table $table): Table + { + return $table + ->query(PersonalAccessToken::query()->where('tokenable_id', Auth::id())) + ->columns([ + TextColumn::make('name')->searchable(), + TextColumn::make('abilities')->badge(), + TextColumn::make('created_at') + ->dateTime(config('app.datetime_format')) + ->timezone(config('app.display_timezone')) + ->toggleable() + ->sortable() + ->alignEnd(), + TextColumn::make('last_used_at') + ->dateTime(config('app.datetime_format')) + ->timezone(config('app.display_timezone')) + ->toggleable() + ->toggledHiddenByDefault() + ->sortable() + ->alignEnd(), + TextColumn::make('expires_at') + ->dateTime(config('app.datetime_format')) + ->timezone(config('app.display_timezone')) + ->toggleable() + ->sortable() + ->alignEnd(), + ]) + ->filters([ + TernaryFilter::make('expired') + ->label('Token Status') + ->placeholder('All tokens') + ->falseLabel('Active tokens') + ->trueLabel('Expired tokens') + ->native(false) + ->queries( + true: fn (Builder $query) => $query + ->where('expires_at', '<=', now()), + + false: fn (Builder $query) => $query + ->where(function (Builder $q) { + $q->whereNull('expires_at') + ->orWhere('expires_at', '>', now()); + }), + + blank: fn (Builder $query) => $query, + ), + SelectFilter::make('abilities') + ->label('Abilities') + ->multiple() + ->options([ + 'results:read' => 'Read results', + 'speedtests:run' => 'Run speedtest', + 'ookla:list-servers' => 'List servers', + ]) + ->query(function (Builder $query, array $data): Builder { + foreach ($data['values'] ?? [] as $value) { + $query->whereJsonContains('abilities', $value); + } + + return $query; + }), + ]) + ->actions([ + ActionGroup::make([ + EditAction::make() + ->disabled(fn ($record) => $record->expires_at !== null && $record->expires_at->isPast()) + ->modalWidth('xl'), + DeleteAction::make(), + ]), + ]) + ->bulkActions([ + DeleteBulkAction::make(), + ]); + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListApiTokens::route('/'), + ]; + } +} diff --git a/app/Filament/Resources/ApiTokenResource/Pages/ListApiTokens.php b/app/Filament/Resources/ApiTokenResource/Pages/ListApiTokens.php new file mode 100644 index 000000000..2f5cda512 --- /dev/null +++ b/app/Filament/Resources/ApiTokenResource/Pages/ListApiTokens.php @@ -0,0 +1,38 @@ +label('Create API Token') + ->form(ApiTokenResource::getTokenFormSchema()) + ->action(function (array $data): void { + $token = auth()->user()->createToken( + $data['name'], + $data['abilities'], + $data['expires_at'] ? Carbon::parse($data['expires_at']) : null + ); + + Notification::make() + ->title('Token Created') + ->body('Your token: `'.explode('|', $token->plainTextToken)[1].'`') + ->success() + ->persistent() + ->send(); + }) + ->modalWidth('xl'), + ]; + } +} diff --git a/resources/views/filament/pages/api-tokens.blade.php b/resources/views/filament/pages/api-tokens.blade.php deleted file mode 100644 index ff1070b8a..000000000 --- a/resources/views/filament/pages/api-tokens.blade.php +++ /dev/null @@ -1,11 +0,0 @@ - - @filled($token) -
- {{ $this->tokenInfolist }} -
- @endfilled - -
- {{ $this->table }} -
-