You've already forked Atomcms-edit
0c6c558a59
- Embed widget: standalone iframe player with dark/light/transparent themes, copy-paste embed code admin page - Real-time SSE: streaming now-playing/listeners/dj events, replaces polling in radio-player and embed - Song history: auto-records song changes to radio_song_plays table, Filament resource to view - DJ moderation: unified panel for shouts approval, song request queue, DJ applications - Auto DJ: playlist management with round-robin playback when no DJ is live - Refactored radio-player Alpine component to use EventSource API with auto-reconnect
197 lines
7.3 KiB
PHP
197 lines
7.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Pages\Radio;
|
|
|
|
use App\Models\RadioApiKey;
|
|
use Filament\Actions\Action;
|
|
use Filament\Actions\ActionGroup;
|
|
use Filament\Forms\Components\Select;
|
|
use Filament\Forms\Components\TextInput;
|
|
use Filament\Forms\Components\Toggle;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Filament\Schemas\Components\Section;
|
|
use Filament\Schemas\Components\Tabs;
|
|
use Filament\Schemas\Components\Tabs\Tab;
|
|
use Filament\Schemas\Schema;
|
|
use Filament\Tables\Columns\IconColumn;
|
|
use Filament\Tables\Columns\TextColumn;
|
|
use Filament\Tables\Concerns\InteractsWithTable;
|
|
use Filament\Tables\Contracts\HasTable;
|
|
use Filament\Tables\Table;
|
|
use Illuminate\Support\Facades\Blade;
|
|
use Illuminate\Support\HtmlString;
|
|
|
|
final class ApiKeys extends Page implements HasTable
|
|
{
|
|
use InteractsWithTable;
|
|
|
|
#[\Override]
|
|
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-key';
|
|
|
|
#[\Override]
|
|
protected static string|\UnitEnum|null $navigationGroup = 'Radio';
|
|
|
|
#[\Override]
|
|
protected static ?string $navigationLabel = 'API Sleutels';
|
|
|
|
#[\Override]
|
|
protected static ?string $title = 'Radio API Sleutels';
|
|
|
|
#[\Override]
|
|
protected string $view = 'filament.pages.radio.api-keys';
|
|
|
|
public ?string $newKey = null;
|
|
|
|
public function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->query(RadioApiKey::query())
|
|
->defaultSort('created_at', 'desc')
|
|
->columns([
|
|
TextColumn::make('name')
|
|
->label('Naam')
|
|
->searchable(),
|
|
TextColumn::make('key')
|
|
->label('API Key')
|
|
->formatStateUsing(fn ($state) => substr($state, 0, 16) . '...')
|
|
->copyable()
|
|
->copyMessage('Gekopieerd!'),
|
|
TextColumn::make('rate_limit')
|
|
->label('Rate Limit')
|
|
->badge()
|
|
->color('info'),
|
|
IconColumn::make('is_active')
|
|
->label('Actief')
|
|
->boolean()
|
|
->trueIcon('heroicon-o-check-circle')
|
|
->falseIcon('heroicon-o-x-circle')
|
|
->trueColor('success')
|
|
->falseColor('danger'),
|
|
TextColumn::make('last_used_at')
|
|
->label('Laatst gebruikt')
|
|
->dateTime('d-m-Y H:i')
|
|
->placeholder('Nooit'),
|
|
TextColumn::make('expires_at')
|
|
->label('Verloopt')
|
|
->dateTime('d-m-Y')
|
|
->placeholder('Nooit'),
|
|
TextColumn::make('created_at')
|
|
->label('Aangemaakt')
|
|
->dateTime('d-m-Y H:i'),
|
|
])
|
|
->recordActions(function ($record) {
|
|
return [
|
|
ActionGroup::make([
|
|
Action::make('toggle')
|
|
->label($record->is_active ? 'Deactiveren' : 'Activeren')
|
|
->icon($record->is_active ? 'heroicon-o-pause' : 'heroicon-o-play')
|
|
->action(fn () => $this->toggleKey($record)),
|
|
Action::make('delete')
|
|
->label('Verwijderen')
|
|
->icon('heroicon-o-trash')
|
|
->color('danger')
|
|
->requiresConfirmation()
|
|
->action(fn () => $record->delete()),
|
|
]),
|
|
];
|
|
})
|
|
->toolbarActions([
|
|
Action::make('create')
|
|
->label('Nieuwe API Sleutel')
|
|
->icon('heroicon-o-plus')
|
|
->color('primary')
|
|
->form([
|
|
TextInput::make('name')
|
|
->label('Naam')
|
|
->required()
|
|
->maxLength(255)
|
|
->placeholder('Bijv. Discord Bot, Mobiele App'),
|
|
TextInput::make('allowed_ips')
|
|
->label('Toegestane IP-adressen')
|
|
->placeholder('Leeg laten voor alle IPs (bijv. 1.2.3.4, 5.6.7.8)')
|
|
->helperText('Komma-gescheiden lijst van IP-adressen. Leeg = alle IPs toegestaan.'),
|
|
Select::make('rate_limit')
|
|
->label('Rate Limit (verzoeken per minuut)')
|
|
->options([
|
|
60 => '60/min',
|
|
100 => '100/min',
|
|
300 => '300/min',
|
|
500 => '500/min',
|
|
1000 => '1000/min',
|
|
])
|
|
->default(300),
|
|
Select::make('permissions')
|
|
->label('Permissies')
|
|
->multiple()
|
|
->options([
|
|
'*' => 'Alle permissies',
|
|
'now-playing' => 'Nu Afspelen',
|
|
'listeners' => 'Luisteraars',
|
|
'current-dj' => 'Huidige DJ',
|
|
'config' => 'Configuratie',
|
|
'shouts' => 'Shouts',
|
|
'points' => 'Punten',
|
|
'requests' => 'Song Requests',
|
|
])
|
|
->default(['*']),
|
|
Toggle::make('set_expiration')
|
|
->label('Vervaldatum instellen')
|
|
->live(),
|
|
TextInput::make('expires_at')
|
|
->label('Vervaldatum')
|
|
->type('date')
|
|
->visible(fn ($get) => $get('set_expiration')),
|
|
])
|
|
->action(function (array $data) {
|
|
$this->createKey($data);
|
|
}),
|
|
]);
|
|
}
|
|
|
|
private function createKey(array $data): void
|
|
{
|
|
$key = RadioApiKey::generate($data['name'], [
|
|
'allowed_ips' => $data['allowed_ips'] ?? null,
|
|
'permissions' => $data['permissions'] ?? ['*'],
|
|
'rate_limit' => $data['rate_limit'] ?? 300,
|
|
'expires_at' => ! empty($data['set_expiration']) && ! empty($data['expires_at'])
|
|
? $data['expires_at'] : null,
|
|
]);
|
|
|
|
$this->newKey = $key->key;
|
|
|
|
Notification::make()
|
|
->success()
|
|
->title('API Sleutel Aangemaakt!')
|
|
->body('Kopieer de sleutel nu - deze wordt niet meer getoond.')
|
|
->send();
|
|
}
|
|
|
|
private function toggleKey(RadioApiKey $key): void
|
|
{
|
|
$key->update(['is_active' => ! $key->is_active]);
|
|
|
|
Notification::make()
|
|
->success()
|
|
->title($key->is_active ? 'API Sleutel geactiveerd' : 'API Sleutel gedeactiveerd')
|
|
->send();
|
|
}
|
|
|
|
#[\Override]
|
|
protected function getHeaderActions(): array
|
|
{
|
|
return [];
|
|
}
|
|
|
|
public function getNewKey(): ?string
|
|
{
|
|
$key = $this->newKey;
|
|
$this->newKey = null;
|
|
|
|
return $key;
|
|
}
|
|
}
|