wip: services
This commit is contained in:
parent
a86e971020
commit
b4d69a22df
@ -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');
|
function fqdnLabelsForTraefik($domain, $container_name, $is_force_https_enabled)
|
||||||
$labels[] = 'coolify.applicationId=' . $appId;
|
{
|
||||||
$labels[] = 'coolify.type=application';
|
$labels = collect([]);
|
||||||
$labels[] = 'coolify.name=' . $application->name;
|
$labels->push('traefik.enable=true');
|
||||||
if ($pull_request_id !== 0) {
|
|
||||||
$labels[] = 'coolify.pullRequestId=' . $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) {
|
|
||||||
$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(':')) {
|
||||||
@ -111,7 +136,7 @@ function generateServiceFromTemplate(string $template, Application $application)
|
|||||||
}
|
}
|
||||||
data_set($yaml, 'networks', [
|
data_set($yaml, 'networks', [
|
||||||
$network => [
|
$network => [
|
||||||
'name'=> $network
|
'name' => $network
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
data_set($yaml, 'volumes', $composeVolumes->toArray());
|
data_set($yaml, 'volumes', $composeVolumes->toArray());
|
||||||
|
@ -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');
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user