This commit is contained in:
Andras Bacsai
2025-04-23 13:22:01 +02:00
parent 0dceb40a6e
commit 78ef80f800
9 changed files with 257 additions and 53 deletions

View File

@@ -45,6 +45,7 @@ class ApplicationsController extends Controller
'private_key_id',
'value',
'real_value',
'http_basic_auth_password',
]);
}
@@ -183,6 +184,9 @@ class ApplicationsController extends Controller
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'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'],
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
],
)
),
@@ -299,6 +303,9 @@ class ApplicationsController extends Controller
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'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'],
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
],
)
),
@@ -415,6 +422,9 @@ class ApplicationsController extends Controller
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'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'],
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
],
)
),
@@ -515,6 +525,9 @@ class ApplicationsController extends Controller
'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.'],
'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'],
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
],
)
),
@@ -612,6 +625,9 @@ class ApplicationsController extends Controller
'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.'],
'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'],
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
],
)
),
@@ -711,7 +727,6 @@ class ApplicationsController extends Controller
private function create_application(Request $request, $type)
{
$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', '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'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
return invalidTokenResponse();
@@ -721,6 +736,8 @@ class ApplicationsController extends Controller
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', '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'];
$validator = customApiValidator($request->all(), [
'name' => 'string|max:255',
'description' => 'string|nullable',
@@ -729,6 +746,9 @@ class ApplicationsController extends Controller
'environment_uuid' => 'string|nullable',
'server_uuid' => 'string|required',
'destination_uuid' => 'string',
'is_http_basic_auth_enabled' => 'boolean',
'http_basic_auth_username' => 'string|nullable',
'http_basic_auth_password' => 'string|nullable',
]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
@@ -1758,25 +1778,19 @@ class ApplicationsController extends Controller
if (is_null($teamId)) {
return invalidTokenResponse();
}
if ($request->collect()->count() == 0) {
return response()->json([
'message' => 'Invalid request.',
], 400);
}
$return = validateIncomingRequest($request);
if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return;
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
if (! $application) {
return response()->json([
'message' => 'Application not found',
], 404);
}
$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', '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_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy', 'use_build_server', 'custom_nginx_configuration'];
$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', '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_raw', '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'];
$validationRules = [
'name' => 'string|max:255',
@@ -1789,6 +1803,9 @@ class ApplicationsController extends Controller
'docker_compose_custom_start_command' => 'string|nullable',
'docker_compose_custom_build_command' => 'string|nullable',
'custom_nginx_configuration' => 'string|nullable',
'is_http_basic_auth_enabled' => 'boolean|nullable',
'http_basic_auth_username' => 'string',
'http_basic_auth_password' => 'string',
];
$validationRules = array_merge(sharedDataApplications(), $validationRules);
$validator = customApiValidator($request->all(), $validationRules);
@@ -1844,6 +1861,29 @@ class ApplicationsController extends Controller
'errors' => $errors,
], 422);
}
if ($request->has('is_http_basic_auth_enabled') && $request->is_http_basic_auth_enabled === true) {
if (blank($application->http_basic_auth_username) || blank($application->http_basic_auth_password)) {
$validationErrors = [];
if (blank($request->http_basic_auth_username)) {
$validationErrors['http_basic_auth_username'] = 'The http_basic_auth_username is required.';
}
if (blank($request->http_basic_auth_password)) {
$validationErrors['http_basic_auth_password'] = 'The http_basic_auth_password is required.';
}
if (count($validationErrors) > 0) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validationErrors,
], 422);
}
}
}
if ($request->has('is_http_basic_auth_enabled') && $application->is_container_label_readonly_enabled === false) {
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->save();
}
$domains = $request->domains;
$requestHasDomains = $request->has('domains');
if ($requestHasDomains && $server->isProxyShouldRun()) {

View File

@@ -92,11 +92,11 @@ class General extends Component
'application.settings.is_container_label_escape_enabled' => 'boolean|required',
'application.settings.is_container_label_readonly_enabled' => 'boolean|required',
'application.settings.is_preserve_repository_enabled' => 'boolean|required',
'application.is_http_basic_auth_enabled' => 'boolean|required',
'application.http_basic_auth_username' => 'string|nullable',
'application.http_basic_auth_password' => 'string|nullable',
'application.watch_paths' => 'nullable',
'application.redirect' => 'string|required',
'application.http_basic_auth_enabled' => 'boolean|required',
'application.http_basic_auth_username' => 'nullable',
'application.http_basic_auth_password' => 'nullable',
];
protected $validationAttributes = [
@@ -181,6 +181,9 @@ class General extends Component
if ($this->application->settings->isDirty('is_spa')) {
$this->generateNginxConfiguration($this->application->settings->is_spa ? 'spa' : 'static');
}
if ($this->application->isDirty('is_http_basic_auth_enabled')) {
$this->application->save();
}
$this->application->settings->save();
$this->dispatch('success', 'Settings saved.');
$this->application->refresh();

View File

@@ -103,7 +103,7 @@ use Visus\Cuid2\Cuid2;
'deleted_at' => ['type' => 'string', 'format' => 'date-time', 'nullable' => true, 'description' => 'The date and time when the application was deleted.'],
'compose_parsing_version' => ['type' => 'string', 'description' => 'How Coolify parse the compose file.'],
'custom_nginx_configuration' => ['type' => 'string', 'nullable' => true, 'description' => 'Custom Nginx configuration base64 encoded.'],
'http_basic_auth_enabled' => ['type' => 'boolean', 'description' => 'HTTP Basic Authentication enabled.'],
'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'],
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
]
@@ -119,7 +119,10 @@ class Application extends BaseModel
protected $appends = ['server_status'];
protected $casts = ['custom_network_aliases' => 'array'];
protected $casts = [
'custom_network_aliases' => 'array',
'http_basic_auth_password' => 'encrypted',
];
public function customNetworkAliases(): Attribute
{

View File

@@ -297,7 +297,7 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
return $payload;
}
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both', ?string $predefinedPort = null, bool $http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null)
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both', ?string $predefinedPort = null, bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null)
{
$labels = collect([]);
if ($serviceLabels) {
@@ -306,7 +306,8 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
$labels->push("caddy_ingress_network={$network}");
}
$http_basic_auth_enabled = $http_basic_auth_enabled && $http_basic_auth_username !== null && $http_basic_auth_password !== null;
$is_http_basic_auth_enabled = $is_http_basic_auth_enabled && $http_basic_auth_username !== null && $http_basic_auth_password !== null;
$hashedPassword = password_hash($http_basic_auth_password, PASSWORD_BCRYPT, ['cost' => 10]);
foreach ($domains as $loop => $domain) {
$url = Url::fromString($domain);
@@ -344,28 +345,27 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
if ($redirect_direction === 'non-www' && str($host)->startsWith('www.')) {
$labels->push("caddy_{$loop}.redir={$schema}://{$host_without_www}{uri}");
}
if ($http_basic_auth_enabled) {
$http_basic_auth_password = password_hash($http_basic_auth_password, PASSWORD_BCRYPT, ['cost' => 10]);
$labels->push("caddy_{$loop}.basicauth.{$http_basic_auth_username}=\"{$http_basic_auth_password}\"");
if ($is_http_basic_auth_enabled) {
$labels->push("caddy_{$loop}.basicauth.{$http_basic_auth_username}=\"{$hashedPassword}\"");
}
}
return $labels->sort();
}
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null, string $redirect_direction = 'both', bool $http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null)
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null, string $redirect_direction = 'both', bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null)
{
$labels = collect([]);
$labels->push('traefik.enable=true');
$labels->push('traefik.http.middlewares.gzip.compress=true');
$labels->push('traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https');
$http_basic_auth_enabled = $http_basic_auth_enabled && $http_basic_auth_username !== null && $http_basic_auth_password !== null;
$is_http_basic_auth_enabled = $is_http_basic_auth_enabled && $http_basic_auth_username !== null && $http_basic_auth_password !== null;
$http_basic_auth_label = "http-basic-auth-{$uuid}";
$hashedPassword = password_hash($http_basic_auth_password, PASSWORD_BCRYPT, ['cost' => 10]);
if ($http_basic_auth_enabled) {
$http_basic_auth_password = password_hash($http_basic_auth_password, PASSWORD_BCRYPT, ['cost' => 10]);
$labels->push("traefik.http.middlewares.{$http_basic_auth_label}.basicauth.users={$http_basic_auth_username}:{$http_basic_auth_password}");
if ($is_http_basic_auth_enabled) {
$labels->push("traefik.http.middlewares.{$http_basic_auth_label}.basicauth.users={$http_basic_auth_username}:{$hashedPassword}");
}
$middlewares_from_labels = collect([]);
@@ -453,7 +453,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels = $labels->merge($redirect_to_www);
$middlewares->push($to_www_name);
}
if ($http_basic_auth_enabled) {
if ($is_http_basic_auth_enabled) {
$middlewares->push($http_basic_auth_label);
}
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
@@ -479,7 +479,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels = $labels->merge($redirect_to_www);
$middlewares->push($to_www_name);
}
if ($http_basic_auth_enabled) {
if ($is_http_basic_auth_enabled) {
$middlewares->push($http_basic_auth_label);
}
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
@@ -531,7 +531,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels = $labels->merge($redirect_to_www);
$middlewares->push($to_www_name);
}
if ($http_basic_auth_enabled) {
if ($is_http_basic_auth_enabled) {
$middlewares->push($http_basic_auth_label);
}
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
@@ -557,7 +557,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels = $labels->merge($redirect_to_www);
$middlewares->push($to_www_name);
}
if ($http_basic_auth_enabled) {
if ($is_http_basic_auth_enabled) {
$middlewares->push($http_basic_auth_label);
}
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
@@ -604,7 +604,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect,
http_basic_auth_enabled: $application->http_basic_auth_enabled,
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
http_basic_auth_username: $application->http_basic_auth_username,
http_basic_auth_password: $application->http_basic_auth_password,
));
@@ -619,7 +619,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect,
http_basic_auth_enabled: $application->http_basic_auth_enabled,
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
http_basic_auth_username: $application->http_basic_auth_username,
http_basic_auth_password: $application->http_basic_auth_password,
));
@@ -634,7 +634,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect,
http_basic_auth_enabled: $application->http_basic_auth_enabled,
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
http_basic_auth_username: $application->http_basic_auth_username,
http_basic_auth_password: $application->http_basic_auth_password,
));
@@ -647,7 +647,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect,
http_basic_auth_enabled: $application->http_basic_auth_enabled,
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
http_basic_auth_username: $application->http_basic_auth_username,
http_basic_auth_password: $application->http_basic_auth_password,
));
@@ -670,7 +670,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
http_basic_auth_enabled: $application->http_basic_auth_enabled,
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
http_basic_auth_username: $application->http_basic_auth_username,
http_basic_auth_password: $application->http_basic_auth_password,
));
@@ -684,7 +684,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
http_basic_auth_enabled: $application->http_basic_auth_enabled,
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
http_basic_auth_username: $application->http_basic_auth_username,
http_basic_auth_password: $application->http_basic_auth_password,
));
@@ -698,7 +698,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
http_basic_auth_enabled: $application->http_basic_auth_enabled,
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
http_basic_auth_username: $application->http_basic_auth_username,
http_basic_auth_password: $application->http_basic_auth_password,
));
@@ -710,7 +710,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
http_basic_auth_enabled: $application->http_basic_auth_enabled,
is_http_basic_auth_enabled: $application->is_http_basic_auth_enabled,
http_basic_auth_username: $application->http_basic_auth_username,
http_basic_auth_password: $application->http_basic_auth_password,
));
@@ -779,11 +779,14 @@ function convertDockerRunToCompose(?string $custom_docker_run_options = null)
$options[$option] = array_unique($options[$option]);
}
if ($option === '--hostname') {
$regexForParsingHostname = '/hostname=([^\s]+)/';
// Match --hostname=value or --hostname value
$regexForParsingHostname = '/--hostname(?:=|\s+)([^\s]+)/';
preg_match($regexForParsingHostname, $custom_docker_run_options, $hostname_matches);
$value = $hostname_matches[1] ?? null;
$options[$option][] = $value;
$options[$option] = array_unique($options[$option]);
if ($value) {
$options[$option][] = $value;
$options[$option] = array_unique($options[$option]);
}
}
if (isset($match[2]) && $match[2] !== '') {
$value = $match[2];

View File

@@ -12,7 +12,7 @@ return new class extends Migration
public function up(): void
{
Schema::table('applications', function (Blueprint $table) {
$table->boolean('http_basic_auth_enabled')->default(false);
$table->boolean('is_http_basic_auth_enabled')->default(false);
$table->string('http_basic_auth_username')->nullable(true)->default(null);
$table->string('http_basic_auth_password')->nullable(true)->default(null);
});
@@ -24,7 +24,7 @@ return new class extends Migration
public function down(): void
{
Schema::table('applications', function (Blueprint $table) {
$table->dropColumn('http_basic_auth_enabled');
$table->dropColumn('is_http_basic_auth_enabled');
$table->dropColumn('http_basic_auth_username');
$table->dropColumn('http_basic_auth_password');
});

View File

@@ -339,6 +339,20 @@
"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"
},
"http_basic_auth_password": {
"type": "string",
"nullable": true,
"description": "Password for HTTP Basic Authentication"
}
},
"type": "object"
@@ -673,6 +687,20 @@
"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"
},
"http_basic_auth_password": {
"type": "string",
"nullable": true,
"description": "Password for HTTP Basic Authentication"
}
},
"type": "object"
@@ -1007,6 +1035,20 @@
"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"
},
"http_basic_auth_password": {
"type": "string",
"nullable": true,
"description": "Password for HTTP Basic Authentication"
}
},
"type": "object"
@@ -1270,6 +1312,20 @@
"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"
},
"http_basic_auth_password": {
"type": "string",
"nullable": true,
"description": "Password for HTTP Basic Authentication"
}
},
"type": "object"
@@ -1516,6 +1572,20 @@
"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"
},
"http_basic_auth_password": {
"type": "string",
"nullable": true,
"description": "Password for HTTP Basic Authentication"
}
},
"type": "object"
@@ -7554,7 +7624,7 @@
"nullable": true,
"description": "Custom Nginx configuration base64 encoded."
},
"http_basic_auth_enabled": {
"is_http_basic_auth_enabled": {
"type": "boolean",
"description": "HTTP Basic Authentication enabled."
},

View File

@@ -248,6 +248,17 @@ paths:
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'
http_basic_auth_password:
type: string
nullable: true
description: 'Password for HTTP Basic Authentication'
type: object
responses:
'201':
@@ -487,6 +498,17 @@ paths:
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'
http_basic_auth_password:
type: string
nullable: true
description: 'Password for HTTP Basic Authentication'
type: object
responses:
'201':
@@ -726,6 +748,17 @@ paths:
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'
http_basic_auth_password:
type: string
nullable: true
description: 'Password for HTTP Basic Authentication'
type: object
responses:
'201':
@@ -912,6 +945,17 @@ paths:
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'
http_basic_auth_password:
type: string
nullable: true
description: 'Password for HTTP Basic Authentication'
type: object
responses:
'201':
@@ -1089,6 +1133,17 @@ paths:
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'
http_basic_auth_password:
type: string
nullable: true
description: 'Password for HTTP Basic Authentication'
type: object
responses:
'201':
@@ -5042,7 +5097,7 @@ components:
type: string
nullable: true
description: 'Custom Nginx configuration base64 encoded.'
http_basic_auth_enabled:
is_http_basic_auth_enabled:
type: boolean
description: 'HTTP Basic Authentication enabled.'
http_basic_auth_username:

View File

@@ -350,16 +350,18 @@
</div>
<h3 class="pt-8">HTTP Basic Authentication</h3>
<div x-data="{ enabled: {{ $application->http_basic_auth_enabled ? 'true' : 'false' }} }">
<div>
<div class="w-96">
<x-forms.checkbox helper="This will add the proper proxy labels to the container."
label="Enable" id="application.http_basic_auth_enabled" x-model="enabled" />
</div>
<div class="flex gap-2 py-2" x-show="enabled">
<x-forms.input id="application.http_basic_auth_username" label="Username" />
<x-forms.input id="application.http_basic_auth_password" type="password" label="Password" />
<x-forms.checkbox helper="This will add the proper proxy labels to the container." instantSave
label="Enable" id="application.is_http_basic_auth_enabled" />
</div>
@if ($application->is_http_basic_auth_enabled)
<div class="flex gap-2 py-2">
<x-forms.input id="application.http_basic_auth_username" label="Username" required />
<x-forms.input id="application.http_basic_auth_password" type="password" label="Password"
required />
</div>
@endif
</div>
@if ($application->settings->is_container_label_readonly_enabled)

View File

@@ -1,11 +1,39 @@
<?php
test('Hostname', function () {
$input = '--hostname=test';
$output = convertDockerRunToCompose($input);
expect($output)->toBe([
'hostname' => 'test',
]);
});
test('HostnameWithoutEqualSign', function () {
$input = '--hostname test';
$output = convertDockerRunToCompose($input);
expect($output)->toBe([
'hostname' => 'test',
]);
});
test('HostnameWithoutEqualSignAndHyphens', function () {
$input = '--hostname my-super-host';
$output = convertDockerRunToCompose($input);
expect($output)->toBe([
'hostname' => 'my-super-host',
]);
});
test('HostnameWithHyphens', function () {
$input = '--hostname=my-super-host';
$output = convertDockerRunToCompose($input);
expect($output)->toBe([
'hostname' => 'my-super-host',
]);
});
test('ConvertCapAdd', function () {
$input = '--cap-add=NET_ADMIN --cap-add=NET_RAW --cap-add SYS_ADMIN --hostname=my-super-host';
$input = '--cap-add=NET_ADMIN --cap-add=NET_RAW --cap-add SYS_ADMIN';
$output = convertDockerRunToCompose($input);
expect($output)->toBe([
'cap_add' => ['NET_ADMIN', 'NET_RAW', 'SYS_ADMIN'],
'hostname' => 'my-super-host',
]);
});