mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-20 14:23:44 +00:00
Merge remote-tracking branch 'origin/next' into fix/url-validator-underscore-hostnames
This commit is contained in:
@@ -30,7 +30,6 @@ use Illuminate\Validation\Rule;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class ApplicationsController extends Controller
|
||||
{
|
||||
@@ -59,6 +58,10 @@ class ApplicationsController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
if ($application->is_shown_once ?? false) {
|
||||
$application->makeHidden(['value', 'real_value']);
|
||||
}
|
||||
|
||||
return serializeApiResponse($application);
|
||||
}
|
||||
|
||||
@@ -949,6 +952,10 @@ class ApplicationsController extends Controller
|
||||
}
|
||||
$serverUuid = $request->server_uuid;
|
||||
$fqdn = $request->domains;
|
||||
if ($request->has('domains') && is_string($request->domains)) {
|
||||
$fqdn = ValidationPatterns::normalizeApplicationDomains($request->domains);
|
||||
$request->offsetSet('domains', $fqdn);
|
||||
}
|
||||
$autogenerateDomain = $request->boolean('autogenerate_domain', true);
|
||||
$instantDeploy = $request->instant_deploy;
|
||||
$githubAppUuid = $request->github_app_uuid;
|
||||
@@ -1028,7 +1035,7 @@ class ApplicationsController extends Controller
|
||||
'docker_compose_domains' => 'array|nullable',
|
||||
'docker_compose_domains.*' => 'array:name,domain',
|
||||
'docker_compose_domains.*.name' => 'string|required',
|
||||
'docker_compose_domains.*.domain' => 'string|nullable',
|
||||
'docker_compose_domains.*.domain' => ValidationPatterns::applicationDomainRules(),
|
||||
];
|
||||
// ports_exposes is not required for dockercompose
|
||||
if ($request->build_pack === 'dockercompose') {
|
||||
@@ -1134,15 +1141,15 @@ class ApplicationsController extends Controller
|
||||
$request->offsetUnset('docker_compose_domains');
|
||||
}
|
||||
if ($dockerComposeDomainsJson->count() > 0) {
|
||||
$application->docker_compose_domains = $dockerComposeDomainsJson;
|
||||
$application->docker_compose_domains = json_encode($dockerComposeDomainsJson);
|
||||
}
|
||||
$repository_url_parsed = Url::fromString($request->git_repository);
|
||||
$git_host = $repository_url_parsed->getHost();
|
||||
if ($git_host === 'github.com') {
|
||||
$application->source_type = GithubApp::class;
|
||||
$application->source_id = GithubApp::find(0)->id;
|
||||
$application->git_repository = str($repository_url_parsed->getSegment(1).'/'.$repository_url_parsed->getSegment(2))->trim()->toString();
|
||||
}
|
||||
$application->git_repository = str($repository_url_parsed->getSegment(1).'/'.$repository_url_parsed->getSegment(2))->trim()->toString();
|
||||
$application->fqdn = $fqdn;
|
||||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
@@ -1193,7 +1200,7 @@ class ApplicationsController extends Controller
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
$deployment_uuid = new Cuid2;
|
||||
$deployment_uuid = new_public_id();
|
||||
|
||||
$result = queue_application_deployment(
|
||||
application: $application,
|
||||
@@ -1236,7 +1243,7 @@ class ApplicationsController extends Controller
|
||||
'docker_compose_domains' => 'array|nullable',
|
||||
'docker_compose_domains.*' => 'array:name,domain',
|
||||
'docker_compose_domains.*.name' => 'string|required',
|
||||
'docker_compose_domains.*.domain' => 'string|nullable',
|
||||
'docker_compose_domains.*.domain' => ValidationPatterns::applicationDomainRules(),
|
||||
];
|
||||
$validationRules = array_merge(sharedDataApplications(), $validationRules);
|
||||
$validationMessages = [
|
||||
@@ -1375,7 +1382,7 @@ class ApplicationsController extends Controller
|
||||
$request->offsetUnset('docker_compose_domains');
|
||||
}
|
||||
if ($dockerComposeDomainsJson->count() > 0) {
|
||||
$application->docker_compose_domains = $dockerComposeDomainsJson;
|
||||
$application->docker_compose_domains = json_encode($dockerComposeDomainsJson);
|
||||
}
|
||||
$application->fqdn = $fqdn;
|
||||
$application->git_repository = str($gitRepository)->trim()->toString();
|
||||
@@ -1432,7 +1439,7 @@ class ApplicationsController extends Controller
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
$deployment_uuid = new Cuid2;
|
||||
$deployment_uuid = new_public_id();
|
||||
|
||||
$result = queue_application_deployment(
|
||||
application: $application,
|
||||
@@ -1476,7 +1483,7 @@ class ApplicationsController extends Controller
|
||||
'docker_compose_domains' => 'array|nullable',
|
||||
'docker_compose_domains.*' => 'array:name,domain',
|
||||
'docker_compose_domains.*.name' => 'string|required',
|
||||
'docker_compose_domains.*.domain' => 'string|nullable',
|
||||
'docker_compose_domains.*.domain' => ValidationPatterns::applicationDomainRules(),
|
||||
];
|
||||
|
||||
$validationRules = array_merge(sharedDataApplications(), $validationRules);
|
||||
@@ -1588,7 +1595,7 @@ class ApplicationsController extends Controller
|
||||
$request->offsetUnset('docker_compose_domains');
|
||||
}
|
||||
if ($dockerComposeDomainsJson->count() > 0) {
|
||||
$application->docker_compose_domains = $dockerComposeDomainsJson;
|
||||
$application->docker_compose_domains = json_encode($dockerComposeDomainsJson);
|
||||
}
|
||||
$application->fqdn = $fqdn;
|
||||
$application->private_key_id = $privateKey->id;
|
||||
@@ -1641,7 +1648,7 @@ class ApplicationsController extends Controller
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
$deployment_uuid = new Cuid2;
|
||||
$deployment_uuid = new_public_id();
|
||||
|
||||
$result = queue_application_deployment(
|
||||
application: $application,
|
||||
@@ -1687,7 +1694,7 @@ class ApplicationsController extends Controller
|
||||
], 422);
|
||||
}
|
||||
if (! $request->has('name')) {
|
||||
$request->offsetSet('name', 'dockerfile-'.new Cuid2);
|
||||
$request->offsetSet('name', 'dockerfile-'.new_public_id());
|
||||
}
|
||||
|
||||
$return = $this->validateDataApplications($request, $server);
|
||||
@@ -1761,7 +1768,7 @@ class ApplicationsController extends Controller
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
$deployment_uuid = new Cuid2;
|
||||
$deployment_uuid = new_public_id();
|
||||
|
||||
$result = queue_application_deployment(
|
||||
application: $application,
|
||||
@@ -1805,7 +1812,7 @@ class ApplicationsController extends Controller
|
||||
], 422);
|
||||
}
|
||||
if (! $request->has('name')) {
|
||||
$request->offsetSet('name', 'docker-image-'.new Cuid2);
|
||||
$request->offsetSet('name', 'docker-image-'.new_public_id());
|
||||
}
|
||||
$return = $this->validateDataApplications($request, $server);
|
||||
if ($return instanceof JsonResponse) {
|
||||
@@ -1880,7 +1887,7 @@ class ApplicationsController extends Controller
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
$deployment_uuid = new Cuid2;
|
||||
$deployment_uuid = new_public_id();
|
||||
|
||||
$result = queue_application_deployment(
|
||||
application: $application,
|
||||
@@ -2010,6 +2017,13 @@ class ApplicationsController extends Controller
|
||||
default: 100,
|
||||
)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'show_timestamps',
|
||||
in: 'query',
|
||||
description: 'Show timestamps in the logs.',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'boolean', default: false),
|
||||
),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
@@ -2073,8 +2087,9 @@ class ApplicationsController extends Controller
|
||||
], 400);
|
||||
}
|
||||
|
||||
$lines = $request->query->get('lines', 100) ?: 100;
|
||||
$logs = getContainerLogs($application->destination->server, $container['ID'], $lines);
|
||||
$lines = normalizeLogLines($request->query('lines'));
|
||||
$showTimestamps = parseLogTimestampFlag($request->query('show_timestamps'));
|
||||
$logs = getContainerLogs($application->destination->server, $container['ID'], $lines, $showTimestamps);
|
||||
|
||||
return response()->json([
|
||||
'logs' => $logs,
|
||||
@@ -2280,6 +2295,7 @@ class ApplicationsController extends Controller
|
||||
'force_domain_override' => ['type' => 'boolean', 'description' => 'Force domain usage even if conflicts are detected. Default is false.'],
|
||||
'is_container_label_escape_enabled' => ['type' => 'boolean', 'default' => true, 'description' => 'Escape special characters in labels. By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$. If you want to use env variables inside the labels, turn this off.'],
|
||||
'is_preserve_repository_enabled' => ['type' => 'boolean', 'description' => 'Preserve git repository during application update. If false, the existing repository will be removed and replaced with the new one. If true, the existing repository will be kept and the new one will be ignored. Default is false.'],
|
||||
'include_source_commit_in_build' => ['type' => 'boolean', 'description' => 'Include source commit information in the build. Default is false.'],
|
||||
],
|
||||
)
|
||||
),
|
||||
@@ -2365,7 +2381,7 @@ class ApplicationsController extends Controller
|
||||
$this->authorize('update', $application);
|
||||
|
||||
$server = $application->destination->server;
|
||||
$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_type', 'health_check_command', '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', 'dockerfile_target_build', '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', 'is_preserve_repository_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_type', 'health_check_command', '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', 'dockerfile_target_build', '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', 'is_preserve_repository_enabled', 'include_source_commit_in_build'];
|
||||
|
||||
$validationRules = [
|
||||
'name' => 'string|max:255',
|
||||
@@ -2375,11 +2391,12 @@ class ApplicationsController extends Controller
|
||||
'docker_compose_domains' => 'array|nullable',
|
||||
'docker_compose_domains.*' => 'array:name,domain',
|
||||
'docker_compose_domains.*.name' => 'string|required',
|
||||
'docker_compose_domains.*.domain' => 'string|nullable',
|
||||
'docker_compose_domains.*.domain' => ValidationPatterns::applicationDomainRules(),
|
||||
'custom_nginx_configuration' => 'string|nullable',
|
||||
'is_http_basic_auth_enabled' => 'boolean|nullable',
|
||||
'http_basic_auth_username' => 'string',
|
||||
'http_basic_auth_password' => 'string',
|
||||
'include_source_commit_in_build' => 'boolean',
|
||||
];
|
||||
$validationRules = array_merge(sharedDataApplications(), $validationRules);
|
||||
$validationMessages = [
|
||||
@@ -2479,29 +2496,7 @@ class ApplicationsController extends Controller
|
||||
$requestHasDomains = $request->has('domains');
|
||||
if ($requestHasDomains && $server->isProxyShouldRun()) {
|
||||
$uuid = $request->uuid;
|
||||
$urls = $request->domains;
|
||||
$urls = str($urls)->replaceStart(',', '')->replaceEnd(',', '')->trim();
|
||||
$errors = [];
|
||||
$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;
|
||||
}
|
||||
|
||||
if (! isValidDomainUrl($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();
|
||||
});
|
||||
$errors = ValidationPatterns::validateApplicationDomains($request->domains);
|
||||
|
||||
if (count($errors) > 0) {
|
||||
return response()->json([
|
||||
@@ -2509,6 +2504,9 @@ class ApplicationsController extends Controller
|
||||
'errors' => $errors,
|
||||
], 422);
|
||||
}
|
||||
$domains = ValidationPatterns::normalizeApplicationDomains($request->domains);
|
||||
$request->offsetSet('domains', $domains);
|
||||
$urls = collect(ValidationPatterns::applicationDomainList($domains));
|
||||
// Check for domain conflicts
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId, $uuid);
|
||||
if (isset($result['error'])) {
|
||||
@@ -2616,6 +2614,7 @@ class ApplicationsController extends Controller
|
||||
$useBuildServer = $request->use_build_server;
|
||||
$isContainerLabelEscapeEnabled = $request->boolean('is_container_label_escape_enabled');
|
||||
$isPreserveRepositoryEnabled = $request->boolean('is_preserve_repository_enabled');
|
||||
$includeSourceCommitInBuild = $request->boolean('include_source_commit_in_build');
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
@@ -2654,6 +2653,10 @@ class ApplicationsController extends Controller
|
||||
$application->settings->is_preserve_repository_enabled = $isPreserveRepositoryEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
if ($request->has('include_source_commit_in_build')) {
|
||||
$application->settings->include_source_commit_in_build = $includeSourceCommitInBuild;
|
||||
$application->settings->save();
|
||||
}
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$data = $request->only($allowedFields);
|
||||
@@ -2678,7 +2681,7 @@ class ApplicationsController extends Controller
|
||||
]);
|
||||
|
||||
if ($instantDeploy) {
|
||||
$deployment_uuid = new Cuid2;
|
||||
$deployment_uuid = new_public_id();
|
||||
|
||||
$result = queue_application_deployment(
|
||||
application: $application,
|
||||
@@ -2873,8 +2876,12 @@ class ApplicationsController extends Controller
|
||||
|
||||
$this->authorize('manageEnvironment', $application);
|
||||
|
||||
if ($request->has('key')) {
|
||||
$request->merge(['key' => ValidationPatterns::normalizeEnvironmentVariableKey((string) $request->key)]);
|
||||
}
|
||||
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'key' => 'string|required',
|
||||
'key' => ValidationPatterns::environmentVariableKeyRules(),
|
||||
'value' => 'string|nullable',
|
||||
'is_preview' => 'boolean',
|
||||
'is_literal' => 'boolean',
|
||||
@@ -3097,12 +3104,18 @@ class ApplicationsController extends Controller
|
||||
], 400);
|
||||
}
|
||||
$bulk_data = collect($bulk_data)->map(function ($item) {
|
||||
return collect($item)->only(['key', 'value', 'is_preview', 'is_literal', 'is_multiline', 'is_shown_once', 'is_runtime', 'is_buildtime', 'comment']);
|
||||
$item = collect($item)->only(['key', 'value', 'is_preview', 'is_literal', 'is_multiline', 'is_shown_once', 'is_runtime', 'is_buildtime', 'comment']);
|
||||
|
||||
if ($item->has('key')) {
|
||||
$item->put('key', ValidationPatterns::normalizeEnvironmentVariableKey((string) $item->get('key')));
|
||||
}
|
||||
|
||||
return $item;
|
||||
});
|
||||
$returnedEnvs = collect();
|
||||
foreach ($bulk_data as $item) {
|
||||
$validator = customApiValidator($item, [
|
||||
'key' => 'string|required',
|
||||
'key' => ValidationPatterns::environmentVariableKeyRules(),
|
||||
'value' => 'string|nullable',
|
||||
'is_preview' => 'boolean',
|
||||
'is_literal' => 'boolean',
|
||||
@@ -3299,8 +3312,12 @@ class ApplicationsController extends Controller
|
||||
|
||||
$this->authorize('manageEnvironment', $application);
|
||||
|
||||
if ($request->has('key')) {
|
||||
$request->merge(['key' => ValidationPatterns::normalizeEnvironmentVariableKey((string) $request->key)]);
|
||||
}
|
||||
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'key' => 'string|required',
|
||||
'key' => ValidationPatterns::environmentVariableKeyRules(),
|
||||
'value' => 'string|nullable',
|
||||
'is_preview' => 'boolean',
|
||||
'is_literal' => 'boolean',
|
||||
@@ -3585,7 +3602,7 @@ class ApplicationsController extends Controller
|
||||
|
||||
$this->authorize('deploy', $application);
|
||||
|
||||
$deployment_uuid = new Cuid2;
|
||||
$deployment_uuid = new_public_id();
|
||||
|
||||
$result = queue_application_deployment(
|
||||
application: $application,
|
||||
@@ -3607,7 +3624,7 @@ class ApplicationsController extends Controller
|
||||
'team_id' => $teamId,
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'deployment_uuid' => $deployment_uuid->toString(),
|
||||
'deployment_uuid' => $deployment_uuid,
|
||||
'force_rebuild' => $force,
|
||||
'instant_deploy' => $instant_deploy,
|
||||
]);
|
||||
@@ -3615,7 +3632,7 @@ class ApplicationsController extends Controller
|
||||
return response()->json(
|
||||
[
|
||||
'message' => 'Deployment request queued.',
|
||||
'deployment_uuid' => $deployment_uuid->toString(),
|
||||
'deployment_uuid' => $deployment_uuid,
|
||||
],
|
||||
200
|
||||
);
|
||||
@@ -3783,7 +3800,7 @@ class ApplicationsController extends Controller
|
||||
|
||||
$this->authorize('deploy', $application);
|
||||
|
||||
$deployment_uuid = new Cuid2;
|
||||
$deployment_uuid = new_public_id();
|
||||
|
||||
$result = queue_application_deployment(
|
||||
application: $application,
|
||||
@@ -3801,13 +3818,13 @@ class ApplicationsController extends Controller
|
||||
'team_id' => $teamId,
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'deployment_uuid' => $deployment_uuid->toString(),
|
||||
'deployment_uuid' => $deployment_uuid,
|
||||
]);
|
||||
|
||||
return response()->json(
|
||||
[
|
||||
'message' => 'Restart request queued.',
|
||||
'deployment_uuid' => $deployment_uuid->toString(),
|
||||
'deployment_uuid' => $deployment_uuid,
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -3854,36 +3871,16 @@ class ApplicationsController extends Controller
|
||||
}
|
||||
if ($request->has('domains') && $server->isProxyShouldRun()) {
|
||||
$uuid = $request->uuid;
|
||||
$urls = $request->domains;
|
||||
$urls = str($urls)->replaceEnd(',', '')->trim();
|
||||
$urls = str($urls)->replaceStart(',', '')->trim();
|
||||
$errors = [];
|
||||
$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;
|
||||
}
|
||||
|
||||
if (! isValidDomainUrl($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();
|
||||
});
|
||||
$errors = ValidationPatterns::validateApplicationDomains($request->domains);
|
||||
if (count($errors) > 0) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => $errors,
|
||||
], 422);
|
||||
}
|
||||
$normalizedDomains = ValidationPatterns::normalizeApplicationDomains($request->domains);
|
||||
$request->offsetSet('domains', $normalizedDomains);
|
||||
$urls = collect(ValidationPatterns::applicationDomainList($normalizedDomains));
|
||||
// Check for domain conflicts
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId, $uuid);
|
||||
if (isset($result['error'])) {
|
||||
@@ -4262,10 +4259,11 @@ class ApplicationsController extends Controller
|
||||
'host_path' => ['string', 'nullable', 'regex:'.ValidationPatterns::DIRECTORY_PATH_PATTERN],
|
||||
'content' => 'string|nullable',
|
||||
'is_directory' => 'boolean',
|
||||
'is_host_file' => 'boolean',
|
||||
'fs_path' => 'string',
|
||||
]);
|
||||
|
||||
$allAllowedFields = ['type', 'name', 'mount_path', 'host_path', 'content', 'is_directory', 'fs_path'];
|
||||
$allAllowedFields = ['type', 'name', 'mount_path', 'host_path', 'content', 'is_directory', 'is_host_file', 'fs_path'];
|
||||
$extraFields = array_diff(array_keys($request->all()), $allAllowedFields);
|
||||
if ($validator->fails() || ! empty($extraFields)) {
|
||||
$errors = $validator->errors();
|
||||
@@ -4289,7 +4287,7 @@ class ApplicationsController extends Controller
|
||||
], 422);
|
||||
}
|
||||
|
||||
$typeSpecificInvalidFields = array_intersect(['content', 'is_directory', 'fs_path'], array_keys($request->all()));
|
||||
$typeSpecificInvalidFields = array_intersect(['content', 'is_directory', 'is_host_file', 'fs_path'], array_keys($request->all()));
|
||||
if (! empty($typeSpecificInvalidFields)) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
@@ -4320,6 +4318,14 @@ class ApplicationsController extends Controller
|
||||
}
|
||||
|
||||
$isDirectory = $request->boolean('is_directory', false);
|
||||
$isHostFile = $request->boolean('is_host_file', false);
|
||||
|
||||
if ($isDirectory && $isHostFile) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['is_host_file' => 'Host file mounts cannot also be directory mounts.'],
|
||||
], 422);
|
||||
}
|
||||
|
||||
if ($isDirectory) {
|
||||
if (! $request->fs_path) {
|
||||
@@ -4342,12 +4348,50 @@ class ApplicationsController extends Controller
|
||||
'resource_id' => $application->id,
|
||||
'resource_type' => get_class($application),
|
||||
]);
|
||||
} elseif ($isHostFile) {
|
||||
if (! $request->fs_path) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['fs_path' => 'The fs_path field is required for host file mounts.'],
|
||||
], 422);
|
||||
}
|
||||
|
||||
if ($request->filled('content')) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['content' => 'Content is not valid for host file mounts.'],
|
||||
], 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$fsPath = validateHostFileMountPath($request->fs_path, 'host file source path');
|
||||
$mountPath = validateFileMountPath($request->mount_path, 'host file destination path');
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['mount_path' => $e->getMessage()],
|
||||
], 422);
|
||||
}
|
||||
|
||||
$storage = LocalFileVolume::create([
|
||||
'fs_path' => $fsPath,
|
||||
'mount_path' => $mountPath,
|
||||
'content' => null,
|
||||
'is_directory' => false,
|
||||
'is_host_file' => true,
|
||||
'resource_id' => $application->id,
|
||||
'resource_type' => get_class($application),
|
||||
]);
|
||||
} else {
|
||||
$mountPath = str($request->mount_path)->trim()->start('/')->value();
|
||||
|
||||
validateShellSafePath($mountPath, 'file storage path');
|
||||
|
||||
$fsPath = application_configuration_dir().'/'.$application->uuid.$mountPath;
|
||||
try {
|
||||
$mountPath = validateFileMountPath($request->mount_path, 'file storage path');
|
||||
$fsPath = confineFileMountPath(application_configuration_dir().'/'.$application->uuid, $mountPath, 'file storage path');
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['mount_path' => $e->getMessage()],
|
||||
], 422);
|
||||
}
|
||||
|
||||
$storage = LocalFileVolume::create([
|
||||
'fs_path' => $fsPath,
|
||||
|
||||
Reference in New Issue
Block a user