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 @@ private function generate_build_env_variables()
if ($this->pull_request_id === 0) {
foreach ($this->application->build_environment_variables as $env) {
$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}");
}
} else {
foreach ($this->application->build_environment_variables_preview as $env) {
$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 = $this->build_args->implode(' ');
ray($this->build_args);
}
private function add_build_env_variables_to_dockerfile()
@ -2068,19 +2063,18 @@ private function add_build_env_variables_to_dockerfile()
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
if ($this->pull_request_id === 0) {
foreach ($this->application->build_environment_variables as $env) {
if (str($env->real_value)->contains("\n") && data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $env->real_value);
if (data_get($env, 'is_multiline') === true) {
$dockerfile->splice(1, 0, "ARG {$env->key}");
} else {
$value = $env->real_value;
$dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
}
$dockerfile->splice(1, 0, "ARG {$env->key}={$value}");
}
} else {
foreach ($this->application->build_environment_variables_preview as $env) {
if (str($env->real_value)->contains("\n") && data_get($env, 'is_multiline') === true) {
$value = str_replace("\n", "\\\n", $env->real_value);
if (data_get($env, 'is_multiline') === true) {
$dockerfile->splice(1, 0, "ARG {$env->key}");
} 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}");
}

View File

@ -91,6 +91,7 @@ class General extends Component
'application.settings.is_build_server_enabled' => 'boolean|required',
'application.settings.is_container_label_escape_enabled' => 'boolean|required',
'application.watch_paths' => 'nullable',
'application.redirect' => 'string|required',
];
protected $validationAttributes = [
@ -128,6 +129,7 @@ class General extends Component
'application.settings.is_build_server_enabled' => 'Is build server enabled',
'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled',
'application.watch_paths' => 'Watch paths',
'application.redirect' => 'Redirect',
];
public function mount()
@ -151,7 +153,7 @@ public function mount()
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
$this->customLabels = $this->application->parseContainerLabels();
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->save();
}
@ -298,7 +300,7 @@ public function getWildcardDomain()
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->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
$this->application->custom_labels = base64_encode($this->customLabels);
@ -306,6 +308,7 @@ public function resetDefaultLabels()
if ($this->application->build_pack === 'dockercompose') {
$this->loadComposeFile();
}
$this->dispatch('configurationChanged');
}
public function checkFqdns($showToaster = true)
@ -323,10 +326,26 @@ public function checkFqdns($showToaster = true)
$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)
{
try {
$this->set_redirect();
$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)->trim()->explode(',')->map(function ($domain) {
@ -339,7 +358,7 @@ public function submit($showToaster = true)
$this->application->save();
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->save();
}

View File

@ -51,7 +51,7 @@ public function cloneTo($destination_id)
]);
$new_resource->save();
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->save();
}

View File

@ -632,7 +632,7 @@ public function isLogDrainEnabled()
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) {
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
} else {
@ -1032,7 +1032,7 @@ public function parseContainerLabels(?ApplicationPreview $preview = null)
$customLabels = base64_decode($this->custom_labels);
if (mb_detect_encoding($customLabels, 'ASCII', true) === false) {
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->save();

View File

@ -246,7 +246,7 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
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([]);
if ($serviceLabels) {
@ -259,7 +259,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
$url = Url::fromString($domain);
$host = $url->getHost();
$path = $url->getPath();
$host_without_www = str($host)->replace('www.', '');
$schema = $url->getScheme();
$port = $url->getPort();
if (is_null($port) && ! is_null($onlyPort)) {
@ -278,6 +278,12 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
if ($is_gzip_enabled) {
$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()) {
// $labels->push("caddy_{$loop}.tls=internal");
}
@ -285,7 +291,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
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->push('traefik.enable=true');
@ -296,6 +302,8 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$basic_auth_middleware = null;
$redirect = false;
$redirect_middleware = null;
if ($serviceLabels) {
$basic_auth = $serviceLabels->contains(function ($value) {
return str_contains($value, 'basicauth');
@ -329,6 +337,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if ($generate_unique_uuid) {
$uuid = new Cuid2(7);
}
$url = Url::fromString($domain);
$host = $url->getHost();
$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.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') {
// Set labels for https
$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')) {
$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()) {
$middlewares = $middlewares->join(',');
$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')) {
$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()) {
$middlewares = $middlewares->join(',');
$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')) {
$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()) {
$middlewares = $middlewares->join(',');
$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')) {
$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()) {
$middlewares = $middlewares->join(',');
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
@ -488,7 +542,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
));
// Add Caddy labels
$labels = $labels->merge(fqdnLabelsForCaddy(
@ -498,7 +553,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
));
}
} else {

View File

@ -7,7 +7,7 @@
// The release version of your application
// 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
'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?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>
<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">
@ -11,27 +21,16 @@
</h1>
<form action="/register" method="POST" class="flex flex-col gap-2">
@csrf
@env('local')
<x-forms.input required value="test3 normal user" type="text" name="name"
<x-forms.input id="name" required type="text" name="name" value="{{ $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') }}" />
<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') }}" />
<x-forms.input required value="password" type="password" name="password_confirmation"
label="{{ __('input.password.again') }}" />
<x-forms.input id="password_confirmation" required type="password"
name="password_confirmation" label="{{ __('input.password.again') }}" />
</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>
<a href="/login" class="button bg-coollabs-gradient">
{{ __('auth.already_registered') }}

View File

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

View File

@ -8,7 +8,7 @@
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>
<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
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))
@ -23,7 +23,7 @@ class="w-full mb-1 text-base font-bold leading-none -translate-y-1 text-neutral-
<div>{{ $description }}</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"
stroke-width="1.5" stroke="currentColor" class="w-full h-full">
<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>
</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
@if ($application->build_pack !== 'dockercompose')

View File

@ -50,7 +50,7 @@ class="items-center justify-center box">+ Add New Resource</a>
<div x-data="searchComponent()">
<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">
<template x-for="item in filteredApplications" :key="item.uuid">
<template x-for="item in allFilteredItems" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
@ -83,285 +83,6 @@ class="flex flex-wrap gap-1 pt-1 group-hover:dark:text-white group-hover:text-bl
</div>
</span>
</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>
@endif
@ -393,118 +114,32 @@ function searchComponent() {
const hrefLink = item.hrefLink;
window.location.href = `${hrefLink}#tags`;
},
get filteredApplications() {
filterAndSort(items) {
if (this.search === '') {
return Object.values(this.applications).sort(sortFn);
return Object.values(items).sort(sortFn);
}
this.applications = Object.values(this.applications);
return this.applications.filter(item => {
return item.name?.toLowerCase().includes(this.search.toLowerCase()) ||
item.fqdn?.toLowerCase().includes(this.search.toLowerCase()) ||
item.description?.toLowerCase().includes(this.search.toLowerCase()) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(this.search.toLowerCase()));
const searchLower = this.search.toLowerCase();
return Object.values(items).filter(item => {
return (item.name?.toLowerCase().includes(searchLower) ||
item.fqdn?.toLowerCase().includes(searchLower) ||
item.description?.toLowerCase().includes(searchLower) ||
item.tags?.some(tag => tag.name.toLowerCase().includes(searchLower)));
}).sort(sortFn);
},
get filteredPostgresqls() {
if (this.search === '') {
return Object.values(this.postgresqls).sort(sortFn);
}
this.postgresqls = Object.values(this.postgresqls);
return this.postgresqls.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 filteredRedis() {
if (this.search === '') {
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);
},
get allFilteredItems() {
return [
this.applications,
this.postgresqls,
this.redis,
this.mongodbs,
this.mysqls,
this.mariadbs,
this.keydbs,
this.dragonflies,
this.clickhouses,
this.services
].flatMap((items) => this.filterAndSort(items))
}
};
}
</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 -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"
CDN="https://cdn.coollabs.io/coolify"
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
if [ "$OS_TYPE" = "manjaro" ]; then
if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then
OS_TYPE="arch"
fi
@ -35,7 +35,7 @@ fi
# Install xargs on Amazon Linux 2023 - lol
if [ "$OS_TYPE" = 'amzn' ]; then
dnf install -y findutils >/dev/null 2>&1
dnf install -y findutils >/dev/null
fi
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
arch)
pacman -Sy >/dev/null 2>&1 || true
if ! pacman -Q curl wget git jq >/dev/null 2>&1; then
pacman -S --noconfirm curl wget git jq >/dev/null 2>&1 || true
pacman -Sy >/dev/null || true
if ! pacman -Q curl wget git jq >/dev/null; then
pacman -S --noconfirm curl wget git jq >/dev/null || true
fi
;;
ubuntu | debian | raspbian)
apt update -y >/dev/null 2>&1
apt install -y curl wget git jq >/dev/null 2>&1
apt update -y >/dev/null
apt install -y curl wget git jq >/dev/null
;;
centos | fedora | rhel | ol | rocky | almalinux | amzn)
if [ "$OS_TYPE" = "amzn" ]; then
dnf install -y wget git jq >/dev/null 2>&1
dnf install -y wget git jq >/dev/null
else
if ! command -v dnf >/dev/null 2>&1; then
yum install -y dnf >/dev/null 2>&1
if ! command -v dnf >/dev/null; then
yum install -y dnf >/dev/null
fi
dnf install -y curl wget git jq >/dev/null 2>&1
dnf install -y curl wget git jq >/dev/null
fi
;;
sles | opensuse-leap | opensuse-tumbleweed)
zypper refresh >/dev/null 2>&1
zypper install -y curl wget git jq >/dev/null 2>&1
zypper refresh >/dev/null
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."

View File

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

View File

@ -1,7 +1,7 @@
# 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.
# tags: logto,identity,login,authentication,oauth,oidc,openid
# icon: svgs/logto_dark.svg
# logo: svgs/logto_dark.svg
services:
logto:
@ -32,7 +32,7 @@ services:
volumes:
- logto-postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "$SERVICE_USER_POSTGRES"]
test: ["CMD", "pg_isready", "-U", "$SERVICE_USER_POSTGRES", "-d", "$POSTGRES_DB"]
interval: 5s
timeout: 20s
retries: 10

File diff suppressed because one or more lines are too long

View File

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