Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ef91600
Add shared Blade interface primitives
vaheed Jul 20, 2026
4c5332a
Add shared Blade interface primitives
vaheed Jul 20, 2026
fe3bcd7
Add shared Blade interface primitives
vaheed Jul 20, 2026
4bb07e4
Add shared Blade interface primitives
vaheed Jul 20, 2026
d83fa0a
Replace the starter landing page
vaheed Jul 20, 2026
0ef8cce
Replace the starter landing page
vaheed Jul 20, 2026
f77d4a1
Adopt the shared Blade interface primitives
vaheed Jul 20, 2026
75e22fc
Adopt the shared Blade interface primitives
vaheed Jul 20, 2026
82e97e2
Adopt the shared Blade interface primitives
vaheed Jul 20, 2026
9746995
Expose supported runtime operations in Filament
vaheed Jul 20, 2026
7972d57
Expose supported runtime operations in Filament
vaheed Jul 20, 2026
e530f78
Expose supported runtime operations in Filament
vaheed Jul 20, 2026
4a0978d
Organize panel navigation consistently
vaheed Jul 20, 2026
23b803f
Organize panel navigation consistently
vaheed Jul 20, 2026
da61403
Test frontend operation and permission coverage
vaheed Jul 20, 2026
27592f1
Test frontend operation and permission coverage
vaheed Jul 20, 2026
36766f5
Add a reusable responsive data table
vaheed Jul 20, 2026
3452b15
Document frontend route coverage
vaheed Jul 20, 2026
4116835
Unify analytics tables and states
vaheed Jul 20, 2026
fa821e4
Unify analytics tables and states
vaheed Jul 20, 2026
e28a498
Document frontend routes and manual qualification
vaheed Jul 20, 2026
5725fc2
Use the product title on the landing page
vaheed Jul 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ make dev-scale-e2e
`make dev-e2e` uses the real HTTP APIs, PostgreSQL, queues, PowerDNS, DNSdist,
mTLS edge control, pool migration, Pebble DNS-01 issuance, cache/purge delivery,
and OpenResty HTTP/HTTPS runtime. Browser acceptance is deliberately manual and is specified in
[manual browser qualification](docs/manual-browser-qualification.md).
[manual browser qualification](docs/manual-browser-qualification.md). The
[frontend route coverage audit](docs/frontend-route-coverage.md) maps every
application route family to its human, automation-only, or agent-only surface.

Production uses immutable commit-SHA GHCR images through
[`compose.prod.yml`](compose.prod.yml); it has no `latest` tags and no production
Expand Down
39 changes: 39 additions & 0 deletions core/app/Filament/Admin/Pages/Telemetry.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

namespace App\Filament\Admin\Pages;

use App\Http\Controllers\Admin\UsageController;
use App\Models\Domain;
use App\Models\UsageRollup;
use App\Support\AnalyticsStore;
use Carbon\CarbonImmutable;
use Filament\Actions\Action;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\Select;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Support\Facades\Http;
use Throwable;
Expand All @@ -21,6 +27,39 @@ class Telemetry extends Page

protected string $view = 'filament.admin.pages.telemetry';

protected function getHeaderActions(): array
{
return [
Action::make('rebuildUsage')
->label('Rebuild usage')
->icon('heroicon-o-arrow-path')
->color('warning')
->schema([
Select::make('domain_id')->label('Domain (optional)')
->options(fn (): array => Domain::query()->orderBy('name')->limit(500)->pluck('name', 'id')->all())
->searchable(),
DateTimePicker::make('from')->label('From (UTC)')->timezone('UTC')->seconds(false)->required(),
DateTimePicker::make('to')->label('To (UTC)')->timezone('UTC')->seconds(false)->required()->after('from'),
])
->fillForm(fn (): array => [
'from' => CarbonImmutable::now('UTC')->subHour()->startOfHour(),
'to' => CarbonImmutable::now('UTC')->startOfHour(),
])
->requiresConfirmation()
->action(function (array $data): void {
request()->merge([
'domain_id' => $data['domain_id'] ?? null,
'from' => CarbonImmutable::parse($data['from'], 'UTC')->startOfHour()->toIso8601String(),
'to' => CarbonImmutable::parse($data['to'], 'UTC')->startOfHour()->toIso8601String(),
]);
$response = app(UsageController::class)->rebuild(request());
$operation = $response->getData(true)['data'];
Notification::make()->info()->title('Usage rebuild queued')
->body("Operation {$operation['operation_id']} will rebuild complete UTC hours without changing serving behavior.")->send();
}),
];
}

public function getStateProperty(): array
{
$store = app(AnalyticsStore::class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
namespace App\Filament\Admin\Resources\DnsClusters\Pages;

use App\Filament\Admin\Resources\DnsClusters\DnsClusterResource;
use App\Http\Controllers\Admin\DnsOperationController;
use Filament\Actions\Action;
use Filament\Actions\CreateAction;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords;

class ListDnsClusters extends ListRecords
Expand All @@ -12,6 +15,19 @@ class ListDnsClusters extends ListRecords

protected function getHeaderActions(): array
{
return [CreateAction::make()];
return [
Action::make('reconcileAllZones')
->label('Reconcile all zones')
->icon('heroicon-o-arrow-path')
->color('warning')
->requiresConfirmation()
->action(function (): void {
$response = app(DnsOperationController::class)->reconcile(request());
$operation = $response->getData(true)['data'];
Notification::make()->info()->title('Global DNS reconciliation queued')
->body("Operation {$operation['id']} will process active zones in bounded chunks.")->send();
}),
CreateAction::make(),
];
}
}
22 changes: 20 additions & 2 deletions core/app/Filament/Domain/Resources/Domains/Pages/ViewDomain.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use App\Enums\DomainLifecycleState;
use App\Filament\Domain\Resources\Domains\DomainResource;
use App\Http\Controllers\CacheController;
use App\Http\Controllers\DnsDeploymentController;
use App\Http\Controllers\ProxyController;
use App\Jobs\EnsureManagedCertificates;
use App\Jobs\ImportDnsZone;
use App\Jobs\ReconcileDnsZone;
Expand Down Expand Up @@ -57,6 +59,22 @@ public function getSubheading(): ?string
protected function getHeaderActions(): array
{
$actions = [
Action::make('reconcileDns')->label('Reconcile authoritative DNS')->icon('heroicon-o-arrow-path')
->visible(fn (): bool => $this->record->lifecycle_state === DomainLifecycleState::Active)
->action(function (): void {
$response = app(DnsDeploymentController::class)->reconcile(request(), $this->record);
$operation = $response->getData(true)['data'];
Notification::make()->info()->title('DNS reconciliation queued')
->body("Operation {$operation['id']} will preserve the previous valid zone until activation succeeds.")->send();
}),
Action::make('deployEdge')->label('Reconcile edge delivery')->icon('heroicon-o-cloud-arrow-up')
->visible(fn (): bool => $this->record->dnsRecords()->where('mode', 'proxied')->exists())
->action(function (): void {
$response = app(ProxyController::class)->deploy(request(), $this->record);
$operation = $response->getData(true)['data'];
Notification::make()->info()->title('Edge reconciliation queued')
->body("Operation {$operation['operation_id']} will deploy the latest desired revision.")->send();
}),
Action::make('tlsMode')->label('TLS mode')->icon('heroicon-o-lock-closed')->schema([
Select::make('mode')->options(['managed' => 'Managed', 'custom' => 'Custom', 'disabled' => 'Disabled'])->required(),
])->fillForm(fn (): array => ['mode' => $this->record->tls_mode])
Expand Down Expand Up @@ -399,12 +417,12 @@ static function () use ($zone): void {
->all();

return [
ActionGroup::make($group(['verifyNameservers', 'forceVerifyNameservers', 'activate', 'disable', 'importZone', 'exportZone']))
ActionGroup::make($group(['verifyNameservers', 'forceVerifyNameservers', 'reconcileDns', 'activate', 'disable', 'importZone', 'exportZone']))
->label('Domain actions')
->icon('heroicon-o-globe-alt')
->color('gray')
->button(),
ActionGroup::make($group(['proxyDefaults', 'rollbackProxy', 'moveEdgePool']))
ActionGroup::make($group(['deployEdge', 'proxyDefaults', 'rollbackProxy', 'moveEdgePool']))
->label('Delivery')
->icon('heroicon-o-cloud')
->button(),
Expand Down
2 changes: 2 additions & 0 deletions core/app/Filament/Shared/Pages/ApiTokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class ApiTokens extends Page

protected static ?string $navigationLabel = 'API tokens';

protected static string|\UnitEnum|null $navigationGroup = 'Account';

protected static ?string $slug = 'tokens';

protected string $view = 'filament.shared.pages.api-tokens';
Expand Down
2 changes: 1 addition & 1 deletion core/app/Providers/Filament/AdminPanelProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function panel(Panel $panel): Panel
->colors(['primary' => Color::Blue])
->sidebarCollapsibleOnDesktop()
->databaseNotifications()
->navigationGroups(['Control plane', 'Customers', 'Edge network', 'Operations'])
->navigationGroups(['Control plane', 'Customers', 'Edge network', 'Operations', 'Observe', 'Account'])
->discoverResources(in: app_path('Filament/Admin/Resources'), for: 'App\\Filament\\Admin\\Resources')
->resources([DomainResource::class])
->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages')
Expand Down
44 changes: 44 additions & 0 deletions core/resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,47 @@
--font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
}

@layer base {
body { margin: 0; }
}

@layer components {
.cdn-landing-body { min-height: 100vh; background: #f8fafc; color: #0f172a; font-family: var(--font-sans); }
.cdn-landing-shell { width: min(72rem, calc(100% - 2rem)); margin-inline: auto; }
.cdn-landing-header { display: flex; min-height: 5rem; align-items: center; justify-content: space-between; gap: 1rem; border-bottom: 1px solid #e2e8f0; }
.cdn-landing-brand { display: inline-flex; align-items: center; gap: .65rem; color: inherit; font-weight: 750; letter-spacing: -.02em; text-decoration: none; }
.cdn-landing-mark { display: grid; width: 2rem; height: 2rem; place-items: center; border-radius: .65rem; background: #2563eb; color: white; font-size: .875rem; }
.cdn-landing-link { color: #475569; font-size: .875rem; font-weight: 600; text-decoration: none; }
.cdn-landing-link:hover { color: #1d4ed8; }
.cdn-landing-hero { display: grid; align-items: center; gap: 3rem; padding-block: clamp(4rem, 10vw, 8rem); }
.cdn-landing-eyebrow { margin: 0 0 1rem; color: #2563eb; font-size: .75rem; font-weight: 750; letter-spacing: .1em; text-transform: uppercase; }
.cdn-landing-hero h1 { max-width: 48rem; margin: 0; font-size: clamp(2.4rem, 7vw, 4.5rem); font-weight: 750; letter-spacing: -.055em; line-height: 1.02; }
.cdn-landing-copy { max-width: 42rem; margin: 1.5rem 0 0; color: #475569; font-size: 1.05rem; line-height: 1.75; }
.cdn-landing-actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 2rem; }
.cdn-landing-button { display: inline-flex; min-height: 2.75rem; align-items: center; justify-content: center; border: 1px solid #2563eb; border-radius: .7rem; background: #2563eb; color: white; padding: .65rem 1rem; font-size: .875rem; font-weight: 700; text-decoration: none; }
.cdn-landing-button:hover { background: #1d4ed8; }
.cdn-landing-button-secondary { border-color: #cbd5e1; background: white; color: #1e293b; }
.cdn-landing-button-secondary:hover { border-color: #94a3b8; background: #f8fafc; }
.cdn-landing-panel { border: 1px solid #dbeafe; border-radius: 1.25rem; background: white; padding: 1.5rem; box-shadow: 0 24px 60px rgb(15 23 42 / .08); }
.cdn-landing-panel-label { color: #64748b; font-size: .75rem; font-weight: 750; letter-spacing: .08em; text-transform: uppercase; }
.cdn-landing-panel ul { display: grid; gap: .25rem; margin: 1rem 0 0; padding: 0; list-style: none; }
.cdn-landing-panel li { display: flex; gap: 1rem; align-items: center; border-radius: .75rem; padding: .9rem; }
.cdn-landing-panel li:nth-child(odd) { background: #f8fafc; }
.cdn-landing-panel li > span { color: #2563eb; font-size: .75rem; font-weight: 750; }
.cdn-landing-panel strong, .cdn-landing-panel small { display: block; }
.cdn-landing-panel small { margin-top: .2rem; color: #64748b; }
}

@media (min-width: 56rem) {
.cdn-landing-hero { grid-template-columns: minmax(0, 1.45fr) minmax(18rem, .55fr); }
}

@media (prefers-color-scheme: dark) {
.cdn-landing-body { background: #090e1a; color: #f8fafc; }
.cdn-landing-header { border-color: #1e293b; }
.cdn-landing-link, .cdn-landing-copy { color: #94a3b8; }
.cdn-landing-button-secondary, .cdn-landing-panel { border-color: #334155; background: #111827; color: #f8fafc; }
.cdn-landing-button-secondary:hover, .cdn-landing-panel li:nth-child(odd) { background: #1e293b; }
.cdn-landing-panel small, .cdn-landing-panel-label { color: #94a3b8; }
}
13 changes: 13 additions & 0 deletions core/resources/views/components/ui/data-table.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@props(['caption' => null])

<div {{ $attributes->class('cdn-table-wrap') }}>
<table class="cdn-data-table">
@if (filled($caption))
<caption class="sr-only">{{ $caption }}</caption>
@endif
@if (isset($header))
<thead>{{ $header }}</thead>
@endif
<tbody>{{ $slot }}</tbody>
</table>
</div>
14 changes: 14 additions & 0 deletions core/resources/views/components/ui/empty-state.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@props([
'title' => null,
'description' => null,
])

<div {{ $attributes->class('cdn-empty-state') }} role="status">
@if (filled($title))
<div class="cdn-empty-title">{{ $title }}</div>
@endif
<div class="cdn-empty-description">{{ $description ?? $slot }}</div>
@if (isset($actions))
<div class="cdn-empty-actions">{{ $actions }}</div>
@endif
</div>
20 changes: 20 additions & 0 deletions core/resources/views/components/ui/list-row.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@props([
'title',
'meta' => null,
'href' => null,
])

@php($tag = $href ? 'a' : 'div')

<{{ $tag }} {{ $attributes->class('cdn-list-row') }} @if ($href) href="{{ $href }}" @endif>
<div class="min-w-0">
<div class="cdn-row-title">{{ $title }}</div>
@if (filled($meta))
<div class="cdn-row-meta">{{ $meta }}</div>
@endif
{{ $slot }}
</div>
@if (isset($aside))
<div class="cdn-row-aside">{{ $aside }}</div>
@endif
</{{ $tag }}>
21 changes: 21 additions & 0 deletions core/resources/views/components/ui/stat-card.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@props([
'label',
'value',
'description' => null,
'tone' => 'neutral',
'href' => null,
])

@php($tag = $href ? 'a' : 'div')

<{{ $tag }}
{{ $attributes->class('cdn-stat-card') }}
data-tone="{{ $tone }}"
@if ($href) href="{{ $href }}" aria-label="Open {{ $label }}" @endif
>
<div class="cdn-stat-label">{{ $label }}</div>
<div class="cdn-stat-value">{{ $value }}</div>
@if (filled($description))
<div class="cdn-stat-description">{{ $description }}</div>
@endif
</{{ $tag }}>
5 changes: 5 additions & 0 deletions core/resources/views/components/ui/status-pill.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@props(['tone' => 'neutral'])

<span {{ $attributes->class('cdn-status-pill') }} data-tone="{{ $tone }}">
{{ $slot }}
</span>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<x-filament-panels::page>
<form wire:submit="save" class="space-y-6">
{{ $this->form }}
<x-filament::button type="submit">Validate and save platform settings</x-filament::button>
<x-ui.form-actions submit="save" label="Validate and save platform settings" loading-label="Validating and saving…" />
</form>
</x-filament-panels::page>
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<x-filament-panels::page>
<form wire:submit="previewChanges" class="space-y-6">
{{ $this->form }}
<x-filament::button type="submit">Validate and preview</x-filament::button>
<x-ui.form-actions submit="previewChanges" label="Validate and preview" loading-label="Validating…" />
</form>
@if ($preview)
<div class="mt-6 space-y-4 rounded-xl border border-gray-200 p-4 dark:border-white/10">
<p class="text-sm">Review the normalized high-risk DNS identity payload before applying it.</p>
<pre class="max-h-80 overflow-auto text-xs">{{ json_encode($preview, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) }}</pre>
<x-filament::button wire:click="save" color="danger">Confirm and queue update</x-filament::button>
<x-filament::button wire:click="save" wire:loading.attr="disabled" wire:target="save" color="danger">
<span wire:loading.remove wire:target="save">Confirm and queue update</span>
<span wire:loading wire:target="save">Queuing update…</span>
</x-filament::button>
</div>
@endif
</x-filament-panels::page>
Loading
Loading