mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
Traffic analytics now covers every resource on both proxies:
- Caddy: Compose applications and services get access-log labels. The
log_append key is {uuid}-{service}, the same key Sentinel reads from
Traefik router names.
- A resource owns the Sentinel key {uuid} and every key that starts
with {uuid}-. Application pages and the traffic card now merge all
keys, so Compose applications and previews show data on Traefik too.
- Global analytics: the leaderboard groups the keys of one resource
into one row, lists services, and never names keys of other teams.
- New service analytics page (project.service.analytics) with the same
view and authorization as the application page.
- Switching the proxy type restarts Sentinel when analytics is on, so
it reads the new log path. Sentinel gets no traffic mount when the
proxy has no analytics support.
- Caddy servers show a note that resources log only after a redeploy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
96 lines
3.7 KiB
PHP
96 lines
3.7 KiB
PHP
<?php
|
|
|
|
use App\Actions\Proxy\StartProxy;
|
|
use App\Actions\Server\StartSentinel;
|
|
use App\Enums\ProxyTypes;
|
|
use App\Models\Server;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
Queue::fake();
|
|
Server::flushIdentityMap();
|
|
$this->team = User::factory()->create()->teams()->first();
|
|
});
|
|
|
|
afterEach(fn () => Server::flushIdentityMap());
|
|
|
|
function proxySwitchServer(object $test, string $proxyType, bool $sentinelEnabled, bool $analyticsEnabled): Server
|
|
{
|
|
$server = Server::factory()->create(['team_id' => $test->team->id]);
|
|
$server->proxy->set('type', $proxyType);
|
|
$server->save();
|
|
$server->settings->is_sentinel_enabled = $sentinelEnabled;
|
|
$server->settings->is_traffic_analytics_enabled = $analyticsEnabled;
|
|
$server->settings->save();
|
|
Server::flushIdentityMap();
|
|
|
|
return $server->fresh();
|
|
}
|
|
|
|
it('restarts Sentinel after a proxy switch when Sentinel and traffic analytics are on', function (string $from, string $to) {
|
|
$server = proxySwitchServer($this, $from, sentinelEnabled: true, analyticsEnabled: true);
|
|
|
|
$server->changeProxy($to, async: true);
|
|
|
|
StartSentinel::assertPushed(1);
|
|
})->with([
|
|
'Traefik to Caddy' => [ProxyTypes::TRAEFIK->value, ProxyTypes::CADDY->value],
|
|
'Caddy to Traefik' => [ProxyTypes::CADDY->value, ProxyTypes::TRAEFIK->value],
|
|
'Traefik to none' => [ProxyTypes::TRAEFIK->value, ProxyTypes::NONE->value],
|
|
'Caddy to Nginx' => [ProxyTypes::CADDY->value, ProxyTypes::NGINX->value],
|
|
]);
|
|
|
|
it('restarts Sentinel when the Livewire switch selects the proxy synchronously', function () {
|
|
StartProxy::shouldRun();
|
|
$server = proxySwitchServer($this, ProxyTypes::TRAEFIK->value, sentinelEnabled: true, analyticsEnabled: true);
|
|
|
|
$server->changeProxy(ProxyTypes::CADDY->value, async: false);
|
|
|
|
StartSentinel::assertPushed(1);
|
|
});
|
|
|
|
it('does not restart Sentinel after a proxy switch when nothing relevant is on', function (bool $sentinelEnabled, bool $analyticsEnabled) {
|
|
$server = proxySwitchServer($this, ProxyTypes::TRAEFIK->value, $sentinelEnabled, $analyticsEnabled);
|
|
|
|
$server->changeProxy(ProxyTypes::CADDY->value, async: true);
|
|
|
|
StartSentinel::assertNotPushed();
|
|
expect((bool) $server->fresh()->settings->is_sentinel_enabled)->toBe($sentinelEnabled);
|
|
})->with([
|
|
'analytics off' => [true, false],
|
|
'Sentinel off' => [false, true],
|
|
'both off' => [false, false],
|
|
]);
|
|
|
|
it('does not restart Sentinel when the proxy type does not change', function () {
|
|
$server = proxySwitchServer($this, ProxyTypes::CADDY->value, sentinelEnabled: true, analyticsEnabled: true);
|
|
|
|
$server->changeProxy(ProxyTypes::CADDY->value, async: true);
|
|
|
|
StartSentinel::assertNotPushed();
|
|
});
|
|
|
|
it('points Sentinel at the Caddy access log after a switch from Traefik', function () {
|
|
$server = proxySwitchServer($this, ProxyTypes::TRAEFIK->value, sentinelEnabled: true, analyticsEnabled: true);
|
|
expect(StartSentinel::trafficLogDirectory($server))->toBe('/data/coolify/proxy');
|
|
|
|
$server->changeProxy(ProxyTypes::CADDY->value, async: true);
|
|
$server = $server->fresh();
|
|
|
|
expect(StartSentinel::trafficLogDirectory($server))->toBe('/data/coolify/proxy/caddy')
|
|
->and(StartSentinel::sentinelTrafficEnvironment($server)['TRAFFIC_ACCESS_LOG_PATH'])
|
|
->toBe('/data/coolify/proxy/caddy/access.log');
|
|
});
|
|
|
|
it('gives Sentinel no traffic environment after a switch to a proxy without analytics support', function () {
|
|
$server = proxySwitchServer($this, ProxyTypes::TRAEFIK->value, sentinelEnabled: true, analyticsEnabled: true);
|
|
|
|
$server->changeProxy(ProxyTypes::NONE->value, async: true);
|
|
|
|
expect(StartSentinel::sentinelTrafficEnvironment($server->fresh()))->toBe([]);
|
|
});
|