Merge branch 'next' into feat/deployment-token

This commit is contained in:
Andras Bacsai
2024-12-09 09:16:59 +01:00
449 changed files with 21219 additions and 5891 deletions

View File

@@ -70,7 +70,8 @@ class ApplicationsController extends Controller
items: new OA\Items(ref: '#/components/schemas/Application')
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -180,8 +181,10 @@ class ApplicationsController extends Controller
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
)
),
]
),
responses: [
new OA\Response(
response: 200,
@@ -284,8 +287,10 @@ class ApplicationsController extends Controller
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
)
),
]
),
responses: [
new OA\Response(
response: 200,
@@ -388,8 +393,10 @@ class ApplicationsController extends Controller
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
)
),
]
),
responses: [
new OA\Response(
response: 200,
@@ -476,8 +483,10 @@ class ApplicationsController extends Controller
'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.'],
],
)),
]),
)
),
]
),
responses: [
new OA\Response(
response: 200,
@@ -561,8 +570,10 @@ class ApplicationsController extends Controller
'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.'],
],
)),
]),
)
),
]
),
responses: [
new OA\Response(
response: 200,
@@ -612,8 +623,10 @@ class ApplicationsController extends Controller
'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.'],
],
)),
]),
)
),
]
),
responses: [
new OA\Response(
response: 200,
@@ -636,7 +649,7 @@ class ApplicationsController extends Controller
private function create_application(Request $request, $type)
{
$allowedFields = ['project_uuid', 'environment_name', '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'];
$allowedFields = ['project_uuid', 'environment_name', '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();
@@ -676,6 +689,27 @@ class ApplicationsController extends Controller
$githubAppUuid = $request->github_app_uuid;
$useBuildServer = $request->use_build_server;
$isStatic = $request->is_static;
$customNginxConfiguration = $request->custom_nginx_configuration;
if (! is_null($customNginxConfiguration)) {
if (! isBase64Encoded($customNginxConfiguration)) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'custom_nginx_configuration' => 'The custom_nginx_configuration should be base64 encoded.',
],
], 422);
}
$customNginxConfiguration = base64_decode($customNginxConfiguration);
if (mb_detect_encoding($customNginxConfiguration, 'ASCII', true) === false) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'custom_nginx_configuration' => 'The custom_nginx_configuration should be base64 encoded.',
],
], 422);
}
}
$project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first();
if (! $project) {
@@ -1247,7 +1281,8 @@ class ApplicationsController extends Controller
ref: '#/components/schemas/Application'
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1319,7 +1354,8 @@ class ApplicationsController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1445,8 +1481,10 @@ class ApplicationsController extends Controller
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
)
),
]
),
responses: [
new OA\Response(
response: 200,
@@ -1461,7 +1499,8 @@ class ApplicationsController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1500,7 +1539,7 @@ class ApplicationsController extends Controller
], 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'];
$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'];
$validationRules = [
'name' => 'string|max:255',
@@ -1512,6 +1551,7 @@ class ApplicationsController extends Controller
'docker_compose_domains' => 'array|nullable',
'docker_compose_custom_start_command' => 'string|nullable',
'docker_compose_custom_build_command' => 'string|nullable',
'custom_nginx_configuration' => 'string|nullable',
];
$validationRules = array_merge($validationRules, sharedDataApplications());
$validator = customApiValidator($request->all(), $validationRules);
@@ -1530,6 +1570,25 @@ class ApplicationsController extends Controller
}
}
}
if ($request->has('custom_nginx_configuration')) {
if (! isBase64Encoded($request->custom_nginx_configuration)) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'custom_nginx_configuration' => 'The custom_nginx_configuration should be base64 encoded.',
],
], 422);
}
$customNginxConfiguration = base64_decode($request->custom_nginx_configuration);
if (mb_detect_encoding($customNginxConfiguration, 'ASCII', true) === false) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'custom_nginx_configuration' => 'The custom_nginx_configuration should be base64 encoded.',
],
], 422);
}
}
$return = $this->validateDataApplications($request, $server);
if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return;
@@ -1550,16 +1609,33 @@ class ApplicationsController extends Controller
}
$domains = $request->domains;
if ($request->has('domains') && $server->isProxyShouldRun()) {
$errors = [];
$uuid = $request->uuid;
$fqdn = $request->domains;
$fqdn = str($fqdn)->replaceEnd(',', '')->trim();
$fqdn = str($fqdn)->replaceStart(',', '')->trim();
$application->fqdn = $fqdn;
if (! $application->settings->is_container_label_readonly_enabled) {
$customLabels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
$application->custom_labels = base64_encode($customLabels);
$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;
}
return $domain;
});
if (count($errors) > 0) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $errors,
], 422);
}
if (checkIfDomainIsAlreadyUsed($fqdn, $teamId, $uuid)) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'domains' => 'One of the domain is already used.',
],
], 422);
}
$request->offsetUnset('domains');
}
$dockerComposeDomainsJson = collect();
@@ -1649,7 +1725,8 @@ class ApplicationsController extends Controller
items: new OA\Items(ref: '#/components/schemas/EnvironmentVariable')
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1755,7 +1832,8 @@ class ApplicationsController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1943,7 +2021,8 @@ class ApplicationsController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -2124,7 +2203,8 @@ class ApplicationsController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -2273,7 +2353,8 @@ class ApplicationsController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -2365,9 +2446,11 @@ class ApplicationsController extends Controller
properties: [
'message' => ['type' => 'string', 'example' => 'Deployment request queued.', 'description' => 'Message.'],
'deployment_uuid' => ['type' => 'string', 'example' => 'doogksw', 'description' => 'UUID of the deployment.'],
])
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -2453,7 +2536,8 @@ class ApplicationsController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -2527,7 +2611,8 @@ class ApplicationsController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,

View File

@@ -211,8 +211,9 @@ class DatabasesController extends Controller
'mongo_conf' => ['type' => 'string', 'description' => 'Mongo conf'],
'mongo_initdb_root_username' => ['type' => 'string', 'description' => 'Mongo initdb root username'],
'mongo_initdb_root_password' => ['type' => 'string', 'description' => 'Mongo initdb root password'],
'mongo_initdb_init_database' => ['type' => 'string', 'description' => 'Mongo initdb init database'],
'mongo_initdb_database' => ['type' => 'string', 'description' => 'Mongo initdb init database'],
'mysql_root_password' => ['type' => 'string', 'description' => 'MySQL root password'],
'mysql_password' => ['type' => 'string', 'description' => 'MySQL password'],
'mysql_user' => ['type' => 'string', 'description' => 'MySQL user'],
'mysql_database' => ['type' => 'string', 'description' => 'MySQL database'],
'mysql_conf' => ['type' => 'string', 'description' => 'MySQL conf'],
@@ -241,7 +242,7 @@ class DatabasesController extends Controller
)]
public function update_by_uuid(Request $request)
{
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database', 'mysql_root_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
return invalidTokenResponse();
@@ -413,12 +414,12 @@ class DatabasesController extends Controller
}
break;
case 'standalone-mongodb':
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database'];
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database'];
$validator = customApiValidator($request->all(), [
'mongo_conf' => 'string',
'mongo_initdb_root_username' => 'string',
'mongo_initdb_root_password' => 'string',
'mongo_initdb_init_database' => 'string',
'mongo_initdb_database' => 'string',
]);
if ($request->has('mongo_conf')) {
if (! isBase64Encoded($request->mongo_conf)) {
@@ -443,9 +444,10 @@ class DatabasesController extends Controller
break;
case 'standalone-mysql':
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
$validator = customApiValidator($request->all(), [
'mysql_root_password' => 'string',
'mysql_password' => 'string',
'mysql_user' => 'string',
'mysql_database' => 'string',
'mysql_conf' => 'string',
@@ -909,6 +911,7 @@ class DatabasesController extends Controller
'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'],
'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'],
'mysql_root_password' => ['type' => 'string', 'description' => 'MySQL root password'],
'mysql_password' => ['type' => 'string', 'description' => 'MySQL password'],
'mysql_user' => ['type' => 'string', 'description' => 'MySQL user'],
'mysql_database' => ['type' => 'string', 'description' => 'MySQL database'],
'mysql_conf' => ['type' => 'string', 'description' => 'MySQL conf'],
@@ -1013,7 +1016,7 @@ class DatabasesController extends Controller
public function create_database(Request $request, NewDatabaseTypes $type)
{
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database', 'mysql_root_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
@@ -1220,9 +1223,10 @@ class DatabasesController extends Controller
return response()->json(serializeApiResponse($payload))->setStatusCode(201);
} elseif ($type === NewDatabaseTypes::MYSQL) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_user', 'mysql_database', 'mysql_conf'];
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
$validator = customApiValidator($request->all(), [
'mysql_root_password' => 'string',
'mysql_password' => 'string',
'mysql_user' => 'string',
'mysql_database' => 'string',
'mysql_conf' => 'string',
@@ -1456,12 +1460,12 @@ class DatabasesController extends Controller
return response()->json(serializeApiResponse($payload))->setStatusCode(201);
} elseif ($type === NewDatabaseTypes::MONGODB) {
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_init_database'];
$allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database'];
$validator = customApiValidator($request->all(), [
'mongo_conf' => 'string',
'mongo_initdb_root_username' => 'string',
'mongo_initdb_root_password' => 'string',
'mongo_initdb_init_database' => 'string',
'mongo_initdb_database' => 'string',
]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || ! empty($extraFields)) {
@@ -1557,7 +1561,8 @@ class DatabasesController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1632,9 +1637,11 @@ class DatabasesController extends Controller
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Database starting request queued.'],
])
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1708,9 +1715,11 @@ class DatabasesController extends Controller
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Database stopping request queued.'],
])
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1784,9 +1793,11 @@ class DatabasesController extends Controller
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Database restaring request queued.'],
])
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',

View File

@@ -37,7 +37,7 @@ class OtherController extends Controller
)]
public function version(Request $request)
{
return response(config('version'));
return response(config('constants.coolify.version'));
}
#[OA\Get(
@@ -147,7 +147,7 @@ class OtherController extends Controller
public function feedback(Request $request)
{
$content = $request->input('content');
$webhook_url = config('coolify.feedback_discord_webhook');
$webhook_url = config('constants.webhooks.feedback_discord_webhook');
if ($webhook_url) {
Http::post($webhook_url, [
'content' => $content,

View File

@@ -116,7 +116,7 @@ class ProjectController extends Controller
responses: [
new OA\Response(
response: 200,
description: 'Project details',
description: 'Environment details',
content: new OA\JsonContent(ref: '#/components/schemas/Environment')),
new OA\Response(
response: 401,
@@ -422,7 +422,7 @@ class ProjectController extends Controller
if (! $project) {
return response()->json(['message' => 'Project not found.'], 404);
}
if ($project->resource_count() > 0) {
if (! $project->isEmpty()) {
return response()->json(['message' => 'Project has resources, so it cannot be deleted.'], 400);
}

View File

@@ -81,15 +81,8 @@ class SecurityController extends Controller
new OA\Response(
response: 200,
description: 'Get all private keys.',
content: [
new OA\MediaType(
mediaType: 'application/json',
schema: new OA\Schema(
type: 'array',
items: new OA\Items(ref: '#/components/schemas/PrivateKey')
)
),
]),
content: new OA\JsonContent(ref: '#/components/schemas/PrivateKey')
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',

View File

@@ -426,6 +426,7 @@ class ServersController extends Controller
'private_key_uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the private key.'],
'is_build_server' => ['type' => 'boolean', 'example' => false, 'description' => 'Is build server.'],
'instant_validate' => ['type' => 'boolean', 'example' => false, 'description' => 'Instant validate.'],
'proxy_type' => ['type' => 'string', 'enum' => ['traefik', 'caddy', 'none'], 'example' => 'traefik', 'description' => 'The proxy type.'],
],
),
),
@@ -461,7 +462,7 @@ class ServersController extends Controller
)]
public function create_server(Request $request)
{
$allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate'];
$allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate', 'proxy_type'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
@@ -481,6 +482,7 @@ class ServersController extends Controller
'user' => 'string|nullable',
'is_build_server' => 'boolean|nullable',
'instant_validate' => 'boolean|nullable',
'proxy_type' => 'string|nullable',
]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
@@ -512,6 +514,14 @@ class ServersController extends Controller
if (is_null($request->instant_validate)) {
$request->offsetSet('instant_validate', false);
}
if ($request->proxy_type) {
$validProxyTypes = collect(ProxyTypes::cases())->map(function ($proxyType) {
return str($proxyType->value)->lower();
});
if (! $validProxyTypes->contains(str($request->proxy_type)->lower())) {
return response()->json(['message' => 'Invalid proxy type.'], 422);
}
}
$privateKey = PrivateKey::whereTeamId($teamId)->whereUuid($request->private_key_uuid)->first();
if (! $privateKey) {
return response()->json(['message' => 'Private key not found.'], 404);
@@ -521,6 +531,8 @@ class ServersController extends Controller
return response()->json(['message' => 'Server with this IP already exists.'], 400);
}
$proxyType = $request->proxy_type ? str($request->proxy_type)->upper() : ProxyTypes::TRAEFIK->value;
$server = ModelsServer::create([
'name' => $request->name,
'description' => $request->description,
@@ -530,7 +542,7 @@ class ServersController extends Controller
'private_key_id' => $privateKey->id,
'team_id' => $teamId,
'proxy' => [
'type' => ProxyTypes::TRAEFIK->value,
'type' => $proxyType,
'status' => ProxyStatus::EXITED->value,
],
]);
@@ -555,6 +567,9 @@ class ServersController extends Controller
['bearerAuth' => []],
],
tags: ['Servers'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server UUID', schema: new OA\Schema(type: 'string')),
],
requestBody: new OA\RequestBody(
required: true,
description: 'Server updated.',
@@ -571,6 +586,7 @@ class ServersController extends Controller
'private_key_uuid' => ['type' => 'string', 'description' => 'The UUID of the private key.'],
'is_build_server' => ['type' => 'boolean', 'description' => 'Is build server.'],
'instant_validate' => ['type' => 'boolean', 'description' => 'Instant validate.'],
'proxy_type' => ['type' => 'string', 'enum' => ['traefik', 'caddy', 'none'], 'description' => 'The proxy type.'],
],
),
),
@@ -583,8 +599,7 @@ class ServersController extends Controller
new OA\MediaType(
mediaType: 'application/json',
schema: new OA\Schema(
type: 'array',
items: new OA\Items(ref: '#/components/schemas/Server')
ref: '#/components/schemas/Server'
)
),
]),
@@ -604,7 +619,7 @@ class ServersController extends Controller
)]
public function update_server(Request $request)
{
$allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate'];
$allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate', 'proxy_type'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
@@ -624,6 +639,7 @@ class ServersController extends Controller
'user' => 'string|nullable',
'is_build_server' => 'boolean|nullable',
'instant_validate' => 'boolean|nullable',
'proxy_type' => 'string|nullable',
]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
@@ -644,6 +660,16 @@ class ServersController extends Controller
if (! $server) {
return response()->json(['message' => 'Server not found.'], 404);
}
if ($request->proxy_type) {
$validProxyTypes = collect(ProxyTypes::cases())->map(function ($proxyType) {
return str($proxyType->value)->lower();
});
if ($validProxyTypes->contains(str($request->proxy_type)->lower())) {
$server->changeProxy($request->proxy_type, async: true);
} else {
return response()->json(['message' => 'Invalid proxy type.'], 422);
}
}
$server->update($request->only(['name', 'description', 'ip', 'port', 'user']));
if ($request->is_build_server) {
$server->settings()->update([
@@ -654,7 +680,9 @@ class ServersController extends Controller
ValidateServer::dispatch($server);
}
return response()->json(serializeApiResponse($server))->setStatusCode(201);
return response()->json([
'uuid' => $server->uuid,
])->setStatusCode(201);
}
#[OA\Delete(