From eb76d6311728a4ea960cc5f79d4ac7a49b684ba1 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 25 Jun 2024 21:22:23 +0200 Subject: [PATCH] extend application put api --- app/Http/Controllers/Api/Applications.php | 113 +++++++++++++++++++++- bootstrap/helpers/shared.php | 7 +- 2 files changed, 113 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/Applications.php index fa58e25b6..080f03ed8 100644 --- a/app/Http/Controllers/Api/Applications.php +++ b/app/Http/Controllers/Api/Applications.php @@ -54,7 +54,7 @@ class Applications extends Controller if ($request->collect()->count() == 0) { return response()->json([ - 'message' => 'No data provided.', + 'message' => 'Invalid request.', ], 400); } $application = Application::where('uuid', $request->uuid)->first(); @@ -65,11 +65,116 @@ class Applications extends Controller 'message' => 'Application not found', ], 404); } - $allowedFields = ['name', 'domains']; + $server = $application->destination->server; + $allowedFields = ['name', 'description', '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', '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', 'docker_compose_raw', 'docker_compose_domains', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'redirect']; $validator = Validator::make($request->all(), [ 'name' => 'string|max:255', + 'description' => 'string|nullable', 'domains' => 'string', + 'git_repository' => 'string', + 'git_branch' => 'string', + 'git_commit_sha' => 'string', + 'docker_registry_image_name' => 'string|nullable', + 'docker_registry_image_tag' => 'string|nullable', + 'build_pack' => 'string', + 'static_image' => 'string', + 'install_command' => 'string|nullable', + 'build_command' => 'string|nullable', + 'start_command' => 'string|nullable', + 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/', + 'ports_mappings' => 'string|regex:/^(\d+:\d+)(,\d+:\d+)*$/|nullable', + 'base_directory' => 'string|nullable', + 'publish_directory' => 'string|nullable', + 'health_check_enabled' => 'boolean', + 'health_check_path' => 'string', + 'health_check_port' => 'string|nullable', + 'health_check_host' => 'string', + 'health_check_method' => 'string', + 'health_check_return_code' => 'numeric', + 'health_check_scheme' => 'string', + 'health_check_response_text' => 'string|nullable', + 'health_check_interval' => 'numeric', + 'health_check_timeout' => 'numeric', + 'health_check_retries' => 'numeric', + 'health_check_start_period' => 'numeric', + 'limits_memory' => 'string', + 'limits_memory_swap' => 'string', + 'limits_memory_swappiness' => 'numeric', + 'limits_memory_reservation' => 'string', + 'limits_cpus' => 'string', + 'limits_cpuset' => 'string|nullable', + 'limits_cpu_shares' => 'numeric', + 'custom_labels' => 'string|nullable', + 'custom_docker_run_options' => 'string|nullable', + 'post_deployment_command' => 'string|nullable', + 'post_deployment_command_container' => 'string', + 'pre_deployment_command' => 'string|nullable', + 'pre_deployment_command_container' => 'string', + 'watch_paths' => 'string|nullable', + 'manual_webhook_secret_github' => 'string|nullable', + 'manual_webhook_secret_gitlab' => 'string|nullable', + 'manual_webhook_secret_bitbucket' => 'string|nullable', + 'manual_webhook_secret_gitea' => 'string|nullable', + 'docker_compose_location' => 'string', + 'docker_compose' => 'string|nullable', + 'docker_compose_raw' => 'string|nullable', + 'docker_compose_domains' => 'string|nullable', // must be like: "{\"api\":{\"domain\":\"http:\\/\\/b8sos8k.127.0.0.1.sslip.io\"}}" + 'docker_compose_custom_start_command' => 'string|nullable', + 'docker_compose_custom_build_command' => 'string|nullable', + 'redirect' => 'enum:both,www,non-www', ]); + + // Validate ports_exposes + if ($request->has('ports_exposes')) { + $ports = explode(',', $request->ports_exposes); + foreach ($ports as $port) { + if (! is_numeric($port)) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => [ + 'ports_exposes' => 'The ports_exposes should be a comma separated list of numbers.', + ], + ], 422); + } + } + } + // Validate ports_mappings + if ($request->has('ports_mappings')) { + $ports = []; + foreach (explode(',', $request->ports_mappings) as $portMapping) { + $port = explode(':', $portMapping); + if (in_array($port[0], $ports)) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => [ + 'ports_mappings' => 'The first number before : should be unique between mappings.', + ], + ], 422); + } + $ports[] = $port[0]; + } + } + // Validate custom_labels + if ($request->has('custom_labels')) { + if (! isBase64Encoded($request->custom_labels)) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => [ + 'custom_labels' => 'The custom_labels should be base64 encoded.', + ], + ], 422); + } + $customLabels = base64_decode($request->custom_labels); + if (mb_detect_encoding($customLabels, 'ASCII', true) === false) { + return response()->json([ + 'message' => 'Validation failed', + 'errors' => [ + 'custom_labels' => 'The custom_labels should be base64 encoded.', + ], + ], 422); + + } + } $extraFields = array_diff(array_keys($request->all()), $allowedFields); if ($validator->fails() || ! empty($extraFields)) { $errors = $validator->errors(); @@ -84,8 +189,7 @@ class Applications extends Controller 'errors' => $errors, ], 422); } - - if ($request->has('domains')) { + if ($request->has('domains') && $server->isProxyShouldRun()) { $fqdn = $request->domains; $fqdn = str($fqdn)->replaceEnd(',', '')->trim(); $fqdn = str($fqdn)->replaceStart(',', '')->trim(); @@ -93,7 +197,6 @@ class Applications extends Controller $fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) { if (filter_var($domain, FILTER_VALIDATE_URL) === false) { $errors[] = 'Invalid domain: '.$domain; - } return str($domain)->trim()->lower(); diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index fe51f78ac..9d9c92330 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1907,8 +1907,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'networks' => $topLevelNetworks->toArray(), ]; if ($isSameDockerComposeFile) { - $resource->docker_compose_pr_raw = Yaml::dump($yaml, 10, 2); - $resource->docker_compose_pr = Yaml::dump($finalServices, 10, 2); $resource->docker_compose_raw = Yaml::dump($yaml, 10, 2); $resource->docker_compose = Yaml::dump($finalServices, 10, 2); } else { @@ -2316,3 +2314,8 @@ function generateSentinelToken() return $token; } + +function isBase64Encoded($strValue) +{ + return base64_encode(base64_decode($strValue, true)) === $strValue; +}