Merge pull request #1650 from coollabsio/next

v4.0.0-beta.191
This commit is contained in:
Andras Bacsai 2024-01-15 11:06:15 +01:00 committed by GitHub
commit af11d8cf3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 243 additions and 61 deletions

View File

@ -50,12 +50,8 @@ class StartPostgresql
], ],
'healthcheck' => [ 'healthcheck' => [
'test' => [ 'test' => [
'CMD-SHELL', "CMD-SHELL",
'pg_isready', "psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1"
'-d',
$this->database->postgres_db,
'-U',
$this->database->postgres_user,
], ],
'interval' => '5s', 'interval' => '5s',
'timeout' => '5s', 'timeout' => '5s',

View File

@ -209,7 +209,7 @@ class General extends Component
public function updatedApplicationFqdn() public function updatedApplicationFqdn()
{ {
$this->resetDefaultLabels(false); $this->resetDefaultLabels(false);
$this->dispatch('success', 'Labels reset to default!'); // $this->dispatch('success', 'Labels reset to default!');
} }
public function submit($showToaster = true) public function submit($showToaster = true)
{ {
@ -235,9 +235,16 @@ class General extends Component
]); ]);
} }
if (data_get($this->application, 'fqdn')) { if (data_get($this->application, 'fqdn')) {
$domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) { $this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
return Str::of($domain)->trim()->lower(); $domains = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
return str($domain)->trim()->lower();
}); });
$domains = $domains->unique();
foreach ($domains as $domain) {
if (!validate_dns_entry($domain, $this->application->destination->server)) {
$showToaster && $this->dispatch('error', "Validating DNS settings for: $domain failed.<br>Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline' href='https://coolify.io/docs/dns-settings'>documentation</a> for further help.");
}
}
$this->application->fqdn = $domains->implode(','); $this->application->fqdn = $domains->implode(',');
} }

View File

@ -39,7 +39,7 @@ class Heading extends Component
} else { } else {
dispatch(new ServerStatusJob($this->application->destination->server)); dispatch(new ServerStatusJob($this->application->destination->server));
} }
if ($showNotification) $this->dispatch('success', "Application ({$this->application->name}) status updated."); if ($showNotification) $this->dispatch('success', "Application status updated.");
} }
public function force_deploy_without_cache() public function force_deploy_without_cache()

View File

@ -76,7 +76,7 @@ class Form extends Component
$this->server->settings->is_usable = true; $this->server->settings->is_usable = true;
$this->server->settings->save(); $this->server->settings->save();
} else { } else {
$this->dispatch('error', 'Server is not reachable. Please check your connection and configuration.'); $this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/configuration#openssh-server">documentation</a> for further help.');
return; return;
} }
} }
@ -85,7 +85,7 @@ class Form extends Component
try { try {
$uptime = $this->server->validateConnection(); $uptime = $this->server->validateConnection();
if (!$uptime) { if (!$uptime) {
$install && $this->dispatch('error', 'Server is not reachable. Please check your connection and configuration.'); $install && $this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/configuration#openssh-server">documentation</a> for further help.');
return; return;
} }
$supported_os_type = $this->server->validateOS(); $supported_os_type = $this->server->validateOS();

View File

@ -39,7 +39,7 @@ class ShowPrivateKey extends Component
if ($uptime) { if ($uptime) {
$this->dispatch('success', 'Server is reachable.'); $this->dispatch('success', 'Server is reachable.');
} else { } else {
$this->dispatch('error', 'Server is not reachable. Please check your connection and private key configuration.'); $this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/configuration#openssh-server">documentation</a> for further help.');
return; return;
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {

View File

@ -15,6 +15,7 @@ class Configuration extends Component
public bool $do_not_track; public bool $do_not_track;
public bool $is_auto_update_enabled; public bool $is_auto_update_enabled;
public bool $is_registration_enabled; public bool $is_registration_enabled;
public bool $is_dns_validation_enabled;
public bool $next_channel; public bool $next_channel;
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic'; protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
protected Server $server; protected Server $server;
@ -24,12 +25,14 @@ class Configuration extends Component
'settings.resale_license' => 'nullable', 'settings.resale_license' => 'nullable',
'settings.public_port_min' => 'required', 'settings.public_port_min' => 'required',
'settings.public_port_max' => 'required', 'settings.public_port_max' => 'required',
'settings.custom_dns_servers' => 'nullable',
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
'settings.fqdn' => 'FQDN', 'settings.fqdn' => 'FQDN',
'settings.resale_license' => 'Resale License', 'settings.resale_license' => 'Resale License',
'settings.public_port_min' => 'Public port min', 'settings.public_port_min' => 'Public port min',
'settings.public_port_max' => 'Public port max', 'settings.public_port_max' => 'Public port max',
'settings.custom_dns_servers' => 'Custom DNS servers',
]; ];
public function mount() public function mount()
@ -38,6 +41,7 @@ class Configuration extends Component
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled; $this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
$this->is_registration_enabled = $this->settings->is_registration_enabled; $this->is_registration_enabled = $this->settings->is_registration_enabled;
$this->next_channel = $this->settings->next_channel; $this->next_channel = $this->settings->next_channel;
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
} }
public function instantSave() public function instantSave()
@ -45,6 +49,7 @@ class Configuration extends Component
$this->settings->do_not_track = $this->do_not_track; $this->settings->do_not_track = $this->do_not_track;
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled; $this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
$this->settings->is_registration_enabled = $this->is_registration_enabled; $this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
if ($this->next_channel) { if ($this->next_channel) {
$this->settings->next_channel = false; $this->settings->next_channel = false;
$this->next_channel = false; $this->next_channel = false;
@ -63,6 +68,14 @@ class Configuration extends Component
return; return;
} }
$this->validate(); $this->validate();
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim();
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
return str($dns)->trim()->lower();
});
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique();
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(',');
$this->settings->save(); $this->settings->save();
$this->server = Server::findOrFail(0); $this->server = Server::findOrFail(0);
$this->setup_instance_fqdn(); $this->setup_instance_fqdn();

View File

@ -229,11 +229,12 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$http_label = "http-{$loop}-{$uuid}"; $http_label = "http-{$loop}-{$uuid}";
$https_label = "https-{$loop}-{$uuid}"; $https_label = "https-{$loop}-{$uuid}";
$labels->push("traefik.http.middlewares.gzip.compress=true");
$labels->push("traefik.http.routers.{$https_label}.middlewares=gzip");
if ($schema === 'https') { if ($schema === 'https') {
// Set labels for https // Set labels for https
$labels->push("traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)"); $labels->push("traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
$labels->push("traefik.http.routers.{$https_label}.entryPoints=https"); $labels->push("traefik.http.routers.{$https_label}.entryPoints=https");
$labels->push("traefik.http.routers.{$https_label}.middlewares=gzip");
if ($port) { if ($port) {
$labels->push("traefik.http.routers.{$https_label}.service={$https_label}"); $labels->push("traefik.http.routers.{$https_label}.service={$https_label}");
$labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port"); $labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port");
@ -254,13 +255,13 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}"); $labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
} }
if ($is_force_https_enabled) { if ($is_force_https_enabled) {
$labels->push("traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https");
$labels->push("traefik.http.routers.{$http_label}.middlewares=redirect-to-https"); $labels->push("traefik.http.routers.{$http_label}.middlewares=redirect-to-https");
} }
} else { } else {
// Set labels for http // Set labels for http
$labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)"); $labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
$labels->push("traefik.http.routers.{$http_label}.entryPoints=http"); $labels->push("traefik.http.routers.{$http_label}.entryPoints=http");
$labels->push("traefik.http.routers.{$http_label}.middlewares=gzip");
if ($port) { if ($port) {
$labels->push("traefik.http.services.{$http_label}.loadbalancer.server.port=$port"); $labels->push("traefik.http.services.{$http_label}.loadbalancer.server.port=$port");
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}"); $labels->push("traefik.http.routers.{$http_label}.service={$http_label}");

View File

@ -103,9 +103,6 @@ function generate_default_proxy_configuration(Server $server)
"traefik.http.routers.traefik.entrypoints=http", "traefik.http.routers.traefik.entrypoints=http",
"traefik.http.routers.traefik.service=api@internal", "traefik.http.routers.traefik.service=api@internal",
"traefik.http.services.traefik.loadbalancer.server.port=8080", "traefik.http.services.traefik.loadbalancer.server.port=8080",
// Global Middlewares
"traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https",
"traefik.http.middlewares.gzip.compress=true",
]; ];
$config = [ $config = [
"version" => "3.8", "version" => "3.8",
@ -198,10 +195,23 @@ function setup_dynamic_configuration()
$traefik_dynamic_conf = [ $traefik_dynamic_conf = [
'http' => 'http' =>
[ [
'middlewares' => [
'redirect-to-https' => [
'redirectscheme' => [
'scheme' => 'https',
],
],
'gzip' => [
'compress' => true,
],
],
'routers' => 'routers' =>
[ [
'coolify-http' => 'coolify-http' =>
[ [
'middlewares' => [
0 => 'gzip',
],
'entryPoints' => [ 'entryPoints' => [
0 => 'http', 0 => 'http',
], ],
@ -251,7 +261,7 @@ function setup_dynamic_configuration()
if ($schema === 'https') { if ($schema === 'https') {
$traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [ $traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [
0 => 'redirect-to-https@docker', 0 => 'redirect-to-https',
]; ];
$traefik_dynamic_conf['http']['routers']['coolify-https'] = [ $traefik_dynamic_conf['http']['routers']['coolify-https'] = [
@ -288,7 +298,7 @@ function setup_dynamic_configuration()
], $server); ], $server);
if (config('app.env') == 'local') { if (config('app.env') == 'local') {
ray($yaml); // ray($yaml);
} }
} }
} }

View File

@ -22,14 +22,11 @@ use App\Notifications\Channels\EmailChannel;
use App\Notifications\Channels\TelegramChannel; use App\Notifications\Channels\TelegramChannel;
use App\Notifications\Internal\GeneralNotification; use App\Notifications\Internal\GeneralNotification;
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException; use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
use Illuminate\Database\QueryException;
use Illuminate\Mail\Message; use Illuminate\Mail\Message;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
@ -40,6 +37,7 @@ use Visus\Cuid2\Cuid2;
use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\RSA;
use Spatie\Url\Url; use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use PurplePixie\PhpDns\DNSQuery;
function base_configuration_dir(): string function base_configuration_dir(): string
{ {
@ -1592,3 +1590,50 @@ function getRealtime()
return $envDefined; return $envDefined;
} }
} }
function validate_dns_entry(string $fqdn, Server $server)
{
$url = Url::fromString($fqdn);
$host = $url->getHost();
if (str($host)->contains('sslip.io')) {
return true;
}
$settings = InstanceSettings::get();
$is_dns_validation_enabled = data_get($settings, 'is_dns_validation_enabled');
if (!$is_dns_validation_enabled) {
return true;
}
$dnsServers = data_get($settings, 'custom_dns_servers');
$dnsServers = str($dnsServers)->explode(',');
if ($server->id === 0) {
$ip = data_get($settings, 'public_ipv4') || data_get($settings, 'public_ipv6') || $server->ip;
} else {
$ip = $server->ip;
}
$foundMatch = false;
$type = \PurplePixie\PhpDns\DNSTypes::NAME_A;
foreach ($dnsServers as $dnsServer) {
try {
ray("Checking $host on $dnsServer");
$query = new DNSQuery($dnsServer);
$results = $query->query($host, $type);
if ($results === false || $query->hasError()) {
ray("Error: " . $query->getLasterror());
} else {
foreach ($results as $result) {
if ($result->getType() == $type) {
if ($result->getData() === $ip) {
ray($host . " has IP address " . $result->getData());
ray($result->getString());
$foundMatch = true;
break;
}
}
}
}
} catch (\Exception $e) {
}
}
ray("Found match: $foundMatch");
return $foundMatch;
}

View File

@ -28,6 +28,7 @@
"nubs/random-name-generator": "^2.2", "nubs/random-name-generator": "^2.2",
"phpseclib/phpseclib": "~3.0", "phpseclib/phpseclib": "~3.0",
"poliander/cron": "^3.0", "poliander/cron": "^3.0",
"purplepixie/phpdns": "^2.1",
"pusher/pusher-php-server": "^7.2", "pusher/pusher-php-server": "^7.2",
"resend/resend-laravel": "^0.5.0", "resend/resend-laravel": "^0.5.0",
"sentry/sentry-laravel": "^3.4", "sentry/sentry-laravel": "^3.4",

50
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "44337ff4ff1d9c435d9776fec01ebe9c", "content-hash": "de3b59fade9b132d2582a40dcf3c00f9",
"packages": [ "packages": [
{ {
"name": "amphp/amp", "name": "amphp/amp",
@ -6287,6 +6287,54 @@
}, },
"time": "2023-10-14T21:56:36+00:00" "time": "2023-10-14T21:56:36+00:00"
}, },
{
"name": "purplepixie/phpdns",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/purplepixie/phpdns.git",
"reference": "e1e4f18a60d01947e2aac7157325a9e2e7755bf7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/purplepixie/phpdns/zipball/e1e4f18a60d01947e2aac7157325a9e2e7755bf7",
"reference": "e1e4f18a60d01947e2aac7157325a9e2e7755bf7",
"shasum": ""
},
"require": {
"php": ">=7.4"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"psr-0": {
"PurplePixie": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-3.0-only"
],
"authors": [
{
"name": "David Cutting",
"email": "dcutting@purplepixie.org"
}
],
"description": "PHP DNS Direct Query Module",
"support": {
"issues": "https://github.com/purplepixie/phpdns/issues",
"source": "https://github.com/purplepixie/phpdns/tree/2.1.0"
},
"time": "2023-11-06T15:37:19+00:00"
},
{ {
"name": "pusher/pusher-php-server", "name": "pusher/pusher-php-server",
"version": "7.2.4", "version": "7.2.4",

View File

@ -7,7 +7,7 @@ return [
// The release version of your application // The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.190', 'release' => '4.0.0-beta.191',
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'), 'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.190'; return '4.0.0-beta.191';

View File

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('instance_settings', function (Blueprint $table) {
$table->boolean('is_dns_validation_enabled')->default(true);
$table->string('custom_dns_servers')->nullable()->default('1.1.1.1');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('instance_settings', function (Blueprint $table) {
$table->dropColumn('is_dns_validation_enabled');
$table->dropColumn('custom_dns_servers');
});
}
};

View File

@ -1,7 +1,7 @@
version: '3.8' version: '3.8'
services: services:
coolify: coolify:
image: "ghcr.io/coollabsio/coolify:${LATEST_IMAGE:-4.0.0-beta.153}" image: "ghcr.io/coollabsio/coolify:${LATEST_IMAGE:-4.0.0-beta.190}"
volumes: volumes:
- type: bind - type: bind
source: /data/coolify/source/.env source: /data/coolify/source/.env
@ -10,6 +10,7 @@ services:
- /data/coolify/ssh:/var/www/html/storage/app/ssh - /data/coolify/ssh:/var/www/html/storage/app/ssh
- /data/coolify/applications:/var/www/html/storage/app/applications - /data/coolify/applications:/var/www/html/storage/app/applications
- /data/coolify/databases:/var/www/html/storage/app/databases - /data/coolify/databases:/var/www/html/storage/app/databases
- /data/coolify/services:/var/www/html/storage/app/services
- /data/coolify/backups:/var/www/html/storage/app/backups - /data/coolify/backups:/var/www/html/storage/app/backups
environment: environment:
- APP_ID - APP_ID

View File

@ -56,6 +56,8 @@
Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user Please make sure you have the correct public key in your ~/.ssh/authorized_keys file for user
'root' or skip the boarding process and add a new private key manually to Coolify and to the 'root' or skip the boarding process and add a new private key manually to Coolify and to the
server. server.
<br />
Check this <a target="_blank" class="underline" href="https://coolify.io/docs/configuration#openssh-server">documentation</a> for further help.
<x-forms.input readonly id="serverPublicKey"></x-forms.input> <x-forms.input readonly id="serverPublicKey"></x-forms.input>
<x-forms.button class="box" wire:target="setServerType('localhost')" <x-forms.button class="box" wire:target="setServerType('localhost')"
wire:click="setServerType('localhost')">Check again wire:click="setServerType('localhost')">Check again

View File

@ -29,7 +29,7 @@
@if ($database->started_at) @if ($database->started_at)
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input label="Initial Username" id="database.postgres_username" placeholder="If empty: postgres" <x-forms.input label="Initial Username" id="database.postgres_user" placeholder="If empty: postgres"
readonly helper="You can only change this in the database." /> readonly helper="You can only change this in the database." />
<x-forms.input label="Initial Password" id="database.postgres_password" type="password" required <x-forms.input label="Initial Password" id="database.postgres_password" type="password" required
readonly helper="You can only change this in the database." /> readonly helper="You can only change this in the database." />

View File

@ -202,10 +202,7 @@
<li class="step step-secondary">Select a Server</li> <li class="step step-secondary">Select a Server</li>
<li class="step">Select a Destination</li> <li class="step">Select a Destination</li>
</ul> </ul>
@if ($isDatabase)
<div class="text-center">Swarm clusters are excluded from this type of resource at the moment. It will
be activated soon. Stay tuned.</div>
@endif
{{-- @if ($isDatabase) {{-- @if ($isDatabase)
<div class="flex items-center justify-center pt-4"> <div class="flex items-center justify-center pt-4">
<x-forms.checkbox instantSave wire:model="includeSwarm" <x-forms.checkbox instantSave wire:model="includeSwarm"
@ -235,6 +232,10 @@
</div> </div>
@endforelse @endforelse
</div> </div>
@if ($isDatabase)
<div class="text-center">Swarm clusters are excluded from this type of resource at the moment. It will
be activated soon. Stay tuned.</div>
@endif
@endif @endif
@if ($current_step === 'destinations') @if ($current_step === 'destinations')
<ul class="pb-10 steps"> <ul class="pb-10 steps">
@ -267,7 +268,7 @@
</div> </div>
@endforeach @endforeach
@endif @endif
<a href="{{ route('destination.new', ['server_id' => $server_id]) }}" <a href="{{ route('destination.new', ['server_id' => $server_id]) }}"
class="items-center justify-center pb-10 text-center box-without-bg group bg-coollabs hover:bg-coollabs-100"> class="items-center justify-center pb-10 text-center box-without-bg group bg-coollabs hover:bg-coollabs-100">
<div class="flex flex-col mx-6 "> <div class="flex flex-col mx-6 ">
<div class="font-bold text-white"> <div class="font-bold text-white">

View File

@ -68,8 +68,7 @@
<div class="text-xs">{{ $application->status }}</div> <div class="text-xs">{{ $application->status }}</div>
</div> </div>
<div class="flex items-center px-4"> <div class="flex items-center px-4">
<a <a class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
href="{{ route('project.service.index', [...$parameters, 'service_name' => $application->name]) }}"> href="{{ route('project.service.index', [...$parameters, 'service_name' => $application->name]) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning" <svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
@ -115,8 +114,7 @@
<div class="text-xs">{{ $database->status }}</div> <div class="text-xs">{{ $database->status }}</div>
</div> </div>
<div class="flex items-center px-4"> <div class="flex items-center px-4">
<a <a class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
href="{{ route('project.service.index', [...$parameters, 'service_name' => $database->name]) }}"> href="{{ route('project.service.index', [...$parameters, 'service_name' => $database->name]) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning" <svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
@ -157,9 +155,7 @@
<livewire:project.shared.execute-container-command :resource="$service" /> <livewire:project.shared.execute-container-command :resource="$service" />
</div> </div>
<div x-cloak x-show="activeTab === 'environment-variables'"> <div x-cloak x-show="activeTab === 'environment-variables'">
<div x-cloak x-show="activeTab === 'environment-variables'"> <livewire:project.shared.environment-variable.all :resource="$service" />
<livewire:project.shared.environment-variable.all :resource="$service" />
</div>
</div> </div>
<div x-cloak x-show="activeTab === 'danger'"> <div x-cloak x-show="activeTab === 'danger'">
<livewire:project.shared.danger :resource="$service" /> <livewire:project.shared.danger :resource="$service" />

View File

@ -13,19 +13,10 @@
@click.prevent="activeTab = 'storages'; window.location.hash = 'storages'; if(window.location.search) window.location.search = ''" @click.prevent="activeTab = 'storages'; window.location.hash = 'storages'; if(window.location.search) window.location.search = ''"
href="#">Storages href="#">Storages
</a> </a>
<a :class="activeTab === 'environment-variables' && 'text-white'"
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"
href="#">Environment
Variables</a>
<a :class="activeTab === 'scheduled-tasks' && 'text-white'" <a :class="activeTab === 'scheduled-tasks' && 'text-white'"
@click.prevent="activeTab = 'scheduled-tasks'; window.location.hash = 'scheduled-tasks'" @click.prevent="activeTab = 'scheduled-tasks'; window.location.hash = 'scheduled-tasks'"
href="#">Scheduled Tasks href="#">Scheduled Tasks
</a> </a>
<a :class="activeTab === 'danger' && 'text-white'"
@click.prevent="activeTab = 'danger';
window.location.hash = 'danger'"
href="#">Danger Zone
</a>
@if ( @if (
$serviceDatabase?->databaseType() === 'standalone-mysql' || $serviceDatabase?->databaseType() === 'standalone-mysql' ||
$serviceDatabase?->databaseType() === 'standalone-postgresql' || $serviceDatabase?->databaseType() === 'standalone-postgresql' ||
@ -69,14 +60,10 @@
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" :s3s="$s3s" /> <livewire:project.database.create-scheduled-backup :database="$serviceDatabase" :s3s="$s3s" />
<livewire:project.database.scheduled-backups :database="$serviceDatabase" /> <livewire:project.database.scheduled-backups :database="$serviceDatabase" />
</div> </div>
</div> @endisset
<div x-cloak x-show="activeTab === 'scheduled-tasks'"> <div x-cloak x-show="activeTab === 'scheduled-tasks'">
<livewire:project.shared.scheduled-task.all :resource="$service" /> <livewire:project.shared.scheduled-task.all :resource="$service" />
</div> </div>
<div x-cloak x-show="activeTab === 'danger'"> </div>
<livewire:project.shared.danger :resource="$service" />
</div>
@endisset
</div> </div>
</div> </div>
</div>

View File

@ -48,5 +48,10 @@
@endforeach @endforeach
</div> </div>
@endif @endif
@if (
$resource->persistentStorages()->get()->count() == 0 &&
$resource->fileStorages()->get()->count() == 0)
<div class="pt-4">No storages found.</div>
@endif
@endif @endif
</div> </div>

View File

@ -9,8 +9,10 @@
<div>General configuration for your Coolify instance.</div> <div>General configuration for your Coolify instance.</div>
<div class="flex flex-col gap-2 pt-4"> <div class="flex flex-col gap-2 pt-4">
<div class="flex gap-2 w-96"> <div class="flex items-end gap-2">
<x-forms.input id="settings.fqdn" label="Instance's Domain" placeholder="https://coolify.io" /> <x-forms.input id="settings.fqdn" label="Instance's Domain" placeholder="https://coolify.io" />
<x-forms.input id="settings.custom_dns_servers" label="DNS Servers" helper="DNS servers for validation FQDNS againts. A comma separated list of DNS servers." placeholder="1.1.1.1,8.8.8.8" />
<x-forms.checkbox instantSave id="is_dns_validation_enabled" label="Validate DNS settings?" />
</div> </div>
{{-- <div class="flex gap-2 "> {{-- <div class="flex gap-2 ">

View File

@ -6,7 +6,7 @@ set -e # Exit immediately if a command exits with a non-zero status
#set -u # Treat unset variables as an error and exit #set -u # Treat unset variables as an error and exit
set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status
VERSION="1.1.0" VERSION="1.2.0"
DOCKER_VERSION="24.0" DOCKER_VERSION="24.0"
CDN="https://cdn.coollabs.io/coolify" CDN="https://cdn.coollabs.io/coolify"
@ -65,6 +65,44 @@ sles | opensuse-leap | opensuse-tumbleweed)
;; ;;
esac esac
# Detect OpenSSH server
SSH_DETECTED=false
if [ -x "$(command -v systemctl)" ]; then
if systemctl status sshd >/dev/null 2>&1; then
echo "OpenSSH server is installed and running."
SSH_DETECTED=true
fi
elif [ -x "$(command -v service)" ]; then
if service sshd status >/dev/null 2>&1; then
echo "OpenSSH server is installed and running."
SSH_DETECTED=true
fi
fi
if [ "$SSH_DETECTED" = "false" ]; then
echo "###############################################################################"
echo "WARNING: Could not detect if OpenSSH server is installed and running - this does not mean that it is not installed, just that we could not detect it."
echo -e "Please make sure it is set, otherwise Coolify cannot connect to the host system. \n"
echo "###############################################################################"
fi
# Detect SSH PermitRootLogin
SSH_PERMIT_ROOT_LOGIN=false
SSH_PERMIT_ROOT_LOGIN_CONFIG=$(grep "^PermitRootLogin" /etc/ssh/sshd_config | awk '{print $2}') || SSH_PERMIT_ROOT_LOGIN_CONFIG="N/A (commented out or not found at all)"
if [ "$SSH_PERMIT_ROOT_LOGIN_CONFIG" = "prohibit-password" ] || [ "$SSH_PERMIT_ROOT_LOGIN_CONFIG" = "yes" ] || [ "$SSH_PERMIT_ROOT_LOGIN_CONFIG" = "without-password" ]; then
echo "PermitRootLogin is enabled."
SSH_PERMIT_ROOT_LOGIN=true
fi
if [ "$SSH_PERMIT_ROOT_LOGIN" != "true" ]; then
echo "###############################################################################"
echo "WARNING: PermitRootLogin is not enabled in /etc/ssh/sshd_config."
echo -e "It is set to $SSH_PERMIT_ROOT_LOGIN_CONFIG. Should be prohibit-password, yes or without-password.\n"
echo -e "Please make sure it is set, otherwise Coolify cannot connect to the host system. \n"
echo "(Currently we only support root user to login via SSH, this will be changed in the future.)"
echo "###############################################################################"
fi
if ! [ -x "$(command -v docker)" ]; then if ! [ -x "$(command -v docker)" ]; then
echo "Docker is not installed. Installing Docker." echo "Docker is not installed. Installing Docker."
curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh
@ -127,9 +165,8 @@ fi
echo -e "-------------" echo -e "-------------"
mkdir -p /data/coolify/ssh/keys mkdir -p /data/coolify/{source,ssh,applications,databases,backups,services,proxy}
mkdir -p /data/coolify/ssh/mux mkdir -p /data/coolify/ssh/{keys,mux}
mkdir -p /data/coolify/source
mkdir -p /data/coolify/proxy/dynamic mkdir -p /data/coolify/proxy/dynamic
chown -R 9999:root /data/coolify chown -R 9999:root /data/coolify

View File

@ -4,7 +4,7 @@
"version": "3.12.36" "version": "3.12.36"
}, },
"v4": { "v4": {
"version": "4.0.0-beta.190" "version": "4.0.0-beta.191"
} }
} }
} }