From 754448d9d447c8bc333dc61daf49bf7bdde46ce0 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:35:37 +0100 Subject: [PATCH] feat(api): improve docker_compose_domains - add url conflict checking and force_domain_override support - refactor docker_compose_domains URL validation function --- .../Api/ApplicationsController.php | 268 +++++++++++------- 1 file changed, 172 insertions(+), 96 deletions(-) diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index d655a9d1d..24e8394a4 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -1130,39 +1130,58 @@ 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 []; } - } - if (! empty($domainErrors)) { + 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; + }); + + 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')]); }); @@ -1319,39 +1338,58 @@ 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 []; } - } - if (! empty($domainErrors)) { + 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; + }); + + 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')]); }); @@ -1476,39 +1514,58 @@ 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 []; } - } - if (! empty($domainErrors)) { + 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; + }); + + 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')]); }); @@ -2466,39 +2523,58 @@ 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 []; } - } - if (! empty($domainErrors)) { + 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; + }); + + 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) {