fix(api): application endpoint issues part 2 (#7948)

This commit is contained in:
🏔️ Peak 2026-01-15 14:06:07 +01:00 committed by GitHub
commit fbacf7076e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 537 additions and 149 deletions

View file

@ -153,11 +153,14 @@ public function applications(Request $request)
'destination_uuid' => ['type' => 'string', 'description' => 'The destination UUID.'],
'name' => ['type' => 'string', 'description' => 'The application name.'],
'description' => ['type' => 'string', 'description' => 'The application description.'],
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
'git_commit_sha' => ['type' => 'string', 'description' => 'The git commit SHA.'],
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
'is_spa' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'],
'is_auto_deploy_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'],
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
@ -198,6 +201,7 @@ public function applications(Request $request)
// 'github_app_uuid' => ['type' => 'string', 'description' => 'The Github App UUID.'],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
'dockerfile' => ['type' => 'string', 'description' => 'The Dockerfile content.'],
'dockerfile_location' => ['type' => 'string', 'description' => 'The Dockerfile location in the repository.'],
'docker_compose_location' => ['type' => 'string', 'description' => 'The Docker Compose location.'],
'docker_compose_custom_start_command' => ['type' => 'string', 'description' => 'The Docker Compose custom start command.'],
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
@ -315,11 +319,14 @@ public function create_public_application(Request $request)
'build_pack' => ['type' => 'string', 'enum' => ['nixpacks', 'static', 'dockerfile', 'dockercompose'], 'description' => 'The build pack type.'],
'name' => ['type' => 'string', 'description' => 'The application name.'],
'description' => ['type' => 'string', 'description' => 'The application description.'],
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
'git_commit_sha' => ['type' => 'string', 'description' => 'The git commit SHA.'],
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
'is_spa' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'],
'is_auto_deploy_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'],
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
@ -359,6 +366,7 @@ public function create_public_application(Request $request)
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
'dockerfile' => ['type' => 'string', 'description' => 'The Dockerfile content.'],
'dockerfile_location' => ['type' => 'string', 'description' => 'The Dockerfile location in the repository'],
'docker_compose_location' => ['type' => 'string', 'description' => 'The Docker Compose location.'],
'docker_compose_custom_start_command' => ['type' => 'string', 'description' => 'The Docker Compose custom start command.'],
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
@ -476,11 +484,14 @@ public function create_private_gh_app_application(Request $request)
'build_pack' => ['type' => 'string', 'enum' => ['nixpacks', 'static', 'dockerfile', 'dockercompose'], 'description' => 'The build pack type.'],
'name' => ['type' => 'string', 'description' => 'The application name.'],
'description' => ['type' => 'string', 'description' => 'The application description.'],
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
'git_commit_sha' => ['type' => 'string', 'description' => 'The git commit SHA.'],
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
'is_spa' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'],
'is_auto_deploy_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'],
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
@ -520,6 +531,7 @@ public function create_private_gh_app_application(Request $request)
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
'dockerfile' => ['type' => 'string', 'description' => 'The Dockerfile content.'],
'dockerfile_location' => ['type' => 'string', 'description' => 'The Dockerfile location in the repository.'],
'docker_compose_location' => ['type' => 'string', 'description' => 'The Docker Compose location.'],
'docker_compose_custom_start_command' => ['type' => 'string', 'description' => 'The Docker Compose custom start command.'],
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
@ -635,7 +647,7 @@ public function create_private_deploy_key_application(Request $request)
'destination_uuid' => ['type' => 'string', 'description' => 'The destination UUID.'],
'name' => ['type' => 'string', 'description' => 'The application name.'],
'description' => ['type' => 'string', 'description' => 'The application description.'],
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
'ports_mappings' => ['type' => 'string', 'description' => 'The ports mappings.'],
@ -671,6 +683,7 @@ public function create_private_deploy_key_application(Request $request)
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
'is_http_basic_auth_enabled' => ['type' => 'boolean', 'description' => 'HTTP Basic Authentication enabled.'],
'http_basic_auth_username' => ['type' => 'string', 'nullable' => true, 'description' => 'Username for HTTP Basic Authentication'],
@ -771,7 +784,7 @@ public function create_dockerfile_application(Request $request)
'destination_uuid' => ['type' => 'string', 'description' => 'The destination UUID.'],
'name' => ['type' => 'string', 'description' => 'The application name.'],
'description' => ['type' => 'string', 'description' => 'The application description.'],
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
'ports_mappings' => ['type' => 'string', 'description' => 'The ports mappings.'],
'health_check_enabled' => ['type' => 'boolean', 'description' => 'Health check enabled.'],
'health_check_path' => ['type' => 'string', 'description' => 'Health check path.'],
@ -804,6 +817,7 @@ public function create_dockerfile_application(Request $request)
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
'is_http_basic_auth_enabled' => ['type' => 'boolean', 'description' => 'HTTP Basic Authentication enabled.'],
'http_basic_auth_username' => ['type' => 'string', 'nullable' => true, 'description' => 'Username for HTTP Basic Authentication'],
@ -987,7 +1001,7 @@ private function create_application(Request $request, $type)
if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return;
}
$allowedFields = ['project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'custom_network_aliases', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server', 'static_image', 'custom_nginx_configuration', 'is_http_basic_auth_enabled', 'http_basic_auth_username', 'http_basic_auth_password', 'connect_to_docker_network', 'force_domain_override', 'autogenerate_domain', 'is_container_label_escape_enabled'];
$allowedFields = ['project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'is_spa', 'is_auto_deploy_enabled', 'is_force_https_enabled', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'custom_network_aliases', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'dockerfile_location', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server', 'static_image', 'custom_nginx_configuration', 'is_http_basic_auth_enabled', 'http_basic_auth_username', 'http_basic_auth_password', 'connect_to_docker_network', 'force_domain_override', 'autogenerate_domain', 'is_container_label_escape_enabled'];
$validator = customApiValidator($request->all(), [
'name' => 'string|max:255',
@ -1030,6 +1044,9 @@ private function create_application(Request $request, $type)
$githubAppUuid = $request->github_app_uuid;
$useBuildServer = $request->use_build_server;
$isStatic = $request->is_static;
$isSpa = $request->is_spa;
$isAutoDeployEnabled = $request->is_auto_deploy_enabled;
$isForceHttpsEnabled = $request->is_force_https_enabled;
$connectToDockerNetwork = $request->connect_to_docker_network;
$customNginxConfiguration = $request->custom_nginx_configuration;
$isContainerLabelEscapeEnabled = $request->boolean('is_container_label_escape_enabled', true);
@ -1130,39 +1147,63 @@ private function create_application(Request $request, $type)
$dockerComposeDomainsJson = collect();
if ($request->has('docker_compose_domains')) {
$dockerComposeDomains = collect($request->docker_compose_domains);
$domainErrors = [];
foreach ($dockerComposeDomains as $index => $item) {
// Collect all URLs from all docker_compose_domains items
$urls = $dockerComposeDomains->flatMap(function ($item) {
$domainValue = data_get($item, 'domain');
if (filled($domainValue)) {
$urls = str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim();
str($urls)->explode(',')->each(function ($url) use (&$domainErrors) {
$url = trim($url);
if (empty($url)) {
return;
}
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$domainErrors[] = "Invalid URL: {$url}";
return;
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$domainErrors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
});
if (blank($domainValue)) {
return [];
}
return str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim()->explode(',')->map(fn ($url) => trim($url))->filter();
});
$errors = [];
$urls = $urls->map(function ($url) use (&$errors) {
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$errors[] = "Invalid URL: {$url}";
return $url;
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
return $url;
});
$duplicates = $urls->duplicates()->unique()->values();
if ($duplicates->isNotEmpty() && ! $request->boolean('force_domain_override')) {
$errors[] = 'The current request contains conflicting URLs: '.implode(', ', $duplicates->toArray()).' Use force_domain_override=true to proceed.';
}
if (! empty($domainErrors)) {
if (count($errors) > 0) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'docker_compose_domains' => $domainErrors,
],
'errors' => ['docker_compose_domains' => $errors],
], 422);
}
// Check for domain conflicts
if ($urls->isNotEmpty()) {
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId);
if (isset($result['error'])) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['docker_compose_domains' => $result['error']],
], 422);
}
if ($result['hasConflicts'] && ! $request->boolean('force_domain_override')) {
return response()->json([
'message' => 'Domain conflicts detected. Use force_domain_override=true to proceed.',
'conflicts' => $result['conflicts'],
'warning' => 'Using the same domain for multiple resources can cause routing conflicts and unpredictable behavior.',
], 409);
}
}
$dockerComposeDomains->each(function ($domain) use ($dockerComposeDomainsJson) {
$dockerComposeDomainsJson->put(data_get($domain, 'name'), ['domain' => data_get($domain, 'domain')]);
});
@ -1187,6 +1228,18 @@ private function create_application(Request $request, $type)
$application->settings->is_static = $isStatic;
$application->settings->save();
}
if (isset($isSpa)) {
$application->settings->is_spa = $isSpa;
$application->settings->save();
}
if (isset($isAutoDeployEnabled)) {
$application->settings->is_auto_deploy_enabled = $isAutoDeployEnabled;
$application->settings->save();
}
if (isset($isForceHttpsEnabled)) {
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
$application->settings->save();
}
if (isset($connectToDockerNetwork)) {
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
$application->settings->save();
@ -1319,39 +1372,63 @@ private function create_application(Request $request, $type)
$dockerComposeDomainsJson = collect();
if ($request->has('docker_compose_domains')) {
$dockerComposeDomains = collect($request->docker_compose_domains);
$domainErrors = [];
foreach ($dockerComposeDomains as $index => $item) {
// Collect all URLs from all docker_compose_domains items
$urls = $dockerComposeDomains->flatMap(function ($item) {
$domainValue = data_get($item, 'domain');
if (filled($domainValue)) {
$urls = str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim();
str($urls)->explode(',')->each(function ($url) use (&$domainErrors) {
$url = trim($url);
if (empty($url)) {
return;
}
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$domainErrors[] = "Invalid URL: {$url}";
return;
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$domainErrors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
});
if (blank($domainValue)) {
return [];
}
return str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim()->explode(',')->map(fn ($url) => trim($url))->filter();
});
$errors = [];
$urls = $urls->map(function ($url) use (&$errors) {
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$errors[] = "Invalid URL: {$url}";
return $url;
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
return $url;
});
$duplicates = $urls->duplicates()->unique()->values();
if ($duplicates->isNotEmpty() && ! $request->boolean('force_domain_override')) {
$errors[] = 'The current request contains conflicting URLs: '.implode(', ', $duplicates->toArray()).' Use force_domain_override=true to proceed. ';
}
if (! empty($domainErrors)) {
if (count($errors) > 0) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'docker_compose_domains' => $domainErrors,
],
'errors' => ['docker_compose_domains' => $errors],
], 422);
}
// Check for domain conflicts
if ($urls->isNotEmpty()) {
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId);
if (isset($result['error'])) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['docker_compose_domains' => $result['error']],
], 422);
}
if ($result['hasConflicts'] && ! $request->boolean('force_domain_override')) {
return response()->json([
'message' => 'Domain conflicts detected. Use force_domain_override=true to proceed.',
'conflicts' => $result['conflicts'],
'warning' => 'Using the same domain for multiple resources can cause routing conflicts and unpredictable behavior.',
], 409);
}
}
$dockerComposeDomains->each(function ($domain) use ($dockerComposeDomainsJson) {
$dockerComposeDomainsJson->put(data_get($domain, 'name'), ['domain' => data_get($domain, 'domain')]);
});
@ -1376,6 +1453,26 @@ private function create_application(Request $request, $type)
$application->fqdn = generateUrl(server: $server, random: $application->uuid);
$application->save();
}
if (isset($isStatic)) {
$application->settings->is_static = $isStatic;
$application->settings->save();
}
if (isset($isSpa)) {
$application->settings->is_spa = $isSpa;
$application->settings->save();
}
if (isset($isAutoDeployEnabled)) {
$application->settings->is_auto_deploy_enabled = $isAutoDeployEnabled;
$application->settings->save();
}
if (isset($isForceHttpsEnabled)) {
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
$application->settings->save();
}
if (isset($connectToDockerNetwork)) {
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
$application->settings->save();
}
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save();
@ -1476,39 +1573,63 @@ private function create_application(Request $request, $type)
$dockerComposeDomainsJson = collect();
if ($request->has('docker_compose_domains')) {
$dockerComposeDomains = collect($request->docker_compose_domains);
$domainErrors = [];
foreach ($dockerComposeDomains as $index => $item) {
// Collect all URLs from all docker_compose_domains items
$urls = $dockerComposeDomains->flatMap(function ($item) {
$domainValue = data_get($item, 'domain');
if (filled($domainValue)) {
$urls = str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim();
str($urls)->explode(',')->each(function ($url) use (&$domainErrors) {
$url = trim($url);
if (empty($url)) {
return;
}
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$domainErrors[] = "Invalid URL: {$url}";
return;
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$domainErrors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
});
if (blank($domainValue)) {
return [];
}
return str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim()->explode(',')->map(fn ($url) => trim($url))->filter();
});
$errors = [];
$urls = $urls->map(function ($url) use (&$errors) {
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$errors[] = "Invalid URL: {$url}";
return $url;
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
return $url;
});
$duplicates = $urls->duplicates()->unique()->values();
if ($duplicates->isNotEmpty() && ! $request->boolean('force_domain_override')) {
$errors[] = 'The current request contains conflicting URLs: '.implode(', ', $duplicates->toArray()).' Use force_domain_override=true to proceed.';
}
if (! empty($domainErrors)) {
if (count($errors) > 0) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'docker_compose_domains' => $domainErrors,
],
'errors' => ['docker_compose_domains' => $errors],
], 422);
}
// Check for domain conflicts
if ($urls->isNotEmpty()) {
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId);
if (isset($result['error'])) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['docker_compose_domains' => $result['error']],
], 422);
}
if ($result['hasConflicts'] && ! $request->boolean('force_domain_override')) {
return response()->json([
'message' => 'Domain conflicts detected. Use force_domain_override=true to proceed.',
'conflicts' => $result['conflicts'],
'warning' => 'Using the same domain for multiple resources can cause routing conflicts and unpredictable behavior.',
], 409);
}
}
$dockerComposeDomains->each(function ($domain) use ($dockerComposeDomainsJson) {
$dockerComposeDomainsJson->put(data_get($domain, 'name'), ['domain' => data_get($domain, 'domain')]);
});
@ -1529,6 +1650,26 @@ private function create_application(Request $request, $type)
$application->fqdn = generateUrl(server: $server, random: $application->uuid);
$application->save();
}
if (isset($isStatic)) {
$application->settings->is_static = $isStatic;
$application->settings->save();
}
if (isset($isSpa)) {
$application->settings->is_spa = $isSpa;
$application->settings->save();
}
if (isset($isAutoDeployEnabled)) {
$application->settings->is_auto_deploy_enabled = $isAutoDeployEnabled;
$application->settings->save();
}
if (isset($isForceHttpsEnabled)) {
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
$application->settings->save();
}
if (isset($connectToDockerNetwork)) {
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
$application->settings->save();
}
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save();
@ -1632,6 +1773,14 @@ private function create_application(Request $request, $type)
$application->fqdn = generateUrl(server: $server, random: $application->uuid);
$application->save();
}
if (isset($isForceHttpsEnabled)) {
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
$application->settings->save();
}
if (isset($connectToDockerNetwork)) {
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
$application->settings->save();
}
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save();
@ -1734,6 +1883,14 @@ private function create_application(Request $request, $type)
$application->fqdn = generateUrl(server: $server, random: $application->uuid);
$application->save();
}
if (isset($isForceHttpsEnabled)) {
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
$application->settings->save();
}
if (isset($connectToDockerNetwork)) {
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
$application->settings->save();
}
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save();
@ -2150,11 +2307,14 @@ public function delete_by_uuid(Request $request)
'build_pack' => ['type' => 'string', 'enum' => ['nixpacks', 'static', 'dockerfile', 'dockercompose'], 'description' => 'The build pack type.'],
'name' => ['type' => 'string', 'description' => 'The application name.'],
'description' => ['type' => 'string', 'description' => 'The application description.'],
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
'git_commit_sha' => ['type' => 'string', 'description' => 'The git commit SHA.'],
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
'is_spa' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'],
'is_auto_deploy_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'],
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
'start_command' => ['type' => 'string', 'description' => 'The start command.'],
@ -2193,6 +2353,7 @@ public function delete_by_uuid(Request $request)
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
'dockerfile' => ['type' => 'string', 'description' => 'The Dockerfile content.'],
'dockerfile_location' => ['type' => 'string', 'description' => 'The Dockerfile location in the repository.'],
'docker_compose_location' => ['type' => 'string', 'description' => 'The Docker Compose location.'],
'docker_compose_custom_start_command' => ['type' => 'string', 'description' => 'The Docker Compose custom start command.'],
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
@ -2297,7 +2458,7 @@ public function update_by_uuid(Request $request)
$this->authorize('update', $application);
$server = $application->destination->server;
$allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings','custom_network_aliases', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy', 'use_build_server', 'custom_nginx_configuration', 'is_http_basic_auth_enabled', 'http_basic_auth_username', 'http_basic_auth_password', 'connect_to_docker_network', 'force_domain_override', 'is_container_label_escape_enabled'];
$allowedFields = ['name', 'description', 'is_static', 'is_spa', 'is_auto_deploy_enabled', 'is_force_https_enabled', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'custom_network_aliases', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'dockerfile_location', 'docker_compose_location', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy', 'use_build_server', 'custom_nginx_configuration', 'is_http_basic_auth_enabled', 'http_basic_auth_username', 'http_basic_auth_password', 'connect_to_docker_network', 'force_domain_override', 'is_container_label_escape_enabled'];
$validationRules = [
'name' => 'string|max:255',
@ -2411,18 +2572,30 @@ public function update_by_uuid(Request $request)
$requestHasDomains = $request->has('domains');
if ($requestHasDomains && $server->isProxyShouldRun()) {
$uuid = $request->uuid;
$fqdn = $request->domains;
$fqdn = str($fqdn)->replaceEnd(',', '')->trim();
$fqdn = str($fqdn)->replaceStart(',', '')->trim();
$urls = $request->domains;
$urls = str($urls)->replaceStart(',', '')->replaceEnd(',', '')->trim();
$errors = [];
$fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) {
$domain = trim($domain);
if (filter_var($domain, FILTER_VALIDATE_URL) === false || ! preg_match('/^https?:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}/', $domain)) {
$errors[] = 'Invalid domain: '.$domain;
$urls = str($urls)->trim()->explode(',')->map(function ($url) use (&$errors) {
$url = trim($url);
// If "domains" is empty clear all URLs from the fqdn column
if (blank($url)) {
return null;
}
return $domain;
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$errors[] = 'Invalid URL: '.$url;
return $url;
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
return str($url)->lower();
});
if (count($errors) > 0) {
return response()->json([
'message' => 'Validation failed.',
@ -2430,7 +2603,7 @@ public function update_by_uuid(Request $request)
], 422);
}
// Check for domain conflicts
$result = checkIfDomainIsAlreadyUsedViaAPI($fqdn, $teamId, $uuid);
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId, $uuid);
if (isset($result['error'])) {
return response()->json([
'message' => 'Validation failed.',
@ -2460,39 +2633,63 @@ public function update_by_uuid(Request $request)
}
$dockerComposeDomains = collect($request->docker_compose_domains);
$domainErrors = [];
foreach ($dockerComposeDomains as $item) {
// Collect all URLs from all docker_compose_domains items
$urls = $dockerComposeDomains->flatMap(function ($item) {
$domainValue = data_get($item, 'domain');
if (filled($domainValue)) {
$urls = str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim();
str($urls)->explode(',')->each(function ($url) use (&$domainErrors) {
$url = trim($url);
if (empty($url)) {
return;
}
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$domainErrors[] = "Invalid URL: {$url}";
return;
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$domainErrors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
});
if (blank($domainValue)) {
return [];
}
return str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim()->explode(',')->map(fn ($url) => trim($url))->filter();
});
$errors = [];
$urls = $urls->map(function ($url) use (&$errors) {
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$errors[] = "Invalid URL: {$url}";
return $url;
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
return $url;
});
$duplicates = $urls->duplicates()->unique()->values();
if ($duplicates->isNotEmpty() && ! $request->boolean('force_domain_override')) {
$errors[] = 'The current request contains conflicting URLs: '.implode(', ', $duplicates->toArray()).' Use force_domain_override=true to proceed.';
}
if (! empty($domainErrors)) {
if (count($errors) > 0) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'docker_compose_domains' => $domainErrors,
],
'errors' => ['docker_compose_domains' => $errors],
], 422);
}
// Check for domain conflicts
if ($urls->isNotEmpty()) {
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId, $request->uuid);
if (isset($result['error'])) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['docker_compose_domains' => $result['error']],
], 422);
}
if ($result['hasConflicts'] && ! $request->boolean('force_domain_override')) {
return response()->json([
'message' => 'Domain conflicts detected. Use force_domain_override=true to proceed.',
'conflicts' => $result['conflicts'],
'warning' => 'Using the same domain for multiple resources can cause routing conflicts and unpredictable behavior.',
], 409);
}
}
$yaml = Yaml::parse($application->docker_compose_raw);
$services = data_get($yaml, 'services', []);
$dockerComposeDomains->each(function ($domain) use ($services, $dockerComposeDomainsJson) {
@ -2505,6 +2702,9 @@ public function update_by_uuid(Request $request)
}
$instantDeploy = $request->instant_deploy;
$isStatic = $request->is_static;
$isSpa = $request->is_spa;
$isAutoDeployEnabled = $request->is_auto_deploy_enabled;
$isForceHttpsEnabled = $request->is_force_https_enabled;
$connectToDockerNetwork = $request->connect_to_docker_network;
$useBuildServer = $request->use_build_server;
$isContainerLabelEscapeEnabled = $request->boolean('is_container_label_escape_enabled');
@ -2519,6 +2719,21 @@ public function update_by_uuid(Request $request)
$application->settings->save();
}
if (isset($isSpa)) {
$application->settings->is_spa = $isSpa;
$application->settings->save();
}
if (isset($isAutoDeployEnabled)) {
$application->settings->is_auto_deploy_enabled = $isAutoDeployEnabled;
$application->settings->save();
}
if (isset($isForceHttpsEnabled)) {
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
$application->settings->save();
}
if (isset($connectToDockerNetwork)) {
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
$application->settings->save();
@ -3626,17 +3841,29 @@ private function validateDataApplications(Request $request, Server $server)
}
if ($request->has('domains') && $server->isProxyShouldRun()) {
$uuid = $request->uuid;
$fqdn = $request->domains;
$fqdn = str($fqdn)->replaceEnd(',', '')->trim();
$fqdn = str($fqdn)->replaceStart(',', '')->trim();
$urls = $request->domains;
$urls = str($urls)->replaceEnd(',', '')->trim();
$urls = str($urls)->replaceStart(',', '')->trim();
$errors = [];
$fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) {
$domain = trim($domain);
if (filter_var($domain, FILTER_VALIDATE_URL) === false) {
$errors[] = 'Invalid domain: '.$domain;
$urls = str($urls)->trim()->explode(',')->map(function ($url) use (&$errors) {
$url = trim($url);
// If "domains" is empty clear all URLs from the fqdn column
if (blank($url)) {
return null;
}
return str($domain)->lower();
if (! filter_var($url, FILTER_VALIDATE_URL)) {
$errors[] = 'Invalid URL: '.$url;
return str($url)->lower();
}
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
if (! in_array(strtolower($scheme), ['http', 'https'])) {
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
}
return str($url)->lower();
});
if (count($errors) > 0) {
return response()->json([
@ -3645,7 +3872,7 @@ private function validateDataApplications(Request $request, Server $server)
], 422);
}
// Check for domain conflicts
$result = checkIfDomainIsAlreadyUsedViaAPI($fqdn, $teamId, $uuid);
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId, $uuid);
if (isset($result['error'])) {
return response()->json([
'message' => 'Validation failed.',

View file

@ -86,8 +86,11 @@ function sharedDataApplications()
'git_branch' => 'string',
'build_pack' => Rule::enum(BuildPackTypes::class),
'is_static' => 'boolean',
'is_spa' => 'boolean',
'is_auto_deploy_enabled' => 'boolean',
'is_force_https_enabled' => 'boolean',
'static_image' => Rule::enum(StaticImageTypes::class),
'domains' => 'string',
'domains' => 'string|nullable',
'redirect' => Rule::enum(RedirectTypes::class),
'git_commit_sha' => 'string',
'docker_registry_image_name' => 'string|nullable',
@ -129,6 +132,7 @@ function sharedDataApplications()
'manual_webhook_secret_gitlab' => 'string|nullable',
'manual_webhook_secret_bitbucket' => 'string|nullable',
'manual_webhook_secret_gitea' => 'string|nullable',
'dockerfile_location' => 'string|nullable',
'docker_compose_location' => 'string',
'docker_compose' => 'string|nullable',
'docker_compose_domains' => 'array|nullable',
@ -177,6 +181,10 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
$request->offsetUnset('private_key_uuid');
$request->offsetUnset('use_build_server');
$request->offsetUnset('is_static');
$request->offsetUnset('is_spa');
$request->offsetUnset('is_auto_deploy_enabled');
$request->offsetUnset('is_force_https_enabled');
$request->offsetUnset('connect_to_docker_network');
$request->offsetUnset('force_domain_override');
$request->offsetUnset('autogenerate_domain');
$request->offsetUnset('is_container_label_escape_enabled');

View file

@ -158,8 +158,7 @@ function checkIfDomainIsAlreadyUsedViaAPI(Collection|array $domains, ?string $te
return str($domain);
});
// Check applications within the same team
$applications = Application::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid', 'name', 'id']);
$applications = Application::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid', 'name', 'id', 'docker_compose_domains', 'build_pack']);
$serviceApplications = ServiceApplication::ownedByCurrentTeamAPI($teamId)->with('service:id,name')->get(['fqdn', 'uuid', 'id', 'service_id']);
if ($uuid) {
@ -168,23 +167,51 @@ function checkIfDomainIsAlreadyUsedViaAPI(Collection|array $domains, ?string $te
}
foreach ($applications as $app) {
if (is_null($app->fqdn)) {
continue;
}
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== '');
foreach ($list_of_domains as $domain) {
if (str($domain)->endsWith('/')) {
$domain = str($domain)->beforeLast('/');
if (! is_null($app->fqdn)) {
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== '');
foreach ($list_of_domains as $domain) {
if (str($domain)->endsWith('/')) {
$domain = str($domain)->beforeLast('/');
}
$naked_domain = str($domain)->value();
if ($domains->contains($naked_domain)) {
$conflicts[] = [
'domain' => $naked_domain,
'resource_name' => $app->name,
'resource_uuid' => $app->uuid,
'resource_type' => 'application',
'message' => "Domain $naked_domain is already in use by application '{$app->name}'",
];
}
}
$naked_domain = str($domain)->value();
if ($domains->contains($naked_domain)) {
$conflicts[] = [
'domain' => $naked_domain,
'resource_name' => $app->name,
'resource_uuid' => $app->uuid,
'resource_type' => 'application',
'message' => "Domain $naked_domain is already in use by application '{$app->name}'",
];
}
if ($app->build_pack === 'dockercompose' && ! empty($app->docker_compose_domains)) {
$dockerComposeDomains = json_decode($app->docker_compose_domains, true);
if (is_array($dockerComposeDomains)) {
foreach ($dockerComposeDomains as $serviceName => $domainConfig) {
$domainValue = data_get($domainConfig, 'domain');
if (empty($domainValue)) {
continue;
}
$list_of_domains = collect(explode(',', $domainValue))->filter(fn ($fqdn) => $fqdn !== '');
foreach ($list_of_domains as $domain) {
if (str($domain)->endsWith('/')) {
$domain = str($domain)->beforeLast('/');
}
$naked_domain = str($domain)->value();
if ($domains->contains($naked_domain)) {
$conflicts[] = [
'domain' => $naked_domain,
'resource_name' => $app->name,
'resource_uuid' => $app->uuid,
'resource_type' => 'application',
'service_name' => $serviceName,
'message' => "Domain $naked_domain is already in use by application '{$app->name}' (service: {$serviceName})",
];
}
}
}
}
}
}

View file

@ -135,7 +135,7 @@
},
"domains": {
"type": "string",
"description": "The application domains."
"description": "The application URLs in a comma-separated list."
},
"git_commit_sha": {
"type": "string",
@ -153,6 +153,18 @@
"type": "boolean",
"description": "The flag to indicate if the application is static."
},
"is_spa": {
"type": "boolean",
"description": "The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true."
},
"is_auto_deploy_enabled": {
"type": "boolean",
"description": "The flag to indicate if auto-deploy is enabled on git push. Defaults to true."
},
"is_force_https_enabled": {
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"static_image": {
"type": "string",
"enum": [
@ -322,6 +334,10 @@
"type": "string",
"description": "The Dockerfile content."
},
"dockerfile_location": {
"type": "string",
"description": "The Dockerfile location in the repository."
},
"docker_compose_location": {
"type": "string",
"description": "The Docker Compose location."
@ -564,7 +580,7 @@
},
"domains": {
"type": "string",
"description": "The application domains."
"description": "The application URLs in a comma-separated list."
},
"git_commit_sha": {
"type": "string",
@ -582,6 +598,18 @@
"type": "boolean",
"description": "The flag to indicate if the application is static."
},
"is_spa": {
"type": "boolean",
"description": "The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true."
},
"is_auto_deploy_enabled": {
"type": "boolean",
"description": "The flag to indicate if auto-deploy is enabled on git push. Defaults to true."
},
"is_force_https_enabled": {
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"static_image": {
"type": "string",
"enum": [
@ -751,6 +779,10 @@
"type": "string",
"description": "The Dockerfile content."
},
"dockerfile_location": {
"type": "string",
"description": "The Dockerfile location in the repository"
},
"docker_compose_location": {
"type": "string",
"description": "The Docker Compose location."
@ -993,7 +1025,7 @@
},
"domains": {
"type": "string",
"description": "The application domains."
"description": "The application URLs in a comma-separated list."
},
"git_commit_sha": {
"type": "string",
@ -1011,6 +1043,18 @@
"type": "boolean",
"description": "The flag to indicate if the application is static."
},
"is_spa": {
"type": "boolean",
"description": "The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true."
},
"is_auto_deploy_enabled": {
"type": "boolean",
"description": "The flag to indicate if auto-deploy is enabled on git push. Defaults to true."
},
"is_force_https_enabled": {
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"static_image": {
"type": "string",
"enum": [
@ -1180,6 +1224,10 @@
"type": "string",
"description": "The Dockerfile content."
},
"dockerfile_location": {
"type": "string",
"description": "The Dockerfile location in the repository."
},
"docker_compose_location": {
"type": "string",
"description": "The Docker Compose location."
@ -1410,7 +1458,7 @@
},
"domains": {
"type": "string",
"description": "The application domains."
"description": "The application URLs in a comma-separated list."
},
"docker_registry_image_name": {
"type": "string",
@ -1562,6 +1610,10 @@
"type": "boolean",
"description": "The flag to indicate if the application should be deployed instantly."
},
"is_force_https_enabled": {
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"use_build_server": {
"type": "boolean",
"nullable": true,
@ -1754,7 +1806,7 @@
},
"domains": {
"type": "string",
"description": "The application domains."
"description": "The application URLs in a comma-separated list."
},
"ports_mappings": {
"type": "string",
@ -1894,6 +1946,10 @@
"type": "boolean",
"description": "The flag to indicate if the application should be deployed instantly."
},
"is_force_https_enabled": {
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"use_build_server": {
"type": "boolean",
"nullable": true,
@ -2402,7 +2458,7 @@
},
"domains": {
"type": "string",
"description": "The application domains."
"description": "The application URLs in a comma-separated list."
},
"git_commit_sha": {
"type": "string",
@ -2420,6 +2476,18 @@
"type": "boolean",
"description": "The flag to indicate if the application is static."
},
"is_spa": {
"type": "boolean",
"description": "The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true."
},
"is_auto_deploy_enabled": {
"type": "boolean",
"description": "The flag to indicate if auto-deploy is enabled on git push. Defaults to true."
},
"is_force_https_enabled": {
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"install_command": {
"type": "string",
"description": "The install command."
@ -2582,6 +2650,10 @@
"type": "string",
"description": "The Dockerfile content."
},
"dockerfile_location": {
"type": "string",
"description": "The Dockerfile location in the repository."
},
"docker_compose_location": {
"type": "string",
"description": "The Docker Compose location."

View file

@ -97,7 +97,7 @@ paths:
description: 'The application description.'
domains:
type: string
description: 'The application domains.'
description: 'The application URLs in a comma-separated list.'
git_commit_sha:
type: string
description: 'The git commit SHA.'
@ -110,6 +110,15 @@ paths:
is_static:
type: boolean
description: 'The flag to indicate if the application is static.'
is_spa:
type: boolean
description: 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'
is_auto_deploy_enabled:
type: boolean
description: 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
static_image:
type: string
enum: ['nginx:alpine']
@ -234,6 +243,9 @@ paths:
dockerfile:
type: string
description: 'The Dockerfile content.'
dockerfile_location:
type: string
description: 'The Dockerfile location in the repository.'
docker_compose_location:
type: string
description: 'The Docker Compose location.'
@ -369,7 +381,7 @@ paths:
description: 'The application description.'
domains:
type: string
description: 'The application domains.'
description: 'The application URLs in a comma-separated list.'
git_commit_sha:
type: string
description: 'The git commit SHA.'
@ -382,6 +394,15 @@ paths:
is_static:
type: boolean
description: 'The flag to indicate if the application is static.'
is_spa:
type: boolean
description: 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'
is_auto_deploy_enabled:
type: boolean
description: 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
static_image:
type: string
enum: ['nginx:alpine']
@ -506,6 +527,9 @@ paths:
dockerfile:
type: string
description: 'The Dockerfile content.'
dockerfile_location:
type: string
description: 'The Dockerfile location in the repository'
docker_compose_location:
type: string
description: 'The Docker Compose location.'
@ -641,7 +665,7 @@ paths:
description: 'The application description.'
domains:
type: string
description: 'The application domains.'
description: 'The application URLs in a comma-separated list.'
git_commit_sha:
type: string
description: 'The git commit SHA.'
@ -654,6 +678,15 @@ paths:
is_static:
type: boolean
description: 'The flag to indicate if the application is static.'
is_spa:
type: boolean
description: 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'
is_auto_deploy_enabled:
type: boolean
description: 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
static_image:
type: string
enum: ['nginx:alpine']
@ -778,6 +811,9 @@ paths:
dockerfile:
type: string
description: 'The Dockerfile content.'
dockerfile_location:
type: string
description: 'The Dockerfile location in the repository.'
docker_compose_location:
type: string
description: 'The Docker Compose location.'
@ -903,7 +939,7 @@ paths:
description: 'The application description.'
domains:
type: string
description: 'The application domains.'
description: 'The application URLs in a comma-separated list.'
docker_registry_image_name:
type: string
description: 'The docker registry image name.'
@ -1015,6 +1051,9 @@ paths:
instant_deploy:
type: boolean
description: 'The flag to indicate if the application should be deployed instantly.'
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
use_build_server:
type: boolean
nullable: true
@ -1124,7 +1163,7 @@ paths:
description: 'The application description.'
domains:
type: string
description: 'The application domains.'
description: 'The application URLs in a comma-separated list.'
ports_mappings:
type: string
description: 'The ports mappings.'
@ -1227,6 +1266,9 @@ paths:
instant_deploy:
type: boolean
description: 'The flag to indicate if the application should be deployed instantly.'
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
use_build_server:
type: boolean
nullable: true
@ -1524,7 +1566,7 @@ paths:
description: 'The application description.'
domains:
type: string
description: 'The application domains.'
description: 'The application URLs in a comma-separated list.'
git_commit_sha:
type: string
description: 'The git commit SHA.'
@ -1537,6 +1579,15 @@ paths:
is_static:
type: boolean
description: 'The flag to indicate if the application is static.'
is_spa:
type: boolean
description: 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'
is_auto_deploy_enabled:
type: boolean
description: 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
install_command:
type: string
description: 'The install command.'
@ -1657,6 +1708,9 @@ paths:
dockerfile:
type: string
description: 'The Dockerfile content.'
dockerfile_location:
type: string
description: 'The Dockerfile location in the repository.'
docker_compose_location:
type: string
description: 'The Docker Compose location.'