Compare commits
20 commits
b87d54f9e4
...
972f66317e
| Author | SHA1 | Date | |
|---|---|---|---|
| 972f66317e | |||
| 06072915d1 | |||
|
|
a2fa98deb7 | ||
|
|
b971440202 | ||
|
|
e0f8ac4159 | ||
|
|
95091e918f | ||
|
|
04e71916e5 | ||
|
|
51301fd12e | ||
|
|
fbacf7076e | ||
|
|
650186b1ab | ||
|
|
a05c198554 | ||
|
|
e53c71908f | ||
|
|
161e0d2b05 | ||
|
|
6ca04b5613 | ||
|
|
8a1d76cd99 | ||
|
|
fb56959418 | ||
|
|
5f5c26d841 | ||
|
|
754448d9d4 | ||
|
|
c66b6490e6 | ||
|
|
f4acf7ca10 |
126 changed files with 1068 additions and 550 deletions
|
|
@ -102,7 +102,8 @@ public function handle(Server $server, $fromUI = false): bool
|
|||
foreach ($conflicts as $port => $conflict) {
|
||||
if ($conflict) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port $port is in use.<br>You must stop the process using this port.<br><br>Docs: <a target='_blank' class='dark:text-white hover:underline' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' class='dark:text-white hover:underline' href='https://coolify.io/discord'>https://coolify.io/discord</a>");
|
||||
// MapleDeploy branding: support links
|
||||
throw new \Exception("Port $port is in use.<br>You must stop the process using this port.<br><br>Support: <a target='_blank' class='dark:text-white hover:underline' href='https://mapledeploy.ca/contact'>https://mapledeploy.ca/contact</a>");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,15 +263,11 @@ private function restoreCoolifyDbBackup()
|
|||
}
|
||||
}
|
||||
|
||||
// MapleDeploy branding: telemetry disabled — no phone-home signal
|
||||
private function sendAliveSignal()
|
||||
{
|
||||
$id = config('app.id');
|
||||
$version = config('constants.coolify.version');
|
||||
try {
|
||||
Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version");
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in sending live signal: {$e->getMessage()}\n";
|
||||
}
|
||||
// Disabled for MapleDeploy: do not send telemetry to coolify.io
|
||||
return;
|
||||
}
|
||||
|
||||
private function replaceSlashInEnvironmentName()
|
||||
|
|
|
|||
|
|
@ -153,11 +153,14 @@ public function applications(Request $request)
|
|||
'destination_uuid' => ['type' => 'string', 'description' => 'The destination UUID.'],
|
||||
'name' => ['type' => 'string', 'description' => 'The application name.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
|
||||
'git_commit_sha' => ['type' => 'string', 'description' => 'The git commit SHA.'],
|
||||
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
|
||||
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
|
||||
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
|
||||
'is_spa' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'],
|
||||
'is_auto_deploy_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'],
|
||||
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
|
||||
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
|
||||
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
|
||||
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
|
||||
|
|
@ -198,6 +201,7 @@ public function applications(Request $request)
|
|||
// 'github_app_uuid' => ['type' => 'string', 'description' => 'The Github App UUID.'],
|
||||
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
|
||||
'dockerfile' => ['type' => 'string', 'description' => 'The Dockerfile content.'],
|
||||
'dockerfile_location' => ['type' => 'string', 'description' => 'The Dockerfile location in the repository.'],
|
||||
'docker_compose_location' => ['type' => 'string', 'description' => 'The Docker Compose location.'],
|
||||
'docker_compose_custom_start_command' => ['type' => 'string', 'description' => 'The Docker Compose custom start command.'],
|
||||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
|
|
@ -315,11 +319,14 @@ public function create_public_application(Request $request)
|
|||
'build_pack' => ['type' => 'string', 'enum' => ['nixpacks', 'static', 'dockerfile', 'dockercompose'], 'description' => 'The build pack type.'],
|
||||
'name' => ['type' => 'string', 'description' => 'The application name.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
|
||||
'git_commit_sha' => ['type' => 'string', 'description' => 'The git commit SHA.'],
|
||||
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
|
||||
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
|
||||
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
|
||||
'is_spa' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'],
|
||||
'is_auto_deploy_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'],
|
||||
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
|
||||
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
|
||||
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
|
||||
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
|
||||
|
|
@ -359,6 +366,7 @@ public function create_public_application(Request $request)
|
|||
'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.'],
|
||||
'dockerfile' => ['type' => 'string', 'description' => 'The Dockerfile content.'],
|
||||
'dockerfile_location' => ['type' => 'string', 'description' => 'The Dockerfile location in the repository'],
|
||||
'docker_compose_location' => ['type' => 'string', 'description' => 'The Docker Compose location.'],
|
||||
'docker_compose_custom_start_command' => ['type' => 'string', 'description' => 'The Docker Compose custom start command.'],
|
||||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
|
|
@ -476,11 +484,14 @@ public function create_private_gh_app_application(Request $request)
|
|||
'build_pack' => ['type' => 'string', 'enum' => ['nixpacks', 'static', 'dockerfile', 'dockercompose'], 'description' => 'The build pack type.'],
|
||||
'name' => ['type' => 'string', 'description' => 'The application name.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
|
||||
'git_commit_sha' => ['type' => 'string', 'description' => 'The git commit SHA.'],
|
||||
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
|
||||
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
|
||||
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
|
||||
'is_spa' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'],
|
||||
'is_auto_deploy_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'],
|
||||
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
|
||||
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
|
||||
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
|
||||
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
|
||||
|
|
@ -520,6 +531,7 @@ public function create_private_gh_app_application(Request $request)
|
|||
'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.'],
|
||||
'dockerfile' => ['type' => 'string', 'description' => 'The Dockerfile content.'],
|
||||
'dockerfile_location' => ['type' => 'string', 'description' => 'The Dockerfile location in the repository.'],
|
||||
'docker_compose_location' => ['type' => 'string', 'description' => 'The Docker Compose location.'],
|
||||
'docker_compose_custom_start_command' => ['type' => 'string', 'description' => 'The Docker Compose custom start command.'],
|
||||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
|
|
@ -635,7 +647,7 @@ public function create_private_deploy_key_application(Request $request)
|
|||
'destination_uuid' => ['type' => 'string', 'description' => 'The destination UUID.'],
|
||||
'name' => ['type' => 'string', 'description' => 'The application name.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
|
||||
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
|
||||
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
|
||||
'ports_mappings' => ['type' => 'string', 'description' => 'The ports mappings.'],
|
||||
|
|
@ -671,6 +683,7 @@ public function create_private_deploy_key_application(Request $request)
|
|||
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
|
||||
'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.'],
|
||||
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
|
||||
'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'],
|
||||
|
|
@ -771,7 +784,7 @@ public function create_dockerfile_application(Request $request)
|
|||
'destination_uuid' => ['type' => 'string', 'description' => 'The destination UUID.'],
|
||||
'name' => ['type' => 'string', 'description' => 'The application name.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
|
||||
'ports_mappings' => ['type' => 'string', 'description' => 'The ports mappings.'],
|
||||
'health_check_enabled' => ['type' => 'boolean', 'description' => 'Health check enabled.'],
|
||||
'health_check_path' => ['type' => 'string', 'description' => 'Health check path.'],
|
||||
|
|
@ -804,6 +817,7 @@ public function create_dockerfile_application(Request $request)
|
|||
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
|
||||
'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.'],
|
||||
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
|
||||
'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'],
|
||||
|
|
@ -987,7 +1001,7 @@ private function create_application(Request $request, $type)
|
|||
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', 'custom_network_aliases', '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', 'connect_to_docker_network', 'force_domain_override', 'autogenerate_domain', 'is_container_label_escape_enabled'];
|
||||
$allowedFields = ['project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'is_spa', 'is_auto_deploy_enabled', 'is_force_https_enabled', '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', 'custom_network_aliases', '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', 'dockerfile_location', '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', 'connect_to_docker_network', 'force_domain_override', 'autogenerate_domain', 'is_container_label_escape_enabled'];
|
||||
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'name' => 'string|max:255',
|
||||
|
|
@ -1030,6 +1044,9 @@ private function create_application(Request $request, $type)
|
|||
$githubAppUuid = $request->github_app_uuid;
|
||||
$useBuildServer = $request->use_build_server;
|
||||
$isStatic = $request->is_static;
|
||||
$isSpa = $request->is_spa;
|
||||
$isAutoDeployEnabled = $request->is_auto_deploy_enabled;
|
||||
$isForceHttpsEnabled = $request->is_force_https_enabled;
|
||||
$connectToDockerNetwork = $request->connect_to_docker_network;
|
||||
$customNginxConfiguration = $request->custom_nginx_configuration;
|
||||
$isContainerLabelEscapeEnabled = $request->boolean('is_container_label_escape_enabled', true);
|
||||
|
|
@ -1130,39 +1147,63 @@ private function create_application(Request $request, $type)
|
|||
$dockerComposeDomainsJson = collect();
|
||||
if ($request->has('docker_compose_domains')) {
|
||||
$dockerComposeDomains = collect($request->docker_compose_domains);
|
||||
$domainErrors = [];
|
||||
|
||||
foreach ($dockerComposeDomains as $index => $item) {
|
||||
// Collect all URLs from all docker_compose_domains items
|
||||
$urls = $dockerComposeDomains->flatMap(function ($item) {
|
||||
$domainValue = data_get($item, 'domain');
|
||||
if (filled($domainValue)) {
|
||||
$urls = str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim();
|
||||
str($urls)->explode(',')->each(function ($url) use (&$domainErrors) {
|
||||
$url = trim($url);
|
||||
if (empty($url)) {
|
||||
return;
|
||||
if (blank($domainValue)) {
|
||||
return [];
|
||||
}
|
||||
if (! filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$domainErrors[] = "Invalid URL: {$url}";
|
||||
|
||||
return;
|
||||
return str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim()->explode(',')->map(fn ($url) => trim($url))->filter();
|
||||
});
|
||||
|
||||
$errors = [];
|
||||
$urls = $urls->map(function ($url) use (&$errors) {
|
||||
if (! filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$errors[] = "Invalid URL: {$url}";
|
||||
|
||||
return $url;
|
||||
}
|
||||
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
|
||||
if (! in_array(strtolower($scheme), ['http', 'https'])) {
|
||||
$domainErrors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
|
||||
}
|
||||
});
|
||||
}
|
||||
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
|
||||
}
|
||||
|
||||
if (! empty($domainErrors)) {
|
||||
return $url;
|
||||
});
|
||||
|
||||
$duplicates = $urls->duplicates()->unique()->values();
|
||||
if ($duplicates->isNotEmpty() && ! $request->boolean('force_domain_override')) {
|
||||
$errors[] = 'The current request contains conflicting URLs: '.implode(', ', $duplicates->toArray()).' Use force_domain_override=true to proceed.';
|
||||
}
|
||||
|
||||
if (count($errors) > 0) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => [
|
||||
'docker_compose_domains' => $domainErrors,
|
||||
],
|
||||
'errors' => ['docker_compose_domains' => $errors],
|
||||
], 422);
|
||||
}
|
||||
|
||||
// Check for domain conflicts
|
||||
if ($urls->isNotEmpty()) {
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId);
|
||||
if (isset($result['error'])) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['docker_compose_domains' => $result['error']],
|
||||
], 422);
|
||||
}
|
||||
|
||||
if ($result['hasConflicts'] && ! $request->boolean('force_domain_override')) {
|
||||
return response()->json([
|
||||
'message' => 'Domain conflicts detected. Use force_domain_override=true to proceed.',
|
||||
'conflicts' => $result['conflicts'],
|
||||
'warning' => 'Using the same domain for multiple resources can cause routing conflicts and unpredictable behavior.',
|
||||
], 409);
|
||||
}
|
||||
}
|
||||
|
||||
$dockerComposeDomains->each(function ($domain) use ($dockerComposeDomainsJson) {
|
||||
$dockerComposeDomainsJson->put(data_get($domain, 'name'), ['domain' => data_get($domain, 'domain')]);
|
||||
});
|
||||
|
|
@ -1187,6 +1228,18 @@ private function create_application(Request $request, $type)
|
|||
$application->settings->is_static = $isStatic;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isSpa)) {
|
||||
$application->settings->is_spa = $isSpa;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isAutoDeployEnabled)) {
|
||||
$application->settings->is_auto_deploy_enabled = $isAutoDeployEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isForceHttpsEnabled)) {
|
||||
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($connectToDockerNetwork)) {
|
||||
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
|
||||
$application->settings->save();
|
||||
|
|
@ -1319,39 +1372,63 @@ private function create_application(Request $request, $type)
|
|||
$dockerComposeDomainsJson = collect();
|
||||
if ($request->has('docker_compose_domains')) {
|
||||
$dockerComposeDomains = collect($request->docker_compose_domains);
|
||||
$domainErrors = [];
|
||||
|
||||
foreach ($dockerComposeDomains as $index => $item) {
|
||||
// Collect all URLs from all docker_compose_domains items
|
||||
$urls = $dockerComposeDomains->flatMap(function ($item) {
|
||||
$domainValue = data_get($item, 'domain');
|
||||
if (filled($domainValue)) {
|
||||
$urls = str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim();
|
||||
str($urls)->explode(',')->each(function ($url) use (&$domainErrors) {
|
||||
$url = trim($url);
|
||||
if (empty($url)) {
|
||||
return;
|
||||
if (blank($domainValue)) {
|
||||
return [];
|
||||
}
|
||||
if (! filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$domainErrors[] = "Invalid URL: {$url}";
|
||||
|
||||
return;
|
||||
return str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim()->explode(',')->map(fn ($url) => trim($url))->filter();
|
||||
});
|
||||
|
||||
$errors = [];
|
||||
$urls = $urls->map(function ($url) use (&$errors) {
|
||||
if (! filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$errors[] = "Invalid URL: {$url}";
|
||||
|
||||
return $url;
|
||||
}
|
||||
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
|
||||
if (! in_array(strtolower($scheme), ['http', 'https'])) {
|
||||
$domainErrors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
|
||||
}
|
||||
});
|
||||
}
|
||||
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
|
||||
}
|
||||
|
||||
if (! empty($domainErrors)) {
|
||||
return $url;
|
||||
});
|
||||
|
||||
$duplicates = $urls->duplicates()->unique()->values();
|
||||
if ($duplicates->isNotEmpty() && ! $request->boolean('force_domain_override')) {
|
||||
$errors[] = 'The current request contains conflicting URLs: '.implode(', ', $duplicates->toArray()).' Use force_domain_override=true to proceed. ';
|
||||
}
|
||||
|
||||
if (count($errors) > 0) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => [
|
||||
'docker_compose_domains' => $domainErrors,
|
||||
],
|
||||
'errors' => ['docker_compose_domains' => $errors],
|
||||
], 422);
|
||||
}
|
||||
|
||||
// Check for domain conflicts
|
||||
if ($urls->isNotEmpty()) {
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId);
|
||||
if (isset($result['error'])) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['docker_compose_domains' => $result['error']],
|
||||
], 422);
|
||||
}
|
||||
|
||||
if ($result['hasConflicts'] && ! $request->boolean('force_domain_override')) {
|
||||
return response()->json([
|
||||
'message' => 'Domain conflicts detected. Use force_domain_override=true to proceed.',
|
||||
'conflicts' => $result['conflicts'],
|
||||
'warning' => 'Using the same domain for multiple resources can cause routing conflicts and unpredictable behavior.',
|
||||
], 409);
|
||||
}
|
||||
}
|
||||
|
||||
$dockerComposeDomains->each(function ($domain) use ($dockerComposeDomainsJson) {
|
||||
$dockerComposeDomainsJson->put(data_get($domain, 'name'), ['domain' => data_get($domain, 'domain')]);
|
||||
});
|
||||
|
|
@ -1376,6 +1453,26 @@ private function create_application(Request $request, $type)
|
|||
$application->fqdn = generateUrl(server: $server, random: $application->uuid);
|
||||
$application->save();
|
||||
}
|
||||
if (isset($isStatic)) {
|
||||
$application->settings->is_static = $isStatic;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isSpa)) {
|
||||
$application->settings->is_spa = $isSpa;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isAutoDeployEnabled)) {
|
||||
$application->settings->is_auto_deploy_enabled = $isAutoDeployEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isForceHttpsEnabled)) {
|
||||
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($connectToDockerNetwork)) {
|
||||
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
|
|
@ -1476,39 +1573,63 @@ private function create_application(Request $request, $type)
|
|||
$dockerComposeDomainsJson = collect();
|
||||
if ($request->has('docker_compose_domains')) {
|
||||
$dockerComposeDomains = collect($request->docker_compose_domains);
|
||||
$domainErrors = [];
|
||||
|
||||
foreach ($dockerComposeDomains as $index => $item) {
|
||||
// Collect all URLs from all docker_compose_domains items
|
||||
$urls = $dockerComposeDomains->flatMap(function ($item) {
|
||||
$domainValue = data_get($item, 'domain');
|
||||
if (filled($domainValue)) {
|
||||
$urls = str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim();
|
||||
str($urls)->explode(',')->each(function ($url) use (&$domainErrors) {
|
||||
$url = trim($url);
|
||||
if (empty($url)) {
|
||||
return;
|
||||
if (blank($domainValue)) {
|
||||
return [];
|
||||
}
|
||||
if (! filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$domainErrors[] = "Invalid URL: {$url}";
|
||||
|
||||
return;
|
||||
return str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim()->explode(',')->map(fn ($url) => trim($url))->filter();
|
||||
});
|
||||
|
||||
$errors = [];
|
||||
$urls = $urls->map(function ($url) use (&$errors) {
|
||||
if (! filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$errors[] = "Invalid URL: {$url}";
|
||||
|
||||
return $url;
|
||||
}
|
||||
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
|
||||
if (! in_array(strtolower($scheme), ['http', 'https'])) {
|
||||
$domainErrors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
|
||||
}
|
||||
});
|
||||
}
|
||||
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
|
||||
}
|
||||
|
||||
if (! empty($domainErrors)) {
|
||||
return $url;
|
||||
});
|
||||
|
||||
$duplicates = $urls->duplicates()->unique()->values();
|
||||
if ($duplicates->isNotEmpty() && ! $request->boolean('force_domain_override')) {
|
||||
$errors[] = 'The current request contains conflicting URLs: '.implode(', ', $duplicates->toArray()).' Use force_domain_override=true to proceed.';
|
||||
}
|
||||
|
||||
if (count($errors) > 0) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => [
|
||||
'docker_compose_domains' => $domainErrors,
|
||||
],
|
||||
'errors' => ['docker_compose_domains' => $errors],
|
||||
], 422);
|
||||
}
|
||||
|
||||
// Check for domain conflicts
|
||||
if ($urls->isNotEmpty()) {
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId);
|
||||
if (isset($result['error'])) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['docker_compose_domains' => $result['error']],
|
||||
], 422);
|
||||
}
|
||||
|
||||
if ($result['hasConflicts'] && ! $request->boolean('force_domain_override')) {
|
||||
return response()->json([
|
||||
'message' => 'Domain conflicts detected. Use force_domain_override=true to proceed.',
|
||||
'conflicts' => $result['conflicts'],
|
||||
'warning' => 'Using the same domain for multiple resources can cause routing conflicts and unpredictable behavior.',
|
||||
], 409);
|
||||
}
|
||||
}
|
||||
|
||||
$dockerComposeDomains->each(function ($domain) use ($dockerComposeDomainsJson) {
|
||||
$dockerComposeDomainsJson->put(data_get($domain, 'name'), ['domain' => data_get($domain, 'domain')]);
|
||||
});
|
||||
|
|
@ -1529,6 +1650,26 @@ private function create_application(Request $request, $type)
|
|||
$application->fqdn = generateUrl(server: $server, random: $application->uuid);
|
||||
$application->save();
|
||||
}
|
||||
if (isset($isStatic)) {
|
||||
$application->settings->is_static = $isStatic;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isSpa)) {
|
||||
$application->settings->is_spa = $isSpa;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isAutoDeployEnabled)) {
|
||||
$application->settings->is_auto_deploy_enabled = $isAutoDeployEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isForceHttpsEnabled)) {
|
||||
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($connectToDockerNetwork)) {
|
||||
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
|
|
@ -1632,6 +1773,14 @@ private function create_application(Request $request, $type)
|
|||
$application->fqdn = generateUrl(server: $server, random: $application->uuid);
|
||||
$application->save();
|
||||
}
|
||||
if (isset($isForceHttpsEnabled)) {
|
||||
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($connectToDockerNetwork)) {
|
||||
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
|
|
@ -1734,6 +1883,14 @@ private function create_application(Request $request, $type)
|
|||
$application->fqdn = generateUrl(server: $server, random: $application->uuid);
|
||||
$application->save();
|
||||
}
|
||||
if (isset($isForceHttpsEnabled)) {
|
||||
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($connectToDockerNetwork)) {
|
||||
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
|
|
@ -2150,11 +2307,14 @@ public function delete_by_uuid(Request $request)
|
|||
'build_pack' => ['type' => 'string', 'enum' => ['nixpacks', 'static', 'dockerfile', 'dockercompose'], 'description' => 'The build pack type.'],
|
||||
'name' => ['type' => 'string', 'description' => 'The application name.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application domains.'],
|
||||
'domains' => ['type' => 'string', 'description' => 'The application URLs in a comma-separated list.'],
|
||||
'git_commit_sha' => ['type' => 'string', 'description' => 'The git commit SHA.'],
|
||||
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
|
||||
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
|
||||
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
|
||||
'is_spa' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'],
|
||||
'is_auto_deploy_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'],
|
||||
'is_force_https_enabled' => ['type' => 'boolean', 'description' => 'The flag to indicate if HTTPS is forced. Defaults to true.'],
|
||||
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
|
||||
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
|
||||
'start_command' => ['type' => 'string', 'description' => 'The start command.'],
|
||||
|
|
@ -2193,6 +2353,7 @@ public function delete_by_uuid(Request $request)
|
|||
'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.'],
|
||||
'dockerfile' => ['type' => 'string', 'description' => 'The Dockerfile content.'],
|
||||
'dockerfile_location' => ['type' => 'string', 'description' => 'The Dockerfile location in the repository.'],
|
||||
'docker_compose_location' => ['type' => 'string', 'description' => 'The Docker Compose location.'],
|
||||
'docker_compose_custom_start_command' => ['type' => 'string', 'description' => 'The Docker Compose custom start command.'],
|
||||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
|
|
@ -2297,7 +2458,7 @@ public function update_by_uuid(Request $request)
|
|||
$this->authorize('update', $application);
|
||||
|
||||
$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','custom_network_aliases', '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_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'];
|
||||
$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_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', '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'];
|
||||
|
||||
$validationRules = [
|
||||
'name' => 'string|max:255',
|
||||
|
|
@ -2411,18 +2572,30 @@ public function update_by_uuid(Request $request)
|
|||
$requestHasDomains = $request->has('domains');
|
||||
if ($requestHasDomains && $server->isProxyShouldRun()) {
|
||||
$uuid = $request->uuid;
|
||||
$fqdn = $request->domains;
|
||||
$fqdn = str($fqdn)->replaceEnd(',', '')->trim();
|
||||
$fqdn = str($fqdn)->replaceStart(',', '')->trim();
|
||||
$urls = $request->domains;
|
||||
$urls = str($urls)->replaceStart(',', '')->replaceEnd(',', '')->trim();
|
||||
$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;
|
||||
$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;
|
||||
}
|
||||
|
||||
return $domain;
|
||||
if (! filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$errors[] = 'Invalid URL: '.$url;
|
||||
|
||||
return $url;
|
||||
}
|
||||
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
|
||||
if (! in_array(strtolower($scheme), ['http', 'https'])) {
|
||||
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
|
||||
}
|
||||
|
||||
return str($url)->lower();
|
||||
});
|
||||
|
||||
if (count($errors) > 0) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
|
|
@ -2430,7 +2603,7 @@ public function update_by_uuid(Request $request)
|
|||
], 422);
|
||||
}
|
||||
// Check for domain conflicts
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($fqdn, $teamId, $uuid);
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId, $uuid);
|
||||
if (isset($result['error'])) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
|
|
@ -2460,39 +2633,63 @@ public function update_by_uuid(Request $request)
|
|||
}
|
||||
|
||||
$dockerComposeDomains = collect($request->docker_compose_domains);
|
||||
$domainErrors = [];
|
||||
|
||||
foreach ($dockerComposeDomains as $item) {
|
||||
// Collect all URLs from all docker_compose_domains items
|
||||
$urls = $dockerComposeDomains->flatMap(function ($item) {
|
||||
$domainValue = data_get($item, 'domain');
|
||||
if (filled($domainValue)) {
|
||||
$urls = str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim();
|
||||
str($urls)->explode(',')->each(function ($url) use (&$domainErrors) {
|
||||
$url = trim($url);
|
||||
if (empty($url)) {
|
||||
return;
|
||||
if (blank($domainValue)) {
|
||||
return [];
|
||||
}
|
||||
if (! filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$domainErrors[] = "Invalid URL: {$url}";
|
||||
|
||||
return;
|
||||
return str($domainValue)->replaceStart(',', '')->replaceEnd(',', '')->trim()->explode(',')->map(fn ($url) => trim($url))->filter();
|
||||
});
|
||||
|
||||
$errors = [];
|
||||
$urls = $urls->map(function ($url) use (&$errors) {
|
||||
if (! filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$errors[] = "Invalid URL: {$url}";
|
||||
|
||||
return $url;
|
||||
}
|
||||
$scheme = parse_url($url, PHP_URL_SCHEME) ?? '';
|
||||
if (! in_array(strtolower($scheme), ['http', 'https'])) {
|
||||
$domainErrors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
|
||||
}
|
||||
});
|
||||
}
|
||||
$errors[] = "Invalid URL scheme: {$scheme} for URL: {$url}. Only http and https are supported.";
|
||||
}
|
||||
|
||||
if (! empty($domainErrors)) {
|
||||
return $url;
|
||||
});
|
||||
|
||||
$duplicates = $urls->duplicates()->unique()->values();
|
||||
if ($duplicates->isNotEmpty() && ! $request->boolean('force_domain_override')) {
|
||||
$errors[] = 'The current request contains conflicting URLs: '.implode(', ', $duplicates->toArray()).' Use force_domain_override=true to proceed.';
|
||||
}
|
||||
|
||||
if (count($errors) > 0) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => [
|
||||
'docker_compose_domains' => $domainErrors,
|
||||
],
|
||||
'errors' => ['docker_compose_domains' => $errors],
|
||||
], 422);
|
||||
}
|
||||
|
||||
// Check for domain conflicts
|
||||
if ($urls->isNotEmpty()) {
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId, $request->uuid);
|
||||
if (isset($result['error'])) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => ['docker_compose_domains' => $result['error']],
|
||||
], 422);
|
||||
}
|
||||
|
||||
if ($result['hasConflicts'] && ! $request->boolean('force_domain_override')) {
|
||||
return response()->json([
|
||||
'message' => 'Domain conflicts detected. Use force_domain_override=true to proceed.',
|
||||
'conflicts' => $result['conflicts'],
|
||||
'warning' => 'Using the same domain for multiple resources can cause routing conflicts and unpredictable behavior.',
|
||||
], 409);
|
||||
}
|
||||
}
|
||||
|
||||
$yaml = Yaml::parse($application->docker_compose_raw);
|
||||
$services = data_get($yaml, 'services', []);
|
||||
$dockerComposeDomains->each(function ($domain) use ($services, $dockerComposeDomainsJson) {
|
||||
|
|
@ -2505,6 +2702,9 @@ public function update_by_uuid(Request $request)
|
|||
}
|
||||
$instantDeploy = $request->instant_deploy;
|
||||
$isStatic = $request->is_static;
|
||||
$isSpa = $request->is_spa;
|
||||
$isAutoDeployEnabled = $request->is_auto_deploy_enabled;
|
||||
$isForceHttpsEnabled = $request->is_force_https_enabled;
|
||||
$connectToDockerNetwork = $request->connect_to_docker_network;
|
||||
$useBuildServer = $request->use_build_server;
|
||||
$isContainerLabelEscapeEnabled = $request->boolean('is_container_label_escape_enabled');
|
||||
|
|
@ -2519,6 +2719,21 @@ public function update_by_uuid(Request $request)
|
|||
$application->settings->save();
|
||||
}
|
||||
|
||||
if (isset($isSpa)) {
|
||||
$application->settings->is_spa = $isSpa;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
if (isset($isAutoDeployEnabled)) {
|
||||
$application->settings->is_auto_deploy_enabled = $isAutoDeployEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
if (isset($isForceHttpsEnabled)) {
|
||||
$application->settings->is_force_https_enabled = $isForceHttpsEnabled;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
if (isset($connectToDockerNetwork)) {
|
||||
$application->settings->connect_to_docker_network = $connectToDockerNetwork;
|
||||
$application->settings->save();
|
||||
|
|
@ -3626,17 +3841,29 @@ private function validateDataApplications(Request $request, Server $server)
|
|||
}
|
||||
if ($request->has('domains') && $server->isProxyShouldRun()) {
|
||||
$uuid = $request->uuid;
|
||||
$fqdn = $request->domains;
|
||||
$fqdn = str($fqdn)->replaceEnd(',', '')->trim();
|
||||
$fqdn = str($fqdn)->replaceStart(',', '')->trim();
|
||||
$urls = $request->domains;
|
||||
$urls = str($urls)->replaceEnd(',', '')->trim();
|
||||
$urls = str($urls)->replaceStart(',', '')->trim();
|
||||
$errors = [];
|
||||
$fqdn = str($fqdn)->trim()->explode(',')->map(function ($domain) use (&$errors) {
|
||||
$domain = trim($domain);
|
||||
if (filter_var($domain, FILTER_VALIDATE_URL) === false) {
|
||||
$errors[] = 'Invalid domain: '.$domain;
|
||||
$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;
|
||||
}
|
||||
|
||||
return str($domain)->lower();
|
||||
if (! filter_var($url, FILTER_VALIDATE_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();
|
||||
});
|
||||
if (count($errors) > 0) {
|
||||
return response()->json([
|
||||
|
|
@ -3645,7 +3872,7 @@ private function validateDataApplications(Request $request, Server $server)
|
|||
], 422);
|
||||
}
|
||||
// Check for domain conflicts
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($fqdn, $teamId, $uuid);
|
||||
$result = checkIfDomainIsAlreadyUsedViaAPI($urls, $teamId, $uuid);
|
||||
if (isset($result['error'])) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Info(title: 'Coolify', version: '0.1')]
|
||||
#[OA\Server(url: 'https://app.coolify.io/api/v1', description: 'Coolify Cloud API. Change the host to your own instance if you are self-hosting.')]
|
||||
// MapleDeploy branding: API documentation
|
||||
#[OA\Info(title: 'MapleDeploy', version: '0.1')]
|
||||
#[OA\Server(url: '/api/v1', description: 'MapleDeploy API. Powered by Coolify.')]
|
||||
#[OA\SecurityScheme(
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public function handle()
|
|||
match ($this->status) {
|
||||
ProcessStatus::QUEUED => $this->body = "The preview deployment for **{$serviceName}** is queued. ⏳\n\n",
|
||||
ProcessStatus::IN_PROGRESS => $this->body = "The preview deployment for **{$serviceName}** is in progress. 🟡\n\n",
|
||||
ProcessStatus::FINISHED => $this->body = "The preview deployment for **{$serviceName}** is ready. 🟢\n\n".($this->preview->fqdn ? "[Open Preview]({$this->preview->fqdn}) | " : ''),
|
||||
ProcessStatus::FINISHED => $this->body = "The preview deployment for **{$serviceName}** is ready. 🟢\n\n".$this->getPreviewLinks(),
|
||||
ProcessStatus::ERROR => $this->body = "The preview deployment for **{$serviceName}** failed. 🔴\n\n",
|
||||
ProcessStatus::KILLED => $this->body = "The preview deployment for **{$serviceName}** was killed. ⚫\n\n",
|
||||
ProcessStatus::CANCELLED => $this->body = "The preview deployment for **{$serviceName}** was cancelled. 🚫\n\n",
|
||||
|
|
@ -91,4 +91,27 @@ private function delete_comment()
|
|||
{
|
||||
githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->preview->pull_request_issue_comment_id}", method: 'delete');
|
||||
}
|
||||
|
||||
private function getPreviewLinks(): string
|
||||
{
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$dockerComposeDomains = json_decode($this->preview->docker_compose_domains, true) ?? [];
|
||||
$links = [];
|
||||
|
||||
foreach ($dockerComposeDomains as $serviceName => $config) {
|
||||
$domain = data_get($config, 'domain');
|
||||
if (! empty($domain)) {
|
||||
$firstDomain = str($domain)->explode(',')->first();
|
||||
$firstDomain = trim($firstDomain);
|
||||
if (! empty($firstDomain)) {
|
||||
$links[] = "[Open {$serviceName}]({$firstDomain})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ! empty($links) ? implode(' | ', $links).' | ' : '';
|
||||
}
|
||||
|
||||
return $this->preview->fqdn ? "[Open Preview]({$this->preview->fqdn}) | " : '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,36 @@ public function __construct(
|
|||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
if ($this->isSlackWebhook()) {
|
||||
$this->sendToSlack();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* This works with Mattermost and as a fallback also with Slack, the notifications just look slightly different and advanced formatting for slack is not supported with Mattermost.
|
||||
*
|
||||
* @see https://github.com/coollabsio/coolify/pull/6139#issuecomment-3756777708
|
||||
*/
|
||||
$this->sendToMattermost();
|
||||
}
|
||||
|
||||
private function isSlackWebhook(): bool
|
||||
{
|
||||
$parsedUrl = parse_url($this->webhookUrl);
|
||||
|
||||
if ($parsedUrl === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$scheme = $parsedUrl['scheme'] ?? '';
|
||||
$host = $parsedUrl['host'] ?? '';
|
||||
|
||||
return $scheme === 'https' && $host === 'hooks.slack.com';
|
||||
}
|
||||
|
||||
private function sendToSlack(): void
|
||||
{
|
||||
Http::post($this->webhookUrl, [
|
||||
'text' => $this->message->title,
|
||||
|
|
@ -57,4 +87,24 @@ public function handle(): void
|
|||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo v5 refactor: Extract this into a separate SendMessageToMattermostJob.php triggered via the "mattermost" notification channel type.
|
||||
*/
|
||||
private function sendToMattermost(): void
|
||||
{
|
||||
$username = config('app.name');
|
||||
|
||||
Http::post($this->webhookUrl, [
|
||||
'username' => $username,
|
||||
'attachments' => [
|
||||
[
|
||||
'title' => $this->message->title,
|
||||
'color' => $this->message->color,
|
||||
'text' => $this->message->description,
|
||||
'footer' => $username,
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,14 +35,12 @@ public function submit()
|
|||
$mail->subject("[HELP]: {$this->subject}");
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
|
||||
// Sending feedback through Cloud API
|
||||
// MapleDeploy branding: feedback sent to MapleDeploy support
|
||||
if (blank($type)) {
|
||||
$url = 'https://app.coolify.io/api/feedback';
|
||||
Http::post($url, [
|
||||
'content' => 'User: `'.auth()->user()?->email.'` with subject: `'.$this->subject.'` has the following problem: `'.$this->description.'`',
|
||||
]);
|
||||
// No external API — log locally when SMTP not configured
|
||||
\Illuminate\Support\Facades\Log::info('Feedback from '.auth()->user()?->email.': '.$this->subject.' — '.$this->description);
|
||||
} else {
|
||||
send_user_an_email($mail, auth()->user()?->email, 'feedback@coollabs.io');
|
||||
send_user_an_email($mail, auth()->user()?->email, 'support@mapledeploy.ca');
|
||||
}
|
||||
$this->dispatch('success', 'Feedback sent.', 'We will get in touch with you as soon as possible.');
|
||||
$this->reset('description', 'subject');
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ class Index extends Component
|
|||
|
||||
public Collection $services;
|
||||
|
||||
public Collection $allProjects;
|
||||
|
||||
public Collection $allEnvironments;
|
||||
|
||||
public array $parameters;
|
||||
|
||||
public function mount()
|
||||
|
|
@ -50,6 +54,33 @@ public function mount()
|
|||
->firstOrFail();
|
||||
|
||||
$this->project = $project;
|
||||
|
||||
// Load projects and environments for breadcrumb navigation (avoids inline queries in view)
|
||||
$this->allProjects = Project::ownedByCurrentTeamCached();
|
||||
$this->allEnvironments = $project->environments()
|
||||
->with([
|
||||
'applications.additional_servers',
|
||||
'applications.destination.server',
|
||||
'services',
|
||||
'services.destination.server',
|
||||
'postgresqls',
|
||||
'postgresqls.destination.server',
|
||||
'redis',
|
||||
'redis.destination.server',
|
||||
'mongodbs',
|
||||
'mongodbs.destination.server',
|
||||
'mysqls',
|
||||
'mysqls.destination.server',
|
||||
'mariadbs',
|
||||
'mariadbs.destination.server',
|
||||
'keydbs',
|
||||
'keydbs.destination.server',
|
||||
'dragonflies',
|
||||
'dragonflies.destination.server',
|
||||
'clickhouses',
|
||||
'clickhouses.destination.server',
|
||||
])->get();
|
||||
|
||||
$this->environment = $environment->loadCount([
|
||||
'applications',
|
||||
'redis',
|
||||
|
|
@ -71,11 +102,13 @@ public function mount()
|
|||
'destination.server.settings',
|
||||
'settings',
|
||||
])->get()->sortBy('name');
|
||||
$this->applications = $this->applications->map(function ($application) {
|
||||
$projectUuid = $this->project->uuid;
|
||||
$environmentUuid = $this->environment->uuid;
|
||||
$this->applications = $this->applications->map(function ($application) use ($projectUuid, $environmentUuid) {
|
||||
$application->hrefLink = route('project.application.configuration', [
|
||||
'project_uuid' => data_get($application, 'environment.project.uuid'),
|
||||
'environment_uuid' => data_get($application, 'environment.uuid'),
|
||||
'application_uuid' => data_get($application, 'uuid'),
|
||||
'project_uuid' => $projectUuid,
|
||||
'environment_uuid' => $environmentUuid,
|
||||
'application_uuid' => $application->uuid,
|
||||
]);
|
||||
|
||||
return $application;
|
||||
|
|
@ -98,11 +131,11 @@ public function mount()
|
|||
'tags',
|
||||
'destination.server.settings',
|
||||
])->get()->sortBy('name');
|
||||
$this->{$property} = $this->{$property}->map(function ($db) {
|
||||
$this->{$property} = $this->{$property}->map(function ($db) use ($projectUuid, $environmentUuid) {
|
||||
$db->hrefLink = route('project.database.configuration', [
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'project_uuid' => $projectUuid,
|
||||
'database_uuid' => $db->uuid,
|
||||
'environment_uuid' => data_get($this->environment, 'uuid'),
|
||||
'environment_uuid' => $environmentUuid,
|
||||
]);
|
||||
|
||||
return $db;
|
||||
|
|
@ -114,11 +147,11 @@ public function mount()
|
|||
'tags',
|
||||
'destination.server.settings',
|
||||
])->get()->sortBy('name');
|
||||
$this->services = $this->services->map(function ($service) {
|
||||
$this->services = $this->services->map(function ($service) use ($projectUuid, $environmentUuid) {
|
||||
$service->hrefLink = route('project.service.configuration', [
|
||||
'project_uuid' => data_get($service, 'environment.project.uuid'),
|
||||
'environment_uuid' => data_get($service, 'environment.uuid'),
|
||||
'service_uuid' => data_get($service, 'uuid'),
|
||||
'project_uuid' => $projectUuid,
|
||||
'environment_uuid' => $environmentUuid,
|
||||
'service_uuid' => $service->uuid,
|
||||
]);
|
||||
|
||||
return $service;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Once;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
class InstanceSettings extends Model
|
||||
|
|
@ -35,6 +36,9 @@ class InstanceSettings extends Model
|
|||
protected static function booted(): void
|
||||
{
|
||||
static::updated(function ($settings) {
|
||||
// Clear once() cache so subsequent calls get fresh data
|
||||
Once::flush();
|
||||
|
||||
// Clear trusted hosts cache when FQDN changes
|
||||
if ($settings->wasChanged('fqdn')) {
|
||||
\Cache::forget('instance_settings_fqdn_host');
|
||||
|
|
@ -82,7 +86,7 @@ public function autoUpdateFrequency(): Attribute
|
|||
|
||||
public static function get()
|
||||
{
|
||||
return InstanceSettings::findOrFail(0);
|
||||
return once(fn () => InstanceSettings::findOrFail(0));
|
||||
}
|
||||
|
||||
// public function getRecipients($notification)
|
||||
|
|
|
|||
|
|
@ -108,6 +108,12 @@ class Server extends BaseModel
|
|||
|
||||
public static $batch_counter = 0;
|
||||
|
||||
/**
|
||||
* Identity map cache for request-scoped Server lookups.
|
||||
* Prevents N+1 queries when the same Server is accessed multiple times.
|
||||
*/
|
||||
private static ?array $identityMapCache = null;
|
||||
|
||||
protected $appends = ['is_coolify_host'];
|
||||
|
||||
protected static function booted()
|
||||
|
|
@ -186,6 +192,40 @@ protected static function booted()
|
|||
$server->settings()->delete();
|
||||
$server->sslCertificates()->delete();
|
||||
});
|
||||
|
||||
static::updated(function () {
|
||||
static::flushIdentityMap();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a Server by ID using the identity map cache.
|
||||
* This prevents N+1 queries when the same Server is accessed multiple times.
|
||||
*/
|
||||
public static function findCached($id): ?static
|
||||
{
|
||||
if ($id === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (static::$identityMapCache === null) {
|
||||
static::$identityMapCache = [];
|
||||
}
|
||||
|
||||
if (! isset(static::$identityMapCache[$id])) {
|
||||
static::$identityMapCache[$id] = static::query()->find($id);
|
||||
}
|
||||
|
||||
return static::$identityMapCache[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the identity map cache.
|
||||
* Called automatically on update, and should be called in tests.
|
||||
*/
|
||||
public static function flushIdentityMap(): void
|
||||
{
|
||||
static::$identityMapCache = null;
|
||||
}
|
||||
|
||||
protected $casts = [
|
||||
|
|
|
|||
|
|
@ -73,6 +73,28 @@ public function server()
|
|||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server attribute using identity map caching.
|
||||
* This intercepts lazy-loading to use cached Server lookups.
|
||||
*/
|
||||
public function getServerAttribute(): ?Server
|
||||
{
|
||||
// Use eager loaded data if available
|
||||
if ($this->relationLoaded('server')) {
|
||||
return $this->getRelation('server');
|
||||
}
|
||||
|
||||
// Use identity map for lazy loading
|
||||
$server = Server::findCached($this->server_id);
|
||||
|
||||
// Cache in relation for future access on this instance
|
||||
if ($server) {
|
||||
$this->setRelation('server', $server);
|
||||
}
|
||||
|
||||
return $server;
|
||||
}
|
||||
|
||||
public function services()
|
||||
{
|
||||
return $this->morphMany(Service::class, 'destination');
|
||||
|
|
|
|||
|
|
@ -56,6 +56,28 @@ public function server()
|
|||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server attribute using identity map caching.
|
||||
* This intercepts lazy-loading to use cached Server lookups.
|
||||
*/
|
||||
public function getServerAttribute(): ?Server
|
||||
{
|
||||
// Use eager loaded data if available
|
||||
if ($this->relationLoaded('server')) {
|
||||
return $this->getRelation('server');
|
||||
}
|
||||
|
||||
// Use identity map for lazy loading
|
||||
$server = Server::findCached($this->server_id);
|
||||
|
||||
// Cache in relation for future access on this instance
|
||||
if ($server) {
|
||||
$this->setRelation('server', $server);
|
||||
}
|
||||
|
||||
return $server;
|
||||
}
|
||||
|
||||
public function services()
|
||||
{
|
||||
return $this->morphMany(Service::class, 'destination');
|
||||
|
|
|
|||
|
|
@ -86,8 +86,11 @@ function sharedDataApplications()
|
|||
'git_branch' => 'string',
|
||||
'build_pack' => Rule::enum(BuildPackTypes::class),
|
||||
'is_static' => 'boolean',
|
||||
'is_spa' => 'boolean',
|
||||
'is_auto_deploy_enabled' => 'boolean',
|
||||
'is_force_https_enabled' => 'boolean',
|
||||
'static_image' => Rule::enum(StaticImageTypes::class),
|
||||
'domains' => 'string',
|
||||
'domains' => 'string|nullable',
|
||||
'redirect' => Rule::enum(RedirectTypes::class),
|
||||
'git_commit_sha' => 'string',
|
||||
'docker_registry_image_name' => 'string|nullable',
|
||||
|
|
@ -129,6 +132,7 @@ function sharedDataApplications()
|
|||
'manual_webhook_secret_gitlab' => 'string|nullable',
|
||||
'manual_webhook_secret_bitbucket' => 'string|nullable',
|
||||
'manual_webhook_secret_gitea' => 'string|nullable',
|
||||
'dockerfile_location' => 'string|nullable',
|
||||
'docker_compose_location' => 'string',
|
||||
'docker_compose' => 'string|nullable',
|
||||
'docker_compose_domains' => 'array|nullable',
|
||||
|
|
@ -177,6 +181,10 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
|
|||
$request->offsetUnset('private_key_uuid');
|
||||
$request->offsetUnset('use_build_server');
|
||||
$request->offsetUnset('is_static');
|
||||
$request->offsetUnset('is_spa');
|
||||
$request->offsetUnset('is_auto_deploy_enabled');
|
||||
$request->offsetUnset('is_force_https_enabled');
|
||||
$request->offsetUnset('connect_to_docker_network');
|
||||
$request->offsetUnset('force_domain_override');
|
||||
$request->offsetUnset('autogenerate_domain');
|
||||
$request->offsetUnset('is_container_label_escape_enabled');
|
||||
|
|
|
|||
|
|
@ -158,8 +158,7 @@ function checkIfDomainIsAlreadyUsedViaAPI(Collection|array $domains, ?string $te
|
|||
return str($domain);
|
||||
});
|
||||
|
||||
// Check applications within the same team
|
||||
$applications = Application::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid', 'name', 'id']);
|
||||
$applications = Application::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid', 'name', 'id', 'docker_compose_domains', 'build_pack']);
|
||||
$serviceApplications = ServiceApplication::ownedByCurrentTeamAPI($teamId)->with('service:id,name')->get(['fqdn', 'uuid', 'id', 'service_id']);
|
||||
|
||||
if ($uuid) {
|
||||
|
|
@ -168,9 +167,7 @@ function checkIfDomainIsAlreadyUsedViaAPI(Collection|array $domains, ?string $te
|
|||
}
|
||||
|
||||
foreach ($applications as $app) {
|
||||
if (is_null($app->fqdn)) {
|
||||
continue;
|
||||
}
|
||||
if (! is_null($app->fqdn)) {
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== '');
|
||||
foreach ($list_of_domains as $domain) {
|
||||
if (str($domain)->endsWith('/')) {
|
||||
|
|
@ -189,6 +186,36 @@ function checkIfDomainIsAlreadyUsedViaAPI(Collection|array $domains, ?string $te
|
|||
}
|
||||
}
|
||||
|
||||
if ($app->build_pack === 'dockercompose' && ! empty($app->docker_compose_domains)) {
|
||||
$dockerComposeDomains = json_decode($app->docker_compose_domains, true);
|
||||
if (is_array($dockerComposeDomains)) {
|
||||
foreach ($dockerComposeDomains as $serviceName => $domainConfig) {
|
||||
$domainValue = data_get($domainConfig, 'domain');
|
||||
if (empty($domainValue)) {
|
||||
continue;
|
||||
}
|
||||
$list_of_domains = collect(explode(',', $domainValue))->filter(fn ($fqdn) => $fqdn !== '');
|
||||
foreach ($list_of_domains as $domain) {
|
||||
if (str($domain)->endsWith('/')) {
|
||||
$domain = str($domain)->beforeLast('/');
|
||||
}
|
||||
$naked_domain = str($domain)->value();
|
||||
if ($domains->contains($naked_domain)) {
|
||||
$conflicts[] = [
|
||||
'domain' => $naked_domain,
|
||||
'resource_name' => $app->name,
|
||||
'resource_uuid' => $app->uuid,
|
||||
'resource_type' => 'application',
|
||||
'service_name' => $serviceName,
|
||||
'message' => "Domain $naked_domain is already in use by application '{$app->name}' (service: {$serviceName})",
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($serviceApplications as $app) {
|
||||
if (str($app->fqdn)->isEmpty()) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Coolify'),
|
||||
'name' => env('APP_NAME', 'MapleDeploy'), // MapleDeploy branding
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
// MapleDeploy branding: registry pointed to Forgejo, auto-update disabled by default
|
||||
'coolify' => [
|
||||
'version' => '4.0.0-beta.461',
|
||||
'version' => '4.0.0-beta.462',
|
||||
'helper_version' => '1.0.12',
|
||||
'realtime_version' => '1.0.10',
|
||||
'self_hosted' => env('SELF_HOSTED', true),
|
||||
'autoupdate' => env('AUTOUPDATE'),
|
||||
'autoupdate' => env('AUTOUPDATE', false),
|
||||
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
|
||||
'registry_url' => env('REGISTRY_URL', 'ghcr.io'),
|
||||
'helper_image' => env('HELPER_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-helper'),
|
||||
'realtime_image' => env('REALTIME_IMAGE', env('REGISTRY_URL', 'ghcr.io').'/coollabsio/coolify-realtime'),
|
||||
'registry_url' => env('REGISTRY_URL', 'forgejo.mapledeploy.ca'),
|
||||
'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper'),
|
||||
'realtime_image' => env('REALTIME_IMAGE', 'ghcr.io/coollabsio/coolify-realtime'),
|
||||
'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false),
|
||||
'cdn_url' => env('CDN_URL', 'https://cdn.coollabs.io'),
|
||||
'versions_url' => env('VERSIONS_URL', env('CDN_URL', 'https://cdn.coollabs.io').'/coolify/versions.json'),
|
||||
|
|
@ -19,8 +20,8 @@
|
|||
],
|
||||
|
||||
'urls' => [
|
||||
'docs' => 'https://coolify.io/docs',
|
||||
'contact' => 'https://coolify.io/docs/contact',
|
||||
'docs' => 'https://mapledeploy.ca/docs',
|
||||
'contact' => 'https://mapledeploy.ca/contact',
|
||||
],
|
||||
|
||||
'services' => [
|
||||
|
|
@ -85,12 +86,13 @@
|
|||
'verification_code_expiry_minutes' => 10,
|
||||
],
|
||||
|
||||
// MapleDeploy branding: telemetry disabled
|
||||
'sentry' => [
|
||||
'sentry_dsn' => env('SENTRY_DSN'),
|
||||
'sentry_dsn' => null,
|
||||
],
|
||||
|
||||
'webhooks' => [
|
||||
'feedback_discord_webhook' => env('FEEDBACK_DISCORD_WEBHOOK'),
|
||||
'feedback_discord_webhook' => null,
|
||||
'dev_webhook' => env('SERVEO_URL'),
|
||||
],
|
||||
|
||||
|
|
|
|||
84
openapi.json
84
openapi.json
|
|
@ -135,7 +135,7 @@
|
|||
},
|
||||
"domains": {
|
||||
"type": "string",
|
||||
"description": "The application domains."
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"git_commit_sha": {
|
||||
"type": "string",
|
||||
|
|
@ -153,6 +153,18 @@
|
|||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application is static."
|
||||
},
|
||||
"is_spa": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true."
|
||||
},
|
||||
"is_auto_deploy_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if auto-deploy is enabled on git push. Defaults to true."
|
||||
},
|
||||
"is_force_https_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
|
||||
},
|
||||
"static_image": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
@ -322,6 +334,10 @@
|
|||
"type": "string",
|
||||
"description": "The Dockerfile content."
|
||||
},
|
||||
"dockerfile_location": {
|
||||
"type": "string",
|
||||
"description": "The Dockerfile location in the repository."
|
||||
},
|
||||
"docker_compose_location": {
|
||||
"type": "string",
|
||||
"description": "The Docker Compose location."
|
||||
|
|
@ -564,7 +580,7 @@
|
|||
},
|
||||
"domains": {
|
||||
"type": "string",
|
||||
"description": "The application domains."
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"git_commit_sha": {
|
||||
"type": "string",
|
||||
|
|
@ -582,6 +598,18 @@
|
|||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application is static."
|
||||
},
|
||||
"is_spa": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true."
|
||||
},
|
||||
"is_auto_deploy_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if auto-deploy is enabled on git push. Defaults to true."
|
||||
},
|
||||
"is_force_https_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
|
||||
},
|
||||
"static_image": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
@ -751,6 +779,10 @@
|
|||
"type": "string",
|
||||
"description": "The Dockerfile content."
|
||||
},
|
||||
"dockerfile_location": {
|
||||
"type": "string",
|
||||
"description": "The Dockerfile location in the repository"
|
||||
},
|
||||
"docker_compose_location": {
|
||||
"type": "string",
|
||||
"description": "The Docker Compose location."
|
||||
|
|
@ -993,7 +1025,7 @@
|
|||
},
|
||||
"domains": {
|
||||
"type": "string",
|
||||
"description": "The application domains."
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"git_commit_sha": {
|
||||
"type": "string",
|
||||
|
|
@ -1011,6 +1043,18 @@
|
|||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application is static."
|
||||
},
|
||||
"is_spa": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true."
|
||||
},
|
||||
"is_auto_deploy_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if auto-deploy is enabled on git push. Defaults to true."
|
||||
},
|
||||
"is_force_https_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
|
||||
},
|
||||
"static_image": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
@ -1180,6 +1224,10 @@
|
|||
"type": "string",
|
||||
"description": "The Dockerfile content."
|
||||
},
|
||||
"dockerfile_location": {
|
||||
"type": "string",
|
||||
"description": "The Dockerfile location in the repository."
|
||||
},
|
||||
"docker_compose_location": {
|
||||
"type": "string",
|
||||
"description": "The Docker Compose location."
|
||||
|
|
@ -1410,7 +1458,7 @@
|
|||
},
|
||||
"domains": {
|
||||
"type": "string",
|
||||
"description": "The application domains."
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"docker_registry_image_name": {
|
||||
"type": "string",
|
||||
|
|
@ -1562,6 +1610,10 @@
|
|||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application should be deployed instantly."
|
||||
},
|
||||
"is_force_https_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
|
||||
},
|
||||
"use_build_server": {
|
||||
"type": "boolean",
|
||||
"nullable": true,
|
||||
|
|
@ -1754,7 +1806,7 @@
|
|||
},
|
||||
"domains": {
|
||||
"type": "string",
|
||||
"description": "The application domains."
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"ports_mappings": {
|
||||
"type": "string",
|
||||
|
|
@ -1894,6 +1946,10 @@
|
|||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application should be deployed instantly."
|
||||
},
|
||||
"is_force_https_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
|
||||
},
|
||||
"use_build_server": {
|
||||
"type": "boolean",
|
||||
"nullable": true,
|
||||
|
|
@ -2402,7 +2458,7 @@
|
|||
},
|
||||
"domains": {
|
||||
"type": "string",
|
||||
"description": "The application domains."
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"git_commit_sha": {
|
||||
"type": "string",
|
||||
|
|
@ -2420,6 +2476,18 @@
|
|||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application is static."
|
||||
},
|
||||
"is_spa": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true."
|
||||
},
|
||||
"is_auto_deploy_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if auto-deploy is enabled on git push. Defaults to true."
|
||||
},
|
||||
"is_force_https_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
|
||||
},
|
||||
"install_command": {
|
||||
"type": "string",
|
||||
"description": "The install command."
|
||||
|
|
@ -2582,6 +2650,10 @@
|
|||
"type": "string",
|
||||
"description": "The Dockerfile content."
|
||||
},
|
||||
"dockerfile_location": {
|
||||
"type": "string",
|
||||
"description": "The Dockerfile location in the repository."
|
||||
},
|
||||
"docker_compose_location": {
|
||||
"type": "string",
|
||||
"description": "The Docker Compose location."
|
||||
|
|
|
|||
66
openapi.yaml
66
openapi.yaml
|
|
@ -97,7 +97,7 @@ paths:
|
|||
description: 'The application description.'
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application domains.'
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
git_commit_sha:
|
||||
type: string
|
||||
description: 'The git commit SHA.'
|
||||
|
|
@ -110,6 +110,15 @@ paths:
|
|||
is_static:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application is static.'
|
||||
is_spa:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'
|
||||
is_auto_deploy_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'
|
||||
is_force_https_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
|
||||
static_image:
|
||||
type: string
|
||||
enum: ['nginx:alpine']
|
||||
|
|
@ -234,6 +243,9 @@ paths:
|
|||
dockerfile:
|
||||
type: string
|
||||
description: 'The Dockerfile content.'
|
||||
dockerfile_location:
|
||||
type: string
|
||||
description: 'The Dockerfile location in the repository.'
|
||||
docker_compose_location:
|
||||
type: string
|
||||
description: 'The Docker Compose location.'
|
||||
|
|
@ -369,7 +381,7 @@ paths:
|
|||
description: 'The application description.'
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application domains.'
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
git_commit_sha:
|
||||
type: string
|
||||
description: 'The git commit SHA.'
|
||||
|
|
@ -382,6 +394,15 @@ paths:
|
|||
is_static:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application is static.'
|
||||
is_spa:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'
|
||||
is_auto_deploy_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'
|
||||
is_force_https_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
|
||||
static_image:
|
||||
type: string
|
||||
enum: ['nginx:alpine']
|
||||
|
|
@ -506,6 +527,9 @@ paths:
|
|||
dockerfile:
|
||||
type: string
|
||||
description: 'The Dockerfile content.'
|
||||
dockerfile_location:
|
||||
type: string
|
||||
description: 'The Dockerfile location in the repository'
|
||||
docker_compose_location:
|
||||
type: string
|
||||
description: 'The Docker Compose location.'
|
||||
|
|
@ -641,7 +665,7 @@ paths:
|
|||
description: 'The application description.'
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application domains.'
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
git_commit_sha:
|
||||
type: string
|
||||
description: 'The git commit SHA.'
|
||||
|
|
@ -654,6 +678,15 @@ paths:
|
|||
is_static:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application is static.'
|
||||
is_spa:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'
|
||||
is_auto_deploy_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'
|
||||
is_force_https_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
|
||||
static_image:
|
||||
type: string
|
||||
enum: ['nginx:alpine']
|
||||
|
|
@ -778,6 +811,9 @@ paths:
|
|||
dockerfile:
|
||||
type: string
|
||||
description: 'The Dockerfile content.'
|
||||
dockerfile_location:
|
||||
type: string
|
||||
description: 'The Dockerfile location in the repository.'
|
||||
docker_compose_location:
|
||||
type: string
|
||||
description: 'The Docker Compose location.'
|
||||
|
|
@ -903,7 +939,7 @@ paths:
|
|||
description: 'The application description.'
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application domains.'
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
docker_registry_image_name:
|
||||
type: string
|
||||
description: 'The docker registry image name.'
|
||||
|
|
@ -1015,6 +1051,9 @@ paths:
|
|||
instant_deploy:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application should be deployed instantly.'
|
||||
is_force_https_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
|
||||
use_build_server:
|
||||
type: boolean
|
||||
nullable: true
|
||||
|
|
@ -1124,7 +1163,7 @@ paths:
|
|||
description: 'The application description.'
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application domains.'
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
ports_mappings:
|
||||
type: string
|
||||
description: 'The ports mappings.'
|
||||
|
|
@ -1227,6 +1266,9 @@ paths:
|
|||
instant_deploy:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application should be deployed instantly.'
|
||||
is_force_https_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
|
||||
use_build_server:
|
||||
type: boolean
|
||||
nullable: true
|
||||
|
|
@ -1524,7 +1566,7 @@ paths:
|
|||
description: 'The application description.'
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application domains.'
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
git_commit_sha:
|
||||
type: string
|
||||
description: 'The git commit SHA.'
|
||||
|
|
@ -1537,6 +1579,15 @@ paths:
|
|||
is_static:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application is static.'
|
||||
is_spa:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application is a single-page application (SPA). Only relevant when is_static is true.'
|
||||
is_auto_deploy_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if auto-deploy is enabled on git push. Defaults to true.'
|
||||
is_force_https_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
|
||||
install_command:
|
||||
type: string
|
||||
description: 'The install command.'
|
||||
|
|
@ -1657,6 +1708,9 @@ paths:
|
|||
dockerfile:
|
||||
type: string
|
||||
description: 'The Dockerfile content.'
|
||||
dockerfile_location:
|
||||
type: string
|
||||
description: 'The Dockerfile location in the repository.'
|
||||
docker_compose_location:
|
||||
type: string
|
||||
description: 'The Docker Compose location.'
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.461"
|
||||
"version": "4.0.0-beta.462"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.462"
|
||||
"version": "4.0.0-beta.463"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.12"
|
||||
|
|
|
|||
BIN
public/mapledeploy-favicon.ico
Normal file
BIN
public/mapledeploy-favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -13,10 +13,12 @@
|
|||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
/* MapleDeploy branding: Canadian red accent, stone greys */
|
||||
@theme {
|
||||
--font-sans: Inter, sans-serif;
|
||||
--font-display: 'Overlock', sans-serif;
|
||||
|
||||
--color-base: #101010;
|
||||
--color-base: #0c0a09;
|
||||
--color-warning: #fcd452;
|
||||
--color-warning-50: #fefce8;
|
||||
--color-warning-100: #fef9c3;
|
||||
|
|
@ -30,16 +32,16 @@ @theme {
|
|||
--color-warning-900: #713f12;
|
||||
--color-success: #22C55E;
|
||||
--color-error: #dc2626;
|
||||
--color-coollabs-50: #f5f0ff;
|
||||
--color-coollabs: #6b16ed;
|
||||
--color-coollabs-100: #7317ff;
|
||||
--color-coollabs-200: #5a12c7;
|
||||
--color-coollabs-300: #4a0fa3;
|
||||
--color-coolgray-100: #181818;
|
||||
--color-coolgray-200: #202020;
|
||||
--color-coolgray-300: #242424;
|
||||
--color-coolgray-400: #282828;
|
||||
--color-coolgray-500: #323232;
|
||||
--color-coollabs-50: #fef3f2;
|
||||
--color-coollabs: #d52b1e;
|
||||
--color-coollabs-100: #f34d40;
|
||||
--color-coollabs-200: #bc2519;
|
||||
--color-coollabs-300: #9c2118;
|
||||
--color-coolgray-100: #1c1917;
|
||||
--color-coolgray-200: #292524;
|
||||
--color-coolgray-300: #44403c;
|
||||
--color-coolgray-400: #57534e;
|
||||
--color-coolgray-500: #78716c;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -70,3 +70,12 @@ @font-face {
|
|||
src: url('../fonts/inter-v13-cyrillic_cyrillic-ext_greek_greek-ext_latin_latin-ext_vietnamese-regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
/* MapleDeploy branding: Overlock for headings */
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Overlock';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: url('../fonts/overlock-v19-latin-900.woff2') format('woff2'),
|
||||
url('../fonts/overlock-v19-latin-900.ttf') format('truetype');
|
||||
}
|
||||
|
|
|
|||
BIN
resources/fonts/overlock-v19-latin-900.ttf
Normal file
BIN
resources/fonts/overlock-v19-latin-900.ttf
Normal file
Binary file not shown.
BIN
resources/fonts/overlock-v19-latin-900.woff2
Normal file
BIN
resources/fonts/overlock-v19-latin-900.woff2
Normal file
Binary file not shown.
|
|
@ -3,9 +3,10 @@
|
|||
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<div class="text-center space-y-2">
|
||||
<h1 class="!text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
|
||||
Coolify
|
||||
</h1>
|
||||
<div class="flex justify-center">
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80" alt="MapleDeploy" class="h-12 dark:hidden" />
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80&dark=true" alt="MapleDeploy" class="hidden h-12 dark:block" />
|
||||
</div>
|
||||
<p class="text-lg dark:text-neutral-400">
|
||||
Confirm Your Password
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<div class="text-center space-y-2">
|
||||
<h1 class="!text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
|
||||
Coolify
|
||||
</h1>
|
||||
<div class="flex justify-center">
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80" alt="MapleDeploy" class="h-12 dark:hidden" />
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80&dark=true" alt="MapleDeploy" class="hidden h-12 dark:block" />
|
||||
</div>
|
||||
<p class="text-lg dark:text-neutral-400">
|
||||
{{ __('auth.forgot_password_heading') }}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<div class="text-center space-y-2">
|
||||
<h1 class="!text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
|
||||
Coolify
|
||||
</h1>
|
||||
<div class="flex justify-center">
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80" alt="MapleDeploy" class="h-12 dark:hidden" />
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80&dark=true" alt="MapleDeploy" class="hidden h-12 dark:block" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ function getOldOrLocal($key, $localValue)
|
|||
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<div class="text-center space-y-2">
|
||||
<h1 class="!text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
|
||||
Coolify
|
||||
</h1>
|
||||
<div class="flex justify-center">
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80" alt="MapleDeploy" class="h-12 dark:hidden" />
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80&dark=true" alt="MapleDeploy" class="hidden h-12 dark:block" />
|
||||
</div>
|
||||
<p class="text-lg dark:text-neutral-400">
|
||||
Create your account
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<div class="text-center space-y-2">
|
||||
<h1 class="!text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
|
||||
Coolify
|
||||
</h1>
|
||||
<div class="flex justify-center">
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80" alt="MapleDeploy" class="h-12 dark:hidden" />
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80&dark=true" alt="MapleDeploy" class="hidden h-12 dark:block" />
|
||||
</div>
|
||||
<p class="text-lg dark:text-neutral-400">
|
||||
{{ __('auth.reset_password') }}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -47,9 +47,10 @@
|
|||
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||
<div class="w-full max-w-md space-y-8">
|
||||
<div class="text-center space-y-2">
|
||||
<h1 class="!text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
|
||||
Coolify
|
||||
</h1>
|
||||
<div class="flex justify-center">
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80" alt="MapleDeploy" class="h-12 dark:hidden" />
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80&dark=true" alt="MapleDeploy" class="hidden h-12 dark:block" />
|
||||
</div>
|
||||
<p class="text-lg dark:text-neutral-400">
|
||||
Two-Factor Authentication
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
{{ Illuminate\Mail\Markdown::parse('---') }}
|
||||
|
||||
Thank you,<br>
|
||||
{{ config('app.name') ?? 'Coolify' }}
|
||||
{{ config('app.name') ?? 'MapleDeploy' }}
|
||||
|
||||
{{ Illuminate\Mail\Markdown::parse('[Contact Support](https://coolify.io/docs/contact)') }}
|
||||
{{-- MapleDeploy branding: support link --}}
|
||||
{{ Illuminate\Mail\Markdown::parse('[Contact Support](https://mapledeploy.ca/contact)') }}
|
||||
|
|
|
|||
|
|
@ -78,9 +78,13 @@
|
|||
}
|
||||
}">
|
||||
<div class="flex lg:pt-6 pt-4 pb-4 pl-2">
|
||||
{{-- MapleDeploy branding --}}
|
||||
<div class="flex flex-col w-full">
|
||||
<a href="/" {{ wireNavigate() }} class="text-2xl font-bold tracking-wide dark:text-white hover:opacity-80 transition-opacity">Coolify</a>
|
||||
<x-version />
|
||||
<a href="/" {{ wireNavigate() }} class="hover:opacity-80 transition-opacity">
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=40" alt="MapleDeploy" class="h-6 dark:hidden" />
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=40&dark=true" alt="MapleDeploy" class="hidden h-6 dark:block" />
|
||||
</a>
|
||||
<span class="text-xs opacity-75 dark:text-neutral-400">Powered by Coolify</span>
|
||||
</div>
|
||||
<div>
|
||||
<!-- Search button that triggers global search modal -->
|
||||
|
|
@ -291,20 +295,7 @@ class="{{ request()->is('team*') ? 'menu-item-active menu-item' : 'menu-item' }}
|
|||
<span class="menu-item-label">Teams</span>
|
||||
</a>
|
||||
</li>
|
||||
@if (isCloud() && auth()->user()->isAdmin())
|
||||
<li>
|
||||
<a title="Subscription" {{ wireNavigate() }}
|
||||
class="{{ request()->is('subscription*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||
href="{{ route('subscription.show') }}">
|
||||
<svg class="menu-item-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 8a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3zm0 2h18M7 15h.01M11 15h2" />
|
||||
</svg>
|
||||
<span class="menu-item-label">Subscription</span>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
{{-- MapleDeploy branding: Cloud subscription menu removed --}}
|
||||
@if (isInstanceAdmin())
|
||||
<li>
|
||||
|
||||
|
|
@ -324,20 +315,7 @@ class="{{ request()->is('settings*') ? 'menu-item-active menu-item' : 'menu-item
|
|||
</li>
|
||||
@endif
|
||||
|
||||
@if (isCloud() || isDev())
|
||||
@if (isInstanceAdmin() || session('impersonating'))
|
||||
<li>
|
||||
<a title="Admin" class="menu-item" href="/admin" {{ wireNavigate() }}>
|
||||
<svg class="text-pink-500 menu-item-icon" viewBox="0 0 256 256"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor"
|
||||
d="M177.62 159.6a52 52 0 0 1-34 34a12.2 12.2 0 0 1-3.6.55a12 12 0 0 1-3.6-23.45a28 28 0 0 0 18.32-18.32a12 12 0 0 1 22.9 7.2ZM220 144a92 92 0 0 1-184 0c0-28.81 11.27-58.18 33.48-87.28a12 12 0 0 1 17.9-1.33l19.69 19.11L127 19.89a12 12 0 0 1 18.94-5.12C168.2 33.25 220 82.85 220 144m-24 0c0-41.71-30.61-78.39-52.52-99.29l-20.21 55.4a12 12 0 0 1-19.63 4.5L80.71 82.36C67 103.38 60 124.06 60 144a68 68 0 0 0 136 0" />
|
||||
</svg>
|
||||
<span class="menu-item-label">Admin</span>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@endif
|
||||
{{-- MapleDeploy branding: Cloud admin menu removed --}}
|
||||
<div class="flex-1"></div>
|
||||
@if (isInstanceAdmin() && !isCloud())
|
||||
@persist('upgrade')
|
||||
|
|
@ -357,24 +335,19 @@ class="{{ request()->is('onboarding*') ? 'menu-item-active menu-item' : 'menu-it
|
|||
Onboarding
|
||||
</a>
|
||||
</li> --}}
|
||||
{{-- MapleDeploy branding: AGPL source code link (license requirement) --}}
|
||||
<li>
|
||||
<a title="Sponsor us" class="menu-item" href="https://coolify.io/sponsorships"
|
||||
<a title="Source code (AGPL-3.0)" class="menu-item" href="https://forgejo.mapledeploy.ca/mapledeploy/coolify"
|
||||
target="_blank">
|
||||
<svg class="text-pink-500 menu-item-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2">
|
||||
<path d="M19.5 12.572L12 20l-7.5-7.428A5 5 0 1 1 12 6.006a5 5 0 1 1 7.5 6.572" />
|
||||
<path
|
||||
d="M12 6L8.707 9.293a1 1 0 0 0 0 1.414l.543.543c.69.69 1.81.69 2.5 0l1-1a3.182 3.182 0 0 1 4.5 0l2.25 2.25m-7 3l2 2M15 13l2 2" />
|
||||
</g>
|
||||
<svg class="menu-item-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="M16 18l6-6-6-6M8 6l-6 6 6 6" />
|
||||
</svg>
|
||||
<span class="menu-item-label">Sponsor us</span>
|
||||
<span class="menu-item-label">Source code</span>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@if (!isSubscribed() && isCloud() && auth()->user()->teams()->get()->count() > 1)
|
||||
<livewire:navbar-delete-team />
|
||||
@endif
|
||||
<li>
|
||||
<x-modal-input title="How can we help?">
|
||||
<x-slot:content>
|
||||
|
|
|
|||
|
|
@ -35,19 +35,21 @@ class="font-bold dark:text-warning">{{ config('constants.limits.trial_period') }
|
|||
</div>
|
||||
</div>
|
||||
<div class="p-4 rounded-sm bg-coolgray-400">
|
||||
{{-- MapleDeploy branding: link to Forgejo source repo --}}
|
||||
<h2 id="tier-hobby" class="flex items-start gap-4 text-4xl font-bold tracking-tight">Unlimited Trial
|
||||
<x-forms.button><a class="font-bold dark:text-white hover:no-underline"
|
||||
href="https://github.com/coollabsio/coolify">Get Started</a></x-forms.button>
|
||||
href="https://forgejo.mapledeploy.ca/mapledeploy/coolify">Get Started</a></x-forms.button>
|
||||
</h2>
|
||||
<p class="mt-4 text-sm leading-6">Start self-hosting <span class="dark:text-warning">without limits</span>
|
||||
with
|
||||
our
|
||||
OSS version. Same features as the paid version, but you have to manage by yourself.</p>
|
||||
the
|
||||
open source version. Same features as the paid version, but you have to manage by yourself.</p>
|
||||
</div>
|
||||
|
||||
<div class="flow-root mt-12">
|
||||
{{-- MapleDeploy branding: link to mapledeploy.ca --}}
|
||||
<div class="pb-10 text-xl text-center">For the detailed list of features, please visit our landing page: <a
|
||||
class="font-bold underline dark:text-white" href="https://coolify.io">coolify.io</a></div>
|
||||
class="font-bold underline dark:text-white" href="https://mapledeploy.ca">mapledeploy.ca</a></div>
|
||||
<div
|
||||
class="grid max-w-sm grid-cols-1 -mt-16 divide-y divide-neutral-200 dark:divide-coolgray-500 isolate gap-y-16 sm:mx-auto lg:-mx-8 lg:mt-0 lg:max-w-none lg:grid-cols-3 lg:divide-x lg:divide-y-0 xl:-mx-4">
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,28 @@
|
|||
'lastDeploymentInfo' => null,
|
||||
'lastDeploymentLink' => null,
|
||||
'resource' => null,
|
||||
'projects' => null,
|
||||
'environments' => null,
|
||||
])
|
||||
@php
|
||||
$projects = auth()->user()->currentTeam()->projects()->get();
|
||||
$environments = $resource->environment->project
|
||||
use App\Models\Project;
|
||||
|
||||
// Use passed props if available, otherwise query (backwards compatible)
|
||||
$projects = $projects ?? Project::ownedByCurrentTeamCached();
|
||||
$environments = $environments ?? $resource->environment->project
|
||||
->environments()
|
||||
->with(['applications', 'services'])
|
||||
->with([
|
||||
'applications',
|
||||
'services',
|
||||
'postgresqls',
|
||||
'redis',
|
||||
'mongodbs',
|
||||
'mysqls',
|
||||
'mariadbs',
|
||||
'keydbs',
|
||||
'dragonflies',
|
||||
'clickhouses',
|
||||
])
|
||||
->get();
|
||||
$currentProjectUuid = data_get($resource, 'environment.project.uuid');
|
||||
$currentEnvironmentUuid = data_get($resource, 'environment.uuid');
|
||||
|
|
@ -74,6 +90,16 @@ class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolg
|
|||
class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200 max-h-96 overflow-y-auto scrollbar">
|
||||
@foreach ($environments as $environment)
|
||||
@php
|
||||
// Use pre-loaded relations instead of databases() method to avoid N+1 queries
|
||||
$envDatabases = collect()
|
||||
->merge($environment->postgresqls ?? collect())
|
||||
->merge($environment->redis ?? collect())
|
||||
->merge($environment->mongodbs ?? collect())
|
||||
->merge($environment->mysqls ?? collect())
|
||||
->merge($environment->mariadbs ?? collect())
|
||||
->merge($environment->keydbs ?? collect())
|
||||
->merge($environment->dragonflies ?? collect())
|
||||
->merge($environment->clickhouses ?? collect());
|
||||
$envResources = collect()
|
||||
->merge(
|
||||
$environment->applications->map(
|
||||
|
|
@ -81,9 +107,7 @@ class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 bor
|
|||
),
|
||||
)
|
||||
->merge(
|
||||
$environment
|
||||
->databases()
|
||||
->map(fn($db) => ['type' => 'database', 'resource' => $db]),
|
||||
$envDatabases->map(fn($db) => ['type' => 'database', 'resource' => $db]),
|
||||
)
|
||||
->merge(
|
||||
$environment->services->map(
|
||||
|
|
@ -173,7 +197,9 @@ class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 bor
|
|||
]),
|
||||
};
|
||||
$isCurrentResource = $res->uuid === $currentResourceUuid;
|
||||
$resHasMultipleServers = $resType === 'application' && method_exists($res, 'additional_servers') && $res->additional_servers()->count() > 0;
|
||||
// Use loaded relation count if available, otherwise check additional_servers_count attribute
|
||||
$resHasMultipleServers = $resType === 'application' && method_exists($res, 'additional_servers') &&
|
||||
($res->relationLoaded('additional_servers') ? $res->additional_servers->count() > 0 : ($res->additional_servers_count ?? 0) > 0);
|
||||
$resServerName = $resHasMultipleServers ? null : data_get($res, 'destination.server.name');
|
||||
@endphp
|
||||
<div @mouseenter="openRes('{{ $environment->uuid }}-{{ $res->uuid }}'); resPositions['{{ $environment->uuid }}-{{ $res->uuid }}'] = $el.offsetTop - ($el.closest('.overflow-y-auto')?.scrollTop || 0)"
|
||||
|
|
@ -405,7 +431,9 @@ class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolg
|
|||
$isApplication = $resourceType === 'App\Models\Application';
|
||||
$isService = $resourceType === 'App\Models\Service';
|
||||
$isDatabase = str_contains($resourceType, 'Database') || str_contains($resourceType, 'Standalone');
|
||||
$hasMultipleServers = $isApplication && method_exists($resource, 'additional_servers') && $resource->additional_servers()->count() > 0;
|
||||
// Use loaded relation count if available, otherwise check additional_servers_count attribute
|
||||
$hasMultipleServers = $isApplication && method_exists($resource, 'additional_servers') &&
|
||||
($resource->relationLoaded('additional_servers') ? $resource->additional_servers->count() > 0 : ($resource->additional_servers_count ?? 0) > 0);
|
||||
$serverName = $hasMultipleServers ? null : data_get($resource, 'destination.server.name');
|
||||
$routeParams = [
|
||||
'project_uuid' => $currentProjectUuid,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div class="pb-5">
|
||||
<h1>Settings</h1>
|
||||
<div class="subtitle">Instance wide settings for Coolify.</div>
|
||||
<div class="subtitle">Instance wide settings for MapleDeploy.</div>
|
||||
<div class="navbar-main">
|
||||
<nav class="flex items-center gap-6 min-h-10 whitespace-nowrap">
|
||||
<a class="{{ request()->routeIs('settings.index') ? 'dark:text-white' : '' }}" {{ wireNavigate() }}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<a {{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-90 hover:opacity-100 dark:hover:text-white hover:text-black']) }}
|
||||
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('constants.coolify.version') }}" target="_blank">
|
||||
{{-- MapleDeploy branding: show version without linking to upstream releases --}}
|
||||
<span {{ $attributes->merge(['class' => 'text-xs opacity-90 dark:text-neutral-500']) }}>
|
||||
v{{ config('constants.coolify.version') }}
|
||||
</a>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<x-emails.layout>
|
||||
We would like to inform you that a {{ config('constants.limits.trial_period') }} days of trial has been added to all subscription plans.
|
||||
|
||||
You can try out Coolify, without payment information for free. If you like it, you can upgrade to a paid plan at any time.
|
||||
You can try out MapleDeploy, without payment information for free. If you like it, you can upgrade to a paid plan at any time.
|
||||
|
||||
[Click here](https://app.coolify.io/subscription/new) to start your trial.
|
||||
</x-emails.layout>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
A resource ({{ $containerName }}) has been restarted automatically on {{ $serverName }}, because it was stopped unexpectedly.
|
||||
|
||||
@if ($containerName === 'coolify-proxy')
|
||||
{{-- Note: Coolify Proxy is the technical component name, not a branding reference --}}
|
||||
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
|
||||
|
||||
If you don't want to use Coolify Proxy, please remove FQDN from your resources or set Proxy type to Custom(None).
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
{{ $errorMessage }}
|
||||
</pre>
|
||||
|
||||
The server has been removed from Coolify, but may still exist in your Hetzner Cloud account.
|
||||
The server has been removed from MapleDeploy, but may still exist in your Hetzner Cloud account.
|
||||
|
||||
Please check your Hetzner Cloud console and manually delete the server if needed to avoid ongoing charges.
|
||||
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@
|
|||
|
||||
---
|
||||
|
||||
You can manage your server and view more details in your [Coolify Dashboard]({{ $server_url }}).
|
||||
You can manage your server and view more details in your [MapleDeploy dashboard]({{ $server_url }}).
|
||||
</x-emails.layout>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
1. Review the available updates
|
||||
2. Plan maintenance window if critical packages are involved
|
||||
3. Apply updates through the Coolify dashboard
|
||||
3. Apply updates through the MapleDeploy dashboard
|
||||
4. Monitor services after updates are applied
|
||||
@else
|
||||
Your server is up to date! No packages require updating at this time.
|
||||
|
|
@ -49,5 +49,5 @@
|
|||
|
||||
---
|
||||
|
||||
You can manage server patches in your [Coolify Dashboard]({{ $server_url }}).
|
||||
You can manage server patches in your [MapleDeploy dashboard]({{ $server_url }}).
|
||||
</x-emails.layout>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{{-- MapleDeploy branding: Coolify Cloud references removed --}}
|
||||
<x-emails.layout>
|
||||
Your last invoice has failed to be paid for Coolify Cloud.
|
||||
Your last invoice has failed to be paid for MapleDeploy.
|
||||
|
||||
Please update payment details [here]({{ $stripeCustomerPortal }}).
|
||||
Please update your payment details [here]({{ $stripeCustomerPortal }}).
|
||||
</x-emails.layout>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{{-- MapleDeploy branding: Coolify Cloud references removed --}}
|
||||
<x-emails.layout>
|
||||
Your trial ended. All automations and integrations are disabled for all of your servers.
|
||||
Your trial has ended. All automations and integrations are disabled for your servers.
|
||||
|
||||
Please update payment details [here]({{ $stripeCustomerPortal }}) or in [Coolify Cloud](https://app.coolify.io) to continue using our services.
|
||||
Please update your payment details [here]({{ $stripeCustomerPortal }}) to continue using MapleDeploy.
|
||||
</x-emails.layout>
|
||||
|
|
|
|||
|
|
@ -49,8 +49,10 @@
|
|||
<div
|
||||
class="sticky top-0 z-40 flex items-center justify-between px-4 py-4 gap-x-6 sm:px-6 lg:hidden bg-white/95 dark:bg-base/95 backdrop-blur-sm border-b border-neutral-300/50 dark:border-coolgray-200/50">
|
||||
<div class="flex items-center gap-3 flex-shrink-0">
|
||||
<a href="/"
|
||||
class="text-xl font-bold tracking-wide dark:text-white hover:opacity-80 transition-opacity">Coolify</a>
|
||||
<a href="/" class="hover:opacity-80 transition-opacity">
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=32" alt="MapleDeploy" class="h-5 dark:hidden" />
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=32&dark=true" alt="MapleDeploy" class="hidden h-5 dark:block" />
|
||||
</a>
|
||||
<livewire:switch-team />
|
||||
</div>
|
||||
<button type="button" class="-m-2.5 p-2.5 dark:text-warning" x-on:click="open = !open">
|
||||
|
|
|
|||
|
|
@ -16,19 +16,17 @@
|
|||
<meta name="robots" content="noindex">
|
||||
<meta name="theme-color" content="#ffffff" id="theme-color-meta" />
|
||||
<meta name="color-scheme" content="dark light" />
|
||||
<meta name="Description" content="Coolify: An open-source & self-hostable Heroku / Netlify / Vercel alternative" />
|
||||
{{-- MapleDeploy branding --}}
|
||||
<meta name="Description" content="MapleDeploy: Managed Coolify hosting on Canadian infrastructure" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:site" content="@coolifyio" />
|
||||
<meta name="twitter:title" content="Coolify" />
|
||||
<meta name="twitter:description" content="An open-source & self-hostable Heroku / Netlify / Vercel alternative." />
|
||||
<meta name="twitter:image" content="https://cdn.coollabs.io/assets/coolify/og-image.png" />
|
||||
<meta name="twitter:title" content="MapleDeploy" />
|
||||
<meta name="twitter:description" content="Managed Coolify hosting on Canadian infrastructure." />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://coolify.io" />
|
||||
<meta property="og:title" content="Coolify" />
|
||||
<meta property="og:description" content="An open-source & self-hostable Heroku / Netlify / Vercel alternative." />
|
||||
<meta property="og:site_name" content="Coolify" />
|
||||
<meta property="og:image" content="https://cdn.coollabs.io/assets/coolify/og-image.png" />
|
||||
<meta property="og:url" content="https://mapledeploy.ca" />
|
||||
<meta property="og:title" content="MapleDeploy" />
|
||||
<meta property="og:description" content="Managed Coolify hosting on Canadian infrastructure." />
|
||||
<meta property="og:site_name" content="MapleDeploy" />
|
||||
@use('App\Models\InstanceSettings')
|
||||
@php
|
||||
|
||||
|
|
@ -43,12 +41,9 @@
|
|||
}
|
||||
}
|
||||
@endphp
|
||||
<title>{{ $name }}{{ $title ?? 'Coolify' }}</title>
|
||||
@env('local')
|
||||
<link rel="icon" href="{{ asset('coolify-logo-dev-transparent.png') }}" type="image/png" />
|
||||
@else
|
||||
<link rel="icon" href="{{ asset('coolify-logo.svg') }}" type="image/svg+xml" />
|
||||
@endenv
|
||||
<title>{{ $name }}{{ $title ?? 'MapleDeploy' }}</title> {{-- MapleDeploy branding --}}
|
||||
{{-- MapleDeploy branding: single favicon for all environments --}}
|
||||
<link rel="icon" href="{{ asset('mapledeploy-favicon.ico') }}" type="image/x-icon" />
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@vite(['resources/js/app.js', 'resources/css/app.css'])
|
||||
<script>
|
||||
|
|
@ -62,10 +57,7 @@
|
|||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
@if (config('app.name') == 'Coolify Cloud')
|
||||
<script defer data-domain="app.coolify.io" src="https://analytics.coollabs.io/js/plausible.js"></script>
|
||||
<script src="https://js.sentry-cdn.com/0f8593910512b5cdd48c6da78d4093be.min.js" crossorigin="anonymous"></script>
|
||||
@endif
|
||||
{{-- MapleDeploy branding: Coolify Cloud analytics removed --}}
|
||||
@auth
|
||||
<script type="text/javascript" src="{{ URL::asset('js/echo.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ URL::asset('js/pusher.js') }}"></script>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
@php use App\Enums\ProxyTypes; @endphp
|
||||
<x-slot:title>
|
||||
Onboarding | Coolify
|
||||
Onboarding | MapleDeploy
|
||||
</x-slot>
|
||||
<section class="w-full">
|
||||
<div class="flex flex-col items-center w-full space-y-8">
|
||||
@if ($currentState === 'welcome')
|
||||
<div class="w-full max-w-2xl text-center space-y-8">
|
||||
<div class="space-y-4">
|
||||
<h1 class="text-4xl font-bold lg:text-6xl">Welcome to Coolify</h1>
|
||||
<h1 class="text-4xl font-bold lg:text-6xl">Welcome to MapleDeploy</h1>
|
||||
<p class="text-lg lg:text-xl dark:text-neutral-400">
|
||||
Connect your first server and start deploying in minutes
|
||||
</p>
|
||||
|
|
@ -81,17 +81,17 @@ class="text-sm dark:text-neutral-400 hover:text-coollabs dark:hover:text-warning
|
|||
<x-boarding-progress :currentStep="0" />
|
||||
<x-boarding-step title="Platform Overview">
|
||||
<x-slot:question>
|
||||
Coolify automates deployment and infrastructure management on your own servers. Deploy applications
|
||||
MapleDeploy automates deployment and infrastructure management on your own servers. Deploy applications
|
||||
from Git, manage databases, and monitor everything—without vendor lock-in.
|
||||
</x-slot:question>
|
||||
<x-slot:explanation>
|
||||
<p>
|
||||
<x-highlighted text="Automation:" /> Coolify handles server configuration, Docker management,
|
||||
<x-highlighted text="Automation:" /> MapleDeploy handles server configuration, Docker management,
|
||||
and
|
||||
deployments automatically.
|
||||
</p>
|
||||
<p>
|
||||
<x-highlighted text="Self-hosted:" /> All data and configurations live on your infrastructure.
|
||||
<x-highlighted text="Your infrastructure:" /> All data and configurations live on your servers.
|
||||
Works offline except for external integrations.
|
||||
</p>
|
||||
<p>
|
||||
|
|
@ -132,7 +132,7 @@ class="px-2 py-1 text-xs font-bold uppercase tracking-wide bg-neutral-100 dark:b
|
|||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">This Machine</h3>
|
||||
<p class="text-sm dark:text-neutral-400">
|
||||
Deploy on the server running Coolify. Best for testing and single-server setups.
|
||||
Deploy on the server running MapleDeploy. Best for testing and single-server setups.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -163,38 +163,7 @@ class="px-2 py-1 text-xs font-bold uppercase tracking-wide bg-coollabs/10 dark:b
|
|||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@can('viewAny', App\Models\CloudProviderToken::class)
|
||||
@if ($currentState === 'select-server-type')
|
||||
<x-modal-input title="Connect a Hetzner Server" isFullWidth>
|
||||
<x-slot:content>
|
||||
<div
|
||||
class="group relative box-without-bg cursor-pointer hover:border-coollabs transition-all duration-200 p-6 h-full min-h-[210px]">
|
||||
<div class="flex flex-col gap-4 text-left">
|
||||
<div class="flex items-center justify-between">
|
||||
<svg class="size-10" viewBox="0 0 200 200"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="200" height="200" fill="#D50C2D" rx="8" />
|
||||
<path d="M40 40 H60 V90 H140 V40 H160 V160 H140 V110 H60 V160 H40 Z"
|
||||
fill="white" />
|
||||
</svg>
|
||||
<span
|
||||
class="px-2 py-1 text-xs font-bold uppercase tracking-wide bg-coollabs/10 dark:bg-warning/20 text-coollabs dark:text-warning rounded">
|
||||
Recommended
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-2">Hetzner Cloud</h3>
|
||||
<p class="text-sm dark:text-neutral-400">
|
||||
Deploy servers directly from your Hetzner Cloud account.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot:content>
|
||||
<livewire:server.new.by-hetzner :limit_reached="false" :from_onboarding="true" />
|
||||
</x-modal-input>
|
||||
@endif
|
||||
@endcan
|
||||
{{-- MapleDeploy branding: Hetzner Cloud provider removed --}}
|
||||
</div>
|
||||
|
||||
@if (!$serverReachable)
|
||||
|
|
@ -211,6 +180,7 @@ class="px-2 py-1 text-xs font-bold uppercase tracking-wide bg-coollabs/10 dark:b
|
|||
wire:model="remoteServerUser" :value="$remoteServerUser" />
|
||||
<p class="text-xs mt-1">
|
||||
Non-root user is experimental:
|
||||
{{-- MapleDeploy branding: link to upstream Coolify docs for technical reference --}}
|
||||
<a class="font-bold underline" target="_blank"
|
||||
href="https://coolify.io/docs/knowledge-base/server/non-root-user">docs</a>
|
||||
</p>
|
||||
|
|
@ -229,6 +199,7 @@ class="bg-red-200 dark:bg-red-900 px-1 rounded-sm">~/.ssh/authorized_keys</code>
|
|||
</div>
|
||||
|
||||
<p class="mb-4">
|
||||
{{-- MapleDeploy branding: link to upstream Coolify docs for technical reference --}}
|
||||
For more help, check this <a target="_blank" class="underline font-semibold"
|
||||
href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a>.
|
||||
</p>
|
||||
|
|
@ -248,12 +219,12 @@ class="bg-red-200 dark:bg-red-900 px-1 rounded-sm">~/.ssh/authorized_keys</code>
|
|||
called resources). All CPU-intensive operations run on the target server.
|
||||
</p>
|
||||
<p>
|
||||
<x-highlighted text="Localhost:" /> The machine running Coolify. Not recommended for production
|
||||
<x-highlighted text="Localhost:" /> The machine running MapleDeploy. Not recommended for production
|
||||
workloads due to resource contention.
|
||||
</p>
|
||||
<p>
|
||||
<x-highlighted text="Remote Server:" /> Any SSH-accessible server—cloud providers (AWS, Hetzner,
|
||||
DigitalOcean), bare metal, or self-hosted infrastructure.
|
||||
<x-highlighted text="Remote Server:" /> Any SSH-accessible server—cloud providers,
|
||||
bare metal, or self-hosted infrastructure.
|
||||
</p>
|
||||
</x-slot:explanation>
|
||||
</x-boarding-step>
|
||||
|
|
@ -336,7 +307,7 @@ class="text-xs bg-coolgray-300 dark:bg-coolgray-400 px-1 py-0.5 rounded">~/.ssh/
|
|||
file.
|
||||
</p>
|
||||
<p>
|
||||
<x-highlighted text="Key Generation:" /> Coolify generates ED25519 keys by default for optimal
|
||||
<x-highlighted text="Key Generation:" /> MapleDeploy generates ED25519 keys by default for optimal
|
||||
security and performance.
|
||||
</p>
|
||||
</x-slot:explanation>
|
||||
|
|
@ -386,7 +357,7 @@ class="text-xs bg-coolgray-300 dark:bg-coolgray-400 px-1 py-0.5 rounded">~/.ssh/
|
|||
</x-slot:actions>
|
||||
<x-slot:explanation>
|
||||
<p>
|
||||
<x-highlighted text="Key Storage:" /> Private keys are encrypted at rest in Coolify's database.
|
||||
<x-highlighted text="Key Storage:" /> Private keys are encrypted at rest in the database.
|
||||
</p>
|
||||
<p>
|
||||
<x-highlighted text="Public Key Distribution:" /> Deploy the public key to
|
||||
|
|
@ -443,6 +414,7 @@ class="grid grid-cols-1 lg:grid-cols-2 gap-4 p-4 rounded-lg border border-neutra
|
|||
wire:model="remoteServerUser" />
|
||||
<p class="mt-1 text-xs dark:text-white text-black">
|
||||
Non-root user support is experimental.
|
||||
{{-- MapleDeploy branding: link to upstream Coolify docs for technical reference --}}
|
||||
<a class="font-bold underline hover:text-coollabs" target="_blank"
|
||||
href="https://coolify.io/docs/knowledge-base/server/non-root-user">Learn
|
||||
more</a>
|
||||
|
|
@ -473,7 +445,7 @@ class="grid grid-cols-1 lg:grid-cols-2 gap-4 p-4 rounded-lg border border-neutra
|
|||
<x-boarding-progress :currentStep="2" />
|
||||
<x-boarding-step title="Server Validation">
|
||||
<x-slot:question>
|
||||
Coolify will automatically install Docker {{ $minDockerVersion }}+ if not present.
|
||||
MapleDeploy will automatically install Docker {{ $minDockerVersion }}+ if not present.
|
||||
</x-slot:question>
|
||||
<x-slot:actions>
|
||||
<div class="w-full space-y-6">
|
||||
|
|
@ -567,7 +539,7 @@ class="p-6 bg-neutral-50 dark:bg-coolgray-200 rounded-lg border border-neutral-2
|
|||
</x-slot:actions>
|
||||
<x-slot:explanation>
|
||||
<p>
|
||||
<x-highlighted text="Automated Setup:" /> Coolify installs Docker Engine, Docker Compose, and
|
||||
<x-highlighted text="Automated Setup:" /> MapleDeploy installs Docker Engine, Docker Compose, and
|
||||
configures system requirements automatically.
|
||||
</p>
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Dashboard | Coolify
|
||||
Dashboard | MapleDeploy
|
||||
</x-slot>
|
||||
@if (session('error'))
|
||||
<span x-data x-init="$wire.emit('error', '{{ session('error') }}')" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Destinations | Coolify
|
||||
Destinations | MapleDeploy
|
||||
</x-slot>
|
||||
<div class="flex items-center gap-2">
|
||||
<h1>Destinations</h1>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<section class="bg-gray-50 dark:bg-base">
|
||||
<div class="flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0">
|
||||
<a class="flex items-center mb-6 text-5xl font-extrabold tracking-tight text-gray-900 dark:text-white">
|
||||
Coolify
|
||||
</a>
|
||||
<div class="flex items-center justify-center mb-6">
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80" alt="MapleDeploy" class="h-12 dark:hidden" />
|
||||
<img src="https://mapledeploy.ca/api/logo/lockup?height=80&dark=true" alt="MapleDeploy" class="hidden h-12 dark:block" />
|
||||
</div>
|
||||
<div class="w-full bg-white shadow-sm md:mt-0 sm:max-w-md xl:p-0 dark:bg-base ">
|
||||
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
|
||||
<form class="flex flex-col gap-2" wire:submit='submit'>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<div class="flex flex-col w-full gap-2">
|
||||
<div>Your feedback helps us to improve Coolify. Thank you! 💜</div>
|
||||
{{-- MapleDeploy branding --}}
|
||||
<div>Your feedback helps us improve MapleDeploy. Thank you!</div>
|
||||
<form wire:submit="submit" class="flex flex-col gap-4 pt-4">
|
||||
<x-forms.input minlength="3" required id="subject" label="Subject" placeholder="Help with..."></x-forms.input>
|
||||
<x-forms.textarea minlength="10" maxlength="1000" required rows="10" id="description" label="Description"
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@
|
|||
if (checkNumber > 5) {
|
||||
this.popups.realtime = true;
|
||||
console.error(
|
||||
'Coolify could not connect to its real-time service. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/knowledge-base/cloudflare/tunnels/overview) or get help on Discord (https://coollabs.io/discord).)'
|
||||
);
|
||||
'MapleDeploy could not connect to its real-time service. This will cause unusual problems on the UI if not fixed! Please contact support at support@mapledeploy.ca.'
|
||||
); // MapleDeploy branding
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -70,13 +70,11 @@
|
|||
<x-slot:title>
|
||||
<span class="font-bold text-left text-red-500">WARNING: </span> Cannot connect to real-time service
|
||||
</x-slot:title>
|
||||
{{-- MapleDeploy branding: support links updated --}}
|
||||
<x-slot:description>
|
||||
<div>This will cause unusual problems on the
|
||||
UI! <br><br>
|
||||
Please ensure that you have opened the
|
||||
<a class="underline" href='https://coolify.io/docs/knowledge-base/server/firewall'
|
||||
target='_blank'>required ports</a> or get
|
||||
help on <a class="underline" href='https://coollabs.io/discord' target='_blank'>Discord</a>.
|
||||
Please contact <a class="underline" href='mailto:support@mapledeploy.ca'>MapleDeploy support</a> for help.
|
||||
</div>
|
||||
</x-slot:description>
|
||||
<x-slot:button-text @click="disableRealtime()">
|
||||
|
|
@ -86,52 +84,7 @@
|
|||
@endif
|
||||
</span>
|
||||
@endauth
|
||||
@if (instanceSettings()->is_sponsorship_popup_enabled && !isCloud())
|
||||
<span x-show="popups.sponsorship">
|
||||
<x-popup>
|
||||
<x-slot:customActions>
|
||||
<div
|
||||
class="flex md:flex-row flex-col max-w-4xl p-6 mx-auto bg-white border shadow-lg lg:border-t dark:border-coolgray-300 border-neutral-200 dark:bg-coolgray-100 lg:p-8 lg:pb-4 sm:rounded-sm gap-2">
|
||||
<div class="md:block hidden">
|
||||
<img src="{{ asset('heart.png') }}" class="w-20 h-20">
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 lg:px-10 px-1">
|
||||
<div class="lg:text-xl text-md dark:text-white font-bold">Love Coolify? Support our work.
|
||||
</div>
|
||||
<div class="lg:text-sm text-xs dark:text-white">
|
||||
We are already profitable thanks to <span class="font-bold text-pink-500">YOU</span>
|
||||
but...<br />We
|
||||
would
|
||||
like to
|
||||
make
|
||||
more cool features.
|
||||
</div>
|
||||
<div class="lg:text-sm text-xs dark:text-white pt-2 ">
|
||||
For this we need your help to support our work financially.
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 text-center md:mx-auto lg:py-0 pt-2">
|
||||
<x-forms.button isHighlighted class="md:w-36 w-full"><a target="_blank"
|
||||
href="https://github.com/sponsors/coollabsio"
|
||||
class="font-bold dark:text-white">GitHub
|
||||
Sponsors</a></x-forms.button>
|
||||
<x-forms.button isHighlighted class="md:w-36 w-full"><a target="_blank"
|
||||
href="https://opencollective.com/coollabsio/donate?interval=month&amount=10&name=&legalName=&email="
|
||||
class="font-bold dark:text-white">Open
|
||||
Collective</a></x-forms.button>
|
||||
<x-forms.button isHighlighted class="md:w-36 w-full"><a
|
||||
href="https://donate.stripe.com/8x2bJ104ifmB9kB45u38402" target="_blank"
|
||||
class="font-bold dark:text-white">Stripe</a></x-forms.button>
|
||||
<div class="pt-4 dark:text-white hover:underline cursor-pointer lg:text-base text-xs"
|
||||
@click="bannerVisible=false;disableSponsorship()">
|
||||
Maybe next time
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot:customActions>
|
||||
</x-popup>
|
||||
</span>
|
||||
@endif
|
||||
{{-- MapleDeploy branding: Coolify sponsorship popup removed --}}
|
||||
@if (currentTeam()->subscriptionPastOverDue())
|
||||
<x-banner :closable=false>
|
||||
<div><span class="font-bold text-red-500">WARNING:</span> Your subscription is in over-due. If your
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
Notifications | MapleDeploy
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
Notifications | MapleDeploy
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
Notifications | MapleDeploy
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
Notifications | MapleDeploy
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
Notifications | MapleDeploy
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
Notifications | MapleDeploy
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
|
|
@ -29,7 +29,7 @@ class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
|
|||
<div class="flex items-end gap-2">
|
||||
|
||||
<x-forms.input canGate="update" :canResource="$settings" type="password"
|
||||
helper="Enter a valid HTTP or HTTPS URL. Coolify will send POST requests to this endpoint when events occur."
|
||||
helper="Enter a valid HTTP or HTTPS URL. MapleDeploy will send POST requests to this endpoint when events occur."
|
||||
required id="webhookUrl" label="Webhook URL (POST)" />
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Profile | Coolify
|
||||
Profile | MapleDeploy
|
||||
</x-slot>
|
||||
<h1>Profile</h1>
|
||||
<div class="subtitle -mt-2">Your user profile settings.</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($application, 'name')->limit(10) }} > Configuration | Coolify
|
||||
{{ data_get_str($application, 'name')->limit(10) }} > Configuration | MapleDeploy
|
||||
</x-slot>
|
||||
<h1>Configuration</h1>
|
||||
<livewire:project.shared.configuration-checker :resource="$application" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div>
|
||||
<x-slot:title>{{ data_get_str($application, 'name')->limit(10) }} > Deployments | Coolify</x-slot>
|
||||
<x-slot:title>{{ data_get_str($application, 'name')->limit(10) }} > Deployments | MapleDeploy</x-slot>
|
||||
<h1>Deployments</h1>
|
||||
<livewire:project.shared.configuration-checker :resource="$application" />
|
||||
<livewire:project.application.heading :application="$application" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($application, 'name')->limit(10) }} > Deployment | Coolify
|
||||
{{ data_get_str($application, 'name')->limit(10) }} > Deployment | MapleDeploy
|
||||
</x-slot>
|
||||
<h1 class="py-0">Deployment</h1>
|
||||
<livewire:project.shared.configuration-checker :resource="$application" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<form>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Clone | Coolify
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Clone | MapleDeploy
|
||||
</x-slot>
|
||||
<div class="flex flex-col">
|
||||
<h1>Clone</h1>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($database, 'name')->limit(10) }} > Backup | Coolify
|
||||
{{ data_get_str($database, 'name')->limit(10) }} > Backup | MapleDeploy
|
||||
</x-slot>
|
||||
<h1>Backups</h1>
|
||||
<livewire:project.shared.configuration-checker :resource="$database" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($database, 'name')->limit(10) }} > Backups | Coolify
|
||||
{{ data_get_str($database, 'name')->limit(10) }} > Backups | MapleDeploy
|
||||
</x-slot>
|
||||
<h1>Backups</h1>
|
||||
<livewire:project.shared.configuration-checker :resource="$database" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($database, 'name')->limit(10) }} > Configuration | Coolify
|
||||
{{ data_get_str($database, 'name')->limit(10) }} > Configuration | MapleDeploy
|
||||
</x-slot>
|
||||
<h1>Configuration</h1>
|
||||
<livewire:project.shared.configuration-checker :resource="$database" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Edit | Coolify
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Edit | MapleDeploy
|
||||
</x-slot>
|
||||
<form wire:submit='submit' class="flex flex-col pb-10">
|
||||
<div class="flex gap-2">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Edit | Coolify
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Edit | MapleDeploy
|
||||
</x-slot>
|
||||
<form wire:submit='submit' class="flex flex-col">
|
||||
<div class="flex items-end gap-2">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Projects | Coolify
|
||||
Projects | MapleDeploy
|
||||
</x-slot>
|
||||
<div class="flex gap-2">
|
||||
<h1>Projects</h1>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2 -mt-1">
|
||||
@foreach ($projects as $project)
|
||||
<div class="relative gap-2 cursor-pointer coolbox group">
|
||||
<a href="{{ $project->navigateTo() }}" class="absolute inset-0"></a>
|
||||
<a href="{{ $project->navigateTo() }}" {{ wireNavigate() }} class="absolute inset-0"></a>
|
||||
<div class="flex flex-1 mx-6">
|
||||
<div class="flex flex-col justify-center flex-1">
|
||||
<div class="box-title">{{ $project->name }}</div>
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@
|
|||
</x-forms.button>
|
||||
</div>
|
||||
<div>
|
||||
{{-- MapleDeploy branding: link to upstream examples (still useful) --}}
|
||||
For example application deployments, checkout <a class="underline dark:text-white"
|
||||
href="https://github.com/coollabsio/coolify-examples/" target="_blank">Coolify
|
||||
Examples</a>.
|
||||
href="https://github.com/coollabsio/coolify-examples/" target="_blank">example
|
||||
repositories</a>.
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > New | Coolify
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > New | MapleDeploy
|
||||
</x-slot>
|
||||
@if ($type === 'public')
|
||||
<livewire:project.new.public-git-repository :type="$type" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Resources | Coolify
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Resources | MapleDeploy
|
||||
</x-slot>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center gap-2">
|
||||
|
|
@ -29,9 +29,6 @@
|
|||
<livewire:project.delete-environment :disabled="!$environment->isEmpty()" :environment_id="$environment->id" />
|
||||
@endcan
|
||||
</div>
|
||||
@php
|
||||
$projects = auth()->user()->currentTeam()->projects()->get();
|
||||
@endphp
|
||||
<nav class="flex pt-2 pb-6">
|
||||
<ol class="flex items-center">
|
||||
<li class="inline-flex items-center" x-data="{ projectOpen: false, toggle() { this.projectOpen = !this.projectOpen }, open() { this.projectOpen = true }, close() { this.projectOpen = false } }">
|
||||
|
|
@ -53,7 +50,7 @@
|
|||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95"
|
||||
class="absolute z-20 top-full mt-1 w-56 -ml-2 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 border border-neutral-200 dark:border-coolgray-200 max-h-96 overflow-y-auto scrollbar">
|
||||
@foreach ($projects as $proj)
|
||||
@foreach ($allProjects as $proj)
|
||||
<a href="{{ route('project.show', ['project_uuid' => $proj->uuid]) }}"
|
||||
class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolgray-200 {{ $proj->uuid === $project->uuid ? 'dark:text-warning font-semibold' : '' }}"
|
||||
title="{{ $proj->name }}">
|
||||
|
|
@ -63,12 +60,6 @@ class="block px-4 py-2 text-sm truncate hover:bg-neutral-100 dark:hover:bg-coolg
|
|||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@php
|
||||
$allEnvironments = $project
|
||||
->environments()
|
||||
->with(['applications', 'services'])
|
||||
->get();
|
||||
@endphp
|
||||
<li class="inline-flex items-center" x-data="{ envOpen: false, activeEnv: null, envPositions: {}, activeRes: null, resPositions: {}, activeMenuEnv: null, menuPositions: {}, closeTimeout: null, envTimeout: null, resTimeout: null, menuTimeout: null, toggle() { this.envOpen = !this.envOpen; if (!this.envOpen) { this.activeEnv = null;
|
||||
this.activeRes = null;
|
||||
this.activeMenuEnv = null; } }, open() { clearTimeout(this.closeTimeout);
|
||||
|
|
@ -162,6 +153,16 @@ class="flex items-center gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover
|
|||
<!-- Resources Sub-dropdown (2nd level) -->
|
||||
@foreach ($allEnvironments as $env)
|
||||
@php
|
||||
// Use pre-loaded relations instead of databases() method to avoid N+1 queries
|
||||
$envDatabases = collect()
|
||||
->merge($env->postgresqls ?? collect())
|
||||
->merge($env->redis ?? collect())
|
||||
->merge($env->mongodbs ?? collect())
|
||||
->merge($env->mysqls ?? collect())
|
||||
->merge($env->mariadbs ?? collect())
|
||||
->merge($env->keydbs ?? collect())
|
||||
->merge($env->dragonflies ?? collect())
|
||||
->merge($env->clickhouses ?? collect());
|
||||
$envResources = collect()
|
||||
->merge(
|
||||
$env->applications->map(
|
||||
|
|
@ -169,9 +170,7 @@ class="flex items-center gap-2 px-4 py-2 text-sm hover:bg-neutral-100 dark:hover
|
|||
),
|
||||
)
|
||||
->merge(
|
||||
$env
|
||||
->databases()
|
||||
->map(fn($db) => ['type' => 'database', 'resource' => $db]),
|
||||
$envDatabases->map(fn($db) => ['type' => 'database', 'resource' => $db]),
|
||||
)
|
||||
->merge(
|
||||
$env->services->map(fn($svc) => ['type' => 'service', 'resource' => $svc]),
|
||||
|
|
@ -208,10 +207,11 @@ class="relative w-48 bg-white dark:bg-coolgray-100 rounded-md shadow-lg py-1 bor
|
|||
'database_uuid' => $res->uuid,
|
||||
]),
|
||||
};
|
||||
// Use loaded relation to check additional_servers (avoids N+1 query)
|
||||
$resHasMultipleServers =
|
||||
$resType === 'application' &&
|
||||
method_exists($res, 'additional_servers') &&
|
||||
$res->additional_servers()->count() > 0;
|
||||
($res->relationLoaded('additional_servers') ? $res->additional_servers->count() > 0 : false);
|
||||
$resServerName = $resHasMultipleServers
|
||||
? null
|
||||
: data_get($res, 'destination.server.name');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($service, 'name')->limit(10) }} > Configuration | Coolify
|
||||
{{ data_get_str($service, 'name')->limit(10) }} > Configuration | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:project.service.heading :service="$service" :parameters="$parameters" :query="$query" />
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<div class="w-full">
|
||||
<x-slot:title>
|
||||
{{ data_get_str($service, 'name')->limit(10) }} >
|
||||
{{ data_get_str($serviceDatabase, 'name')->limit(10) }} > Backups | Coolify
|
||||
{{ data_get_str($serviceDatabase, 'name')->limit(10) }} > Backups | MapleDeploy
|
||||
</x-slot>
|
||||
<div class="flex gap-2">
|
||||
<h2 class="pb-4">Scheduled Backups</h2>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-activ
|
|||
@if ($resourceType === 'application')
|
||||
<x-slot:title>
|
||||
{{ data_get_str($service, 'name')->limit(10) }} >
|
||||
{{ data_get_str($serviceApplication, 'name')->limit(10) }} | Coolify
|
||||
{{ data_get_str($serviceApplication, 'name')->limit(10) }} | MapleDeploy
|
||||
</x-slot>
|
||||
<form wire:submit='submitApplication'>
|
||||
<div class="flex items-center gap-2 pb-4">
|
||||
|
|
@ -174,7 +174,7 @@ class="w-auto dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
|
|||
@elseif ($resourceType === 'database')
|
||||
<x-slot:title>
|
||||
{{ data_get_str($service, 'name')->limit(10) }} >
|
||||
{{ data_get_str($serviceDatabase, 'name')->limit(10) }} | Coolify
|
||||
{{ data_get_str($serviceDatabase, 'name')->limit(10) }} | MapleDeploy
|
||||
</x-slot>
|
||||
@if ($currentRoute === 'project.service.database.import')
|
||||
<livewire:project.database.import :resource="$serviceDatabase" :key="'import-' . $serviceDatabase->uuid" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($resource, 'name')->limit(10) }} > Commands | Coolify
|
||||
{{ data_get_str($resource, 'name')->limit(10) }} > Commands | MapleDeploy
|
||||
</x-slot>
|
||||
@if ($type === 'application')
|
||||
<livewire:project.shared.configuration-checker :resource="$resource" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($resource, 'name')->limit(10) }} > Logs | Coolify
|
||||
{{ data_get_str($resource, 'name')->limit(10) }} > Logs | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:project.shared.configuration-checker :resource="$resource" />
|
||||
@if ($type === 'application')
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($resource, 'name')->limit(10) }} > Scheduled Tasks | Coolify
|
||||
{{ data_get_str($resource, 'name')->limit(10) }} > Scheduled Tasks | MapleDeploy
|
||||
</x-slot>
|
||||
@if ($type === 'application')
|
||||
<h1>Scheduled Task</h1>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Environments | Coolify
|
||||
{{ data_get_str($project, 'name')->limit(10) }} > Environments | MapleDeploy
|
||||
</x-slot>
|
||||
<div class="flex items-center gap-2">
|
||||
<h1>Environments</h1>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
API Tokens | Coolify
|
||||
API Tokens | MapleDeploy
|
||||
</x-slot>
|
||||
<x-security.navbar />
|
||||
<div class="pb-4">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Cloud Tokens | Coolify
|
||||
Cloud Tokens | MapleDeploy
|
||||
</x-slot>
|
||||
<x-security.navbar />
|
||||
<livewire:security.cloud-provider-tokens />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div x-init="$wire.loadPublicKey()">
|
||||
<x-slot:title>
|
||||
Private Key | Coolify
|
||||
Private Key | MapleDeploy
|
||||
</x-slot>
|
||||
<x-security.navbar />
|
||||
<div x-data="{ showPrivateKey: false }">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Advanced | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Advanced | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > CA Certificate | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > CA Certificate | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Metrics | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Metrics | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Hetzner Token | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Hetzner Token | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Cloudflare Tunnel | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Cloudflare Tunnel | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,31 +1,6 @@
|
|||
{{-- MapleDeploy branding: Hetzner provider removed --}}
|
||||
<div class="w-full">
|
||||
<div class="flex flex-col gap-4">
|
||||
@can('viewAny', App\Models\CloudProviderToken::class)
|
||||
<div>
|
||||
<x-modal-input title="Connect a Hetzner Server">
|
||||
<x-slot:content>
|
||||
<div class="relative gap-2 cursor-pointer coolbox group">
|
||||
<div class="flex items-center gap-4 mx-6">
|
||||
<svg class="w-10 h-10 flex-shrink-0" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="200" height="200" fill="#D50C2D" rx="8" />
|
||||
<path d="M40 40 H60 V90 H140 V40 H160 V160 H140 V110 H60 V160 H40 Z" fill="white" />
|
||||
</svg>
|
||||
<div class="flex flex-col justify-center flex-1">
|
||||
<div class="box-title">Connect a Hetzner Server</div>
|
||||
<div class="box-description">
|
||||
Deploy servers directly from your Hetzner Cloud account
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-slot:content>
|
||||
<livewire:server.new.by-hetzner :private_keys="$private_keys" :limit_reached="$limit_reached" />
|
||||
</x-modal-input>
|
||||
</div>
|
||||
|
||||
<div class="border-t dark:border-coolgray-300 my-4"></div>
|
||||
@endcan
|
||||
|
||||
<div>
|
||||
<h3 class="pb-2">Add Server by IP Address</h3>
|
||||
<livewire:server.new.by-ip :private_keys="$private_keys" :limit_reached="$limit_reached" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Delete Server | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Delete Server | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Destinations | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Destinations | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Docker Cleanup | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Docker Cleanup | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Servers | Coolify
|
||||
Servers | MapleDeploy
|
||||
</x-slot>
|
||||
<div class="flex items-center gap-2">
|
||||
<h1>Servers</h1>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Log Drains | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Log Drains | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Private Key | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Private Key | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Proxy Dynamic Configuration | Coolify
|
||||
Proxy Dynamic Configuration | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Proxy Logs | Coolify
|
||||
Proxy Logs | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Proxy Configuration | Coolify
|
||||
Proxy Configuration | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
@if ($server->isFunctional())
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Server Resources | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Server Resources | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div x-data="{ activeTab: 'managed' }" class="flex flex-col h-full gap-8 md:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Security | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Security | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<x-slide-over closeWithX fullScreen @startupdate.window="slideOverOpen = true">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Terminal Access | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Terminal Access | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Sentinel | Coolify
|
||||
{{ data_get_str($server, 'name')->limit(10) }} > Sentinel | MapleDeploy
|
||||
</x-slot>
|
||||
<livewire:server.navbar :server="$server" />
|
||||
<div class="flex flex-col h-full gap-8 sm:flex-row">
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue