mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-14 16:52:45 -04:00
A resource with several traffic keys (Compose services, previews) was
queried key by key, and unique visitors were summed, so one visitor
on two keys counted twice. When Sentinel has the new
/api/resource/{uuid}/traffic routes, Coolify now asks once per
resource and gets merged unique visitors and latency percentiles; the
"approximate" badges go away for a resource on one server. Older
Sentinel versions answer 404: Coolify remembers this for 5 minutes and
uses the per-key merge as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
87 lines
3.0 KiB
PHP
87 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Project\Application;
|
|
|
|
use App\Models\Application;
|
|
use App\Services\SentinelTrafficClient;
|
|
use App\Services\TrafficAnalyticsAggregator;
|
|
use Livewire\Attributes\Lazy;
|
|
use Livewire\Component;
|
|
|
|
/**
|
|
* Compact last-24h traffic KPI card for the application General page. Lazy-loaded so
|
|
* the General page isn't blocked by Sentinel's docker-exec round-trip.
|
|
*/
|
|
#[Lazy]
|
|
class TrafficOverview extends Component
|
|
{
|
|
public Application $application;
|
|
|
|
public bool $enabled = false;
|
|
|
|
public bool $eligible = false;
|
|
|
|
public ?string $serverUuid = null;
|
|
|
|
public ?array $overview = null;
|
|
|
|
public function mount(): void
|
|
{
|
|
// Runs in the deferred lazy-load request, so the Sentinel fetch never blocks
|
|
// the initial General-page render.
|
|
$server = $this->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'
|
|
<div class="h-24 w-full animate-pulse rounded-xl border border-neutral-200 bg-neutral-50 dark:border-white/[0.08] dark:bg-white/[0.02]"></div>
|
|
HTML;
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.project.application.traffic-overview');
|
|
}
|
|
}
|