Merge pull request #3845 from KaelWD/feat/disable-default-redirect

feat: allow disabling default redirect, set status to 503
This commit is contained in:
Andras Bacsai
2024-12-06 13:48:24 +01:00
committed by GitHub
8 changed files with 118 additions and 89 deletions

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Proxy; namespace App\Actions\Proxy;
use App\Enums\ProxyTypes;
use App\Events\ProxyStarted; use App\Events\ProxyStarted;
use App\Models\Server; use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction; use Lorisleiva\Actions\Concerns\AsAction;
@@ -37,11 +38,16 @@ class StartProxy
"echo 'Successfully started coolify-proxy.'", "echo 'Successfully started coolify-proxy.'",
]); ]);
} else { } else {
$caddfile = 'import /dynamic/*.caddy'; if (isDev()) {
if ($proxyType === ProxyTypes::CADDY->value) {
$proxy_path = '/data/coolify/proxy/caddy';
}
}
$caddyfile = 'import /dynamic/*.caddy';
$commands = $commands->merge([ $commands = $commands->merge([
"mkdir -p $proxy_path/dynamic", "mkdir -p $proxy_path/dynamic",
"cd $proxy_path", "cd $proxy_path",
"echo '$caddfile' > $proxy_path/dynamic/Caddyfile", "echo '$caddyfile' > $proxy_path/dynamic/Caddyfile",
"echo 'Creating required Docker Compose file.'", "echo 'Creating required Docker Compose file.'",
"echo 'Pulling docker image.'", "echo 'Pulling docker image.'",
'docker compose pull', 'docker compose pull',

View File

@@ -14,7 +14,7 @@ class ProxyStartedNotification
public function handle(ProxyStarted $event): void public function handle(ProxyStarted $event): void
{ {
$this->server = data_get($event, 'data'); $this->server = data_get($event, 'data');
$this->server->setupDefault404Redirect(); $this->server->setupDefaultRedirect();
$this->server->setupDynamicProxyConfiguration(); $this->server->setupDynamicProxyConfiguration();
$this->server->proxy->force_stop = false; $this->server->proxy->force_stop = false;
$this->server->save(); $this->server->save();

View File

@@ -15,6 +15,7 @@ class Proxy extends Component
public $proxy_settings = null; public $proxy_settings = null;
public bool $redirect_enabled = true;
public ?string $redirect_url = null; public ?string $redirect_url = null;
protected $listeners = ['proxyStatusUpdated', 'saveConfiguration' => 'submit']; protected $listeners = ['proxyStatusUpdated', 'saveConfiguration' => 'submit'];
@@ -26,6 +27,7 @@ class Proxy extends Component
public function mount() public function mount()
{ {
$this->selectedProxy = $this->server->proxyType(); $this->selectedProxy = $this->server->proxyType();
$this->redirect_enabled = data_get($this->server, 'proxy.redirect_enabled', true);
$this->redirect_url = data_get($this->server, 'proxy.redirect_url'); $this->redirect_url = data_get($this->server, 'proxy.redirect_url');
} }
@@ -63,13 +65,25 @@ class Proxy extends Component
} }
} }
public function instantSaveRedirect()
{
try {
$this->server->proxy->redirect_enabled = $this->redirect_enabled;
$this->server->save();
$this->server->setupDefaultRedirect();
$this->dispatch('success', 'Proxy configuration saved.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function submit() public function submit()
{ {
try { try {
SaveConfiguration::run($this->server, $this->proxy_settings); SaveConfiguration::run($this->server, $this->proxy_settings);
$this->server->proxy->redirect_url = $this->redirect_url; $this->server->proxy->redirect_url = $this->redirect_url;
$this->server->save(); $this->server->save();
$this->server->setupDefault404Redirect(); $this->server->setupDefaultRedirect();
$this->dispatch('success', 'Proxy configuration saved.'); $this->dispatch('success', 'Proxy configuration saved.');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);

View File

@@ -65,7 +65,7 @@ class Deploy extends Component
public function restart() public function restart()
{ {
try { try {
$this->stop(forceStop: false); $this->stop();
$this->dispatch('checkProxy'); $this->dispatch('checkProxy');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
@@ -105,6 +105,7 @@ class Deploy extends Component
$startTime = Carbon::now()->getTimestamp(); $startTime = Carbon::now()->getTimestamp();
while ($process->running()) { while ($process->running()) {
ray('running');
if (Carbon::now()->getTimestamp() - $startTime >= $timeout) { if (Carbon::now()->getTimestamp() - $startTime >= $timeout) {
$this->forceStopContainer($containerName); $this->forceStopContainer($containerName);
break; break;

View File

@@ -105,6 +105,14 @@ class Server extends BaseModel
]); ]);
} }
} }
if (! isset($server->proxy->redirect_enabled)) {
$server->proxy->redirect_enabled = true;
}
});
static::retrieved(function ($server) {
if (! isset($server->proxy->redirect_enabled)) {
$server->proxy->redirect_enabled = true;
}
}); });
static::forceDeleting(function ($server) { static::forceDeleting(function ($server) {
@@ -184,73 +192,77 @@ class Server extends BaseModel
return $this->proxyType() && $this->proxyType() !== 'NONE' && $this->isFunctional() && ! $this->isSwarmWorker() && ! $this->settings->is_build_server; return $this->proxyType() && $this->proxyType() !== 'NONE' && $this->isFunctional() && ! $this->isSwarmWorker() && ! $this->settings->is_build_server;
} }
public function setupDefault404Redirect() public function setupDefaultRedirect()
{ {
$banner =
"# This file is generated by Coolify, do not edit it manually.\n".
"# Disable the default redirect to customize (only if you know what are you doing).\n\n";
$dynamic_conf_path = $this->proxyPath().'/dynamic'; $dynamic_conf_path = $this->proxyPath().'/dynamic';
$proxy_type = $this->proxyType(); $proxy_type = $this->proxyType();
$redirect_enabled = $this->proxy->redirect_enabled ?? true;
$redirect_url = $this->proxy->redirect_url; $redirect_url = $this->proxy->redirect_url;
if ($proxy_type === ProxyTypes::TRAEFIK->value) { if (isDev()) {
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml";
} elseif ($proxy_type === ProxyTypes::CADDY->value) {
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.caddy";
}
if (empty($redirect_url)) {
if ($proxy_type === ProxyTypes::CADDY->value) { if ($proxy_type === ProxyTypes::CADDY->value) {
$conf = ':80, :443 { $dynamic_conf_path = '/data/coolify/proxy/caddy/dynamic';
respond 404
}';
$conf =
"# This file is automatically generated by Coolify.\n".
"# Do not edit it manually (only if you know what are you doing).\n\n".
$conf;
$base64 = base64_encode($conf);
instant_remote_process([
"mkdir -p $dynamic_conf_path",
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
], $this);
$this->reloadCaddy();
return;
} }
instant_remote_process([
"mkdir -p $dynamic_conf_path",
"rm -f $default_redirect_file",
], $this);
return;
} }
if ($proxy_type === ProxyTypes::TRAEFIK->value) { if ($proxy_type === ProxyTypes::TRAEFIK->value) {
$dynamic_conf = [ $default_redirect_file = "$dynamic_conf_path/default_redirect_503.yaml";
'http' => [ } elseif ($proxy_type === ProxyTypes::CADDY->value) {
'routers' => [ $default_redirect_file = "$dynamic_conf_path/default_redirect_503.caddy";
'catchall' => [ }
'entryPoints' => [
0 => 'http', instant_remote_process([
1 => 'https', "mkdir -p $dynamic_conf_path",
], "rm -f $dynamic_conf_path/default_redirect_404.yaml",
'service' => 'noop', "rm -f $dynamic_conf_path/default_redirect_404.caddy",
'rule' => 'HostRegexp(`.+`)', ], $this);
'tls' => [
'certResolver' => 'letsencrypt', if ($redirect_enabled === false) {
], instant_remote_process(["rm -f $default_redirect_file"], $this);
'priority' => 1, } else {
'middlewares' => [ if ($proxy_type === ProxyTypes::CADDY->value) {
0 => 'redirect-regexp', if (filled($redirect_url)) {
$conf = ":80, :443 {
redir $redirect_url
}";
} else {
$conf = ':80, :443 {
respond 503
}';
}
} elseif ($proxy_type === ProxyTypes::TRAEFIK->value) {
$dynamic_conf = [
'http' => [
'routers' => [
'catchall' => [
'entryPoints' => [
0 => 'http',
1 => 'https',
],
'service' => 'noop',
'rule' => 'PathPrefix(`/`)',
'priority' => -1000,
], ],
], ],
], 'services' => [
'services' => [ 'noop' => [
'noop' => [ 'loadBalancer' => [
'loadBalancer' => [ 'servers' => [],
'servers' => [
0 => [
'url' => '',
],
], ],
], ],
], ],
], ],
'middlewares' => [ ];
if (filled($redirect_url)) {
$dynamic_conf['http']['routers']['catchall']['middlewares'] = [
0 => 'redirect-regexp',
];
$dynamic_conf['http']['services']['noop']['loadBalancer']['servers'][0] = [
'url' => '',
];
$dynamic_conf['http']['middlewares'] = [
'redirect-regexp' => [ 'redirect-regexp' => [
'redirectRegex' => [ 'redirectRegex' => [
'regex' => '(.*)', 'regex' => '(.*)',
@@ -258,32 +270,17 @@ respond 404
'permanent' => false, 'permanent' => false,
], ],
], ],
], ];
], }
]; $conf = Yaml::dump($dynamic_conf, 12, 2);
$conf = Yaml::dump($dynamic_conf, 12, 2); }
$conf = $conf = $banner.$conf;
"# This file is automatically generated by Coolify.\n".
"# Do not edit it manually (only if you know what are you doing).\n\n".
$conf;
$base64 = base64_encode($conf);
} elseif ($proxy_type === ProxyTypes::CADDY->value) {
$conf = ":80, :443 {
redir $redirect_url
}";
$conf =
"# This file is automatically generated by Coolify.\n".
"# Do not edit it manually (only if you know what are you doing).\n\n".
$conf;
$base64 = base64_encode($conf); $base64 = base64_encode($conf);
instant_remote_process([
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
], $this);
} }
instant_remote_process([
"mkdir -p $dynamic_conf_path",
"echo '$base64' | base64 -d | tee $default_redirect_file > /dev/null",
], $this);
if ($proxy_type === 'CADDY') { if ($proxy_type === 'CADDY') {
$this->reloadCaddy(); $this->reloadCaddy();
} }
@@ -612,6 +609,7 @@ $schema://$host {
$memory = json_decode($memory, true); $memory = json_decode($memory, true);
$parsedCollection = collect($memory)->map(function ($metric) { $parsedCollection = collect($memory)->map(function ($metric) {
$usedPercent = $metric['usedPercent'] ?? 0.0; $usedPercent = $metric['usedPercent'] ?? 0.0;
return [(int) $metric['time'], (float) $usedPercent]; return [(int) $metric['time'], (float) $usedPercent];
}); });

View File

@@ -173,13 +173,12 @@ function generate_default_proxy_configuration(Server $server)
], ],
'volumes' => [ 'volumes' => [
'/var/run/docker.sock:/var/run/docker.sock:ro', '/var/run/docker.sock:/var/run/docker.sock:ro',
"{$proxy_path}:/traefik",
], ],
'command' => [ 'command' => [
'--ping=true', '--ping=true',
'--ping.entrypoint=http', '--ping.entrypoint=http',
'--api.dashboard=true', '--api.dashboard=true',
'--api.insecure=false',
'--entrypoints.http.address=:80', '--entrypoints.http.address=:80',
'--entrypoints.https.address=:443', '--entrypoints.https.address=:443',
'--entrypoints.http.http.encodequerysemicolons=true', '--entrypoints.http.http.encodequerysemicolons=true',
@@ -187,21 +186,26 @@ function generate_default_proxy_configuration(Server $server)
'--entrypoints.https.http.encodequerysemicolons=true', '--entrypoints.https.http.encodequerysemicolons=true',
'--entryPoints.https.http2.maxConcurrentStreams=50', '--entryPoints.https.http2.maxConcurrentStreams=50',
'--entrypoints.https.http3', '--entrypoints.https.http3',
'--providers.docker.exposedbydefault=false',
'--providers.file.directory=/traefik/dynamic/', '--providers.file.directory=/traefik/dynamic/',
'--providers.docker.exposedbydefault=false',
'--providers.file.watch=true', '--providers.file.watch=true',
'--certificatesresolvers.letsencrypt.acme.httpchallenge=true', '--certificatesresolvers.letsencrypt.acme.httpchallenge=true',
'--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json',
'--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http', '--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=http',
'--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json',
], ],
'labels' => $labels, 'labels' => $labels,
], ],
], ],
]; ];
if (isDev()) { if (isDev()) {
// $config['services']['traefik']['command'][] = "--log.level=debug"; $config['services']['traefik']['command'][] = '--api.insecure=true';
$config['services']['traefik']['command'][] = '--log.level=debug';
$config['services']['traefik']['command'][] = '--accesslog.filepath=/traefik/access.log'; $config['services']['traefik']['command'][] = '--accesslog.filepath=/traefik/access.log';
$config['services']['traefik']['command'][] = '--accesslog.bufferingsize=100'; $config['services']['traefik']['command'][] = '--accesslog.bufferingsize=100';
$config['services']['traefik']['volumes'][] = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/:/traefik';
} else {
$config['services']['traefik']['command'][] = '--api.insecure=false';
$config['services']['traefik']['volumes'][] = "{$proxy_path}:/traefik";
} }
if ($server->isSwarm()) { if ($server->isSwarm()) {
data_forget($config, 'services.traefik.container_name'); data_forget($config, 'services.traefik.container_name');

View File

@@ -27,6 +27,13 @@
helper="If set, all resources will only have docker container labels for {{ str($server->proxyType())->title() }}.<br>For applications, labels needs to be regenerated manually. <br>Resources needs to be restarted." helper="If set, all resources will only have docker container labels for {{ str($server->proxyType())->title() }}.<br>For applications, labels needs to be regenerated manually. <br>Resources needs to be restarted."
id="server.settings.generate_exact_labels" id="server.settings.generate_exact_labels"
label="Generate labels only for {{ str($server->proxyType())->title() }}" instantSave /> label="Generate labels only for {{ str($server->proxyType())->title() }}" instantSave />
<x-forms.checkbox instantSave="instantSaveRedirect" id="redirect_enabled"
label="Override default request handler"
helper="Requests to unknown hosts or stopped services will recieve a 503 response or be redirected to the URL you set below (need to enable this first)." />
@if ($redirect_enabled)
<x-forms.input placeholder="https://app.coolify.io" id="redirect_url"
label="Redirect to (optional)" />
@endif
</div> </div>
@if ($server->proxyType() === ProxyTypes::TRAEFIK->value) @if ($server->proxyType() === ProxyTypes::TRAEFIK->value)
<h4>Traefik</h4> <h4>Traefik</h4>
@@ -40,8 +47,6 @@
configurations. configurations.
</div> </div>
@endif @endif
<x-forms.input placeholder="https://app.coolify.io" id="redirect_url" label="Default Redirect 404"
helper="All urls that has no service available will be redirected to this domain." />
<div wire:loading wire:target="loadProxyConfiguration" class="pt-4"> <div wire:loading wire:target="loadProxyConfiguration" class="pt-4">
<x-loading text="Loading proxy configuration..." /> <x-loading text="Loading proxy configuration..." />
</div> </div>

View File

@@ -29,7 +29,8 @@
@if (str_replace('|', '.', $fileName) === 'coolify.yaml' || @if (str_replace('|', '.', $fileName) === 'coolify.yaml' ||
str_replace('|', '.', $fileName) === 'Caddyfile' || str_replace('|', '.', $fileName) === 'Caddyfile' ||
str_replace('|', '.', $fileName) === 'coolify.caddy' || str_replace('|', '.', $fileName) === 'coolify.caddy' ||
str_replace('|', '.', $fileName) === 'default_redirect_404.caddy') str_replace('|', '.', $fileName) === 'default_redirect_503.yaml' ||
str_replace('|', '.', $fileName) === 'default_redirect_503.caddy')
<div> <div>
<h3 class="dark:text-white">File: {{ str_replace('|', '.', $fileName) }}</h3> <h3 class="dark:text-white">File: {{ str_replace('|', '.', $fileName) }}</h3>
</div> </div>