Files
coolify/app/Actions/Server/ConfigureTrafficAnalytics.php
T
Andras Bacsai 165d2f3dc8 fix: harden traffic analytics, terminal errors and Postgres restores
Traffic analytics:
- Reject enabling unless the server runs a Coolify-managed Traefik or
  Caddy proxy, and show the reason in the settings UI
- Save the proxy configuration before the setting so a failure leaves
  both unchanged; allow disabling after the proxy was removed
- Skip the proxy restart when the proxy is stopped and report that the
  config applies on next start
- Preserve the user's own Traefik --accesslog* flags and restore them
  when analytics is disabled
- Rotate the Traefik access log in the sidecar with BusyBox tools
  (copytruncate, 5 gzip rotations)
- Create the access log before starting Sentinel, which opens it once

Terminal:
- Detect and surface WebSocket connection rejections in the browser
  terminal, with shared helpers in terminal-connection.js and
  terminal-utils.js

Database import:
- Restore PostgreSQL backups in a single transaction; SQL replace
  restores go into a temporary database and swap in only on success,
  leaving the current database untouched on failure
2026-09-25 22:58:07 +02:00

88 lines
3.2 KiB
PHP

<?php
namespace App\Actions\Server;
use App\Actions\Proxy\GetProxyConfiguration;
use App\Actions\Proxy\SaveProxyConfiguration;
use App\Jobs\RestartProxyJob;
use App\Models\Server;
use App\Services\ProxyPortParser;
use Lorisleiva\Actions\Concerns\AsAction;
use RuntimeException;
use Throwable;
class ConfigureTrafficAnalytics
{
use AsAction;
private const STOPPED_PROXY_STATUSES = ['exited', 'stopped', 'stopping', 'dead'];
/**
* Turn traffic analytics on or off. The proxy configuration is saved before the setting,
* so a failure leaves both unchanged.
*
* @return bool Whether a proxy restart was queued to apply the new configuration.
*/
public function handle(Server $server, bool $enable): bool
{
if ($enable && ($reason = $server->trafficAnalyticsUnsupportedReason()) !== null) {
throw new RuntimeException($reason);
}
$sentinelWasEnabled = (bool) $server->settings->is_sentinel_enabled;
// Disabling must still work after the proxy was removed, so there may be no proxy configuration to update.
$hasProxy = $server->hasTrafficAnalyticsProxy();
if ($hasProxy) {
$this->saveProxyConfiguration($server, $enable);
}
$server->settings->is_traffic_analytics_enabled = $enable;
$server->settings->save();
$server->refresh();
// A proxy the user stopped stays stopped; its next start applies the saved configuration.
$restartProxy = $hasProxy && ! $this->proxyIsStopped($server);
if ($restartProxy) {
RestartProxyJob::dispatch($server);
}
// Recreate Sentinel so it picks up (enabling) or drops (disabling) the traffic env + proxy-log mount.
// Enabling analytics needs Sentinel running; when disabling, only restart if Sentinel was already
// enabled so we never turn Sentinel on as a side effect of disabling analytics.
if ($enable || $sentinelWasEnabled) {
StartSentinel::run($server, restart: true);
}
return $restartProxy;
}
private function saveProxyConfiguration(Server $server, bool $enable): void
{
$previousEnabled = (bool) $server->settings->is_traffic_analytics_enabled;
$previousProxy = $server->proxy->all();
try {
$configuration = GetProxyConfiguration::run($server);
ProxyPortParser::fromConfiguration($configuration);
// The configuration is built from the in-memory setting; it is persisted only after the save succeeds.
$server->settings->is_traffic_analytics_enabled = $enable;
$configuration = applyTrafficAnalyticsToProxyConfiguration($server, $configuration);
SaveProxyConfiguration::run($server, $configuration);
} catch (Throwable $exception) {
$server->settings->is_traffic_analytics_enabled = $previousEnabled;
$server->proxy = $previousProxy;
$server->save();
throw $exception;
}
}
private function proxyIsStopped(Server $server): bool
{
return (bool) $server->proxy->get('force_stop')
|| in_array($server->proxy->get('status'), self::STOPPED_PROXY_STATUSES, true);
}
}