mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
Merge remote-tracking branch 'origin/main' into next
This commit is contained in:
@@ -122,7 +122,8 @@ class TrafficAnalyticsAggregator
|
||||
|
||||
/**
|
||||
* Merge path rows keyed by (app, path), so the same path under two apps stays two
|
||||
* rows, each with the domain of the row's Sentinel `app` key.
|
||||
* rows, each with the domain of the row's Sentinel `app` key. Sentinel's `__other__`
|
||||
* overflow rows (the long tail past its top-N cap) fold into one row without a domain.
|
||||
*
|
||||
* @param iterable<int, mixed> $paths
|
||||
* @param callable(string): ?string $domainForKey
|
||||
@@ -132,7 +133,7 @@ class TrafficAnalyticsAggregator
|
||||
foreach ($paths as $path) {
|
||||
$data = $path->toArray();
|
||||
$pathStr = (string) ($data['path'] ?? '');
|
||||
$appId = (string) ($data['app'] ?? '');
|
||||
$appId = $pathStr === '__other__' ? '' : (string) ($data['app'] ?? '');
|
||||
$key = $appId."\n".$pathStr;
|
||||
|
||||
$this->paths[$key] ??= [
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
Paginated "top paths" list. Each row shows the request path, a proportional
|
||||
request-volume bar, and right-aligned compact metrics (requests / bytes / p95).
|
||||
The path links to its live URL when the owning domain is known (new tab,
|
||||
rel="noopener noreferrer nofollow"). Expects `$paths` in scope; optional
|
||||
rel="noopener noreferrer nofollow"); the domain shows next to the path so the
|
||||
same path of two apps stays apart. Sentinel's `__other__` overflow row reads
|
||||
as "Other paths". Expects `$paths` in scope; optional
|
||||
`$keyPrefix` to namespace wire:keys.
|
||||
|
||||
@param iterable $paths path rows: ['path', 'domain'?, 'requests', 'bytesOut', 'p95']
|
||||
@@ -32,21 +34,32 @@
|
||||
@php
|
||||
$domain = $path['domain'] ?? null;
|
||||
$pathStr = (string) ($path['path'] ?? '');
|
||||
$href = $domain ? 'https://'.$domain.$pathStr : null;
|
||||
$isOther = $pathStr === '__other__';
|
||||
$href = $domain && ! $isOther ? 'https://'.$domain.$pathStr : null;
|
||||
$requests = (int) ($path['requests'] ?? 0);
|
||||
$s4xx = (int) ($path['s4xx'] ?? 0);
|
||||
$s5xx = (int) ($path['s5xx'] ?? 0);
|
||||
$errorRate = $requests > 0 ? round((($s4xx + $s5xx) / $requests) * 100, 1) : 0;
|
||||
$width = min(100, round(($requests / $maxRequests) * 100, 1));
|
||||
@endphp
|
||||
<div wire:key="{{ $keyPrefix }}-{{ md5($pathStr) }}"
|
||||
<div wire:key="{{ $keyPrefix }}-{{ md5($domain."\n".$pathStr) }}"
|
||||
x-show="{{ $loop->index }} >= page * per && {{ $loop->index }} < (page + 1) * per"
|
||||
class="flex min-h-11 items-center gap-3 border-b border-neutral-200 px-4 py-2 last:border-b-0 dark:border-white/[0.07]">
|
||||
@if ($href)
|
||||
<a href="{{ $href }}" target="_blank" rel="noopener noreferrer nofollow"
|
||||
class="min-w-0 flex-1 truncate font-mono text-[12px] text-black hover:underline dark:text-fg">{{ $pathStr }}</a>
|
||||
@if ($isOther)
|
||||
<span class="min-w-0 flex-1 truncate text-[12px] text-neutral-500 dark:text-fg-dim"
|
||||
title="All paths outside the top paths that Sentinel tracks">Other paths</span>
|
||||
@else
|
||||
<span class="min-w-0 flex-1 truncate font-mono text-[12px] text-black dark:text-fg">{{ $pathStr }}</span>
|
||||
<span class="flex min-w-0 flex-1 items-baseline gap-1.5">
|
||||
@if ($href)
|
||||
<a href="{{ $href }}" target="_blank" rel="noopener noreferrer nofollow"
|
||||
class="truncate font-mono text-[12px] text-black hover:underline dark:text-fg">{{ $pathStr }}</a>
|
||||
@else
|
||||
<span class="truncate font-mono text-[12px] text-black dark:text-fg">{{ $pathStr }}</span>
|
||||
@endif
|
||||
@if ($domain)
|
||||
<span class="max-w-48 shrink-0 truncate text-[11px] text-neutral-400 dark:text-fg-faint" title="{{ $domain }}">{{ $domain }}</span>
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
<div class="hidden h-1 w-16 shrink-0 overflow-hidden rounded-full bg-neutral-100 sm:block dark:bg-white/[0.06]">
|
||||
<div class="h-full rounded-full bg-[var(--chart-status-3xx)]" style="width: {{ $width }}%;"></div>
|
||||
|
||||
@@ -237,6 +237,18 @@ it('merges overviews, paths, breakdowns, and series of several keys', function (
|
||||
->and($aggregator->series()[0]['s2xx'])->toBe(5);
|
||||
});
|
||||
|
||||
it('folds the overflow path rows of all keys into one unlinked row', function () {
|
||||
$aggregator = new TrafficAnalyticsAggregator([]);
|
||||
$domains = ['k-api' => 'api.test', 'k-web' => 'web.test'];
|
||||
$domainForKey = fn (string $key) => $domains[$key] ?? null;
|
||||
|
||||
$aggregator->addPaths(collect([TrafficPathData::fromSentinel(['path' => '__other__', 'app' => 'k-api', 'requests' => 7, 's4xx' => 1, 'p95' => 3])]), $domainForKey);
|
||||
$aggregator->addPaths(collect([TrafficPathData::fromSentinel(['path' => '__other__', 'app' => 'k-web', 'requests' => 5, 's4xx' => 2, 'p95' => 8])]), $domainForKey);
|
||||
|
||||
expect($aggregator->topPaths())->toHaveCount(1)
|
||||
->and($aggregator->topPaths()[0])->toMatchArray(['path' => '__other__', 'domain' => null, 'requests' => 12, 's4xx' => 3, 'p95' => 8.0]);
|
||||
});
|
||||
|
||||
it('shows a compose application with the data of all its compose service keys', function () {
|
||||
$application = makeTrafficComposeApplication();
|
||||
$apiKey = $application->uuid.'-'.traefikSafeServiceNameSegment('api');
|
||||
|
||||
Reference in New Issue
Block a user