Merge pull request #1 from OSINTRoach/budaydev

merge
This commit is contained in:
David Buday 2024-12-03 22:36:06 +01:00 committed by GitHub
commit 8c32dda14b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 169 additions and 119 deletions

View file

@ -52,8 +52,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private Application $application;
private DockerRegistry $dockerRegistry;
private string $deployment_uuid;
private int $pull_request_id;
@ -176,10 +174,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->build_pack = data_get($this->application, 'build_pack');
$this->build_args = collect([]);
if ($this->application->docker_registry_id) {
$this->dockerRegistry = DockerRegistry::find($this->application->docker_registry_id);
}
$this->application_deployment_queue_id = $application_deployment_queue_id;
$this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
$this->pull_request_id = $this->application_deployment_queue->pull_request_id;
@ -396,7 +390,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private function deploy_dockerimage_buildpack()
{
$useCustomRegistry = $this->application->docker_use_custom_registry;
try {
// setup
$this->dockerImage = $this->application->docker_registry_image_name;
@ -408,7 +401,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.");
// login if use custom registry
if ($useCustomRegistry) {
if ($this->application->docker_use_custom_registry) {
$this->handleRegistryAuth();
}
@ -419,7 +412,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
} catch (Exception $e) {
throw $e;
} finally {
if ($useCustomRegistry) {
if ($this->application->docker_use_custom_registry) {
$this->application_deployment_queue->addLogEntry('Logging out from registry...');
$this->execute_remote_command([
'docker logout',
@ -2459,35 +2452,35 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
private function handleRegistryAuth()
{
if (!$this->dockerRegistry) {
throw new Exception('Registry not found.');
$registries = $this->application->registries;
if ($registries->isEmpty()) {
throw new Exception('No registries found.');
}
$token = escapeshellarg($this->dockerRegistry->token);
$username = escapeshellarg($this->dockerRegistry->username);
foreach ($registries as $registry) {
$token = escapeshellarg($registry->token);
$username = escapeshellarg($registry->username);
// Handle different registry types
$url = match ($this->dockerRegistry->type) {
'docker_hub' => '', // Docker Hub doesn't need URL specified
'custom' => escapeshellarg($this->dockerRegistry->url),
default => escapeshellarg($this->dockerRegistry->url)
};
$url = match ($registry->type) {
'docker_hub' => '',
'custom' => escapeshellarg($registry->url),
default => escapeshellarg($registry->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 = $this->dockerRegistry->type === 'docker_hub'
? "echo {{secrets.token}} | docker login -u {$username} --password-stdin"
: "echo {{secrets.token}} | docker login {$url} -u {$username} --password-stdin";
$command = $registry->type === 'docker_hub'
? "echo {{secrets.token}} | docker login -u {$username} --password-stdin"
: "echo {{secrets.token}} | docker login {$url} -u {$username} --password-stdin";
$this->execute_remote_command(
[
$this->execute_remote_command([
'command' => $command,
'secrets' => [
'token' => $token,
],
'hidden' => true,
]
);
]);
}
}
}

View file

@ -8,7 +8,7 @@ use Livewire\Component;
class Create extends Component
{
#[Validate('required|string|max:255')]
#[Validate('required|string|max:255|unique:docker_registries,name')]
public string $name = '';
#[Validate('required|string')]

View file

@ -30,6 +30,8 @@ class General extends Component
public string $build_pack;
public array $selectedRegistries = [];
public ?string $ports_exposes = null;
public bool $is_preserve_repository_enabled = false;
@ -73,7 +75,6 @@ class General extends Component
'application.docker_registry_image_name' => 'nullable',
'application.docker_registry_image_tag' => 'nullable',
'application.docker_use_custom_registry' => 'boolean',
'application.docker_registry_id' => 'nullable|required_if:application.docker_use_custom_registry,true',
'application.dockerfile_location' => 'nullable',
'application.docker_compose_location' => 'nullable',
'application.docker_compose' => 'nullable',
@ -95,6 +96,8 @@ class General extends Component
'application.settings.is_preserve_repository_enabled' => 'boolean|required',
'application.watch_paths' => 'nullable',
'application.redirect' => 'string|required',
'selectedRegistries' => 'required_if:application.docker_use_custom_registry,true|array',
'selectedRegistries.*' => 'exists:docker_registries,id',
];
protected $validationAttributes = [
@ -117,7 +120,6 @@ class General extends Component
'application.docker_registry_image_name' => 'Docker registry image name',
'application.docker_registry_image_tag' => 'Docker registry image tag',
'application.docker_use_custom_registry' => 'Use private registry',
'application.docker_registry_id' => 'Registry',
'application.dockerfile_location' => 'Dockerfile location',
'application.docker_compose_location' => 'Docker compose location',
'application.docker_compose' => 'Docker compose',
@ -135,6 +137,7 @@ class General extends Component
'application.settings.is_preserve_repository_enabled' => 'Is preserve repository enabled',
'application.watch_paths' => 'Watch paths',
'application.redirect' => 'Redirect',
'selectedRegistries' => 'Registries',
];
public function mount()
@ -154,6 +157,9 @@ class General extends Component
$this->application->settings->save();
}
if ($this->application->docker_use_custom_registry) {
$this->selectedRegistries = $this->application->registries->pluck('id')->toArray();
}
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
$this->ports_exposes = $this->application->ports_exposes;
$this->is_preserve_repository_enabled = $this->application->settings->is_preserve_repository_enabled;
@ -395,7 +401,8 @@ class General extends Component
if (data_get($this->application, 'build_pack') === 'dockerimage') {
$this->validate([
'application.docker_registry_image_name' => 'required',
'application.docker_registry_id' => 'required_if:application.docker_use_custom_registry,true',
'selectedRegistries' => 'required_if:application.docker_use_custom_registry,true|array',
'selectedRegistries.*' => 'exists:docker_registries,id',
]);
}
@ -431,6 +438,12 @@ class General extends Component
}
}
$this->application->custom_labels = base64_encode($this->customLabels);
if ($this->application->docker_use_custom_registry) {
$this->application->registries()->sync($this->selectedRegistries);
} else {
$this->application->registries()->detach();
}
$this->application->save();
$showToaster && ! $warning && $this->dispatch('success', 'Application settings updated!');
} catch (\Throwable $e) {

View file

@ -14,13 +14,14 @@ class DockerImage extends Component
{
public string $dockerImage = '';
public bool $useCustomRegistry = false;
public ?int $selectedRegistry = null;
public array $selectedRegistries = [];
public array $parameters;
public array $query;
protected $rules = [
'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()
@ -31,54 +32,70 @@ class DockerImage extends Component
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(':');
$tag = str($this->dockerImage)->contains(':') ?
str($this->dockerImage)->after(':') :
'latest';
try {
$image = str($this->dockerImage)->before(':');
if (str($this->dockerImage)->contains(':')) {
$tag = str($this->dockerImage)->after(':');
} else {
$tag = 'latest';
}
$destination_uuid = $this->query['destination'];
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
$destination_uuid = $this->query['destination'];
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
$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,
'environment_id' => $environment->id,
'destination_id' => $destination->id,
'destination_type' => $destination_class,
'health_check_enabled' => false,
'docker_use_custom_registry' => $this->useCustomRegistry,
]);
if ($this->useCustomRegistry && !empty($this->selectedRegistries)) {
$application->registries()->sync($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()

View file

@ -110,6 +110,15 @@ class Application extends BaseModel
private static $parserVersion = '4';
protected $fillable = [
'docker_use_custom_registry',
];
protected $casts = [
'docker_use_custom_registry' => 'boolean',
'selectedRegistries' => 'array',
];
protected $guarded = [];
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', 'application_id');
}
}

View file

@ -25,9 +25,4 @@ class DockerRegistry extends Model
'custom' => 'Custom Registry'
];
}
public function applications(): HasMany
{
return $this->hasMany(Application::class, 'docker_registry_id', 'id');
}
}

View file

@ -19,6 +19,7 @@ class Select extends Component
public ?string $label = null,
public ?string $helper = null,
public bool $required = false,
public bool $multiple = false,
public string $defaultClass = 'select'
) {
//
@ -33,11 +34,13 @@ class Select extends Component
$this->id = new Cuid2;
}
if (is_null($this->name)) {
$this->name = $this->id;
$this->name = $this->multiple ? "{$this->id}[]" : $this->id;
}
$this->label = Str::title($this->label);
return view('components.forms.select');
return view('components.forms.select', [
'multiple' => $this->multiple,
]);
}
}

View file

@ -8,7 +8,6 @@ return new class extends Migration {
public function up(): void
{
Schema::table('applications', function (Blueprint $table) {
$table->foreignId('docker_registry_id')->nullable();
$table->boolean('docker_use_custom_registry')->default(false);
});
}
@ -16,7 +15,6 @@ return new class extends Migration {
public function down(): void
{
Schema::table('applications', function (Blueprint $table) {
$table->dropColumn('docker_registry_id');
$table->dropColumn('docker_use_custom_registry');
});
}

View file

@ -9,29 +9,17 @@ return new class extends Migration {
{
Schema::create('docker_registries', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('name')->unique();
$table->string('type'); // docker_hub, gcr, ghcr, quay, custom
$table->string('url')->nullable();
$table->string('username')->nullable();
$table->text('token')->nullable();
$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
{
Schema::table('applications', function (Blueprint $table) {
$table->dropForeign(['docker_registry_id']);
});
Schema::dropIfExists('docker_registries');
}
};

View file

@ -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');
}
};

View file

@ -13,6 +13,10 @@ body {
@apply text-sm antialiased scrollbar;
}
option[selected] {
@apply bg-coollabs;
}
.apexcharts-tooltip {
@apply dark:text-white dark:border-coolgray-300 dark:bg-coolgray-200 shadow-none !important;
}

View file

@ -1,6 +1,7 @@
<div class="w-full">
@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)
<x-highlighted text="*" />
@endif
@ -11,8 +12,8 @@
@endif
<select {{ $attributes->merge(['class' => $defaultClass]) }} @required($required)
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 }}
@if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model={{ $id }} @endif>
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled"
name="{{ $id }}" @if ($multiple) multiple @endif id="{{ $id }}">
{{ $slot }}
</select>
@error($id)

View file

@ -153,14 +153,19 @@
@if ($application->docker_use_custom_registry)
<x-forms.select wire:model.live="application.docker_registry_id"
id="application.docker_registry_id" label="Select Registry"
required="required_if:application.docker_use_custom_registry,true">
{{-- <option>Select a registry...</option> --}}
@foreach ($registries as $registry)
<option value="{{ $registry['id'] }}">{{ $registry['name'] }}</option>
@endforeach
</x-forms.select>
<div class="pt-4">
<x-forms.select multiple wire:model="selectedRegistries" id="selectedRegistries"
label="Select Registries"
required="required_if:application.docker_use_custom_registry,true"
helper="Select one or more registries to pull the image from">
@foreach ($registries as $registry)
<option value="{{ $registry->id }}"
{{ in_array($registry->id, $selectedRegistries) ? 'selected' : '' }}>
{{ $registry->name }}
</option>
@endforeach
</x-forms.select>
</div>
@endif
</div>
@ -187,8 +192,7 @@
</div>
<div class="pt-1 text-xs">Nixpacks will detect the required configuration
automatically.
<a class="underline"
href="https://coolify.io/docs/applications">Framework
<a class="underline" href="https://coolify.io/docs/applications">Framework
Specific Docs</a>
</div>
@endif

View file

@ -15,10 +15,11 @@
@if ($useCustomRegistry)
<div class="pt-4">
<x-forms.select id="selectedRegistry" wire:model="selectedRegistry" label="Select Registry" required>
<option value="">Select a registry...</option>
<x-forms.select multiple name="selectedRegistries" id="selectedRegistries"
wire:model="selectedRegistries" label="Select Registries" required>
@foreach ($registries as $registry)
<option value="{{ $registry->id }}">{{ $registry->name }}</option>
<option {{ collect($selectedRegistries)->contains($registry->id) ? 'selected' : '' }}
value="{{ $registry->id }}">{{ $registry->name }}</option>
@endforeach
</x-forms.select>
</div>