Merge remote-tracking branch 'upstream/next' into docker-image-login

This commit is contained in:
David Buday 2024-11-18 09:02:12 +00:00
commit b7e00ce054
32 changed files with 1001 additions and 953 deletions

View file

@ -13,7 +13,6 @@ class CleanupRedis extends Command
public function handle() public function handle()
{ {
echo "Cleanup Redis keys.\n";
$prefix = config('database.redis.options.prefix'); $prefix = config('database.redis.options.prefix');
$keys = Redis::connection()->keys('*:laravel*'); $keys = Redis::connection()->keys('*:laravel*');

View file

@ -30,7 +30,6 @@ class CleanupStuckedResources extends Command
public function handle() public function handle()
{ {
echo "Running cleanup stucked resources.\n";
$this->cleanup_stucked_resources(); $this->cleanup_stucked_resources();
} }

View file

@ -13,7 +13,7 @@ class Horizon extends Command
public function handle() public function handle()
{ {
if (config('constants.horizon.is_horizon_enabled')) { if (config('constants.horizon.is_horizon_enabled')) {
$this->info('Horizon is enabled. Starting.'); $this->info('[x]: Horizon is enabled. Starting.');
$this->call('horizon'); $this->call('horizon');
exit(0); exit(0);
} else { } else {

View file

@ -2,9 +2,9 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Actions\Server\StopSentinel;
use App\Enums\ActivityTypes; use App\Enums\ActivityTypes;
use App\Enums\ApplicationDeploymentStatus; use App\Enums\ApplicationDeploymentStatus;
use App\Jobs\CheckHelperImageJob;
use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationDeploymentQueue;
use App\Models\Environment; use App\Models\Environment;
use App\Models\ScheduledDatabaseBackup; use App\Models\ScheduledDatabaseBackup;
@ -12,6 +12,7 @@ use App\Models\Server;
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use App\Models\User; use App\Models\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
@ -25,6 +26,8 @@ class Init extends Command
public function handle() public function handle()
{ {
$this->optimize();
if (isCloud() && ! $this->option('force-cloud')) { if (isCloud() && ! $this->option('force-cloud')) {
echo "Skipping init as we are on cloud and --force-cloud option is not set\n"; echo "Skipping init as we are on cloud and --force-cloud option is not set\n";
@ -39,7 +42,6 @@ class Init extends Command
} }
// Backward compatibility // Backward compatibility
// $this->disable_metrics();
$this->replace_slash_in_environment_name(); $this->replace_slash_in_environment_name();
$this->restore_coolify_db_backup(); $this->restore_coolify_db_backup();
$this->update_user_emails(); $this->update_user_emails();
@ -53,9 +55,18 @@ class Init extends Command
} else { } else {
$this->cleanup_in_progress_application_deployments(); $this->cleanup_in_progress_application_deployments();
} }
echo "[3]: Cleanup Redis keys.\n";
$this->call('cleanup:redis'); $this->call('cleanup:redis');
echo "[4]: Cleanup stucked resources.\n";
$this->call('cleanup:stucked-resources'); $this->call('cleanup:stucked-resources');
try {
$this->pullHelperImage();
} catch (\Throwable $e) {
//
}
if (isCloud()) { if (isCloud()) {
try { try {
$this->pullTemplatesFromCDN(); $this->pullTemplatesFromCDN();
@ -87,6 +98,11 @@ class Init extends Command
} }
} }
private function pullHelperImage()
{
CheckHelperImageJob::dispatch();
}
private function pullTemplatesFromCDN() private function pullTemplatesFromCDN()
{ {
$response = Http::retry(3, 1000)->get(config('constants.services.official')); $response = Http::retry(3, 1000)->get(config('constants.services.official'));
@ -95,19 +111,13 @@ class Init extends Command
File::put(base_path('templates/service-templates.json'), json_encode($services)); File::put(base_path('templates/service-templates.json'), json_encode($services));
} }
} }
// private function disable_metrics()
// { private function optimize()
// if (version_compare('4.0.0-beta.312', config('version'), '<=')) { {
// foreach ($this->servers as $server) { echo "[1]: Optimizing Laravel (caching config, routes, views).\n";
// if ($server->settings->is_metrics_enabled === true) { Artisan::call('optimize:clear');
// $server->settings->update(['is_metrics_enabled' => false]); Artisan::call('optimize');
// } }
// if ($server->isFunctional()) {
// StopSentinel::dispatch($server)->onQueue('high');
// }
// }
// }
// }
private function update_user_emails() private function update_user_emails()
{ {
@ -222,15 +232,15 @@ class Init extends Command
$settings = instanceSettings(); $settings = instanceSettings();
$do_not_track = data_get($settings, 'do_not_track'); $do_not_track = data_get($settings, 'do_not_track');
if ($do_not_track == true) { if ($do_not_track == true) {
echo "Skipping alive as do_not_track is enabled\n"; echo "[2]: Skipping sending live signal as do_not_track is enabled\n";
return; return;
} }
try { try {
Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version"); Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version");
echo "I am alive!\n"; echo "[2]: Sending live signal!\n";
} catch (\Throwable $e) { } catch (\Throwable $e) {
echo "Error in alive: {$e->getMessage()}\n"; echo "[2]: Error in sending live signal: {$e->getMessage()}\n";
} }
} }

View file

@ -13,7 +13,7 @@ class Scheduler extends Command
public function handle() public function handle()
{ {
if (config('constants.horizon.is_scheduler_enabled')) { if (config('constants.horizon.is_scheduler_enabled')) {
$this->info('Scheduler is enabled. Starting.'); $this->info('[x]: Scheduler is enabled. Starting.');
$this->call('schedule:work'); $this->call('schedule:work');
exit(0); exit(0);
} else { } else {

View file

@ -25,7 +25,7 @@ class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
return isDev() ? 1 : 3; return isDev() ? 1 : 3;
} }
public function __construct(public Server $server, public ?int $percentage = null) {} public function __construct(public Server $server, public int|string|null $percentage = null) {}
public function handle() public function handle()
{ {

View file

@ -21,8 +21,8 @@ class CreateScheduledBackup extends Component
public bool $enabled = true; public bool $enabled = true;
#[Validate(['required', 'integer'])] #[Validate(['nullable', 'integer'])]
public int $s3StorageId; public ?int $s3StorageId = null;
public Collection $definedS3s; public Collection $definedS3s;
@ -49,6 +49,7 @@ class CreateScheduledBackup extends Component
return; return;
} }
$payload = [ $payload = [
'enabled' => true, 'enabled' => true,
'frequency' => $this->frequency, 'frequency' => $this->frequency,
@ -58,6 +59,7 @@ class CreateScheduledBackup extends Component
'database_type' => $this->database->getMorphClass(), 'database_type' => $this->database->getMorphClass(),
'team_id' => currentTeam()->id, 'team_id' => currentTeam()->id,
]; ];
if ($this->database->type() === 'standalone-postgresql') { if ($this->database->type() === 'standalone-postgresql') {
$payload['databases_to_backup'] = $this->database->postgres_db; $payload['databases_to_backup'] = $this->database->postgres_db;
} elseif ($this->database->type() === 'standalone-mysql') { } elseif ($this->database->type() === 'standalone-mysql') {
@ -72,11 +74,11 @@ class CreateScheduledBackup extends Component
} else { } else {
$this->dispatch('refreshScheduledBackups'); $this->dispatch('refreshScheduledBackups');
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} finally { } finally {
$this->frequency = ''; $this->frequency = '';
$this->saveToS3 = true;
} }
} }
} }

View file

@ -37,6 +37,7 @@ class Tags extends Component
$this->validate(); $this->validate();
$tags = str($this->newTags)->trim()->explode(' '); $tags = str($this->newTags)->trim()->explode(' ');
foreach ($tags as $tag) { foreach ($tags as $tag) {
$tag = strip_tags($tag);
if (strlen($tag) < 2) { if (strlen($tag) < 2) {
$this->dispatch('error', 'Invalid tag.', "Tag <span class='dark:text-warning'>$tag</span> is invalid. Min length is 2."); $this->dispatch('error', 'Invalid tag.', "Tag <span class='dark:text-warning'>$tag</span> is invalid. Min length is 2.");
@ -65,6 +66,7 @@ class Tags extends Component
public function addTag(string $id, string $name) public function addTag(string $id, string $name)
{ {
try { try {
$name = strip_tags($name);
if ($this->resource->tags()->where('id', $id)->exists()) { if ($this->resource->tags()->where('id', $id)->exists()) {
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='dark:text-warning'>$name</span> already added."); $this->dispatch('error', 'Duplicate tags.', "Tag <span class='dark:text-warning'>$name</span> already added.");

View file

@ -6,64 +6,60 @@ use App\Enums\ProxyTypes;
use App\Models\Server; use App\Models\Server;
use App\Models\Team; use App\Models\Team;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Validate;
use Livewire\Component; use Livewire\Component;
class ByIp extends Component class ByIp extends Component
{ {
#[Locked]
public $private_keys; public $private_keys;
#[Locked]
public $limit_reached; public $limit_reached;
#[Validate('nullable|integer', as: 'Private Key')]
public ?int $private_key_id = null; public ?int $private_key_id = null;
#[Validate('nullable|string', as: 'Private Key Name')]
public $new_private_key_name; public $new_private_key_name;
#[Validate('nullable|string', as: 'Private Key Description')]
public $new_private_key_description; public $new_private_key_description;
#[Validate('nullable|string', as: 'Private Key Value')]
public $new_private_key_value; public $new_private_key_value;
#[Validate('required|string', as: 'Name')]
public string $name; public string $name;
#[Validate('nullable|string', as: 'Description')]
public ?string $description = null; public ?string $description = null;
#[Validate('required|string', as: 'IP Address/Domain')]
public string $ip; public string $ip;
#[Validate('required|string', as: 'User')]
public string $user = 'root'; public string $user = 'root';
#[Validate('required|integer|between:1,65535', as: 'Port')]
public int $port = 22; public int $port = 22;
#[Validate('required|boolean', as: 'Swarm Manager')]
public bool $is_swarm_manager = false; public bool $is_swarm_manager = false;
#[Validate('required|boolean', as: 'Swarm Worker')]
public bool $is_swarm_worker = false; public bool $is_swarm_worker = false;
#[Validate('nullable|integer', as: 'Swarm Cluster')]
public $selected_swarm_cluster = null; public $selected_swarm_cluster = null;
#[Validate('required|boolean', as: 'Build Server')]
public bool $is_build_server = false; public bool $is_build_server = false;
#[Locked]
public Collection $swarm_managers; public Collection $swarm_managers;
protected $rules = [
'name' => 'required|string',
'description' => 'nullable|string',
'ip' => 'required',
'user' => 'required|string',
'port' => 'required|integer',
'is_swarm_manager' => 'required|boolean',
'is_swarm_worker' => 'required|boolean',
'is_build_server' => 'required|boolean',
];
protected $validationAttributes = [
'name' => 'Name',
'description' => 'Description',
'ip' => 'IP Address/Domain',
'user' => 'User',
'port' => 'Port',
'is_swarm_manager' => 'Swarm Manager',
'is_swarm_worker' => 'Swarm Worker',
'is_build_server' => 'Build Server',
];
public function mount() public function mount()
{ {
$this->name = generate_random_name(); $this->name = generate_random_name();
@ -88,6 +84,12 @@ class ByIp extends Component
{ {
$this->validate(); $this->validate();
try { try {
if (Server::where('team_id', currentTeam()->id)
->where('ip', $this->ip)
->exists()) {
return $this->dispatch('error', 'This IP/Domain is already in use by another server in your team.');
}
if (is_null($this->private_key_id)) { if (is_null($this->private_key_id)) {
return $this->dispatch('error', 'You must select a private key'); return $this->dispatch('error', 'You must select a private key');
} }

View file

@ -107,6 +107,15 @@ class Show extends Component
{ {
if ($toModel) { if ($toModel) {
$this->validate(); $this->validate();
if (Server::where('team_id', currentTeam()->id)
->where('ip', $this->ip)
->where('id', '!=', $this->server->id)
->exists()) {
$this->ip = $this->server->ip;
throw new \Exception('This IP/Domain is already in use by another server in your team.');
}
$this->server->name = $this->name; $this->server->name = $this->name;
$this->server->description = $this->description; $this->server->description = $this->description;
$this->server->ip = $this->ip; $this->server->ip = $this->ip;

View file

@ -218,10 +218,12 @@ class PrivateKey extends BaseModel
private static function fingerprintExists($fingerprint, $excludeId = null) private static function fingerprintExists($fingerprint, $excludeId = null)
{ {
$query = self::where('fingerprint', $fingerprint); $query = self::query()
->where('fingerprint', $fingerprint)
->where('id', '!=', $excludeId);
if (! is_null($excludeId)) { if (currentTeam()) {
$query->where('id', '!=', $excludeId); $query->where('team_id', currentTeam()->id);
} }
return $query->exists(); return $query->exists();

View file

@ -17,11 +17,14 @@ class Checkbox extends Component
public ?string $value = null, public ?string $value = null,
public ?string $label = null, public ?string $label = null,
public ?string $helper = null, public ?string $helper = null,
public string|bool|null $checked = false,
public string|bool $instantSave = false, public string|bool $instantSave = false,
public bool $disabled = false, public bool $disabled = false,
public string $defaultClass = 'dark:border-neutral-700 text-coolgray-400 focus:ring-warning dark:bg-coolgray-100 rounded cursor-pointer dark:disabled:bg-base dark:disabled:cursor-not-allowed', public string $defaultClass = 'dark:border-neutral-700 text-coolgray-400 focus:ring-warning dark:bg-coolgray-100 rounded cursor-pointer dark:disabled:bg-base dark:disabled:cursor-not-allowed',
) { ) {
// if ($this->disabled) {
$this->defaultClass .= ' opacity-40';
}
} }
/** /**

View file

@ -227,13 +227,17 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
$MINIO_BROWSER_REDIRECT_URL = $variables->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first(); $MINIO_BROWSER_REDIRECT_URL = $variables->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first();
$MINIO_SERVER_URL = $variables->where('key', 'MINIO_SERVER_URL')->first(); $MINIO_SERVER_URL = $variables->where('key', 'MINIO_SERVER_URL')->first();
if (str($MINIO_BROWSER_REDIRECT_URL->value)->isEmpty()) { if (is_null($MINIO_BROWSER_REDIRECT_URL) || is_null($MINIO_SERVER_URL)) {
$MINIO_BROWSER_REDIRECT_URL?->update([ return collect([]);
}
if (str($MINIO_BROWSER_REDIRECT_URL->value ?? '')->isEmpty()) {
$MINIO_BROWSER_REDIRECT_URL->update([
'value' => generateFqdn($server, 'console-'.$uuid, true), 'value' => generateFqdn($server, 'console-'.$uuid, true),
]); ]);
} }
if (str($MINIO_SERVER_URL->value)->isEmpty()) { if (str($MINIO_SERVER_URL->value ?? '')->isEmpty()) {
$MINIO_SERVER_URL?->update([ $MINIO_SERVER_URL->update([
'value' => generateFqdn($server, 'minio-'.$uuid, true), 'value' => generateFqdn($server, 'minio-'.$uuid, true),
]); ]);
} }
@ -246,13 +250,17 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
$LOGTO_ENDPOINT = $variables->where('key', 'LOGTO_ENDPOINT')->first(); $LOGTO_ENDPOINT = $variables->where('key', 'LOGTO_ENDPOINT')->first();
$LOGTO_ADMIN_ENDPOINT = $variables->where('key', 'LOGTO_ADMIN_ENDPOINT')->first(); $LOGTO_ADMIN_ENDPOINT = $variables->where('key', 'LOGTO_ADMIN_ENDPOINT')->first();
if (str($LOGTO_ENDPOINT?->value)->isEmpty()) { if (is_null($LOGTO_ENDPOINT) || is_null($LOGTO_ADMIN_ENDPOINT)) {
$LOGTO_ENDPOINT?->update([ return collect([]);
}
if (str($LOGTO_ENDPOINT->value ?? '')->isEmpty()) {
$LOGTO_ENDPOINT->update([
'value' => generateFqdn($server, 'logto-'.$uuid), 'value' => generateFqdn($server, 'logto-'.$uuid),
]); ]);
} }
if (str($LOGTO_ADMIN_ENDPOINT?->value)->isEmpty()) { if (str($LOGTO_ADMIN_ENDPOINT->value ?? '')->isEmpty()) {
$LOGTO_ADMIN_ENDPOINT?->update([ $LOGTO_ADMIN_ENDPOINT->update([
'value' => generateFqdn($server, 'logto-admin-'.$uuid), 'value' => generateFqdn($server, 'logto-admin-'.$uuid),
]); ]);
} }

View file

@ -4075,7 +4075,7 @@ function defaultNginxConfiguration(): string
location / { location / {
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html index.htm; index index.html index.htm;
try_files $uri $uri/index.html =404; try_files $uri $uri.html $uri/index.html $uri/index.htm $uri/ /index.html /index.htm =404;
} }
error_page 500 502 503 504 /50x.html; error_page 500 502 503 504 /50x.html;

View file

@ -42,6 +42,7 @@
"socialiteproviders/microsoft-azure": "^5.1", "socialiteproviders/microsoft-azure": "^5.1",
"spatie/laravel-activitylog": "^4.7.3", "spatie/laravel-activitylog": "^4.7.3",
"spatie/laravel-data": "^4.11", "spatie/laravel-data": "^4.11",
"spatie/laravel-ray": "^1.37",
"spatie/laravel-schemaless-attributes": "^2.4", "spatie/laravel-schemaless-attributes": "^2.4",
"spatie/url": "^2.2", "spatie/url": "^2.2",
"stripe/stripe-php": "^16.2.0", "stripe/stripe-php": "^16.2.0",
@ -63,7 +64,6 @@
"phpunit/phpunit": "^11.4", "phpunit/phpunit": "^11.4",
"serversideup/spin": "^2.3", "serversideup/spin": "^2.3",
"spatie/laravel-ignition": "^2.1.0", "spatie/laravel-ignition": "^2.1.0",
"spatie/laravel-ray": "^1.37",
"symfony/http-client": "^7.1" "symfony/http-client": "^7.1"
}, },
"minimum-stability": "stable", "minimum-stability": "stable",

1686
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,9 @@
return [ return [
'coolify' => [ 'coolify' => [
'version' => '4.0.0-beta.368', 'version' => '4.0.0-beta.370',
'self_hosted' => env('SELF_HOSTED', true), 'self_hosted' => env('SELF_HOSTED', true),
'autoupdate' => env('AUTOUPDATE', false), 'autoupdate' => env('AUTOUPDATE'),
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper'), 'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper'),
'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false), 'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false),

View file

@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.368'; return '4.0.0-beta.370';

View file

@ -22,9 +22,9 @@ class ProductionSeeder extends Seeder
public function run(): void public function run(): void
{ {
if (isCloud()) { if (isCloud()) {
echo "Running in cloud mode.\n"; echo "[x]: Running in cloud mode.\n";
} else { } else {
echo "Running in self-hosted mode.\n"; echo "[x]: Running in self-hosted mode.\n";
} }
// Fix for 4.0.0-beta.37 // Fix for 4.0.0-beta.37

View file

@ -1,4 +1,5 @@
# Versions # Versions
# https://hub.docker.com/_/alpine # https://hub.docker.com/_/alpine
ARG BASE_IMAGE=alpine:3.20 ARG BASE_IMAGE=alpine:3.20
# https://download.docker.com/linux/static/stable/ # https://download.docker.com/linux/static/stable/

View file

@ -1,5 +1,6 @@
FROM quay.io/soketi/soketi:1.6-16-alpine FROM quay.io/soketi/soketi:1.6-16-alpine
ARG TARGETPLATFORM ARG TARGETPLATFORM
# https://github.com/cloudflare/cloudflared/releases # https://github.com/cloudflare/cloudflared/releases
ARG CLOUDFLARED_VERSION=2024.4.1 ARG CLOUDFLARED_VERSION=2024.4.1

View file

@ -57,13 +57,6 @@ RUN composer dump-autoload
COPY --from=static-assets --chown=9999:9999 /app/public/build ./public/build COPY --from=static-assets --chown=9999:9999 /app/public/build ./public/build
COPY --chmod=755 docker/prod/etc/s6-overlay/ /etc/s6-overlay/ COPY --chmod=755 docker/prod/etc/s6-overlay/ /etc/s6-overlay/
RUN php artisan route:clear
RUN php artisan view:clear
RUN php artisan config:clear
RUN php artisan route:cache
RUN php artisan view:cache
RUN php artisan config:cache
RUN echo "alias ll='ls -al'" >>/etc/bash.bashrc RUN echo "alias ll='ls -al'" >>/etc/bash.bashrc
RUN echo "alias a='php artisan'" >>/etc/bash.bashrc RUN echo "alias a='php artisan'" >>/etc/bash.bashrc
RUN echo "alias logs='tail -f storage/logs/laravel.log'" >>/etc/bash.bashrc RUN echo "alias logs='tail -f storage/logs/laravel.log'" >>/etc/bash.bashrc
@ -86,4 +79,4 @@ RUN { \
} > /etc/php/current_version/cli/conf.d/upload-limits.ini } > /etc/php/current_version/cli/conf.d/upload-limits.ini
COPY --from=minio-client /usr/bin/mc /usr/bin/mc COPY --from=minio-client /usr/bin/mc /usr/bin/mc
RUN chmod +x /usr/bin/mc RUN chmod +x /usr/bin/mc

View file

@ -9,7 +9,7 @@ CDN="https://cdn.coollabs.io/coolify-nightly"
DATE=$(date +"%Y%m%d-%H%M%S") DATE=$(date +"%Y%m%d-%H%M%S")
VERSION="1.6" VERSION="1.6"
DOCKER_VERSION="27.3" DOCKER_VERSION="27.0"
# TODO: Ask for a user # TODO: Ask for a user
CURRENT_USER=$USER CURRENT_USER=$USER

View file

@ -5,6 +5,7 @@
'disabled' => false, 'disabled' => false,
'instantSave' => false, 'instantSave' => false,
'value' => null, 'value' => null,
'checked' => false,
'hideLabel' => false, 'hideLabel' => false,
'fullWidth' => false, 'fullWidth' => false,
]) ])
@ -14,7 +15,9 @@
'w-full' => $fullWidth, 'w-full' => $fullWidth,
])> ])>
@if (!$hideLabel) @if (!$hideLabel)
<label @class(['flex gap-4 items-center px-0 min-w-fit label w-full cursor-pointer', 'opacity-40' => $disabled])> <label @class([
'flex gap-4 items-center px-0 min-w-fit label w-full cursor-pointer',
])>
<span class="flex flex-grow gap-2"> <span class="flex flex-grow gap-2">
@if ($label) @if ($label)
{!! $label !!} {!! $label !!}
@ -26,9 +29,10 @@
@endif @endif
</span> </span>
@endif @endif
<input @disabled($disabled) type="checkbox" {{ $attributes->merge(['class' => $defaultClass]) }} <input @disabled($disabled) type="checkbox" {{ $attributes->merge(['class' => $defaultClass]) }}
@if ($instantSave) wire:loading.attr="disabled" wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}' @if ($instantSave) wire:loading.attr="disabled" wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}'
wire:model={{ $id }} @else wire:model={{ $value ?? $id }} @endif /> @if ($checked) checked @endif
wire:model={{ $id }} @else wire:model={{ $value ?? $id }} @endif />
@if (!$hideLabel) @if (!$hideLabel)
</label> </label>
@endif @endif

View file

@ -9,7 +9,7 @@
'closeOutside' => true, 'closeOutside' => true,
]) ])
<div x-data="{ modalOpen: false }" :class="{ 'z-40': modalOpen }" @keydown.window.escape="modalOpen=false" <div x-data="{ modalOpen: false }" :class="{ 'z-40': modalOpen }" @keydown.window.escape="modalOpen=false"
class="relative w-auto h-auto"> class="relative w-auto h-auto" wire:ignore>
@if ($content) @if ($content)
<div @click="modalOpen=true"> <div @click="modalOpen=true">
{{ $content }} {{ $content }}
@ -27,7 +27,7 @@
@endif @endif
<template x-teleport="body"> <template x-teleport="body">
<div x-show="modalOpen" <div x-show="modalOpen"
class="fixed top-0 left-0 lg:px-0 px-4 z-[99] flex items-center justify-center w-screen h-screen" x-cloak> class="fixed top-0 left-0 lg:px-0 px-4 z-[99] flex items-center justify-center w-screen h-screen">
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0" <div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"

View file

@ -8,7 +8,7 @@
@forelse ($private_keys as $key) @forelse ($private_keys as $key)
@if ($private_key_id == $key->id) @if ($private_key_id == $key->id)
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 box" <div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 box"
wire:click.defer="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}"> wire:click="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}">
<div class="flex flex-col mx-6"> <div class="flex flex-col mx-6">
<div class="box-title"> <div class="box-title">
{{ $key->name }} {{ $key->name }}
@ -21,7 +21,7 @@
</div> </div>
@else @else
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 box" <div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 box"
wire:click.defer="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}"> wire:click="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}">
<div class="flex flex-col mx-6"> <div class="flex flex-col mx-6">
<div class="box-title"> <div class="box-title">
{{ $key->name }} {{ $key->name }}

View file

@ -103,7 +103,7 @@
@if (!is_null(config('constants.coolify.autoupdate', null))) @if (!is_null(config('constants.coolify.autoupdate', null)))
<div class="text-right md:w-96"> <div class="text-right md:w-96">
<x-forms.checkbox instantSave helper="AUTOUPDATE is set in .env file, you need to modify it there." <x-forms.checkbox instantSave helper="AUTOUPDATE is set in .env file, you need to modify it there."
disabled id="is_auto_update_enabled" label="Enabled" /> disabled checked="{{ config('constants.coolify.autoupdate') }}" label="Auto Update Enabled" />
</div> </div>
@else @else
<x-forms.checkbox instantSave id="is_auto_update_enabled" label="Auto Update Enabled" /> <x-forms.checkbox instantSave id="is_auto_update_enabled" label="Auto Update Enabled" />

View file

@ -9,7 +9,7 @@ CDN="https://cdn.coollabs.io/coolify"
DATE=$(date +"%Y%m%d-%H%M%S") DATE=$(date +"%Y%m%d-%H%M%S")
VERSION="1.6" VERSION="1.6"
DOCKER_VERSION="27.3" DOCKER_VERSION="27.0"
# TODO: Ask for a user # TODO: Ask for a user
CURRENT_USER=$USER CURRENT_USER=$USER
@ -151,24 +151,24 @@ echo "| Coolify | $LATEST_VERSION"
echo "| Helper | $LATEST_HELPER_VERSION" echo "| Helper | $LATEST_HELPER_VERSION"
echo "| Realtime | $LATEST_REALTIME_VERSION" echo "| Realtime | $LATEST_REALTIME_VERSION"
echo -e "---------------------------------------------\n" echo -e "---------------------------------------------\n"
echo -e "1. Installing required packages (curl, wget, git, jq). " echo -e "1. Installing required packages (curl, wget, git, jq, openssl). "
case "$OS_TYPE" in case "$OS_TYPE" in
arch) arch)
pacman -Sy --noconfirm --needed curl wget git jq >/dev/null || true pacman -Sy --noconfirm --needed curl wget git jq openssl >/dev/null || true
;; ;;
alpine) alpine)
sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories
apk update >/dev/null apk update >/dev/null
apk add curl wget git jq >/dev/null apk add curl wget git jq openssl >/dev/null
;; ;;
ubuntu | debian | raspbian) ubuntu | debian | raspbian)
apt-get update -y >/dev/null apt-get update -y >/dev/null
apt-get install -y curl wget git jq >/dev/null apt-get install -y curl wget git jq openssl >/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 dnf install -y wget git jq openssl >/dev/null
else else
if ! command -v dnf >/dev/null; then if ! command -v dnf >/dev/null; then
yum install -y dnf >/dev/null yum install -y dnf >/dev/null
@ -176,12 +176,12 @@ centos | fedora | rhel | ol | rocky | almalinux | amzn)
if ! command -v curl >/dev/null; then if ! command -v curl >/dev/null; then
dnf install -y curl >/dev/null dnf install -y curl >/dev/null
fi fi
dnf install -y wget git jq >/dev/null dnf install -y wget git jq openssl >/dev/null
fi fi
;; ;;
sles | opensuse-leap | opensuse-tumbleweed) sles | opensuse-leap | opensuse-tumbleweed)
zypper refresh >/dev/null zypper refresh >/dev/null
zypper install -y curl wget git jq >/dev/null zypper install -y curl wget git jq openssl >/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."
@ -326,6 +326,22 @@ if ! [ -x "$(command -v docker)" ]; then
exit 1 exit 1
fi fi
;; ;;
"fedora")
if [ -x "$(command -v dnf5)" ]; then
# dnf5 is available
dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo --overwrite >/dev/null 2>&1
else
# dnf5 is not available, use dnf
dnf config-manager --add-repo=https://download.docker.com/linux/fedora/docker-ce.repo >/dev/null 2>&1
fi
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin >/dev/null 2>&1
if ! [ -x "$(command -v docker)" ]; then
echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue."
exit 1
fi
systemctl start docker >/dev/null 2>&1
systemctl enable docker >/dev/null 2>&1
;;
*) *)
if [ "$OS_TYPE" = "ubuntu" ] && [ "$OS_VERSION" = "24.10" ]; then if [ "$OS_TYPE" = "ubuntu" ] && [ "$OS_VERSION" = "24.10" ]; then
echo "Docker automated installation is not supported on Ubuntu 24.10 (non-LTS release)." echo "Docker automated installation is not supported on Ubuntu 24.10 (non-LTS release)."
@ -516,9 +532,8 @@ echo -e "\033[0;35m
\____\___/|_| |_|\__, |_| \__,_|\__|\__,_|_|\__,_|\__|_|\___/|_| |_|___(_) \____\___/|_| |_|\__, |_| \__,_|\__|\__,_|_|\__,_|\__|_|\___/|_| |_|___(_)
|___/ |___/
\033[0m" \033[0m"
echo -e "\nYour instance is ready to use." echo -e "\nYour instance is ready to use!\n"
echo -e "You can access Coolify through:" echo -e "You can access Coolify through your Public IP: http://$(curl -4s https://ifconfig.io):8000"
echo -e "- Public IP: http://$(curl -4s https://ifconfig.io):8000"
set +e set +e
DEFAULT_PRIVATE_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p') DEFAULT_PRIVATE_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
@ -526,15 +541,12 @@ PRIVATE_IPS=$(hostname -I)
set -e set -e
if [ -n "$PRIVATE_IPS" ]; then if [ -n "$PRIVATE_IPS" ]; then
echo -e "If your Public IP is not accessible, you can use the following Private IPs:\n" echo -e "\nIf your Public IP is not accessible, you can use the following Private IPs:\n"
for IP in $PRIVATE_IPS; do for IP in $PRIVATE_IPS; do
if [ "$IP" == "$DEFAULT_PRIVATE_IP" ]; then if [ "$IP" != "$DEFAULT_PRIVATE_IP" ]; then
echo -e "http://$DEFAULT_PRIVATE_IP:8000 (default)"
else
echo -e "http://$IP:8000" echo -e "http://$IP:8000"
fi fi
done done
fi fi
echo -e "\n" echo -e "\nWARNING: It is highly recommended to backup your Environment variables file (/data/coolify/source/.env) to a safe location, outside of this server (e.g. into a Password Manager).\n"
echo -e "WARNING: It is highly recommended to backup your Environment variables file (/data/coolify/source/.env) to a safe location, outside of this server (e.g. into a Password Manager).\n"
cp /data/coolify/source/.env /data/coolify/source/.env.backup cp /data/coolify/source/.env /data/coolify/source/.env.backup

View file

@ -9,7 +9,7 @@ services:
image: ghcr.io/browserless/chromium image: ghcr.io/browserless/chromium
environment: environment:
- SERVICE_FQDN_BROWSERLESS_3000 - SERVICE_FQDN_BROWSERLESS_3000
- TOKEN=$SERVICE_BASE64_BROWSERLESS_TOKEN - TOKEN=$SERVICE_PASSWORD_BROWSERLESS
expose: expose:
- 3000 - 3000
healthcheck: healthcheck:

View file

@ -22,6 +22,7 @@ services:
- MONGO_URI=mongodb://mongo:27017/heyform - MONGO_URI=mongodb://mongo:27017/heyform
- REDIS_HOST=keydb - REDIS_HOST=keydb
- REDIS_PORT=6379 - REDIS_PORT=6379
- REDIS_PASSWORD=${SERVICE_PASSWORD_KEYDB}
healthcheck: healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8000 || exit 1"] test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8000 || exit 1"]
interval: 5s interval: 5s

View file

@ -192,7 +192,7 @@
"browserless": { "browserless": {
"documentation": "https://docs.browserless.io/?utm_source=coolify.io", "documentation": "https://docs.browserless.io/?utm_source=coolify.io",
"slogan": "A headless Chrome browser as a service .", "slogan": "A headless Chrome browser as a service .",
"compose": "c2VydmljZXM6CiAgYnJvd3Nlcmxlc3M6CiAgICBpbWFnZTogZ2hjci5pby9icm93c2VybGVzcy9jaHJvbWl1bQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0JST1dTRVJMRVNTXzMwMDAKICAgICAgLSBUT0tFTj0kU0VSVklDRV9CQVNFNjRfQlJPV1NFUkxFU1NfVE9LRU4KICAgIGV4cG9zZToKICAgICAgLSAzMDAwCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gY3VybAogICAgICAgIC0gJy1mJwogICAgICAgIC0gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMC9kb2NzJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==", "compose": "c2VydmljZXM6CiAgYnJvd3Nlcmxlc3M6CiAgICBpbWFnZTogZ2hjci5pby9icm93c2VybGVzcy9jaHJvbWl1bQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0JST1dTRVJMRVNTXzMwMDAKICAgICAgLSBUT0tFTj0kU0VSVklDRV9QQVNTV09SRF9CUk9XU0VSTEVTUwogICAgZXhwb3NlOgogICAgICAtIDMwMDAKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL2RvY3MnCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK",
"tags": [ "tags": [
"chrome", "chrome",
"headless", "headless",
@ -1131,7 +1131,7 @@
"heyform": { "heyform": {
"documentation": "https://docs.heyform.net/open-source/self-hosting?utm_source=coolify.io", "documentation": "https://docs.heyform.net/open-source/self-hosting?utm_source=coolify.io",
"slogan": "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", "slogan": "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.",
"compose": "c2VydmljZXM6CiAgaGV5Zm9ybToKICAgIGltYWdlOiAnaGV5Zm9ybS9jb21tdW5pdHktZWRpdGlvbjpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdoZXlmb3JtLWFzc2V0czovYXBwL3N0YXRpYy91cGxvYWQnCiAgICBkZXBlbmRzX29uOgogICAgICBtb25nbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBrZXlkYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hFWUZPUk1fODAwMAogICAgICAtICdBUFBfSE9NRVBBR0VfVVJMPSR7U0VSVklDRV9GUUROX0hFWUZPUk19JwogICAgICAtICdTRVNTSU9OX0tFWT0ke1NFUlZJQ0VfQkFTRTY0XzY0X1NFU1NJT059JwogICAgICAtICdGT1JNX0VOQ1JZUFRJT05fS0VZPSR7U0VSVklDRV9CQVNFNjRfNjRfRk9STX0nCiAgICAgIC0gJ01PTkdPX1VSST1tb25nb2RiOi8vbW9uZ286MjcwMTcvaGV5Zm9ybScKICAgICAgLSBSRURJU19IT1NUPWtleWRiCiAgICAgIC0gUkVESVNfUE9SVD02Mzc5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3dnZXQgLXFPLSBodHRwOi8vMTI3LjAuMC4xOjgwMDAgfHwgZXhpdCAxJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogMwogIG1vbmdvOgogICAgaW1hZ2U6ICdwZXJjb25hL3BlcmNvbmEtc2VydmVyLW1vbmdvZGI6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnaGV5Zm9ybS1tb25nby1kYXRhOi9kYXRhL2RiJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICJlY2hvICdvaycgPiAvZGV2L251bGwgMj4mMSIKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEwCiAgICAgIHN0YXJ0X3BlcmlvZDogNXMKICBrZXlkYjoKICAgIGltYWdlOiAnZXFhbHBoYS9rZXlkYjpsYXRlc3QnCiAgICBjb21tYW5kOiAna2V5ZGItc2VydmVyIC0tYXBwZW5kb25seSB5ZXMnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnS0VZREJfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0tFWURCfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2hleWZvcm0ta2V5ZGItZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSBrZXlkYi1jbGkKICAgICAgICAtICctLXBhc3MnCiAgICAgICAgLSAnJHtTRVJWSUNFX1BBU1NXT1JEX0tFWURCfScKICAgICAgICAtIHBpbmcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEwCiAgICAgIHN0YXJ0X3BlcmlvZDogNXMK", "compose": "c2VydmljZXM6CiAgaGV5Zm9ybToKICAgIGltYWdlOiAnaGV5Zm9ybS9jb21tdW5pdHktZWRpdGlvbjpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdoZXlmb3JtLWFzc2V0czovYXBwL3N0YXRpYy91cGxvYWQnCiAgICBkZXBlbmRzX29uOgogICAgICBtb25nbzoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBrZXlkYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0hFWUZPUk1fODAwMAogICAgICAtICdBUFBfSE9NRVBBR0VfVVJMPSR7U0VSVklDRV9GUUROX0hFWUZPUk19JwogICAgICAtICdTRVNTSU9OX0tFWT0ke1NFUlZJQ0VfQkFTRTY0XzY0X1NFU1NJT059JwogICAgICAtICdGT1JNX0VOQ1JZUFRJT05fS0VZPSR7U0VSVklDRV9CQVNFNjRfNjRfRk9STX0nCiAgICAgIC0gJ01PTkdPX1VSST1tb25nb2RiOi8vbW9uZ286MjcwMTcvaGV5Zm9ybScKICAgICAgLSBSRURJU19IT1NUPWtleWRiCiAgICAgIC0gUkVESVNfUE9SVD02Mzc5CiAgICAgIC0gJ1JFRElTX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9LRVlEQn0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3dnZXQgLXFPLSBodHRwOi8vMTI3LjAuMC4xOjgwMDAgfHwgZXhpdCAxJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogMwogIG1vbmdvOgogICAgaW1hZ2U6ICdwZXJjb25hL3BlcmNvbmEtc2VydmVyLW1vbmdvZGI6bGF0ZXN0JwogICAgdm9sdW1lczoKICAgICAgLSAnaGV5Zm9ybS1tb25nby1kYXRhOi9kYXRhL2RiJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICJlY2hvICdvaycgPiAvZGV2L251bGwgMj4mMSIKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEwCiAgICAgIHN0YXJ0X3BlcmlvZDogNXMKICBrZXlkYjoKICAgIGltYWdlOiAnZXFhbHBoYS9rZXlkYjpsYXRlc3QnCiAgICBjb21tYW5kOiAna2V5ZGItc2VydmVyIC0tYXBwZW5kb25seSB5ZXMnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnS0VZREJfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0tFWURCfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2hleWZvcm0ta2V5ZGItZGF0YTovZGF0YScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ELVNIRUxMCiAgICAgICAgLSBrZXlkYi1jbGkKICAgICAgICAtICctLXBhc3MnCiAgICAgICAgLSAnJHtTRVJWSUNFX1BBU1NXT1JEX0tFWURCfScKICAgICAgICAtIHBpbmcKICAgICAgaW50ZXJ2YWw6IDVzCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIHJldHJpZXM6IDEwCiAgICAgIHN0YXJ0X3BlcmlvZDogNXMK",
"tags": [ "tags": [
"form", "form",
"builder", "builder",

View file

@ -1,19 +1,19 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.368" "version": "4.0.0-beta.370"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.369" "version": "4.0.0-beta.371"
}, },
"helper": { "helper": {
"version": "1.0.3" "version": "1.0.4"
}, },
"realtime": { "realtime": {
"version": "1.0.4" "version": "1.0.5"
}, },
"sentinel": { "sentinel": {
"version": "0.0.15" "version": "0.0.15"
} }
} }
} }