wip: services
This commit is contained in:
		@@ -4,6 +4,7 @@ namespace App\Http\Livewire\Project\Application;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Models\Application;
 | 
					use App\Models\Application;
 | 
				
			||||||
use App\Models\InstanceSettings;
 | 
					use App\Models\InstanceSettings;
 | 
				
			||||||
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Spatie\Url\Url;
 | 
					use Spatie\Url\Url;
 | 
				
			||||||
@@ -14,7 +15,7 @@ class General extends Component
 | 
				
			|||||||
    public string $applicationId;
 | 
					    public string $applicationId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Application $application;
 | 
					    public Application $application;
 | 
				
			||||||
    public ?array $services = null;
 | 
					    public Collection $services;
 | 
				
			||||||
    public string $name;
 | 
					    public string $name;
 | 
				
			||||||
    public string|null $fqdn;
 | 
					    public string|null $fqdn;
 | 
				
			||||||
    public string $git_repository;
 | 
					    public string $git_repository;
 | 
				
			||||||
@@ -33,6 +34,7 @@ class General extends Component
 | 
				
			|||||||
    public bool $is_auto_deploy_enabled;
 | 
					    public bool $is_auto_deploy_enabled;
 | 
				
			||||||
    public bool $is_force_https_enabled;
 | 
					    public bool $is_force_https_enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public array $service_configurations = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $rules = [
 | 
					    protected $rules = [
 | 
				
			||||||
        'application.name' => 'required',
 | 
					        'application.name' => 'required',
 | 
				
			||||||
@@ -54,6 +56,8 @@ class General extends Component
 | 
				
			|||||||
        'application.dockercompose_raw' => 'nullable',
 | 
					        'application.dockercompose_raw' => 'nullable',
 | 
				
			||||||
        'application.dockercompose' => 'nullable',
 | 
					        'application.dockercompose' => 'nullable',
 | 
				
			||||||
        'application.service_configurations.*' => 'nullable',
 | 
					        'application.service_configurations.*' => 'nullable',
 | 
				
			||||||
 | 
					        'service_configurations.*.fqdn' => 'nullable|url',
 | 
				
			||||||
 | 
					        'service_configurations.*.port' => 'integer',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    protected $validationAttributes = [
 | 
					    protected $validationAttributes = [
 | 
				
			||||||
        'application.name' => 'name',
 | 
					        'application.name' => 'name',
 | 
				
			||||||
@@ -74,6 +78,8 @@ class General extends Component
 | 
				
			|||||||
        'application.dockerfile' => 'Dockerfile',
 | 
					        'application.dockerfile' => 'Dockerfile',
 | 
				
			||||||
        'application.dockercompose_raw' => 'Docker Compose (raw)',
 | 
					        'application.dockercompose_raw' => 'Docker Compose (raw)',
 | 
				
			||||||
        'application.dockercompose' => 'Docker Compose',
 | 
					        'application.dockercompose' => 'Docker Compose',
 | 
				
			||||||
 | 
					        'service_configurations.*.fqdn' => 'FQDN',
 | 
				
			||||||
 | 
					        'service_configurations.*.port' => 'Port',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,8 +101,8 @@ class General extends Component
 | 
				
			|||||||
        $this->application->settings->save();
 | 
					        $this->application->settings->save();
 | 
				
			||||||
        $this->application->save();
 | 
					        $this->application->save();
 | 
				
			||||||
        $this->application->refresh();
 | 
					        $this->application->refresh();
 | 
				
			||||||
        $this->emit('success', 'Application settings updated!');
 | 
					 | 
				
			||||||
        $this->checkWildCardDomain();
 | 
					        $this->checkWildCardDomain();
 | 
				
			||||||
 | 
					        $this->emit('success', 'Application settings updated!');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected function checkWildCardDomain()
 | 
					    protected function checkWildCardDomain()
 | 
				
			||||||
@@ -109,6 +115,7 @@ class General extends Component
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        $this->services = $this->application->services();
 | 
				
			||||||
        $this->is_static = $this->application->settings->is_static;
 | 
					        $this->is_static = $this->application->settings->is_static;
 | 
				
			||||||
        $this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
 | 
					        $this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
 | 
				
			||||||
        $this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
 | 
					        $this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
 | 
				
			||||||
@@ -117,8 +124,8 @@ class General extends Component
 | 
				
			|||||||
        $this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
 | 
					        $this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
 | 
				
			||||||
        $this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
 | 
					        $this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
 | 
				
			||||||
        $this->checkWildCardDomain();
 | 
					        $this->checkWildCardDomain();
 | 
				
			||||||
        if (data_get($this->application, 'dockercompose_raw')) {
 | 
					        if (data_get($this->application, 'service_configurations')) {
 | 
				
			||||||
            $this->services = data_get(Yaml::parse($this->application->dockercompose_raw), 'services');
 | 
					            $this->service_configurations = $this->application->service_configurations;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -149,8 +156,8 @@ class General extends Component
 | 
				
			|||||||
    public function submit()
 | 
					    public function submit()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            ray($this->application->service_configurations);
 | 
					            $this->application->service_configurations = $this->service_configurations;
 | 
				
			||||||
            // $this->validate();
 | 
					            $this->validate();
 | 
				
			||||||
            if (data_get($this->application, 'fqdn')) {
 | 
					            if (data_get($this->application, 'fqdn')) {
 | 
				
			||||||
                $domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
 | 
					                $domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
 | 
				
			||||||
                    return Str::of($domain)->trim()->lower();
 | 
					                    return Str::of($domain)->trim()->lower();
 | 
				
			||||||
@@ -170,7 +177,7 @@ class General extends Component
 | 
				
			|||||||
                $this->application->publish_directory = rtrim($this->application->publish_directory, '/');
 | 
					                $this->application->publish_directory = rtrim($this->application->publish_directory, '/');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (data_get($this->application, 'dockercompose_raw')) {
 | 
					            if (data_get($this->application, 'dockercompose_raw')) {
 | 
				
			||||||
                $details = generateServiceFromTemplate($this->application->dockercompose_raw, $this->application);
 | 
					                $details = generateServiceFromTemplate( $this->application);
 | 
				
			||||||
                $this->application->dockercompose = data_get($details, 'dockercompose');
 | 
					                $this->application->dockercompose = data_get($details, 'dockercompose');
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            $this->application->save();
 | 
					            $this->application->save();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,7 +64,7 @@ class Heading extends Component
 | 
				
			|||||||
        foreach ($containers as $container) {
 | 
					        foreach ($containers as $container) {
 | 
				
			||||||
            $containerName = data_get($container, 'Names');
 | 
					            $containerName = data_get($container, 'Names');
 | 
				
			||||||
            if ($containerName) {
 | 
					            if ($containerName) {
 | 
				
			||||||
                remote_process(
 | 
					                instant_remote_process(
 | 
				
			||||||
                    ["docker rm -f {$containerName}"],
 | 
					                    ["docker rm -f {$containerName}"],
 | 
				
			||||||
                    $this->application->destination->server
 | 
					                    $this->application->destination->server
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,11 +7,15 @@ use App\Models\EnvironmentVariable;
 | 
				
			|||||||
use App\Models\GithubApp;
 | 
					use App\Models\GithubApp;
 | 
				
			||||||
use App\Models\LocalPersistentVolume;
 | 
					use App\Models\LocalPersistentVolume;
 | 
				
			||||||
use App\Models\Project;
 | 
					use App\Models\Project;
 | 
				
			||||||
 | 
					use App\Models\Service;
 | 
				
			||||||
 | 
					use App\Models\ServiceApplication;
 | 
				
			||||||
 | 
					use App\Models\ServiceDatabase;
 | 
				
			||||||
use App\Models\StandaloneDocker;
 | 
					use App\Models\StandaloneDocker;
 | 
				
			||||||
use App\Models\SwarmDocker;
 | 
					use App\Models\SwarmDocker;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
use Visus\Cuid2\Cuid2;
 | 
					use Visus\Cuid2\Cuid2;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DockerCompose extends Component
 | 
					class DockerCompose extends Component
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -70,68 +74,81 @@ class DockerCompose extends Component
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $project = Project::where('uuid', $this->parameters['project_uuid'])->first();
 | 
					        $project = Project::where('uuid', $this->parameters['project_uuid'])->first();
 | 
				
			||||||
        $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
 | 
					        $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
 | 
				
			||||||
        $application = Application::create([
 | 
					        $service = new Service();
 | 
				
			||||||
            'name' => 'dockercompose-' . new Cuid2(7),
 | 
					        $service->uuid = (string) new Cuid2(7);
 | 
				
			||||||
            'repository_project_id' => 0,
 | 
					        $service->name = 'service-' . new Cuid2(7);
 | 
				
			||||||
            'fqdn' => 'https://app.coolify.io',
 | 
					        $service->docker_compose_raw = $this->dockercompose;
 | 
				
			||||||
            'git_repository' => "coollabsio/coolify",
 | 
					        $service->environment_id = $environment->id;
 | 
				
			||||||
            'git_branch' => 'main',
 | 
					        $service->destination_id = $destination->id;
 | 
				
			||||||
            'build_pack' => 'dockercompose',
 | 
					        $service->destination_type = $destination_class;
 | 
				
			||||||
            'ports_exposes' => '0',
 | 
					        $service->save();
 | 
				
			||||||
            'dockercompose_raw' => $this->dockercompose,
 | 
					        $service->parse(saveIt: true);
 | 
				
			||||||
            'environment_id' => $environment->id,
 | 
					 | 
				
			||||||
            'destination_id' => $destination->id,
 | 
					 | 
				
			||||||
            'destination_type' => $destination_class,
 | 
					 | 
				
			||||||
            'source_id' => 0,
 | 
					 | 
				
			||||||
            'source_type' => GithubApp::class
 | 
					 | 
				
			||||||
        ]);
 | 
					 | 
				
			||||||
        $fqdn = "http://{$application->uuid}.{$destination->server->ip}.sslip.io";
 | 
					 | 
				
			||||||
        if (isDev()) {
 | 
					 | 
				
			||||||
            $fqdn = "http://{$application->uuid}.127.0.0.1.sslip.io";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        $application->update([
 | 
					 | 
				
			||||||
            'name' => 'dockercompose-' . $application->uuid,
 | 
					 | 
				
			||||||
            'fqdn' => $fqdn,
 | 
					 | 
				
			||||||
        ]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $details = generateServiceFromTemplate($this->dockercompose, $application);
 | 
					        return redirect()->route('project.service', [
 | 
				
			||||||
        $envs = data_get($details, 'envs', []);
 | 
					            'service_uuid' => $service->uuid,
 | 
				
			||||||
        if ($envs->count() > 0) {
 | 
					 | 
				
			||||||
            foreach ($envs as $env) {
 | 
					 | 
				
			||||||
                $key = Str::of($env)->before('=');
 | 
					 | 
				
			||||||
                $value = Str::of($env)->after('=');
 | 
					 | 
				
			||||||
                EnvironmentVariable::create([
 | 
					 | 
				
			||||||
                    'key' => $key,
 | 
					 | 
				
			||||||
                    'value' => $value,
 | 
					 | 
				
			||||||
                    'is_build_time' => false,
 | 
					 | 
				
			||||||
                    'application_id' => $application->id,
 | 
					 | 
				
			||||||
                    'is_preview' => false,
 | 
					 | 
				
			||||||
                ]);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        $volumes = data_get($details, 'volumes', []);
 | 
					 | 
				
			||||||
        if ($volumes->count() > 0) {
 | 
					 | 
				
			||||||
            foreach ($volumes as $volume => $mount_path) {
 | 
					 | 
				
			||||||
                LocalPersistentVolume::create([
 | 
					 | 
				
			||||||
                    'name' => $volume,
 | 
					 | 
				
			||||||
                    'mount_path' => $mount_path,
 | 
					 | 
				
			||||||
                    'resource_id' => $application->id,
 | 
					 | 
				
			||||||
                    'resource_type' => $application->getMorphClass(),
 | 
					 | 
				
			||||||
                    'is_readonly' => false
 | 
					 | 
				
			||||||
                ]);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        $dockercompose_coolified = data_get($details, 'dockercompose', '');
 | 
					 | 
				
			||||||
        $application->update([
 | 
					 | 
				
			||||||
            'dockercompose' => $dockercompose_coolified,
 | 
					 | 
				
			||||||
            'ports_exposes' => data_get($details, 'ports', 0)->implode(','),
 | 
					 | 
				
			||||||
        ]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        redirect()->route('project.application.configuration', [
 | 
					 | 
				
			||||||
            'application_uuid' => $application->uuid,
 | 
					 | 
				
			||||||
            'environment_name' => $environment->name,
 | 
					            'environment_name' => $environment->name,
 | 
				
			||||||
            'project_uuid' => $project->uuid,
 | 
					            'project_uuid' => $project->uuid,
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					        // $compose = data_get($parsedService, 'docker_compose');
 | 
				
			||||||
 | 
					        // $service->docker_compose = $compose;
 | 
				
			||||||
 | 
					        // $shouldDefine = data_get($parsedService, 'should_define', collect([]));
 | 
				
			||||||
 | 
					        // if ($shouldDefine->count() > 0) {
 | 
				
			||||||
 | 
					        //     $envs = data_get($shouldDefine, 'envs', []);
 | 
				
			||||||
 | 
					        //     foreach($envs as $env) {
 | 
				
			||||||
 | 
					        //         ray($env);
 | 
				
			||||||
 | 
					        //         $variableName = Str::of($env)->before('=');
 | 
				
			||||||
 | 
					        //         $variableValue = Str::of($env)->after('=');
 | 
				
			||||||
 | 
					        //         ray($variableName, $variableValue);
 | 
				
			||||||
 | 
					        //     }
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        // foreach ($services as $serviceName => $serviceDetails) {
 | 
				
			||||||
 | 
					        //     if (data_get($serviceDetails,'is_database')) {
 | 
				
			||||||
 | 
					        //         $serviceDatabase = new ServiceDatabase();
 | 
				
			||||||
 | 
					        //         $serviceDatabase->name = $serviceName . '-' . $service->uuid;
 | 
				
			||||||
 | 
					        //         $serviceDatabase->service_id = $service->id;
 | 
				
			||||||
 | 
					        //         $serviceDatabase->save();
 | 
				
			||||||
 | 
					        //     } else {
 | 
				
			||||||
 | 
					        //         $serviceApplication = new ServiceApplication();
 | 
				
			||||||
 | 
					        //         $serviceApplication->name = $serviceName . '-' . $service->uuid;
 | 
				
			||||||
 | 
					        //         $serviceApplication->fqdn =
 | 
				
			||||||
 | 
					        //         $serviceApplication->service_id = $service->id;
 | 
				
			||||||
 | 
					        //         $serviceApplication->save();
 | 
				
			||||||
 | 
					        //     }
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // ray($details);
 | 
				
			||||||
 | 
					        // $envs = data_get($details, 'envs', []);
 | 
				
			||||||
 | 
					        // if ($envs->count() > 0) {
 | 
				
			||||||
 | 
					        //     foreach ($envs as $env) {
 | 
				
			||||||
 | 
					        //         $key = Str::of($env)->before('=');
 | 
				
			||||||
 | 
					        //         $value = Str::of($env)->after('=');
 | 
				
			||||||
 | 
					        //         EnvironmentVariable::create([
 | 
				
			||||||
 | 
					        //             'key' => $key,
 | 
				
			||||||
 | 
					        //             'value' => $value,
 | 
				
			||||||
 | 
					        //             'is_build_time' => false,
 | 
				
			||||||
 | 
					        //             'service_id' => $service->id,
 | 
				
			||||||
 | 
					        //             'is_preview' => false,
 | 
				
			||||||
 | 
					        //         ]);
 | 
				
			||||||
 | 
					        //     }
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        // $volumes = data_get($details, 'volumes', []);
 | 
				
			||||||
 | 
					        // if ($volumes->count() > 0) {
 | 
				
			||||||
 | 
					        //     foreach ($volumes as $volume => $mount_path) {
 | 
				
			||||||
 | 
					        //         LocalPersistentVolume::create([
 | 
				
			||||||
 | 
					        //             'name' => $volume,
 | 
				
			||||||
 | 
					        //             'mount_path' => $mount_path,
 | 
				
			||||||
 | 
					        //             'resource_id' => $service->id,
 | 
				
			||||||
 | 
					        //             'resource_type' => $service->getMorphClass(),
 | 
				
			||||||
 | 
					        //             'is_readonly' => false
 | 
				
			||||||
 | 
					        //         ]);
 | 
				
			||||||
 | 
					        //     }
 | 
				
			||||||
 | 
					        // }
 | 
				
			||||||
 | 
					        // $dockercompose_coolified = data_get($details, 'dockercompose', '');
 | 
				
			||||||
 | 
					        // $service->update([
 | 
				
			||||||
 | 
					        //     'docker_compose' => $dockercompose_coolified,
 | 
				
			||||||
 | 
					        // ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								app/Http/Livewire/Project/Service/Index.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/Http/Livewire/Project/Service/Index.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Http\Livewire\Project\Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Service;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Index extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public Service $service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public array $parameters;
 | 
				
			||||||
 | 
					    public array $query;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function mount() {
 | 
				
			||||||
 | 
					        $this->parameters = get_route_parameters();
 | 
				
			||||||
 | 
					        $this->query = request()->query();
 | 
				
			||||||
 | 
					        $this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.project.service.index')->layout('layouts.app');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								app/Http/Livewire/Service/Index.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/Http/Livewire/Service/Index.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Http\Livewire\Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Models\Service;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Index extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public Service $service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public array $parameters;
 | 
				
			||||||
 | 
					    public array $query;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function mount() {
 | 
				
			||||||
 | 
					        $this->parameters = get_route_parameters();
 | 
				
			||||||
 | 
					        $this->query = request()->query();
 | 
				
			||||||
 | 
					        $this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
 | 
				
			||||||
 | 
					        ray($this->service->docker_compose);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.project.service.index')->layout('layouts.app');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -73,6 +73,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        $this->log_model = $this->application_deployment_queue;
 | 
					        $this->log_model = $this->application_deployment_queue;
 | 
				
			||||||
        $this->application = Application::find($this->application_deployment_queue->application_id);
 | 
					        $this->application = Application::find($this->application_deployment_queue->application_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $isService = $this->application->services()->count() > 0;
 | 
				
			||||||
        $this->application_deployment_queue_id = $application_deployment_queue_id;
 | 
					        $this->application_deployment_queue_id = $application_deployment_queue_id;
 | 
				
			||||||
        $this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
 | 
					        $this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
 | 
				
			||||||
        $this->pull_request_id = $this->application_deployment_queue->pull_request_id;
 | 
					        $this->pull_request_id = $this->application_deployment_queue->pull_request_id;
 | 
				
			||||||
@@ -128,7 +129,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            if ($this->application->dockerfile) {
 | 
					            if ($this->application->dockerfile) {
 | 
				
			||||||
                $this->deploy_simple_dockerfile();
 | 
					                $this->deploy_simple_dockerfile();
 | 
				
			||||||
            } else if($this->application->dockercompose) {
 | 
					            } else if ($this->application->services()->count() > 0) {
 | 
				
			||||||
                $this->deploy_docker_compose();
 | 
					                $this->deploy_docker_compose();
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                if ($this->pull_request_id !== 0) {
 | 
					                if ($this->pull_request_id !== 0) {
 | 
				
			||||||
@@ -168,7 +169,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private function deploy_docker_compose() {
 | 
					    private function deploy_docker_compose()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        $dockercompose_base64 = base64_encode($this->application->dockercompose);
 | 
					        $dockercompose_base64 = base64_encode($this->application->dockercompose);
 | 
				
			||||||
        $this->execute_remote_command(
 | 
					        $this->execute_remote_command(
 | 
				
			||||||
            [
 | 
					            [
 | 
				
			||||||
@@ -184,9 +186,26 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
        $this->build_image_name = Str::lower("{$this->application->git_repository}:build");
 | 
					        $this->build_image_name = Str::lower("{$this->application->git_repository}:build");
 | 
				
			||||||
        $this->production_image_name = Str::lower("{$this->application->uuid}:latest");
 | 
					        $this->production_image_name = Str::lower("{$this->application->uuid}:latest");
 | 
				
			||||||
        $this->save_environment_variables();
 | 
					        $this->save_environment_variables();
 | 
				
			||||||
        $this->start_by_compose_file();
 | 
					        $containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id);
 | 
				
			||||||
 | 
					        if ($containers->count() > 0) {
 | 
				
			||||||
 | 
					            foreach ($containers as $container) {
 | 
				
			||||||
 | 
					                $containerName = data_get($container, 'Names');
 | 
				
			||||||
 | 
					                if ($containerName) {
 | 
				
			||||||
 | 
					                    instant_remote_process(
 | 
				
			||||||
 | 
					                        ["docker rm -f {$containerName}"],
 | 
				
			||||||
 | 
					                        $this->application->destination->server
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
    private function save_environment_variables() {
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $this->execute_remote_command(
 | 
				
			||||||
 | 
					            ["echo -n 'Starting services (could take a while)...'"],
 | 
				
			||||||
 | 
					            [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private function save_environment_variables()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        $envs = collect([]);
 | 
					        $envs = collect([]);
 | 
				
			||||||
        foreach ($this->application->environment_variables as $env) {
 | 
					        foreach ($this->application->environment_variables as $env) {
 | 
				
			||||||
            $envs->push($env->key . '=' . $env->value);
 | 
					            $envs->push($env->key . '=' . $env->value);
 | 
				
			||||||
@@ -197,7 +216,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
 | 
				
			|||||||
                executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
 | 
					                executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private function deploy_simple_dockerfile()
 | 
					    private function deploy_simple_dockerfile()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,12 +4,17 @@ namespace App\Models;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
					use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
				
			||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
 | 
					use Illuminate\Database\Eloquent\Relations\HasMany;
 | 
				
			||||||
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
use Spatie\Activitylog\Models\Activity;
 | 
					use Spatie\Activitylog\Models\Activity;
 | 
				
			||||||
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Application extends BaseModel
 | 
					class Application extends BaseModel
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected $guarded = [];
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					    protected $casts = [
 | 
				
			||||||
 | 
					        'service_configurations' => 'array',
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
    protected static function booted()
 | 
					    protected static function booted()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        static::created(function ($application) {
 | 
					        static::created(function ($application) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,12 +33,14 @@ class EnvironmentVariable extends Model
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function service() {
 | 
				
			||||||
 | 
					        return $this->belongsTo(Service::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    protected function value(): Attribute
 | 
					    protected function value(): Attribute
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return Attribute::make(
 | 
					        return Attribute::make(
 | 
				
			||||||
            get: fn (string $value) => $this->get_environment_variables($value),
 | 
					            get: fn (?string $value = null) => $this->get_environment_variables($value),
 | 
				
			||||||
            set: fn (string $value) => $this->set_environment_variables($value),
 | 
					            set: fn (?string $value = null) => $this->set_environment_variables($value),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -57,8 +59,11 @@ class EnvironmentVariable extends Model
 | 
				
			|||||||
        return $environment_variable;
 | 
					        return $environment_variable;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function set_environment_variables(string $environment_variable): string|null
 | 
					    private function set_environment_variables(?string $environment_variable = null): string|null
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        if (is_null($environment_variable) && $environment_variable == '') {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $environment_variable = trim($environment_variable);
 | 
					        $environment_variable = trim($environment_variable);
 | 
				
			||||||
        return encrypt($environment_variable);
 | 
					        return encrypt($environment_variable);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -69,4 +74,5 @@ class EnvironmentVariable extends Model
 | 
				
			|||||||
            set: fn (string $value) => Str::of($value)->trim(),
 | 
					            set: fn (string $value) => Str::of($value)->trim(),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,10 @@ class LocalPersistentVolume extends Model
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->morphTo();
 | 
					        return $this->morphTo();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    public function service()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphTo();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function standalone_postgresql()
 | 
					    public function standalone_postgresql()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,6 +76,9 @@ class Server extends BaseModel
 | 
				
			|||||||
        return $this->hasOne(ServerSetting::class);
 | 
					        return $this->hasOne(ServerSetting::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function proxyType() {
 | 
				
			||||||
 | 
					        return $this->proxy->get('type');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    public function scopeWithProxy(): Builder
 | 
					    public function scopeWithProxy(): Builder
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->proxy->modelScope();
 | 
					        return $this->proxy->modelScope();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										267
									
								
								app/Models/Service.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								app/Models/Service.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,267 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Casts\Attribute;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Relations\HasMany;
 | 
				
			||||||
 | 
					use Illuminate\Support\Collection;
 | 
				
			||||||
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Service extends BaseModel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasFactory;
 | 
				
			||||||
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					    public function destination()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphTo();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function persistentStorages()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->morphMany(LocalPersistentVolume::class, 'resource');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function portsMappingsArray(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            get: fn () => is_null($this->ports_mappings)
 | 
				
			||||||
 | 
					                ? []
 | 
				
			||||||
 | 
					                : explode(',', $this->ports_mappings),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function portsExposesArray(): Attribute
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Attribute::make(
 | 
				
			||||||
 | 
					            get: fn () => is_null($this->ports_exposes)
 | 
				
			||||||
 | 
					                ? []
 | 
				
			||||||
 | 
					                : explode(',', $this->ports_exposes)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function applications()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(ServiceApplication::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function databases()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(ServiceDatabase::class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function environment_variables(): HasMany
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public function parse(bool $saveIt = false): Collection
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if ($this->docker_compose_raw) {
 | 
				
			||||||
 | 
					            ray()->clearAll();
 | 
				
			||||||
 | 
					            $yaml = Yaml::parse($this->docker_compose_raw);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $composeVolumes = collect(data_get($yaml, 'volumes', []));
 | 
				
			||||||
 | 
					            $composeNetworks = collect(data_get($yaml, 'networks', []));
 | 
				
			||||||
 | 
					            $services = data_get($yaml, 'services');
 | 
				
			||||||
 | 
					            $definedNetwork = data_get($this, 'destination.network');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $volumes = collect([]);
 | 
				
			||||||
 | 
					            $envs = collect([]);
 | 
				
			||||||
 | 
					            $ports = collect([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $services = collect($services)->map(function ($service, $serviceName) use ($composeVolumes, $composeNetworks, $definedNetwork, $envs, $volumes, $ports, $saveIt) {
 | 
				
			||||||
 | 
					                $isDatabase = false;
 | 
				
			||||||
 | 
					                // Decide if the service is a database
 | 
				
			||||||
 | 
					                $image = data_get($service, 'image');
 | 
				
			||||||
 | 
					                if ($image) {
 | 
				
			||||||
 | 
					                    $imageName = Str::of($image)->before(':');
 | 
				
			||||||
 | 
					                    if (collect(DATABASE_DOCKER_IMAGES)->contains($imageName)) {
 | 
				
			||||||
 | 
					                        $isDatabase = true;
 | 
				
			||||||
 | 
					                        data_set($service, 'is_database', true);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ($saveIt) {
 | 
				
			||||||
 | 
					                    if ($isDatabase) {
 | 
				
			||||||
 | 
					                        $savedService = ServiceDatabase::create([
 | 
				
			||||||
 | 
					                            'name' => $serviceName,
 | 
				
			||||||
 | 
					                            'service_id' => $this->id
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $savedService = ServiceApplication::create([
 | 
				
			||||||
 | 
					                            'name' => $serviceName,
 | 
				
			||||||
 | 
					                            'service_id' => $this->id
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // Collect ports
 | 
				
			||||||
 | 
					                $servicePorts = collect(data_get($service, 'ports', []));
 | 
				
			||||||
 | 
					                $ports->put($serviceName, $servicePorts);
 | 
				
			||||||
 | 
					                if ($saveIt) {
 | 
				
			||||||
 | 
					                    $savedService->ports_exposes = $servicePorts->implode(',');
 | 
				
			||||||
 | 
					                    $savedService->save();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // Collect volumes
 | 
				
			||||||
 | 
					                $serviceVolumes = collect(data_get($service, 'volumes', []));
 | 
				
			||||||
 | 
					                if ($serviceVolumes->count() > 0) {
 | 
				
			||||||
 | 
					                    foreach ($serviceVolumes as $volume) {
 | 
				
			||||||
 | 
					                        if (is_string($volume)) {
 | 
				
			||||||
 | 
					                            $volumeName = Str::before($volume, ':');
 | 
				
			||||||
 | 
					                            $volumePath = Str::after($volume, ':');
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        if (is_array($volume)) {
 | 
				
			||||||
 | 
					                            $volumeName = data_get($volume, 'source');
 | 
				
			||||||
 | 
					                            $volumePath = data_get($volume, 'target');
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        $volumeExists = $serviceVolumes->contains(function ($_, $key) use ($volumeName) {
 | 
				
			||||||
 | 
					                            return $key == $volumeName;
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        if (!$volumeExists) {
 | 
				
			||||||
 | 
					                            if (!Str::startsWith($volumeName, '/')) {
 | 
				
			||||||
 | 
					                                $composeVolumes->put($volumeName, null);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            $volumes->put($volumeName, $volumePath);
 | 
				
			||||||
 | 
					                            if ($saveIt) {
 | 
				
			||||||
 | 
					                                LocalPersistentVolume::create([
 | 
				
			||||||
 | 
					                                    'name' => $volumeName,
 | 
				
			||||||
 | 
					                                    'mount_path' => $volumePath,
 | 
				
			||||||
 | 
					                                    'resource_id' => $savedService->id,
 | 
				
			||||||
 | 
					                                    'resource_type' => get_class($savedService)
 | 
				
			||||||
 | 
					                                ]);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Collect and add networks
 | 
				
			||||||
 | 
					                $serviceNetworks = collect(data_get($service, 'networks', []));
 | 
				
			||||||
 | 
					                if ($serviceNetworks->count() > 0) {
 | 
				
			||||||
 | 
					                    foreach ($serviceNetworks as $networkName => $networkDetails) {
 | 
				
			||||||
 | 
					                        $networkExists = $composeNetworks->contains(function ($value, $key) use ($networkName) {
 | 
				
			||||||
 | 
					                            return $value == $networkName || $key == $networkName;
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        if (!$networkExists) {
 | 
				
			||||||
 | 
					                            $composeNetworks->put($networkName, null);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // Add Coolify specific networks
 | 
				
			||||||
 | 
					                $definedNetworkExists = $composeNetworks->contains(function ($value, $_) use ($definedNetwork) {
 | 
				
			||||||
 | 
					                    return $value == $definedNetwork;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                if (!$definedNetworkExists) {
 | 
				
			||||||
 | 
					                    $composeNetworks->put($definedNetwork, [
 | 
				
			||||||
 | 
					                        'external' => true
 | 
				
			||||||
 | 
					                    ]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Get variables from the service that does not start with SERVICE_*
 | 
				
			||||||
 | 
					                $serviceVariables = collect(data_get($service, 'environment', []));
 | 
				
			||||||
 | 
					                foreach ($serviceVariables as $variable) {
 | 
				
			||||||
 | 
					                    $value = Str::after($variable, '=');
 | 
				
			||||||
 | 
					                    if (!Str::startsWith($value, '$SERVICE_') && !Str::startsWith($value, '${SERVICE_') && Str::startsWith($value, '$')) {
 | 
				
			||||||
 | 
					                        $value = Str::of(replaceVariables(Str::of($value)));
 | 
				
			||||||
 | 
					                        if ($value->contains(':')) {
 | 
				
			||||||
 | 
					                            $nakedName = $value->before(':');
 | 
				
			||||||
 | 
					                            $nakedValue = $value->after(':');
 | 
				
			||||||
 | 
					                        } else if ($value->contains('-')) {
 | 
				
			||||||
 | 
					                            $nakedName = $value->before('-');
 | 
				
			||||||
 | 
					                            $nakedValue = $value->after('-');
 | 
				
			||||||
 | 
					                        } else if ($value->contains('+')) {
 | 
				
			||||||
 | 
					                            $nakedName = $value->before('+');
 | 
				
			||||||
 | 
					                            $nakedValue = $value->after('+');
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            $nakedName = $value;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        if (isset($nakedName)) {
 | 
				
			||||||
 | 
					                            if (isset($nakedValue)) {
 | 
				
			||||||
 | 
					                                if ($nakedValue->startsWith('-')) {
 | 
				
			||||||
 | 
					                                    $nakedValue = Str::of($nakedValue)->after('-');
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                if ($nakedValue->startsWith('+')) {
 | 
				
			||||||
 | 
					                                    $nakedValue = Str::of($nakedValue)->after('+');
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                if (!$envs->has($nakedName->value())) {
 | 
				
			||||||
 | 
					                                    $envs->put($nakedName->value(), $nakedValue->value());
 | 
				
			||||||
 | 
					                                    if ($saveIt) {
 | 
				
			||||||
 | 
					                                        EnvironmentVariable::create([
 | 
				
			||||||
 | 
					                                            'key' => $nakedName->value(),
 | 
				
			||||||
 | 
					                                            'value' => $nakedValue->value(),
 | 
				
			||||||
 | 
					                                            'is_build_time' => false,
 | 
				
			||||||
 | 
					                                            'service_id' => $this->id,
 | 
				
			||||||
 | 
					                                            'is_preview' => false,
 | 
				
			||||||
 | 
					                                        ]);
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                if (!$envs->has($nakedName->value())) {
 | 
				
			||||||
 | 
					                                    $envs->put($nakedName->value(), null);
 | 
				
			||||||
 | 
					                                    if ($saveIt) {
 | 
				
			||||||
 | 
					                                        EnvironmentVariable::create([
 | 
				
			||||||
 | 
					                                            'key' => $nakedName->value(),
 | 
				
			||||||
 | 
					                                            'value' => null,
 | 
				
			||||||
 | 
					                                            'is_build_time' => false,
 | 
				
			||||||
 | 
					                                            'service_id' => $this->id,
 | 
				
			||||||
 | 
					                                            'is_preview' => false,
 | 
				
			||||||
 | 
					                                        ]);
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        $value = Str::of(replaceVariables(Str::of($value)));
 | 
				
			||||||
 | 
					                        $generatedValue = null;
 | 
				
			||||||
 | 
					                        if ($value->startsWith('SERVICE_USER')) {
 | 
				
			||||||
 | 
					                            $generatedValue = Str::random(10);
 | 
				
			||||||
 | 
					                            if ($saveIt) {
 | 
				
			||||||
 | 
					                                if (!$envs->has($value->value())) {
 | 
				
			||||||
 | 
					                                    $envs->put($value->value(), $generatedValue);
 | 
				
			||||||
 | 
					                                    EnvironmentVariable::create([
 | 
				
			||||||
 | 
					                                        'key' => $value->value(),
 | 
				
			||||||
 | 
					                                        'value' => $generatedValue,
 | 
				
			||||||
 | 
					                                        'is_build_time' => false,
 | 
				
			||||||
 | 
					                                        'service_id' => $this->id,
 | 
				
			||||||
 | 
					                                        'is_preview' => false,
 | 
				
			||||||
 | 
					                                    ]);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else if ($value->startsWith('SERVICE_PASSWORD')) {
 | 
				
			||||||
 | 
					                            $generatedValue = Str::password(symbols: false);
 | 
				
			||||||
 | 
					                            if ($saveIt) {
 | 
				
			||||||
 | 
					                                if (!$envs->has($value->value())) {
 | 
				
			||||||
 | 
					                                    $envs->put($value->value(), $generatedValue);
 | 
				
			||||||
 | 
					                                    EnvironmentVariable::create([
 | 
				
			||||||
 | 
					                                        'key' => $value->value(),
 | 
				
			||||||
 | 
					                                        'value' => $generatedValue,
 | 
				
			||||||
 | 
					                                        'is_build_time' => false,
 | 
				
			||||||
 | 
					                                        'service_id' => $this->id,
 | 
				
			||||||
 | 
					                                        'is_preview' => false,
 | 
				
			||||||
 | 
					                                    ]);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                data_forget($service, 'is_database');
 | 
				
			||||||
 | 
					                data_forget($service, 'documentation');
 | 
				
			||||||
 | 
					                return $service;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            data_set($services, 'volumes', $composeVolumes->toArray());
 | 
				
			||||||
 | 
					            data_set($services, 'networks', $composeNetworks->toArray());
 | 
				
			||||||
 | 
					            $this->docker_compose = Yaml::parse($services);
 | 
				
			||||||
 | 
					            // $compose = Str::of(Yaml::dump($services, 10, 2));
 | 
				
			||||||
 | 
					            // TODO: Replace SERVICE_FQDN_* with the actual FQDN
 | 
				
			||||||
 | 
					            // TODO: Replace SERVICE_URL_*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $shouldBeDefined = collect([
 | 
				
			||||||
 | 
					                'envs' => $envs,
 | 
				
			||||||
 | 
					                'volumes' => $volumes,
 | 
				
			||||||
 | 
					                'ports' => $ports
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					            $parsedCompose = collect([
 | 
				
			||||||
 | 
					                'dockerCompose' => $services,
 | 
				
			||||||
 | 
					                'shouldBeDefined' => $shouldBeDefined
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					            return $parsedCompose;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return collect([]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								app/Models/ServiceApplication.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/Models/ServiceApplication.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceApplication extends BaseModel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasFactory;
 | 
				
			||||||
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								app/Models/ServiceDatabase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/Models/ServiceDatabase.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceDatabase extends BaseModel
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    use HasFactory;
 | 
				
			||||||
 | 
					    protected $guarded = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,6 +21,11 @@ class StandaloneDocker extends BaseModel
 | 
				
			|||||||
        return $this->belongsTo(Server::class);
 | 
					        return $this->belongsTo(Server::class);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function service()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return $this->belongsTo(Service::class, 'destination');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function attachedTo()
 | 
					    public function attachedTo()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->applications?->count() > 0 || $this->databases?->count() > 0;
 | 
					        return $this->applications?->count() > 0 || $this->databases?->count() > 0;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,7 @@ class Textarea extends Component
 | 
				
			|||||||
        public bool        $readonly = false,
 | 
					        public bool        $readonly = false,
 | 
				
			||||||
        public string|null $helper = null,
 | 
					        public string|null $helper = null,
 | 
				
			||||||
        public bool        $realtimeValidation = false,
 | 
					        public bool        $realtimeValidation = false,
 | 
				
			||||||
        public string      $defaultClass = "textarea bg-coolgray-200 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
 | 
					        public string      $defaultClass = "textarea leading-normal bg-coolgray-200 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,3 +10,16 @@ const VALID_CRON_STRINGS = [
 | 
				
			|||||||
    'yearly' => '0 0 1 1 *',
 | 
					    'yearly' => '0 0 1 1 *',
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
const RESTART_MODE = 'unless-stopped';
 | 
					const RESTART_MODE = 'unless-stopped';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DATABASE_DOCKER_IMAGES = [
 | 
				
			||||||
 | 
					    'mysql',
 | 
				
			||||||
 | 
					    'mariadb',
 | 
				
			||||||
 | 
					    'postgres',
 | 
				
			||||||
 | 
					    'mongo',
 | 
				
			||||||
 | 
					    'redis',
 | 
				
			||||||
 | 
					    'memcached',
 | 
				
			||||||
 | 
					    'couchdb',
 | 
				
			||||||
 | 
					    'neo4j',
 | 
				
			||||||
 | 
					    'influxdb',
 | 
				
			||||||
 | 
					    'clickhouse'
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -130,33 +130,23 @@ function get_port_from_dockerfile($dockerfile): int
 | 
				
			|||||||
    return 80;
 | 
					    return 80;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null)
 | 
					function defaultLabels($id, $name, $pull_request_id = 0)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    $labels = collect([]);
 | 
				
			||||||
    $pull_request_id = data_get($preview, 'pull_request_id', 0);
 | 
					    $labels->push('coolify.managed=true');
 | 
				
			||||||
    $container_name = generateApplicationContainerName($application);
 | 
					    $labels->push('coolify.version=' . config('version'));
 | 
				
			||||||
    $appId = $application->id;
 | 
					    $labels->push('coolify.applicationId=' . $id);
 | 
				
			||||||
 | 
					    $labels->push('coolify.type=application');
 | 
				
			||||||
 | 
					    $labels->push('coolify.name=' . $name);
 | 
				
			||||||
    if ($pull_request_id !== 0) {
 | 
					    if ($pull_request_id !== 0) {
 | 
				
			||||||
        $appId = $appId . '-pr-' . $application->pull_request_id;
 | 
					        $labels->push('coolify.pullRequestId=' . $pull_request_id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    $labels = [];
 | 
					    return $labels;
 | 
				
			||||||
    $labels[] = 'coolify.managed=true';
 | 
					 | 
				
			||||||
    $labels[] = 'coolify.version=' . config('version');
 | 
					 | 
				
			||||||
    $labels[] = 'coolify.applicationId=' . $appId;
 | 
					 | 
				
			||||||
    $labels[] = 'coolify.type=application';
 | 
					 | 
				
			||||||
    $labels[] = 'coolify.name=' . $application->name;
 | 
					 | 
				
			||||||
    if ($pull_request_id !== 0) {
 | 
					 | 
				
			||||||
        $labels[] = 'coolify.pullRequestId=' . $pull_request_id;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
    if ($application->fqdn) {
 | 
					function fqdnLabelsForTraefik($domain, $container_name, $is_force_https_enabled)
 | 
				
			||||||
        if ($pull_request_id !== 0) {
 | 
					{
 | 
				
			||||||
            $domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
 | 
					    $labels = collect([]);
 | 
				
			||||||
        } else {
 | 
					    $labels->push('traefik.enable=true');
 | 
				
			||||||
            $domains = Str::of(data_get($application, 'fqdn'))->explode(',');
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if ($application->destination->server->proxy->type === ProxyTypes::TRAEFIK_V2->value) {
 | 
					 | 
				
			||||||
            $labels[] = 'traefik.enable=true';
 | 
					 | 
				
			||||||
            foreach ($domains as $domain) {
 | 
					 | 
				
			||||||
    $url = Url::fromString($domain);
 | 
					    $url = Url::fromString($domain);
 | 
				
			||||||
    $host = $url->getHost();
 | 
					    $host = $url->getHost();
 | 
				
			||||||
    $path = $url->getPath();
 | 
					    $path = $url->getPath();
 | 
				
			||||||
@@ -168,35 +158,95 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if ($schema === 'https') {
 | 
					    if ($schema === 'https') {
 | 
				
			||||||
        // Set labels for https
 | 
					        // Set labels for https
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)";
 | 
					        $labels->push("traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$https_label}.entryPoints=https";
 | 
					        $labels->push("traefik.http.routers.{$https_label}.entryPoints=https");
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$https_label}.middlewares=gzip";
 | 
					        $labels->push("traefik.http.routers.{$https_label}.middlewares=gzip");
 | 
				
			||||||
        if ($path !== '/') {
 | 
					        if ($path !== '/') {
 | 
				
			||||||
                        $labels[] = "traefik.http.routers.{$https_label}.middlewares={$https_label}-stripprefix";
 | 
					            $labels->push("traefik.http.routers.{$https_label}.middlewares={$https_label}-stripprefix");
 | 
				
			||||||
                        $labels[] = "traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}";
 | 
					            $labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$https_label}.tls=true";
 | 
					        $labels->push("traefik.http.routers.{$https_label}.tls=true");
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt";
 | 
					        $labels->push("traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set labels for http (redirect to https)
 | 
					        // Set labels for http (redirect to https)
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)";
 | 
					        $labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$http_label}.entryPoints=http";
 | 
					        $labels->push("traefik.http.routers.{$http_label}.entryPoints=http");
 | 
				
			||||||
                    if ($application->settings->is_force_https_enabled) {
 | 
					        if ($is_force_https_enabled) {
 | 
				
			||||||
                        $labels[] = "traefik.http.routers.{$http_label}.middlewares=redirect-to-https";
 | 
					            $labels->push("traefik.http.routers.{$http_label}.middlewares=redirect-to-https");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        // Set labels for http
 | 
					        // Set labels for http
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)";
 | 
					        $labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$http_label}.entryPoints=http";
 | 
					        $labels->push("traefik.http.routers.{$http_label}.entryPoints=http");
 | 
				
			||||||
                    $labels[] = "traefik.http.routers.{$http_label}.middlewares=gzip";
 | 
					        $labels->push("traefik.http.routers.{$http_label}.middlewares=gzip");
 | 
				
			||||||
        if ($path !== '/') {
 | 
					        if ($path !== '/') {
 | 
				
			||||||
                        $labels[] = "traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix";
 | 
					            $labels->push("traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix");
 | 
				
			||||||
                        $labels[] = "traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}";
 | 
					            $labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return $labels;
 | 
					    return $labels;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $pull_request_id = data_get($preview, 'pull_request_id', 0);
 | 
				
			||||||
 | 
					    $container_name = generateApplicationContainerName($application);
 | 
				
			||||||
 | 
					    $appId = $application->id;
 | 
				
			||||||
 | 
					    if ($pull_request_id !== 0) {
 | 
				
			||||||
 | 
					        $appId = $appId . '-pr-' . $application->pull_request_id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    $labels = collect([]);
 | 
				
			||||||
 | 
					    $labels = $labels->merge(defaultLabels($appId, $container_name, $pull_request_id));
 | 
				
			||||||
 | 
					    if ($application->fqdn) {
 | 
				
			||||||
 | 
					        if ($pull_request_id !== 0) {
 | 
				
			||||||
 | 
					            $domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $domains = Str::of(data_get($application, 'fqdn'))->explode(',');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($application->destination->server->proxy->type === ProxyTypes::TRAEFIK_V2->value) {
 | 
				
			||||||
 | 
					            foreach ($domains as $domain) {
 | 
				
			||||||
 | 
					                $labels = $labels->merge(fqdnLabelsForTraefik($domain, $container_name, $application->settings->is_force_https_enabled));
 | 
				
			||||||
 | 
					                // $url = Url::fromString($domain);
 | 
				
			||||||
 | 
					                // $host = $url->getHost();
 | 
				
			||||||
 | 
					                // $path = $url->getPath();
 | 
				
			||||||
 | 
					                // $schema = $url->getScheme();
 | 
				
			||||||
 | 
					                // $slug = Str::slug($host . $path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // $http_label = "{$container_name}-{$slug}-http";
 | 
				
			||||||
 | 
					                // $https_label = "{$container_name}-{$slug}-https";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // if ($schema === 'https') {
 | 
				
			||||||
 | 
					                //     // Set labels for https
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)";
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$https_label}.entryPoints=https";
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$https_label}.middlewares=gzip";
 | 
				
			||||||
 | 
					                //     if ($path !== '/') {
 | 
				
			||||||
 | 
					                //         $labels[] = "traefik.http.routers.{$https_label}.middlewares={$https_label}-stripprefix";
 | 
				
			||||||
 | 
					                //         $labels[] = "traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}";
 | 
				
			||||||
 | 
					                //     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$https_label}.tls=true";
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                //     // Set labels for http (redirect to https)
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)";
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$http_label}.entryPoints=http";
 | 
				
			||||||
 | 
					                //     if ($application->settings->is_force_https_enabled) {
 | 
				
			||||||
 | 
					                //         $labels[] = "traefik.http.routers.{$http_label}.middlewares=redirect-to-https";
 | 
				
			||||||
 | 
					                //     }
 | 
				
			||||||
 | 
					                // } else {
 | 
				
			||||||
 | 
					                //     // Set labels for http
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)";
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$http_label}.entryPoints=http";
 | 
				
			||||||
 | 
					                //     $labels[] = "traefik.http.routers.{$http_label}.middlewares=gzip";
 | 
				
			||||||
 | 
					                //     if ($path !== '/') {
 | 
				
			||||||
 | 
					                //         $labels[] = "traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix";
 | 
				
			||||||
 | 
					                //         $labels[] = "traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}";
 | 
				
			||||||
 | 
					                //     }
 | 
				
			||||||
 | 
					                // }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return $labels->all();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
<?php
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Enums\ProxyTypes;
 | 
				
			||||||
use App\Models\Application;
 | 
					use App\Models\Application;
 | 
				
			||||||
 | 
					use App\Models\Service;
 | 
				
			||||||
use Symfony\Component\Yaml\Yaml;
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
use Illuminate\Support\Str;
 | 
					use Illuminate\Support\Str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -10,24 +12,31 @@ use Illuminate\Support\Str;
 | 
				
			|||||||
# SERVICE_USER_*: Generated by your application, username (not encrypted)
 | 
					# SERVICE_USER_*: Generated by your application, username (not encrypted)
 | 
				
			||||||
# SERVICE_PASSWORD_*: Generated by your application, password (encrypted)
 | 
					# SERVICE_PASSWORD_*: Generated by your application, password (encrypted)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function generateServiceFromTemplate(string $template, Application $application)
 | 
					
 | 
				
			||||||
 | 
					function generateServiceFromTemplate(Service $service)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // ray()->clearAll();
 | 
					    // ray()->clearAll();
 | 
				
			||||||
 | 
					    $template = data_get($service, 'docker_compose_raw');
 | 
				
			||||||
    $template = Str::of($template);
 | 
					    $network = data_get($service, 'destination.network');
 | 
				
			||||||
    $network = data_get($application, 'destination.network');
 | 
					 | 
				
			||||||
    $yaml = Yaml::parse($template);
 | 
					    $yaml = Yaml::parse($template);
 | 
				
			||||||
    $services = data_get($yaml, 'services');
 | 
					
 | 
				
			||||||
 | 
					    $services = $service->parse();
 | 
				
			||||||
    $volumes = collect(data_get($yaml, 'volumes', []));
 | 
					    $volumes = collect(data_get($yaml, 'volumes', []));
 | 
				
			||||||
    $composeVolumes = collect([]);
 | 
					    $composeVolumes = collect([]);
 | 
				
			||||||
    $env = collect([]);
 | 
					    $env = collect([]);
 | 
				
			||||||
    $ports = collect([]);
 | 
					    $ports = collect([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    foreach ($services as $serviceName => $service) {
 | 
					    foreach ($services as $serviceName => $service) {
 | 
				
			||||||
 | 
					        $container_name = generateApplicationContainerName($application);
 | 
				
			||||||
 | 
					        $domain = data_get($application, "service_configurations.{$serviceName}.fqdn", null);
 | 
				
			||||||
 | 
					        if ($domain === '') {
 | 
				
			||||||
 | 
					            $domain = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        data_forget($service, 'documentation');
 | 
				
			||||||
        // Some default things
 | 
					        // Some default things
 | 
				
			||||||
        data_set($service, 'restart', RESTART_MODE);
 | 
					        data_set($service, 'restart', RESTART_MODE);
 | 
				
			||||||
        data_set($service, 'container_name', generateApplicationContainerName($application));
 | 
					        data_set($service, 'container_name', $container_name);
 | 
				
			||||||
        $healthcheck = data_get($service, 'healthcheck', []);
 | 
					        $healthcheck = data_get($service, 'healthcheck');
 | 
				
			||||||
        if (is_null($healthcheck)) {
 | 
					        if (is_null($healthcheck)) {
 | 
				
			||||||
            $healthcheck = [
 | 
					            $healthcheck = [
 | 
				
			||||||
                'test' => [
 | 
					                'test' => [
 | 
				
			||||||
@@ -41,6 +50,22 @@ function generateServiceFromTemplate(string $template, Application $application)
 | 
				
			|||||||
            ];
 | 
					            ];
 | 
				
			||||||
            data_set($service, 'healthcheck', $healthcheck);
 | 
					            data_set($service, 'healthcheck', $healthcheck);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        // Labels
 | 
				
			||||||
 | 
					        $server = data_get($application, 'destination.server');
 | 
				
			||||||
 | 
					        if ($server->proxyType() === ProxyTypes::TRAEFIK_V2->value) {
 | 
				
			||||||
 | 
					            $labels = collect(data_get($service, 'labels', []));
 | 
				
			||||||
 | 
					            $labels = collect([]);
 | 
				
			||||||
 | 
					            $labels = $labels->merge(defaultLabels($application->id, $container_name));
 | 
				
			||||||
 | 
					            if (!data_get($service, 'is_database')) {
 | 
				
			||||||
 | 
					                if ($domain) {
 | 
				
			||||||
 | 
					                    $labels = $labels->merge(fqdnLabelsForTraefik($domain, $container_name, $application->settings->is_force_https_enabled));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            data_set($service, 'labels', $labels->toArray());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data_forget($service, 'is_database');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add volumes to the volumes collection if they don't already exist
 | 
					        // Add volumes to the volumes collection if they don't already exist
 | 
				
			||||||
        $serviceVolumes = collect(data_get($service, 'volumes', []));
 | 
					        $serviceVolumes = collect(data_get($service, 'volumes', []));
 | 
				
			||||||
@@ -76,7 +101,7 @@ function generateServiceFromTemplate(string $template, Application $application)
 | 
				
			|||||||
        // Get variables from the service that does not start with SERVICE_*
 | 
					        // Get variables from the service that does not start with SERVICE_*
 | 
				
			||||||
        $serviceVariables = collect(data_get($service, 'environment', []));
 | 
					        $serviceVariables = collect(data_get($service, 'environment', []));
 | 
				
			||||||
        foreach ($serviceVariables as $variable) {
 | 
					        foreach ($serviceVariables as $variable) {
 | 
				
			||||||
            $key = Str::before($variable, '=');
 | 
					            // $key = Str::before($variable, '=');
 | 
				
			||||||
            $value = Str::after($variable, '=');
 | 
					            $value = Str::after($variable, '=');
 | 
				
			||||||
            if (!Str::startsWith($value, '$SERVICE_') && !Str::startsWith($value, '${SERVICE_') && Str::startsWith($value, '$')) {
 | 
					            if (!Str::startsWith($value, '$SERVICE_') && !Str::startsWith($value, '${SERVICE_') && Str::startsWith($value, '$')) {
 | 
				
			||||||
                if (Str::of($value)->contains(':')) {
 | 
					                if (Str::of($value)->contains(':')) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
<?php
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use Illuminate\Database\Migrations\Migration;
 | 
					 | 
				
			||||||
use Illuminate\Database\Schema\Blueprint;
 | 
					 | 
				
			||||||
use Illuminate\Support\Facades\Schema;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return new class extends Migration
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Run the migrations.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function up(): void
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Schema::table('applications', function (Blueprint $table) {
 | 
					 | 
				
			||||||
            $table->longText('dockercompose_raw')->nullable();
 | 
					 | 
				
			||||||
            $table->longText('dockercompose')->nullable();
 | 
					 | 
				
			||||||
            $table->json('service_configurations')->nullable();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function down(): void
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        Schema::table('applications', function (Blueprint $table) {
 | 
					 | 
				
			||||||
            $table->dropColumn('dockercompose_raw');
 | 
					 | 
				
			||||||
            $table->dropColumn('dockercompose');
 | 
					 | 
				
			||||||
            $table->dropColumn('service_configurations');
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('services', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->longText('docker_compose_raw');
 | 
				
			||||||
 | 
					            $table->longText('docker_compose')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('services', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('docker_compose_raw');
 | 
				
			||||||
 | 
					            $table->dropColumn('docker_compose');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::create('service_databases', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->id();
 | 
				
			||||||
 | 
					            $table->string('uuid')->unique();
 | 
				
			||||||
 | 
					            $table->string('name');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('ports_exposes')->nullable();
 | 
				
			||||||
 | 
					            $table->string('ports_mappings')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->foreignId('service_id');
 | 
				
			||||||
 | 
					            $table->timestamps();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::dropIfExists('service_databases');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::create('service_applications', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->id();
 | 
				
			||||||
 | 
					            $table->string('uuid')->unique();
 | 
				
			||||||
 | 
					            $table->string('name');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('fqdn')->unique()->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('ports_exposes')->nullable();
 | 
				
			||||||
 | 
					            $table->string('ports_mappings')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('health_check_path')->default('/');
 | 
				
			||||||
 | 
					            $table->string('health_check_port')->nullable();
 | 
				
			||||||
 | 
					            $table->string('health_check_host')->default('localhost');
 | 
				
			||||||
 | 
					            $table->string('health_check_method')->default('GET');
 | 
				
			||||||
 | 
					            $table->integer('health_check_return_code')->default(200);
 | 
				
			||||||
 | 
					            $table->string('health_check_scheme')->default('http');
 | 
				
			||||||
 | 
					            $table->string('health_check_response_text')->nullable();
 | 
				
			||||||
 | 
					            $table->integer('health_check_interval')->default(5);
 | 
				
			||||||
 | 
					            $table->integer('health_check_timeout')->default(5);
 | 
				
			||||||
 | 
					            $table->integer('health_check_retries')->default(10);
 | 
				
			||||||
 | 
					            $table->integer('health_check_start_period')->default(5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->string('status')->default('exited');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $table->foreignId('service_id');
 | 
				
			||||||
 | 
					            $table->timestamps();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::dropIfExists('service_applications');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Migrations\Migration;
 | 
				
			||||||
 | 
					use Illuminate\Database\Schema\Blueprint;
 | 
				
			||||||
 | 
					use Illuminate\Support\Facades\Schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return new class extends Migration
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function up(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('environment_variables', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->foreignId('service_id')->nullable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reverse the migrations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function down(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Schema::table('environment_variables', function (Blueprint $table) {
 | 
				
			||||||
 | 
					            $table->dropColumn('service_id');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										17
									
								
								database/seeders/ServiceApplicationSeeder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								database/seeders/ServiceApplicationSeeder.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Database\Seeders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Console\Seeds\WithoutModelEvents;
 | 
				
			||||||
 | 
					use Illuminate\Database\Seeder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceApplicationSeeder extends Seeder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the database seeds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function run(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								database/seeders/ServiceDatabaseSeeder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								database/seeders/ServiceDatabaseSeeder.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Database\Seeders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Console\Seeds\WithoutModelEvents;
 | 
				
			||||||
 | 
					use Illuminate\Database\Seeder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceDatabaseSeeder extends Seeder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the database seeds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function run(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								database/seeders/ServiceSeeder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								database/seeders/ServiceSeeder.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Database\Seeders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Illuminate\Database\Console\Seeds\WithoutModelEvents;
 | 
				
			||||||
 | 
					use Illuminate\Database\Seeder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ServiceSeeder extends Seeder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Run the database seeds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function run(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								examples/docker-compose-ghost.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								examples/docker-compose-ghost.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					services:
 | 
				
			||||||
 | 
					  ghost:
 | 
				
			||||||
 | 
					    documentation: https://docs.ghost.org/docs/config
 | 
				
			||||||
 | 
					    image: ghost:5
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - ghost-content-data:/var/lib/ghost/content
 | 
				
			||||||
 | 
					      - type: volume
 | 
				
			||||||
 | 
					        source: /data/g
 | 
				
			||||||
 | 
					        target: /data
 | 
				
			||||||
 | 
					        volume:
 | 
				
			||||||
 | 
					          nocopy: true
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      - url=$SERVICE_FQDN_GHOST
 | 
				
			||||||
 | 
					      - database__client=mysql
 | 
				
			||||||
 | 
					      - database__connection__host=mysql
 | 
				
			||||||
 | 
					      - database__connection__user=$SERVICE_USER_MYSQL
 | 
				
			||||||
 | 
					      - database__connection__password=$SERVICE_PASSWORD_MYSQL
 | 
				
			||||||
 | 
					      - database__connection__database=${MYSQL_DATABASE-ghost}
 | 
				
			||||||
 | 
					    networks:
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        aliases:
 | 
				
			||||||
 | 
					          - alias1
 | 
				
			||||||
 | 
					          - alias3
 | 
				
			||||||
 | 
					        ipv4_address: 172.16.238.10
 | 
				
			||||||
 | 
					        ipv6_address: 2001:3984:3989::10
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - "2368"
 | 
				
			||||||
 | 
					      - 1234:2368
 | 
				
			||||||
 | 
					      - target: 2368
 | 
				
			||||||
 | 
					        published: 1234
 | 
				
			||||||
 | 
					        protocol: tcp
 | 
				
			||||||
 | 
					        mode: host
 | 
				
			||||||
 | 
					    depends_on:
 | 
				
			||||||
 | 
					      - mysql
 | 
				
			||||||
 | 
					  mysql:
 | 
				
			||||||
 | 
					    documentation: https://hub.docker.com/_/mysql
 | 
				
			||||||
 | 
					    image: mysql:8.0
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - ghost-mysql-data:/var/lib/mysql
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      - MYSQL_USER=${SERVICE_USER_MYSQL}
 | 
				
			||||||
 | 
					      - MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
 | 
				
			||||||
 | 
					      - MYSQL_DATABASE=${MYSQL_DATABASE}
 | 
				
			||||||
 | 
					      - MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQL_ROOT}
 | 
				
			||||||
 | 
					networks:
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    ipam:
 | 
				
			||||||
 | 
					      driver: default
 | 
				
			||||||
 | 
					      config:
 | 
				
			||||||
 | 
					        - subnet: "172.16.238.0/24"
 | 
				
			||||||
 | 
					        - subnet: "2001:3984:3989::/64"
 | 
				
			||||||
@@ -12,6 +12,7 @@
 | 
				
			|||||||
                <x-forms.input id="application.name" label="Name" required />
 | 
					                <x-forms.input id="application.name" label="Name" required />
 | 
				
			||||||
                <x-forms.input id="application.description" label="Description" />
 | 
					                <x-forms.input id="application.description" label="Description" />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					            @if ($services->count() === 0)
 | 
				
			||||||
                <div class="flex items-end gap-2">
 | 
					                <div class="flex items-end gap-2">
 | 
				
			||||||
                    <x-forms.input placeholder="https://coolify.io" id="application.fqdn" label="Domains"
 | 
					                    <x-forms.input placeholder="https://coolify.io" id="application.fqdn" label="Domains"
 | 
				
			||||||
                        helper="You can specify one domain with path or more with comma.<br><span class='text-helper'>Example</span>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3" />
 | 
					                        helper="You can specify one domain with path or more with comma.<br><span class='text-helper'>Example</span>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3" />
 | 
				
			||||||
@@ -44,6 +45,7 @@
 | 
				
			|||||||
                    @endif
 | 
					                    @endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					            @endif
 | 
				
			||||||
            @if ($application->settings->is_static)
 | 
					            @if ($application->settings->is_static)
 | 
				
			||||||
                <x-forms.select id="application.static_image" label="Static Image" required>
 | 
					                <x-forms.select id="application.static_image" label="Static Image" required>
 | 
				
			||||||
                    <option value="nginx:alpine">nginx:alpine</option>
 | 
					                    <option value="nginx:alpine">nginx:alpine</option>
 | 
				
			||||||
@@ -72,31 +74,38 @@
 | 
				
			|||||||
            @if ($application->dockerfile)
 | 
					            @if ($application->dockerfile)
 | 
				
			||||||
                <x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
 | 
					                <x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
 | 
				
			||||||
            @endif
 | 
					            @endif
 | 
				
			||||||
            @if ($application->dockercompose_raw)
 | 
					            @if ($services->count() > 0)
 | 
				
			||||||
                <h3>Services</h3>
 | 
					                <h3>Services</h3>
 | 
				
			||||||
                @foreach ($services as $serviceName => $service)
 | 
					                @foreach ($services as $serviceName => $service)
 | 
				
			||||||
                    <x-forms.input id="application.service_configurations.{{$serviceName}}.fqdn" label="{{ $serviceName }} FQDN">
 | 
					                    @if (!data_get($service, 'is_database'))
 | 
				
			||||||
 | 
					                        <h4>{{ Str::headline($serviceName) }}</h4>
 | 
				
			||||||
 | 
					                        <div class="flex gap-2">
 | 
				
			||||||
 | 
					                            <x-forms.input id="service_configurations.{{ $serviceName }}.fqdn" label="FQDN">
 | 
				
			||||||
                            </x-forms.input>
 | 
					                            </x-forms.input>
 | 
				
			||||||
                @endforeach
 | 
					                            {{-- <x-forms.input id="service_configurations.{{ $serviceName }}.port"
 | 
				
			||||||
                {{-- <x-forms.textarea label="Docker Compose (Raw)" id="application.dockercompose_raw" rows="16">
 | 
					                            label="{{ Str::headline($serviceName) }} Port">
 | 
				
			||||||
                </x-forms.textarea> --}}
 | 
					                        </x-forms.input> --}}
 | 
				
			||||||
                {{-- <x-forms.textarea readonly helper="Added all required fields" label="Docker Compose (Coolified)"
 | 
					                        </div>
 | 
				
			||||||
                    id="application.dockercompose" rows="6">
 | 
					 | 
				
			||||||
                </x-forms.textarea> --}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    @endif
 | 
					                    @endif
 | 
				
			||||||
 | 
					                @endforeach
 | 
				
			||||||
 | 
					                <x-forms.textarea label="Docker Compose (Raw)" id="application.dockercompose_raw" rows="16">
 | 
				
			||||||
 | 
					                </x-forms.textarea>
 | 
				
			||||||
 | 
					                <x-forms.textarea readonly helper="Added all required fields" label="Docker Compose (Coolified)"
 | 
				
			||||||
 | 
					                    id="application.dockercompose" rows="6">
 | 
				
			||||||
 | 
					                </x-forms.textarea>
 | 
				
			||||||
 | 
					            @else
 | 
				
			||||||
                <h3>Network</h3>
 | 
					                <h3>Network</h3>
 | 
				
			||||||
                <div class="flex flex-col gap-2 xl:flex-row">
 | 
					                <div class="flex flex-col gap-2 xl:flex-row">
 | 
				
			||||||
                    @if ($application->settings->is_static)
 | 
					                    @if ($application->settings->is_static)
 | 
				
			||||||
                        <x-forms.input id="application.ports_exposes" label="Ports Exposes" readonly />
 | 
					                        <x-forms.input id="application.ports_exposes" label="Ports Exposes" readonly />
 | 
				
			||||||
                    @else
 | 
					                    @else
 | 
				
			||||||
                    <x-forms.input placeholder="3000,3001" id="application.ports_exposes" label="Ports Exposes" required
 | 
					                        <x-forms.input placeholder="3000,3001" id="application.ports_exposes" label="Ports Exposes"
 | 
				
			||||||
                        helper="A comma separated list of ports you would like to expose for the proxy." />
 | 
					                            required helper="A comma separated list of ports you would like to expose for the proxy." />
 | 
				
			||||||
                    @endif
 | 
					                    @endif
 | 
				
			||||||
                    <x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
 | 
					                    <x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
 | 
				
			||||||
                        helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><span class='inline-block font-bold text-warning'>Example</span>3000:3000,3002:3002" />
 | 
					                        helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><span class='inline-block font-bold text-warning'>Example</span>3000:3000,3002:3002" />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					            @endif
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <h3>Advanced</h3>
 | 
					        <h3>Advanced</h3>
 | 
				
			||||||
        <div class="flex flex-col">
 | 
					        <div class="flex flex-col">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<div>
 | 
					<div>
 | 
				
			||||||
    <h1>Create a new Application</h1>
 | 
					    <h1>Create a new Service</h1>
 | 
				
			||||||
    <div class="pb-4">You can deploy complex application easily with Docker Compose.</div>
 | 
					    <div class="pb-4">You can deploy complex services easily with Docker Compose.</div>
 | 
				
			||||||
    <form wire:submit.prevent="submit">
 | 
					    <form wire:submit.prevent="submit">
 | 
				
			||||||
        <div class="flex gap-2 pb-1">
 | 
					        <div class="flex gap-2 pb-1">
 | 
				
			||||||
            <h2>Docker Compose</h2>
 | 
					            <h2>Docker Compose</h2>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								resources/views/livewire/project/service/index.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								resources/views/livewire/project/service/index.blade.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					<div>
 | 
				
			||||||
 | 
					    <h1>Configuration</h1>
 | 
				
			||||||
 | 
					    <h3>Applications</h3>
 | 
				
			||||||
 | 
					    @foreach ($service->applications as $application)
 | 
				
			||||||
 | 
					        <p>{{ $application->name }}</p>
 | 
				
			||||||
 | 
					    @endforeach
 | 
				
			||||||
 | 
					    <h3>Databases</h3>
 | 
				
			||||||
 | 
					    @foreach ($service->databases as $database)
 | 
				
			||||||
 | 
					        <p>{{ $database->name }}</p>
 | 
				
			||||||
 | 
					    @endforeach
 | 
				
			||||||
 | 
					    <h3>Variables</h3>
 | 
				
			||||||
 | 
					    @foreach ($service->environment_variables as $variable)
 | 
				
			||||||
 | 
					        <p>{{ $variable->key }}={{ $variable->value }}</p>
 | 
				
			||||||
 | 
					    @endforeach
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -53,5 +53,14 @@
 | 
				
			|||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
        @endforeach
 | 
					        @endforeach
 | 
				
			||||||
 | 
					        @foreach ($environment->services->sortBy('name') as $service)
 | 
				
			||||||
 | 
					        <a class="box group"
 | 
				
			||||||
 | 
					            href="{{ route('project.service', [$project->uuid, $environment->name, $service->uuid]) }}">
 | 
				
			||||||
 | 
					            <div class="flex flex-col mx-6">
 | 
				
			||||||
 | 
					                <div class=" group-hover:text-white">{{ $service->name }}</div>
 | 
				
			||||||
 | 
					                <div class="text-xs text-gray-400 group-hover:text-white">{{ $service->description }}</div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					    @endforeach
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</x-layout>
 | 
					</x-layout>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,12 +6,12 @@ use App\Http\Controllers\DatabaseController;
 | 
				
			|||||||
use App\Http\Controllers\MagicController;
 | 
					use App\Http\Controllers\MagicController;
 | 
				
			||||||
use App\Http\Controllers\ProjectController;
 | 
					use App\Http\Controllers\ProjectController;
 | 
				
			||||||
use App\Http\Controllers\ServerController;
 | 
					use App\Http\Controllers\ServerController;
 | 
				
			||||||
use App\Http\Livewire\Boarding\Index;
 | 
					use App\Http\Livewire\Boarding\Index as BoardingIndex;
 | 
				
			||||||
 | 
					use App\Http\Livewire\Service\Index as ServiceIndex;
 | 
				
			||||||
use App\Http\Livewire\Dashboard;
 | 
					use App\Http\Livewire\Dashboard;
 | 
				
			||||||
use App\Http\Livewire\Server\All;
 | 
					use App\Http\Livewire\Server\All;
 | 
				
			||||||
use App\Http\Livewire\Server\Show;
 | 
					use App\Http\Livewire\Server\Show;
 | 
				
			||||||
use App\Http\Livewire\Waitlist\Index as WaitlistIndex;
 | 
					use App\Http\Livewire\Waitlist\Index as WaitlistIndex;
 | 
				
			||||||
use App\Models\Application;
 | 
					 | 
				
			||||||
use App\Models\GithubApp;
 | 
					use App\Models\GithubApp;
 | 
				
			||||||
use App\Models\GitlabApp;
 | 
					use App\Models\GitlabApp;
 | 
				
			||||||
use App\Models\InstanceSettings;
 | 
					use App\Models\InstanceSettings;
 | 
				
			||||||
@@ -28,10 +28,6 @@ use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse;
 | 
				
			|||||||
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse;
 | 
					use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse;
 | 
				
			||||||
use Laravel\Fortify\Fortify;
 | 
					use Laravel\Fortify\Fortify;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Route::get('/test', function () {
 | 
					 | 
				
			||||||
    $template = Storage::get('templates/docker-compose.yaml');
 | 
					 | 
				
			||||||
    return generateServiceFromTemplate($template, Application::find(1));
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
Route::post('/forgot-password', function (Request $request) {
 | 
					Route::post('/forgot-password', function (Request $request) {
 | 
				
			||||||
    if (is_transactional_emails_active()) {
 | 
					    if (is_transactional_emails_active()) {
 | 
				
			||||||
        $arrayOfRequest = $request->only(Fortify::email());
 | 
					        $arrayOfRequest = $request->only(Fortify::email());
 | 
				
			||||||
@@ -88,6 +84,9 @@ Route::middleware(['auth'])->group(function () {
 | 
				
			|||||||
    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [DatabaseController::class, 'configuration'])->name('project.database.configuration');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [DatabaseController::class, 'configuration'])->name('project.database.configuration');
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups', [DatabaseController::class, 'backups'])->name('project.database.backups.all');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups', [DatabaseController::class, 'backups'])->name('project.database.backups.all');
 | 
				
			||||||
    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups/{backup_uuid}', [DatabaseController::class, 'executions'])->name('project.database.backups.executions');
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups/{backup_uuid}', [DatabaseController::class, 'executions'])->name('project.database.backups.executions');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Services
 | 
				
			||||||
 | 
					    Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}', ServiceIndex::class)->name('project.service');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Route::middleware(['auth'])->group(function () {
 | 
					Route::middleware(['auth'])->group(function () {
 | 
				
			||||||
@@ -109,7 +108,7 @@ Route::middleware(['auth'])->group(function () {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Route::middleware(['auth'])->group(function () {
 | 
					Route::middleware(['auth'])->group(function () {
 | 
				
			||||||
    Route::get('/', Dashboard::class)->name('dashboard');
 | 
					    Route::get('/', Dashboard::class)->name('dashboard');
 | 
				
			||||||
    Route::get('/boarding', Index::class)->name('boarding');
 | 
					    Route::get('/boarding', BoardingIndex::class)->name('boarding');
 | 
				
			||||||
    Route::middleware(['throttle:force-password-reset'])->group(function () {
 | 
					    Route::middleware(['throttle:force-password-reset'])->group(function () {
 | 
				
			||||||
        Route::get('/force-password-reset', [Controller::class, 'force_passoword_reset'])->name('auth.force-password-reset');
 | 
					        Route::get('/force-password-reset', [Controller::class, 'force_passoword_reset'])->name('auth.force-password-reset');
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user