Merge pull request #6096 from coollabsio/next

v4.0.0-beta.420.2
This commit is contained in:
Andras Bacsai
2025-07-03 15:54:22 +02:00
committed by GitHub
62 changed files with 1031 additions and 618 deletions

View File

@@ -27,6 +27,8 @@ class StartDatabaseProxy
$server = data_get($database, 'destination.server');
$containerName = data_get($database, 'uuid');
$proxyContainerName = "{$database->uuid}-proxy";
$isSSLEnabled = $database->enable_ssl ?? false;
if ($database->getMorphClass() === \App\Models\ServiceDatabase::class) {
$databaseType = $database->databaseType();
$network = $database->service->uuid;
@@ -42,6 +44,12 @@ class StartDatabaseProxy
'standalone-mongodb' => 27017,
default => throw new \Exception("Unsupported database type: $databaseType"),
};
if ($isSSLEnabled) {
$internalPort = match ($databaseType) {
'standalone-redis', 'standalone-keydb', 'standalone-dragonfly' => 6380,
default => throw new \Exception("Unsupported database type: $databaseType"),
};
}
$configuration_dir = database_proxy_dir($database->uuid);
if (isDev()) {

View File

@@ -19,6 +19,7 @@ class StartService
StopService::run(service: $service, dockerCleanup: false);
}
$service->saveComposeConfigs();
$service->isConfigurationChanged(save: true);
$commands[] = 'cd '.$service->workdir();
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
if ($pullLatestImages) {

View File

@@ -23,7 +23,7 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho
public function middleware(): array
{
return [(new WithoutOverlapping('cleanup-instance-stuffs'))->dontRelease()];
return [(new WithoutOverlapping('cleanup-instance-stuffs'))->expireAfter(60)->dontRelease()];
}
public function handle(): void

View File

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

View File

@@ -70,7 +70,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
public function middleware(): array
{
return [(new WithoutOverlapping('push-server-update-'.$this->server->uuid))->dontRelease()];
return [(new WithoutOverlapping('push-server-update-'.$this->server->uuid))->expireAfter(30)->dontRelease()];
}
public function backoff(): int

View File

@@ -23,7 +23,7 @@ class RestartProxyJob implements ShouldBeEncrypted, ShouldQueue
public function middleware(): array
{
return [(new WithoutOverlapping('restart-proxy-'.$this->server->uuid))->dontRelease()];
return [(new WithoutOverlapping('restart-proxy-'.$this->server->uuid))->expireAfter(60)->dontRelease()];
}
public function __construct(public Server $server) {}

View File

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

View File

@@ -19,11 +19,11 @@ class ServerPatchCheckJob implements ShouldBeEncrypted, ShouldQueue
public $tries = 3;
public $timeout = 600; // 10 minutes timeout
public $timeout = 600;
public function middleware(): array
{
return [(new WithoutOverlapping('server-patch-check-'.$this->server->uuid))->dontRelease()];
return [(new WithoutOverlapping('server-patch-check-'.$this->server->uuid))->expireAfter(600)->dontRelease()];
}
public function __construct(public Server $server) {}

View File

@@ -56,7 +56,6 @@ class CloneMe extends Component
$this->project_id = $this->project->id;
$this->servers = currentTeam()
->servers()
->with('destinations')
->get()
->reject(fn ($server) => $server->isBuildServer());
$this->newName = str($this->project->name.'-clone-'.(string) new Cuid2)->slug();

View File

@@ -63,7 +63,7 @@ class Heading extends Component
$this->service->databases->each(function ($database) {
$database->refresh();
});
if (is_null($this->service->config_hash) || $this->service->isConfigurationChanged()) {
if (is_null($this->service->config_hash)) {
$this->service->isConfigurationChanged(true);
}
$this->dispatch('configurationChanged');

View File

@@ -170,6 +170,7 @@ class Show extends Component
$this->syncData(true);
$this->dispatch('success', 'Environment variable updated.');
$this->dispatch('envsUpdated');
$this->dispatch('configurationChanged');
} catch (\Exception $e) {
return handleError($e);
}

View File

@@ -68,11 +68,16 @@ class Terminal extends Component
// Escape the identifier for shell usage
$escapedIdentifier = escapeshellarg($identifier);
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$escapedIdentifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
$shellCommand = 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && '.
'if [ -f ~/.profile ]; then . ~/.profile; fi && '.
'if [ -n "$SHELL" ] && [ -x "$SHELL" ]; then exec $SHELL; else sh; fi';
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$escapedIdentifier} sh -c '{$shellCommand}'");
} else {
$command = SshMultiplexingHelper::generateSshCommand($server, 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n "$SHELL" ]; then exec $SHELL; else sh; fi');
$shellCommand = 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && '.
'if [ -f ~/.profile ]; then . ~/.profile; fi && '.
'if [ -n "$SHELL" ] && [ -x "$SHELL" ]; then exec $SHELL; else sh; fi';
$command = SshMultiplexingHelper::generateSshCommand($server, $shellCommand);
}
// ssh command is sent back to frontend then to websocket
// this is done because the websocket connection is not available here
// a better solution would be to remove websocket on NodeJS and work with something like

View File

@@ -0,0 +1,118 @@
<?php
namespace App\Livewire\Settings;
use App\Models\InstanceSettings;
use App\Models\Server;
use Auth;
use Hash;
use Livewire\Attributes\Validate;
use Livewire\Component;
class Advanced extends Component
{
#[Validate('required')]
public Server $server;
public InstanceSettings $settings;
#[Validate('boolean')]
public bool $is_registration_enabled;
#[Validate('boolean')]
public bool $do_not_track;
#[Validate('boolean')]
public bool $is_dns_validation_enabled;
#[Validate('nullable|string')]
public ?string $custom_dns_servers = null;
#[Validate('boolean')]
public bool $is_api_enabled;
#[Validate('nullable|string')]
public ?string $allowed_ips = null;
#[Validate('boolean')]
public bool $is_sponsorship_popup_enabled;
#[Validate('boolean')]
public bool $disable_two_step_confirmation;
public function mount()
{
if (! isInstanceAdmin()) {
return redirect()->route('dashboard');
}
$this->server = Server::findOrFail(0);
$this->settings = instanceSettings();
$this->custom_dns_servers = $this->settings->custom_dns_servers;
$this->allowed_ips = $this->settings->allowed_ips;
$this->do_not_track = $this->settings->do_not_track;
$this->is_registration_enabled = $this->settings->is_registration_enabled;
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
$this->is_api_enabled = $this->settings->is_api_enabled;
$this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation;
$this->is_sponsorship_popup_enabled = $this->settings->is_sponsorship_popup_enabled;
}
public function submit()
{
try {
$this->validate();
$this->custom_dns_servers = str($this->custom_dns_servers)->replaceEnd(',', '')->trim();
$this->custom_dns_servers = str($this->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
return str($dns)->trim()->lower();
})->unique()->implode(',');
$this->allowed_ips = str($this->allowed_ips)->replaceEnd(',', '')->trim();
$this->allowed_ips = str($this->allowed_ips)->trim()->explode(',')->map(function ($ip) {
return str($ip)->trim();
})->unique()->implode(',');
$this->instantSave();
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function instantSave()
{
try {
$this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->do_not_track = $this->do_not_track;
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
$this->settings->custom_dns_servers = $this->custom_dns_servers;
$this->settings->is_api_enabled = $this->is_api_enabled;
$this->settings->allowed_ips = $this->allowed_ips;
$this->settings->is_sponsorship_popup_enabled = $this->is_sponsorship_popup_enabled;
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation;
$this->settings->save();
$this->dispatch('success', 'Settings updated!');
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function toggleTwoStepConfirmation($password): bool
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return false;
}
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation = true;
$this->settings->save();
$this->dispatch('success', 'Two step confirmation has been disabled.');
return true;
}
public function render()
{
return view('livewire.settings.advanced');
}
}

View File

@@ -2,11 +2,8 @@
namespace App\Livewire\Settings;
use App\Jobs\CheckForUpdatesJob;
use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Validate;
use Livewire\Component;
@@ -15,10 +12,7 @@ class Index extends Component
{
public InstanceSettings $settings;
protected Server $server;
#[Validate('boolean')]
public bool $is_auto_update_enabled;
public Server $server;
#[Validate('nullable|string|max:255')]
public ?string $fqdn = null;
@@ -29,48 +23,18 @@ class Index extends Component
#[Validate('required|integer|min:1025|max:65535')]
public int $public_port_max;
#[Validate('nullable|string')]
public ?string $custom_dns_servers = null;
#[Validate('nullable|string|max:255')]
public ?string $instance_name = null;
#[Validate('nullable|string')]
public ?string $allowed_ips = null;
#[Validate('nullable|string')]
public ?string $public_ipv4 = null;
#[Validate('nullable|string')]
public ?string $public_ipv6 = null;
#[Validate('string')]
public string $auto_update_frequency;
#[Validate('string|required')]
public string $update_check_frequency;
#[Validate('required|string|timezone')]
public string $instance_timezone;
#[Validate('boolean')]
public bool $do_not_track;
#[Validate('boolean')]
public bool $is_registration_enabled;
#[Validate('boolean')]
public bool $is_dns_validation_enabled;
#[Validate('boolean')]
public bool $is_api_enabled;
#[Validate('boolean')]
public bool $disable_two_step_confirmation;
#[Validate('boolean')]
public bool $is_sponsorship_popup_enabled;
public function render()
{
return view('livewire.settings.index');
@@ -80,27 +44,16 @@ class Index extends Component
{
if (! isInstanceAdmin()) {
return redirect()->route('dashboard');
} else {
}
$this->settings = instanceSettings();
$this->server = Server::findOrFail(0);
$this->fqdn = $this->settings->fqdn;
$this->public_port_min = $this->settings->public_port_min;
$this->public_port_max = $this->settings->public_port_max;
$this->custom_dns_servers = $this->settings->custom_dns_servers;
$this->instance_name = $this->settings->instance_name;
$this->allowed_ips = $this->settings->allowed_ips;
$this->public_ipv4 = $this->settings->public_ipv4;
$this->public_ipv6 = $this->settings->public_ipv6;
$this->do_not_track = $this->settings->do_not_track;
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
$this->is_registration_enabled = $this->settings->is_registration_enabled;
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
$this->is_api_enabled = $this->settings->is_api_enabled;
$this->auto_update_frequency = $this->settings->auto_update_frequency;
$this->update_check_frequency = $this->settings->update_check_frequency;
$this->instance_timezone = $this->settings->instance_timezone;
$this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation;
$this->is_sponsorship_popup_enabled = $this->settings->is_sponsorship_popup_enabled;
}
}
#[Computed]
@@ -115,30 +68,13 @@ class Index extends Component
public function instantSave($isSave = true)
{
$this->validate();
if ($this->settings->is_auto_update_enabled === true) {
$this->validate([
'auto_update_frequency' => ['required', 'string'],
]);
}
$this->settings->fqdn = $this->fqdn;
$this->settings->public_port_min = $this->public_port_min;
$this->settings->public_port_max = $this->public_port_max;
$this->settings->custom_dns_servers = $this->custom_dns_servers;
$this->settings->instance_name = $this->instance_name;
$this->settings->allowed_ips = $this->allowed_ips;
$this->settings->public_ipv4 = $this->public_ipv4;
$this->settings->public_ipv6 = $this->public_ipv6;
$this->settings->do_not_track = $this->do_not_track;
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
$this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
$this->settings->is_api_enabled = $this->is_api_enabled;
$this->settings->auto_update_frequency = $this->auto_update_frequency;
$this->settings->update_check_frequency = $this->update_check_frequency;
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation;
$this->settings->instance_timezone = $this->instance_timezone;
$this->settings->is_sponsorship_popup_enabled = $this->is_sponsorship_popup_enabled;
if ($isSave) {
$this->settings->save();
$this->dispatch('success', 'Settings updated!');
@@ -149,7 +85,6 @@ class Index extends Component
{
try {
$error_show = false;
$this->server = Server::findOrFail(0);
$this->resetErrorBag();
if (! validate_timezone($this->instance_timezone)) {
@@ -166,46 +101,15 @@ class Index extends Component
}
$this->validate();
if ($this->is_auto_update_enabled && ! validate_cron_expression($this->auto_update_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.');
if (empty($this->auto_update_frequency)) {
$this->auto_update_frequency = '0 0 * * *';
}
return;
}
if (! validate_cron_expression($this->update_check_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.');
if (empty($this->update_check_frequency)) {
$this->update_check_frequency = '0 * * * *';
}
return;
}
if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) {
if (! validate_dns_entry($this->settings->fqdn, $this->server)) {
$this->dispatch('error', "Validating DNS failed.<br><br>Make sure you have added the DNS records correctly.<br><br>{$this->settings->fqdn}->{$this->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
if ($this->settings->is_dns_validation_enabled && $this->fqdn) {
if (! validate_dns_entry($this->fqdn, $this->server)) {
$this->dispatch('error', "Validating DNS failed.<br><br>Make sure you have added the DNS records correctly.<br><br>{$this->fqdn}->{$this->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
$error_show = true;
}
}
if ($this->settings->fqdn) {
check_domain_usage(domain: $this->settings->fqdn);
if ($this->fqdn) {
check_domain_usage(domain: $this->fqdn);
}
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim();
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
return str($dns)->trim()->lower();
});
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique();
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(',');
$this->settings->allowed_ips = str($this->settings->allowed_ips)->replaceEnd(',', '')->trim();
$this->settings->allowed_ips = str($this->settings->allowed_ips)->trim()->explode(',')->map(function ($ip) {
return str($ip)->trim();
});
$this->settings->allowed_ips = $this->settings->allowed_ips->unique();
$this->settings->allowed_ips = $this->settings->allowed_ips->implode(',');
$this->instantSave(isSave: false);
@@ -218,31 +122,4 @@ class Index extends Component
return handleError($e, $this);
}
}
public function checkManually()
{
CheckForUpdatesJob::dispatchSync();
$this->dispatch('updateAvailable');
$settings = instanceSettings();
if ($settings->new_version_available) {
$this->dispatch('success', 'New version available!');
} else {
$this->dispatch('success', 'No new version available.');
}
}
public function toggleTwoStepConfirmation($password): bool
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return false;
}
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation = true;
$this->settings->save();
$this->dispatch('success', 'Two step confirmation has been disabled.');
return true;
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace App\Livewire\Settings;
use App\Jobs\CheckForUpdatesJob;
use App\Models\InstanceSettings;
use App\Models\Server;
use Livewire\Attributes\Validate;
use Livewire\Component;
class Updates extends Component
{
public InstanceSettings $settings;
public Server $server;
#[Validate('string')]
public string $auto_update_frequency;
#[Validate('string|required')]
public string $update_check_frequency;
#[Validate('boolean')]
public bool $is_auto_update_enabled;
public function mount()
{
$this->server = Server::findOrFail(0);
$this->settings = instanceSettings();
$this->auto_update_frequency = $this->settings->auto_update_frequency;
$this->update_check_frequency = $this->settings->update_check_frequency;
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
}
public function instantSave()
{
try {
if ($this->settings->is_auto_update_enabled === true) {
$this->validate([
'auto_update_frequency' => ['required', 'string'],
]);
}
$this->settings->auto_update_frequency = $this->auto_update_frequency;
$this->settings->update_check_frequency = $this->update_check_frequency;
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
$this->settings->save();
$this->dispatch('success', 'Settings updated!');
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function submit()
{
try {
$this->resetErrorBag();
$this->validate();
if ($this->is_auto_update_enabled && ! validate_cron_expression($this->auto_update_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.');
if (empty($this->auto_update_frequency)) {
$this->auto_update_frequency = '0 0 * * *';
}
return;
}
if (! validate_cron_expression($this->update_check_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.');
if (empty($this->update_check_frequency)) {
$this->update_check_frequency = '0 * * * *';
}
return;
}
$this->instantSave();
$this->server->setupDynamicProxyConfiguration();
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function checkManually()
{
CheckForUpdatesJob::dispatchSync();
$this->dispatch('updateAvailable');
$settings = instanceSettings();
if ($settings->new_version_available) {
$this->dispatch('success', 'New version available!');
} else {
$this->dispatch('success', 'No new version available.');
}
}
public function render()
{
return view('livewire.settings.updates');
}
}

View File

@@ -46,7 +46,7 @@ class SettingsBackup extends Component
{
if (! isInstanceAdmin()) {
return redirect()->route('dashboard');
} else {
}
$settings = instanceSettings();
$this->server = Server::findOrFail(0);
$this->database = StandalonePostgresql::whereName('coolify-db')->first();
@@ -72,7 +72,6 @@ class SettingsBackup extends Component
$this->settings = $settings;
$this->s3s = $s3s;
}
}
public function addCoolifyDatabase()
{

View File

@@ -31,7 +31,7 @@ class Form extends Component
'storage.endpoint' => 'Endpoint',
];
public function test_s3_connection()
public function testConnection()
{
try {
$this->storage->testConnection(shouldSave: true);
@@ -45,6 +45,8 @@ class Form extends Component
public function delete()
{
try {
$this->authorize('delete', $this->storage);
$this->storage->delete();
return redirect()->route('storage.index');
@@ -57,7 +59,7 @@ class Form extends Component
{
$this->validate();
try {
$this->test_s3_connection();
$this->testConnection();
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -887,7 +887,7 @@ $schema://$host {
public function muxFilename()
{
return $this->uuid;
return 'mux_'.$this->uuid;
}
public function team()

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\S3Storage;
use App\Models\Server;
use App\Models\User;
class S3StoragePolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return true;
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, S3Storage $storage): bool
{
return $user->teams()->where('id', $storage->team_id)->exists();
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return true;
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Server $server): bool
{
return $user->teams()->get()->firstWhere('id', $server->team_id) !== null;
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, S3Storage $storage): bool
{
return $user->teams()->where('id', $storage->team_id)->exists();
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, S3Storage $storage): bool
{
return false;
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, S3Storage $storage): bool
{
return false;
}
}

View File

@@ -2,7 +2,7 @@
return [
'coolify' => [
'version' => '4.0.0-beta.420.1',
'version' => '4.0.0-beta.420.2',
'helper_version' => '1.0.8',
'realtime_version' => '1.0.9',
'self_hosted' => env('SELF_HOSTED', true),

View File

@@ -1,10 +1,10 @@
{
"coolify": {
"v4": {
"version": "4.0.0-beta.420"
"version": "4.0.0-beta.420.2"
},
"nightly": {
"version": "4.0.0-beta.421"
"version": "4.0.0-beta.420.3"
},
"helper": {
"version": "1.0.8"

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
<rect width="1000" height="1000" rx="200" ry="200" fill="#fff" />
<svg viewBox="0 0 107 101" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
<path style="fill:none" d="M24 17h121v121H24z" transform="matrix(.8843 0 0 .83471 -21.223 -14.19)" />
<path d="M119.81 105.98a.549.549 0 0 0-.53-.12c-4.19-6.19-9.52-12.06-14.68-17.73l-.85-.93c0-.11-.05-.21-.12-.3a.548.548 0 0 0-.34-.2l-.17-.18-.12-.09c-.15-.32-.53-.56-.95-.35-1.58.81-3 1.97-4.4 3.04-1.87 1.43-3.7 2.92-5.42 4.52-.7.65-1.39 1.33-1.97 2.09-.28.37-.07.72.27.87-1.22 1.2-2.45 2.45-3.68 3.74-.11.12-.17.28-.16.44.01.16.09.31.22.41l2.16 1.65s.01.03.03.04c3.09 3.05 8.51 7.28 14.25 11.76.85.67 1.71 1.34 2.57 2.01.39.47.76.94 1.12 1.4.19.25.55.3.8.11.13.1.26.21.39.31a.57.57 0 0 0 .8-.1c.07-.09.1-.2.11-.31.04 0 .07.03.1.03.15 0 .31-.06.42-.18l10.18-11.12a.56.56 0 0 0-.04-.8l.01-.01Zm-29.23-3.85c.07.09.14.17.21.25 1.16.98 2.4 2.04 3.66 3.12l-5.12-3.91s-.32-.22-.52-.36c-.11-.08-.21-.16-.31-.24l-.38-.32s.07-.07.1-.11l.35-.35c1.72-1.74 4.67-4.64 6.19-6.06-1.61 1.62-4.87 6.37-4.17 7.98h-.01Zm17.53 13.81-4.22-3.22c-1.65-1.71-3.43-3.4-5.24-5.03 2.28 1.76 4.23 3.25 4.52 3.51 2.21 1.97 2.11 1.61 3.63 2.91l1.83 1.33c-.18.16-.36.33-.53.49l.01.01Zm1.06.81-.08-.06c.16-.13.33-.25.49-.38l-.4.44h-.01ZM42.24 51.45c.14.72.27 1.43.4 2.11.69 3.7 1.33 7.03 2.55 9.56l.48 1.92c.19.73.46 1.64.71 1.83 2.85 2.52 7.22 6.28 11.89 9.82.21.16.5.15.7-.01.01.02.03.03.04.04.11.1.24.15.38.15.16 0 .31-.06.42-.19 5.98-6.65 10.43-12.12 13.6-16.7.2-.25.3-.54.29-.84.2-.24.41-.48.6-.68a.558.558 0 0 0-.1-.86.578.578 0 0 0-.17-.36c-1.39-1.34-2.42-2.31-3.46-3.28-1.84-1.72-3.74-3.5-7.77-7.51-.02-.02-.05-.04-.07-.06a.555.555 0 0 0-.22-.14c-1.11-.39-3.39-.78-6.26-1.28-4.22-.72-10-1.72-15.2-3.27h-.04v-.01s-.02 0-.03.02h-.01l.04-.02s-.31.01-.37.04c-.08.04-.14.09-.19.15-.05.06-.09.12-.47.2-.38.08.08 0 .11 0h-.11v.03c.07.34.05.58.16.97-.02.1.21 1.02.24 1.11l1.83 7.26h.03Zm30.95 6.54s-.03.04-.04.05l-.64-.71c.22.21.44.42.68.66Zm-7.09 9.39s-.07.08-.1.12l-.02-.02c.04-.03.08-.07.13-.1h-.01Zm-7.07 8.47Zm3.02-28.57c.35.35 1.74 1.65 2.06 1.97-1.45-.66-5.06-2.34-6.74-2.88 1.65.29 3.93.66 4.68.91Zm-19.18-2.77c.84 1.44 1.5 6.49 2.16 11.4-.37-1.58-.69-3.12-.99-4.6-.52-2.56-1-4.85-1.67-6.88.14.01.31.03.49.05 0 .01 0 .02.02.03h-.01Zm-.29-1.21c-.23-.02-.44-.04-.62-.05-.02-.04-.03-.08-.04-.12l.66.18v-.01Zm-2.22.45v-.02.02ZM118.9 42.57c.04-.23-1.1-1.24-.74-1.26.85-.04.86-1.35 0-1.31-1.13.06-2.27.32-3.37.53-1.98.37-3.95.78-5.92 1.21-4.39.94-8.77 1.93-13.1 3.11-1.36.37-2.86.7-4.11 1.36-.42.22-.4.67-.17.95-.09.05-.18.08-.28.09-.37.07-.74.13-1.11.19a.566.566 0 0 0-.39.86c-2.32 3.1-4.96 6.44-7.82 9.95-2.81 3.21-5.73 6.63-8.72 10.14-9.41 11.06-20.08 23.6-31.9 34.64-.23.21-.24.57-.03.8.05.06.12.1.19.13-.16.15-.32.3-.48.44-.1.09-.14.2-.16.32-.08.08-.16.17-.23.25-.21.23-.2.59.03.8.23.21.59.2.8-.03.04-.04.08-.09.12-.13a.84.84 0 0 1 1.22 0c.69.74 1.34 1.44 1.95 2.09l-1.38-1.15a.57.57 0 0 0-.8.07c-.2.24-.17.6.07.8l14.82 12.43c.11.09.24.13.37.13.15 0 .29-.06.4-.17l.36-.36a.56.56 0 0 0 .63-.12c20.09-20.18 36.27-35.43 54.8-49.06.17-.12.25-.32.23-.51a.57.57 0 0 0 .48-.39c3.42-10.46 4.08-19.72 4.28-24.27 0-.03.01-.05.02-.07.02-.05.03-.1.04-.14.03-.11.05-.19.05-.19.26-.78.17-1.53-.15-2.15v.02ZM82.98 58.94c.9-1.03 1.79-2.04 2.67-3.02-5.76 7.58-15.3 19.26-28.81 33.14 9.2-10.18 18.47-20.73 26.14-30.12Zm-32.55 52.81-.03-.03c.11.02.19.04.2.04a.47.47 0 0 0-.17 0v-.01Zm6.9 6.42-.05-.04.03-.03c.02 0 .03.02.04.02 0 .02-.02.03-.03.05h.01Zm8.36-7.21 1.38-1.44c.01.01.02.03.03.05-.47.46-.94.93-1.42 1.39h.01Zm2.24-2.21c.26-.3.56-.65.87-1.02.01-.01.02-.03.04-.04 3.29-3.39 6.68-6.82 10.18-10.25.02-.02.05-.04.07-.06.86-.66 1.82-1.39 2.72-2.08-4.52 4.32-9.11 8.78-13.88 13.46v-.01Zm21.65-55.88c-1.86 2.42-3.9 5.56-5.63 8.07-5.46 7.91-23.04 27.28-23.43 27.65-2.71 2.62-10.88 10.46-16.09 15.37-.14.13-.25.24-.34.35a.794.794 0 0 1 .03-1.13c24.82-23.4 39.88-42.89 46-51.38-.13.33-.24.69-.55 1.09l.01-.02Zm16.51 7.1-.01.02c0-.02-.02-.07.01-.02Zm-.91-5.13Zm-5.89 9.45c-2.26-1.31-3.32-3.27-2.71-5.25l.19-.66c.08-.19.17-.38.28-.57.59-.98 1.49-1.85 2.52-2.36.05-.02.1-.03.15-.04a.795.795 0 0 1-.04-.43c.05-.31.25-.58.66-.58.67 0 2.75.62 3.54 1.3.24.19.47.4.68.63.3.35.74.92.96 1.33.13.06.23.62.38.91.14.46.2.93.18 1.4 0 .02 0 .02.01.03-.03.07 0 .37-.04.4-.1.72-.36 1.43-.75 2.05-.04.05-.07.11-.11.16 0 .01-.02.02-.03.04-.3.43-.65.83-1.08 1.13-1.26.89-2.73 1.16-4.2.79a6.33 6.33 0 0 1-.57-.25l-.02-.03Zm16.27-1.63c-.49 2.05-1.09 4.19-1.8 6.38-.03.08-.03.16-.03.23-.1.01-.19.05-.27.11-4.44 3.26-8.73 6.62-12.98 10.11 3.67-3.32 7.39-6.62 11.23-9.95a6.409 6.409 0 0 0 2.11-3.74l.56-3.37.03-.1c.25-.71 1.34-.4 1.17.33h-.02Z" style="fill:#6965db;fill-rule:nonzero" transform="matrix(1 0 0 1 -26.41 -29.49)" />
</svg>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -42,7 +42,7 @@
}
@utility button {
@apply flex gap-2 justify-center items-center px-2 py-1 text-sm text-black normal-case rounded-sm border outline-0 cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit dark:disabled:text-neutral-600 disabled:border-none disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300;
@apply flex gap-2 justify-center items-center px-2 py-1 text-sm text-black normal-case rounded-sm border outline-0 cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit dark:disabled:text-neutral-600 disabled:border-transparent disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300;
}
@utility alert-success {

View File

@@ -5,19 +5,19 @@
<nav class="flex items-center gap-6 min-h-10 whitespace-nowrap">
<a class="{{ request()->routeIs('settings.index') ? 'dark:text-white' : '' }}"
href="{{ route('settings.index') }}">
<button>Configuration</button>
Configuration
</a>
<a class="{{ request()->routeIs('settings.backup') ? 'dark:text-white' : '' }}"
href="{{ route('settings.backup') }}">
<button>Backup</button>
Backup
</a>
<a class="{{ request()->routeIs('settings.email') ? 'dark:text-white' : '' }}"
href="{{ route('settings.email') }}">
<button>Transactional Email</button>
Transactional Email
</a>
<a class="{{ request()->routeIs('settings.oauth') ? 'dark:text-white' : '' }}"
href="{{ route('settings.oauth') }}">
<button>OAuth</button>
OAuth
</a>
<div class="flex-1"></div>
</nav>

View File

@@ -0,0 +1,8 @@
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class="menu-item {{ $activeMenu === 'general' ? 'menu-item-active' : '' }}"
href="{{ route('settings.index') }}">General</a>
<a class="menu-item {{ $activeMenu === 'advanced' ? 'menu-item-active' : '' }}"
href="{{ route('settings.advanced') }}">Advanced</a>
<a class="menu-item {{ $activeMenu === 'updates' ? 'menu-item-active' : '' }}"
href="{{ route('settings.updates') }}">Updates</a>
</div>

View File

@@ -9,16 +9,16 @@
<div class="navbar-main">
<nav class="flex items-center gap-6 min-h-10">
<a class="{{ request()->routeIs('team.index') ? 'dark:text-white' : '' }}" href="{{ route('team.index') }}">
<button>General</button>
General
</a>
<a class="{{ request()->routeIs('team.member.index') ? 'dark:text-white' : '' }}"
href="{{ route('team.member.index') }}">
<button>Members</button>
Members
</a>
@if (isInstanceAdmin())
<a class="{{ request()->routeIs('team.admin-view') ? 'dark:text-white' : '' }}"
href="{{ route('team.admin-view') }}">
<button>Admin View</button>
Admin View
</a>
@endif
<div class="flex-1"></div>

View File

@@ -63,12 +63,13 @@
<section>
<h3 class="pb-2">Servers</h3>
@if ($servers->count() > 0)
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
@foreach ($servers as $server)
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
@class([
'gap-2 border cursor-pointer box group',
'border-red-500' => !$server->settings->is_reachable,
'border-red-500' =>
!$server->settings->is_reachable || $server->settings->force_disabled,
])>
<div class="flex flex-col justify-center mx-6">
<div class="box-title">

View File

@@ -11,13 +11,13 @@
@endif
</div>
<div class="subtitle">Network endpoints to deploy your resources.</div>
<div class="grid gap-2 lg:grid-cols-1">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($servers as $server)
@forelse ($server->destinations() as $destination)
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
<a class="box group"
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
<div class="flex flex-col mx-6">
<div class="flex flex-col justify-center mx-6">
<div class="box-title">{{ $destination->name }}</div>
<div class="box-description">Server: {{ $destination->server->name }}</div>
</div>

View File

@@ -2,5 +2,5 @@
<x-modal-confirmation buttonFullWidth title="Confirm Team Deletion?" buttonTitle="Delete Team" isErrorButton
submitAction="delete" :actions="['The current Team will be permanently deleted.']" confirmationText="{{ $team }}"
confirmationLabel="Please confirm the execution of the actions by entering the Team Name below"
shortConfirmationLabel="Team Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Team Name" />
</div>

View File

@@ -7,86 +7,101 @@
<div class="subtitle ">Quickly clone all resources to a new project or environment.</div>
</div>
<x-forms.input required id="newName" label="New Name" />
<x-forms.button isHighlighted wire:click="clone('project')" class="mt-4">Clone to a new Project</x-forms.button>
<x-forms.button isHighlighted wire:click="clone('environment')" class="mt-4">Clone to a new Environment</x-forms.button>
{{--
<div class="mt-8">
<h3 class="text-lg font-bold mb-2">Clone Volume Data</h3>
<div class="text-sm text-gray-600 dark:text-gray-300 mb-4">
Clone your volume data to the new resources volumes. This process requires a brief container downtime to ensure data consistency.
</div>
<div wire:poll>
@if(!$cloneVolumeData)
<div wire:key="volume-disabled">
<x-modal-confirmation
title="Enable Volume Data Cloning?"
buttonTitle="Enable Volume Cloning"
submitAction="toggleVolumeCloning(true)"
:actions="['This will temporarily stop all the containers to copy the volume data to the new resources to ensure data consistency.', 'The process runs in the background and may take a few minutes.']"
:confirmWithPassword="false"
:confirmWithText="false"
/>
</div>
@else
<div wire:key="volume-enabled" class="max-w-md">
<x-forms.checkbox
label="Clone Volume Data"
id="cloneVolumeData"
wire:model="cloneVolumeData"
wire:change="toggleVolumeCloning(false)"
:checked="$cloneVolumeData"
helper="Volume Data will be cloned to the new resources. Containers will be temporarily stopped during the cloning process." />
</div>
@endif
</div>
</div> --}}
<h3 class="pt-8 pb-2">Servers</h3>
<div>Choose the server and network to clone the resources to.</div>
<div class="flex flex-col gap-4">
@foreach ($servers->sortBy('id') as $server)
<div class="p-4">
<h4>{{ $server->name }}</h4>
<div class="pt-4 pb-2">Docker Networks</div>
<div class="grid grid-cols-1 gap-2 pb-4 lg:grid-cols-4">
@foreach ($server->destinations() as $destination)
<div class="cursor-pointer box-without-bg group"
:class="'{{ $selectedDestination === $destination->id }}' ? 'bg-coollabs text-white' : 'dark:bg-coolgray-100 bg-white'"
wire:click="selectServer('{{ $server->id }}', '{{ $destination->id }}')">
{{ $destination->name }}
</div>
@endforeach
</div>
</div>
@endforeach
</div>
<h3 class="pt-8 pb-2">Resources</h3>
<div>These will be cloned to the new project</div>
<div class="grid grid-cols-1 gap-2 pt-4 opacity-95 lg:grid-cols-2 xl:grid-cols-3">
@foreach ($environment->applications->sortBy('name') as $application)
<div class="bg-white cursor-default box-without-bg dark:bg-coolgray-100 group">
<h3 class="pt-8 ">Destination Server</h3>
<div class="pb-2">Choose the server and network to clone the resources to.</div>
<div class="flex flex-col">
<div class="font-bold dark:text-white">{{ $application->name }}</div>
<div class="description">{{ $application->description }}</div>
<div class="flex flex-col">
<div class="overflow-x-auto">
<div class="inline-block min-w-full">
<div class="overflow-hidden">
<table class="min-w-full">
<thead>
<tr>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Server</th>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Network</th>
</tr>
</thead>
<tbody>
@foreach ($servers->sortBy('id') as $server)
@foreach ($server->destinations() as $destination)
<tr class="cursor-pointer hover:bg-coolgray-50 dark:hover:bg-coolgray-200"
wire:click="selectServer('{{ $server->id }}', '{{ $destination->id }}')">
<td class="px-5 py-4 text-sm whitespace-nowrap dark:text-white"
:class="'{{ $selectedDestination === $destination->id }}' ?
'bg-coollabs text-white' : 'dark:bg-coolgray-100 bg-white'">
{{ $server->name }}</td>
<td class="px-5 py-4 text-sm whitespace-nowrap dark:text-white "
:class="'{{ $selectedDestination === $destination->id }}' ?
'bg-coollabs text-white' : 'dark:bg-coolgray-100 bg-white'">
{{ $destination->name }}
</td>
</tr>
@endforeach
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<h3 class="pt-8">Resources</h3>
<div class="pb-2">These will be cloned to the new project</div>
<div class="flex flex-col pt-4">
<div class="flex flex-col">
<div class="overflow-x-auto">
<div class="inline-block min-w-full">
<div class="overflow-hidden">
<table class="min-w-full">
<thead>
<tr>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Name</th>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Type</th>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Description</th>
</tr>
</thead>
<tbody>
@foreach ($environment->applications->sortBy('name') as $application)
<tr>
<td class="px-5 py-4 text-sm whitespace-nowrap font-bold dark:text-white">
{{ $application->name }}</td>
<td class="px-5 py-4 text-sm whitespace-nowrap dark:text-white">Application</td>
<td class="px-5 py-4 text-sm dark:text-white">
{{ $application->description ?: '-' }}</td>
</tr>
@endforeach
@foreach ($environment->databases()->sortBy('name') as $database)
<div class="bg-white cursor-default box-without-bg dark:bg-coolgray-100 group">
<div class="flex flex-col">
<div class="font-bold dark:text-white">{{ $database->name }}</div>
<div class="description">{{ $database->description }}</div>
</div>
</div>
<tr>
<td class="px-5 py-4 text-sm whitespace-nowrap font-bold dark:text-white">
{{ $database->name }}
</td>
<td class="px-5 py-4 text-sm whitespace-nowrap dark:text-white">Database</td>
<td class="px-5 py-4 text-sm dark:text-white">
{{ $database->description ?: '-' }}</td>
</tr>
@endforeach
@foreach ($environment->services->sortBy('name') as $service)
<div class="bg-white cursor-default box-without-bg dark:bg-coolgray-100 group">
<div class="flex flex-col">
<div class="font-bold dark:text-white">{{ $service->name }}</div>
<div class="description">{{ $service->description }}</div>
</div>
</div>
<tr>
<td class="px-5 py-4 text-sm whitespace-nowrap font-bold dark:text-white">
{{ $service->name }}
</td>
<td class="px-5 py-4 text-sm whitespace-nowrap dark:text-white">Service</td>
<td class="px-5 py-4 text-sm dark:text-white">
{{ $service->description ?: '-' }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="flex gap-4 pt-4 w-full">
<x-forms.button isHighlighted class="w-full" wire:click="clone('project')" :disabled="!filled($selectedDestination)">Clone to new
Project</x-forms.button>
<x-forms.button isHighlighted class="w-full" wire:click="clone('environment')" :disabled="!filled($selectedDestination)">Clone to new
Environment</x-forms.button>
</div>
</form>

View File

@@ -8,7 +8,8 @@
@forelse($executions as $execution)
<div wire:key="{{ data_get($execution, 'id') }}" @class([
'flex flex-col border-l-2 transition-colors p-4 bg-white dark:bg-coolgray-100 text-black dark:text-white',
'border-blue-500/50 border-dashed' => data_get($execution, 'status') === 'running',
'border-blue-500/50 border-dashed' =>
data_get($execution, 'status') === 'running',
'border-error' => data_get($execution, 'status') === 'failed',
'border-success' => data_get($execution, 'status') === 'success',
])>
@@ -20,16 +21,19 @@
<div class="flex items-center gap-2 mb-2">
<span @class([
'px-3 py-1 rounded-md text-xs font-medium tracking-wide shadow-xs',
'bg-blue-100/80 text-blue-700 dark:bg-blue-500/20 dark:text-blue-300 dark:shadow-blue-900/5' => data_get($execution, 'status') === 'running',
'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-200 dark:shadow-red-900/5' => data_get($execution, 'status') === 'failed',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200 dark:shadow-green-900/5' => data_get($execution, 'status') === 'success',
'bg-blue-100/80 text-blue-700 dark:bg-blue-500/20 dark:text-blue-300 dark:shadow-blue-900/5' =>
data_get($execution, 'status') === 'running',
'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-200 dark:shadow-red-900/5' =>
data_get($execution, 'status') === 'failed',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200 dark:shadow-green-900/5' =>
data_get($execution, 'status') === 'success',
])>
@php
$statusText = match (data_get($execution, 'status')) {
'success' => 'Success',
'running' => 'In Progress',
'failed' => 'Failed',
default => ucfirst(data_get($execution, 'status'))
default => ucfirst(data_get($execution, 'status')),
};
@endphp
{{ $statusText }}
@@ -38,8 +42,10 @@
<div class="text-gray-600 dark:text-gray-400 text-sm">
Started: {{ formatDateInServerTimezone(data_get($execution, 'created_at'), $this->server()) }}
@if (data_get($execution, 'status') !== 'running')
<br>Ended: {{ formatDateInServerTimezone(data_get($execution, 'finished_at'), $this->server()) }}
<br>Duration: {{ calculateDuration(data_get($execution, 'created_at'), data_get($execution, 'finished_at')) }}
<br>Ended:
{{ formatDateInServerTimezone(data_get($execution, 'finished_at'), $this->server()) }}
<br>Duration:
{{ calculateDuration(data_get($execution, 'created_at'), data_get($execution, 'finished_at')) }}
<br>Finished {{ \Carbon\Carbon::parse(data_get($execution, 'finished_at'))->diffForHumans() }}
@endif
</div>
@@ -60,17 +66,29 @@
</div>
<span @class([
'px-2 py-1 rounded-sm text-xs font-medium',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get($execution, 'local_storage_deleted', false),
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get($execution, 'local_storage_deleted', false),
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get(
$execution,
'local_storage_deleted',
false),
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get(
$execution,
'local_storage_deleted',
false),
])>
<span class="flex items-center gap-1">
@if (!data_get($execution, 'local_storage_deleted', false))
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"></path>
</svg>
@else
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd"></path>
</svg>
@endif
Local Storage
@@ -79,17 +97,29 @@
@if ($backup->save_s3)
<span @class([
'px-2 py-1 rounded-sm text-xs font-medium',
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get($execution, 's3_storage_deleted', false),
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get($execution, 's3_storage_deleted', false),
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get(
$execution,
's3_storage_deleted',
false),
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get(
$execution,
's3_storage_deleted',
false),
])>
<span class="flex items-center gap-1">
@if (!data_get($execution, 's3_storage_deleted', false))
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"></path>
</svg>
@else
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd"></path>
</svg>
@endif
S3 Storage
@@ -108,11 +138,10 @@
x-on:click="download_file('{{ data_get($execution, 'id') }}')">Download</x-forms.button>
@endif
<x-modal-confirmation title="Confirm Backup Deletion?" buttonTitle="Delete" isErrorButton
submitAction="deleteBackup({{ data_get($execution, 'id') }})"
:checkboxes="$checkboxes"
submitAction="deleteBackup({{ data_get($execution, 'id') }})" :checkboxes="$checkboxes"
:actions="['This backup will be permanently deleted from local storage.']" confirmationText="{{ data_get($execution, 'filename') }}"
confirmationLabel="Please confirm the execution of the actions by entering the Backup Filename below"
shortConfirmationLabel="Backup Filename" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Backup Filename" 1 />
</div>
</div>
@empty

View File

@@ -253,7 +253,8 @@
@endif
@if ($current_step === 'destinations')
<h2>Select a destination</h2>
<div>Destinations are used to segregate resources by network. If you are unsure, select the default
<div class="pb-4">Destinations are used to segregate resources by network. If you are unsure, select the
default
Standalone Docker (coolify).</div>
<div class="flex flex-col justify-center gap-4 text-left xl:flex-row xl:flex-wrap">
@if ($server->isSwarm())

View File

@@ -11,11 +11,11 @@
buttonTitle="Convert to Application" submitAction="convertToApplication" :actions="['The selected resource will be converted to an application.']"
confirmationText="{{ Str::headline($database->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Database Name below"
shortConfirmationLabel="Service Database Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Service Database Name" />
<x-modal-confirmation title="Confirm Service Database Deletion?" buttonTitle="Delete" isErrorButton
submitAction="delete" :actions="['The selected service database container will be stopped and permanently deleted.']" confirmationText="{{ Str::headline($database->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Database Name below"
shortConfirmationLabel="Service Database Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Service Database Name" />
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">

View File

@@ -21,7 +21,7 @@
]"
confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Filepath" />
@else
@if (!$fileStorage->is_binary)
<x-modal-confirmation :ignoreWire="false" title="Confirm File Conversion to Directory?"
@@ -37,7 +37,7 @@
isErrorButton submitAction="delete" :checkboxes="$fileDeletionCheckboxes" :actions="['The selected file will be permanently deleted from the container.']"
confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Filepath" />
@endif
</div>
@if (!$fileStorage->is_directory)

View File

@@ -11,11 +11,11 @@
buttonTitle="Convert to Database" submitAction="convertToDatabase" :actions="['The selected resource will be converted to a service database.']"
confirmationText="{{ Str::headline($application->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Service Application Name" />
<x-modal-confirmation title="Confirm Service Application Deletion?" buttonTitle="Delete" isErrorButton
submitAction="delete" :actions="['The selected service application container will be stopped and permanently deleted.']" confirmationText="{{ Str::headline($application->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Service Application Name" />
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">

View File

@@ -7,5 +7,5 @@
<x-modal-confirmation title="Confirm Resource Deletion?" buttonTitle="Delete" isErrorButton submitAction="delete"
buttonTitle="Delete" :checkboxes="$checkboxes" :actions="['Permanently delete all containers of this resource.']" confirmationText="{{ $resourceName }}"
confirmationLabel="Please confirm the execution of the actions by entering the Resource Name below"
shortConfirmationLabel="Resource Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Resource Name" />
</div>

View File

@@ -70,7 +70,7 @@
'This will stop the all running applications on this server and remove it as a deployment destination.',
]" confirmationText="{{ data_get($destination, 'server.name') }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step3ButtonText="Remove application from server" />
shortConfirmationLabel="Server Name" />
</div>
</div>
@endforeach

View File

@@ -48,10 +48,10 @@
<x-forms.checkbox instantSave id="is_build_time"
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for docker file, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
label="Is Build Variable?" />
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
<x-forms.checkbox instantSave id="is_literal"
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
label="Is Literal?" />
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
@else
@if ($is_shared)
<x-forms.checkbox instantSave id="is_build_time"

View File

@@ -43,7 +43,7 @@
@if ($type === 'server')
<livewire:server.navbar :server="$servers->first()" />
@if ($servers->first()->isTerminalEnabled())
@if ($servers->first()->isTerminalEnabled() && $servers->first()->isFunctional())
<form class="w-full flex gap-2 items-start" wire:submit="$dispatchSelf('connectToServer')"
wire:init="$dispatchSelf('connectToServer')">
<h2 class="pb-4">Terminal</h2>
@@ -54,7 +54,7 @@
<livewire:project.shared.terminal />
</div>
@else
<div>Terminal access is disabled on this server.</div>
<div>Server is not functional or terminal access is disabled.</div>
@endif
@endif
</div>

View File

@@ -60,7 +60,7 @@
'If the persistent storage/volume is actvily used by a resource data will be lost.',
]" confirmationText="{{ $storage->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Storage Name below"
shortConfirmationLabel="Storage Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Storage Name" />
</div>
@endif
</form>

View File

@@ -1,6 +1,5 @@
<div>
<x-security.navbar />
<div class="flex gap-2">
<h2 class="pb-4">Private Keys</h2>
<x-modal-input buttonTitle="+ Add" title="New Private Key">
@@ -9,18 +8,19 @@
<x-modal-confirmation title="Confirm unused SSH Key Deletion?" buttonTitle="Delete unused SSH Keys" isErrorButton
submitAction="cleanupUnusedKeys" :actions="['All unused SSH keys (marked with unused) are permanently deleted.']" :confirmWithText="false" :confirmWithPassword="false" />
</div>
<div class="grid gap-2 lg:grid-cols-2">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($privateKeys as $key)
<a class="box group"
href="{{ route('security.private-key.show', ['private_key_uuid' => data_get($key, 'uuid')]) }}">
<div class="flex flex-col mx-6">
<div class="flex flex-col justify-center mx-6">
<div class="box-title">
{{ data_get($key, 'name') }}
</div>
<div class="box-description">
{{ $key->description }}
@if (!$key->isInUse())
<span class="inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-400 text-black">Unused</span>
<span
class="inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-400 text-black">Unused</span>
@endif
</div>

View File

@@ -4,29 +4,30 @@
</x-slot>
<x-security.navbar />
<div x-data="{ showPrivateKey: false }">
<form class="flex flex-col gap-2" wire:submit='changePrivateKey'>
<div class="flex items-end gap-2">
<h2>Private Key</h2>
<form class="flex flex-col" wire:submit='changePrivateKey'>
<div class="flex items-start gap-2">
<h2 class="pb-4">Private Key</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
@if (data_get($private_key, 'id') > 0)
<x-modal-confirmation
title="Confirm Private Key Deletion?"
isErrorButton
buttonTitle="Delete"
submitAction="delete({{ $private_key->id }})"
:actions="['This private key will be permanently deleted.', 'All servers connected to this private key will stop working.', 'Any git app using this private key will stop working.']"
<x-modal-confirmation title="Confirm Private Key Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $private_key->id }})" :actions="[
'This private key will be permanently deleted.',
'All servers connected to this private key will stop working.',
'Any git app using this private key will stop working.',
]"
confirmationText="{{ $private_key->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Private Key Name below"
shortConfirmationLabel="Private Key Name"
:confirmWithPassword="false"
step2ButtonText="Delete Private Key"
/>
shortConfirmationLabel="Private Key Name" :confirmWithPassword="false"
step2ButtonText="Delete Private Key" />
@endif
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<x-forms.input id="private_key.name" label="Name" required />
<x-forms.input id="private_key.description" label="Description" />
</div>
<div>
<div class="flex items-end gap-2 py-2 ">
<div class="pl-1">Public Key</div>
@@ -56,6 +57,7 @@
<x-forms.textarea rows="10" id="private_key.private_key" required />
</div>
</div>
</div>
</form>
</div>
</div>

View File

@@ -42,8 +42,7 @@
]"
confirmationText="DISABLE CLOUDFLARE TUNNEL"
confirmationLabel="Please type the confirmation text to disable Cloudflare Tunnel."
shortConfirmationLabel="Confirmation text"
step3ButtonText="Disable Cloudflare Tunnel" />
shortConfirmationLabel="Confirmation text" />
@else
<x-modal-confirmation title="Disable Cloudflare Tunnel?"
buttonTitle="Disable Cloudflare Tunnel" isErrorButton
@@ -55,8 +54,7 @@
]"
confirmationText="DISABLE CLOUDFLARE TUNNEL"
confirmationLabel="Please type the confirmation text to disable Cloudflare Tunnel."
shortConfirmationLabel="Confirmation text"
step3ButtonText="Disable Cloudflare Tunnel" />
shortConfirmationLabel="Confirmation text" />
@endif
</div>
@@ -120,7 +118,7 @@
'If you missed something, the connection will not work.',
]" confirmationText="I manually configured Cloudflare Tunnel"
confirmationLabel="Please type the confirmation text to confirm that you manually configured Cloudflare Tunnel."
shortConfirmationLabel="Confirmation text" step3ButtonText="Confirm" />
shortConfirmationLabel="Confirmation text" />
</div>
@endif
</div>

View File

@@ -18,12 +18,12 @@
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Server Name" />
@else
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Server Name" />
@endif
@endif
</div>

View File

@@ -9,15 +9,11 @@
</x-modal-input>
</div>
<div class="subtitle">All your servers are here.</div>
<div class="grid gap-2 lg:grid-cols-2">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($servers as $server)
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
@class([
'gap-2 border cursor-pointer box group',
'border-transparent' =>
$server->settings->is_reachable &&
$server->settings->is_usable &&
!$server->settings->force_disabled,
'border-red-500' =>
!$server->settings->is_reachable || $server->settings->force_disabled,
])>

View File

@@ -0,0 +1,79 @@
<div>
<x-slot:title>
Advanced Settings | Coolify
</x-slot>
<x-settings.navbar />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
<x-settings.sidebar activeMenu="advanced" />
<form wire:submit='submit' class="flex flex-col">
<div class="flex items-center gap-2">
<h2>Advanced</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="pb-4">Advanced settings for your Coolify instance.</div>
<div class="flex flex-col gap-1 md:w-96">
<x-forms.checkbox instantSave id="is_registration_enabled"
helper="If enabled, users can register themselves. If disabled, only administrators can create new users."
label="Registration Allowed" />
<x-forms.checkbox instantSave id="do_not_track"
helper="If enabled, Coolify will not track any data. This is useful if you are concerned about privacy."
label="Do Not Track" />
<h4 class="pt-4">DNS Settings</h4>
<x-forms.checkbox instantSave id="is_dns_validation_enabled"
helper="If you set a custom domain, Coolify will validate the domain in your DNS provider."
label="DNS Validation" />
<x-forms.input id="custom_dns_servers" label="Custom DNS Servers"
helper="DNS servers to validate domains against. A comma separated list of DNS servers."
placeholder="1.1.1.1,8.8.8.8" />
<h4 class="pt-4">API Settings</h4>
<x-forms.checkbox instantSave id="is_api_enabled" label="API Access"
helper="If enabled, the API will be enabled. If disabled, the API will be disabled." />
<x-forms.input id="allowed_ips" label="Allowed IPs for API Access"
helper="Allowed IP lists for the API. A comma separated list of IPs. Empty means you allow from everywhere."
placeholder="1.1.1.1,8.8.8.8" />
<h4 class="pt-4">Confirmation Settings</h4>
<div class="md:w-96 pb-1">
<x-forms.checkbox instantSave id="is_sponsorship_popup_enabled" label="Show Sponsorship Popup"
helper="When enabled, sponsorship popups will be shown monthly to users. When disabled, the sponsorship popup will be permanently hidden for all users." />
</div>
</div>
<div class="flex flex-col gap-1">
@if ($disable_two_step_confirmation)
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-enabled">
<x-forms.checkbox instantSave id="disable_two_step_confirmation"
label="Disable Two Step Confirmation"
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers." />
</div>
@else
<div class="md:w-96 pb-4 flex items-center justify-between gap-2"
wire:key="two-step-confirmation-disabled">
<label class="flex items-center gap-2">
Disable Two Step Confirmation
<x-helper
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers.">
</x-helper>
</label>
<x-modal-confirmation title="Disable Two Step Confirmation?" buttonTitle="Disable" isErrorButton
submitAction="toggleTwoStepConfirmation" :actions="[
'Two Step confirmation will be disabled globally.',
'Disabling two step confirmation reduces security (as anyone can easily delete anything).',
'The risk of accidental actions will increase.',
]"
confirmationText="DISABLE TWO STEP CONFIRMATION"
confirmationLabel="Please type the confirmation text to disable two step confirmation."
shortConfirmationLabel="Confirmation text" />
</div>
<div class="w-full px-4 py-2 mb-4 text-white rounded-xs border-l-4 border-red-500 bg-error">
<p class="font-bold">Warning!</p>
<p>Disabling two step confirmation reduces security (as anyone can easily delete anything) and
increases
the risk of accidental actions. This is not recommended for production servers.</p>
</div>
@endif
</div>
</form>
</div>
</div>

View File

@@ -3,23 +3,25 @@
Settings | Coolify
</x-slot>
<x-settings.navbar />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
<x-settings.sidebar activeMenu="general" />
<form wire:submit='submit' class="flex flex-col">
<div class="flex items-center gap-2 pb-2">
<h2>Configuration</h2>
<div class="flex items-center gap-2">
<h2>General</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div>General configuration for your Coolify instance.</div>
<div class="pb-4">General configuration for your Coolify instance.</div>
<div class="flex flex-col gap-2">
<h4 class="pt-6">Instance Settings</h4>
<div class="flex flex-wrap items-end gap-2">
<div class="flex gap-2 md:flex-row flex-col w-full">
<x-forms.input id="fqdn" label="Instance's Domain"
<x-forms.input id="fqdn" label="Domain"
helper="Enter the full domain name (FQDN) of the instance, including 'https://' if you want to secure the dashboard with HTTPS. Setting this will make the dashboard accessible via this domain, secured by HTTPS, instead of just the IP address."
placeholder="https://coolify.yourdomain.com" />
<x-forms.input id="instance_name" label="Instance's Name" placeholder="Coolify" />
<x-forms.input id="instance_name" label="Name" placeholder="Coolify"
helper="Custom name for your Coolify instance, shown in the URL." />
<div class="w-full" x-data="{
open: false,
search: '{{ $settings->instance_timezone ?: '' }}',
@@ -67,95 +69,15 @@
</div>
</div>
<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 Public IPv4"
helper="Enter the IPv4 address of the instance.<br><br>It is useful if you have several IPv4 addresses and Coolify could not detect the correct one."
placeholder="1.2.3.4" autocomplete="new-password" />
<x-forms.input id="public_ipv6" type="password" label="Instance's IPv6"
<x-forms.input id="public_ipv6" type="password" label="Instance's Public IPv6"
helper="Enter the IPv6 address of the instance.<br><br>It is useful if you have several IPv6 addresses and Coolify could not detect the correct one."
placeholder="2001:db8::1" autocomplete="new-password" />
</div>
<h4 class="w-full pt-6">DNS Validation</h4>
<div class="md:w-96">
<x-forms.checkbox instantSave id="is_dns_validation_enabled" label="Enabled" />
</div>
<x-forms.input id="custom_dns_servers" label="DNS Servers"
helper="DNS servers to validate FQDNs against. A comma separated list of DNS servers."
placeholder="1.1.1.1,8.8.8.8" />
</div>
{{-- <div class="flex gap-2 ">
<x-forms.input type="number" id="public_port_min" label="Public Port Min" />
<x-forms.input type="number" id="public_port_max" label="Public Port Max" />
</div> --}}
</div>
<h4 class="pt-6">API</h4>
<div class="md:w-96 pb-2">
<x-forms.checkbox instantSave id="is_api_enabled" label="Enabled" />
</div>
<x-forms.input id="allowed_ips" label="Allowed IPs"
helper="Allowed IP lists for the API. A comma separated list of IPs. Empty means you allow from everywhere."
placeholder="1.1.1.1,8.8.8.8" />
<h4 class="pt-6">Update</h4>
<div class="text-right md:w-96 pb-4">
@if (!is_null(config('constants.coolify.autoupdate', null)))
<div class="text-right md:w-96">
<x-forms.checkbox instantSave helper="AUTOUPDATE is set in .env file, you need to modify it there."
disabled checked="{{ config('constants.coolify.autoupdate') }}" label="Auto Update Enabled" />
</div>
@else
<x-forms.checkbox instantSave id="is_auto_update_enabled" label="Auto Update Enabled" />
@endif
</div>
<div class="flex flex-col gap-2">
<div class="flex items-end gap-2">
<x-forms.input required id="update_check_frequency" label="Update Check Frequency"
placeholder="0 * * * *"
helper="Cron expression for update check frequency (check for new Coolify versions and pull new Service Templates from CDN).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every hour." />
<x-forms.button wire:click='checkManually'>Check Manually</x-forms.button>
</div>
@if (is_null(config('constants.coolify.autoupdate', null)) && $is_auto_update_enabled)
<x-forms.input required id="auto_update_frequency" label="Auto Update Frequency" placeholder="0 0 * * *"
helper="Cron expression for auto update frequency (automatically update coolify).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every day at 00:00" />
@endif
</div>
<h4 class="pt-6">Advanced</h4>
<div class="text-right md:w-96">
<x-forms.checkbox instantSave id="is_registration_enabled" label="Registration Allowed" />
<x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" />
</div>
<h4 class="py-4">Confirmation Settings</h4>
<div class="md:w-96 ">
<x-forms.checkbox instantSave id="is_sponsorship_popup_enabled" label="Show Sponsorship Popup"
helper="When enabled, sponsorship popups will be shown monthly to users. When disabled, the sponsorship popup will be permanently hidden for all users." />
</div>
@if ($disable_two_step_confirmation)
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-enabled">
<x-forms.checkbox instantSave id="disable_two_step_confirmation" label="Disable Two Step Confirmation"
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers." />
</div>
@else
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-disabled">
<x-modal-confirmation title="Disable Two Step Confirmation?"
buttonTitle="Disable Two Step Confirmation" isErrorButton submitAction="toggleTwoStepConfirmation"
:actions="[
'Two Step confirmation will be disabled globally.',
'Disabling two step confirmation reduces security (as anyone can easily delete anything).',
'The risk of accidental actions will increase.',
]" confirmationText="DISABLE TWO STEP CONFIRMATION"
confirmationLabel="Please type the confirmation text to disable two step confirmation."
shortConfirmationLabel="Confirmation text" step3ButtonText="Disable Two Step Confirmation" />
</div>
<div class="w-full px-4 py-2 mb-4 text-white rounded-xs border-l-4 border-red-500 bg-error">
<p class="font-bold">Warning!</p>
<p>Disabling two step confirmation reduces security (as anyone can easily delete anything) and
increases
the risk of accidental actions. This is not recommended for production servers.</p>
</div>
@endif
</form>
</div>
</div>

View File

@@ -0,0 +1,51 @@
<div>
<x-slot:title>
Auto Update | Coolify
</x-slot>
<x-settings.navbar />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
<x-settings.sidebar activeMenu="updates" />
<form wire:submit='submit' class="flex flex-col w-full">
<div class="flex items-center gap-2">
<h2>Updates</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="pb-4">Your instance's update settings.</div>
<div class="flex flex-col gap-2">
<div class="flex items-end gap-2">
<x-forms.input required id="update_check_frequency" label="Update Check Frequency"
placeholder="0 * * * *"
helper="Frequency (cron expression) to check for new Coolify versions and pull new Service Templates from CDN.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every hour." />
<x-forms.button wire:click='checkManually'>Check Manually</x-forms.button>
</div>
<h4 class="pt-4">Auto Update</h4>
<div class="text-right md:w-64">
@if (!is_null(config('constants.coolify.autoupdate', null)))
<div class="text-right">
<x-forms.checkbox instantSave
helper="AUTOUPDATE is set in .env file, you need to modify it there." disabled
checked="{{ config('constants.coolify.autoupdate') }}" label="Enabled" />
</div>
@else
<x-forms.checkbox instantSave id="is_auto_update_enabled" label="Enabled" />
@endif
</div>
@if (is_null(config('constants.coolify.autoupdate', null)) && $is_auto_update_enabled)
<x-forms.input required id="auto_update_frequency" label="Frequency (cron expression)"
placeholder="0 0 * * *"
helper="Frequency (cron expression) (automatically update coolify).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every day at 00:00" />
@else
<x-forms.input required label="Frequency (cron expression)" disabled placeholder="disabled"
helper="Frequency (cron expression) (automatically update coolify).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every day at 00:00" />
@endif
</div>
</form>
</div>
</div>

View File

@@ -1,21 +1,28 @@
<div>
<form class="flex flex-col gap-2 pb-6" wire:submit='submit'>
<div class="flex items-start gap-2">
<div class="pb-4">
<div class="">
<h1>Storage Details</h1>
<div class="subtitle">{{ $storage->name }}</div>
<div class="flex items-center gap-2 pb-4">
<div>Current Status:</div>
@if ($storage->is_usable)
<div>Usable</div>
<span
class="px-2 py-1 text-xs font-semibold text-green-800 bg-green-100 rounded dark:text-green-100 dark:bg-green-800">
Usable
</span>
@else
<div class="text-red-500">Not Usable</div>
<span
class="px-2 py-1 text-xs font-semibold text-red-800 bg-red-100 rounded dark:text-red-100 dark:bg-red-800">
Not Usable
</span>
@endif
</div>
</div>
<x-forms.button type="submit">
Save
</x-forms.button>
<x-forms.button wire:click="test_s3_connection">
Validate Connection
</x-forms.button>
<x-modal-confirmation title="Confirm Storage Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $storage->id }})" :actions="[
'The selected storage location will be permanently deleted from Coolify.',
@@ -37,5 +44,8 @@
<x-forms.input required type="password" label="Access Key" id="storage.key" />
<x-forms.input required type="password" label="Secret Key" id="storage.secret" />
</div>
<x-forms.button class="mt-4" isHighlighted wire:click="testConnection">
Validate Connection
</x-forms.button>
</form>
</div>

View File

@@ -9,10 +9,10 @@
</x-modal-input>
</div>
<div class="subtitle">S3 storages for backups.</div>
<div class="grid gap-2 lg:grid-cols-2">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($s3 as $storage)
<a href="/storages/{{ $storage->uuid }}" @class(['gap-2 border cursor-pointer box group border-transparent'])>
<div class="flex flex-col mx-6">
<a href="/storages/{{ $storage->uuid }}" @class(['gap-2 border cursor-pointer box group'])>
<div class="flex flex-col justify-center mx-6">
<div class="box-title">
{{ $storage->name }}
</div>
@@ -20,7 +20,10 @@
{{ $storage->description }}
</div>
@if (!$storage->is_usable)
<div class="text-red-500">Not Usable</div>
<span
class="px-2 py-1 text-xs font-semibold text-red-800 bg-red-100 rounded dark:text-red-100 dark:bg-red-800">
Not Usable
</span>
@endif
</div>
</a>

View File

@@ -7,8 +7,8 @@
</div>
<div class="flex flex-wrap gap-2 ">
@forelse ($tags as $oneTag)
<a :class="{{ $tag?->id == $oneTag->id }} && 'dark:bg-coollabs hover:bg-coollabs-100'"
class="w-64 box-without-bg dark:bg-coolgray-100 dark:text-white font-bold"
<a :class="{{ $tag?->id == $oneTag->id }} && 'dark:bg-coollabs'"
class="w-32 box-without-bg dark:bg-coolgray-100 dark:text-white font-bold dark:hover:bg-coollabs-100 flex justify-center items-center"
href="{{ route('tags.show', ['tagName' => $oneTag->name]) }}">{{ $oneTag->name }}</a>
@empty
<div>No tags yet defined yet. Go to a resource and add a tag there.</div>
@@ -16,7 +16,7 @@
</div>
@if (isset($tag))
<div>
<h3 class="py-4">Details</h3>
<h3 class="py-4">Tag Details</h3>
<div class="flex items-end gap-2 ">
<div class="w-[500px]">
<x-forms.input readonly label="Deploy Webhook URL" id="webhook" />
@@ -35,7 +35,7 @@
@if (isset($applications) && count($applications) > 0)
@foreach ($applications as $application)
<a href="{{ $application->link() }}" class="box group">
<div class="flex flex-col">
<div class="flex flex-col justify-center">
<div class="box-title">
{{ $application->project()->name }}/{{ $application->environment->name }}
</div>

View File

@@ -3,12 +3,16 @@
Team Admin | Coolify
</x-slot>
<x-team.navbar />
<h2>Admin View</h2>
<div class="subtitle">
Manage users of this instance.
</div>
<form wire:submit="submitSearch" class="flex flex-col gap-2 lg:flex-row">
<x-forms.input wire:model="search" placeholder="Search for a user" />
<x-forms.button type="submit">Search</x-forms.button>
</form>
<h3 class="pt-4">Users</h3>
<div class="flex flex-col gap-2 ">
<h3 class="py-4">Users</h3>
<div class="grid grid-cols-1 gap-2 lg:grid-cols-2">
@forelse ($users as $user)
<div wire:key="user-{{ $user->id }}"
class="flex items-center justify-center gap-2 bg-white box-without-bg dark:bg-coolgray-100">
@@ -23,7 +27,7 @@
]"
confirmationText="{{ $user->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the User Name below"
shortConfirmationLabel="User Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="User Name" />
</div>
</div>
@empty

View File

@@ -4,17 +4,19 @@
</x-slot>
<x-team.navbar />
<form class="flex flex-col gap-2 pb-6" wire:submit='submit'>
<div class="flex items-end gap-2">
<form class="flex flex-col" wire:submit='submit'>
<h2>General</h2>
<div class="subtitle">
Manage the general settings of this team.
</div>
<div class="flex items-end gap-2 pb-6">
<x-forms.input id="team.name" label="Name" required />
<x-forms.input id="team.description" label="Description" />
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="flex gap-2">
<x-forms.input id="team.name" label="Name" required />
<x-forms.input id="team.description" label="Description" />
</div>
</form>
<div>

View File

@@ -4,7 +4,9 @@
</x-slot>
<x-team.navbar />
<h2>Members</h2>
<div class="subtitle">
Manage or invite members of this team.
</div>
<div class="flex flex-col">
<div class="flex flex-col">
<div class="overflow-x-auto">
@@ -38,8 +40,8 @@
@else
<h2>Invite New Member</h2>
@if (isInstanceAdmin())
<div class="pb-4 text-xs dark:text-warning">You need to configure (as root team) <a href="/settings#smtp"
class="underline dark:text-warning">Transactional
<div class="pb-4 text-xs dark:text-warning">You need to configure (as root team) <a
href="/settings#smtp" class="underline dark:text-warning">Transactional
Emails</a>
before
you can invite a

View File

@@ -1,8 +0,0 @@
<x-layout>
<x-security.navbar />
<a class="text-center hover:no-underline group" href="{{ route('security.private-key.index') }}">
<div class="dark:group-hover:text-white">
<div>Private Keys</div>
</div>
</a>
</x-layout>

View File

@@ -9,24 +9,28 @@
</x-modal-input>
</div>
<div class="subtitle">Git sources for your applications.</div>
<div class="grid gap-2 lg:grid-cols-2">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($sources as $source)
@if ($source->getMorphClass() === 'App\Models\GithubApp')
<a class="flex gap-4 text-center hover:no-underline box group"
<a class="flex gap-2 text-center hover:no-underline box group"
href="{{ route('source.github.show', ['github_app_uuid' => data_get($source, 'uuid')]) }}">
<x-git-icon class="dark:text-white w-9 h-9" git="{{ $source->getMorphClass() }}" />
<div class="text-left dark:group-hover:text-white">
{{-- <x-git-icon class="dark:text-white w-8 h-8 mt-1" git="{{ $source->getMorphClass() }}" /> --}}
<div class="text-left dark:group-hover:text-white flex flex-col justify-center mx-6">
<div class="box-title">{{ $source->name }}</div>
@if (is_null($source->app_id))
<span class="box-description text-error! ">Configuration is not finished.</span>
@else
@if ($source->organization)
<span class="box-description">Organization: {{ $source->organization }}</span>
@endif
@endif
</div>
</a>
@endif
@if ($source->getMorphClass() === 'App\Models\GitlabApp')
{{-- @if ($source->getMorphClass() === 'App\Models\GitlabApp')
<a class="flex gap-4 text-center hover:no-underline box group"
href="{{ route('source.gitlab.show', ['gitlab_app_uuid' => data_get($source, 'uuid')]) }}">
<x-git-icon class="dark:text-white w-9 h-9" git="{{ $source->getMorphClass() }}" />
<x-git-icon class="dark:text-white w-8 h-8" git="{{ $source->getMorphClass() }}" />
<div class="text-left dark:group-hover:text-white">
<div>{{ $source->name }}</div>
@if (is_null($source->app_id))
@@ -34,7 +38,7 @@
@endif
</div>
</a>
@endif
@endif --}}
@empty
<div>
<div>No sources found.</div>

View File

@@ -52,7 +52,9 @@ use App\Livewire\Server\Proxy\Show as ProxyShow;
use App\Livewire\Server\Resources as ResourcesShow;
use App\Livewire\Server\Security\Patches;
use App\Livewire\Server\Show as ServerShow;
use App\Livewire\Settings\Advanced as SettingsAdvanced;
use App\Livewire\Settings\Index as SettingsIndex;
use App\Livewire\Settings\Updates as SettingsUpdates;
use App\Livewire\SettingsBackup;
use App\Livewire\SettingsEmail;
use App\Livewire\SettingsOauth;
@@ -105,6 +107,9 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/subscription/new', SubscriptionIndex::class)->name('subscription.index');
Route::get('/settings', SettingsIndex::class)->name('settings.index');
Route::get('/settings/advanced', SettingsAdvanced::class)->name('settings.advanced');
Route::get('/settings/updates', SettingsUpdates::class)->name('settings.updates');
Route::get('/settings/backup', SettingsBackup::class)->name('settings.backup');
Route::get('/settings/email', SettingsEmail::class)->name('settings.email');
Route::get('/settings/oauth', SettingsOauth::class)->name('settings.oauth');

View File

@@ -5,14 +5,17 @@
services:
appwrite:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
container_name: appwrite
volumes:
- appwrite-uploads:/storage/uploads:rw
- appwrite-imports:/storage/imports:rw
- appwrite-cache:/storage/cache:rw
- appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
depends_on:
- appwrite-mariadb
- appwrite-redis
@@ -21,10 +24,12 @@ services:
- _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_LOCALE=${_APP_LOCALE:-en}
- _APP_COMPRESSION_MIN_SIZE_BYTES=${_APP_COMPRESSION_MIN_SIZE_BYTES}
- _APP_CONSOLE_WHITELIST_ROOT=${_APP_CONSOLE_WHITELIST_ROOT:-enabled}
- _APP_CONSOLE_WHITELIST_EMAILS=${_APP_CONSOLE_WHITELIST_EMAILS}
- _APP_CONSOLE_SESSION_ALERTS=${_APP_CONSOLE_SESSION_ALERTS}
- _APP_CONSOLE_WHITELIST_IPS=${_APP_CONSOLE_WHITELIST_IPS}
- _APP_CONSOLE_HOSTNAMES=${_APP_CONSOLE_HOSTNAMES:-localhost,appwrite.io,*.appwrite.io}
- _APP_CONSOLE_HOSTNAMES=${_APP_CONSOLE_HOSTNAMES}
- _APP_SYSTEM_EMAIL_NAME=${_APP_SYSTEM_EMAIL_NAME:-Appwrite}
- _APP_SYSTEM_EMAIL_ADDRESS=${_APP_SYSTEM_EMAIL_ADDRESS:-team@appwrite.io}
- _APP_SYSTEM_TEAM_EMAIL=${_APP_SYSTEM_TEAM_EMAIL:-team@appwrite.io}
@@ -33,10 +38,12 @@ services:
- _APP_OPTIONS_ABUSE=${_APP_OPTIONS_ABUSE:-enabled}
- _APP_OPTIONS_ROUTER_PROTECTION=${_APP_OPTIONS_ROUTER_PROTECTION:-disabled}
- _APP_OPTIONS_FORCE_HTTPS=${_APP_OPTIONS_FORCE_HTTPS:-disabled}
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=${_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS:-disabled}
- _APP_OPTIONS_ROUTER_FORCE_HTTPS=${_APP_OPTIONS_ROUTER_FORCE_HTTPS:-disabled}
- _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_DOMAIN=$SERVICE_URL_APPWRITE
- _APP_DOMAIN_TARGET=$SERVICE_URL_APPWRITE
- _APP_DOMAIN_TARGET_CNAME=${_APP_DOMAIN_TARGET_CNAME:-localhost}
- _APP_DOMAIN_TARGET_AAAA=${_APP_DOMAIN_TARGET_AAAA:-::1}
- _APP_DOMAIN_TARGET_A=${_APP_DOMAIN_TARGET_A:-127.0.0.1}
- _APP_DOMAIN_FUNCTIONS=$SERVICE_URL_APPWRITE
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
@@ -63,6 +70,7 @@ services:
- _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET}
- _APP_STORAGE_S3_ENDPOINT=${_APP_STORAGE_S3_ENDPOINT}
- _APP_STORAGE_DO_SPACES_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1}
@@ -79,21 +87,26 @@ services:
- _APP_STORAGE_WASABI_SECRET=${_APP_STORAGE_WASABI_SECRET}
- _APP_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION:-eu-central-1}
- _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET}
- _APP_FUNCTIONS_SIZE_LIMIT=${_APP_FUNCTIONS_SIZE_LIMIT:-30000000}
- _APP_COMPUTE_SIZE_LIMIT=${_APP_COMPUTE_SIZE_LIMIT:-30000000}
- _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900}
- _APP_FUNCTIONS_BUILD_TIMEOUT=${_APP_FUNCTIONS_BUILD_TIMEOUT:-900}
- _APP_FUNCTIONS_CPUS=${_APP_FUNCTIONS_CPUS:-0}
- _APP_FUNCTIONS_MEMORY=${_APP_FUNCTIONS_MEMORY:-0}
- _APP_SITES_TIMEOUT=${_APP_SITES_TIMEOUT:-900}
- _APP_COMPUTE_BUILD_TIMEOUT=${_APP_COMPUTE_BUILD_TIMEOUT:-900}
- _APP_COMPUTE_CPUS=${_APP_COMPUTE_CPUS:-0}
- _APP_COMPUTE_MEMORY=${_APP_COMPUTE_MEMORY:-0}
- _APP_FUNCTIONS_RUNTIMES=${_APP_FUNCTIONS_RUNTIMES:-node-20.0,php-8.2,python-3.11,ruby-3.2}
- _APP_SITES_RUNTIMES=${_APP_SITES_RUNTIMES}
- _APP_DOMAIN_SITES=${_APP_DOMAIN_SITES:-appwrite.network}
- _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_MAINTENANCE_INTERVAL=${_APP_MAINTENANCE_INTERVAL:-86400}
- _APP_MAINTENANCE_DELAY=${_APP_MAINTENANCE_DELAY}
- _APP_MAINTENANCE_START_TIME=${_APP_MAINTENANCE_START_TIME}
- _APP_MAINTENANCE_RETENTION_EXECUTION=${_APP_MAINTENANCE_RETENTION_EXECUTION:-1209600}
- _APP_MAINTENANCE_RETENTION_CACHE=${_APP_MAINTENANCE_RETENTION_CACHE:-2592000}
- _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400}
- _APP_MAINTENANCE_RETENTION_AUDIT=${_APP_MAINTENANCE_RETENTION_AUDIT:-1209600}
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=${_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE}
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY=${_APP_MAINTENANCE_RETENTION_USAGE_HOURLY:-8640000}
- _APP_MAINTENANCE_RETENTION_SCHEDULES=${_APP_MAINTENANCE_RETENTION_SCHEDULES:-86400}
- _APP_SMS_PROVIDER=${_APP_SMS_PROVIDER}
@@ -112,13 +125,13 @@ services:
- _APP_ASSISTANT_OPENAI_API_KEY=${_APP_ASSISTANT_OPENAI_API_KEY}
appwrite-console:
image: appwrite/console:5.0.12
image: appwrite/console:6.0.13
container_name: appwrite-console
environment:
- SERVICE_FQDN_APPWRITE=/console
appwrite-realtime:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: realtime
container_name: appwrite-realtime
depends_on:
@@ -129,6 +142,7 @@ services:
- _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPTIONS_ABUSE=${_APP_OPTIONS_ABUSE:-enabled}
- _APP_OPTIONS_ROUTER_PROTECTION=${_APP_OPTIONS_ROUTER_PROTECTION:-disabled}
- _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
@@ -143,7 +157,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-audits:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-audits
container_name: appwrite-worker-audits
depends_on:
@@ -165,7 +179,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-webhooks:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-webhooks
container_name: appwrite-worker-webhooks
depends_on:
@@ -189,7 +203,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-deletes:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-deletes
container_name: appwrite-worker-deletes
depends_on:
@@ -199,6 +213,7 @@ services:
- appwrite-uploads:/storage/uploads:rw
- appwrite-cache:/storage/cache:rw
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
- appwrite-certificates:/storage/certificates:rw
environment:
@@ -215,10 +230,11 @@ services:
- _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local}
- _APP_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY:-local}
- _APP_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY}
- _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET}
- _APP_STORAGE_S3_ENDPOINT=${_APP_STORAGE_S3_ENDPOINT}
- _APP_STORAGE_DO_SPACES_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1}
@@ -240,10 +256,13 @@ services:
- _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
- _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400}
- _APP_MAINTENANCE_RETENTION_AUDIT=${_APP_MAINTENANCE_RETENTION_AUDIT:-1209600}
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=${_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE}
- _APP_MAINTENANCE_RETENTION_EXECUTION=${_APP_MAINTENANCE_RETENTION_EXECUTION:-1209600}
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=${_APP_SYSTEM_SECURITY_EMAIL_ADDRESS}
- _APP_EMAIL_CERTIFICATES=${_APP_EMAIL_CERTIFICATES}
appwrite-worker-databases:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-databases
container_name: appwrite-worker-databases
depends_on:
@@ -265,7 +284,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-builds:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-builds
container_name: appwrite-worker-builds
depends_on:
@@ -273,7 +292,9 @@ services:
- appwrite-mariadb
volumes:
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
- appwrite-uploads:/storage/uploads:rw
environment:
- _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
@@ -294,18 +315,20 @@ services:
- _APP_VCS_GITHUB_PRIVATE_KEY=${_APP_VCS_GITHUB_PRIVATE_KEY}
- _APP_VCS_GITHUB_APP_ID=${_APP_VCS_GITHUB_APP_ID}
- _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900}
- _APP_FUNCTIONS_BUILD_TIMEOUT=${_APP_FUNCTIONS_BUILD_TIMEOUT:-900}
- _APP_FUNCTIONS_CPUS=${_APP_FUNCTIONS_CPUS:-0}
- _APP_FUNCTIONS_MEMORY=${_APP_FUNCTIONS_MEMORY:-0}
- _APP_FUNCTIONS_SIZE_LIMIT=${_APP_FUNCTIONS_SIZE_LIMIT:-30000000}
- _APP_SITES_TIMEOUT=${_APP_SITES_TIMEOUT:-900}
- _APP_COMPUTE_BUILD_TIMEOUT=${_APP_COMPUTE_BUILD_TIMEOUT:-900}
- _APP_COMPUTE_CPUS=${_APP_COMPUTE_CPUS:-0}
- _APP_COMPUTE_MEMORY=${_APP_COMPUTE_MEMORY:-0}
- _APP_COMPUTE_SIZE_LIMIT=${_APP_COMPUTE_SIZE_LIMIT:-30000000}
- _APP_OPTIONS_FORCE_HTTPS=${_APP_OPTIONS_FORCE_HTTPS:-disabled}
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=${_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS:-disabled}
- _APP_OPTIONS_ROUTER_FORCE_HTTPS=${_APP_OPTIONS_ROUTER_FORCE_HTTPS:-disabled}
- _APP_DOMAIN=$SERVICE_URL_APPWRITE
- _APP_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local}
- _APP_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY:-local}
- _APP_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY}
- _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET}
- _APP_STORAGE_S3_ENDPOINT=${_APP_STORAGE_S3_ENDPOINT}
- _APP_STORAGE_DO_SPACES_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1}
@@ -322,9 +345,10 @@ services:
- _APP_STORAGE_WASABI_SECRET=${_APP_STORAGE_WASABI_SECRET}
- _APP_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION:-eu-central-1}
- _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET}
- _APP_DOMAIN_SITES=${_APP_DOMAIN_SITES}
appwrite-worker-certificates:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-certificates
container_name: appwrite-worker-certificates
depends_on:
@@ -338,10 +362,11 @@ services:
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_DOMAIN=$SERVICE_URL_APPWRITE
- _APP_DOMAIN_TARGET=$SERVICE_URL_APPWRITE
- _APP_DOMAIN_TARGET_CNAME=${_APP_DOMAIN_TARGET_CNAME}
- _APP_DOMAIN_TARGET_AAAA=${_APP_DOMAIN_TARGET_AAAA}
- _APP_DOMAIN_TARGET_A=${_APP_DOMAIN_TARGET_A}
- _APP_DOMAIN_FUNCTIONS=$SERVICE_URL_APPWRITE
- _APP_EMAIL_CERTIFICATES=${_APP_EMAIL_CERTIFICATES:-enabled}
- _APP_EMAIL_SECURITY=${_APP_EMAIL_SECURITY:-certs@appwrite.io}
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER=${_APP_REDIS_USER}
@@ -354,7 +379,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-functions:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-functions
container_name: appwrite-worker-functions
depends_on:
@@ -377,9 +402,10 @@ services:
- _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900}
- _APP_FUNCTIONS_BUILD_TIMEOUT=${_APP_FUNCTIONS_BUILD_TIMEOUT:-900}
- _APP_FUNCTIONS_CPUS=${_APP_FUNCTIONS_CPUS:-0}
- _APP_FUNCTIONS_MEMORY=${_APP_FUNCTIONS_MEMORY:-0}
- _APP_SITES_TIMEOUT=${_APP_SITES_TIMEOUT:-900}
- _APP_COMPUTE_BUILD_TIMEOUT=${_APP_COMPUTE_BUILD_TIMEOUT:-900}
- _APP_COMPUTE_CPUS=${_APP_COMPUTE_CPUS:-0}
- _APP_COMPUTE_MEMORY=${_APP_COMPUTE_MEMORY:-0}
- _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
- _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
@@ -388,7 +414,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-mails:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-mails
container_name: appwrite-worker-mails
depends_on:
@@ -415,9 +441,10 @@ services:
- _APP_SMTP_PASSWORD=${_APP_SMTP_PASSWORD}
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_DOMAIN=$SERVICE_URL_APPWRITE
- _APP_OPTIONS_FORCE_HTTPS=${_APP_OPTIONS_FORCE_HTTPS:-disabled}
appwrite-worker-messaging:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-messaging
container_name: appwrite-worker-messaging
volumes:
@@ -445,6 +472,7 @@ services:
- _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET}
- _APP_STORAGE_S3_ENDPOINT=${_APP_STORAGE_S3_ENDPOINT}
- _APP_STORAGE_DO_SPACES_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1}
@@ -463,9 +491,11 @@ services:
- _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET}
appwrite-worker-migrations:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: worker-migrations
container_name: appwrite-worker-migrations
volumes:
- appwrite-imports:/storage/imports:rw
depends_on:
- appwrite-mariadb
environment:
@@ -473,7 +503,9 @@ services:
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_DOMAIN=$SERVICE_URL_APPWRITE
- _APP_DOMAIN_TARGET=$SERVICE_URL_APPWRITE
- _APP_DOMAIN_TARGET_CNAME=${_APP_DOMAIN_TARGET_CNAME}
- _APP_DOMAIN_TARGET_AAAA=${_APP_DOMAIN_TARGET_AAAA}
- _APP_DOMAIN_TARGET_A=${_APP_DOMAIN_TARGET_A}
- _APP_EMAIL_SECURITY=${_APP_EMAIL_SECURITY:-certs@appwrite.io}
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
@@ -489,7 +521,7 @@ services:
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=${_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET}
appwrite-task-maintenance:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: maintenance
container_name: appwrite-task-maintenance
depends_on:
@@ -498,7 +530,9 @@ services:
- _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_DOMAIN=$SERVICE_URL_APPWRITE
- _APP_DOMAIN_TARGET=$SERVICE_URL_APPWRITE
- _APP_DOMAIN_TARGET_CNAME=${_APP_DOMAIN_TARGET_CNAME}
- _APP_DOMAIN_TARGET_AAAA=${_APP_DOMAIN_TARGET_AAAA}
- _APP_DOMAIN_TARGET_A=${_APP_DOMAIN_TARGET_A}
- _APP_DOMAIN_FUNCTIONS=$SERVICE_URL_APPWRITE
- _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
@@ -515,14 +549,14 @@ services:
- _APP_MAINTENANCE_RETENTION_CACHE=${_APP_MAINTENANCE_RETENTION_CACHE:-2592000}
- _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400}
- _APP_MAINTENANCE_RETENTION_AUDIT=${_APP_MAINTENANCE_RETENTION_AUDIT:-1209600}
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=${_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE}
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY=${_APP_MAINTENANCE_RETENTION_USAGE_HOURLY:-8640000}
- _APP_MAINTENANCE_RETENTION_SCHEDULES=${_APP_MAINTENANCE_RETENTION_SCHEDULES:-86400}
appwrite-worker-usage:
image: appwrite/appwrite:1.6.0
entrypoint: worker-usage
container_name: appwrite-worker-usage
restart: unless-stopped
appwrite-task-stats-resources:
image: appwrite/appwrite:1.7.4
container_name: appwrite-task-stats-resources
entrypoint: stats-resources
depends_on:
- appwrite-redis
- appwrite-mariadb
@@ -541,12 +575,37 @@ services:
- _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_USAGE_AGGREGATION_INTERVAL=${_APP_USAGE_AGGREGATION_INTERVAL:-30}
- _APP_DATABASE_SHARED_TABLES=${_APP_DATABASE_SHARED_TABLES}
- _APP_STATS_RESOURCES_INTERVAL=${_APP_STATS_RESOURCES_INTERVAL}
appwrite-worker-usage-dump:
image: appwrite/appwrite:1.6.0
entrypoint: worker-usage-dump
container_name: appwrite-worker-usage-dump
appwrite-worker-stats-resources:
image: appwrite/appwrite:1.7.4
entrypoint: worker-stats-resources
container_name: appwrite-worker-stats-resources
depends_on:
- appwrite-redis
- appwrite-mariadb
environment:
- _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_DB_HOST=${_APP_DB_HOST:-appwrite-mariadb}
- _APP_DB_PORT=${_APP_DB_PORT:-3306}
- _APP_DB_SCHEMA=${_APP_DB_SCHEMA:-appwrite}
- _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER=${_APP_REDIS_USER}
- _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_STATS_RESOURCES_INTERVAL=${_APP_STATS_RESOURCES_INTERVAL}
appwrite-worker-stats-usage:
image: appwrite/appwrite:1.7.4
entrypoint: worker-stats-usage
container_name: appwrite-worker-stats-usage
depends_on:
- appwrite-redis
- appwrite-mariadb
@@ -568,7 +627,7 @@ services:
- _APP_USAGE_AGGREGATION_INTERVAL=${_APP_USAGE_AGGREGATION_INTERVAL:-30}
appwrite-task-scheduler-functions:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: schedule-functions
container_name: appwrite-task-scheduler-functions
depends_on:
@@ -589,7 +648,7 @@ services:
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
appwrite-task-scheduler-executions:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: schedule-executions
container_name: appwrite-task-scheduler-executions
depends_on:
@@ -610,7 +669,7 @@ services:
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
appwrite-task-scheduler-messages:
image: appwrite/appwrite:1.6.0
image: appwrite/appwrite:1.7.4
entrypoint: schedule-messages
container_name: appwrite-task-scheduler-messages
depends_on:
@@ -636,33 +695,40 @@ services:
environment:
- _APP_ASSISTANT_OPENAI_API_KEY=${_APP_ASSISTANT_OPENAI_API_KEY}
appwrite-browser:
image: appwrite/browser:0.2.4
container_name: appwrite-browser
openruntimes-executor:
container_name: openruntimes-executor
hostname: appwrite-executor
stop_signal: SIGINT
image: openruntimes/executor:0.6.11
image: openruntimes/executor:0.7.14
networks:
- runtimes
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- appwrite-builds:/storage/builds:rw
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- /tmp:/tmp:rw
environment:
- OPR_EXECUTOR_INACTIVE_TRESHOLD=${_APP_FUNCTIONS_INACTIVE_THRESHOLD}
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=${_APP_FUNCTIONS_MAINTENANCE_INTERVAL}
- OPR_EXECUTOR_NETWORK=${_APP_FUNCTIONS_RUNTIMES_NETWORK:-runtimes}
- OPR_EXECUTOR_INACTIVE_TRESHOLD=${_APP_COMPUTE_INACTIVE_THRESHOLD}
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=${_APP_COMPUTE_MAINTENANCE_INTERVAL}
- OPR_EXECUTOR_NETWORK=${_APP_COMPUTE_RUNTIMES_NETWORK:-runtimes}
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=${_APP_DOCKER_HUB_USERNAME}
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=${_APP_DOCKER_HUB_PASSWORD}
- OPR_EXECUTOR_ENV=${_APP_ENV:-production}
- OPR_EXECUTOR_RUNTIMES=${_APP_FUNCTIONS_RUNTIMES}
- OPR_EXECUTOR_RUNTIMES=${_APP_FUNCTIONS_RUNTIMES},${_APP_SITES_RUNTIMES}
- OPR_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- OPR_EXECUTOR_RUNTIME_VERSIONS=v5
- OPR_EXECUTOR_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- OPR_EXECUTOR_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local}
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY:-local}
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=${_APP_STORAGE_S3_ACCESS_KEY}
- OPR_EXECUTOR_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- OPR_EXECUTOR_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION}
- OPR_EXECUTOR_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET}
- OPR_EXECUTOR_STORAGE_S3_ENDPOINT=${_APP_STORAGE_S3_ENDPOINT}
- OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION}
@@ -703,6 +769,7 @@ services:
--maxmemory-samples 5
volumes:
- appwrite-redis:/data:rw
networks:
runtimes:
name: runtimes
@@ -712,7 +779,9 @@ volumes:
appwrite-redis:
appwrite-cache:
appwrite-uploads:
appwrite-imports:
appwrite-certificates:
appwrite-functions:
appwrite-sites:
appwrite-builds:
appwrite-config:

View File

@@ -6,7 +6,7 @@
services:
authentik-server:
image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2025.4.1}
image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2025.6.3}
restart: unless-stopped
command: server
environment:
@@ -35,7 +35,7 @@ services:
redis:
condition: service_healthy
authentik-worker:
image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2025.4.1}
image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2025.6.3}
restart: unless-stopped
command: worker
environment:

View File

@@ -0,0 +1,21 @@
# documentation: https://docs.excalidraw.com/docs
# slogan: Virtual whiteboard for sketching hand-drawn like diagrams
# tags: canvas,diagrams,drawing,productivity,whiteboard
# logo: svgs/excalidraw.svg
# port: 80
services:
excalidraw:
image: excalidraw/excalidraw:latest
environment:
- SERVICE_FQDN_EXCALIDRAW_80
healthcheck:
test:
- CMD
- wget
- '--spider'
- '--quiet'
- 'http://localhost'
interval: 10s
timeout: 5s
retries: 10

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,10 @@
{
"coolify": {
"v4": {
"version": "4.0.0-beta.420.1"
"version": "4.0.0-beta.420.2"
},
"nightly": {
"version": "4.0.0-beta.421"
"version": "4.0.0-beta.420.3"
},
"helper": {
"version": "1.0.8"