@@ -25,7 +25,7 @@ class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
if (isDev() || isCloud()) {
|
||||
if (isDev()) {
|
||||
return;
|
||||
}
|
||||
$response = Http::retry(3, 1000)->get(config('constants.services.official'));
|
||||
|
||||
@@ -442,6 +442,7 @@ class General extends Component
|
||||
{
|
||||
$config = GenerateConfig::run($this->application, true);
|
||||
$fileName = str($this->application->name)->slug()->append('_config.json');
|
||||
dd($config);
|
||||
|
||||
return response()->streamDownload(function () use ($config) {
|
||||
echo $config;
|
||||
|
||||
@@ -27,6 +27,8 @@ class ExecuteContainerCommand extends Component
|
||||
|
||||
public Collection $servers;
|
||||
|
||||
public bool $hasShell = true;
|
||||
|
||||
protected $rules = [
|
||||
'server' => 'required',
|
||||
'container' => 'required',
|
||||
@@ -141,6 +143,16 @@ class ExecuteContainerCommand extends Component
|
||||
}
|
||||
}
|
||||
|
||||
private function checkShellAvailability(Server $server, string $container): bool
|
||||
{
|
||||
$escapedContainer = escapeshellarg($container);
|
||||
$result = instant_remote_process([
|
||||
"docker exec {$escapedContainer} which bash || docker exec {$escapedContainer} which sh",
|
||||
], $server, false);
|
||||
|
||||
return ! empty($result);
|
||||
}
|
||||
|
||||
#[On('connectToServer')]
|
||||
public function connectToServer()
|
||||
{
|
||||
@@ -148,6 +160,7 @@ class ExecuteContainerCommand extends Component
|
||||
if ($this->server->isForceDisabled()) {
|
||||
throw new \RuntimeException('Server is disabled.');
|
||||
}
|
||||
$this->hasShell = true;
|
||||
$this->dispatch(
|
||||
'send-terminal-command',
|
||||
false,
|
||||
@@ -201,6 +214,11 @@ class ExecuteContainerCommand extends Component
|
||||
throw new \RuntimeException('Server ownership verification failed.');
|
||||
}
|
||||
|
||||
$this->hasShell = $this->checkShellAvailability($server, data_get($container, 'container.Names'));
|
||||
if (! $this->hasShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dispatch(
|
||||
'send-terminal-command',
|
||||
true,
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Services\ConfigurationGenerator;
|
||||
use App\Traits\HasConfiguration;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
@@ -105,7 +107,7 @@ use Visus\Cuid2\Cuid2;
|
||||
|
||||
class Application extends BaseModel
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
use HasConfiguration, HasFactory, SoftDeletes;
|
||||
|
||||
private static $parserVersion = '4';
|
||||
|
||||
@@ -1640,35 +1642,28 @@ class Application extends BaseModel
|
||||
}
|
||||
}
|
||||
|
||||
public function getLimits(): array
|
||||
{
|
||||
return [
|
||||
'limits_memory' => $this->limits_memory,
|
||||
'limits_memory_swap' => $this->limits_memory_swap,
|
||||
'limits_memory_swappiness' => $this->limits_memory_swappiness,
|
||||
'limits_memory_reservation' => $this->limits_memory_reservation,
|
||||
'limits_cpus' => $this->limits_cpus,
|
||||
'limits_cpuset' => $this->limits_cpuset,
|
||||
'limits_cpu_shares' => $this->limits_cpu_shares,
|
||||
];
|
||||
}
|
||||
|
||||
public function generateConfig($is_json = false)
|
||||
{
|
||||
$config = collect([]);
|
||||
if ($this->build_pack = 'nixpacks') {
|
||||
$config = collect([
|
||||
'build_pack' => 'nixpacks',
|
||||
'docker_registry_image_name' => $this->docker_registry_image_name,
|
||||
'docker_registry_image_tag' => $this->docker_registry_image_tag,
|
||||
'install_command' => $this->install_command,
|
||||
'build_command' => $this->build_command,
|
||||
'start_command' => $this->start_command,
|
||||
'base_directory' => $this->base_directory,
|
||||
'publish_directory' => $this->publish_directory,
|
||||
'custom_docker_run_options' => $this->custom_docker_run_options,
|
||||
'ports_exposes' => $this->ports_exposes,
|
||||
'ports_mappings' => $this->ports_mapping,
|
||||
'settings' => collect([
|
||||
'is_static' => $this->settings->is_static,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
$config = $config->filter(function ($value) {
|
||||
return str($value)->isNotEmpty();
|
||||
});
|
||||
$generator = new ConfigurationGenerator($this);
|
||||
|
||||
if ($is_json) {
|
||||
return json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
return $generator->toJson();
|
||||
}
|
||||
|
||||
return $config;
|
||||
return $generator->toArray();
|
||||
}
|
||||
|
||||
public function setConfig($config)
|
||||
|
||||
@@ -53,6 +53,7 @@ class EmailChannel
|
||||
if (! $type) {
|
||||
throw new Exception('No email settings found.');
|
||||
}
|
||||
config()->set('mail.default', $type);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
194
app/Services/ConfigurationGenerator.php
Normal file
194
app/Services/ConfigurationGenerator.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Application;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ConfigurationGenerator
|
||||
{
|
||||
protected array $config = [];
|
||||
|
||||
public function __construct(protected Application $resource)
|
||||
{
|
||||
$this->generateConfig();
|
||||
}
|
||||
|
||||
protected function generateConfig(): void
|
||||
{
|
||||
if ($this->resource instanceof Application) {
|
||||
$this->config = [
|
||||
'id' => $this->resource->id,
|
||||
'name' => $this->resource->name,
|
||||
'uuid' => $this->resource->uuid,
|
||||
'description' => $this->resource->description,
|
||||
'coolify_details' => [
|
||||
'project_uuid' => $this->resource->project()->uuid,
|
||||
'environment_uuid' => $this->resource->environment->uuid,
|
||||
|
||||
'destination_type' => $this->resource->destination_type,
|
||||
'destination_id' => $this->resource->destination_id,
|
||||
'source_type' => $this->resource->source_type,
|
||||
'source_id' => $this->resource->source_id,
|
||||
'private_key_id' => $this->resource->private_key_id,
|
||||
],
|
||||
|
||||
'post_deployment_command' => $this->resource->post_deployment_command,
|
||||
'post_deployment_command_container' => $this->resource->post_deployment_command_container,
|
||||
'pre_deployment_command' => $this->resource->pre_deployment_command,
|
||||
'pre_deployment_command_container' => $this->resource->pre_deployment_command_container,
|
||||
'build' => [
|
||||
'type' => $this->resource->build_pack,
|
||||
'static_image' => $this->resource->static_image,
|
||||
'base_directory' => $this->resource->base_directory,
|
||||
'publish_directory' => $this->resource->publish_directory,
|
||||
'dockerfile' => $this->resource->dockerfile,
|
||||
'dockerfile_location' => $this->resource->dockerfile_location,
|
||||
'dockerfile_target_build' => $this->resource->dockerfile_target_build,
|
||||
'custom_docker_run_options' => $this->resource->custom_docker_options,
|
||||
'compose_parsing_version' => $this->resource->compose_parsing_version,
|
||||
'docker_compose' => $this->resource->docker_compose,
|
||||
'docker_compose_location' => $this->resource->docker_compose_location,
|
||||
'docker_compose_raw' => $this->resource->docker_compose_raw,
|
||||
'docker_compose_domains' => $this->resource->docker_compose_domains,
|
||||
'docker_compose_custom_start_command' => $this->resource->docker_compose_custom_start_command,
|
||||
'docker_compose_custom_build_command' => $this->resource->docker_compose_custom_build_command,
|
||||
'install_command' => $this->resource->install_command,
|
||||
'build_command' => $this->resource->build_command,
|
||||
'start_command' => $this->resource->start_command,
|
||||
'watch_paths' => $this->resource->watch_paths,
|
||||
],
|
||||
'source' => [
|
||||
'git_repository' => $this->resource->git_repository,
|
||||
'git_branch' => $this->resource->git_branch,
|
||||
'git_commit_sha' => $this->resource->git_commit_sha,
|
||||
'repository_project_id' => $this->resource->repository_project_id,
|
||||
],
|
||||
'docker_registry_image' => $this->getDockerRegistryImage(),
|
||||
'domains' => [
|
||||
'fqdn' => $this->resource->fqdn,
|
||||
'ports_exposes' => $this->resource->ports_exposes,
|
||||
'ports_mappings' => $this->resource->ports_mappings,
|
||||
'redirect' => $this->resource->redirect,
|
||||
'custom_nginx_configuration' => $this->resource->custom_nginx_configuration,
|
||||
],
|
||||
'environment_variables' => [
|
||||
'production' => $this->getEnvironmentVariables(),
|
||||
'preview' => $this->getPreviewEnvironmentVariables(),
|
||||
],
|
||||
'settings' => $this->getApplicationSettings(),
|
||||
'preview' => $this->getPreview(),
|
||||
'limits' => $this->resource->getLimits(),
|
||||
'health_check' => [
|
||||
'health_check_path' => $this->resource->health_check_path,
|
||||
'health_check_port' => $this->resource->health_check_port,
|
||||
'health_check_host' => $this->resource->health_check_host,
|
||||
'health_check_method' => $this->resource->health_check_method,
|
||||
'health_check_return_code' => $this->resource->health_check_return_code,
|
||||
'health_check_scheme' => $this->resource->health_check_scheme,
|
||||
'health_check_response_text' => $this->resource->health_check_response_text,
|
||||
'health_check_interval' => $this->resource->health_check_interval,
|
||||
'health_check_timeout' => $this->resource->health_check_timeout,
|
||||
'health_check_retries' => $this->resource->health_check_retries,
|
||||
'health_check_start_period' => $this->resource->health_check_start_period,
|
||||
'health_check_enabled' => $this->resource->health_check_enabled,
|
||||
],
|
||||
'webhooks_secrets' => [
|
||||
'manual_webhook_secret_github' => $this->resource->manual_webhook_secret_github,
|
||||
'manual_webhook_secret_gitlab' => $this->resource->manual_webhook_secret_gitlab,
|
||||
'manual_webhook_secret_bitbucket' => $this->resource->manual_webhook_secret_bitbucket,
|
||||
'manual_webhook_secret_gitea' => $this->resource->manual_webhook_secret_gitea,
|
||||
],
|
||||
'swarm' => [
|
||||
'swarm_replicas' => $this->resource->swarm_replicas,
|
||||
'swarm_placement_constraints' => $this->resource->swarm_placement_constraints,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
protected function getPreview(): array
|
||||
{
|
||||
return [
|
||||
'preview_url_template' => $this->resource->preview_url_template,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getDockerRegistryImage(): array
|
||||
{
|
||||
return [
|
||||
'image' => $this->resource->docker_registry_image_name,
|
||||
'tag' => $this->resource->docker_registry_image_tag,
|
||||
];
|
||||
}
|
||||
|
||||
protected function getEnvironmentVariables(): array
|
||||
{
|
||||
$variables = collect([]);
|
||||
foreach ($this->resource->environment_variables as $env) {
|
||||
$variables->push([
|
||||
'key' => $env->key,
|
||||
'value' => $env->value,
|
||||
'is_build_time' => $env->is_build_time,
|
||||
'is_preview' => $env->is_preview,
|
||||
'is_multiline' => $env->is_multiline,
|
||||
]);
|
||||
}
|
||||
|
||||
return $variables->toArray();
|
||||
}
|
||||
|
||||
protected function getPreviewEnvironmentVariables(): array
|
||||
{
|
||||
$variables = collect([]);
|
||||
foreach ($this->resource->environment_variables_preview as $env) {
|
||||
$variables->push([
|
||||
'key' => $env->key,
|
||||
'value' => $env->value,
|
||||
'is_build_time' => $env->is_build_time,
|
||||
'is_preview' => $env->is_preview,
|
||||
'is_multiline' => $env->is_multiline,
|
||||
]);
|
||||
}
|
||||
|
||||
return $variables->toArray();
|
||||
}
|
||||
|
||||
protected function getApplicationSettings(): array
|
||||
{
|
||||
$removedKeys = ['id', 'application_id', 'created_at', 'updated_at'];
|
||||
$settings = $this->resource->settings->attributesToArray();
|
||||
$settings = collect($settings)->filter(function ($value, $key) use ($removedKeys) {
|
||||
return ! in_array($key, $removedKeys);
|
||||
})->sortBy(function ($value, $key) {
|
||||
return $key;
|
||||
})->toArray();
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
public function saveJson(string $path): void
|
||||
{
|
||||
file_put_contents($path, json_encode($this->config, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
public function saveYaml(string $path): void
|
||||
{
|
||||
file_put_contents($path, Yaml::dump($this->config, 6, 2));
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public function toJson(): string
|
||||
{
|
||||
return json_encode($this->config, JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public function toYaml(): string
|
||||
{
|
||||
return Yaml::dump($this->config, 6, 2);
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,11 @@ class DockerImageParser
|
||||
|
||||
public function getFullImageNameWithoutTag(): string
|
||||
{
|
||||
return $this->registryUrl.'/'.$this->imageName;
|
||||
if ($this->registryUrl) {
|
||||
return $this->registryUrl.'/'.$this->imageName;
|
||||
}
|
||||
|
||||
return $this->imageName;
|
||||
}
|
||||
|
||||
public function getRegistryUrl(): string
|
||||
|
||||
42
app/Traits/HasConfiguration.php
Normal file
42
app/Traits/HasConfiguration.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use App\Services\ConfigurationGenerator;
|
||||
|
||||
trait HasConfiguration
|
||||
{
|
||||
public function generateConfigurationFiles(): void
|
||||
{
|
||||
$generator = new ConfigurationGenerator($this);
|
||||
|
||||
$configDir = base_configuration_dir()."/{$this->uuid}";
|
||||
if (! is_dir($configDir)) {
|
||||
mkdir($configDir, 0755, true);
|
||||
}
|
||||
|
||||
$generator->saveJson($configDir.'/coolify.json');
|
||||
$generator->saveYaml($configDir.'/coolify.yaml');
|
||||
|
||||
// Generate a README file with basic information
|
||||
file_put_contents(
|
||||
$configDir.'/README.md',
|
||||
generate_readme_file($this->name, now()->toIso8601String())
|
||||
);
|
||||
}
|
||||
|
||||
public function getConfigurationAsJson(): string
|
||||
{
|
||||
return (new ConfigurationGenerator($this))->toJson();
|
||||
}
|
||||
|
||||
public function getConfigurationAsYaml(): string
|
||||
{
|
||||
return (new ConfigurationGenerator($this))->toYaml();
|
||||
}
|
||||
|
||||
public function getConfigurationAsArray(): array
|
||||
{
|
||||
return (new ConfigurationGenerator($this))->toArray();
|
||||
}
|
||||
}
|
||||
@@ -2872,7 +2872,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
data_forget($service, 'volumes.*.is_directory');
|
||||
data_forget($service, 'exclude_from_hc');
|
||||
data_set($service, 'environment', $serviceVariables->toArray());
|
||||
updateCompose($service);
|
||||
|
||||
return $service;
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
return [
|
||||
'coolify' => [
|
||||
'version' => '4.0.0-beta.384',
|
||||
'version' => '4.0.0-beta.385',
|
||||
'self_hosted' => env('SELF_HOSTED', true),
|
||||
'autoupdate' => env('AUTOUPDATE'),
|
||||
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
try {
|
||||
DB::table('application_deployment_queues')
|
||||
->whereNull('finished_at')
|
||||
->update(['finished_at' => DB::raw('updated_at')]);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Failed to update not set finished_at timestamps for application_deployment_queues: '.$e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
DB::table('scheduled_database_backup_executions')
|
||||
->whereNull('finished_at')
|
||||
->update(['finished_at' => DB::raw('updated_at')]);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Failed to update not set finished_at timestamps for scheduled_database_backup_executions: '.$e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
DB::table('scheduled_task_executions')
|
||||
->whereNull('finished_at')
|
||||
->update(['finished_at' => DB::raw('updated_at')]);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Failed to update not set finished_at timestamps for scheduled_task_executions: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -5,13 +5,14 @@
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
{{--
|
||||
<x-forms.button wire:click="downloadConfig">
|
||||
|
||||
{{-- <x-forms.button wire:click="downloadConfig">
|
||||
Download Config
|
||||
<x-modal-input buttonTitle="Upload Config" title="Upload Config" :closeOutside="false">
|
||||
</x-forms.button> --}}
|
||||
{{-- <x-modal-input buttonTitle="Upload Config" title="Upload Config" :closeOutside="false">
|
||||
<livewire:project.shared.upload-config :applicationId="$application->id" />
|
||||
</x-modal-input>
|
||||
--}}
|
||||
</x-modal-input> --}}
|
||||
|
||||
</div>
|
||||
<div>General configuration for your application.</div>
|
||||
<div class="flex flex-col gap-2 py-4">
|
||||
|
||||
@@ -55,10 +55,10 @@
|
||||
<h3>Preview Deployments Environment Variables</h3>
|
||||
<div>Environment (secrets) variables for Preview Deployments.</div>
|
||||
</div>
|
||||
{{-- @foreach ($resource->environment_variables_preview as $env)
|
||||
@foreach ($resource->environment_variables_preview as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" :type="$resource->type()" />
|
||||
@endforeach --}}
|
||||
@endforeach
|
||||
@endif
|
||||
@else
|
||||
<form wire:submit.prevent='submit' class="flex flex-col gap-2">
|
||||
|
||||
@@ -16,43 +16,58 @@
|
||||
@elseif ($type === 'server')
|
||||
<x-server.navbar :server="$server" :parameters="$parameters" />
|
||||
@endif
|
||||
@if ($type === 'server')
|
||||
<form class="w-full" wire:submit="$dispatchSelf('connectToServer')" wire:init="$dispatchSelf('connectToServer')">
|
||||
<x-forms.button class="w-full" type="submit">Reconnect</x-forms.button>
|
||||
</form>
|
||||
<div class="mx-auto w-full">
|
||||
<livewire:project.shared.terminal />
|
||||
|
||||
@if(!$hasShell)
|
||||
<div class="flex items-center justify-center w-full py-4 mx-auto">
|
||||
<div class="p-4 w-full rounded border dark:bg-coolgray-100 dark:border-coolgray-300">
|
||||
<div class="flex flex-col items-center justify-center space-y-4">
|
||||
<svg class="w-12 h-12 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>
|
||||
<div class="text-center">
|
||||
<h3 class="text-lg font-medium">Terminal Not Available</h3>
|
||||
<p class="mt-2 text-sm text-gray-500">No shell (bash/sh) is available in this container. Please ensure either bash or sh is installed to use the terminal.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
@if (count($containers) > 0)
|
||||
@if (count($containers) === 1)
|
||||
<form class="w-full pt-4" wire:submit="$dispatchSelf('connectToContainer')"
|
||||
wire:init="$dispatchSelf('connectToContainer')">
|
||||
<x-forms.button class="w-full" type="submit">Reconnect</x-forms.button>
|
||||
</form>
|
||||
@else
|
||||
<form class="w-full pt-4 flex gap-2 flex-col" wire:submit="$dispatchSelf('connectToContainer')">
|
||||
<x-forms.select label="Container" id="container" required wire:model="selected_container">
|
||||
@foreach ($containers as $container)
|
||||
@if ($loop->first)
|
||||
<option disabled value="default">Select a container</option>
|
||||
@endif
|
||||
<option value="{{ data_get($container, 'container.Names') }}">
|
||||
{{ data_get($container, 'container.Names') }}
|
||||
({{ data_get($container, 'server.name') }})
|
||||
</option>
|
||||
@endforeach
|
||||
</x-forms.select>
|
||||
<x-forms.button class="w-full" type="submit">
|
||||
Connect
|
||||
</x-forms.button>
|
||||
</form>
|
||||
@endif
|
||||
@if ($type === 'server')
|
||||
<form class="w-full" wire:submit="$dispatchSelf('connectToServer')" wire:init="$dispatchSelf('connectToServer')">
|
||||
<x-forms.button class="w-full" type="submit">Reconnect</x-forms.button>
|
||||
</form>
|
||||
<div class="mx-auto w-full">
|
||||
<livewire:project.shared.terminal />
|
||||
</div>
|
||||
@else
|
||||
<div class="pt-4">No containers are running.</div>
|
||||
@if (count($containers) === 0)
|
||||
<div class="pt-4">No containers are running.</div>
|
||||
@else
|
||||
@if (count($containers) === 1)
|
||||
<form class="w-full pt-4" wire:submit="$dispatchSelf('connectToContainer')"
|
||||
wire:init="$dispatchSelf('connectToContainer')">
|
||||
<x-forms.button class="w-full" type="submit">Reconnect</x-forms.button>
|
||||
</form>
|
||||
@else
|
||||
<form class="w-full pt-4 flex gap-2 flex-col" wire:submit="$dispatchSelf('connectToContainer')">
|
||||
<x-forms.select label="Container" id="container" required wire:model="selected_container">
|
||||
@foreach ($containers as $container)
|
||||
@if ($loop->first)
|
||||
<option disabled value="default">Select a container</option>
|
||||
@endif
|
||||
<option value="{{ data_get($container, 'container.Names') }}">
|
||||
{{ data_get($container, 'container.Names') }}
|
||||
({{ data_get($container, 'server.name') }})
|
||||
</option>
|
||||
@endforeach
|
||||
</x-forms.select>
|
||||
<x-forms.button class="w-full" type="submit">Connect</x-forms.button>
|
||||
</form>
|
||||
@endif
|
||||
<div class="mx-auto w-full">
|
||||
<livewire:project.shared.terminal />
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<div id="terminal-container" x-data="terminalData()">
|
||||
{{-- <div x-show="!terminalActive" class="flex items-center justify-center w-full py-4 mx-auto h-[510px]">
|
||||
<div class="p-1 w-full h-full rounded border dark:bg-coolgray-100 dark:border-coolgray-300">
|
||||
<span class="font-mono text-sm text-gray-500" x-text="message"></span>
|
||||
</div>
|
||||
</div> --}}
|
||||
<div x-ref="terminalWrapper"
|
||||
:class="fullscreen ? 'fullscreen' : 'relative w-full h-full py-4 mx-auto max-h-[510px]'">
|
||||
<div id="terminal" wire:ignore></div>
|
||||
|
||||
@@ -6,72 +6,75 @@
|
||||
<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-server.sidebar :server="$server" activeMenu="docker-cleanup" />
|
||||
<div class="w-full">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Docker Cleanup</h2>
|
||||
<form wire:submit='submit'>
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Docker Cleanup</h2>
|
||||
<x-forms.button type="submit">Save</x-forms.button>
|
||||
</div>
|
||||
<div class="mt-3 mb-4">Configure Docker cleanup settings for your server.</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-4">Configure Docker cleanup settings for your server.</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-4">
|
||||
<h3>Docker Cleanup</h3>
|
||||
<x-modal-confirmation title="Confirm Docker Cleanup?" buttonTitle="Trigger Manual Cleanup"
|
||||
isHighlightedButton submitAction="manualCleanup" :actions="[
|
||||
'Permanently deletes all stopped containers managed by Coolify (as containers are non-persistent, no data will be lost)',
|
||||
'Permanently deletes all unused images',
|
||||
'Clears build cache',
|
||||
'Removes old versions of the Coolify helper image',
|
||||
'Optionally permanently deletes all unused volumes (if enabled in advanced options).',
|
||||
'Optionally permanently deletes all unused networks (if enabled in advanced options).',
|
||||
]" :confirmWithText="false"
|
||||
:confirmWithPassword="false" step2ButtonText="Trigger Docker Cleanup" />
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<x-forms.input placeholder="*/10 * * * *" id="dockerCleanupFrequency"
|
||||
label="Docker cleanup frequency" required
|
||||
helper="Cron expression for Docker Cleanup.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every night at midnight." />
|
||||
@if (!$forceDockerCleanup)
|
||||
<x-forms.input id="dockerCleanupThreshold" label="Docker cleanup threshold (%)" required
|
||||
helper="The Docker cleanup tasks will run when the disk usage exceeds this threshold." />
|
||||
@endif
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-4">
|
||||
<h3>Docker Cleanup</h3>
|
||||
<x-modal-confirmation title="Confirm Docker Cleanup?" buttonTitle="Trigger Manual Cleanup"
|
||||
isHighlightedButton submitAction="manualCleanup" :actions="[
|
||||
'Permanently deletes all stopped containers managed by Coolify (as containers are non-persistent, no data will be lost)',
|
||||
'Permanently deletes all unused images',
|
||||
'Clears build cache',
|
||||
'Removes old versions of the Coolify helper image',
|
||||
'Optionally permanently deletes all unused volumes (if enabled in advanced options).',
|
||||
'Optionally permanently deletes all unused networks (if enabled in advanced options).',
|
||||
]" :confirmWithText="false"
|
||||
:confirmWithPassword="false" step2ButtonText="Trigger Docker Cleanup" />
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<x-forms.input placeholder="*/10 * * * *" id="dockerCleanupFrequency"
|
||||
label="Docker cleanup frequency" required
|
||||
helper="Cron expression for Docker Cleanup.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every night at midnight." />
|
||||
@if (!$forceDockerCleanup)
|
||||
<x-forms.input id="dockerCleanupThreshold" label="Docker cleanup threshold (%)" required
|
||||
helper="The Docker cleanup tasks will run when the disk usage exceeds this threshold." />
|
||||
@endif
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox
|
||||
helper="Enabling Force Docker Cleanup or manually triggering a cleanup will perform the following actions:
|
||||
<ul class='list-disc pl-4 mt-2'>
|
||||
<li>Removes stopped containers managed by Coolify (as containers are none persistent, no data will be lost).</li>
|
||||
<li>Deletes unused images.</li>
|
||||
<li>Clears build cache.</li>
|
||||
<li>Removes old versions of the Coolify helper image.</li>
|
||||
<li>Optionally delete unused volumes (if enabled in advanced options).</li>
|
||||
<li>Optionally remove unused networks (if enabled in advanced options).</li>
|
||||
</ul>"
|
||||
instantSave id="forceDockerCleanup" label="Force Docker Cleanup" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">
|
||||
<span class="dark:text-warning font-bold">Warning: Enable these
|
||||
options only if you fully understand their implications and
|
||||
consequences!</span><br>Improper use will result in data loss and could cause
|
||||
functional issues.
|
||||
</p>
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox
|
||||
helper="Enabling Force Docker Cleanup or manually triggering a cleanup will perform the following actions:
|
||||
<x-forms.checkbox instantSave id="deleteUnusedVolumes" label="Delete Unused Volumes"
|
||||
helper="This option will remove all unused Docker volumes during cleanup.<br><br><strong>Warning: Data form stopped containers will be lost!</strong><br><br>Consequences include:<br>
|
||||
<ul class='list-disc pl-4 mt-2'>
|
||||
<li>Removes stopped containers managed by Coolify (as containers are none persistent, no data will be lost).</li>
|
||||
<li>Deletes unused images.</li>
|
||||
<li>Clears build cache.</li>
|
||||
<li>Removes old versions of the Coolify helper image.</li>
|
||||
<li>Optionally delete unused volumes (if enabled in advanced options).</li>
|
||||
<li>Optionally remove unused networks (if enabled in advanced options).</li>
|
||||
</ul>"
|
||||
instantSave id="forceDockerCleanup" label="Force Docker Cleanup" />
|
||||
<li>Volumes not attached to running containers will be deleted and data will be permanently lost (stopped containers are affected).</li>
|
||||
<li>Data from stopped containers volumes will be permanently lost.</li>
|
||||
<li>No way to recover deleted volume data.</li>
|
||||
</ul>" />
|
||||
<x-forms.checkbox instantSave id="deleteUnusedNetworks" label="Delete Unused Networks"
|
||||
helper="This option will remove all unused Docker networks during cleanup.<br><br><strong>Warning: Functionality may be lost and containers may not be able to communicate with each other!</strong><br><br>Consequences include:<br>
|
||||
<ul class='list-disc pl-4 mt-2'>
|
||||
<li>Networks not attached to running containers will be permanently deleted (stopped containers are affected).</li>
|
||||
<li>Custom networks for stopped containers will be permanently deleted.</li>
|
||||
<li>Functionality may be lost and containers may not be able to communicate with each other.</li>
|
||||
</ul>" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">
|
||||
<span class="dark:text-warning font-bold">Warning: Enable these
|
||||
options only if you fully understand their implications and
|
||||
consequences!</span><br>Improper use will result in data loss and could cause
|
||||
functional issues.
|
||||
</p>
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave id="deleteUnusedVolumes" label="Delete Unused Volumes"
|
||||
helper="This option will remove all unused Docker volumes during cleanup.<br><br><strong>Warning: Data form stopped containers will be lost!</strong><br><br>Consequences include:<br>
|
||||
<ul class='list-disc pl-4 mt-2'>
|
||||
<li>Volumes not attached to running containers will be deleted and data will be permanently lost (stopped containers are affected).</li>
|
||||
<li>Data from stopped containers volumes will be permanently lost.</li>
|
||||
<li>No way to recover deleted volume data.</li>
|
||||
</ul>" />
|
||||
<x-forms.checkbox instantSave id="deleteUnusedNetworks" label="Delete Unused Networks"
|
||||
helper="This option will remove all unused Docker networks during cleanup.<br><br><strong>Warning: Functionality may be lost and containers may not be able to communicate with each other!</strong><br><br>Consequences include:<br>
|
||||
<ul class='list-disc pl-4 mt-2'>
|
||||
<li>Networks not attached to running containers will be permanently deleted (stopped containers are affected).</li>
|
||||
<li>Custom networks for stopped containers will be permanently deleted.</li>
|
||||
<li>Functionality may be lost and containers may not be able to communicate with each other.</li>
|
||||
</ul>" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-8">
|
||||
<h3 class="mb-4">Recent executions <span class="text-xs text-neutral-500">(click to check output)</span></h3>
|
||||
|
||||
@@ -449,7 +449,7 @@
|
||||
"cloudflared": {
|
||||
"documentation": "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/?utm_source=coolify.io",
|
||||
"slogan": "Client for Cloudflare Tunnel, a daemon that exposes private services through the Cloudflare edge.",
|
||||
"compose": "c2VydmljZXM6CiAgY2xvdWRmbGFyZWQ6CiAgICBjb250YWluZXJfbmFtZTogY2xvdWRmbGFyZS10dW5uZWwKICAgIGltYWdlOiAnY2xvdWRmbGFyZS9jbG91ZGZsYXJlZDpsYXRlc3QnCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya19tb2RlOiBob3N0CiAgICBjb21tYW5kOiAndHVubmVsIC0tbm8tYXV0b3VwZGF0ZSBydW4nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnVFVOTkVMX1RPS0VOPSR7Q0xPVURGTEFSRV9UVU5ORUxfVE9LRU59Jwo=",
|
||||
"compose": "c2VydmljZXM6CiAgY2xvdWRmbGFyZWQ6CiAgICBjb250YWluZXJfbmFtZTogY2xvdWRmbGFyZS10dW5uZWwKICAgIGltYWdlOiAnY2xvdWRmbGFyZS9jbG91ZGZsYXJlZDpsYXRlc3QnCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgbmV0d29ya19tb2RlOiBob3N0CiAgICBjb21tYW5kOiAndHVubmVsIC0tbm8tYXV0b3VwZGF0ZSBydW4nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnVFVOTkVMX1RPS0VOPSR7Q0xPVURGTEFSRV9UVU5ORUxfVE9LRU59JwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGNsb3VkZmxhcmVkCiAgICAgICAgLSAnLS12ZXJzaW9uJwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==",
|
||||
"tags": null,
|
||||
"logo": "svgs/cloudflared.svg",
|
||||
"minversion": "0.0.0"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.384"
|
||||
"version": "4.0.0-beta.385"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.385"
|
||||
"version": "4.0.0-beta.386"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.4"
|
||||
|
||||
Reference in New Issue
Block a user