From 2aead9c914ef4ef050b8ca6008db52a1329bf04a Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 5 Oct 2026 11:28:03 +0200 Subject: [PATCH] refactor(analytics): remove application traffic overview card --- .../Project/Application/TrafficOverview.php | 86 ------------- .../application/traffic-overview.blade.php | 66 ---------- .../ApplicationTrafficOverviewTest.php | 121 ------------------ .../ResourceScopeTrafficTest.php | 23 +--- .../ResourceTrafficKeysTest.php | 5 +- 5 files changed, 3 insertions(+), 298 deletions(-) delete mode 100644 app/Livewire/Project/Application/TrafficOverview.php delete mode 100644 resources/views/livewire/project/application/traffic-overview.blade.php delete mode 100644 tests/Feature/TrafficAnalytics/ApplicationTrafficOverviewTest.php diff --git a/app/Livewire/Project/Application/TrafficOverview.php b/app/Livewire/Project/Application/TrafficOverview.php deleted file mode 100644 index 6cc30605a0..0000000000 --- a/app/Livewire/Project/Application/TrafficOverview.php +++ /dev/null @@ -1,86 +0,0 @@ -application->destination?->server; - $this->serverUuid = $server?->uuid; - $this->enabled = (bool) $server?->isTrafficAnalyticsEnabled(); - $this->eligible = $server ? (! $server->isSwarm() && ! $server->isBuildServer()) : false; - - if ($this->enabled && $server) { - try { - $client = app(SentinelTrafficClient::class, ['server' => $server]); - [$from, $to] = SentinelTrafficClient::rangeWindow('24h'); - // Compose services and previews record under their own keys. Sentinel's resource - // scope merges them exactly; an older Sentinel gets every key summed here. - $overview = $client->tryResourceOverview($this->application->uuid, $from, $to); - if ($overview === null) { - $keys = $client->resourceKeys($this->application->uuid); - if (count($keys) > 1) { - $client->prefetchAppOverviews($keys, $from, $to); - } - $overviews = array_map(fn (string $key) => $client->overview($key, $from, $to), $keys); - $overview = TrafficAnalyticsAggregator::sumOverviews($overviews)['overview']; - } - $this->overview = $overview->toArray(); - } catch (\Throwable $e) { - $this->overview = null; - } - } - } - - public function hasData(): bool - { - return $this->overview !== null && (int) ($this->overview['requests'] ?? 0) > 0; - } - - public function errorRate(): float - { - if (! $this->overview || (int) ($this->overview['requests'] ?? 0) === 0) { - return 0.0; - } - - $errors = (int) ($this->overview['s4xx'] ?? 0) + (int) ($this->overview['s5xx'] ?? 0); - - return round(($errors / $this->overview['requests']) * 100, 2); - } - - public function placeholder(): string - { - return <<<'HTML' -
- HTML; - } - - public function render() - { - return view('livewire.project.application.traffic-overview'); - } -} diff --git a/resources/views/livewire/project/application/traffic-overview.blade.php b/resources/views/livewire/project/application/traffic-overview.blade.php deleted file mode 100644 index 831a372ec2..0000000000 --- a/resources/views/livewire/project/application/traffic-overview.blade.php +++ /dev/null @@ -1,66 +0,0 @@ -@php - $analyticsRoute = route('project.application.analytics', [ - 'project_uuid' => $application->environment->project->uuid, - 'environment_uuid' => $application->environment->uuid, - 'application_uuid' => $application->uuid, - ]); -@endphp - -
- @if (! $enabled) - @if ($eligible && $serverUuid) -
-
-
-

Traffic analytics

-

- Enable Sentinel traffic analytics on this server to see requests, visitors, and - geography for this application. Restarts the proxy + Sentinel. -

-
- - Server settings - - -
-
- @endif - @else -
-
-
-

Traffic (last 24h)

-

Request activity for this application

-
- - View full analytics → - -
- - @if (! $this->hasData()) -

- No traffic recorded in the last 24h yet. -

- @else -
-
- Requests - {{ number_format($overview['requests'] ?? 0) }} -
-
- Unique visitors - {{ number_format($overview['uniqueVisitors'] ?? 0) }} -
-
- Error rate - {{ $this->errorRate() }}% -
-
- p95 latency - {{ number_format($overview['latencyP95'] ?? 0, 1) }} ms -
-
- @endif -
- @endif -
diff --git a/tests/Feature/TrafficAnalytics/ApplicationTrafficOverviewTest.php b/tests/Feature/TrafficAnalytics/ApplicationTrafficOverviewTest.php deleted file mode 100644 index 4c958f1a79..0000000000 --- a/tests/Feature/TrafficAnalytics/ApplicationTrafficOverviewTest.php +++ /dev/null @@ -1,121 +0,0 @@ -responses as $needle => $response) { - if (str_contains($url, $needle)) { - return $response; - } - } - - return '{}'; - } -} - -beforeEach(function () { - // Servers are memoized via once()/cache; clear both so DB-id reuse across tests doesn't bleed a stale enabled server. - Cache::flush(); - Server::flushIdentityMap(); - $this->team = Team::factory()->create(); - $this->user = User::factory()->create(); - $this->team->members()->attach($this->user->id, ['role' => 'owner']); - $this->actingAs($this->user); - session(['currentTeam' => $this->team]); - $this->privateKey = PrivateKey::factory()->create(['team_id' => $this->team->id]); -}); - -function makeAppOnServer(bool $analyticsEnabled): Application -{ - $server = Server::factory()->create([ - 'team_id' => test()->team->id, - 'private_key_id' => test()->privateKey->id, - ]); - $server->settings->is_traffic_analytics_enabled = $analyticsEnabled; - $server->settings->save(); - - $project = Project::factory()->create(['team_id' => test()->team->id]); - $environment = Environment::factory()->create(['project_id' => $project->id]); - $destination = StandaloneDocker::where('server_id', $server->id)->first() - ?? StandaloneDocker::factory()->create(['server_id' => $server->id, 'network' => 'coolify-test']); - - return Application::factory()->create([ - 'name' => 'Widget App', - 'environment_id' => $environment->id, - 'destination_id' => $destination->id, - 'destination_type' => StandaloneDocker::class, - ]); -} - -it('shows last-24h KPIs and a link to full analytics when enabled with data', function () { - $application = makeAppOnServer(true); - - app()->bind(SentinelTrafficClient::class, function ($app, $params) { - $client = new FakeAppOverviewTrafficClient($params['server']); - $client->responses = [ - '/traffic/overview' => json_encode([ - 'requests' => 4200, - 'bytes_in' => 5000, - 'bytes_out' => 25000, - 'status' => ['s2xx' => 4000, 's3xx' => 100, 's4xx' => 80, 's5xx' => 20], - 'latency' => ['p50' => 12.5, 'p95' => 45.2, 'p99' => 90.1], - 'unique_visitors' => 1234, - ]), - ]; - - return $client; - }); - - $component = loadLazy(Livewire::test(TrafficOverview::class, ['application' => $application])); - $component->assertOk() - ->assertSet('enabled', true) - ->assertSee('Traffic (last 24h)') - ->assertSee('4,200') - ->assertSee('1,234') - ->assertSee('View full analytics'); -}); - -it('shows the muted no-data note when enabled but no traffic recorded', function () { - $application = makeAppOnServer(true); - - app()->bind(SentinelTrafficClient::class, function ($app, $params) { - $client = new FakeAppOverviewTrafficClient($params['server']); - $client->responses = ['/traffic/overview' => json_encode(['requests' => 0])]; - - return $client; - }); - - loadLazy(Livewire::test(TrafficOverview::class, ['application' => $application])) - ->assertOk() - ->assertSee('No traffic recorded in the last 24h yet'); -}); - -it('shows the enable nudge when analytics is disabled on an eligible server', function () { - $application = makeAppOnServer(false); - - loadLazy(Livewire::test(TrafficOverview::class, ['application' => $application])) - ->assertOk() - ->assertSet('enabled', false) - ->assertSee('Traffic analytics') - ->assertSee('Server settings') - ->assertDontSee('Traffic (last 24h)'); -}); diff --git a/tests/Feature/TrafficAnalytics/ResourceScopeTrafficTest.php b/tests/Feature/TrafficAnalytics/ResourceScopeTrafficTest.php index 36ef965c08..2b6f6589e7 100644 --- a/tests/Feature/TrafficAnalytics/ResourceScopeTrafficTest.php +++ b/tests/Feature/TrafficAnalytics/ResourceScopeTrafficTest.php @@ -2,7 +2,6 @@ use App\Livewire\Analytics as GlobalAnalytics; use App\Livewire\Project\Application\Analytics as ApplicationAnalytics; -use App\Livewire\Project\Application\TrafficOverview; use App\Livewire\Project\Service\Analytics as ServiceAnalytics; use App\Models\Application; use App\Models\Environment; @@ -369,31 +368,11 @@ it('falls back to the per-key merge with approximate badges when Sentinel lacks // The absence is cached: a refresh within the ttl goes straight to the per-key path. $component->call('loadData')->assertSet('uniquesApproximate', true); - loadLazy(Livewire::test(TrafficOverview::class, ['application' => $this->application]))->assertOk(); + loadLazy(Livewire::test(ApplicationAnalytics::class, ['application' => $this->application]))->assertOk(); expect(requestedResourceScopeUrls('/resource/'))->toHaveCount($probes); }); -it('shows the resource overview in the application traffic card', function () { - bindResourceScopeFake(resourceScopeResponses($this->application->uuid, resourceScopeOverview(4200, 33))); - - $component = loadLazy(Livewire::test(TrafficOverview::class, ['application' => $this->application])) - ->assertOk() - ->assertSee('4,200'); - - expect($component->instance()->overview['uniqueVisitors'])->toBe(33) - ->and(requestedResourceScopeUrls('/resource/'))->toHaveCount(1) - ->and(requestedResourceScopeUrls('/traffic/dashboard'))->toBeEmpty(); -}); - -it('sums the keys in the application traffic card when Sentinel lacks the resource routes', function () { - bindResourceScopeFake(perKeyFallbackResponses($this->application->uuid, $this->apiKey, $this->webKey)); - - loadLazy(Livewire::test(TrafficOverview::class, ['application' => $this->application])) - ->assertOk() - ->assertSee('150'); -}); - it('uses the resource scope when the global analytics filter selects a resource', function () { bindResourceScopeFake([ ...resourceScopeResponses($this->application->uuid, resourceScopeOverview(150, 12)), diff --git a/tests/Feature/TrafficAnalytics/ResourceTrafficKeysTest.php b/tests/Feature/TrafficAnalytics/ResourceTrafficKeysTest.php index a2d904796b..4b2f6ce84e 100644 --- a/tests/Feature/TrafficAnalytics/ResourceTrafficKeysTest.php +++ b/tests/Feature/TrafficAnalytics/ResourceTrafficKeysTest.php @@ -6,7 +6,6 @@ use App\Data\Traffic\TrafficPathData; use App\Data\Traffic\TrafficSeriesBucketData; use App\Livewire\Analytics as GlobalAnalytics; use App\Livewire\Project\Application\Analytics as ApplicationAnalytics; -use App\Livewire\Project\Application\TrafficOverview; use App\Models\Application; use App\Models\Environment; use App\Models\PrivateKey; @@ -286,7 +285,7 @@ it('shows a compose application with the data of all its compose service keys', ->and($paths['/checkout']['domain'])->toBe('www.shop.test'); }); -it('sums every compose key in the application traffic card', function () { +it('sums every compose key on the application analytics page', function () { $application = makeTrafficComposeApplication(); $apiKey = $application->uuid.'-'.traefikSafeServiceNameSegment('api'); $webKey = $application->uuid.'-'.traefikSafeServiceNameSegment('web'); @@ -297,7 +296,7 @@ it('sums every compose key in the application traffic card', function () { "/app/{$webKey}/traffic/overview" => resourceKeysOverview(200), ]); - loadLazy(Livewire::test(TrafficOverview::class, ['application' => $application])) + loadLazy(Livewire::test(ApplicationAnalytics::class, ['application' => $application])) ->assertOk() ->assertSee('4,200'); });