Merge branch 'next' into php-codestyle

This commit is contained in:
Andras Bacsai 2024-06-11 11:36:07 +02:00 committed by GitHub
commit 7ad2e1ca05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 198 additions and 453 deletions

View File

@ -2042,22 +2042,17 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if ($this->pull_request_id === 0) { if ($this->pull_request_id === 0) {
foreach ($this->application->build_environment_variables as $env) { foreach ($this->application->build_environment_variables as $env) {
$value = escapeshellarg($env->real_value); $value = escapeshellarg($env->real_value);
if (str($value)->contains("\n") && data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $value);
}
$this->build_args->push("--build-arg {$env->key}={$value}"); $this->build_args->push("--build-arg {$env->key}={$value}");
} }
} else { } else {
foreach ($this->application->build_environment_variables_preview as $env) { foreach ($this->application->build_environment_variables_preview as $env) {
$value = escapeshellarg($env->real_value); $value = escapeshellarg($env->real_value);
if (str($value)->contains("\n") && data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $value);
}
$this->build_args->push("--build-arg {$env->key}={$value}"); $this->build_args->push("--build-arg {$env->key}={$value}");
} }
} }
$this->build_args = $this->build_args->implode(' '); $this->build_args = $this->build_args->implode(' ');
ray($this->build_args);
} }
private function add_build_env_variables_to_dockerfile() private function add_build_env_variables_to_dockerfile()
@ -2068,19 +2063,18 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n")); $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
if ($this->pull_request_id === 0) { if ($this->pull_request_id === 0) {
foreach ($this->application->build_environment_variables as $env) { foreach ($this->application->build_environment_variables as $env) {
if (str($env->real_value)->contains("\n") && data_get($env, 'is_multiline') === true) { if (data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $env->real_value); $dockerfile->splice(1, 0, "ARG {$env->key}");
} else { } else {
$value = $env->real_value; $dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
} }
$dockerfile->splice(1, 0, "ARG {$env->key}={$value}");
} }
} else { } else {
foreach ($this->application->build_environment_variables_preview as $env) { foreach ($this->application->build_environment_variables_preview as $env) {
if (str($env->real_value)->contains("\n") && data_get($env, 'is_multiline') === true) { if (data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $env->real_value); $dockerfile->splice(1, 0, "ARG {$env->key}");
} else { } else {
$value = $env->real_value; $dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
} }
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}"); $dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
} }

View File

@ -91,6 +91,7 @@ class General extends Component
'application.settings.is_build_server_enabled' => 'boolean|required', 'application.settings.is_build_server_enabled' => 'boolean|required',
'application.settings.is_container_label_escape_enabled' => 'boolean|required', 'application.settings.is_container_label_escape_enabled' => 'boolean|required',
'application.watch_paths' => 'nullable', 'application.watch_paths' => 'nullable',
'application.redirect' => 'string|required',
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
@ -128,6 +129,7 @@ class General extends Component
'application.settings.is_build_server_enabled' => 'Is build server enabled', 'application.settings.is_build_server_enabled' => 'Is build server enabled',
'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled', 'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled',
'application.watch_paths' => 'Watch paths', 'application.watch_paths' => 'Watch paths',
'application.redirect' => 'Redirect',
]; ];
public function mount() public function mount()
@ -151,7 +153,7 @@ class General extends Component
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled; $this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
$this->customLabels = $this->application->parseContainerLabels(); $this->customLabels = $this->application->parseContainerLabels();
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') { if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
$this->customLabels = str(implode('|', generateLabelsApplication($this->application)))->replace('|', "\n"); $this->customLabels = str(implode("|coolify|", generateLabelsApplication($this->application)))->replace("|coolify|", "\n");
$this->application->custom_labels = base64_encode($this->customLabels); $this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save(); $this->application->save();
} }
@ -298,7 +300,7 @@ class General extends Component
public function resetDefaultLabels() public function resetDefaultLabels()
{ {
$this->customLabels = str(implode('|', generateLabelsApplication($this->application)))->replace('|', "\n"); $this->customLabels = str(implode("|coolify|", generateLabelsApplication($this->application)))->replace("|coolify|", "\n");
$this->ports_exposes = $this->application->ports_exposes; $this->ports_exposes = $this->application->ports_exposes;
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled; $this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
$this->application->custom_labels = base64_encode($this->customLabels); $this->application->custom_labels = base64_encode($this->customLabels);
@ -306,6 +308,7 @@ class General extends Component
if ($this->application->build_pack === 'dockercompose') { if ($this->application->build_pack === 'dockercompose') {
$this->loadComposeFile(); $this->loadComposeFile();
} }
$this->dispatch('configurationChanged');
} }
public function checkFqdns($showToaster = true) public function checkFqdns($showToaster = true)
@ -323,10 +326,26 @@ class General extends Component
$this->application->fqdn = $domains->implode(','); $this->application->fqdn = $domains->implode(',');
} }
} }
public function set_redirect()
{
try {
$has_www = collect($this->application->fqdns)->filter(fn ($fqdn) => str($fqdn)->contains('www.'))->count();
if ($has_www === 0 && $this->application->redirect === 'www') {
$this->dispatch('error', 'You want to redirect to www, but you do not have a www domain set.<br><br>Please add www to your domain list and as an A DNS record (if applicable).');
return;
}
$this->application->save();
$this->resetDefaultLabels();
$this->dispatch('success', 'Redirect updated.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function submit($showToaster = true) public function submit($showToaster = true)
{ {
try { try {
$this->set_redirect();
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim(); $this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim(); $this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) { $this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
@ -339,7 +358,7 @@ class General extends Component
$this->application->save(); $this->application->save();
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') { if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
$this->customLabels = str(implode('|', generateLabelsApplication($this->application)))->replace('|', "\n"); $this->customLabels = str(implode("|coolify|", generateLabelsApplication($this->application)))->replace("|coolify|", "\n");
$this->application->custom_labels = base64_encode($this->customLabels); $this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save(); $this->application->save();
} }

View File

@ -51,7 +51,7 @@ class ResourceOperations extends Component
]); ]);
$new_resource->save(); $new_resource->save();
if ($new_resource->destination->server->proxyType() !== 'NONE') { if ($new_resource->destination->server->proxyType() !== 'NONE') {
$customLabels = str(implode('|', generateLabelsApplication($new_resource)))->replace('|', "\n"); $customLabels = str(implode("|coolify|", generateLabelsApplication($new_resource)))->replace("|coolify|", "\n");
$new_resource->custom_labels = base64_encode($customLabels); $new_resource->custom_labels = base64_encode($customLabels);
$new_resource->save(); $new_resource->save();
} }

View File

@ -632,7 +632,7 @@ class Application extends BaseModel
public function isConfigurationChanged(bool $save = false) public function isConfigurationChanged(bool $save = false)
{ {
$newConfigHash = $this->fqdn.$this->git_repository.$this->git_branch.$this->git_commit_sha.$this->build_pack.$this->static_image.$this->install_command.$this->build_command.$this->start_command.$this->ports_exposes.$this->ports_mappings.$this->base_directory.$this->publish_directory.$this->dockerfile.$this->dockerfile_location.$this->custom_labels.$this->custom_docker_run_options.$this->dockerfile_target_build; $newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->ports_exposes . $this->ports_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels . $this->custom_docker_run_options . $this->dockerfile_target_build . $this->redirect;
if ($this->pull_request_id === 0 || $this->pull_request_id === null) { if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort()); $newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
} else { } else {
@ -1032,7 +1032,7 @@ class Application extends BaseModel
$customLabels = base64_decode($this->custom_labels); $customLabels = base64_decode($this->custom_labels);
if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { if (mb_detect_encoding($customLabels, 'ASCII', true) === false) {
ray('custom_labels contains non-ascii characters'); ray('custom_labels contains non-ascii characters');
$customLabels = str(implode('|', generateLabelsApplication($this, $preview)))->replace('|', "\n"); $customLabels = str(implode("|coolify|", generateLabelsApplication($this, $preview)))->replace("|coolify|", "\n");
} }
$this->custom_labels = base64_encode($customLabels); $this->custom_labels = base64_encode($customLabels);
$this->save(); $this->save();

View File

@ -246,7 +246,7 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
return $payload; return $payload;
} }
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null) function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both')
{ {
$labels = collect([]); $labels = collect([]);
if ($serviceLabels) { if ($serviceLabels) {
@ -259,7 +259,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
$url = Url::fromString($domain); $url = Url::fromString($domain);
$host = $url->getHost(); $host = $url->getHost();
$path = $url->getPath(); $path = $url->getPath();
$host_without_www = str($host)->replace('www.', '');
$schema = $url->getScheme(); $schema = $url->getScheme();
$port = $url->getPort(); $port = $url->getPort();
if (is_null($port) && ! is_null($onlyPort)) { if (is_null($port) && ! is_null($onlyPort)) {
@ -278,6 +278,12 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
if ($is_gzip_enabled) { if ($is_gzip_enabled) {
$labels->push("caddy_{$loop}.encode=zstd gzip"); $labels->push("caddy_{$loop}.encode=zstd gzip");
} }
if ($redirect_direction === 'www' && !str($host)->startsWith('www.')) {
$labels->push("caddy_{$loop}.redir={$schema}://www.{$host}{uri}");
}
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels->push("caddy_{$loop}.redir={$schema}://{$host_without_www}{uri}");
}
if (isDev()) { if (isDev()) {
// $labels->push("caddy_{$loop}.tls=internal"); // $labels->push("caddy_{$loop}.tls=internal");
} }
@ -285,7 +291,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
return $labels->sort(); return $labels->sort();
} }
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null) function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null, string $redirect_direction = 'both')
{ {
$labels = collect([]); $labels = collect([]);
$labels->push('traefik.enable=true'); $labels->push('traefik.enable=true');
@ -296,6 +302,8 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$basic_auth_middleware = null; $basic_auth_middleware = null;
$redirect = false; $redirect = false;
$redirect_middleware = null; $redirect_middleware = null;
if ($serviceLabels) { if ($serviceLabels) {
$basic_auth = $serviceLabels->contains(function ($value) { $basic_auth = $serviceLabels->contains(function ($value) {
return str_contains($value, 'basicauth'); return str_contains($value, 'basicauth');
@ -329,6 +337,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if ($generate_unique_uuid) { if ($generate_unique_uuid) {
$uuid = new Cuid2(7); $uuid = new Cuid2(7);
} }
$url = Url::fromString($domain); $url = Url::fromString($domain);
$host = $url->getHost(); $host = $url->getHost();
$path = $url->getPath(); $path = $url->getPath();
@ -347,6 +356,19 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels->push("traefik.http.middlewares.redir-ghost.redirectregex.regex=^{$path}/(.*)"); $labels->push("traefik.http.middlewares.redir-ghost.redirectregex.regex=^{$path}/(.*)");
$labels->push('traefik.http.middlewares.redir-ghost.redirectregex.replacement=/$1'); $labels->push('traefik.http.middlewares.redir-ghost.redirectregex.replacement=/$1');
} }
$to_www_name = "{$loop}-{$uuid}-to-www";
$to_non_www_name = "{$loop}-{$uuid}-to-non-www";
$redirect_to_non_www = [
"traefik.http.middlewares.{$to_non_www_name}.redirectregex.regex=^(http|https)://www\.(.+)",
"traefik.http.middlewares.{$to_non_www_name}.redirectregex.replacement=\${1}://\${2}",
"traefik.http.middlewares.{$to_non_www_name}.redirectregex.permanent=false"
];
$redirect_to_www = [
"traefik.http.middlewares.{$to_www_name}.redirectregex.regex=^(http|https)://(?:www\.)?(.+)",
"traefik.http.middlewares.{$to_www_name}.redirectregex.replacement=\${1}://www.\${2}",
"traefik.http.middlewares.{$to_www_name}.redirectregex.permanent=false"
];
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}`)");
@ -373,6 +395,14 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if (str($image)->contains('ghost')) { if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost'); $middlewares->push('redir-ghost');
} }
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www);
$middlewares->push($to_non_www_name);
}
if ($redirect_direction === 'www' && !str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_www);
$middlewares->push($to_www_name);
}
if ($middlewares->isNotEmpty()) { if ($middlewares->isNotEmpty()) {
$middlewares = $middlewares->join(','); $middlewares = $middlewares->join(',');
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}"); $labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
@ -391,6 +421,14 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if (str($image)->contains('ghost')) { if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost'); $middlewares->push('redir-ghost');
} }
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www);
$middlewares->push($to_non_www_name);
}
if ($redirect_direction === 'www' && !str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_www);
$middlewares->push($to_www_name);
}
if ($middlewares->isNotEmpty()) { if ($middlewares->isNotEmpty()) {
$middlewares = $middlewares->join(','); $middlewares = $middlewares->join(',');
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}"); $labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
@ -435,6 +473,14 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if (str($image)->contains('ghost')) { if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost'); $middlewares->push('redir-ghost');
} }
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www);
$middlewares->push($to_non_www_name);
}
if ($redirect_direction === 'www' && !str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_www);
$middlewares->push($to_www_name);
}
if ($middlewares->isNotEmpty()) { if ($middlewares->isNotEmpty()) {
$middlewares = $middlewares->join(','); $middlewares = $middlewares->join(',');
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}"); $labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
@ -453,6 +499,14 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if (str($image)->contains('ghost')) { if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost'); $middlewares->push('redir-ghost');
} }
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www);
$middlewares->push($to_non_www_name);
}
if ($redirect_direction === 'www' && !str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_www);
$middlewares->push($to_www_name);
}
if ($middlewares->isNotEmpty()) { if ($middlewares->isNotEmpty()) {
$middlewares = $middlewares->join(','); $middlewares = $middlewares->join(',');
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}"); $labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
@ -488,7 +542,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
onlyPort: $onlyPort, onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(), is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled() is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
)); ));
// Add Caddy labels // Add Caddy labels
$labels = $labels->merge(fqdnLabelsForCaddy( $labels = $labels->merge(fqdnLabelsForCaddy(
@ -498,7 +553,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
onlyPort: $onlyPort, onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(), is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(), is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled() is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
)); ));
} }
} else { } else {

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.296', 'release' => '4.0.0-beta.297',
// 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.296'; return '4.0.0-beta.297';

View File

@ -0,0 +1,28 @@
<?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('applications', function (Blueprint $table) {
$table->string('redirect')->enum('www', 'non-www', 'both')->default('both')->after('domain');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('applications', function (Blueprint $table) {
$table->dropColumn('redirect');
});
}
};

View File

@ -1,3 +1,13 @@
<?php
function getOldOrLocal($key, $localValue)
{
return old($key) != '' ? old($key) : (app()->environment('local') ? $localValue : '');
}
$name = getOldOrLocal('name', 'test3 normal user');
$email = getOldOrLocal('email', 'test3@example.com');
?>
<x-layout-simple> <x-layout-simple>
<section class="bg-gray-50 dark:bg-base"> <section class="bg-gray-50 dark:bg-base">
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0"> <div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
@ -11,27 +21,16 @@
</h1> </h1>
<form action="/register" method="POST" class="flex flex-col gap-2"> <form action="/register" method="POST" class="flex flex-col gap-2">
@csrf @csrf
@env('local') <x-forms.input id="name" required type="text" name="name" value="{{ $name }}"
<x-forms.input required value="test3 normal user" type="text" name="name"
label="{{ __('input.name') }}" /> label="{{ __('input.name') }}" />
<x-forms.input required value="test3@example.com" type="email" name="email" <x-forms.input id="email" required type="email" name="email" value="{{ $email }}"
label="{{ __('input.email') }}" /> label="{{ __('input.email') }}" />
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input required value="password" type="password" name="password" <x-forms.input id="password" required type="password" name="password"
label="{{ __('input.password') }}" /> label="{{ __('input.password') }}" />
<x-forms.input required value="password" type="password" name="password_confirmation" <x-forms.input id="password_confirmation" required type="password"
label="{{ __('input.password.again') }}" /> name="password_confirmation" label="{{ __('input.password.again') }}" />
</div> </div>
@else
<x-forms.input required type="text" name="name" label="{{ __('input.name') }}" />
<x-forms.input required type="email" name="email" label="{{ __('input.email') }}" />
<div class="flex gap-2">
<x-forms.input required type="password" name="password"
label="{{ __('input.password') }}" />
<x-forms.input required type="password" name="password_confirmation"
label="{{ __('input.password.again') }}" />
</div>
@endenv
<x-forms.button class="mb-4" type="submit">Register</x-forms.button> <x-forms.button class="mb-4" type="submit">Register</x-forms.button>
<a href="/login" class="button bg-coollabs-gradient"> <a href="/login" class="button bg-coollabs-gradient">
{{ __('auth.already_registered') }} {{ __('auth.already_registered') }}

View File

@ -9,8 +9,8 @@
@endif @endif
</label> </label>
@endif @endif
<select {{ $attributes->merge(['class' => $defaultClass]) }} @required($required) wire:dirty.class.remove='dark:text-white' <select {{ $attributes->merge(['class' => $defaultClass]) }} @required($required) wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="text-black bg-warning" wire:loading.attr="disabled" name={{ $id }} wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled" name={{ $id }}
@if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model={{ $id }} @endif> @if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model={{ $id }} @endif>
{{ $slot }} {{ $slot }}
</select> </select>

View File

@ -8,7 +8,7 @@
x-transition:leave-end="translate-y-full" x-init="setTimeout(() => { bannerVisible = true }, bannerVisibleAfter);" x-transition:leave-end="translate-y-full" x-init="setTimeout(() => { bannerVisible = true }, bannerVisibleAfter);"
class="fixed bottom-0 right-0 h-auto duration-300 ease-out px-5 pb-5 max-w-[46rem] z-[999]" x-cloak> class="fixed bottom-0 right-0 h-auto duration-300 ease-out px-5 pb-5 max-w-[46rem] z-[999]" x-cloak>
<div <div
class="flex flex-col items-center justify-between w-full h-full max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 dark:bg-coolgray-100 hover:dark:bg-coolgray-100 lg:p-8 lg:flex-row sm:rounded"> class="flex flex-row items-center justify-between w-full h-full max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 dark:bg-coolgray-100 hover:dark:bg-coolgray-100 lg:p-8 sm:rounded">
<div <div
class="flex flex-col items-start h-full pb-0 text-xs lg:items-center lg:flex-row lg:pr-6 lg:space-x-5 dark:text-neutral-300 "> class="flex flex-col items-start h-full pb-0 text-xs lg:items-center lg:flex-row lg:pr-6 lg:space-x-5 dark:text-neutral-300 ">
@if (isset($icon)) @if (isset($icon))
@ -23,7 +23,7 @@
<div>{{ $description }}</div> <div>{{ $description }}</div>
</div> </div>
</div> </div>
<button @click="bannerVisible=false"> <button @click="bannerVisible=false" class="pl-6 lg:pl-0">
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" <svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="w-full h-full"> stroke-width="1.5" stroke="currentColor" class="w-full h-full">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />

View File

@ -66,6 +66,20 @@
<x-forms.button wire:click="getWildcardDomain">Generate Domain <x-forms.button wire:click="getWildcardDomain">Generate Domain
</x-forms.button> </x-forms.button>
</div> </div>
<div class="flex items-end gap-2">
<x-forms.select label="Direction" id="application.redirect" required
helper="You must need to add www and non-www as an A DNS record.">
<option value="both">Allow www & non-www.</option>
<option value="www">Redirect to www.</option>
<option value="non-www">Redirect to non-www.</option>
</x-forms.select>
<x-modal-confirmation action="set_redirect" >
<x-slot:customButton>
<div class="w-[7.2rem]">Set Direction</div>
</x-slot:customButton>
This will reset the container labels. Are you sure?
</x-modal-confirmation>
</div>
@endif @endif
@if ($application->build_pack !== 'dockercompose') @if ($application->build_pack !== 'dockercompose')

View File

@ -50,7 +50,7 @@
<div x-data="searchComponent()"> <div x-data="searchComponent()">
<x-forms.input autofocus placeholder="Search for name, fqdn..." x-model="search" id="null" /> <x-forms.input autofocus placeholder="Search for name, fqdn..." x-model="search" id="null" />
<div class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3"> <div class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
<template x-for="item in filteredApplications" :key="item.uuid"> <template x-for="item in allFilteredItems" :key="item.uuid">
<span> <span>
<a class="h-24 box group" :href="item.hrefLink"> <a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
@ -83,285 +83,6 @@
</div> </div>
</span> </span>
</template> </template>
<template x-for="item in filteredPostgresqls" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate box-description" x-text="item.description"></div>
</div>
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
<template x-for="item in filteredRedis" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate description" x-text="item.description"></div>
</div>
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
<template x-for="item in filteredMongodbs" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate description" x-text="item.description"></div>
</div>
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
<template x-for="item in filteredMysqls" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate description" x-text="item.description"></div>
</div>
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
<template x-for="item in filteredMariadbs" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate description" x-text="item.description"></div>
</div>
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
<template x-for="item in filteredKeydbs" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate description" x-text="item.description"></div>
</div>
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
<template x-for="item in filteredDragonflies" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate description" x-text="item.description"></div>
</div>
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
<template x-for="item in filteredClickhouses" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate description" x-text="item.description"></div>
</div>
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
<template x-for="item in filteredServices" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate description" x-text="item.description"></div>
</div>
</a>
<div class="flex gap-1 pt-1 group-hover:dark:text-white group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
</div> </div>
</div> </div>
@endif @endif
@ -393,118 +114,32 @@
const hrefLink = item.hrefLink; const hrefLink = item.hrefLink;
window.location.href = `${hrefLink}#tags`; window.location.href = `${hrefLink}#tags`;
}, },
get filteredApplications() { filterAndSort(items) {
if (this.search === '') { if (this.search === '') {
return Object.values(this.applications).sort(sortFn); return Object.values(items).sort(sortFn);
} }
this.applications = Object.values(this.applications); const searchLower = this.search.toLowerCase();
return this.applications.filter(item => { return Object.values(items).filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) || return (item.name?.toLowerCase().includes(searchLower) ||
item.fqdn?.toLowerCase().includes(this.search.toLowerCase()) || item.fqdn?.toLowerCase().includes(searchLower) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) || item.description?.toLowerCase().includes(searchLower) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase())); item.tags?.some(tag => tag.name.toLowerCase().includes(searchLower)));
}).sort(sortFn); }).sort(sortFn);
}, },
get filteredPostgresqls() { get allFilteredItems() {
if (this.search === '') { return [
return Object.values(this.postgresqls).sort(sortFn); this.applications,
} this.postgresqls,
this.postgresqls = Object.values(this.postgresqls); this.redis,
return this.postgresqls.filter(item => { this.mongodbs,
return item.name?.toLowerCase().includes(this.search.toLowerCase()) || this.mysqls,
item.description?.toLowerCase().includes(this.search.toLowerCase()) || this.mariadbs,
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase())); this.keydbs,
}).sort(sortFn); this.dragonflies,
}, this.clickhouses,
get filteredRedis() { this.services
if (this.search === '') { ].flatMap((items) => this.filterAndSort(items))
return Object.values(this.redis).sort(sortFn); }
}
this.redis = Object.values(this.redis);
return this.redis.filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase()));
}).sort(sortFn);
},
get filteredMongodbs() {
if (this.search === '') {
return Object.values(this.mongodbs).sort(sortFn);
}
this.mongodbs = Object.values(this.mongodbs);
return this.mongodbs.filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase()));
}).sort(sortFn);
},
get filteredMysqls() {
if (this.search === '') {
return Object.values(this.mysqls).sort(sortFn);
}
this.mysqls = Object.values(this.mysqls);
return this.mysqls.filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase()));
}).sort(sortFn);
},
get filteredMariadbs() {
if (this.search === '') {
return Object.values(this.mariadbs).sort(sortFn);
}
this.mariadbs = Object.values(this.mariadbs);
return this.mariadbs.filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase()));
}).sort(sortFn);
},
get filteredKeydbs() {
if (this.search === '') {
return Object.values(this.keydbs).sort(sortFn);
}
this.keydbs = Object.values(this.keydbs);
return this.keydbs.filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase()));
}).sort(sortFn);
},
get filteredDragonflies() {
if (this.search === '') {
return Object.values(this.dragonflies).sort(sortFn);
}
this.dragonflies = Object.values(this.dragonflies);
return this.dragonflies.filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase()));
}).sort(sortFn);
},
get filteredClickhouses() {
if (this.search === '') {
return Object.values(this.clickhouses).sort(sortFn);
}
this.clickhouses = Object.values(this.clickhouses);
return this.clickhouses.filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase()));
}).sort(sortFn);
},
get filteredServices() {
if (this.search === '') {
return Object.values(this.services).sort(sortFn);
}
this.services = Object.values(this.services);
return this.services.filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase()));
}).sort(sortFn);
},
}; };
} }
</script> </script>

View File

@ -6,14 +6,14 @@ 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.3.2" VERSION="1.3.3"
DOCKER_VERSION="26.0" DOCKER_VERSION="26.0"
CDN="https://cdn.coollabs.io/coolify" CDN="https://cdn.coollabs.io/coolify"
OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"') OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
# Check if the OS is manjaro, if so, change it to arch # Check if the OS is manjaro, if so, change it to arch
if [ "$OS_TYPE" = "manjaro" ]; then if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then
OS_TYPE="arch" OS_TYPE="arch"
fi fi
@ -35,7 +35,7 @@ fi
# Install xargs on Amazon Linux 2023 - lol # Install xargs on Amazon Linux 2023 - lol
if [ "$OS_TYPE" = 'amzn' ]; then if [ "$OS_TYPE" = 'amzn' ]; then
dnf install -y findutils >/dev/null 2>&1 dnf install -y findutils >/dev/null
fi fi
LATEST_VERSION=$(curl --silent $CDN/versions.json | grep -i version | xargs | awk '{print $2}' | tr -d ',') LATEST_VERSION=$(curl --silent $CDN/versions.json | grep -i version | xargs | awk '{print $2}' | tr -d ',')
@ -75,28 +75,28 @@ echo "Installing required packages..."
case "$OS_TYPE" in case "$OS_TYPE" in
arch) arch)
pacman -Sy >/dev/null 2>&1 || true pacman -Sy >/dev/null || true
if ! pacman -Q curl wget git jq >/dev/null 2>&1; then if ! pacman -Q curl wget git jq >/dev/null; then
pacman -S --noconfirm curl wget git jq >/dev/null 2>&1 || true pacman -S --noconfirm curl wget git jq >/dev/null || true
fi fi
;; ;;
ubuntu | debian | raspbian) ubuntu | debian | raspbian)
apt update -y >/dev/null 2>&1 apt update -y >/dev/null
apt install -y curl wget git jq >/dev/null 2>&1 apt install -y curl wget git jq >/dev/null
;; ;;
centos | fedora | rhel | ol | rocky | almalinux | amzn) centos | fedora | rhel | ol | rocky | almalinux | amzn)
if [ "$OS_TYPE" = "amzn" ]; then if [ "$OS_TYPE" = "amzn" ]; then
dnf install -y wget git jq >/dev/null 2>&1 dnf install -y wget git jq >/dev/null
else else
if ! command -v dnf >/dev/null 2>&1; then if ! command -v dnf >/dev/null; then
yum install -y dnf >/dev/null 2>&1 yum install -y dnf >/dev/null
fi fi
dnf install -y curl wget git jq >/dev/null 2>&1 dnf install -y curl wget git jq >/dev/null
fi fi
;; ;;
sles | opensuse-leap | opensuse-tumbleweed) sles | opensuse-leap | opensuse-tumbleweed)
zypper refresh >/dev/null 2>&1 zypper refresh >/dev/null
zypper install -y curl wget git jq >/dev/null 2>&1 zypper install -y curl wget git jq >/dev/null
;; ;;
*) *)
echo "This script only supports Debian, Redhat, Arch Linux, or SLES based operating systems for now." echo "This script only supports Debian, Redhat, Arch Linux, or SLES based operating systems for now."

View File

@ -39,7 +39,7 @@ services:
test: test:
[ [
"CMD", "CMD",
"mysqladmin", "mariadb-admin",
"ping", "ping",
"-h", "-h",
"127.0.0.1", "127.0.0.1",

View File

@ -1,7 +1,7 @@
# documentation: https://docs.logto.io/docs/tutorials/get-started/#logto-oss-self-hosted # documentation: https://docs.logto.io/docs/tutorials/get-started/#logto-oss-self-hosted
# slogan: A comprehensive identity solution covering both the front and backend, complete with pre-built infrastructure and enterprise-grade solutions. # slogan: A comprehensive identity solution covering both the front and backend, complete with pre-built infrastructure and enterprise-grade solutions.
# tags: logto,identity,login,authentication,oauth,oidc,openid # tags: logto,identity,login,authentication,oauth,oidc,openid
# icon: svgs/logto_dark.svg # logo: svgs/logto_dark.svg
services: services:
logto: logto:
@ -32,7 +32,7 @@ services:
volumes: volumes:
- logto-postgres-data:/var/lib/postgresql/data - logto-postgres-data:/var/lib/postgresql/data
healthcheck: healthcheck:
test: ["CMD", "pg_isready", "-U", "$SERVICE_USER_POSTGRES"] test: ["CMD", "pg_isready", "-U", "$SERVICE_USER_POSTGRES", "-d", "$POSTGRES_DB"]
interval: 5s interval: 5s
timeout: 20s timeout: 20s
retries: 10 retries: 10

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.296" "version": "4.0.0-beta.297"
}, },
"sentinel": { "sentinel": {
"version": "0.0.4" "version": "0.0.4"