Files
jabali-panel/app/Filament/Jabali/Pages/CdnIntegration.php
2026-01-28 04:19:30 +02:00

170 lines
6.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Filament\Jabali\Pages;
use App\Models\CloudflareZone;
use App\Models\Domain;
use BackedEnum;
use Exception;
use Filament\Actions\Action;
use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
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\Contracts\Support\Htmlable;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
class CdnIntegration extends Page implements HasActions, HasTable
{
use InteractsWithActions;
use InteractsWithTable;
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-cloud';
protected static ?int $navigationSort = 20;
protected static ?string $slug = 'cdn-integration';
protected string $view = 'filament.jabali.pages.cdn-integration';
public function getTitle(): string|Htmlable
{
return __('CDN Integration');
}
public static function getNavigationLabel(): string
{
return __('CDN Integration');
}
public function table(Table $table): Table
{
return $table
->query(Domain::query()->where('user_id', Auth::id()))
->columns([
TextColumn::make('domain')
->label(__('Domain'))
->searchable()
->sortable(),
IconColumn::make('cloudflare')
->label(__('Cloudflare'))
->boolean()
->state(fn (Domain $record) => $this->getZone($record) !== null)
->trueColor('success')
->falseColor('gray'),
])
->recordActions([
Action::make('connect')
->label(__('Connect'))
->icon('heroicon-o-link')
->color('primary')
->visible(fn (Domain $record) => $this->getZone($record) === null)
->form([
TextInput::make('api_token')
->label(__('Cloudflare API Token'))
->password()
->revealable()
->required(),
])
->action(function (Domain $record, array $data): void {
$token = $data['api_token'];
try {
$zone = $this->fetchCloudflareZone($record->domain, $token);
CloudflareZone::updateOrCreate(
[
'user_id' => Auth::id(),
'domain_id' => $record->id,
],
[
'zone_id' => $zone['id'],
'account_id' => $zone['account']['id'] ?? null,
'api_token' => $token,
]
);
Notification::make()->title(__('Cloudflare connected'))->success()->send();
} catch (Exception $e) {
Notification::make()->title(__('Connection failed'))->body($e->getMessage())->danger()->send();
}
}),
Action::make('purge')
->label(__('Purge Cache'))
->icon('heroicon-o-arrow-path')
->color('warning')
->visible(fn (Domain $record) => $this->getZone($record) !== null)
->requiresConfirmation()
->action(function (Domain $record): void {
$zone = $this->getZone($record);
if (! $zone) {
return;
}
try {
$response = Http::withToken($zone->api_token)
->post("https://api.cloudflare.com/client/v4/zones/{$zone->zone_id}/purge_cache", [
'purge_everything' => true,
]);
if (! ($response->json('success') ?? false)) {
throw new Exception($response->json('errors.0.message') ?? 'Cloudflare request failed');
}
Notification::make()->title(__('Cache purged'))->success()->send();
} catch (Exception $e) {
Notification::make()->title(__('Purge failed'))->body($e->getMessage())->danger()->send();
}
}),
Action::make('disconnect')
->label(__('Disconnect'))
->icon('heroicon-o-x-mark')
->color('gray')
->visible(fn (Domain $record) => $this->getZone($record) !== null)
->requiresConfirmation()
->action(function (Domain $record): void {
$zone = $this->getZone($record);
if ($zone) {
$zone->delete();
}
Notification::make()->title(__('Cloudflare disconnected'))->success()->send();
}),
])
->emptyStateHeading(__('No domains found'))
->emptyStateDescription(__('Add a domain before connecting to a CDN'));
}
protected function getZone(Domain $record): ?CloudflareZone
{
return CloudflareZone::query()
->where('user_id', Auth::id())
->where('domain_id', $record->id)
->first();
}
protected function fetchCloudflareZone(string $domain, string $token): array
{
$response = Http::withToken($token)->get('https://api.cloudflare.com/client/v4/zones', [
'name' => $domain,
]);
if (! ($response->json('success') ?? false)) {
throw new Exception($response->json('errors.0.message') ?? 'Cloudflare request failed');
}
$zone = $response->json('result.0');
if (! $zone) {
throw new Exception(__('Zone not found for :domain', ['domain' => $domain]));
}
return $zone;
}
}