mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
chore: progress select multiple registries
This commit is contained in:
parent
03bfcfb0e6
commit
0ff0cadede
11 changed files with 152 additions and 108 deletions
|
|
@ -2459,35 +2459,35 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||||
|
|
||||||
private function handleRegistryAuth()
|
private function handleRegistryAuth()
|
||||||
{
|
{
|
||||||
if (!$this->dockerRegistry) {
|
$registries = $this->application->registries;
|
||||||
throw new Exception('Registry not found.');
|
|
||||||
|
if ($registries->isEmpty()) {
|
||||||
|
throw new Exception('No registries found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = escapeshellarg($this->dockerRegistry->token);
|
foreach ($registries as $registry) {
|
||||||
$username = escapeshellarg($this->dockerRegistry->username);
|
$token = escapeshellarg($registry->token);
|
||||||
|
$username = escapeshellarg($registry->username);
|
||||||
|
|
||||||
// Handle different registry types
|
$url = match ($registry->type) {
|
||||||
$url = match ($this->dockerRegistry->type) {
|
'docker_hub' => '',
|
||||||
'docker_hub' => '', // Docker Hub doesn't need URL specified
|
'custom' => escapeshellarg($registry->url),
|
||||||
'custom' => escapeshellarg($this->dockerRegistry->url),
|
default => escapeshellarg($registry->url)
|
||||||
default => escapeshellarg($this->dockerRegistry->url)
|
};
|
||||||
};
|
|
||||||
|
|
||||||
$this->application_deployment_queue->addLogEntry('Attempting to log into registry...');
|
$this->application_deployment_queue->addLogEntry("Attempting to log into registry {$registry->name}...");
|
||||||
|
|
||||||
// Build login command based on registry type
|
$command = $registry->type === 'docker_hub'
|
||||||
$command = $this->dockerRegistry->type === 'docker_hub'
|
? "echo {{secrets.token}} | docker login -u {$username} --password-stdin"
|
||||||
? "echo {{secrets.token}} | docker login -u {$username} --password-stdin"
|
: "echo {{secrets.token}} | docker login {$url} -u {$username} --password-stdin";
|
||||||
: "echo {{secrets.token}} | docker login {$url} -u {$username} --password-stdin";
|
|
||||||
|
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command([
|
||||||
[
|
|
||||||
'command' => $command,
|
'command' => $command,
|
||||||
'secrets' => [
|
'secrets' => [
|
||||||
'token' => $token,
|
'token' => $token,
|
||||||
],
|
],
|
||||||
'hidden' => true,
|
'hidden' => true,
|
||||||
]
|
]);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,8 @@ class General extends Component
|
||||||
'application.docker_registry_image_name' => 'nullable',
|
'application.docker_registry_image_name' => 'nullable',
|
||||||
'application.docker_registry_image_tag' => 'nullable',
|
'application.docker_registry_image_tag' => 'nullable',
|
||||||
'application.docker_use_custom_registry' => 'boolean',
|
'application.docker_use_custom_registry' => 'boolean',
|
||||||
'application.docker_registry_id' => 'nullable|required_if:application.docker_use_custom_registry,true',
|
'application.selectedRegistries' => 'required_if:application.docker_use_custom_registry,true|array',
|
||||||
|
'application.selectedRegistries.*' => 'exists:docker_registries,id',
|
||||||
'application.dockerfile_location' => 'nullable',
|
'application.dockerfile_location' => 'nullable',
|
||||||
'application.docker_compose_location' => 'nullable',
|
'application.docker_compose_location' => 'nullable',
|
||||||
'application.docker_compose' => 'nullable',
|
'application.docker_compose' => 'nullable',
|
||||||
|
|
@ -117,7 +118,7 @@ class General extends Component
|
||||||
'application.docker_registry_image_name' => 'Docker registry image name',
|
'application.docker_registry_image_name' => 'Docker registry image name',
|
||||||
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
||||||
'application.docker_use_custom_registry' => 'Use private registry',
|
'application.docker_use_custom_registry' => 'Use private registry',
|
||||||
'application.docker_registry_id' => 'Registry',
|
'application.selectedRegistries' => 'Registries',
|
||||||
'application.dockerfile_location' => 'Dockerfile location',
|
'application.dockerfile_location' => 'Dockerfile location',
|
||||||
'application.docker_compose_location' => 'Docker compose location',
|
'application.docker_compose_location' => 'Docker compose location',
|
||||||
'application.docker_compose' => 'Docker compose',
|
'application.docker_compose' => 'Docker compose',
|
||||||
|
|
@ -173,6 +174,10 @@ class General extends Component
|
||||||
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
||||||
$this->dispatch('configurationChanged');
|
$this->dispatch('configurationChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->application->docker_use_custom_registry) {
|
||||||
|
$this->application->selectedRegistries = $this->application->registries->pluck('id')->toArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
|
|
@ -433,6 +438,13 @@ class General extends Component
|
||||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$showToaster && ! $warning && $this->dispatch('success', 'Application settings updated!');
|
$showToaster && ! $warning && $this->dispatch('success', 'Application settings updated!');
|
||||||
|
if ($this->application->docker_use_custom_registry) {
|
||||||
|
$this->application->registries()->sync($this->application->selectedRegistries);
|
||||||
|
} else {
|
||||||
|
$this->application->registries()->detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->application->save();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$originalFqdn = $this->application->getOriginal('fqdn');
|
$originalFqdn = $this->application->getOriginal('fqdn');
|
||||||
if ($originalFqdn !== $this->application->fqdn) {
|
if ($originalFqdn !== $this->application->fqdn) {
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,14 @@ class DockerImage extends Component
|
||||||
{
|
{
|
||||||
public string $dockerImage = '';
|
public string $dockerImage = '';
|
||||||
public bool $useCustomRegistry = false;
|
public bool $useCustomRegistry = false;
|
||||||
public ?int $selectedRegistry = null;
|
public array $selectedRegistries = [];
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'dockerImage' => 'required|string',
|
'dockerImage' => 'required|string',
|
||||||
'selectedRegistry' => 'required_if:useCustomRegistry,true|nullable|exists:docker_registries,id'
|
'selectedRegistries' => 'required_if:useCustomRegistry,true|array',
|
||||||
|
'selectedRegistries.*' => 'exists:docker_registries,id'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
|
|
@ -31,54 +32,68 @@ class DockerImage extends Component
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate(['dockerImage' => 'required',]);
|
$this->validate([
|
||||||
|
'dockerImage' => 'required',
|
||||||
|
'selectedRegistries' => 'required_if:useCustomRegistry,true|array',
|
||||||
|
'selectedRegistries.*' => 'exists:docker_registries,id'
|
||||||
|
]);
|
||||||
|
|
||||||
$image = str($this->dockerImage)->before(':');
|
try {
|
||||||
$tag = str($this->dockerImage)->contains(':') ?
|
$image = str($this->dockerImage)->before(':');
|
||||||
str($this->dockerImage)->after(':') :
|
$tag = str($this->dockerImage)->contains(':') ?
|
||||||
'latest';
|
str($this->dockerImage)->after(':') :
|
||||||
|
'latest';
|
||||||
|
|
||||||
$destination_uuid = $this->query['destination'];
|
$destination_uuid = $this->query['destination'];
|
||||||
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||||
if (! $destination) {
|
if (! $destination) {
|
||||||
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
||||||
|
}
|
||||||
|
if (! $destination) {
|
||||||
|
throw new \Exception('Destination not found. What?!');
|
||||||
|
}
|
||||||
|
$destination_class = $destination->getMorphClass();
|
||||||
|
|
||||||
|
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||||
|
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||||
|
|
||||||
|
$name = 'docker-image-' . new Cuid2;
|
||||||
|
error_log($name);
|
||||||
|
$application = Application::create([
|
||||||
|
'name' => $name,
|
||||||
|
'repository_project_id' => 0,
|
||||||
|
'git_repository' => 'coollabsio/coolify',
|
||||||
|
'git_branch' => 'main',
|
||||||
|
'build_pack' => 'dockerimage',
|
||||||
|
'ports_exposes' => 80,
|
||||||
|
'docker_registry_image_name' => $image,
|
||||||
|
'docker_registry_image_tag' => $tag,
|
||||||
|
'docker_use_custom_registry' => $this->useCustomRegistry,
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination_class,
|
||||||
|
'health_check_enabled' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// if ($this->useCustomRegistry && !empty($this->selectedRegistries)) {
|
||||||
|
// $application->registries()->attach($this->selectedRegistries);
|
||||||
|
// }
|
||||||
|
|
||||||
|
error_log($application->uuid);
|
||||||
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
|
$application->update([
|
||||||
|
'name' => 'docker-image-' . $application->uuid,
|
||||||
|
'fqdn' => $fqdn,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()->route('project.application.configuration', [
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'environment_name' => $environment->name,
|
||||||
|
'project_uuid' => $project->uuid,
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->dispatch('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
if (! $destination) {
|
|
||||||
throw new \Exception('Destination not found. What?!');
|
|
||||||
}
|
|
||||||
$destination_class = $destination->getMorphClass();
|
|
||||||
|
|
||||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
|
||||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
|
||||||
|
|
||||||
$application = Application::create([
|
|
||||||
'name' => 'docker-image-' . new Cuid2,
|
|
||||||
'repository_project_id' => 0,
|
|
||||||
'git_repository' => 'coollabsio/coolify',
|
|
||||||
'git_branch' => 'main',
|
|
||||||
'build_pack' => 'dockerimage',
|
|
||||||
'ports_exposes' => 80,
|
|
||||||
'docker_registry_image_name' => $image,
|
|
||||||
'docker_registry_image_tag' => $tag,
|
|
||||||
'docker_use_custom_registry' => $this->useCustomRegistry,
|
|
||||||
'docker_registry_id' => $this->selectedRegistry ?? null,
|
|
||||||
'environment_id' => $environment->id,
|
|
||||||
'destination_id' => $destination->id,
|
|
||||||
'destination_type' => $destination_class,
|
|
||||||
'health_check_enabled' => false,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
|
||||||
$application->update([
|
|
||||||
'name' => 'docker-image-' . $application->uuid,
|
|
||||||
'fqdn' => $fqdn,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect()->route('project.application.configuration', [
|
|
||||||
'application_uuid' => $application->uuid,
|
|
||||||
'environment_name' => $environment->name,
|
|
||||||
'project_uuid' => $project->uuid,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,15 @@ class Application extends BaseModel
|
||||||
|
|
||||||
private static $parserVersion = '4';
|
private static $parserVersion = '4';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'docker_use_custom_registry',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'docker_use_custom_registry' => 'boolean',
|
||||||
|
'selectedRegistries' => 'array',
|
||||||
|
];
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $appends = ['server_status'];
|
protected $appends = ['server_status'];
|
||||||
|
|
@ -1653,8 +1662,8 @@ class Application extends BaseModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registry()
|
public function registries()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(DockerRegistry::class);
|
return $this->belongsToMany(DockerRegistry::class, 'application_docker_registry');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ class Select extends Component
|
||||||
public ?string $label = null,
|
public ?string $label = null,
|
||||||
public ?string $helper = null,
|
public ?string $helper = null,
|
||||||
public bool $required = false,
|
public bool $required = false,
|
||||||
|
public bool $multiple = false,
|
||||||
public string $defaultClass = 'select'
|
public string $defaultClass = 'select'
|
||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
|
|
@ -33,11 +34,13 @@ class Select extends Component
|
||||||
$this->id = new Cuid2;
|
$this->id = new Cuid2;
|
||||||
}
|
}
|
||||||
if (is_null($this->name)) {
|
if (is_null($this->name)) {
|
||||||
$this->name = $this->id;
|
$this->name = $this->multiple ? "{$this->id}[]" : $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->label = Str::title($this->label);
|
$this->label = Str::title($this->label);
|
||||||
|
|
||||||
return view('components.forms.select');
|
return view('components.forms.select', [
|
||||||
|
'multiple' => $this->multiple,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,7 @@ return new class extends Migration {
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::table('applications', function (Blueprint $table) {
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
$table->foreignId('docker_registry_id')->nullable();
|
|
||||||
$table->boolean('docker_use_custom_registry')->default(false);
|
$table->boolean('docker_use_custom_registry')->default(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down(): void
|
|
||||||
{
|
|
||||||
Schema::table('applications', function (Blueprint $table) {
|
|
||||||
$table->dropColumn('docker_registry_id');
|
|
||||||
$table->dropColumn('docker_use_custom_registry');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,22 +16,10 @@ return new class extends Migration {
|
||||||
$table->text('token')->nullable();
|
$table->text('token')->nullable();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add foreign key constraint
|
|
||||||
Schema::table('applications', function (Blueprint $table) {
|
|
||||||
$table->foreign('docker_registry_id')
|
|
||||||
->references('id')
|
|
||||||
->on('docker_registries')
|
|
||||||
->nullOnDelete();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down(): void
|
public function down(): void
|
||||||
{
|
{
|
||||||
Schema::table('applications', function (Blueprint $table) {
|
|
||||||
$table->dropForeign(['docker_registry_id']);
|
|
||||||
});
|
|
||||||
|
|
||||||
Schema::dropIfExists('docker_registries');
|
Schema::dropIfExists('docker_registries');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration {
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('application_docker_registry', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedBigInteger('application_id');
|
||||||
|
$table->unsignedBigInteger('docker_registry_id');
|
||||||
|
$table->foreign('application_id')->references('id')->on('applications')->cascadeOnDelete();
|
||||||
|
$table->foreign('docker_registry_id')->references('id')->on('docker_registries')->cascadeOnDelete();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('application_docker_registry');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
@if ($label)
|
@if ($label)
|
||||||
<label class="flex gap-1 items-center mb-1 text-sm font-medium">{{ $label }}
|
<label class="flex gap-1 items-center mb-1 text-sm font-medium" for="{{ $id }}">
|
||||||
|
{{ $label }}
|
||||||
@if ($required)
|
@if ($required)
|
||||||
<x-highlighted text="*" />
|
<x-highlighted text="*" />
|
||||||
@endif
|
@endif
|
||||||
|
|
@ -11,8 +12,8 @@
|
||||||
@endif
|
@endif
|
||||||
<select {{ $attributes->merge(['class' => $defaultClass]) }} @required($required)
|
<select {{ $attributes->merge(['class' => $defaultClass]) }} @required($required)
|
||||||
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
|
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
|
||||||
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled" name={{ $id }}
|
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled"
|
||||||
@if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model={{ $id }} @endif>
|
name="{{ $id }}" @if ($multiple) multiple @endif id="{{ $id }}">
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</select>
|
</select>
|
||||||
@error($id)
|
@error($id)
|
||||||
|
|
|
||||||
|
|
@ -153,14 +153,16 @@
|
||||||
|
|
||||||
|
|
||||||
@if ($application->docker_use_custom_registry)
|
@if ($application->docker_use_custom_registry)
|
||||||
<x-forms.select wire:model.live="application.docker_registry_id"
|
<div class="pt-4">
|
||||||
id="application.docker_registry_id" label="Select Registry"
|
<x-forms.select multiple wire:model.live="application.selectedRegistries"
|
||||||
required="required_if:application.docker_use_custom_registry,true">
|
id="application.selectedRegistries" label="Select Registries"
|
||||||
{{-- <option>Select a registry...</option> --}}
|
required="required_if:application.docker_use_custom_registry,true"
|
||||||
@foreach ($registries as $registry)
|
helper="Select one or more registries to pull the image from">
|
||||||
<option value="{{ $registry['id'] }}">{{ $registry['name'] }}</option>
|
@foreach ($registries as $registry)
|
||||||
@endforeach
|
<option value="{{ $registry->id }}">{{ $registry->name }}</option>
|
||||||
</x-forms.select>
|
@endforeach
|
||||||
|
</x-forms.select>
|
||||||
|
</div>
|
||||||
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -187,8 +189,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-1 text-xs">Nixpacks will detect the required configuration
|
<div class="pt-1 text-xs">Nixpacks will detect the required configuration
|
||||||
automatically.
|
automatically.
|
||||||
<a class="underline"
|
<a class="underline" href="https://coolify.io/docs/applications">Framework
|
||||||
href="https://coolify.io/docs/applications">Framework
|
|
||||||
Specific Docs</a>
|
Specific Docs</a>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@
|
||||||
|
|
||||||
@if ($useCustomRegistry)
|
@if ($useCustomRegistry)
|
||||||
<div class="pt-4">
|
<div class="pt-4">
|
||||||
<x-forms.select id="selectedRegistry" wire:model="selectedRegistry" label="Select Registry" required>
|
<x-forms.select multiple name="selectedRegistries" id="selectedRegistries"
|
||||||
<option value="">Select a registry...</option>
|
wire:model="selectedRegistries" label="Select Registries" required>
|
||||||
@foreach ($registries as $registry)
|
@foreach ($registries as $registry)
|
||||||
<option value="{{ $registry->id }}">{{ $registry->name }}</option>
|
<option value="{{ $registry->id }}">{{ $registry->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue