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'); $server = data_get($database, 'destination.server');
$containerName = data_get($database, 'uuid'); $containerName = data_get($database, 'uuid');
$proxyContainerName = "{$database->uuid}-proxy"; $proxyContainerName = "{$database->uuid}-proxy";
$isSSLEnabled = $database->enable_ssl ?? false;
if ($database->getMorphClass() === \App\Models\ServiceDatabase::class) { if ($database->getMorphClass() === \App\Models\ServiceDatabase::class) {
$databaseType = $database->databaseType(); $databaseType = $database->databaseType();
$network = $database->service->uuid; $network = $database->service->uuid;
@@ -42,6 +44,12 @@ class StartDatabaseProxy
'standalone-mongodb' => 27017, 'standalone-mongodb' => 27017,
default => throw new \Exception("Unsupported database type: $databaseType"), 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); $configuration_dir = database_proxy_dir($database->uuid);
if (isDev()) { if (isDev()) {

View File

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

View File

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

View File

@@ -31,7 +31,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
public function middleware(): array 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) {} 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 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 public function backoff(): int

View File

@@ -23,7 +23,7 @@ class RestartProxyJob implements ShouldBeEncrypted, ShouldQueue
public function middleware(): array 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) {} public function __construct(public Server $server) {}

View File

@@ -28,7 +28,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
public function middleware(): array 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) {} public function __construct(public Server $server) {}

View File

@@ -19,11 +19,11 @@ class ServerPatchCheckJob implements ShouldBeEncrypted, ShouldQueue
public $tries = 3; public $tries = 3;
public $timeout = 600; // 10 minutes timeout public $timeout = 600;
public function middleware(): array 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) {} public function __construct(public Server $server) {}

View File

@@ -56,7 +56,6 @@ class CloneMe extends Component
$this->project_id = $this->project->id; $this->project_id = $this->project->id;
$this->servers = currentTeam() $this->servers = currentTeam()
->servers() ->servers()
->with('destinations')
->get() ->get()
->reject(fn ($server) => $server->isBuildServer()); ->reject(fn ($server) => $server->isBuildServer());
$this->newName = str($this->project->name.'-clone-'.(string) new Cuid2)->slug(); $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) { $this->service->databases->each(function ($database) {
$database->refresh(); $database->refresh();
}); });
if (is_null($this->service->config_hash) || $this->service->isConfigurationChanged()) { if (is_null($this->service->config_hash)) {
$this->service->isConfigurationChanged(true); $this->service->isConfigurationChanged(true);
} }
$this->dispatch('configurationChanged'); $this->dispatch('configurationChanged');

View File

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

View File

@@ -68,11 +68,16 @@ class Terminal extends Component
// Escape the identifier for shell usage // Escape the identifier for shell usage
$escapedIdentifier = escapeshellarg($identifier); $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 { } 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 // ssh command is sent back to frontend then to websocket
// this is done because the websocket connection is not available here // 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 // 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; namespace App\Livewire\Settings;
use App\Jobs\CheckForUpdatesJob;
use App\Models\InstanceSettings; use App\Models\InstanceSettings;
use App\Models\Server; use App\Models\Server;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Livewire\Attributes\Computed; use Livewire\Attributes\Computed;
use Livewire\Attributes\Validate; use Livewire\Attributes\Validate;
use Livewire\Component; use Livewire\Component;
@@ -15,10 +12,7 @@ class Index extends Component
{ {
public InstanceSettings $settings; public InstanceSettings $settings;
protected Server $server; public Server $server;
#[Validate('boolean')]
public bool $is_auto_update_enabled;
#[Validate('nullable|string|max:255')] #[Validate('nullable|string|max:255')]
public ?string $fqdn = null; public ?string $fqdn = null;
@@ -29,48 +23,18 @@ class Index extends Component
#[Validate('required|integer|min:1025|max:65535')] #[Validate('required|integer|min:1025|max:65535')]
public int $public_port_max; public int $public_port_max;
#[Validate('nullable|string')]
public ?string $custom_dns_servers = null;
#[Validate('nullable|string|max:255')] #[Validate('nullable|string|max:255')]
public ?string $instance_name = null; public ?string $instance_name = null;
#[Validate('nullable|string')]
public ?string $allowed_ips = null;
#[Validate('nullable|string')] #[Validate('nullable|string')]
public ?string $public_ipv4 = null; public ?string $public_ipv4 = null;
#[Validate('nullable|string')] #[Validate('nullable|string')]
public ?string $public_ipv6 = null; public ?string $public_ipv6 = null;
#[Validate('string')]
public string $auto_update_frequency;
#[Validate('string|required')]
public string $update_check_frequency;
#[Validate('required|string|timezone')] #[Validate('required|string|timezone')]
public string $instance_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() public function render()
{ {
return view('livewire.settings.index'); return view('livewire.settings.index');
@@ -80,27 +44,16 @@ class Index extends Component
{ {
if (! isInstanceAdmin()) { if (! isInstanceAdmin()) {
return redirect()->route('dashboard'); return redirect()->route('dashboard');
} else { }
$this->settings = instanceSettings(); $this->settings = instanceSettings();
$this->server = Server::findOrFail(0);
$this->fqdn = $this->settings->fqdn; $this->fqdn = $this->settings->fqdn;
$this->public_port_min = $this->settings->public_port_min; $this->public_port_min = $this->settings->public_port_min;
$this->public_port_max = $this->settings->public_port_max; $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->instance_name = $this->settings->instance_name;
$this->allowed_ips = $this->settings->allowed_ips;
$this->public_ipv4 = $this->settings->public_ipv4; $this->public_ipv4 = $this->settings->public_ipv4;
$this->public_ipv6 = $this->settings->public_ipv6; $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->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] #[Computed]
@@ -115,30 +68,13 @@ class Index extends Component
public function instantSave($isSave = true) public function instantSave($isSave = true)
{ {
$this->validate(); $this->validate();
if ($this->settings->is_auto_update_enabled === true) {
$this->validate([
'auto_update_frequency' => ['required', 'string'],
]);
}
$this->settings->fqdn = $this->fqdn; $this->settings->fqdn = $this->fqdn;
$this->settings->public_port_min = $this->public_port_min; $this->settings->public_port_min = $this->public_port_min;
$this->settings->public_port_max = $this->public_port_max; $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->instance_name = $this->instance_name;
$this->settings->allowed_ips = $this->allowed_ips;
$this->settings->public_ipv4 = $this->public_ipv4; $this->settings->public_ipv4 = $this->public_ipv4;
$this->settings->public_ipv6 = $this->public_ipv6; $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->instance_timezone = $this->instance_timezone;
$this->settings->is_sponsorship_popup_enabled = $this->is_sponsorship_popup_enabled;
if ($isSave) { if ($isSave) {
$this->settings->save(); $this->settings->save();
$this->dispatch('success', 'Settings updated!'); $this->dispatch('success', 'Settings updated!');
@@ -149,7 +85,6 @@ class Index extends Component
{ {
try { try {
$error_show = false; $error_show = false;
$this->server = Server::findOrFail(0);
$this->resetErrorBag(); $this->resetErrorBag();
if (! validate_timezone($this->instance_timezone)) { if (! validate_timezone($this->instance_timezone)) {
@@ -166,46 +101,15 @@ class Index extends Component
} }
$this->validate(); $this->validate();
if ($this->is_auto_update_enabled && ! validate_cron_expression($this->auto_update_frequency)) { if ($this->settings->is_dns_validation_enabled && $this->fqdn) {
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.'); if (! validate_dns_entry($this->fqdn, $this->server)) {
if (empty($this->auto_update_frequency)) { $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.");
$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.");
$error_show = true; $error_show = true;
} }
} }
if ($this->settings->fqdn) { if ($this->fqdn) {
check_domain_usage(domain: $this->settings->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); $this->instantSave(isSave: false);
@@ -218,31 +122,4 @@ class Index extends Component
return handleError($e, $this); 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()) { if (! isInstanceAdmin()) {
return redirect()->route('dashboard'); return redirect()->route('dashboard');
} else { }
$settings = instanceSettings(); $settings = instanceSettings();
$this->server = Server::findOrFail(0); $this->server = Server::findOrFail(0);
$this->database = StandalonePostgresql::whereName('coolify-db')->first(); $this->database = StandalonePostgresql::whereName('coolify-db')->first();
@@ -72,7 +72,6 @@ class SettingsBackup extends Component
$this->settings = $settings; $this->settings = $settings;
$this->s3s = $s3s; $this->s3s = $s3s;
} }
}
public function addCoolifyDatabase() public function addCoolifyDatabase()
{ {

View File

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

View File

@@ -887,7 +887,7 @@ $schema://$host {
public function muxFilename() public function muxFilename()
{ {
return $this->uuid; return 'mux_'.$this->uuid;
} }
public function team() 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 [ return [
'coolify' => [ 'coolify' => [
'version' => '4.0.0-beta.420.1', 'version' => '4.0.0-beta.420.2',
'helper_version' => '1.0.8', 'helper_version' => '1.0.8',
'realtime_version' => '1.0.9', 'realtime_version' => '1.0.9',
'self_hosted' => env('SELF_HOSTED', true), 'self_hosted' => env('SELF_HOSTED', true),

View File

@@ -1,10 +1,10 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.420" "version": "4.0.0-beta.420.2"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.421" "version": "4.0.0-beta.420.3"
}, },
"helper": { "helper": {
"version": "1.0.8" "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 { @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 { @utility alert-success {

View File

@@ -5,19 +5,19 @@
<nav class="flex items-center gap-6 min-h-10 whitespace-nowrap"> <nav class="flex items-center gap-6 min-h-10 whitespace-nowrap">
<a class="{{ request()->routeIs('settings.index') ? 'dark:text-white' : '' }}" <a class="{{ request()->routeIs('settings.index') ? 'dark:text-white' : '' }}"
href="{{ route('settings.index') }}"> href="{{ route('settings.index') }}">
<button>Configuration</button> Configuration
</a> </a>
<a class="{{ request()->routeIs('settings.backup') ? 'dark:text-white' : '' }}" <a class="{{ request()->routeIs('settings.backup') ? 'dark:text-white' : '' }}"
href="{{ route('settings.backup') }}"> href="{{ route('settings.backup') }}">
<button>Backup</button> Backup
</a> </a>
<a class="{{ request()->routeIs('settings.email') ? 'dark:text-white' : '' }}" <a class="{{ request()->routeIs('settings.email') ? 'dark:text-white' : '' }}"
href="{{ route('settings.email') }}"> href="{{ route('settings.email') }}">
<button>Transactional Email</button> Transactional Email
</a> </a>
<a class="{{ request()->routeIs('settings.oauth') ? 'dark:text-white' : '' }}" <a class="{{ request()->routeIs('settings.oauth') ? 'dark:text-white' : '' }}"
href="{{ route('settings.oauth') }}"> href="{{ route('settings.oauth') }}">
<button>OAuth</button> OAuth
</a> </a>
<div class="flex-1"></div> <div class="flex-1"></div>
</nav> </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"> <div class="navbar-main">
<nav class="flex items-center gap-6 min-h-10"> <nav class="flex items-center gap-6 min-h-10">
<a class="{{ request()->routeIs('team.index') ? 'dark:text-white' : '' }}" href="{{ route('team.index') }}"> <a class="{{ request()->routeIs('team.index') ? 'dark:text-white' : '' }}" href="{{ route('team.index') }}">
<button>General</button> General
</a> </a>
<a class="{{ request()->routeIs('team.member.index') ? 'dark:text-white' : '' }}" <a class="{{ request()->routeIs('team.member.index') ? 'dark:text-white' : '' }}"
href="{{ route('team.member.index') }}"> href="{{ route('team.member.index') }}">
<button>Members</button> Members
</a> </a>
@if (isInstanceAdmin()) @if (isInstanceAdmin())
<a class="{{ request()->routeIs('team.admin-view') ? 'dark:text-white' : '' }}" <a class="{{ request()->routeIs('team.admin-view') ? 'dark:text-white' : '' }}"
href="{{ route('team.admin-view') }}"> href="{{ route('team.admin-view') }}">
<button>Admin View</button> Admin View
</a> </a>
@endif @endif
<div class="flex-1"></div> <div class="flex-1"></div>

View File

@@ -63,12 +63,13 @@
<section> <section>
<h3 class="pb-2">Servers</h3> <h3 class="pb-2">Servers</h3>
@if ($servers->count() > 0) @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) @foreach ($servers as $server)
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}" <a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
@class([ @class([
'gap-2 border cursor-pointer box group', '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="flex flex-col justify-center mx-6">
<div class="box-title"> <div class="box-title">

View File

@@ -11,13 +11,13 @@
@endif @endif
</div> </div>
<div class="subtitle">Network endpoints to deploy your resources.</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 ($servers as $server)
@forelse ($server->destinations() as $destination) @forelse ($server->destinations() as $destination)
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker') @if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
<a class="box group" <a class="box group"
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}"> 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-title">{{ $destination->name }}</div>
<div class="box-description">Server: {{ $destination->server->name }}</div> <div class="box-description">Server: {{ $destination->server->name }}</div>
</div> </div>

View File

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

View File

@@ -7,86 +7,101 @@
<div class="subtitle ">Quickly clone all resources to a new project or environment.</div> <div class="subtitle ">Quickly clone all resources to a new project or environment.</div>
</div> </div>
<x-forms.input required id="newName" label="New Name" /> <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> <h3 class="pt-8 ">Destination Server</h3>
<x-forms.button isHighlighted wire:click="clone('environment')" class="mt-4">Clone to a new Environment</x-forms.button> <div class="pb-2">Choose the server and network to clone the resources to.</div>
{{--
<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">
<div class="flex flex-col"> <div class="flex flex-col">
<div class="font-bold dark:text-white">{{ $application->name }}</div> <div class="flex flex-col">
<div class="description">{{ $application->description }}</div> <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>
</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 @endforeach
@foreach ($environment->databases()->sortBy('name') as $database) @foreach ($environment->databases()->sortBy('name') as $database)
<div class="bg-white cursor-default box-without-bg dark:bg-coolgray-100 group"> <tr>
<div class="flex flex-col"> <td class="px-5 py-4 text-sm whitespace-nowrap font-bold dark:text-white">
<div class="font-bold dark:text-white">{{ $database->name }}</div> {{ $database->name }}
<div class="description">{{ $database->description }}</div> </td>
</div> <td class="px-5 py-4 text-sm whitespace-nowrap dark:text-white">Database</td>
</div> <td class="px-5 py-4 text-sm dark:text-white">
{{ $database->description ?: '-' }}</td>
</tr>
@endforeach @endforeach
@foreach ($environment->services->sortBy('name') as $service) @foreach ($environment->services->sortBy('name') as $service)
<div class="bg-white cursor-default box-without-bg dark:bg-coolgray-100 group"> <tr>
<div class="flex flex-col"> <td class="px-5 py-4 text-sm whitespace-nowrap font-bold dark:text-white">
<div class="font-bold dark:text-white">{{ $service->name }}</div> {{ $service->name }}
<div class="description">{{ $service->description }}</div> </td>
</div> <td class="px-5 py-4 text-sm whitespace-nowrap dark:text-white">Service</td>
</div> <td class="px-5 py-4 text-sm dark:text-white">
{{ $service->description ?: '-' }}</td>
</tr>
@endforeach @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> </div>
</form> </form>

View File

@@ -8,7 +8,8 @@
@forelse($executions as $execution) @forelse($executions as $execution)
<div wire:key="{{ data_get($execution, 'id') }}" @class([ <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', '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-error' => data_get($execution, 'status') === 'failed',
'border-success' => data_get($execution, 'status') === 'success', 'border-success' => data_get($execution, 'status') === 'success',
])> ])>
@@ -20,16 +21,19 @@
<div class="flex items-center gap-2 mb-2"> <div class="flex items-center gap-2 mb-2">
<span @class([ <span @class([
'px-3 py-1 rounded-md text-xs font-medium tracking-wide shadow-xs', '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-blue-100/80 text-blue-700 dark:bg-blue-500/20 dark:text-blue-300 dark:shadow-blue-900/5' =>
'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', data_get($execution, 'status') === 'running',
'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-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 @php
$statusText = match (data_get($execution, 'status')) { $statusText = match (data_get($execution, 'status')) {
'success' => 'Success', 'success' => 'Success',
'running' => 'In Progress', 'running' => 'In Progress',
'failed' => 'Failed', 'failed' => 'Failed',
default => ucfirst(data_get($execution, 'status')) default => ucfirst(data_get($execution, 'status')),
}; };
@endphp @endphp
{{ $statusText }} {{ $statusText }}
@@ -38,8 +42,10 @@
<div class="text-gray-600 dark:text-gray-400 text-sm"> <div class="text-gray-600 dark:text-gray-400 text-sm">
Started: {{ formatDateInServerTimezone(data_get($execution, 'created_at'), $this->server()) }} Started: {{ formatDateInServerTimezone(data_get($execution, 'created_at'), $this->server()) }}
@if (data_get($execution, 'status') !== 'running') @if (data_get($execution, 'status') !== 'running')
<br>Ended: {{ formatDateInServerTimezone(data_get($execution, 'finished_at'), $this->server()) }} <br>Ended:
<br>Duration: {{ calculateDuration(data_get($execution, 'created_at'), data_get($execution, 'finished_at')) }} {{ 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() }} <br>Finished {{ \Carbon\Carbon::parse(data_get($execution, 'finished_at'))->diffForHumans() }}
@endif @endif
</div> </div>
@@ -60,17 +66,29 @@
</div> </div>
<span @class([ <span @class([
'px-2 py-1 rounded-sm text-xs font-medium', '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-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get(
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get($execution, 'local_storage_deleted', false), $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"> <span class="flex items-center gap-1">
@if (!data_get($execution, 'local_storage_deleted', false)) @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"> <svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"
<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> 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> </svg>
@else @else
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"
<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> 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> </svg>
@endif @endif
Local Storage Local Storage
@@ -79,17 +97,29 @@
@if ($backup->save_s3) @if ($backup->save_s3)
<span @class([ <span @class([
'px-2 py-1 rounded-sm text-xs font-medium', '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-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => !data_get(
'bg-gray-100 text-gray-600 dark:bg-gray-800/50 dark:text-gray-400' => data_get($execution, 's3_storage_deleted', false), $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"> <span class="flex items-center gap-1">
@if (!data_get($execution, 's3_storage_deleted', false)) @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"> <svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"
<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> 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> </svg>
@else @else
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"
<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> 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> </svg>
@endif @endif
S3 Storage S3 Storage
@@ -108,11 +138,10 @@
x-on:click="download_file('{{ data_get($execution, 'id') }}')">Download</x-forms.button> x-on:click="download_file('{{ data_get($execution, 'id') }}')">Download</x-forms.button>
@endif @endif
<x-modal-confirmation title="Confirm Backup Deletion?" buttonTitle="Delete" isErrorButton <x-modal-confirmation title="Confirm Backup Deletion?" buttonTitle="Delete" isErrorButton
submitAction="deleteBackup({{ data_get($execution, 'id') }})" submitAction="deleteBackup({{ data_get($execution, 'id') }})" :checkboxes="$checkboxes"
:checkboxes="$checkboxes"
:actions="['This backup will be permanently deleted from local storage.']" confirmationText="{{ data_get($execution, 'filename') }}" :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" 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>
</div> </div>
@empty @empty

View File

@@ -253,7 +253,8 @@
@endif @endif
@if ($current_step === 'destinations') @if ($current_step === 'destinations')
<h2>Select a destination</h2> <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> Standalone Docker (coolify).</div>
<div class="flex flex-col justify-center gap-4 text-left xl:flex-row xl:flex-wrap"> <div class="flex flex-col justify-center gap-4 text-left xl:flex-row xl:flex-wrap">
@if ($server->isSwarm()) @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.']" buttonTitle="Convert to Application" submitAction="convertToApplication" :actions="['The selected resource will be converted to an application.']"
confirmationText="{{ Str::headline($database->name) }}" confirmationText="{{ Str::headline($database->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Database Name below" 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 <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) }}" 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" 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>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex gap-2"> <div class="flex gap-2">

View File

@@ -21,7 +21,7 @@
]" ]"
confirmationText="{{ $fs_path }}" confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below" confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" /> shortConfirmationLabel="Filepath" />
@else @else
@if (!$fileStorage->is_binary) @if (!$fileStorage->is_binary)
<x-modal-confirmation :ignoreWire="false" title="Confirm File Conversion to Directory?" <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.']" isErrorButton submitAction="delete" :checkboxes="$fileDeletionCheckboxes" :actions="['The selected file will be permanently deleted from the container.']"
confirmationText="{{ $fs_path }}" confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below" confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" /> shortConfirmationLabel="Filepath" />
@endif @endif
</div> </div>
@if (!$fileStorage->is_directory) @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.']" buttonTitle="Convert to Database" submitAction="convertToDatabase" :actions="['The selected resource will be converted to a service database.']"
confirmationText="{{ Str::headline($application->name) }}" confirmationText="{{ Str::headline($application->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below" 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 <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) }}" 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" 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>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex 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" <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 }}" 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" confirmationLabel="Please confirm the execution of the actions by entering the Resource Name below"
shortConfirmationLabel="Resource Name" step3ButtonText="Permanently Delete" /> shortConfirmationLabel="Resource Name" />
</div> </div>

View File

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

View File

@@ -48,10 +48,10 @@
<x-forms.checkbox instantSave id="is_build_time" <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}'`" 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?" /> label="Is Build Variable?" />
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
<x-forms.checkbox instantSave id="is_literal" <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." 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?" /> label="Is Literal?" />
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
@else @else
@if ($is_shared) @if ($is_shared)
<x-forms.checkbox instantSave id="is_build_time" <x-forms.checkbox instantSave id="is_build_time"

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
<div> <div>
<x-security.navbar /> <x-security.navbar />
<div class="flex gap-2"> <div class="flex gap-2">
<h2 class="pb-4">Private Keys</h2> <h2 class="pb-4">Private Keys</h2>
<x-modal-input buttonTitle="+ Add" title="New Private Key"> <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 <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" /> submitAction="cleanupUnusedKeys" :actions="['All unused SSH keys (marked with unused) are permanently deleted.']" :confirmWithText="false" :confirmWithPassword="false" />
</div> </div>
<div class="grid gap-2 lg:grid-cols-2"> <div class="grid gap-4 lg:grid-cols-2">
@forelse ($privateKeys as $key) @forelse ($privateKeys as $key)
<a class="box group" <a class="box group"
href="{{ route('security.private-key.show', ['private_key_uuid' => data_get($key, 'uuid')]) }}"> 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"> <div class="box-title">
{{ data_get($key, 'name') }} {{ data_get($key, 'name') }}
</div> </div>
<div class="box-description"> <div class="box-description">
{{ $key->description }} {{ $key->description }}
@if (!$key->isInUse()) @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 @endif
</div> </div>

View File

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

View File

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

View File

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

View File

@@ -9,15 +9,11 @@
</x-modal-input> </x-modal-input>
</div> </div>
<div class="subtitle">All your servers are here.</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) @forelse ($servers as $server)
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}" <a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
@class([ @class([
'gap-2 border cursor-pointer box group', 'gap-2 border cursor-pointer box group',
'border-transparent' =>
$server->settings->is_reachable &&
$server->settings->is_usable &&
!$server->settings->force_disabled,
'border-red-500' => 'border-red-500' =>
!$server->settings->is_reachable || $server->settings->force_disabled, !$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 Settings | Coolify
</x-slot> </x-slot>
<x-settings.navbar /> <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"> <form wire:submit='submit' class="flex flex-col">
<div class="flex items-center gap-2 pb-2"> <div class="flex items-center gap-2">
<h2>Configuration</h2> <h2>General</h2>
<x-forms.button type="submit"> <x-forms.button type="submit">
Save Save
</x-forms.button> </x-forms.button>
</div> </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"> <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 flex-wrap items-end gap-2">
<div class="flex gap-2 md:flex-row flex-col w-full"> <div class="flex gap-2 md:flex-row flex-col w-full">
<x-forms.input id="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." 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" /> 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="{ <div class="w-full" x-data="{
open: false, open: false,
search: '{{ $settings->instance_timezone ?: '' }}', search: '{{ $settings->instance_timezone ?: '' }}',
@@ -67,95 +69,15 @@
</div> </div>
</div> </div>
<div class="flex gap-2 md:flex-row flex-col w-full"> <div class="flex gap-2 md:flex-row flex-col w-full">
<x-forms.input id="public_ipv4" type="password" label="Instance's IPv4" <x-forms.input id="public_ipv4" type="password" label="Instance's 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." 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" /> 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." 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" /> placeholder="2001:db8::1" autocomplete="new-password" />
</div> </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> </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>
{{-- <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> </form>
</div> </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> <div>
<form class="flex flex-col gap-2 pb-6" wire:submit='submit'> <form class="flex flex-col gap-2 pb-6" wire:submit='submit'>
<div class="flex items-start gap-2"> <div class="flex items-start gap-2">
<div class="pb-4"> <div class="">
<h1>Storage Details</h1> <h1>Storage Details</h1>
<div class="subtitle">{{ $storage->name }}</div> <div class="subtitle">{{ $storage->name }}</div>
<div class="flex items-center gap-2 pb-4">
<div>Current Status:</div>
@if ($storage->is_usable) @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 @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 @endif
</div> </div>
</div>
<x-forms.button type="submit"> <x-forms.button type="submit">
Save Save
</x-forms.button> </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" <x-modal-confirmation title="Confirm Storage Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $storage->id }})" :actions="[ submitAction="delete({{ $storage->id }})" :actions="[
'The selected storage location will be permanently deleted from Coolify.', '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="Access Key" id="storage.key" />
<x-forms.input required type="password" label="Secret Key" id="storage.secret" /> <x-forms.input required type="password" label="Secret Key" id="storage.secret" />
</div> </div>
<x-forms.button class="mt-4" isHighlighted wire:click="testConnection">
Validate Connection
</x-forms.button>
</form> </form>
</div> </div>

View File

@@ -9,10 +9,10 @@
</x-modal-input> </x-modal-input>
</div> </div>
<div class="subtitle">S3 storages for backups.</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) @forelse ($s3 as $storage)
<a href="/storages/{{ $storage->uuid }}" @class(['gap-2 border cursor-pointer box group border-transparent'])> <a href="/storages/{{ $storage->uuid }}" @class(['gap-2 border cursor-pointer box group'])>
<div class="flex flex-col mx-6"> <div class="flex flex-col justify-center mx-6">
<div class="box-title"> <div class="box-title">
{{ $storage->name }} {{ $storage->name }}
</div> </div>
@@ -20,7 +20,10 @@
{{ $storage->description }} {{ $storage->description }}
</div> </div>
@if (!$storage->is_usable) @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 @endif
</div> </div>
</a> </a>

View File

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

View File

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

View File

@@ -4,17 +4,19 @@
</x-slot> </x-slot>
<x-team.navbar /> <x-team.navbar />
<form class="flex flex-col gap-2 pb-6" wire:submit='submit'> <form class="flex flex-col" wire:submit='submit'>
<div class="flex items-end gap-2">
<h2>General</h2> <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"> <x-forms.button type="submit">
Save Save
</x-forms.button> </x-forms.button>
</div> </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> </form>
<div> <div>

View File

@@ -4,7 +4,9 @@
</x-slot> </x-slot>
<x-team.navbar /> <x-team.navbar />
<h2>Members</h2> <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="flex flex-col"> <div class="flex flex-col">
<div class="overflow-x-auto"> <div class="overflow-x-auto">
@@ -38,8 +40,8 @@
@else @else
<h2>Invite New Member</h2> <h2>Invite New Member</h2>
@if (isInstanceAdmin()) @if (isInstanceAdmin())
<div class="pb-4 text-xs dark:text-warning">You need to configure (as root team) <a href="/settings#smtp" <div class="pb-4 text-xs dark:text-warning">You need to configure (as root team) <a
class="underline dark:text-warning">Transactional href="/settings#smtp" class="underline dark:text-warning">Transactional
Emails</a> Emails</a>
before before
you can invite a 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> </x-modal-input>
</div> </div>
<div class="subtitle">Git sources for your applications.</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) @forelse ($sources as $source)
@if ($source->getMorphClass() === 'App\Models\GithubApp') @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')]) }}"> 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() }}" /> {{-- <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"> <div class="text-left dark:group-hover:text-white flex flex-col justify-center mx-6">
<div class="box-title">{{ $source->name }}</div> <div class="box-title">{{ $source->name }}</div>
@if (is_null($source->app_id)) @if (is_null($source->app_id))
<span class="box-description text-error! ">Configuration is not finished.</span> <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 @endif
</div> </div>
</a> </a>
@endif @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" <a class="flex gap-4 text-center hover:no-underline box group"
href="{{ route('source.gitlab.show', ['gitlab_app_uuid' => data_get($source, 'uuid')]) }}"> 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 class="text-left dark:group-hover:text-white">
<div>{{ $source->name }}</div> <div>{{ $source->name }}</div>
@if (is_null($source->app_id)) @if (is_null($source->app_id))
@@ -34,7 +38,7 @@
@endif @endif
</div> </div>
</a> </a>
@endif @endif --}}
@empty @empty
<div> <div>
<div>No sources found.</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\Resources as ResourcesShow;
use App\Livewire\Server\Security\Patches; use App\Livewire\Server\Security\Patches;
use App\Livewire\Server\Show as ServerShow; 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\Index as SettingsIndex;
use App\Livewire\Settings\Updates as SettingsUpdates;
use App\Livewire\SettingsBackup; use App\Livewire\SettingsBackup;
use App\Livewire\SettingsEmail; use App\Livewire\SettingsEmail;
use App\Livewire\SettingsOauth; 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('/subscription/new', SubscriptionIndex::class)->name('subscription.index');
Route::get('/settings', SettingsIndex::class)->name('settings.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/backup', SettingsBackup::class)->name('settings.backup');
Route::get('/settings/email', SettingsEmail::class)->name('settings.email'); Route::get('/settings/email', SettingsEmail::class)->name('settings.email');
Route::get('/settings/oauth', SettingsOauth::class)->name('settings.oauth'); Route::get('/settings/oauth', SettingsOauth::class)->name('settings.oauth');

View File

@@ -5,14 +5,17 @@
services: services:
appwrite: appwrite:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
container_name: appwrite container_name: appwrite
volumes: volumes:
- appwrite-uploads:/storage/uploads:rw - appwrite-uploads:/storage/uploads:rw
- appwrite-imports:/storage/imports:rw
- appwrite-cache:/storage/cache:rw - appwrite-cache:/storage/cache:rw
- appwrite-config:/storage/config:rw - appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw - appwrite-certificates:/storage/certificates:rw
- appwrite-functions:/storage/functions:rw - appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
depends_on: depends_on:
- appwrite-mariadb - appwrite-mariadb
- appwrite-redis - appwrite-redis
@@ -21,10 +24,12 @@ services:
- _APP_ENV=${_APP_ENV:-production} - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6} - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_LOCALE=${_APP_LOCALE:-en} - _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_ROOT=${_APP_CONSOLE_WHITELIST_ROOT:-enabled}
- _APP_CONSOLE_WHITELIST_EMAILS=${_APP_CONSOLE_WHITELIST_EMAILS} - _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_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_NAME=${_APP_SYSTEM_EMAIL_NAME:-Appwrite}
- _APP_SYSTEM_EMAIL_ADDRESS=${_APP_SYSTEM_EMAIL_ADDRESS:-team@appwrite.io} - _APP_SYSTEM_EMAIL_ADDRESS=${_APP_SYSTEM_EMAIL_ADDRESS:-team@appwrite.io}
- _APP_SYSTEM_TEAM_EMAIL=${_APP_SYSTEM_TEAM_EMAIL:-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_ABUSE=${_APP_OPTIONS_ABUSE:-enabled}
- _APP_OPTIONS_ROUTER_PROTECTION=${_APP_OPTIONS_ROUTER_PROTECTION:-disabled} - _APP_OPTIONS_ROUTER_PROTECTION=${_APP_OPTIONS_ROUTER_PROTECTION:-disabled}
- _APP_OPTIONS_FORCE_HTTPS=${_APP_OPTIONS_FORCE_HTTPS:-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_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_DOMAIN=$SERVICE_URL_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_DOMAIN_FUNCTIONS=$SERVICE_URL_APPWRITE
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis} - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379} - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
@@ -63,6 +70,7 @@ services:
- _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET} - _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1} - _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET} - _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_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET} - _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1} - _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_SECRET=${_APP_STORAGE_WASABI_SECRET}
- _APP_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION:-eu-central-1} - _APP_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION:-eu-central-1}
- _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET} - _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_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900}
- _APP_FUNCTIONS_BUILD_TIMEOUT=${_APP_FUNCTIONS_BUILD_TIMEOUT:-900} - _APP_SITES_TIMEOUT=${_APP_SITES_TIMEOUT:-900}
- _APP_FUNCTIONS_CPUS=${_APP_FUNCTIONS_CPUS:-0} - _APP_COMPUTE_BUILD_TIMEOUT=${_APP_COMPUTE_BUILD_TIMEOUT:-900}
- _APP_FUNCTIONS_MEMORY=${_APP_FUNCTIONS_MEMORY:-0} - _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_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_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1} - _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_MAINTENANCE_INTERVAL=${_APP_MAINTENANCE_INTERVAL:-86400} - _APP_MAINTENANCE_INTERVAL=${_APP_MAINTENANCE_INTERVAL:-86400}
- _APP_MAINTENANCE_DELAY=${_APP_MAINTENANCE_DELAY} - _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_EXECUTION=${_APP_MAINTENANCE_RETENTION_EXECUTION:-1209600}
- _APP_MAINTENANCE_RETENTION_CACHE=${_APP_MAINTENANCE_RETENTION_CACHE:-2592000} - _APP_MAINTENANCE_RETENTION_CACHE=${_APP_MAINTENANCE_RETENTION_CACHE:-2592000}
- _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400} - _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400}
- _APP_MAINTENANCE_RETENTION_AUDIT=${_APP_MAINTENANCE_RETENTION_AUDIT:-1209600} - _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_USAGE_HOURLY=${_APP_MAINTENANCE_RETENTION_USAGE_HOURLY:-8640000}
- _APP_MAINTENANCE_RETENTION_SCHEDULES=${_APP_MAINTENANCE_RETENTION_SCHEDULES:-86400} - _APP_MAINTENANCE_RETENTION_SCHEDULES=${_APP_MAINTENANCE_RETENTION_SCHEDULES:-86400}
- _APP_SMS_PROVIDER=${_APP_SMS_PROVIDER} - _APP_SMS_PROVIDER=${_APP_SMS_PROVIDER}
@@ -112,13 +125,13 @@ services:
- _APP_ASSISTANT_OPENAI_API_KEY=${_APP_ASSISTANT_OPENAI_API_KEY} - _APP_ASSISTANT_OPENAI_API_KEY=${_APP_ASSISTANT_OPENAI_API_KEY}
appwrite-console: appwrite-console:
image: appwrite/console:5.0.12 image: appwrite/console:6.0.13
container_name: appwrite-console container_name: appwrite-console
environment: environment:
- SERVICE_FQDN_APPWRITE=/console - SERVICE_FQDN_APPWRITE=/console
appwrite-realtime: appwrite-realtime:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: realtime entrypoint: realtime
container_name: appwrite-realtime container_name: appwrite-realtime
depends_on: depends_on:
@@ -129,6 +142,7 @@ services:
- _APP_ENV=${_APP_ENV:-production} - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6} - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPTIONS_ABUSE=${_APP_OPTIONS_ABUSE:-enabled} - _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_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis} - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379} - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
@@ -143,7 +157,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-audits: appwrite-worker-audits:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-audits entrypoint: worker-audits
container_name: appwrite-worker-audits container_name: appwrite-worker-audits
depends_on: depends_on:
@@ -165,7 +179,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-webhooks: appwrite-worker-webhooks:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-webhooks entrypoint: worker-webhooks
container_name: appwrite-worker-webhooks container_name: appwrite-worker-webhooks
depends_on: depends_on:
@@ -189,7 +203,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-deletes: appwrite-worker-deletes:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-deletes entrypoint: worker-deletes
container_name: appwrite-worker-deletes container_name: appwrite-worker-deletes
depends_on: depends_on:
@@ -199,6 +213,7 @@ services:
- appwrite-uploads:/storage/uploads:rw - appwrite-uploads:/storage/uploads:rw
- appwrite-cache:/storage/cache:rw - appwrite-cache:/storage/cache:rw
- appwrite-functions:/storage/functions:rw - appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw - appwrite-builds:/storage/builds:rw
- appwrite-certificates:/storage/certificates:rw - appwrite-certificates:/storage/certificates:rw
environment: environment:
@@ -215,10 +230,11 @@ services:
- _APP_DB_USER=$SERVICE_USER_MARIADB - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local} - _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_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1} - _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET} - _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_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET} - _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1} - _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_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
- _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400} - _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400}
- _APP_MAINTENANCE_RETENTION_AUDIT=${_APP_MAINTENANCE_RETENTION_AUDIT:-1209600} - _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_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: appwrite-worker-databases:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-databases entrypoint: worker-databases
container_name: appwrite-worker-databases container_name: appwrite-worker-databases
depends_on: depends_on:
@@ -265,7 +284,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-builds: appwrite-worker-builds:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-builds entrypoint: worker-builds
container_name: appwrite-worker-builds container_name: appwrite-worker-builds
depends_on: depends_on:
@@ -273,7 +292,9 @@ services:
- appwrite-mariadb - appwrite-mariadb
volumes: volumes:
- appwrite-functions:/storage/functions:rw - appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw - appwrite-builds:/storage/builds:rw
- appwrite-uploads:/storage/uploads:rw
environment: environment:
- _APP_ENV=${_APP_ENV:-production} - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6} - _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_PRIVATE_KEY=${_APP_VCS_GITHUB_PRIVATE_KEY}
- _APP_VCS_GITHUB_APP_ID=${_APP_VCS_GITHUB_APP_ID} - _APP_VCS_GITHUB_APP_ID=${_APP_VCS_GITHUB_APP_ID}
- _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900} - _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900}
- _APP_FUNCTIONS_BUILD_TIMEOUT=${_APP_FUNCTIONS_BUILD_TIMEOUT:-900} - _APP_SITES_TIMEOUT=${_APP_SITES_TIMEOUT:-900}
- _APP_FUNCTIONS_CPUS=${_APP_FUNCTIONS_CPUS:-0} - _APP_COMPUTE_BUILD_TIMEOUT=${_APP_COMPUTE_BUILD_TIMEOUT:-900}
- _APP_FUNCTIONS_MEMORY=${_APP_FUNCTIONS_MEMORY:-0} - _APP_COMPUTE_CPUS=${_APP_COMPUTE_CPUS:-0}
- _APP_FUNCTIONS_SIZE_LIMIT=${_APP_FUNCTIONS_SIZE_LIMIT:-30000000} - _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_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_DOMAIN=$SERVICE_URL_APPWRITE
- _APP_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local} - _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_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1} - _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET} - _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_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET} - _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1} - _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_SECRET=${_APP_STORAGE_WASABI_SECRET}
- _APP_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION:-eu-central-1} - _APP_STORAGE_WASABI_REGION=${_APP_STORAGE_WASABI_REGION:-eu-central-1}
- _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET} - _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET}
- _APP_DOMAIN_SITES=${_APP_DOMAIN_SITES}
appwrite-worker-certificates: appwrite-worker-certificates:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-certificates entrypoint: worker-certificates
container_name: appwrite-worker-certificates container_name: appwrite-worker-certificates
depends_on: depends_on:
@@ -338,10 +362,11 @@ services:
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6} - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE - _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_DOMAIN=$SERVICE_URL_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_DOMAIN_FUNCTIONS=$SERVICE_URL_APPWRITE
- _APP_EMAIL_CERTIFICATES=${_APP_EMAIL_CERTIFICATES:-enabled} - _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_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379} - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
- _APP_REDIS_USER=${_APP_REDIS_USER} - _APP_REDIS_USER=${_APP_REDIS_USER}
@@ -354,7 +379,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-functions: appwrite-worker-functions:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-functions entrypoint: worker-functions
container_name: appwrite-worker-functions container_name: appwrite-worker-functions
depends_on: depends_on:
@@ -377,9 +402,10 @@ services:
- _APP_DB_USER=$SERVICE_USER_MARIADB - _APP_DB_USER=$SERVICE_USER_MARIADB
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
- _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900} - _APP_FUNCTIONS_TIMEOUT=${_APP_FUNCTIONS_TIMEOUT:-900}
- _APP_FUNCTIONS_BUILD_TIMEOUT=${_APP_FUNCTIONS_BUILD_TIMEOUT:-900} - _APP_SITES_TIMEOUT=${_APP_SITES_TIMEOUT:-900}
- _APP_FUNCTIONS_CPUS=${_APP_FUNCTIONS_CPUS:-0} - _APP_COMPUTE_BUILD_TIMEOUT=${_APP_COMPUTE_BUILD_TIMEOUT:-900}
- _APP_FUNCTIONS_MEMORY=${_APP_FUNCTIONS_MEMORY:-0} - _APP_COMPUTE_CPUS=${_APP_COMPUTE_CPUS:-0}
- _APP_COMPUTE_MEMORY=${_APP_COMPUTE_MEMORY:-0}
- _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE - _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1} - _APP_EXECUTOR_HOST=${_APP_EXECUTOR_HOST:-http://appwrite-executor/v1}
- _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled} - _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
@@ -388,7 +414,7 @@ services:
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
appwrite-worker-mails: appwrite-worker-mails:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-mails entrypoint: worker-mails
container_name: appwrite-worker-mails container_name: appwrite-worker-mails
depends_on: depends_on:
@@ -415,9 +441,10 @@ services:
- _APP_SMTP_PASSWORD=${_APP_SMTP_PASSWORD} - _APP_SMTP_PASSWORD=${_APP_SMTP_PASSWORD}
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- _APP_DOMAIN=$SERVICE_URL_APPWRITE - _APP_DOMAIN=$SERVICE_URL_APPWRITE
- _APP_OPTIONS_FORCE_HTTPS=${_APP_OPTIONS_FORCE_HTTPS:-disabled}
appwrite-worker-messaging: appwrite-worker-messaging:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-messaging entrypoint: worker-messaging
container_name: appwrite-worker-messaging container_name: appwrite-worker-messaging
volumes: volumes:
@@ -445,6 +472,7 @@ services:
- _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET} - _APP_STORAGE_S3_SECRET=${_APP_STORAGE_S3_SECRET}
- _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1} - _APP_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION:-us-east-1}
- _APP_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET} - _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_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET} - _APP_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- _APP_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION:-us-east-1} - _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} - _APP_STORAGE_WASABI_BUCKET=${_APP_STORAGE_WASABI_BUCKET}
appwrite-worker-migrations: appwrite-worker-migrations:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-migrations entrypoint: worker-migrations
container_name: appwrite-worker-migrations container_name: appwrite-worker-migrations
volumes:
- appwrite-imports:/storage/imports:rw
depends_on: depends_on:
- appwrite-mariadb - appwrite-mariadb
environment: environment:
@@ -473,7 +503,9 @@ services:
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6} - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE - _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_DOMAIN=$SERVICE_URL_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_EMAIL_SECURITY=${_APP_EMAIL_SECURITY:-certs@appwrite.io}
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis} - _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis}
- _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379} - _APP_REDIS_PORT=${_APP_REDIS_PORT:-6379}
@@ -489,7 +521,7 @@ services:
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=${_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET} - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=${_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET}
appwrite-task-maintenance: appwrite-task-maintenance:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: maintenance entrypoint: maintenance
container_name: appwrite-task-maintenance container_name: appwrite-task-maintenance
depends_on: depends_on:
@@ -498,7 +530,9 @@ services:
- _APP_ENV=${_APP_ENV:-production} - _APP_ENV=${_APP_ENV:-production}
- _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6} - _APP_WORKER_PER_CORE=${_APP_WORKER_PER_CORE:-6}
- _APP_DOMAIN=$SERVICE_URL_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_DOMAIN_FUNCTIONS=$SERVICE_URL_APPWRITE
- _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE - _APP_OPENSSL_KEY_V1=$SERVICE_PASSWORD_64_APPWRITE
- _APP_REDIS_HOST=${_APP_REDIS_HOST:-appwrite-redis} - _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_CACHE=${_APP_MAINTENANCE_RETENTION_CACHE:-2592000}
- _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400} - _APP_MAINTENANCE_RETENTION_ABUSE=${_APP_MAINTENANCE_RETENTION_ABUSE:-86400}
- _APP_MAINTENANCE_RETENTION_AUDIT=${_APP_MAINTENANCE_RETENTION_AUDIT:-1209600} - _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_USAGE_HOURLY=${_APP_MAINTENANCE_RETENTION_USAGE_HOURLY:-8640000}
- _APP_MAINTENANCE_RETENTION_SCHEDULES=${_APP_MAINTENANCE_RETENTION_SCHEDULES:-86400} - _APP_MAINTENANCE_RETENTION_SCHEDULES=${_APP_MAINTENANCE_RETENTION_SCHEDULES:-86400}
appwrite-worker-usage: appwrite-task-stats-resources:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-usage container_name: appwrite-task-stats-resources
container_name: appwrite-worker-usage entrypoint: stats-resources
restart: unless-stopped
depends_on: depends_on:
- appwrite-redis - appwrite-redis
- appwrite-mariadb - appwrite-mariadb
@@ -541,12 +575,37 @@ services:
- _APP_REDIS_PASS=${_APP_REDIS_PASS} - _APP_REDIS_PASS=${_APP_REDIS_PASS}
- _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled} - _APP_USAGE_STATS=${_APP_USAGE_STATS:-enabled}
- _APP_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - _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: appwrite-worker-stats-resources:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: worker-usage-dump entrypoint: worker-stats-resources
container_name: appwrite-worker-usage-dump 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: depends_on:
- appwrite-redis - appwrite-redis
- appwrite-mariadb - appwrite-mariadb
@@ -568,7 +627,7 @@ services:
- _APP_USAGE_AGGREGATION_INTERVAL=${_APP_USAGE_AGGREGATION_INTERVAL:-30} - _APP_USAGE_AGGREGATION_INTERVAL=${_APP_USAGE_AGGREGATION_INTERVAL:-30}
appwrite-task-scheduler-functions: appwrite-task-scheduler-functions:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: schedule-functions entrypoint: schedule-functions
container_name: appwrite-task-scheduler-functions container_name: appwrite-task-scheduler-functions
depends_on: depends_on:
@@ -589,7 +648,7 @@ services:
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
appwrite-task-scheduler-executions: appwrite-task-scheduler-executions:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: schedule-executions entrypoint: schedule-executions
container_name: appwrite-task-scheduler-executions container_name: appwrite-task-scheduler-executions
depends_on: depends_on:
@@ -610,7 +669,7 @@ services:
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB - _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
appwrite-task-scheduler-messages: appwrite-task-scheduler-messages:
image: appwrite/appwrite:1.6.0 image: appwrite/appwrite:1.7.4
entrypoint: schedule-messages entrypoint: schedule-messages
container_name: appwrite-task-scheduler-messages container_name: appwrite-task-scheduler-messages
depends_on: depends_on:
@@ -636,33 +695,40 @@ services:
environment: environment:
- _APP_ASSISTANT_OPENAI_API_KEY=${_APP_ASSISTANT_OPENAI_API_KEY} - _APP_ASSISTANT_OPENAI_API_KEY=${_APP_ASSISTANT_OPENAI_API_KEY}
appwrite-browser:
image: appwrite/browser:0.2.4
container_name: appwrite-browser
openruntimes-executor: openruntimes-executor:
container_name: openruntimes-executor container_name: openruntimes-executor
hostname: appwrite-executor hostname: appwrite-executor
stop_signal: SIGINT stop_signal: SIGINT
image: openruntimes/executor:0.6.11 image: openruntimes/executor:0.7.14
networks: networks:
- runtimes - runtimes
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- appwrite-builds:/storage/builds:rw - appwrite-builds:/storage/builds:rw
- appwrite-functions:/storage/functions:rw - appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- /tmp:/tmp:rw - /tmp:/tmp:rw
environment: environment:
- OPR_EXECUTOR_INACTIVE_TRESHOLD=${_APP_FUNCTIONS_INACTIVE_THRESHOLD} - OPR_EXECUTOR_INACTIVE_TRESHOLD=${_APP_COMPUTE_INACTIVE_THRESHOLD}
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=${_APP_FUNCTIONS_MAINTENANCE_INTERVAL} - OPR_EXECUTOR_MAINTENANCE_INTERVAL=${_APP_COMPUTE_MAINTENANCE_INTERVAL}
- OPR_EXECUTOR_NETWORK=${_APP_FUNCTIONS_RUNTIMES_NETWORK:-runtimes} - OPR_EXECUTOR_NETWORK=${_APP_COMPUTE_RUNTIMES_NETWORK:-runtimes}
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=${_APP_DOCKER_HUB_USERNAME} - OPR_EXECUTOR_DOCKER_HUB_USERNAME=${_APP_DOCKER_HUB_USERNAME}
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=${_APP_DOCKER_HUB_PASSWORD} - OPR_EXECUTOR_DOCKER_HUB_PASSWORD=${_APP_DOCKER_HUB_PASSWORD}
- OPR_EXECUTOR_ENV=${_APP_ENV:-production} - 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_SECRET=$SERVICE_PASSWORD_64_APPWRITE
- OPR_EXECUTOR_RUNTIME_VERSIONS=v5
- OPR_EXECUTOR_LOGGING_CONFIG=${_APP_LOGGING_CONFIG} - OPR_EXECUTOR_LOGGING_CONFIG=${_APP_LOGGING_CONFIG}
- OPR_EXECUTOR_STORAGE_DEVICE=${_APP_STORAGE_DEVICE:-local} - 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_SECRET=${_APP_STORAGE_S3_SECRET}
- OPR_EXECUTOR_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION} - OPR_EXECUTOR_STORAGE_S3_REGION=${_APP_STORAGE_S3_REGION}
- OPR_EXECUTOR_STORAGE_S3_BUCKET=${_APP_STORAGE_S3_BUCKET} - 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_ACCESS_KEY=${_APP_STORAGE_DO_SPACES_ACCESS_KEY}
- OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET} - OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=${_APP_STORAGE_DO_SPACES_SECRET}
- OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION} - OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=${_APP_STORAGE_DO_SPACES_REGION}
@@ -703,6 +769,7 @@ services:
--maxmemory-samples 5 --maxmemory-samples 5
volumes: volumes:
- appwrite-redis:/data:rw - appwrite-redis:/data:rw
networks: networks:
runtimes: runtimes:
name: runtimes name: runtimes
@@ -712,7 +779,9 @@ volumes:
appwrite-redis: appwrite-redis:
appwrite-cache: appwrite-cache:
appwrite-uploads: appwrite-uploads:
appwrite-imports:
appwrite-certificates: appwrite-certificates:
appwrite-functions: appwrite-functions:
appwrite-sites:
appwrite-builds: appwrite-builds:
appwrite-config: appwrite-config:

View File

@@ -6,7 +6,7 @@
services: services:
authentik-server: 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 restart: unless-stopped
command: server command: server
environment: environment:
@@ -35,7 +35,7 @@ services:
redis: redis:
condition: service_healthy condition: service_healthy
authentik-worker: 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 restart: unless-stopped
command: worker command: worker
environment: 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": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.420.1" "version": "4.0.0-beta.420.2"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.421" "version": "4.0.0-beta.420.3"
}, },
"helper": { "helper": {
"version": "1.0.8" "version": "1.0.8"