2023-04-19 10:42:15 +00:00
< ? php
2023-12-07 18:06:32 +00:00
namespace App\Livewire\Project\Application ;
2023-04-19 10:42:15 +00:00
2024-10-08 13:11:19 +00:00
use App\Actions\Application\GenerateConfig ;
2023-04-19 10:42:15 +00:00
use App\Models\Application ;
2025-08-19 12:15:31 +00:00
use App\Support\ValidationPatterns ;
2025-08-22 14:47:59 +00:00
use Illuminate\Foundation\Auth\Access\AuthorizesRequests ;
2023-09-20 13:42:41 +00:00
use Illuminate\Support\Collection ;
2025-11-04 07:51:05 +00:00
use Livewire\Attributes\Validate ;
2023-11-13 08:04:19 +00:00
use Livewire\Component ;
2024-10-11 07:56:45 +00:00
use Spatie\Url\Url ;
2023-11-24 14:48:23 +00:00
use Visus\Cuid2\Cuid2 ;
2023-04-19 10:42:15 +00:00
class General extends Component
{
2025-08-22 14:47:59 +00:00
use AuthorizesRequests ;
2023-04-19 10:42:15 +00:00
public string $applicationId ;
public Application $application ;
2024-06-10 20:43:34 +00:00
2023-09-20 13:42:41 +00:00
public Collection $services ;
2024-06-10 20:43:34 +00:00
2025-11-04 07:51:05 +00:00
#[Validate('required|regex:/^[a-zA-Z0-9\s\-_.\/:()]+$/')]
2023-04-19 10:42:15 +00:00
public string $name ;
2024-06-10 20:43:34 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-10-13 13:38:59 +00:00
public ? string $description = null ;
2025-11-04 07:51:05 +00:00
#[Validate(['nullable'])]
2023-10-09 09:10:04 +00:00
public ? string $fqdn = null ;
2024-06-10 20:43:34 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['required'])]
2025-11-04 07:43:33 +00:00
public string $gitRepository ;
2024-06-10 20:43:34 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['required'])]
2025-11-04 07:43:33 +00:00
public string $gitBranch ;
2024-06-10 20:43:34 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $gitCommitSha = null ;
2024-06-10 20:43:34 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $installCommand = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $buildCommand = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $startCommand = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['required'])]
2025-11-04 07:43:33 +00:00
public string $buildPack ;
2024-06-10 20:43:34 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['required'])]
2025-11-04 07:43:33 +00:00
public string $staticImage ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['required'])]
2025-11-04 07:43:33 +00:00
public string $baseDirectory ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $publishDirectory = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $portsExposes = null ;
2024-06-10 20:43:34 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $portsMappings = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $customNetworkAliases = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-10-13 13:38:59 +00:00
public ? string $dockerfile = null ;
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $dockerfileLocation = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $dockerfileTargetBuild = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $dockerRegistryImageName = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $dockerRegistryImageTag = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $dockerComposeLocation = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $dockerCompose = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $dockerComposeRaw = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $dockerComposeCustomStartCommand = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $dockerComposeCustomBuildCommand = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $customDockerRunOptions = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $preDeploymentCommand = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $preDeploymentCommandContainer = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $postDeploymentCommand = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $postDeploymentCommandContainer = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $customNginxConfiguration = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['boolean', 'required'])]
2025-11-04 07:43:33 +00:00
public bool $isStatic = false ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['boolean', 'required'])]
2025-11-04 07:43:33 +00:00
public bool $isSpa = false ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['boolean', 'required'])]
2025-11-04 07:43:33 +00:00
public bool $isBuildServerEnabled = false ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['boolean', 'required'])]
2025-11-04 07:43:33 +00:00
public bool $isPreserveRepositoryEnabled = false ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['boolean', 'required'])]
2025-11-04 07:43:33 +00:00
public bool $isContainerLabelEscapeEnabled = true ;
2024-08-12 14:06:24 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['boolean', 'required'])]
2025-11-04 07:43:33 +00:00
public bool $isContainerLabelReadonlyEnabled = false ;
2023-04-19 10:42:15 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['boolean', 'required'])]
2025-11-04 07:43:33 +00:00
public bool $isHttpBasicAuthEnabled = false ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $httpBasicAuthUsername = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $httpBasicAuthPassword = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['nullable'])]
2025-11-04 07:43:33 +00:00
public ? string $watchPaths = null ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:51:05 +00:00
#[Validate(['string', 'required'])]
2025-10-13 13:38:59 +00:00
public string $redirect ;
2025-11-04 07:51:05 +00:00
#[Validate(['nullable'])]
2023-10-18 08:32:08 +00:00
public $customLabels ;
2024-06-10 20:43:34 +00:00
2023-10-18 08:32:08 +00:00
public bool $labelsChanged = false ;
2024-06-10 20:43:34 +00:00
2024-04-12 08:28:40 +00:00
public bool $initLoadingCompose = false ;
2023-10-18 08:32:08 +00:00
2023-11-27 10:54:55 +00:00
public ? string $initialDockerComposeLocation = null ;
2024-06-10 20:43:34 +00:00
public ? Collection $parsedServices ;
2023-11-24 14:48:23 +00:00
public $parsedServiceDomains = [];
2025-08-28 08:52:41 +00:00
public $domainConflicts = [];
public $showDomainConflictModal = false ;
public $forceSaveDomains = false ;
2023-11-20 10:35:31 +00:00
protected $listeners = [
2024-04-25 09:49:34 +00:00
'resetDefaultLabels' ,
2024-06-10 20:43:34 +00:00
'configurationChanged' => '$refresh' ,
2025-08-28 08:52:41 +00:00
'confirmDomainUsage' ,
2023-11-20 10:35:31 +00:00
];
2024-06-10 20:43:34 +00:00
2025-08-19 12:15:31 +00:00
protected function rules () : array
{
return [
2025-10-13 13:38:59 +00:00
'name' => ValidationPatterns :: nameRules (),
'description' => ValidationPatterns :: descriptionRules (),
'fqdn' => 'nullable' ,
2025-11-04 07:43:33 +00:00
'gitRepository' => 'required' ,
'gitBranch' => 'required' ,
'gitCommitSha' => 'nullable' ,
'installCommand' => 'nullable' ,
'buildCommand' => 'nullable' ,
'startCommand' => 'nullable' ,
'buildPack' => 'required' ,
'staticImage' => 'required' ,
'baseDirectory' => 'required' ,
'publishDirectory' => 'nullable' ,
'portsExposes' => 'required' ,
'portsMappings' => 'nullable' ,
'customNetworkAliases' => 'nullable' ,
2025-10-13 13:38:59 +00:00
'dockerfile' => 'nullable' ,
2025-11-04 07:43:33 +00:00
'dockerRegistryImageName' => 'nullable' ,
'dockerRegistryImageTag' => 'nullable' ,
'dockerfileLocation' => 'nullable' ,
'dockerComposeLocation' => 'nullable' ,
'dockerCompose' => 'nullable' ,
'dockerComposeRaw' => 'nullable' ,
'dockerfileTargetBuild' => 'nullable' ,
'dockerComposeCustomStartCommand' => 'nullable' ,
'dockerComposeCustomBuildCommand' => 'nullable' ,
'customLabels' => 'nullable' ,
'customDockerRunOptions' => 'nullable' ,
'preDeploymentCommand' => 'nullable' ,
'preDeploymentCommandContainer' => 'nullable' ,
'postDeploymentCommand' => 'nullable' ,
'postDeploymentCommandContainer' => 'nullable' ,
'customNginxConfiguration' => 'nullable' ,
'isStatic' => 'boolean|required' ,
'isSpa' => 'boolean|required' ,
'isBuildServerEnabled' => 'boolean|required' ,
'isContainerLabelEscapeEnabled' => 'boolean|required' ,
'isContainerLabelReadonlyEnabled' => 'boolean|required' ,
'isPreserveRepositoryEnabled' => 'boolean|required' ,
'isHttpBasicAuthEnabled' => 'boolean|required' ,
'httpBasicAuthUsername' => 'string|nullable' ,
'httpBasicAuthPassword' => 'string|nullable' ,
'watchPaths' => 'nullable' ,
2025-10-13 13:38:59 +00:00
'redirect' => 'string|required' ,
2025-08-19 12:15:31 +00:00
];
}
protected function messages () : array
{
return array_merge (
ValidationPatterns :: combinedMessages (),
[
2025-10-13 13:38:59 +00:00
'name.required' => 'The Name field is required.' ,
'name.regex' => 'The Name may only contain letters, numbers, spaces, dashes (-), underscores (_), dots (.), slashes (/), colons (:), and parentheses ().' ,
'description.regex' => 'The Description contains invalid characters. Only letters, numbers, spaces, and common punctuation (- _ . : / () \' " , ! ? @ # % & + = [] {} | ~ ` *) are allowed.' ,
2025-11-04 07:43:33 +00:00
'gitRepository.required' => 'The Git Repository field is required.' ,
'gitBranch.required' => 'The Git Branch field is required.' ,
'buildPack.required' => 'The Build Pack field is required.' ,
'staticImage.required' => 'The Static Image field is required.' ,
'baseDirectory.required' => 'The Base Directory field is required.' ,
'portsExposes.required' => 'The Exposed Ports field is required.' ,
'isStatic.required' => 'The Static setting is required.' ,
'isStatic.boolean' => 'The Static setting must be true or false.' ,
'isSpa.required' => 'The SPA setting is required.' ,
'isSpa.boolean' => 'The SPA setting must be true or false.' ,
'isBuildServerEnabled.required' => 'The Build Server setting is required.' ,
'isBuildServerEnabled.boolean' => 'The Build Server setting must be true or false.' ,
'isContainerLabelEscapeEnabled.required' => 'The Container Label Escape setting is required.' ,
'isContainerLabelEscapeEnabled.boolean' => 'The Container Label Escape setting must be true or false.' ,
'isContainerLabelReadonlyEnabled.required' => 'The Container Label Readonly setting is required.' ,
'isContainerLabelReadonlyEnabled.boolean' => 'The Container Label Readonly setting must be true or false.' ,
'isPreserveRepositoryEnabled.required' => 'The Preserve Repository setting is required.' ,
'isPreserveRepositoryEnabled.boolean' => 'The Preserve Repository setting must be true or false.' ,
'isHttpBasicAuthEnabled.required' => 'The HTTP Basic Auth setting is required.' ,
'isHttpBasicAuthEnabled.boolean' => 'The HTTP Basic Auth setting must be true or false.' ,
2025-10-13 13:38:59 +00:00
'redirect.required' => 'The Redirect setting is required.' ,
'redirect.string' => 'The Redirect setting must be a string.' ,
2025-08-19 12:15:31 +00:00
]
);
}
2024-06-10 20:43:34 +00:00
2023-06-16 10:35:40 +00:00
protected $validationAttributes = [
2025-11-04 07:43:33 +00:00
'name' => 'name' ,
'description' => 'description' ,
'fqdn' => 'FQDN' ,
'gitRepository' => 'Git repository' ,
'gitBranch' => 'Git branch' ,
'gitCommitSha' => 'Git commit SHA' ,
'installCommand' => 'Install command' ,
'buildCommand' => 'Build command' ,
'startCommand' => 'Start command' ,
'buildPack' => 'Build pack' ,
'staticImage' => 'Static image' ,
'baseDirectory' => 'Base directory' ,
'publishDirectory' => 'Publish directory' ,
'portsExposes' => 'Ports exposes' ,
'portsMappings' => 'Ports mappings' ,
'dockerfile' => 'Dockerfile' ,
'dockerRegistryImageName' => 'Docker registry image name' ,
'dockerRegistryImageTag' => 'Docker registry image tag' ,
'dockerfileLocation' => 'Dockerfile location' ,
'dockerComposeLocation' => 'Docker compose location' ,
'dockerCompose' => 'Docker compose' ,
'dockerComposeRaw' => 'Docker compose raw' ,
'customLabels' => 'Custom labels' ,
'dockerfileTargetBuild' => 'Dockerfile target build' ,
'customDockerRunOptions' => 'Custom docker run commands' ,
'customNetworkAliases' => 'Custom docker network aliases' ,
'dockerComposeCustomStartCommand' => 'Docker compose custom start command' ,
'dockerComposeCustomBuildCommand' => 'Docker compose custom build command' ,
'customNginxConfiguration' => 'Custom Nginx configuration' ,
'isStatic' => 'Is static' ,
'isSpa' => 'Is SPA' ,
'isBuildServerEnabled' => 'Is build server enabled' ,
'isContainerLabelEscapeEnabled' => 'Is container label escape enabled' ,
'isContainerLabelReadonlyEnabled' => 'Is container label readonly' ,
'isPreserveRepositoryEnabled' => 'Is preserve repository enabled' ,
'watchPaths' => 'Watch paths' ,
'redirect' => 'Redirect' ,
2023-06-16 10:35:40 +00:00
];
2024-06-10 20:43:34 +00:00
2023-10-18 08:32:08 +00:00
public function mount ()
{
2023-11-24 14:48:23 +00:00
try {
2024-08-29 10:39:37 +00:00
$this -> parsedServices = $this -> application -> parse ();
2024-05-21 10:02:04 +00:00
if ( is_null ( $this -> parsedServices ) || empty ( $this -> parsedServices )) {
2024-06-10 20:43:34 +00:00
$this -> dispatch ( 'error' , 'Failed to parse your docker-compose file. Please check the syntax and try again.' );
2025-10-13 13:38:59 +00:00
// Still sync data even if parse fails, so form fields are populated
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2024-06-10 20:43:34 +00:00
2024-05-21 10:02:04 +00:00
return ;
}
2023-11-24 14:48:23 +00:00
} catch ( \Throwable $e ) {
2023-12-07 18:06:32 +00:00
$this -> dispatch ( 'error' , $e -> getMessage ());
2025-10-13 13:38:59 +00:00
// Still sync data even on error, so form fields are populated
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2023-11-24 14:48:23 +00:00
}
2024-03-11 08:42:16 +00:00
if ( $this -> application -> build_pack === 'dockercompose' ) {
2025-08-23 16:19:50 +00:00
// Only update if user has permission
try {
$this -> authorize ( 'update' , $this -> application );
$this -> application -> fqdn = null ;
$this -> application -> settings -> save ();
} catch ( \Illuminate\Auth\Access\AuthorizationException $e ) {
// User doesn't have update permission, just continue without saving
}
2024-03-11 08:42:16 +00:00
}
2023-11-24 14:48:23 +00:00
$this -> parsedServiceDomains = $this -> application -> docker_compose_domains ? json_decode ( $this -> application -> docker_compose_domains , true ) : [];
2025-09-25 11:19:12 +00:00
// Convert service names with dots and dashes to use underscores for HTML form binding
2025-07-14 12:45:01 +00:00
$sanitizedDomains = [];
foreach ( $this -> parsedServiceDomains as $serviceName => $domain ) {
2025-09-25 11:19:12 +00:00
$sanitizedKey = str ( $serviceName ) -> replace ( '-' , '_' ) -> replace ( '.' , '_' ) -> toString ();
2025-07-14 12:45:01 +00:00
$sanitizedDomains [ $sanitizedKey ] = $domain ;
}
$this -> parsedServiceDomains = $sanitizedDomains ;
2024-04-15 17:47:17 +00:00
$this -> customLabels = $this -> application -> parseContainerLabels ();
2025-01-23 13:57:18 +00:00
if ( ! $this -> customLabels && $this -> application -> destination -> server -> proxyType () !== 'NONE' && $this -> application -> settings -> is_container_label_readonly_enabled === true ) {
2025-08-23 16:19:50 +00:00
// Only update custom labels if user has permission
try {
$this -> authorize ( 'update' , $this -> application );
$this -> customLabels = str ( implode ( '|coolify|' , generateLabelsApplication ( $this -> application ))) -> replace ( '|coolify|' , " \n " );
$this -> application -> custom_labels = base64_encode ( $this -> customLabels );
$this -> application -> save ();
} catch ( \Illuminate\Auth\Access\AuthorizationException $e ) {
// User doesn't have update permission, just use existing labels
// $this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
}
2024-01-02 15:44:41 +00:00
}
2023-11-27 10:54:55 +00:00
$this -> initialDockerComposeLocation = $this -> application -> docker_compose_location ;
2024-06-10 20:43:34 +00:00
if ( $this -> application -> build_pack === 'dockercompose' && ! $this -> application -> docker_compose_raw ) {
2025-08-23 16:19:50 +00:00
// Only load compose file if user has update permission
try {
$this -> authorize ( 'update' , $this -> application );
$this -> initLoadingCompose = true ;
$this -> dispatch ( 'info' , 'Loading docker compose file.' );
} catch ( \Illuminate\Auth\Access\AuthorizationException $e ) {
// User doesn't have update permission, skip loading compose file
}
2024-04-12 08:28:40 +00:00
}
2024-04-12 10:44:49 +00:00
if ( str ( $this -> application -> status ) -> startsWith ( 'running' ) && is_null ( $this -> application -> config_hash )) {
$this -> dispatch ( 'configurationChanged' );
}
2025-10-13 13:38:59 +00:00
// Sync data from model to properties at the END, after all business logic
// This ensures any modifications to $this->application during mount() are reflected in properties
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2025-10-13 13:38:59 +00:00
}
2025-11-04 07:43:33 +00:00
public function syncData ( bool $toModel = false ) : void
2025-10-13 13:38:59 +00:00
{
2025-11-04 07:43:33 +00:00
if ( $toModel ) {
$this -> validate ();
// Application properties
$this -> application -> name = $this -> name ;
$this -> application -> description = $this -> description ;
$this -> application -> fqdn = $this -> fqdn ;
$this -> application -> git_repository = $this -> gitRepository ;
$this -> application -> git_branch = $this -> gitBranch ;
$this -> application -> git_commit_sha = $this -> gitCommitSha ;
$this -> application -> install_command = $this -> installCommand ;
$this -> application -> build_command = $this -> buildCommand ;
$this -> application -> start_command = $this -> startCommand ;
$this -> application -> build_pack = $this -> buildPack ;
$this -> application -> static_image = $this -> staticImage ;
$this -> application -> base_directory = $this -> baseDirectory ;
$this -> application -> publish_directory = $this -> publishDirectory ;
$this -> application -> ports_exposes = $this -> portsExposes ;
$this -> application -> ports_mappings = $this -> portsMappings ;
$this -> application -> custom_network_aliases = $this -> customNetworkAliases ;
$this -> application -> dockerfile = $this -> dockerfile ;
$this -> application -> dockerfile_location = $this -> dockerfileLocation ;
$this -> application -> dockerfile_target_build = $this -> dockerfileTargetBuild ;
$this -> application -> docker_registry_image_name = $this -> dockerRegistryImageName ;
$this -> application -> docker_registry_image_tag = $this -> dockerRegistryImageTag ;
$this -> application -> docker_compose_location = $this -> dockerComposeLocation ;
$this -> application -> docker_compose = $this -> dockerCompose ;
$this -> application -> docker_compose_raw = $this -> dockerComposeRaw ;
$this -> application -> docker_compose_custom_start_command = $this -> dockerComposeCustomStartCommand ;
$this -> application -> docker_compose_custom_build_command = $this -> dockerComposeCustomBuildCommand ;
2025-11-04 08:57:12 +00:00
$this -> application -> custom_labels = is_null ( $this -> customLabels )
? null
: base64_encode ( $this -> customLabels );
2025-11-04 07:43:33 +00:00
$this -> application -> custom_docker_run_options = $this -> customDockerRunOptions ;
$this -> application -> pre_deployment_command = $this -> preDeploymentCommand ;
$this -> application -> pre_deployment_command_container = $this -> preDeploymentCommandContainer ;
$this -> application -> post_deployment_command = $this -> postDeploymentCommand ;
$this -> application -> post_deployment_command_container = $this -> postDeploymentCommandContainer ;
$this -> application -> custom_nginx_configuration = $this -> customNginxConfiguration ;
$this -> application -> is_http_basic_auth_enabled = $this -> isHttpBasicAuthEnabled ;
$this -> application -> http_basic_auth_username = $this -> httpBasicAuthUsername ;
$this -> application -> http_basic_auth_password = $this -> httpBasicAuthPassword ;
$this -> application -> watch_paths = $this -> watchPaths ;
$this -> application -> redirect = $this -> redirect ;
// Application settings properties
$this -> application -> settings -> is_static = $this -> isStatic ;
$this -> application -> settings -> is_spa = $this -> isSpa ;
$this -> application -> settings -> is_build_server_enabled = $this -> isBuildServerEnabled ;
$this -> application -> settings -> is_preserve_repository_enabled = $this -> isPreserveRepositoryEnabled ;
$this -> application -> settings -> is_container_label_escape_enabled = $this -> isContainerLabelEscapeEnabled ;
$this -> application -> settings -> is_container_label_readonly_enabled = $this -> isContainerLabelReadonlyEnabled ;
$this -> application -> settings -> save ();
} else {
// From model to properties
$this -> name = $this -> application -> name ;
$this -> description = $this -> application -> description ;
$this -> fqdn = $this -> application -> fqdn ;
$this -> gitRepository = $this -> application -> git_repository ;
$this -> gitBranch = $this -> application -> git_branch ;
$this -> gitCommitSha = $this -> application -> git_commit_sha ;
$this -> installCommand = $this -> application -> install_command ;
$this -> buildCommand = $this -> application -> build_command ;
$this -> startCommand = $this -> application -> start_command ;
$this -> buildPack = $this -> application -> build_pack ;
$this -> staticImage = $this -> application -> static_image ;
$this -> baseDirectory = $this -> application -> base_directory ;
$this -> publishDirectory = $this -> application -> publish_directory ;
$this -> portsExposes = $this -> application -> ports_exposes ;
$this -> portsMappings = $this -> application -> ports_mappings ;
$this -> customNetworkAliases = $this -> application -> custom_network_aliases ;
$this -> dockerfile = $this -> application -> dockerfile ;
$this -> dockerfileLocation = $this -> application -> dockerfile_location ;
$this -> dockerfileTargetBuild = $this -> application -> dockerfile_target_build ;
$this -> dockerRegistryImageName = $this -> application -> docker_registry_image_name ;
$this -> dockerRegistryImageTag = $this -> application -> docker_registry_image_tag ;
$this -> dockerComposeLocation = $this -> application -> docker_compose_location ;
$this -> dockerCompose = $this -> application -> docker_compose ;
$this -> dockerComposeRaw = $this -> application -> docker_compose_raw ;
$this -> dockerComposeCustomStartCommand = $this -> application -> docker_compose_custom_start_command ;
$this -> dockerComposeCustomBuildCommand = $this -> application -> docker_compose_custom_build_command ;
$this -> customLabels = $this -> application -> parseContainerLabels ();
$this -> customDockerRunOptions = $this -> application -> custom_docker_run_options ;
$this -> preDeploymentCommand = $this -> application -> pre_deployment_command ;
$this -> preDeploymentCommandContainer = $this -> application -> pre_deployment_command_container ;
$this -> postDeploymentCommand = $this -> application -> post_deployment_command ;
$this -> postDeploymentCommandContainer = $this -> application -> post_deployment_command_container ;
$this -> customNginxConfiguration = $this -> application -> custom_nginx_configuration ;
$this -> isHttpBasicAuthEnabled = $this -> application -> is_http_basic_auth_enabled ;
$this -> httpBasicAuthUsername = $this -> application -> http_basic_auth_username ;
$this -> httpBasicAuthPassword = $this -> application -> http_basic_auth_password ;
$this -> watchPaths = $this -> application -> watch_paths ;
$this -> redirect = $this -> application -> redirect ;
// Application settings properties
$this -> isStatic = $this -> application -> settings -> is_static ;
$this -> isSpa = $this -> application -> settings -> is_spa ;
$this -> isBuildServerEnabled = $this -> application -> settings -> is_build_server_enabled ;
$this -> isPreserveRepositoryEnabled = $this -> application -> settings -> is_preserve_repository_enabled ;
$this -> isContainerLabelEscapeEnabled = $this -> application -> settings -> is_container_label_escape_enabled ;
$this -> isContainerLabelReadonlyEnabled = $this -> application -> settings -> is_container_label_readonly_enabled ;
}
2023-10-18 08:32:08 +00:00
}
2024-06-10 20:43:34 +00:00
2023-11-20 10:35:31 +00:00
public function instantSave ()
{
2025-08-22 14:47:59 +00:00
try {
$this -> authorize ( 'update' , $this -> application );
2024-08-12 14:06:24 +00:00
2025-10-13 13:38:59 +00:00
$oldPortsExposes = $this -> application -> ports_exposes ;
$oldIsContainerLabelEscapeEnabled = $this -> application -> settings -> is_container_label_escape_enabled ;
$oldIsPreserveRepositoryEnabled = $this -> application -> settings -> is_preserve_repository_enabled ;
2025-11-04 08:48:59 +00:00
$oldIsSpa = $this -> application -> settings -> is_spa ;
$oldIsHttpBasicAuthEnabled = $this -> application -> is_http_basic_auth_enabled ;
2025-10-13 13:38:59 +00:00
2025-11-04 07:43:33 +00:00
$this -> syncData ( toModel : true );
2025-10-13 13:38:59 +00:00
2025-11-04 08:48:59 +00:00
if ( $oldIsSpa !== $this -> isSpa ) {
$this -> generateNginxConfiguration ( $this -> isSpa ? 'spa' : 'static' );
2024-08-12 14:06:24 +00:00
}
2025-11-04 08:48:59 +00:00
if ( $oldIsHttpBasicAuthEnabled !== $this -> isHttpBasicAuthEnabled ) {
2025-08-22 14:47:59 +00:00
$this -> application -> save ();
}
2025-11-04 07:43:33 +00:00
2025-08-22 14:47:59 +00:00
$this -> dispatch ( 'success' , 'Settings saved.' );
$this -> application -> refresh ();
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2025-03-31 13:10:50 +00:00
2025-08-22 14:47:59 +00:00
// If port_exposes changed, reset default labels
2025-11-04 07:43:33 +00:00
if ( $oldPortsExposes !== $this -> portsExposes || $oldIsContainerLabelEscapeEnabled !== $this -> isContainerLabelEscapeEnabled ) {
2025-08-22 14:47:59 +00:00
$this -> resetDefaultLabels ( false );
}
2025-11-04 07:43:33 +00:00
if ( $oldIsPreserveRepositoryEnabled !== $this -> isPreserveRepositoryEnabled ) {
if ( $this -> isPreserveRepositoryEnabled === false ) {
2025-08-22 14:47:59 +00:00
$this -> application -> fileStorages -> each ( function ( $storage ) {
2025-11-04 07:43:33 +00:00
$storage -> is_based_on_git = $this -> isPreserveRepositoryEnabled ;
2025-08-22 14:47:59 +00:00
$storage -> save ();
});
}
}
2025-11-04 07:43:33 +00:00
if ( $this -> isContainerLabelReadonlyEnabled ) {
2025-08-22 14:47:59 +00:00
$this -> resetDefaultLabels ( false );
}
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
2023-11-20 10:35:31 +00:00
}
2024-06-10 20:43:34 +00:00
2025-07-18 21:22:24 +00:00
public function loadComposeFile ( $isInit = false , $showToast = true )
2023-11-24 14:48:23 +00:00
{
2023-11-27 10:54:55 +00:00
try {
2025-08-22 14:47:59 +00:00
$this -> authorize ( 'update' , $this -> application );
2023-11-27 10:54:55 +00:00
if ( $isInit && $this -> application -> docker_compose_raw ) {
return ;
}
2024-08-12 09:35:30 +00:00
2024-06-25 19:22:14 +00:00
[ 'parsedServices' => $this -> parsedServices , 'initialDockerComposeLocation' => $this -> initialDockerComposeLocation ] = $this -> application -> loadComposeFile ( $isInit );
2024-05-21 10:02:04 +00:00
if ( is_null ( $this -> parsedServices )) {
2025-07-18 21:22:24 +00:00
$showToast && $this -> dispatch ( 'error' , 'Failed to parse your docker-compose file. Please check the syntax and try again.' );
2024-06-10 20:43:34 +00:00
2024-05-21 10:02:04 +00:00
return ;
}
2025-08-04 08:46:59 +00:00
// Refresh parsedServiceDomains to reflect any changes in docker_compose_domains
$this -> application -> refresh ();
2025-10-27 11:48:20 +00:00
// Sync the docker_compose_raw from the model to the component property
// This ensures the Monaco editor displays the loaded compose file
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2025-10-27 11:48:20 +00:00
2025-08-04 08:46:59 +00:00
$this -> parsedServiceDomains = $this -> application -> docker_compose_domains ? json_decode ( $this -> application -> docker_compose_domains , true ) : [];
2025-09-25 11:19:12 +00:00
// Convert service names with dots and dashes to use underscores for HTML form binding
2025-08-04 08:46:59 +00:00
$sanitizedDomains = [];
foreach ( $this -> parsedServiceDomains as $serviceName => $domain ) {
2025-09-25 11:19:12 +00:00
$sanitizedKey = str ( $serviceName ) -> replace ( '-' , '_' ) -> replace ( '.' , '_' ) -> toString ();
2025-08-04 08:46:59 +00:00
$sanitizedDomains [ $sanitizedKey ] = $domain ;
}
$this -> parsedServiceDomains = $sanitizedDomains ;
2025-07-18 21:22:24 +00:00
$showToast && $this -> dispatch ( 'success' , 'Docker compose file loaded.' );
2024-04-12 08:28:40 +00:00
$this -> dispatch ( 'compose_loaded' );
2024-08-05 18:00:57 +00:00
$this -> dispatch ( 'refreshStorages' );
2024-04-29 09:59:19 +00:00
$this -> dispatch ( 'refreshEnvs' );
2023-11-27 10:54:55 +00:00
} catch ( \Throwable $e ) {
$this -> application -> docker_compose_location = $this -> initialDockerComposeLocation ;
$this -> application -> save ();
2024-06-10 20:43:34 +00:00
2023-11-27 10:54:55 +00:00
return handleError ( $e , $this );
2024-04-12 08:28:40 +00:00
} finally {
$this -> initLoadingCompose = false ;
2023-11-24 14:48:23 +00:00
}
2023-11-27 10:54:55 +00:00
}
2024-06-10 20:43:34 +00:00
2023-11-27 10:54:55 +00:00
public function generateDomain ( string $serviceName )
{
2025-08-22 14:47:59 +00:00
try {
$this -> authorize ( 'update' , $this -> application );
$uuid = new Cuid2 ;
$domain = generateUrl ( server : $this -> application -> destination -> server , random : $uuid );
2025-09-25 11:19:12 +00:00
$sanitizedKey = str ( $serviceName ) -> replace ( '-' , '_' ) -> replace ( '.' , '_' ) -> toString ();
2025-08-22 14:47:59 +00:00
$this -> parsedServiceDomains [ $sanitizedKey ][ 'domain' ] = $domain ;
// Convert back to original service names for storage
$originalDomains = [];
foreach ( $this -> parsedServiceDomains as $key => $value ) {
// Find the original service name by checking parsed services
$originalServiceName = $key ;
if ( isset ( $this -> parsedServices [ 'services' ])) {
foreach ( $this -> parsedServices [ 'services' ] as $originalName => $service ) {
2025-09-25 11:19:12 +00:00
if ( str ( $originalName ) -> replace ( '-' , '_' ) -> replace ( '.' , '_' ) -> toString () === $key ) {
2025-08-22 14:47:59 +00:00
$originalServiceName = $originalName ;
break ;
}
2025-07-14 12:45:01 +00:00
}
}
2025-08-22 14:47:59 +00:00
$originalDomains [ $originalServiceName ] = $value ;
2025-07-14 12:45:01 +00:00
}
2025-08-22 14:47:59 +00:00
$this -> application -> docker_compose_domains = json_encode ( $originalDomains );
$this -> application -> save ();
$this -> dispatch ( 'success' , 'Domain generated.' );
if ( $this -> application -> build_pack === 'dockercompose' ) {
$this -> loadComposeFile ( showToast : false );
}
2024-06-10 20:43:34 +00:00
2025-08-22 14:47:59 +00:00
return $domain ;
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
2023-11-24 14:48:23 +00:00
}
2024-06-10 20:43:34 +00:00
2025-10-13 13:38:59 +00:00
public function updatedBaseDirectory ()
2024-04-08 10:15:44 +00:00
{
2025-11-04 07:43:33 +00:00
if ( $this -> buildPack === 'dockercompose' ) {
2024-03-11 08:42:16 +00:00
$this -> loadComposeFile ();
}
}
2024-06-10 20:43:34 +00:00
2025-10-13 13:38:59 +00:00
public function updatedIsStatic ( $value )
2024-11-11 13:37:19 +00:00
{
if ( $value ) {
$this -> generateNginxConfiguration ();
}
}
2025-10-13 13:38:59 +00:00
public function updatedBuildPack ()
2023-10-18 08:32:08 +00:00
{
2025-08-23 16:19:50 +00:00
// Check if user has permission to update
try {
$this -> authorize ( 'update' , $this -> application );
} catch ( \Illuminate\Auth\Access\AuthorizationException $e ) {
// User doesn't have permission, revert the change and return
$this -> application -> refresh ();
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2025-08-23 16:19:50 +00:00
return ;
}
2025-10-13 13:38:59 +00:00
// Sync property to model before checking/modifying
2025-11-04 07:43:33 +00:00
$this -> syncData ( toModel : true );
2025-10-13 13:38:59 +00:00
2025-11-04 07:43:33 +00:00
if ( $this -> buildPack !== 'nixpacks' ) {
$this -> isStatic = false ;
2023-12-06 20:32:23 +00:00
$this -> application -> settings -> is_static = false ;
2023-10-13 19:08:59 +00:00
$this -> application -> settings -> save ();
2024-01-10 10:00:06 +00:00
} else {
$this -> resetDefaultLabels ( false );
2023-10-13 19:08:59 +00:00
}
2025-11-04 07:43:33 +00:00
if ( $this -> buildPack === 'dockercompose' ) {
2025-08-23 16:19:50 +00:00
// Only update if user has permission
try {
$this -> authorize ( 'update' , $this -> application );
2025-10-13 13:38:59 +00:00
$this -> fqdn = null ;
2025-08-23 16:19:50 +00:00
$this -> application -> fqdn = null ;
$this -> application -> settings -> save ();
} catch ( \Illuminate\Auth\Access\AuthorizationException $e ) {
// User doesn't have update permission, just continue without saving
}
2023-11-27 14:25:15 +00:00
}
2025-11-04 07:43:33 +00:00
if ( $this -> buildPack === 'static' ) {
$this -> portsExposes = '80' ;
$this -> application -> ports_exposes = '80' ;
2024-01-10 09:58:31 +00:00
$this -> resetDefaultLabels ( false );
2024-11-11 13:37:19 +00:00
$this -> generateNginxConfiguration ();
2024-01-10 09:58:31 +00:00
}
2023-10-11 10:12:25 +00:00
$this -> submit ();
2024-03-01 09:36:32 +00:00
$this -> dispatch ( 'buildPackUpdated' );
2023-10-11 10:12:25 +00:00
}
2024-06-10 20:43:34 +00:00
2023-10-18 08:32:08 +00:00
public function getWildcardDomain ()
{
2025-08-22 14:47:59 +00:00
try {
$this -> authorize ( 'update' , $this -> application );
$server = data_get ( $this -> application , 'destination.server' );
if ( $server ) {
2025-08-28 07:49:58 +00:00
$fqdn = generateUrl ( server : $server , random : $this -> application -> uuid );
2025-10-13 13:38:59 +00:00
$this -> fqdn = $fqdn ;
2025-11-04 07:43:33 +00:00
$this -> syncData ( toModel : true );
2025-08-22 14:47:59 +00:00
$this -> application -> save ();
2025-10-13 13:38:59 +00:00
$this -> application -> refresh ();
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2025-08-22 14:47:59 +00:00
$this -> resetDefaultLabels ();
$this -> dispatch ( 'success' , 'Wildcard domain generated.' );
}
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
2023-09-30 13:39:40 +00:00
}
}
2024-06-10 20:43:34 +00:00
2025-03-31 13:10:50 +00:00
public function generateNginxConfiguration ( $type = 'static' )
2024-11-11 13:37:19 +00:00
{
2025-08-22 14:47:59 +00:00
try {
$this -> authorize ( 'update' , $this -> application );
2025-11-04 07:43:33 +00:00
$this -> customNginxConfiguration = defaultNginxConfiguration ( $type );
$this -> syncData ( toModel : true );
2025-08-22 14:47:59 +00:00
$this -> application -> save ();
2025-10-13 13:38:59 +00:00
$this -> application -> refresh ();
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2025-08-22 14:47:59 +00:00
$this -> dispatch ( 'success' , 'Nginx configuration generated.' );
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
2024-11-11 13:37:19 +00:00
}
2024-10-22 09:25:03 +00:00
public function resetDefaultLabels ( $manualReset = false )
2023-04-19 10:42:15 +00:00
{
2024-10-10 08:24:11 +00:00
try {
2025-11-04 07:43:33 +00:00
if ( ! $this -> isContainerLabelReadonlyEnabled && ! $manualReset ) {
2024-10-10 08:24:11 +00:00
return ;
}
$this -> customLabels = str ( implode ( '|coolify|' , generateLabelsApplication ( $this -> application ))) -> replace ( '|coolify|' , " \n " );
2025-11-04 07:43:33 +00:00
$this -> application -> custom_labels = base64_encode ( $this -> customLabels );
2024-10-10 08:24:11 +00:00
$this -> application -> save ();
2025-10-13 13:38:59 +00:00
$this -> application -> refresh ();
2025-11-04 07:43:33 +00:00
$this -> syncData ();
if ( $this -> buildPack === 'dockercompose' ) {
2025-07-24 07:39:27 +00:00
$this -> loadComposeFile ( showToast : false );
2024-10-10 08:24:11 +00:00
}
$this -> dispatch ( 'configurationChanged' );
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
2024-05-15 15:52:14 +00:00
}
2023-04-19 10:42:15 +00:00
}
2023-08-08 09:51:36 +00:00
2024-04-08 10:15:44 +00:00
public function checkFqdns ( $showToaster = true )
2023-10-18 08:32:08 +00:00
{
2025-10-13 13:38:59 +00:00
if ( $this -> fqdn ) {
$domains = str ( $this -> fqdn ) -> trim () -> explode ( ',' );
2024-04-08 10:15:44 +00:00
if ( $this -> application -> additional_servers -> count () === 0 ) {
foreach ( $domains as $domain ) {
2025-09-09 07:00:35 +00:00
if ( ! validateDNSEntry ( $domain , $this -> application -> destination -> server )) {
2024-06-10 20:43:34 +00:00
$showToaster && $this -> dispatch ( 'error' , 'Validating DNS failed.' , " Make sure you have added the DNS records correctly.<br><br> $domain -> { $this -> application -> destination -> server -> ip } <br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help. " );
2024-04-08 10:15:44 +00:00
}
}
}
2025-08-28 08:52:41 +00:00
// Check for domain conflicts if not forcing save
if ( ! $this -> forceSaveDomains ) {
$result = checkDomainUsage ( resource : $this -> application );
if ( $result [ 'hasConflicts' ]) {
$this -> domainConflicts = $result [ 'conflicts' ];
$this -> showDomainConflictModal = true ;
return false ;
}
} else {
// Reset the force flag after using it
$this -> forceSaveDomains = false ;
}
2025-10-13 13:38:59 +00:00
$this -> fqdn = $domains -> implode ( ',' );
$this -> application -> fqdn = $this -> fqdn ;
2025-01-23 11:42:53 +00:00
$this -> resetDefaultLabels ( false );
2024-04-08 10:15:44 +00:00
}
2025-08-28 08:52:41 +00:00
return true ;
}
public function confirmDomainUsage ()
{
$this -> forceSaveDomains = true ;
$this -> showDomainConflictModal = false ;
$this -> submit ();
2023-10-18 08:32:08 +00:00
}
2024-06-11 09:36:42 +00:00
2024-12-16 09:19:11 +00:00
public function setRedirect ()
2024-06-11 09:32:08 +00:00
{
2025-08-22 14:47:59 +00:00
$this -> authorize ( 'update' , $this -> application );
2024-06-11 09:32:08 +00:00
try {
2024-10-17 20:08:23 +00:00
$has_www = collect ( $this -> application -> fqdns ) -> filter ( fn ( $fqdn ) => str ( $fqdn ) -> contains ( 'www.' )) -> count ();
2024-06-11 09:32:08 +00:00
if ( $has_www === 0 && $this -> application -> redirect === 'www' ) {
$this -> dispatch ( 'error' , 'You want to redirect to www, but you do not have a www domain set.<br><br>Please add www to your domain list and as an A DNS record (if applicable).' );
2024-06-11 09:36:42 +00:00
2024-06-11 09:32:08 +00:00
return ;
}
$this -> application -> save ();
$this -> resetDefaultLabels ();
$this -> dispatch ( 'success' , 'Redirect updated.' );
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
}
2024-06-10 20:43:34 +00:00
2023-10-18 08:32:08 +00:00
public function submit ( $showToaster = true )
2023-04-19 10:42:15 +00:00
{
2023-05-16 13:27:47 +00:00
try {
2025-08-22 14:47:59 +00:00
$this -> authorize ( 'update' , $this -> application );
2025-10-06 19:25:24 +00:00
$this -> validate ();
2025-10-13 13:38:59 +00:00
$oldPortsExposes = $this -> application -> ports_exposes ;
$oldIsContainerLabelEscapeEnabled = $this -> application -> settings -> is_container_label_escape_enabled ;
$oldDockerComposeLocation = $this -> initialDockerComposeLocation ;
// Process FQDN with intermediate variable to avoid Collection/string confusion
$this -> fqdn = str ( $this -> fqdn ) -> replaceEnd ( ',' , '' ) -> trim () -> toString ();
$this -> fqdn = str ( $this -> fqdn ) -> replaceStart ( ',' , '' ) -> trim () -> toString ();
$domains = str ( $this -> fqdn ) -> trim () -> explode ( ',' ) -> map ( function ( $domain ) {
2025-08-29 13:09:03 +00:00
$domain = trim ( $domain );
2024-10-11 08:00:50 +00:00
Url :: fromString ( $domain , [ 'http' , 'https' ]);
2024-10-17 20:08:23 +00:00
2025-08-29 13:09:03 +00:00
return str ( $domain ) -> lower ();
2024-04-08 10:15:44 +00:00
});
2024-10-20 20:15:31 +00:00
2025-10-13 13:38:59 +00:00
$this -> fqdn = $domains -> unique () -> implode ( ',' );
$warning = sslipDomainWarning ( $this -> fqdn );
2024-10-20 20:15:31 +00:00
if ( $warning ) {
$this -> dispatch ( 'warning' , __ ( 'warning.sslipdomain' ));
}
2025-10-13 13:38:59 +00:00
2025-11-04 07:43:33 +00:00
$this -> syncData ( toModel : true );
2024-10-11 07:56:45 +00:00
if ( $this -> application -> isDirty ( 'redirect' )) {
2024-12-16 09:19:11 +00:00
$this -> setRedirect ();
2024-10-11 07:56:45 +00:00
}
2025-03-24 10:43:10 +00:00
if ( $this -> application -> isDirty ( 'dockerfile' )) {
$this -> application -> parseHealthcheckFromDockerfile ( $this -> application -> dockerfile );
}
2024-04-08 10:15:44 +00:00
2025-08-28 08:52:41 +00:00
if ( ! $this -> checkFqdns ()) {
return ; // Stop if there are conflicts and user hasn't confirmed
}
2024-04-08 10:15:44 +00:00
$this -> application -> save ();
2024-07-17 12:52:40 +00:00
if ( ! $this -> customLabels && $this -> application -> destination -> server -> proxyType () !== 'NONE' && ! $this -> application -> settings -> is_container_label_readonly_enabled ) {
2024-06-11 09:36:42 +00:00
$this -> customLabels = str ( implode ( '|coolify|' , generateLabelsApplication ( $this -> application ))) -> replace ( '|coolify|' , " \n " );
2024-01-02 16:14:52 +00:00
$this -> application -> custom_labels = base64_encode ( $this -> customLabels );
$this -> application -> save ();
}
2025-11-04 07:43:33 +00:00
if ( $this -> buildPack === 'dockercompose' && $oldDockerComposeLocation !== $this -> dockerComposeLocation ) {
2025-07-24 07:39:27 +00:00
$compose_return = $this -> loadComposeFile ( showToast : false );
2024-05-06 11:03:55 +00:00
if ( $compose_return instanceof \Livewire\Features\SupportEvents\Event ) {
2024-05-15 15:52:14 +00:00
return ;
2024-05-06 11:03:55 +00:00
}
2023-11-27 10:54:55 +00:00
}
2024-07-12 08:35:50 +00:00
2025-11-04 07:43:33 +00:00
if ( $oldPortsExposes !== $this -> portsExposes || $oldIsContainerLabelEscapeEnabled !== $this -> isContainerLabelEscapeEnabled ) {
2024-04-08 10:15:44 +00:00
$this -> resetDefaultLabels ();
2023-10-20 10:34:25 +00:00
}
2025-11-04 07:43:33 +00:00
if ( $this -> buildPack === 'dockerimage' ) {
2023-10-11 10:10:40 +00:00
$this -> validate ([
2025-11-04 07:43:33 +00:00
'dockerRegistryImageName' => 'required' ,
2023-10-11 10:10:40 +00:00
]);
}
2024-04-08 10:15:44 +00:00
2025-11-04 07:43:33 +00:00
if ( $this -> customDockerRunOptions ) {
$this -> customDockerRunOptions = str ( $this -> customDockerRunOptions ) -> trim () -> toString ();
$this -> application -> custom_docker_run_options = $this -> customDockerRunOptions ;
2024-01-29 15:07:00 +00:00
}
2025-10-13 13:38:59 +00:00
if ( $this -> dockerfile ) {
$port = get_port_from_dockerfile ( $this -> dockerfile );
2025-11-04 07:43:33 +00:00
if ( $port && ! $this -> portsExposes ) {
$this -> portsExposes = $port ;
2023-08-21 16:00:12 +00:00
$this -> application -> ports_exposes = $port ;
}
2023-08-11 20:41:47 +00:00
}
2025-11-04 07:43:33 +00:00
if ( $this -> baseDirectory && $this -> baseDirectory !== '/' ) {
$this -> baseDirectory = rtrim ( $this -> baseDirectory , '/' );
$this -> application -> base_directory = $this -> baseDirectory ;
2023-07-07 12:56:20 +00:00
}
2025-11-04 07:43:33 +00:00
if ( $this -> publishDirectory && $this -> publishDirectory !== '/' ) {
$this -> publishDirectory = rtrim ( $this -> publishDirectory , '/' );
$this -> application -> publish_directory = $this -> publishDirectory ;
2023-07-07 12:56:20 +00:00
}
2025-11-04 07:43:33 +00:00
if ( $this -> buildPack === 'dockercompose' ) {
2025-08-11 19:22:26 +00:00
$this -> application -> docker_compose_domains = json_encode ( $this -> parsedServiceDomains );
if ( $this -> application -> isDirty ( 'docker_compose_domains' )) {
foreach ( $this -> parsedServiceDomains as $service ) {
$domain = data_get ( $service , 'domain' );
if ( $domain ) {
2025-09-09 07:00:35 +00:00
if ( ! validateDNSEntry ( $domain , $this -> application -> destination -> server )) {
2025-08-11 19:22:26 +00:00
$showToaster && $this -> dispatch ( 'error' , 'Validating DNS failed.' , " Make sure you have added the DNS records correctly.<br><br> $domain -> { $this -> application -> destination -> server -> ip } <br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help. " );
2025-07-14 12:45:01 +00:00
}
}
}
2025-08-28 08:52:41 +00:00
// Check for domain conflicts if not forcing save
if ( ! $this -> forceSaveDomains ) {
$result = checkDomainUsage ( resource : $this -> application );
if ( $result [ 'hasConflicts' ]) {
$this -> domainConflicts = $result [ 'conflicts' ];
$this -> showDomainConflictModal = true ;
return ;
}
} else {
// Reset the force flag after using it
$this -> forceSaveDomains = false ;
}
2025-08-11 19:22:26 +00:00
$this -> application -> save ();
2024-07-12 08:35:50 +00:00
$this -> resetDefaultLabels ();
}
2023-11-28 10:10:48 +00:00
}
2023-12-12 15:34:05 +00:00
$this -> application -> custom_labels = base64_encode ( $this -> customLabels );
2023-05-16 13:27:47 +00:00
$this -> application -> save ();
2025-10-13 13:38:59 +00:00
$this -> application -> refresh ();
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2024-10-20 20:15:31 +00:00
$showToaster && ! $warning && $this -> dispatch ( 'success' , 'Application settings updated!' );
2023-09-11 15:36:30 +00:00
} catch ( \Throwable $e ) {
2025-10-13 13:38:59 +00:00
$this -> application -> refresh ();
2025-11-04 07:43:33 +00:00
$this -> syncData ();
2024-10-17 20:08:23 +00:00
2023-09-15 13:34:25 +00:00
return handleError ( $e , $this );
2023-10-18 08:32:08 +00:00
} finally {
2024-04-12 10:44:49 +00:00
$this -> dispatch ( 'configurationChanged' );
2023-05-16 13:27:47 +00:00
}
2023-04-19 10:42:15 +00:00
}
2024-10-17 20:08:23 +00:00
2024-10-08 13:11:19 +00:00
public function downloadConfig ()
{
$config = GenerateConfig :: run ( $this -> application , true );
$fileName = str ( $this -> application -> name ) -> slug () -> append ( '_config.json' );
return response () -> streamDownload ( function () use ( $config ) {
echo $config ;
}, $fileName , [
'Content-Type' => 'application/json' ,
2024-10-17 20:08:23 +00:00
'Content-Disposition' => 'attachment; filename=' . $fileName ,
2024-10-08 13:11:19 +00:00
]);
}
2025-07-18 18:48:51 +00:00
private function updateServiceEnvironmentVariables ()
{
$domains = collect ( json_decode ( $this -> application -> docker_compose_domains , true )) ? ? collect ([]);
foreach ( $domains as $serviceName => $service ) {
2025-09-11 11:59:02 +00:00
$serviceNameFormatted = str ( $serviceName ) -> upper () -> replace ( '-' , '_' ) -> replace ( '.' , '_' );
2025-07-18 18:48:51 +00:00
$domain = data_get ( $service , 'domain' );
2025-07-24 07:39:27 +00:00
// Delete SERVICE_FQDN_ and SERVICE_URL_ variables if domain is removed
$this -> application -> environment_variables () -> where ( 'resourceable_type' , Application :: class )
-> where ( 'resourceable_id' , $this -> application -> id )
-> where ( 'key' , 'LIKE' , " SERVICE_FQDN_ { $serviceNameFormatted } % " )
-> delete ();
$this -> application -> environment_variables () -> where ( 'resourceable_type' , Application :: class )
-> where ( 'resourceable_id' , $this -> application -> id )
-> where ( 'key' , 'LIKE' , " SERVICE_URL_ { $serviceNameFormatted } % " )
-> delete ();
2025-07-18 18:48:51 +00:00
if ( $domain ) {
// Create or update SERVICE_FQDN_ and SERVICE_URL_ variables
$fqdn = Url :: fromString ( $domain );
$port = $fqdn -> getPort ();
$path = $fqdn -> getPath ();
2025-07-24 07:39:27 +00:00
$urlValue = $fqdn -> getScheme () . '://' . $fqdn -> getHost ();
2025-07-18 18:48:51 +00:00
if ( $path !== '/' ) {
2025-07-24 07:39:27 +00:00
$urlValue = $urlValue . $path ;
2025-07-18 18:48:51 +00:00
}
2025-07-24 07:39:27 +00:00
$fqdnValue = str ( $domain ) -> after ( '://' );
2025-07-18 18:48:51 +00:00
if ( $path !== '/' ) {
2025-07-24 07:39:27 +00:00
$fqdnValue = $fqdnValue . $path ;
2025-07-18 18:48:51 +00:00
}
// Create/update SERVICE_FQDN_
2025-07-24 07:39:27 +00:00
$this -> application -> environment_variables () -> updateOrCreate ([
2025-07-18 18:48:51 +00:00
'key' => " SERVICE_FQDN_ { $serviceNameFormatted } " ,
], [
'value' => $fqdnValue ,
'is_preview' => false ,
]);
// Create/update SERVICE_URL_
2025-07-24 07:39:27 +00:00
$this -> application -> environment_variables () -> updateOrCreate ([
2025-07-18 18:48:51 +00:00
'key' => " SERVICE_URL_ { $serviceNameFormatted } " ,
], [
'value' => $urlValue ,
'is_preview' => false ,
]);
// Create/update port-specific variables if port exists
2025-07-24 07:39:27 +00:00
if ( filled ( $port )) {
$this -> application -> environment_variables () -> updateOrCreate ([
2025-07-18 18:48:51 +00:00
'key' => " SERVICE_FQDN_ { $serviceNameFormatted } _ { $port } " ,
], [
'value' => $fqdnValue ,
'is_preview' => false ,
]);
2025-07-24 07:39:27 +00:00
$this -> application -> environment_variables () -> updateOrCreate ([
2025-07-18 18:48:51 +00:00
'key' => " SERVICE_URL_ { $serviceNameFormatted } _ { $port } " ,
], [
'value' => $urlValue ,
'is_preview' => false ,
]);
}
}
}
}
2025-11-10 12:43:27 +00:00
public function getDetectedPortInfoProperty () : ? array
{
$detectedPort = $this -> application -> detectPortFromEnvironment ();
if ( ! $detectedPort ) {
return null ;
}
$portsExposesArray = $this -> application -> ports_exposes_array ;
$isMatch = in_array ( $detectedPort , $portsExposesArray );
$isEmpty = empty ( $portsExposesArray );
return [
'port' => $detectedPort ,
'matches' => $isMatch ,
'isEmpty' => $isEmpty ,
];
}
2025-11-18 12:07:12 +00:00
public function getDockerComposeBuildCommandPreviewProperty () : string
{
if ( ! $this -> dockerComposeCustomBuildCommand ) {
return '' ;
}
// Use relative path for clarity in preview (e.g., ./backend/docker-compose.yaml)
// Actual deployment uses absolute path: /artifacts/{deployment_uuid}{base_directory}{docker_compose_location}
return injectDockerComposeFlags (
$this -> dockerComposeCustomBuildCommand ,
" . { $this -> baseDirectory } { $this -> dockerComposeLocation } " ,
'/artifacts/build-time.env'
);
}
public function getDockerComposeStartCommandPreviewProperty () : string
{
if ( ! $this -> dockerComposeCustomStartCommand ) {
return '' ;
}
// Use relative path for clarity in preview (e.g., ./backend/docker-compose.yaml)
// Placeholder {workdir}/.env shows it's the workdir .env file (runtime env, not build-time)
return injectDockerComposeFlags (
$this -> dockerComposeCustomStartCommand ,
" . { $this -> baseDirectory } { $this -> dockerComposeLocation } " ,
'{workdir}/.env'
);
}
2023-04-19 10:42:15 +00:00
}