Merge branch 'next' into new-dockerfiles

This commit is contained in:
🏔️ Peak
2024-11-14 12:49:50 +01:00
committed by GitHub
21 changed files with 129 additions and 90 deletions

4
.gitattributes vendored
View File

@@ -5,3 +5,7 @@
*.html diff=html *.html diff=html
*.md diff=markdown *.md diff=markdown
*.php diff=php *.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

View File

@@ -142,12 +142,10 @@ By subscribing to the cloud version, you get the Coolify server for the same pri
# Core Maintainers # Core Maintainers
<a href="https://github.com/andrasbacsai"> | Andras Bacsai | Peak |
<img src="https://github.com/andrasbacsai.png" width="60px" alt="andrasbacsai" /> |------------|------------|
</a> | <img src="https://github.com/andrasbacsai.png" width="200px" alt="Andras Bacsai" /> | <img src="https://github.com/peaklabs-dev.png" width="200px" alt="Peak Labs" /> |
<a href="https://github.com/peaklabs-dev"> | <a href="https://x.com/heyandras"><img src="https://raw.githubusercontent.com/gauravghongde/social-icons/master/SVG/Color/Twitter.svg" width="25px"></a> <a href="https://github.com/andrasbacsai"><img src="https://raw.githubusercontent.com/gauravghongde/social-icons/master/SVG/Color/Github.svg" width="25px"></a> | <a href="https://x.com/peaklabs_dev"><img src="https://raw.githubusercontent.com/gauravghongde/social-icons/master/SVG/Color/Twitter.svg" width="25px"></a> <a href="https://github.com/peaklabs-dev"><img src="https://raw.githubusercontent.com/gauravghongde/social-icons/master/SVG/Color/Github.svg" width="25px"></a> |
<img src="https://github.com/peaklabs-dev.png" width="60px" alt="peaklabs-dev" />
</a>
# Repo Activity # Repo Activity

View File

@@ -28,6 +28,8 @@ class Kernel extends ConsoleKernel
{ {
private $allServers; private $allServers;
private Schedule $scheduleInstance;
private InstanceSettings $settings; private InstanceSettings $settings;
private string $updateCheckFrequency; private string $updateCheckFrequency;
@@ -36,82 +38,90 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule): void protected function schedule(Schedule $schedule): void
{ {
$this->scheduleInstance = $schedule;
$this->allServers = Server::where('ip', '!=', '1.2.3.4'); $this->allServers = Server::where('ip', '!=', '1.2.3.4');
$this->settings = instanceSettings(); $this->settings = instanceSettings();
$this->updateCheckFrequency = $this->settings->update_check_frequency ?: '0 * * * *'; $this->updateCheckFrequency = $this->settings->update_check_frequency ?: '0 * * * *';
$this->instanceTimezone = $this->settings->instance_timezone ?: config('app.timezone'); $this->instanceTimezone = $this->settings->instance_timezone ?: config('app.timezone');
$schedule->job(new CleanupStaleMultiplexedConnections)->hourly(); if (validate_timezone($this->instanceTimezone) === false) {
$this->instanceTimezone = config('app.timezone');
}
$this->scheduleInstance->job(new CleanupStaleMultiplexedConnections)->hourly();
if (isDev()) { if (isDev()) {
// Instance Jobs // Instance Jobs
$schedule->command('horizon:snapshot')->everyMinute(); $this->scheduleInstance->command('horizon:snapshot')->everyMinute();
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer(); $this->scheduleInstance->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
$schedule->job(new CheckHelperImageJob)->everyTenMinutes()->onOneServer(); $this->scheduleInstance->job(new CheckHelperImageJob)->everyTenMinutes()->onOneServer();
// Server Jobs // Server Jobs
$this->checkResources($schedule); $this->checkResources();
$this->checkScheduledBackups($schedule); $this->checkScheduledBackups();
$this->checkScheduledTasks($schedule); $this->checkScheduledTasks();
$schedule->command('uploads:clear')->everyTwoMinutes(); $this->scheduleInstance->command('uploads:clear')->everyTwoMinutes();
} else { } else {
// Instance Jobs // Instance Jobs
$schedule->command('horizon:snapshot')->everyFiveMinutes(); $this->scheduleInstance->command('horizon:snapshot')->everyFiveMinutes();
$schedule->command('cleanup:unreachable-servers')->daily()->onOneServer(); $this->scheduleInstance->command('cleanup:unreachable-servers')->daily()->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer();
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer(); $this->scheduleInstance->job(new PullTemplatesFromCDN)->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer();
$this->scheduleUpdates($schedule);
$this->scheduleInstance->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
$this->scheduleUpdates();
// Server Jobs // Server Jobs
$this->checkResources($schedule); $this->checkResources();
$this->pullImages($schedule); $this->pullImages();
$this->checkScheduledBackups($schedule); $this->checkScheduledBackups();
$this->checkScheduledTasks($schedule); $this->checkScheduledTasks();
$schedule->command('cleanup:database --yes')->daily(); $this->scheduleInstance->command('cleanup:database --yes')->daily();
$schedule->command('uploads:clear')->everyTwoMinutes(); $this->scheduleInstance->command('uploads:clear')->everyTwoMinutes();
} }
} }
private function pullImages($schedule): void private function pullImages(): void
{ {
$servers = $this->allServers->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_reachable', true)->get(); $servers = $this->allServers->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_reachable', true)->get();
foreach ($servers as $server) { foreach ($servers as $server) {
if ($server->isSentinelEnabled()) { if ($server->isSentinelEnabled()) {
$schedule->job(function () use ($server) { $this->scheduleInstance->job(function () use ($server) {
CheckAndStartSentinelJob::dispatch($server); CheckAndStartSentinelJob::dispatch($server);
})->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer(); })->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer();
} }
} }
$schedule->job(new CheckHelperImageJob) $this->scheduleInstance->job(new CheckHelperImageJob)
->cron($this->updateCheckFrequency) ->cron($this->updateCheckFrequency)
->timezone($this->instanceTimezone) ->timezone($this->instanceTimezone)
->onOneServer(); ->onOneServer();
} }
private function scheduleUpdates($schedule): void private function scheduleUpdates(): void
{ {
$schedule->job(new CheckForUpdatesJob) $this->scheduleInstance->job(new CheckForUpdatesJob)
->cron($this->updateCheckFrequency) ->cron($this->updateCheckFrequency)
->timezone($this->instanceTimezone) ->timezone($this->instanceTimezone)
->onOneServer(); ->onOneServer();
if ($this->settings->is_auto_update_enabled) { if ($this->settings->is_auto_update_enabled) {
$autoUpdateFrequency = $this->settings->auto_update_frequency; $autoUpdateFrequency = $this->settings->auto_update_frequency;
$schedule->job(new UpdateCoolifyJob) $this->scheduleInstance->job(new UpdateCoolifyJob)
->cron($autoUpdateFrequency) ->cron($autoUpdateFrequency)
->timezone($this->instanceTimezone) ->timezone($this->instanceTimezone)
->onOneServer(); ->onOneServer();
} }
} }
private function checkResources($schedule): void private function checkResources(): void
{ {
if (isCloud()) { if (isCloud()) {
$servers = $this->allServers->whereHas('team.subscription')->get(); $servers = $this->allServers->whereHas('team.subscription')->get();
@@ -128,31 +138,34 @@ class Kernel extends ConsoleKernel
$lastSentinelUpdate = $server->sentinel_updated_at; $lastSentinelUpdate = $server->sentinel_updated_at;
if (Carbon::parse($lastSentinelUpdate)->isBefore(now()->subSeconds($server->waitBeforeDoingSshCheck()))) { if (Carbon::parse($lastSentinelUpdate)->isBefore(now()->subSeconds($server->waitBeforeDoingSshCheck()))) {
// Check container status every minute if Sentinel does not activated // Check container status every minute if Sentinel does not activated
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer(); if (validate_timezone($serverTimezone) === false) {
// $schedule->job(new \App\Jobs\ServerCheckNewJob($server))->everyMinute()->onOneServer(); $serverTimezone = config('app.timezone');
}
$this->scheduleInstance->job(new ServerCheckJob($server))->timezone($serverTimezone)->everyMinute()->onOneServer();
// $this->scheduleInstance->job(new \App\Jobs\ServerCheckNewJob($server))->everyMinute()->onOneServer();
// Check storage usage every 10 minutes if Sentinel does not activated // Check storage usage every 10 minutes if Sentinel does not activated
$schedule->job(new ServerStorageCheckJob($server))->everyTenMinutes()->onOneServer(); $this->scheduleInstance->job(new ServerStorageCheckJob($server))->everyTenMinutes()->onOneServer();
} }
if ($server->settings->force_docker_cleanup) { if ($server->settings->force_docker_cleanup) {
$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer(); $this->scheduleInstance->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();
} else { } else {
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->timezone($serverTimezone)->onOneServer(); $this->scheduleInstance->job(new DockerCleanupJob($server))->everyTenMinutes()->timezone($serverTimezone)->onOneServer();
} }
// Cleanup multiplexed connections every hour // Cleanup multiplexed connections every hour
$schedule->job(new ServerCleanupMux($server))->hourly()->onOneServer(); $this->scheduleInstance->job(new ServerCleanupMux($server))->hourly()->onOneServer();
// Temporary solution until we have better memory management for Sentinel // Temporary solution until we have better memory management for Sentinel
if ($server->isSentinelEnabled()) { if ($server->isSentinelEnabled()) {
$schedule->job(function () use ($server) { $this->scheduleInstance->job(function () use ($server) {
$server->restartContainer('coolify-sentinel'); $server->restartContainer('coolify-sentinel');
})->daily()->onOneServer(); })->daily()->onOneServer();
} }
} }
} }
private function checkScheduledBackups($schedule): void private function checkScheduledBackups(): void
{ {
$scheduled_backups = ScheduledDatabaseBackup::where('enabled', true)->get(); $scheduled_backups = ScheduledDatabaseBackup::where('enabled', true)->get();
if ($scheduled_backups->isEmpty()) { if ($scheduled_backups->isEmpty()) {
@@ -174,13 +187,13 @@ class Kernel extends ConsoleKernel
if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) { if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) {
$scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency]; $scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency];
} }
$schedule->job(new DatabaseBackupJob( $this->scheduleInstance->job(new DatabaseBackupJob(
backup: $scheduled_backup backup: $scheduled_backup
))->cron($scheduled_backup->frequency)->timezone($this->instanceTimezone)->onOneServer(); ))->cron($scheduled_backup->frequency)->timezone($this->instanceTimezone)->onOneServer();
} }
} }
private function checkScheduledTasks($schedule): void private function checkScheduledTasks(): void
{ {
$scheduled_tasks = ScheduledTask::where('enabled', true)->get(); $scheduled_tasks = ScheduledTask::where('enabled', true)->get();
if ($scheduled_tasks->isEmpty()) { if ($scheduled_tasks->isEmpty()) {
@@ -214,7 +227,7 @@ class Kernel extends ConsoleKernel
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) { if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency]; $scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
} }
$schedule->job(new ScheduledTaskJob( $this->scheduleInstance->job(new ScheduledTaskJob(
task: $scheduled_task task: $scheduled_task
))->cron($scheduled_task->frequency)->timezone($this->instanceTimezone)->onOneServer(); ))->cron($scheduled_task->frequency)->timezone($this->instanceTimezone)->onOneServer();
} }

View File

@@ -26,7 +26,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
public function middleware(): array public function middleware(): array
{ {
return [(new WithoutOverlapping($this->server->id))->dontRelease()]; return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
} }
public function __construct(public Server $server, public bool $manualCleanup = false) {} public function __construct(public Server $server, public bool $manualCleanup = false) {}

View File

@@ -28,7 +28,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
public function middleware(): array public function middleware(): array
{ {
return [(new WithoutOverlapping($this->server->id))->dontRelease()]; return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
} }
public function __construct(public Server $server) {} public function __construct(public Server $server) {}

View File

@@ -127,7 +127,14 @@ class Show extends Component
$this->server->settings->sentinel_custom_url = $this->sentinelCustomUrl; $this->server->settings->sentinel_custom_url = $this->sentinelCustomUrl;
$this->server->settings->is_sentinel_enabled = $this->isSentinelEnabled; $this->server->settings->is_sentinel_enabled = $this->isSentinelEnabled;
$this->server->settings->is_sentinel_debug_enabled = $this->isSentinelDebugEnabled; $this->server->settings->is_sentinel_debug_enabled = $this->isSentinelDebugEnabled;
$this->server->settings->server_timezone = $this->serverTimezone;
if (! validate_timezone($this->serverTimezone)) {
$this->serverTimezone = config('app.timezone');
throw new \Exception('Invalid timezone.');
} else {
$this->server->settings->server_timezone = $this->serverTimezone;
}
$this->server->settings->save(); $this->server->settings->save();
} else { } else {
$this->name = $this->server->name; $this->name = $this->server->name;

View File

@@ -139,6 +139,14 @@ class Index extends Component
$error_show = false; $error_show = false;
$this->server = Server::findOrFail(0); $this->server = Server::findOrFail(0);
$this->resetErrorBag(); $this->resetErrorBag();
if (! validate_timezone($this->instance_timezone)) {
$this->instance_timezone = config('app.timezone');
throw new \Exception('Invalid timezone.');
} else {
$this->settings->instance_timezone = $this->instance_timezone;
}
if ($this->settings->public_port_min > $this->settings->public_port_max) { if ($this->settings->public_port_min > $this->settings->public_port_max) {
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.'); $this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');

View File

@@ -394,7 +394,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$middlewares->push('gzip'); $middlewares->push('gzip');
} }
if (str($image)->contains('ghost')) { if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost'); $middlewares->push("redir-ghost-{$uuid}");
} }
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) { if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www); $labels = $labels->merge($redirect_to_non_www);
@@ -417,7 +417,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$middlewares->push('gzip'); $middlewares->push('gzip');
} }
if (str($image)->contains('ghost')) { if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost'); $middlewares->push("redir-ghost-{$uuid}");
} }
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) { if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www); $labels = $labels->merge($redirect_to_non_www);
@@ -466,7 +466,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$middlewares->push('gzip'); $middlewares->push('gzip');
} }
if (str($image)->contains('ghost')) { if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost'); $middlewares->push("redir-ghost-{$uuid}");
} }
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) { if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www); $labels = $labels->merge($redirect_to_non_www);
@@ -489,7 +489,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$middlewares->push('gzip'); $middlewares->push('gzip');
} }
if (str($image)->contains('ghost')) { if (str($image)->contains('ghost')) {
$middlewares->push('redir-ghost'); $middlewares->push("redir-ghost-{$uuid}");
} }
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) { if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels = $labels->merge($redirect_to_non_www); $labels = $labels->merge($redirect_to_non_www);

View File

@@ -385,6 +385,11 @@ function validate_cron_expression($expression_to_validate): bool
return $isValid; return $isValid;
} }
function validate_timezone(string $timezone): bool
{
return in_array($timezone, timezone_identifiers_list());
}
function send_internal_notification(string $message): void function send_internal_notification(string $message): void
{ {
try { try {

View File

@@ -74,8 +74,9 @@ services:
networks: networks:
- coolify - coolify
testing-host: testing-host:
image: "ghcr.io/coollabsio/coolify-testing-host:latest" build:
pull_policy: always context: .
dockerfile: ./docker/testing-host/Dockerfile
init: true init: true
container_name: coolify-testing-host container_name: coolify-testing-host
volumes: volumes:

View File

@@ -1,5 +1,5 @@
#!/command/execlineb -P #!/command/execlineb -P
foreground { foreground {
s6-sleep 5 s6-sleep 5
su - www-data -c "php /var/www/html/artisan start:horizon" su - webuser -c "php /var/www/html/artisan start:horizon"
} }

View File

@@ -1,5 +1,5 @@
#!/command/execlineb -P #!/command/execlineb -P
foreground { foreground {
s6-sleep 5 s6-sleep 5
su - www-data -c "php /var/www/html/artisan start:scheduler" su - webuser -c "php /var/www/html/artisan start:scheduler"
} }

View File

@@ -1,5 +1,5 @@
#!/command/execlineb -P #!/command/execlineb -P
foreground { foreground {
s6-sleep 5 s6-sleep 5
su - www-data -c "php /var/www/html/artisan start:horizon" su - webuser -c "php /var/www/html/artisan start:horizon"
} }

View File

@@ -1,3 +1,3 @@
#!/command/execlineb -P #!/command/execlineb -P
s6-setuidgid www-data s6-setuidgid webuser
php /var/www/html/artisan app:init php /var/www/html/artisan app:init

View File

@@ -1,5 +1,5 @@
#!/command/execlineb -P #!/command/execlineb -P
foreground { foreground {
s6-sleep 5 s6-sleep 5
su - www-data -c "php /var/www/html/artisan start:scheduler" su - webuser -c "php /var/www/html/artisan start:scheduler"
} }

View File

@@ -14,8 +14,8 @@
'w-full' => $fullWidth, 'w-full' => $fullWidth,
])> ])>
@if (!$hideLabel) @if (!$hideLabel)
<label @class(['flex gap-4 px-0 min-w-fit label', 'opacity-40' => $disabled])> <label @class(['flex gap-4 items-center px-0 min-w-fit label w-full cursor-pointer', 'opacity-40' => $disabled])>
<span class="flex gap-2"> <span class="flex flex-grow gap-2">
@if ($label) @if ($label)
{!! $label !!} {!! $label !!}
@else @else
@@ -25,11 +25,11 @@
<x-helper :helper="$helper" /> <x-helper :helper="$helper" />
@endif @endif
</span> </span>
@endif
<input @disabled($disabled) type="checkbox" {{ $attributes->merge(['class' => $defaultClass]) }}
@if ($instantSave) wire:loading.attr="disabled" wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}'
wire:model={{ $id }} @else wire:model={{ $value ?? $id }} @endif />
@if (!$hideLabel)
</label> </label>
@endif @endif
<span class="flex-grow"></span>
<input @disabled($disabled) type="checkbox" {{ $attributes->merge(['class' => $defaultClass]) }}
@if ($instantSave) wire:loading.attr="disabled" wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}'
wire:model={{ $id }} @else wire:model={{ $value ?? $id }} @endif />
</div> </div>

View File

@@ -28,7 +28,7 @@
$disableTwoStepConfirmation = data_get(InstanceSettings::get(), 'disable_two_step_confirmation'); $disableTwoStepConfirmation = data_get(InstanceSettings::get(), 'disable_two_step_confirmation');
@endphp @endphp
<div x-data="{ <div wire:ignore x-data="{
modalOpen: false, modalOpen: false,
step: {{ empty($checkboxes) ? 2 : 1 }}, step: {{ empty($checkboxes) ? 2 : 1 }},
initialStep: {{ empty($checkboxes) ? 2 : 1 }}, initialStep: {{ empty($checkboxes) ? 2 : 1 }},
@@ -106,8 +106,8 @@
this.selectedActions.push(id); this.selectedActions.push(id);
} }
} }
}" @keydown.escape.window="modalOpen = false; resetModal()" :class="{ 'z-40': modalOpen }" }" @keydown.escape.window="modalOpen = false; resetModal()"
class="relative w-auto h-auto"> :class="{ 'z-40': modalOpen }" class="relative w-auto h-auto">
@if ($customButton) @if ($customButton)
@if ($buttonFullWidth) @if ($buttonFullWidth)
<x-forms.button @click="modalOpen=true" class="w-full"> <x-forms.button @click="modalOpen=true" class="w-full">
@@ -302,7 +302,8 @@
</x-forms.button> </x-forms.button>
@endif @endif
<x-forms.button <x-forms.button
x-bind:disabled="!disableTwoStepConfirmation && confirmWithText && userConfirmationText !== confirmationText" x-bind:disabled="!disableTwoStepConfirmation && confirmWithText && userConfirmationText !==
confirmationText"
class="w-auto" isError class="w-auto" isError
@click=" @click="
if (dispatchEvent) { if (dispatchEvent) {
@@ -337,11 +338,14 @@
Your Password Your Password
</label> </label>
<form @submit.prevent="false" @keydown.enter.prevent> <form @submit.prevent="false" @keydown.enter.prevent>
<input type="text" name="username" autocomplete="username" value="{{ auth()->user()->email }}" style="display: none;"> <input type="text" name="username" autocomplete="username"
<input type="password" id="password-confirm-{{ $passwordConfirm }}" x-model="password" value="{{ auth()->user()->email }}" style="display: none;">
class="w-full input" placeholder="Enter your password" autocomplete="current-password"> <input type="password" id="password-confirm-{{ $passwordConfirm }}"
x-model="password" class="w-full input" placeholder="Enter your password"
autocomplete="current-password">
</form> </form>
<p x-show="passwordError" x-text="passwordError" class="mt-1 text-sm text-red-500"></p> <p x-show="passwordError" x-text="passwordError" class="mt-1 text-sm text-red-500">
</p>
@error('password') @error('password')
<p class="mt-1 text-sm text-red-500">{{ $message }}</p> <p class="mt-1 text-sm text-red-500">{{ $message }}</p>
@enderror @enderror

View File

@@ -110,8 +110,7 @@
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300' wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search" wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
@focus="open = true" @click.away="open = false" @input="open = true" @focus="open = true" @click.away="open = false" @input="open = true"
class="w-full input" :placeholder="placeholder" class="w-full input" :placeholder="placeholder" wire:model="serverTimezone">
wire:model.debounce.300ms="serverTimezone">
<svg class="absolute right-0 mr-2 w-4 h-4" xmlns="http://www.w3.org/2000/svg" <svg class="absolute right-0 mr-2 w-4 h-4" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
@click="open = true"> @click="open = true">
@@ -124,7 +123,7 @@
<template <template
x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))" x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))"
:key="timezone"> :key="timezone">
<div @click="search = timezone; open = false; $wire.set('serverTimezone', timezone)" <div @click="search = timezone; open = false; $wire.set('serverTimezone', timezone); $wire.submit()"
class="px-4 py-2 text-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 dark:text-gray-200" class="px-4 py-2 text-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 dark:text-gray-200"
x-text="timezone"></div> x-text="timezone"></div>
</template> </template>

View File

@@ -40,14 +40,13 @@
helper="Timezone for the Coolify instance. This is used for the update check and automatic update frequency." /> helper="Timezone for the Coolify instance. This is used for the update check and automatic update frequency." />
</div> </div>
<div class="relative"> <div class="relative">
<div class="inline-flex items-center relative w-full"> <div class="inline-flex relative items-center w-full">
<input autocomplete="off" <input autocomplete="off"
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300' wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search" wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
@focus="open = true" @click.away="open = false" @input="open = true" @focus="open = true" @click.away="open = false" @input="open = true"
class="w-full input " :placeholder="placeholder" class="w-full input" :placeholder="placeholder" wire:model="instance_timezone">
wire:model.debounce.300ms="instance_timezone"> <svg class="absolute right-0 mr-2 w-4 h-4" xmlns="http://www.w3.org/2000/svg"
<svg class="absolute right-0 w-4 h-4 mr-2" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
@click="open = true"> @click="open = true">
<path stroke-linecap="round" stroke-linejoin="round" <path stroke-linecap="round" stroke-linejoin="round"
@@ -55,18 +54,17 @@
</svg> </svg>
</div> </div>
<div x-show="open" <div x-show="open"
class="absolute z-50 w-full mt-1 bg-white dark:bg-coolgray-100 border dark:border-coolgray-200 rounded-md shadow-lg max-h-60 overflow-auto scrollbar overflow-x-hidden"> class="overflow-auto overflow-x-hidden absolute z-50 mt-1 w-full max-h-60 bg-white rounded-md border shadow-lg dark:bg-coolgray-100 dark:border-coolgray-200 scrollbar">
<template <template
x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))" x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))"
:key="timezone"> :key="timezone">
<div @click="search = timezone; open = false; $wire.set('instance_timezone', timezone)" <div @click="search = timezone; open = false; $wire.set('instance_timezone', timezone); $wire.submit()"
class="px-4 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 text-gray-800 dark:text-gray-200" class="px-4 py-2 text-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 dark:text-gray-200"
x-text="timezone"></div> x-text="timezone"></div>
</template> </template>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="flex gap-2 md:flex-row flex-col w-full"> <div class="flex gap-2 md:flex-row flex-col w-full">
<x-forms.input id="public_ipv4" type="password" label="Instance's IPv4" <x-forms.input id="public_ipv4" type="password" label="Instance's IPv4"
@@ -134,7 +132,9 @@
<h4 class="py-4">Confirmation Settings</h4> <h4 class="py-4">Confirmation Settings</h4>
<div x-data="{ open: false }" class="mb-32 md:w-[40rem]"> <div x-data="{ open: false }" class="mb-32 md:w-[40rem]">
<button type="button" @click.prevent="open = !open" <button type="button" @click.prevent="open = !open"
class="flex items-center justify-between w-full p-4 bg-coolgray-100 hover:bg-coolgray-200 rounded-md"> class="flex items-center justify-between w-full p-4 rounded-md
dark:bg-coolgray-100 dark:hover:bg-coolgray-200
bg-gray-100 hover:bg-gray-200">
<span class="font-medium">Two-Step Confirmation Settings</span> <span class="font-medium">Two-Step Confirmation Settings</span>
<svg class="w-5 h-5 transition-transform" :class="{ 'rotate-180': open }" fill="none" <svg class="w-5 h-5 transition-transform" :class="{ 'rotate-180': open }" fill="none"
stroke="currentColor" viewBox="0 0 24 24"> stroke="currentColor" viewBox="0 0 24 24">

View File

@@ -33,7 +33,7 @@
</x-forms.select> </x-forms.select>
<x-forms.button type="submit">Connect</x-forms.button> <x-forms.button type="submit">Connect</x-forms.button>
</form> </form>
<livewire:project.shared.terminal />
@endif @endif
<livewire:project.shared.terminal />
</div> </div>
</div> </div>

View File

@@ -32,14 +32,14 @@ function sync:bunny {
} }
function db:reset { function db:reset {
bash spin exec -u www-data coolify php artisan migrate:fresh --seed bash spin exec -u webuser coolify php artisan migrate:fresh --seed
} }
function db:reset-prod { function db:reset-prod {
bash spin exec -u www-data coolify php artisan migrate:fresh --force --seed --seeder=ProductionSeeder || bash spin exec -u webuser coolify php artisan migrate:fresh --force --seed --seeder=ProductionSeeder ||
php artisan migrate:fresh --force --seed --seeder=ProductionSeeder php artisan migrate:fresh --force --seed --seeder=ProductionSeeder
} }
function coolify { function coolify {
bash spin exec -u www-data coolify bash bash spin exec -u webuser coolify bash
} }
function coolify:root { function coolify:root {
@@ -58,7 +58,7 @@ function vite {
} }
function tinker { function tinker {
bash spin exec -u www-data coolify php artisan tinker bash spin exec -u webuser coolify php artisan tinker
} }
function default { function default {
@@ -66,4 +66,4 @@ function default {
} }
TIMEFORMAT="Task completed in %3lR" TIMEFORMAT="Task completed in %3lR"
time "${@:-default}" time "${@:-default}"