170 lines
6.5 KiB
PHP
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;
|
|
}
|
|
}
|