mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-27 04:59:31 +00:00
Compare commits
114 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10b9c4bcfa | ||
|
|
38d914076e | ||
|
|
102dd6bfb1 | ||
|
|
04379b76f2 | ||
|
|
d6fb54f3c3 | ||
|
|
1d419c6ab8 | ||
|
|
8a4e958663 | ||
|
|
b04f7686fd | ||
|
|
5aabdefaa7 | ||
|
|
f76d45b826 | ||
|
|
6cc86a3c82 | ||
|
|
f1e5b61970 | ||
|
|
65380646f7 | ||
|
|
189a8347ed | ||
|
|
e96e8f6fec | ||
|
|
38299ab507 | ||
|
|
f134171855 | ||
|
|
320204d854 | ||
|
|
b68199a482 | ||
|
|
6f4436fd5e | ||
|
|
0d8cc19698 | ||
|
|
a3a1ff69e1 | ||
|
|
5df7e23aa4 | ||
|
|
35d9691b3f | ||
|
|
465f649641 | ||
|
|
d909e7d802 | ||
|
|
06db6b8502 | ||
|
|
12261b9082 | ||
|
|
583ec432e8 | ||
|
|
8ffbccf7db | ||
|
|
439fe43a04 | ||
|
|
7fd9a799b5 | ||
|
|
7459ab22d1 | ||
|
|
133a68f3eb | ||
|
|
3224110583 | ||
|
|
810488b115 | ||
|
|
b2276147ad | ||
|
|
6c1293c63e | ||
|
|
526d675272 | ||
|
|
14b2442d40 | ||
|
|
d6d194d414 | ||
|
|
0e99f97855 | ||
|
|
14dc933219 | ||
|
|
9497f123b4 | ||
|
|
6feb439d0a | ||
|
|
e4ca5ee5f5 | ||
|
|
f21c12f39b | ||
|
|
6c1e50a914 | ||
|
|
da064def7a | ||
|
|
3af3fa5773 | ||
|
|
005bd55fb2 | ||
|
|
82a5b4c55d | ||
|
|
b8e95b2099 | ||
|
|
8ea50dc029 | ||
|
|
ec191af874 | ||
|
|
d98c742aff | ||
|
|
2529496594 | ||
|
|
1b6114036a | ||
|
|
b33fb6c39a | ||
|
|
0a6826af58 | ||
|
|
1c7034ff78 | ||
|
|
7e11698c55 | ||
|
|
1c4eb31d59 | ||
|
|
b4b6a4294a | ||
|
|
4c031a7c05 | ||
|
|
997a262b6c | ||
|
|
c0e88df3e8 | ||
|
|
85e1cbad53 | ||
|
|
c37398af72 | ||
|
|
19cfe4e514 | ||
|
|
23a1b1925f | ||
|
|
1fb8d1e14c | ||
|
|
804c70b575 | ||
|
|
548c4a4c64 | ||
|
|
2978042162 | ||
|
|
4225ec7060 | ||
|
|
893339fc8e | ||
|
|
356e7b57d2 | ||
|
|
4ee1f1a507 | ||
|
|
7d64df60cd | ||
|
|
eb3a4ca157 | ||
|
|
a7b5157fa6 | ||
|
|
793e6d19eb | ||
|
|
674fa4d09c | ||
|
|
0089e86dd1 | ||
|
|
e1d802b507 | ||
|
|
9927b71af9 | ||
|
|
b1c0f105ab | ||
|
|
35cae1d4dc | ||
|
|
3dab3365e2 | ||
|
|
1bdc7c87ba | ||
|
|
cab8ad0ca0 | ||
|
|
43409f3ff0 | ||
|
|
a5dd4cab52 | ||
|
|
a815240f4e | ||
|
|
2a44e7c5bd | ||
|
|
28c7e439b1 | ||
|
|
4396c786b4 | ||
|
|
b67bb8595f | ||
|
|
bec47487dd | ||
|
|
b110d0c12b | ||
|
|
ae425475b4 | ||
|
|
dc6aee44b3 | ||
|
|
4ffea311e8 | ||
|
|
77a6a6e46a | ||
|
|
2278ba31e7 | ||
|
|
aaeec3d340 | ||
|
|
2cbe530b7e | ||
|
|
7de2b8cbd7 | ||
|
|
852e906736 | ||
|
|
0c40c0d795 | ||
|
|
25f0a8f0b7 | ||
|
|
e7e85456ea | ||
|
|
440baf6009 |
13
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
13
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Bug report
|
||||
description: 'Create a new bug report.'
|
||||
title: '[Bug]: '
|
||||
description: "Create a new bug report."
|
||||
title: "[Bug]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -35,3 +35,12 @@ body:
|
||||
description: Coolify's version (see top of your screen).
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Cloud?
|
||||
description: "Are you using the cloud version of Coolify?"
|
||||
options:
|
||||
- label: 'Yes'
|
||||
required: false
|
||||
- label: 'No'
|
||||
required: false
|
||||
|
||||
@@ -48,6 +48,7 @@ Special thanks to our biggest sponsors!
|
||||
<a href="https://trieve.ai/?ref=coolify.io" target="_blank"><img src="./other/logos/trieve_bg.png" alt="trieve logo" width="180"/></a>
|
||||
<a href="https://blacksmith.sh/?ref=coolify.io" target="_blank"><img src="./other/logos/blacksmith.svg" alt="blacksmith logo" width="200"/></a>
|
||||
<a href="https://latitude.sh/?ref=coolify.io" target="_blank"><img src="./other/logos/latitude.svg" alt="latitude logo" width="200"/></a>
|
||||
<a href="https://brand.dev/?ref=coolify.io" target="_blank"><img src="./other/logos/branddev.png" alt="branddev logo" width="200"/></a>
|
||||
|
||||
## Github Sponsors ($40+)
|
||||
<a href="https://serpapi.com/?ref=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
|
||||
|
||||
@@ -9,7 +9,7 @@ class StopApplication
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Application $application)
|
||||
public function handle(Application $application, bool $previewDeployments = false)
|
||||
{
|
||||
if ($application->destination->server->isSwarm()) {
|
||||
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
|
||||
@@ -26,7 +26,12 @@ class StopApplication
|
||||
if (! $server->isFunctional()) {
|
||||
return 'Server is not functional';
|
||||
}
|
||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
||||
if ($previewDeployments) {
|
||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, includePullrequests: true);
|
||||
} else {
|
||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
||||
}
|
||||
ray($containers);
|
||||
if ($containers->count() > 0) {
|
||||
foreach ($containers as $container) {
|
||||
$containerName = data_get($container, 'Names');
|
||||
|
||||
@@ -21,7 +21,6 @@ class CheckConfiguration
|
||||
"cat $proxy_path/docker-compose.yml",
|
||||
];
|
||||
$proxy_configuration = instant_remote_process($payload, $server, false);
|
||||
|
||||
if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) {
|
||||
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value;
|
||||
}
|
||||
|
||||
67
app/Actions/Server/ValidateServer.php
Normal file
67
app/Actions/Server/ValidateServer.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class ValidateServer
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public ?string $uptime = null;
|
||||
|
||||
public ?string $error = null;
|
||||
|
||||
public ?string $supported_os_type = null;
|
||||
|
||||
public ?string $docker_installed = null;
|
||||
|
||||
public ?string $docker_compose_installed = null;
|
||||
|
||||
public ?string $docker_version = null;
|
||||
|
||||
public function handle(Server $server)
|
||||
{
|
||||
$server->update([
|
||||
'validation_logs' => null,
|
||||
]);
|
||||
['uptime' => $this->uptime, 'error' => $error] = $server->validateConnection();
|
||||
if (! $this->uptime) {
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br>Check this <a target="_blank" class="text-black underline dark:text-white" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br><div class="text-error">Error: '.$error.'</div>';
|
||||
$server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
throw new \Exception($this->error);
|
||||
}
|
||||
$this->supported_os_type = $server->validateOS();
|
||||
if (! $this->supported_os_type) {
|
||||
$this->error = 'Server OS type is not supported. Please install Docker manually before continuing: <a target="_blank" class="text-black underline dark:text-white" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
throw new \Exception($this->error);
|
||||
}
|
||||
|
||||
$this->docker_installed = $server->validateDockerEngine();
|
||||
$this->docker_compose_installed = $server->validateDockerCompose();
|
||||
if (! $this->docker_installed || ! $this->docker_compose_installed) {
|
||||
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="text-black underline dark:text-white" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
throw new \Exception($this->error);
|
||||
}
|
||||
$this->docker_version = $server->validateDockerEngineVersion();
|
||||
|
||||
if ($this->docker_version) {
|
||||
return 'OK';
|
||||
} else {
|
||||
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="text-black underline dark:text-white" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
throw new \Exception($this->error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class CleanupUnreachableServers extends Command
|
||||
if ($servers->count() > 0) {
|
||||
foreach ($servers as $server) {
|
||||
echo "Cleanup unreachable server ($server->id) with name $server->name";
|
||||
send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up...");
|
||||
// send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up...");
|
||||
$server->update([
|
||||
'ip' => '1.2.3.4',
|
||||
]);
|
||||
|
||||
@@ -81,7 +81,7 @@ class Emails extends Command
|
||||
}
|
||||
set_transanctional_email_settings();
|
||||
|
||||
$this->mail = new MailMessage();
|
||||
$this->mail = new MailMessage;
|
||||
$this->mail->subject('Test Email');
|
||||
switch ($type) {
|
||||
case 'updates':
|
||||
@@ -107,7 +107,7 @@ class Emails extends Command
|
||||
$confirmed = confirm('Are you sure?');
|
||||
if ($confirmed) {
|
||||
foreach ($emails as $email) {
|
||||
$this->mail = new MailMessage();
|
||||
$this->mail = new MailMessage;
|
||||
$this->mail->subject('One-click Services, Docker Compose support');
|
||||
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
||||
'token' => encrypt($email),
|
||||
@@ -118,7 +118,7 @@ class Emails extends Command
|
||||
}
|
||||
break;
|
||||
case 'emails-test':
|
||||
$this->mail = (new Test())->toMail();
|
||||
$this->mail = (new Test)->toMail();
|
||||
$this->sendEmail();
|
||||
break;
|
||||
case 'database-backup-statuses-daily':
|
||||
@@ -224,7 +224,7 @@ class Emails extends Command
|
||||
// $this->sendEmail();
|
||||
// break;
|
||||
case 'waitlist-invitation-link':
|
||||
$this->mail = new MailMessage();
|
||||
$this->mail = new MailMessage;
|
||||
$this->mail->view('emails.waitlist-invitation', [
|
||||
'loginLink' => 'https://coolify.io',
|
||||
]);
|
||||
@@ -241,7 +241,7 @@ class Emails extends Command
|
||||
|
||||
break;
|
||||
case 'realusers-before-trial':
|
||||
$this->mail = new MailMessage();
|
||||
$this->mail = new MailMessage;
|
||||
$this->mail->view('emails.before-trial-conversion');
|
||||
$this->mail->subject('Trial period has been added for all subscription plans.');
|
||||
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
|
||||
@@ -287,7 +287,7 @@ class Emails extends Command
|
||||
foreach ($admins as $admin) {
|
||||
$this->info($admin);
|
||||
}
|
||||
$this->mail = new MailMessage();
|
||||
$this->mail = new MailMessage;
|
||||
$this->mail->view('emails.server-lost-connection', [
|
||||
'name' => $server->name,
|
||||
]);
|
||||
|
||||
@@ -103,7 +103,7 @@ class WaitlistInvite extends Command
|
||||
{
|
||||
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
||||
$loginLink = route('auth.link', ['token' => $token]);
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->view('emails.waitlist-invitation', [
|
||||
'loginLink' => $loginLink,
|
||||
]);
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Jobs\CheckLogDrainContainerJob;
|
||||
use App\Jobs\CleanupInstanceStuffsJob;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Jobs\DatabaseBackupJob;
|
||||
use App\Jobs\DockerCleanupJob;
|
||||
use App\Jobs\PullCoolifyImageJob;
|
||||
use App\Jobs\PullHelperImageJob;
|
||||
use App\Jobs\PullSentinelImageJob;
|
||||
@@ -87,6 +88,7 @@ class Kernel extends ConsoleKernel
|
||||
}
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
||||
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -620,7 +620,7 @@ class ApplicationsController extends Controller
|
||||
|
||||
private function create_application(Request $request, $type)
|
||||
{
|
||||
$allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths'];
|
||||
$allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths'];
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
@@ -708,7 +708,7 @@ class ApplicationsController extends Controller
|
||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||
return $return;
|
||||
}
|
||||
$application = new Application();
|
||||
$application = new Application;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$application->fill($request->all());
|
||||
@@ -732,8 +732,10 @@ class ApplicationsController extends Controller
|
||||
$application->environment_id = $environment->id;
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
}
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
@@ -794,7 +796,7 @@ class ApplicationsController extends Controller
|
||||
if (str($gitRepository)->startsWith('http') || str($gitRepository)->contains('github.com')) {
|
||||
$gitRepository = str($gitRepository)->replace('https://', '')->replace('http://', '')->replace('github.com/', '');
|
||||
}
|
||||
$application = new Application();
|
||||
$application = new Application;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$application->fill($request->all());
|
||||
@@ -826,8 +828,10 @@ class ApplicationsController extends Controller
|
||||
$application->source_id = $githubApp->id;
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
}
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
@@ -886,7 +890,7 @@ class ApplicationsController extends Controller
|
||||
return response()->json(['message' => 'Private Key not found.'], 404);
|
||||
}
|
||||
|
||||
$application = new Application();
|
||||
$application = new Application;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$application->fill($request->all());
|
||||
@@ -916,8 +920,10 @@ class ApplicationsController extends Controller
|
||||
$application->environment_id = $environment->id;
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
}
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
@@ -982,7 +988,7 @@ class ApplicationsController extends Controller
|
||||
$port = 80;
|
||||
}
|
||||
|
||||
$application = new Application();
|
||||
$application = new Application;
|
||||
$application->fill($request->all());
|
||||
$application->fqdn = $fqdn;
|
||||
$application->ports_exposes = $port;
|
||||
@@ -996,8 +1002,10 @@ class ApplicationsController extends Controller
|
||||
$application->git_branch = 'main';
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
}
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
@@ -1038,7 +1046,7 @@ class ApplicationsController extends Controller
|
||||
if (! $request->docker_registry_image_tag) {
|
||||
$request->offsetSet('docker_registry_image_tag', 'latest');
|
||||
}
|
||||
$application = new Application();
|
||||
$application = new Application;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$application->fill($request->all());
|
||||
@@ -1052,8 +1060,10 @@ class ApplicationsController extends Controller
|
||||
$application->git_branch = 'main';
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
}
|
||||
$application->isConfigurationChanged(true);
|
||||
|
||||
if ($instantDeploy) {
|
||||
@@ -1130,7 +1140,7 @@ class ApplicationsController extends Controller
|
||||
// return $this->dispatch('error', "Invalid docker-compose file.\n$isValid");
|
||||
// }
|
||||
|
||||
$service = new Service();
|
||||
$service = new Service;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
$service->fill($request->all());
|
||||
|
||||
@@ -1494,8 +1504,10 @@ class ApplicationsController extends Controller
|
||||
$fqdn = str($fqdn)->replaceEnd(',', '')->trim();
|
||||
$fqdn = str($fqdn)->replaceStart(',', '')->trim();
|
||||
$application->fqdn = $fqdn;
|
||||
$customLabels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->custom_labels = base64_encode($customLabels);
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
$customLabels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->custom_labels = base64_encode($customLabels);
|
||||
}
|
||||
$request->offsetUnset('domains');
|
||||
}
|
||||
|
||||
|
||||
@@ -135,8 +135,14 @@ class ProjectController extends Controller
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
$project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
|
||||
$environment = $project->environments()->whereName(request()->environment_name)->first();
|
||||
if (! $request->uuid) {
|
||||
return response()->json(['message' => 'Uuid is required.'], 422);
|
||||
}
|
||||
if (! $request->environment_name) {
|
||||
return response()->json(['message' => 'Environment name is required.'], 422);
|
||||
}
|
||||
$project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first();
|
||||
$environment = $project->environments()->whereName($request->environment_name)->first();
|
||||
if (! $environment) {
|
||||
return response()->json(['message' => 'Environment not found.'], 404);
|
||||
}
|
||||
@@ -144,4 +150,276 @@ class ProjectController extends Controller
|
||||
|
||||
return response()->json(serializeApiResponse($environment));
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
summary: 'Create',
|
||||
description: 'Create Project.',
|
||||
path: '/projects',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Projects'],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
description: 'Project created.',
|
||||
content: new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'uuid' => ['type' => 'string', 'description' => 'The name of the project.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The description of the project.'],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 201,
|
||||
description: 'Project created.',
|
||||
content: [
|
||||
new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the project.'],
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 404,
|
||||
ref: '#/components/responses/404',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function create_project(Request $request)
|
||||
{
|
||||
$allowedFields = ['name', 'description'];
|
||||
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
|
||||
$return = validateIncomingRequest($request);
|
||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||
return $return;
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'name' => 'string|max:255|required',
|
||||
'description' => 'string|nullable',
|
||||
]);
|
||||
|
||||
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
||||
if ($validator->fails() || ! empty($extraFields)) {
|
||||
$errors = $validator->errors();
|
||||
if (! empty($extraFields)) {
|
||||
foreach ($extraFields as $field) {
|
||||
$errors->add($field, 'This field is not allowed.');
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => $errors,
|
||||
], 422);
|
||||
}
|
||||
|
||||
$project = Project::create([
|
||||
'name' => $request->name,
|
||||
'description' => $request->description,
|
||||
'team_id' => $teamId,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'uuid' => $project->uuid,
|
||||
])->setStatusCode(201);
|
||||
}
|
||||
|
||||
#[OA\Patch(
|
||||
summary: 'Update',
|
||||
description: 'Update Project.',
|
||||
path: '/projects/{uuid}',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Projects'],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
description: 'Project updated.',
|
||||
content: new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'name' => ['type' => 'string', 'description' => 'The name of the project.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The description of the project.'],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 201,
|
||||
description: 'Project updated.',
|
||||
content: [
|
||||
new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'uuid' => ['type' => 'string', 'example' => 'og888os'],
|
||||
'name' => ['type' => 'string', 'example' => 'Project Name'],
|
||||
'description' => ['type' => 'string', 'example' => 'Project Description'],
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 404,
|
||||
ref: '#/components/responses/404',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function update_project(Request $request)
|
||||
{
|
||||
$allowedFields = ['name', 'description'];
|
||||
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
|
||||
$return = validateIncomingRequest($request);
|
||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||
return $return;
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'name' => 'string|max:255|nullable',
|
||||
'description' => 'string|nullable',
|
||||
]);
|
||||
|
||||
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
||||
if ($validator->fails() || ! empty($extraFields)) {
|
||||
$errors = $validator->errors();
|
||||
if (! empty($extraFields)) {
|
||||
foreach ($extraFields as $field) {
|
||||
$errors->add($field, 'This field is not allowed.');
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => $errors,
|
||||
], 422);
|
||||
}
|
||||
$uuid = $request->uuid;
|
||||
if (! $uuid) {
|
||||
return response()->json(['message' => 'Uuid is required.'], 422);
|
||||
}
|
||||
|
||||
$project = Project::whereTeamId($teamId)->whereUuid($uuid)->first();
|
||||
if (! $project) {
|
||||
return response()->json(['message' => 'Project not found.'], 404);
|
||||
}
|
||||
|
||||
$project->update($request->only($allowedFields));
|
||||
|
||||
return response()->json([
|
||||
'uuid' => $project->uuid,
|
||||
'name' => $project->name,
|
||||
'description' => $project->description,
|
||||
])->setStatusCode(201);
|
||||
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
summary: 'Delete',
|
||||
description: 'Delete project by UUID.',
|
||||
path: '/projects/{uuid}',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Projects'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'uuid',
|
||||
in: 'path',
|
||||
description: 'UUID of the application.',
|
||||
required: true,
|
||||
schema: new OA\Schema(
|
||||
type: 'string',
|
||||
format: 'uuid',
|
||||
)
|
||||
),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'Project deleted.',
|
||||
content: [
|
||||
new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'message' => ['type' => 'string', 'example' => 'Project deleted.'],
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 404,
|
||||
ref: '#/components/responses/404',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function delete_project(Request $request)
|
||||
{
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
|
||||
if (! $request->uuid) {
|
||||
return response()->json(['message' => 'Uuid is required.'], 422);
|
||||
}
|
||||
$project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first();
|
||||
if (! $project) {
|
||||
return response()->json(['message' => 'Project not found.'], 404);
|
||||
}
|
||||
if ($project->resource_count() > 0) {
|
||||
return response()->json(['message' => 'Project has resources, so it cannot be deleted.'], 400);
|
||||
}
|
||||
|
||||
$project->delete();
|
||||
|
||||
return response()->json(['message' => 'Project deleted.']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Server\ValidateServer;
|
||||
use App\Enums\ProxyStatus;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Application;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server as ModelsServer;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -75,7 +79,7 @@ class ServersController extends Controller
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
$servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) {
|
||||
$servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port', 'description')->get()->load(['settings'])->map(function ($server) {
|
||||
$server['is_reachable'] = $server->settings->is_reachable;
|
||||
$server['is_usable'] = $server->settings->is_usable;
|
||||
|
||||
@@ -392,4 +396,390 @@ class ServersController extends Controller
|
||||
|
||||
return response()->json(serializeApiResponse($domains));
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
summary: 'Create',
|
||||
description: 'Create Server.',
|
||||
path: '/servers',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Servers'],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
description: 'Server created.',
|
||||
content: new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'name' => ['type' => 'string', 'example' => 'My Server', 'description' => 'The name of the server.'],
|
||||
'description' => ['type' => 'string', 'example' => 'My Server Description', 'description' => 'The description of the server.'],
|
||||
'ip' => ['type' => 'string', 'example' => '127.0.0.1', 'description' => 'The IP of the server.'],
|
||||
'port' => ['type' => 'integer', 'example' => 22, 'description' => 'The port of the server.'],
|
||||
'user' => ['type' => 'string', 'example' => 'root', 'description' => 'The user of the server.'],
|
||||
'private_key_uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the private key.'],
|
||||
'is_build_server' => ['type' => 'boolean', 'example' => false, 'description' => 'Is build server.'],
|
||||
'instant_validate' => ['type' => 'boolean', 'example' => false, 'description' => 'Instant validate.'],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 201,
|
||||
description: 'Server created.',
|
||||
content: [
|
||||
new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the server.'],
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 404,
|
||||
ref: '#/components/responses/404',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function create_server(Request $request)
|
||||
{
|
||||
$allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate'];
|
||||
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
|
||||
$return = validateIncomingRequest($request);
|
||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||
return $return;
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'name' => 'string|max:255',
|
||||
'description' => 'string|nullable',
|
||||
'ip' => 'string|required',
|
||||
'port' => 'integer|nullable',
|
||||
'private_key_uuid' => 'string|required',
|
||||
'user' => 'string|nullable',
|
||||
'is_build_server' => 'boolean|nullable',
|
||||
'instant_validate' => 'boolean|nullable',
|
||||
]);
|
||||
|
||||
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
||||
if ($validator->fails() || ! empty($extraFields)) {
|
||||
$errors = $validator->errors();
|
||||
if (! empty($extraFields)) {
|
||||
foreach ($extraFields as $field) {
|
||||
$errors->add($field, 'This field is not allowed.');
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => $errors,
|
||||
], 422);
|
||||
}
|
||||
if (! $request->name) {
|
||||
$request->offsetSet('name', generate_random_name());
|
||||
}
|
||||
if (! $request->user) {
|
||||
$request->offsetSet('user', 'root');
|
||||
}
|
||||
if (is_null($request->port)) {
|
||||
$request->offsetSet('port', 22);
|
||||
}
|
||||
if (is_null($request->is_build_server)) {
|
||||
$request->offsetSet('is_build_server', false);
|
||||
}
|
||||
if (is_null($request->instant_validate)) {
|
||||
$request->offsetSet('instant_validate', false);
|
||||
}
|
||||
$privateKey = PrivateKey::whereTeamId($teamId)->whereUuid($request->private_key_uuid)->first();
|
||||
if (! $privateKey) {
|
||||
return response()->json(['message' => 'Private key not found.'], 404);
|
||||
}
|
||||
$allServers = ModelsServer::whereIp($request->ip)->get();
|
||||
if ($allServers->count() > 0) {
|
||||
return response()->json(['message' => 'Server with this IP already exists.'], 400);
|
||||
}
|
||||
|
||||
$server = ModelsServer::create([
|
||||
'name' => $request->name,
|
||||
'description' => $request->description,
|
||||
'ip' => $request->ip,
|
||||
'port' => $request->port,
|
||||
'user' => $request->user,
|
||||
'private_key_id' => $privateKey->id,
|
||||
'team_id' => $teamId,
|
||||
'proxy' => [
|
||||
'type' => ProxyTypes::TRAEFIK_V2->value,
|
||||
'status' => ProxyStatus::EXITED->value,
|
||||
],
|
||||
]);
|
||||
$server->settings()->update([
|
||||
'is_build_server' => $request->is_build_server,
|
||||
]);
|
||||
if ($request->instant_validate) {
|
||||
ValidateServer::dispatch($server);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'uuid' => $server->uuid,
|
||||
])->setStatusCode(201);
|
||||
}
|
||||
|
||||
#[OA\Patch(
|
||||
summary: 'Update',
|
||||
description: 'Update Server.',
|
||||
path: '/servers/{uuid}',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Servers'],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
description: 'Server updated.',
|
||||
content: new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'name' => ['type' => 'string', 'description' => 'The name of the server.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The description of the server.'],
|
||||
'ip' => ['type' => 'string', 'description' => 'The IP of the server.'],
|
||||
'port' => ['type' => 'integer', 'description' => 'The port of the server.'],
|
||||
'user' => ['type' => 'string', 'description' => 'The user of the server.'],
|
||||
'private_key_uuid' => ['type' => 'string', 'description' => 'The UUID of the private key.'],
|
||||
'is_build_server' => ['type' => 'boolean', 'description' => 'Is build server.'],
|
||||
'instant_validate' => ['type' => 'boolean', 'description' => 'Instant validate.'],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 201,
|
||||
description: 'Server updated.',
|
||||
content: [
|
||||
new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'array',
|
||||
items: new OA\Items(ref: '#/components/schemas/Server')
|
||||
)
|
||||
),
|
||||
]),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 404,
|
||||
ref: '#/components/responses/404',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function update_server(Request $request)
|
||||
{
|
||||
$allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate'];
|
||||
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
|
||||
$return = validateIncomingRequest($request);
|
||||
if ($return instanceof \Illuminate\Http\JsonResponse) {
|
||||
return $return;
|
||||
}
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'name' => 'string|max:255|nullable',
|
||||
'description' => 'string|nullable',
|
||||
'ip' => 'string|nullable',
|
||||
'port' => 'integer|nullable',
|
||||
'private_key_uuid' => 'string|nullable',
|
||||
'user' => 'string|nullable',
|
||||
'is_build_server' => 'boolean|nullable',
|
||||
'instant_validate' => 'boolean|nullable',
|
||||
]);
|
||||
|
||||
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
||||
if ($validator->fails() || ! empty($extraFields)) {
|
||||
$errors = $validator->errors();
|
||||
if (! empty($extraFields)) {
|
||||
foreach ($extraFields as $field) {
|
||||
$errors->add($field, 'This field is not allowed.');
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => $errors,
|
||||
], 422);
|
||||
}
|
||||
$server = ModelsServer::whereTeamId($teamId)->whereUuid($request->uuid)->first();
|
||||
if (! $server) {
|
||||
return response()->json(['message' => 'Server not found.'], 404);
|
||||
}
|
||||
$server->update($request->only(['name', 'description', 'ip', 'port', 'user']));
|
||||
if ($request->is_build_server) {
|
||||
$server->settings()->update([
|
||||
'is_build_server' => $request->is_build_server,
|
||||
]);
|
||||
}
|
||||
if ($request->instant_validate) {
|
||||
ValidateServer::dispatch($server);
|
||||
}
|
||||
|
||||
return response()->json(serializeApiResponse($server))->setStatusCode(201);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
summary: 'Delete',
|
||||
description: 'Delete server by UUID.',
|
||||
path: '/servers/{uuid}',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Servers'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'uuid',
|
||||
in: 'path',
|
||||
description: 'UUID of the server.',
|
||||
required: true,
|
||||
schema: new OA\Schema(
|
||||
type: 'string',
|
||||
format: 'uuid',
|
||||
)
|
||||
),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'Server deleted.',
|
||||
content: [
|
||||
new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'message' => ['type' => 'string', 'example' => 'Server deleted.'],
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 404,
|
||||
ref: '#/components/responses/404',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function delete_server(Request $request)
|
||||
{
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
|
||||
if (! $request->uuid) {
|
||||
return response()->json(['message' => 'Uuid is required.'], 422);
|
||||
}
|
||||
$server = ModelsServer::whereTeamId($teamId)->whereUuid($request->uuid)->first();
|
||||
|
||||
if (! $server) {
|
||||
return response()->json(['message' => 'Server not found.'], 404);
|
||||
}
|
||||
if ($server->definedResources()->count() > 0) {
|
||||
return response()->json(['message' => 'Server has resources, so you need to delete them before.'], 400);
|
||||
}
|
||||
$server->delete();
|
||||
|
||||
return response()->json(['message' => 'Server deleted.']);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
summary: 'Validate',
|
||||
description: 'Validate server by UUID.',
|
||||
path: '/servers/{uuid}/validate',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Servers'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server UUID', schema: new OA\Schema(type: 'integer')),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 201,
|
||||
description: 'Server validation started.',
|
||||
content: [
|
||||
new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'message' => ['type' => 'string', 'example' => 'Validation started.'],
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 404,
|
||||
ref: '#/components/responses/404',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function validate_server(Request $request)
|
||||
{
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
|
||||
if (! $request->uuid) {
|
||||
return response()->json(['message' => 'Uuid is required.'], 422);
|
||||
}
|
||||
$server = ModelsServer::whereTeamId($teamId)->whereUuid($request->uuid)->first();
|
||||
|
||||
if (! $server) {
|
||||
return response()->json(['message' => 'Server not found.'], 404);
|
||||
}
|
||||
ValidateServer::dispatch($server);
|
||||
|
||||
return response()->json(['message' => 'Validation started.']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class UploadController extends BaseController
|
||||
$receiver = new FileReceiver('file', $request, HandlerFactory::classFromRequest($request));
|
||||
|
||||
if ($receiver->isUploaded() === false) {
|
||||
throw new UploadMissingFileException();
|
||||
throw new UploadMissingFileException;
|
||||
}
|
||||
|
||||
$save = $receiver->receive();
|
||||
|
||||
@@ -340,7 +340,6 @@ class Github extends Controller
|
||||
return response("Nothing to do. No applications found with branch '$base_branch'.");
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($applications as $application) {
|
||||
$isFunctional = $application->destination->server->isFunctional();
|
||||
if (! $isFunctional) {
|
||||
@@ -432,8 +431,13 @@ class Github extends Controller
|
||||
if ($action === 'closed' || $action === 'close') {
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if ($found) {
|
||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||
$containers = getCurrentApplicationContainerStatus($application->destination->server, $application->id, $pull_request_id);
|
||||
if ($containers->isNotEmpty()) {
|
||||
$containers->each(function ($container) use ($application) {
|
||||
$container_name = data_get($container, 'Names');
|
||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||
});
|
||||
}
|
||||
|
||||
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
|
||||
$found->delete();
|
||||
|
||||
@@ -157,6 +157,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private ?string $coolify_variables = null;
|
||||
|
||||
private bool $preserveRepository = true;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(int $application_deployment_queue_id)
|
||||
@@ -187,6 +189,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->server = $this->mainServer = $this->destination->server;
|
||||
$this->serverUser = $this->server->user;
|
||||
$this->is_this_additional_server = $this->application->additional_servers()->wherePivot('server_id', $this->server->id)->count() > 0;
|
||||
$this->preserveRepository = $this->application->settings->is_preserve_repository_enabled;
|
||||
|
||||
$this->basedir = $this->application->generateBaseDir($this->deployment_uuid);
|
||||
$this->workdir = "{$this->basedir}".rtrim($this->application->base_directory, '/');
|
||||
@@ -462,7 +465,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->env_filename) {
|
||||
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
|
||||
}
|
||||
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build";
|
||||
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build --pull";
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, $command), 'hidden' => true],
|
||||
);
|
||||
@@ -487,10 +490,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
// Start compose file
|
||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||
if ($this->docker_compose_custom_start_command) {
|
||||
$this->write_deployment_configurations();
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "cd {$this->workdir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true],
|
||||
);
|
||||
$this->write_deployment_configurations();
|
||||
} else {
|
||||
$this->write_deployment_configurations();
|
||||
$server_workdir = $this->application->workdir();
|
||||
@@ -507,20 +510,21 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
} else {
|
||||
if ($this->docker_compose_custom_start_command) {
|
||||
$this->write_deployment_configurations();
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), 'hidden' => true],
|
||||
);
|
||||
$this->write_deployment_configurations();
|
||||
} else {
|
||||
$command = "{$this->coolify_variables} docker compose";
|
||||
if ($this->env_filename) {
|
||||
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
|
||||
}
|
||||
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
|
||||
|
||||
$this->write_deployment_configurations();
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, $command), 'hidden' => true],
|
||||
);
|
||||
$this->write_deployment_configurations();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,6 +609,28 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function write_deployment_configurations()
|
||||
{
|
||||
if ($this->preserveRepository) {
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->original_server;
|
||||
}
|
||||
if (str($this->configuration_dir)->isNotEmpty()) {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
"mkdir -p $this->configuration_dir",
|
||||
],
|
||||
// removing this now as we are using docker cp
|
||||
// [
|
||||
// "rm -rf $this->configuration_dir/{*,.*}",
|
||||
// ],
|
||||
[
|
||||
"docker cp {$this->deployment_uuid}:{$this->workdir}/. {$this->configuration_dir}",
|
||||
],
|
||||
);
|
||||
}
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->build_server;
|
||||
}
|
||||
}
|
||||
if (isset($this->docker_compose_base64)) {
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->original_server;
|
||||
@@ -965,7 +991,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
|
||||
}
|
||||
if (! $nixpacks_php_fallback_path) {
|
||||
$nixpacks_php_fallback_path = new EnvironmentVariable();
|
||||
$nixpacks_php_fallback_path = new EnvironmentVariable;
|
||||
$nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH';
|
||||
$nixpacks_php_fallback_path->value = '/index.php';
|
||||
$nixpacks_php_fallback_path->is_build_time = false;
|
||||
@@ -973,7 +999,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$nixpacks_php_fallback_path->save();
|
||||
}
|
||||
if (! $nixpacks_php_root_dir) {
|
||||
$nixpacks_php_root_dir = new EnvironmentVariable();
|
||||
$nixpacks_php_root_dir = new EnvironmentVariable;
|
||||
$nixpacks_php_root_dir->key = 'NIXPACKS_PHP_ROOT_DIR';
|
||||
$nixpacks_php_root_dir->value = '/app/public';
|
||||
$nixpacks_php_root_dir->is_build_time = false;
|
||||
@@ -1007,7 +1033,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
|
||||
$this->application_deployment_queue->addLogEntry('Consistent container name feature enabled, rolling update is not supported.');
|
||||
}
|
||||
if (isset($this->application->settings->custom_internal_name)) {
|
||||
if (str($this->application->settings->custom_internal_name)->isNotEmpty()) {
|
||||
$this->application_deployment_queue->addLogEntry('Custom internal name is set, rolling update is not supported.');
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
@@ -1247,7 +1273,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
continue;
|
||||
}
|
||||
// ray('Deploying to additional destination: ', $server->name);
|
||||
$deployment_uuid = new Cuid2();
|
||||
$deployment_uuid = new Cuid2;
|
||||
queue_application_deployment(
|
||||
deployment_uuid: $deployment_uuid,
|
||||
application: $this->application,
|
||||
@@ -1421,6 +1447,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
$this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT);
|
||||
$this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true);
|
||||
if ($this->nixpacks_type === 'rust') {
|
||||
// temporary: disable healthcheck for rust because the start phase does not have curl/wget
|
||||
$this->application->health_check_enabled = false;
|
||||
$this->application->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1523,7 +1554,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->application->custom_labels = base64_encode($labels->implode("\n"));
|
||||
$this->application->save();
|
||||
} else {
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||
if (! $this->application->settings->is_container_label_readonly_enabled) {
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||
}
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||
@@ -1624,12 +1657,15 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
],
|
||||
],
|
||||
];
|
||||
if (data_get($this->application, 'swarm_placement_constraints')) {
|
||||
$swarm_placement_constraints = Yaml::parse(base64_decode(data_get($this->application, 'swarm_placement_constraints')));
|
||||
$docker_compose['services'][$this->container_name]['deploy'] = array_merge(
|
||||
$docker_compose['services'][$this->container_name]['deploy'],
|
||||
$swarm_placement_constraints
|
||||
);
|
||||
}
|
||||
if (data_get($this->application, 'settings.is_swarm_only_worker_nodes')) {
|
||||
$docker_compose['services'][$this->container_name]['deploy']['placement'] = [
|
||||
'constraints' => [
|
||||
'node.role == worker',
|
||||
],
|
||||
];
|
||||
$docker_compose['services'][$this->container_name]['deploy']['placement']['constraints'][] = 'node.role == worker';
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
$docker_compose['services'][$this->container_name]['deploy']['replicas'] = 1;
|
||||
@@ -2028,23 +2064,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
}
|
||||
|
||||
private function build_by_compose_file()
|
||||
{
|
||||
$this->application_deployment_queue->addLogEntry('Pulling & building required images.');
|
||||
if ($this->application->build_pack === 'dockerimage') {
|
||||
$this->application_deployment_queue->addLogEntry('Pulling latest images from the registry.');
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} pull"), 'hidden' => true],
|
||||
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} build"), 'hidden' => true],
|
||||
);
|
||||
} else {
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), 'hidden' => true],
|
||||
);
|
||||
}
|
||||
$this->application_deployment_queue->addLogEntry('New images built.');
|
||||
}
|
||||
|
||||
private function start_by_compose_file()
|
||||
{
|
||||
if ($this->application->build_pack === 'dockerimage') {
|
||||
|
||||
@@ -40,7 +40,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
||||
switch ($this->resource->type()) {
|
||||
case 'application':
|
||||
$persistentStorages = $this->resource?->persistentStorages()?->get();
|
||||
StopApplication::run($this->resource);
|
||||
StopApplication::run($this->resource, previewDeployments: true);
|
||||
break;
|
||||
case 'standalone-postgresql':
|
||||
case 'standalone-redis':
|
||||
|
||||
@@ -12,7 +12,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use RuntimeException;
|
||||
|
||||
class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -20,47 +19,48 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
public $timeout = 300;
|
||||
|
||||
public ?int $usageBefore = null;
|
||||
public int|string|null $usageBefore = null;
|
||||
|
||||
public function __construct(public Server $server) {}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$isInprogress = false;
|
||||
$this->server->applications()->each(function ($application) use (&$isInprogress) {
|
||||
if ($application->isDeploymentInprogress()) {
|
||||
$isInprogress = true;
|
||||
// $isInprogress = false;
|
||||
// $this->server->applications()->each(function ($application) use (&$isInprogress) {
|
||||
// if ($application->isDeploymentInprogress()) {
|
||||
// $isInprogress = true;
|
||||
|
||||
return;
|
||||
}
|
||||
});
|
||||
// return;
|
||||
// }
|
||||
// });
|
||||
// if ($isInprogress) {
|
||||
// throw new RuntimeException('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...');
|
||||
// }
|
||||
if (! $this->server->isFunctional()) {
|
||||
return;
|
||||
}
|
||||
if ($this->server->settings->is_force_cleanup_enabled) {
|
||||
Log::info('DockerCleanupJob force cleanup on '.$this->server->name);
|
||||
CleanupDocker::run(server: $this->server, force: true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->usageBefore = $this->server->getDiskUsage();
|
||||
ray('Usage before: '.$this->usageBefore);
|
||||
if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) {
|
||||
ray('Cleaning up '.$this->server->name);
|
||||
CleanupDocker::run($this->server);
|
||||
CleanupDocker::run(server: $this->server, force: false);
|
||||
$usageAfter = $this->server->getDiskUsage();
|
||||
if ($usageAfter < $this->usageBefore) {
|
||||
$this->server->team?->notify(new DockerCleanup($this->server, 'Saved '.($this->usageBefore - $usageAfter).'% disk space.'));
|
||||
// ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||
// send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||
Log::info('DockerCleanupJob done: Saved '.($this->usageBefore - $usageAfter).'% disk space on '.$this->server->name);
|
||||
} else {
|
||||
Log::info('DockerCleanupJob failed to save disk space on '.$this->server->name);
|
||||
}
|
||||
} else {
|
||||
ray('No need to clean up '.$this->server->name);
|
||||
Log::info('No need to clean up '.$this->server->name);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class SendConfirmationForWaitlistJob implements ShouldBeEncrypted, ShouldQueue
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$confirmation_url = base_url().'/webhooks/waitlist/confirm?email='.$this->email.'&confirmation_code='.$this->uuid;
|
||||
$cancel_url = base_url().'/webhooks/waitlist/cancel?email='.$this->email.'&confirmation_code='.$this->uuid;
|
||||
$mail->view('emails.waitlist-confirmation',
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Notifications\Server\HighDiskUsage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@@ -44,7 +43,6 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
try {
|
||||
if ($this->server->isFunctional()) {
|
||||
$this->cleanup(notify: false);
|
||||
$this->remove_unnecessary_coolify_yaml();
|
||||
if ($this->server->isSentinelEnabled()) {
|
||||
$this->server->checkSentinel();
|
||||
@@ -56,45 +54,7 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
return handleError($e);
|
||||
}
|
||||
try {
|
||||
// $this->check_docker_engine();
|
||||
} catch (\Throwable $e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
private function check_docker_engine()
|
||||
{
|
||||
$version = instant_remote_process([
|
||||
'docker info',
|
||||
], $this->server, false);
|
||||
if (is_null($version)) {
|
||||
$os = instant_remote_process([
|
||||
'cat /etc/os-release | grep ^ID=',
|
||||
], $this->server, false);
|
||||
$os = str($os)->after('ID=')->trim();
|
||||
if ($os === 'ubuntu') {
|
||||
try {
|
||||
instant_remote_process([
|
||||
'systemctl start docker',
|
||||
], $this->server);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
|
||||
return handleError($e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
instant_remote_process([
|
||||
'service docker start',
|
||||
], $this->server);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function remove_unnecessary_coolify_yaml()
|
||||
@@ -108,28 +68,4 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
|
||||
], $this->server, false);
|
||||
}
|
||||
}
|
||||
|
||||
public function cleanup(bool $notify = false): void
|
||||
{
|
||||
$this->disk_usage = $this->server->getDiskUsage();
|
||||
if ($this->disk_usage >= $this->server->settings->cleanup_after_percentage) {
|
||||
if ($notify) {
|
||||
if ($this->server->high_disk_usage_notification_sent) {
|
||||
ray('high disk usage notification already sent');
|
||||
|
||||
return;
|
||||
} else {
|
||||
$this->server->high_disk_usage_notification_sent = true;
|
||||
$this->server->save();
|
||||
$this->server->team?->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage));
|
||||
}
|
||||
} else {
|
||||
DockerCleanupJob::dispatchSync($this->server);
|
||||
$this->cleanup(notify: true);
|
||||
}
|
||||
} else {
|
||||
$this->server->high_disk_usage_notification_sent = false;
|
||||
$this->server->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class SubscriptionInvoiceFailedJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
$session = getStripeCustomerPortalSession($this->team);
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->view('emails.subscription-invoice-failed', [
|
||||
'stripeCustomerPortal' => $session->url,
|
||||
]);
|
||||
|
||||
@@ -23,7 +23,7 @@ class SubscriptionTrialEndedJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
$session = getStripeCustomerPortalSession($this->team);
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject('Action required: You trial in Coolify Cloud ended.');
|
||||
$mail->view('emails.trial-ended', [
|
||||
'stripeCustomerPortal' => $session->url,
|
||||
|
||||
@@ -23,7 +23,7 @@ class SubscriptionTrialEndsSoonJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
$session = getStripeCustomerPortalSession($this->team);
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject('You trial in Coolify Cloud ends soon.');
|
||||
$mail->view('emails.trial-ends-soon', [
|
||||
'stripeCustomerPortal' => $session->url,
|
||||
|
||||
@@ -38,7 +38,7 @@ class MaintenanceModeDisabledNotification
|
||||
$class = "App\Http\Controllers\Webhook\\".ucfirst(str($endpoint)->before('::')->value());
|
||||
$method = str($endpoint)->after('::')->value();
|
||||
try {
|
||||
$instance = new $class();
|
||||
$instance = new $class;
|
||||
$instance->$method($request);
|
||||
} catch (\Throwable $th) {
|
||||
ray($th);
|
||||
|
||||
@@ -257,7 +257,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
$this->createdServer->settings->is_swarm_manager = $this->isSwarmManager;
|
||||
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
|
||||
$this->createdServer->settings->save();
|
||||
$this->createdServer->addInitialNetwork();
|
||||
$this->selectedExistingServer = $this->createdServer->id;
|
||||
$this->currentState = 'validate-server';
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class Help extends Component
|
||||
$this->rateLimit(3, 30);
|
||||
$this->validate();
|
||||
$debug = "Route: {$this->path}";
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->view(
|
||||
'emails.help',
|
||||
[
|
||||
|
||||
@@ -56,7 +56,7 @@ class Discord extends Component
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->team?->notify(new Test());
|
||||
$this->team?->notify(new Test);
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ class Telegram extends Component
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->team?->notify(new Test());
|
||||
$this->team?->notify(new Test);
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ class General extends Component
|
||||
'application.settings.is_static' => 'boolean|required',
|
||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||
'application.settings.is_container_label_escape_enabled' => 'boolean|required',
|
||||
'application.settings.is_container_label_readonly_enabled' => 'boolean|required',
|
||||
'application.settings.is_preserve_repository_enabled' => 'boolean|required',
|
||||
'application.watch_paths' => 'nullable',
|
||||
'application.redirect' => 'string|required',
|
||||
];
|
||||
@@ -119,6 +121,8 @@ class General extends Component
|
||||
'application.settings.is_static' => 'Is static',
|
||||
'application.settings.is_build_server_enabled' => 'Is build server enabled',
|
||||
'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled',
|
||||
'application.settings.is_container_label_readonly_enabled' => 'Is container label readonly',
|
||||
'application.settings.is_preserve_repository_enabled' => 'Is preserve repository enabled',
|
||||
'application.watch_paths' => 'Watch paths',
|
||||
'application.redirect' => 'Redirect',
|
||||
];
|
||||
@@ -143,7 +147,7 @@ class General extends Component
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
|
||||
$this->customLabels = $this->application->parseContainerLabels();
|
||||
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
||||
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE' && ! $this->application->settings->is_container_label_readonly_enabled) {
|
||||
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
$this->application->save();
|
||||
@@ -290,6 +294,9 @@ class General extends Component
|
||||
|
||||
public function resetDefaultLabels()
|
||||
{
|
||||
if ($this->application->settings->is_container_label_readonly_enabled) {
|
||||
return;
|
||||
}
|
||||
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
|
||||
@@ -350,7 +357,7 @@ class General extends Component
|
||||
$this->checkFqdns();
|
||||
|
||||
$this->application->save();
|
||||
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
||||
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE' && ! $this->application->settings->is_container_label_readonly_enabled) {
|
||||
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
$this->application->save();
|
||||
|
||||
@@ -53,6 +53,12 @@ class GithubPrivateRepository extends Component
|
||||
|
||||
public ?string $publish_directory = null;
|
||||
|
||||
// In case of docker compose
|
||||
public ?string $base_directory = null;
|
||||
|
||||
public ?string $docker_compose_location = '/docker-compose.yaml';
|
||||
// End of docker compose
|
||||
|
||||
protected int $page = 1;
|
||||
|
||||
public $build_pack = 'nixpacks';
|
||||
@@ -68,6 +74,16 @@ class GithubPrivateRepository extends Component
|
||||
$this->github_apps = GithubApp::private();
|
||||
}
|
||||
|
||||
public function updatedBaseDirectory()
|
||||
{
|
||||
if ($this->base_directory) {
|
||||
$this->base_directory = rtrim($this->base_directory, '/');
|
||||
if (! str($this->base_directory)->startsWith('/')) {
|
||||
$this->base_directory = '/'.$this->base_directory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedBuildPack()
|
||||
{
|
||||
if ($this->build_pack === 'nixpacks') {
|
||||
@@ -184,6 +200,10 @@ class GithubPrivateRepository extends Component
|
||||
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
|
||||
$application->health_check_enabled = false;
|
||||
}
|
||||
if ($this->build_pack === 'dockercompose') {
|
||||
$application['docker_compose_location'] = $this->docker_compose_location;
|
||||
$application['base_directory'] = $this->base_directory;
|
||||
}
|
||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||
$application->fqdn = $fqdn;
|
||||
|
||||
|
||||
@@ -33,6 +33,12 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
|
||||
public ?string $publish_directory = null;
|
||||
|
||||
// In case of docker compose
|
||||
public ?string $base_directory = null;
|
||||
|
||||
public ?string $docker_compose_location = '/docker-compose.yaml';
|
||||
// End of docker compose
|
||||
|
||||
public string $repository_url;
|
||||
|
||||
public string $branch;
|
||||
@@ -163,6 +169,10 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
|
||||
$application_init['health_check_enabled'] = false;
|
||||
}
|
||||
if ($this->build_pack === 'dockercompose') {
|
||||
$application_init['docker_compose_location'] = $this->docker_compose_location;
|
||||
$application_init['base_directory'] = $this->base_directory;
|
||||
}
|
||||
$application = Application::create($application_init);
|
||||
$application->settings->is_static = $this->is_static;
|
||||
$application->settings->save();
|
||||
|
||||
@@ -25,14 +25,20 @@ class PublicGitRepository extends Component
|
||||
|
||||
public $query;
|
||||
|
||||
public bool $branch_found = false;
|
||||
public bool $branchFound = false;
|
||||
|
||||
public string $selected_branch = 'main';
|
||||
public string $selectedBranch = 'main';
|
||||
|
||||
public bool $is_static = false;
|
||||
public bool $isStatic = false;
|
||||
|
||||
public ?string $publish_directory = null;
|
||||
|
||||
// In case of docker compose
|
||||
public ?string $base_directory = null;
|
||||
|
||||
public ?string $docker_compose_location = '/docker-compose.yaml';
|
||||
// End of docker compose
|
||||
|
||||
public string $git_branch = 'main';
|
||||
|
||||
public int $rate_limit_remaining = 0;
|
||||
@@ -56,17 +62,21 @@ class PublicGitRepository extends Component
|
||||
protected $rules = [
|
||||
'repository_url' => 'required|url',
|
||||
'port' => 'required|numeric',
|
||||
'is_static' => 'required|boolean',
|
||||
'isStatic' => 'required|boolean',
|
||||
'publish_directory' => 'nullable|string',
|
||||
'build_pack' => 'required|string',
|
||||
'base_directory' => 'nullable|string',
|
||||
'docker_compose_location' => 'nullable|string',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'repository_url' => 'repository',
|
||||
'port' => 'port',
|
||||
'is_static' => 'static',
|
||||
'isStatic' => 'static',
|
||||
'publish_directory' => 'publish directory',
|
||||
'build_pack' => 'build pack',
|
||||
'base_directory' => 'base directory',
|
||||
'docker_compose_location' => 'docker compose location',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
@@ -79,6 +89,16 @@ class PublicGitRepository extends Component
|
||||
$this->query = request()->query();
|
||||
}
|
||||
|
||||
public function updatedBaseDirectory()
|
||||
{
|
||||
if ($this->base_directory) {
|
||||
$this->base_directory = rtrim($this->base_directory, '/');
|
||||
if (! str($this->base_directory)->startsWith('/')) {
|
||||
$this->base_directory = '/'.$this->base_directory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedBuildPack()
|
||||
{
|
||||
if ($this->build_pack === 'nixpacks') {
|
||||
@@ -86,17 +106,17 @@ class PublicGitRepository extends Component
|
||||
$this->port = 3000;
|
||||
} elseif ($this->build_pack === 'static') {
|
||||
$this->show_is_static = false;
|
||||
$this->is_static = false;
|
||||
$this->isStatic = false;
|
||||
$this->port = 80;
|
||||
} else {
|
||||
$this->show_is_static = false;
|
||||
$this->is_static = false;
|
||||
$this->isStatic = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
if ($this->is_static) {
|
||||
if ($this->isStatic) {
|
||||
$this->port = 80;
|
||||
$this->publish_directory = '/dist';
|
||||
} else {
|
||||
@@ -106,12 +126,7 @@ class PublicGitRepository extends Component
|
||||
$this->dispatch('success', 'Application settings updated!');
|
||||
}
|
||||
|
||||
public function load_any_git()
|
||||
{
|
||||
$this->branch_found = true;
|
||||
}
|
||||
|
||||
public function load_branch()
|
||||
public function loadBranch()
|
||||
{
|
||||
try {
|
||||
if (str($this->repository_url)->startsWith('git@')) {
|
||||
@@ -135,15 +150,21 @@ class PublicGitRepository extends Component
|
||||
return handleError($e, $this);
|
||||
}
|
||||
try {
|
||||
$this->branch_found = false;
|
||||
$this->get_git_source();
|
||||
$this->get_branch();
|
||||
$this->selected_branch = $this->git_branch;
|
||||
$this->branchFound = false;
|
||||
$this->getGitSource();
|
||||
$this->getBranch();
|
||||
$this->selectedBranch = $this->git_branch;
|
||||
} catch (\Throwable $e) {
|
||||
if (! $this->branch_found && $this->git_branch == 'main') {
|
||||
if ($this->rate_limit_remaining == 0) {
|
||||
$this->selectedBranch = $this->git_branch;
|
||||
$this->branchFound = true;
|
||||
|
||||
return;
|
||||
}
|
||||
if (! $this->branchFound && $this->git_branch == 'main') {
|
||||
try {
|
||||
$this->git_branch = 'master';
|
||||
$this->get_branch();
|
||||
$this->getBranch();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -153,13 +174,16 @@ class PublicGitRepository extends Component
|
||||
}
|
||||
}
|
||||
|
||||
private function get_git_source()
|
||||
private function getGitSource()
|
||||
{
|
||||
$this->repository_url_parsed = Url::fromString($this->repository_url);
|
||||
$this->git_host = $this->repository_url_parsed->getHost();
|
||||
$this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2);
|
||||
$this->git_branch = $this->repository_url_parsed->getSegment(4) ?? 'main';
|
||||
|
||||
if ($this->repository_url_parsed->getSegment(3) === 'tree') {
|
||||
$this->git_branch = str($this->repository_url_parsed->getPath())->after('tree/')->value();
|
||||
} else {
|
||||
$this->git_branch = 'main';
|
||||
}
|
||||
if ($this->git_host == 'github.com') {
|
||||
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
||||
|
||||
@@ -169,17 +193,17 @@ class PublicGitRepository extends Component
|
||||
$this->git_source = 'other';
|
||||
}
|
||||
|
||||
private function get_branch()
|
||||
private function getBranch()
|
||||
{
|
||||
if ($this->git_source === 'other') {
|
||||
$this->branch_found = true;
|
||||
$this->branchFound = true;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->git_source->getMorphClass() === 'App\Models\GithubApp') {
|
||||
['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = githubApi(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}");
|
||||
$this->rate_limit_reset = Carbon::parse((int) $this->rate_limit_reset)->format('Y-M-d H:i:s');
|
||||
$this->branch_found = true;
|
||||
$this->branchFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,9 +285,13 @@ class PublicGitRepository extends Component
|
||||
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
|
||||
$application_init['health_check_enabled'] = false;
|
||||
}
|
||||
if ($this->build_pack === 'dockercompose') {
|
||||
$application_init['docker_compose_location'] = $this->docker_compose_location;
|
||||
$application_init['base_directory'] = $this->base_directory;
|
||||
}
|
||||
$application = Application::create($application_init);
|
||||
|
||||
$application->settings->is_static = $this->is_static;
|
||||
$application->settings->is_static = $this->isStatic;
|
||||
$application->settings->save();
|
||||
|
||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||
|
||||
@@ -62,11 +62,15 @@ class Navbar extends Component
|
||||
|
||||
public function checkDeployments()
|
||||
{
|
||||
$activity = Activity::where('properties->type_uuid', $this->service->uuid)->latest()->first();
|
||||
$status = data_get($activity, 'properties.status');
|
||||
if ($status === 'queued' || $status === 'in_progress') {
|
||||
$this->isDeploymentProgress = true;
|
||||
} else {
|
||||
try {
|
||||
$activity = Activity::where('properties->type_uuid', $this->service->uuid)->latest()->first();
|
||||
$status = data_get($activity, 'properties.status');
|
||||
if ($status === 'queued' || $status === 'in_progress') {
|
||||
$this->isDeploymentProgress = true;
|
||||
} else {
|
||||
$this->isDeploymentProgress = false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->isDeploymentProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ class All extends Component
|
||||
|
||||
continue;
|
||||
} else {
|
||||
$environment = new EnvironmentVariable();
|
||||
$environment = new EnvironmentVariable;
|
||||
$environment->key = $key;
|
||||
$environment->value = $variable;
|
||||
if (str($environment->value)->startsWith('{{') && str($environment->value)->endsWith('}}')) {
|
||||
@@ -209,7 +209,7 @@ class All extends Component
|
||||
|
||||
return;
|
||||
}
|
||||
$environment = new EnvironmentVariable();
|
||||
$environment = new EnvironmentVariable;
|
||||
$environment->key = $data['key'];
|
||||
$environment->value = $data['value'];
|
||||
$environment->is_build_time = $data['is_build_time'];
|
||||
|
||||
@@ -24,6 +24,7 @@ class Show extends Component
|
||||
public string $type;
|
||||
|
||||
protected $listeners = [
|
||||
'refresh' => 'refresh',
|
||||
'compose_loaded' => '$refresh',
|
||||
];
|
||||
|
||||
@@ -46,6 +47,12 @@ class Show extends Component
|
||||
'env.is_shown_once' => 'Shown Once',
|
||||
];
|
||||
|
||||
public function refresh()
|
||||
{
|
||||
$this->env->refresh();
|
||||
$this->checkEnvs();
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if ($this->env->getMorphClass() === 'App\Models\SharedEnvironmentVariable') {
|
||||
|
||||
@@ -43,7 +43,7 @@ class All extends Component
|
||||
public function submit($data)
|
||||
{
|
||||
try {
|
||||
$task = new ScheduledTask();
|
||||
$task = new ScheduledTask;
|
||||
$task->name = $data['name'];
|
||||
$task->command = $data['command'];
|
||||
$task->frequency = $data['frequency'];
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Livewire\Project\Shared\Storages;
|
||||
|
||||
use App\Models\LocalPersistentVolume;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
@@ -12,8 +11,6 @@ class Show extends Component
|
||||
|
||||
public bool $isReadOnly = false;
|
||||
|
||||
public ?string $modalId = null;
|
||||
|
||||
public bool $isFirst = true;
|
||||
|
||||
public bool $isService = false;
|
||||
@@ -32,11 +29,6 @@ class Show extends Component
|
||||
'host_path' => 'host',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->modalId = new Cuid2(7);
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
@@ -37,6 +37,7 @@ class Form extends Component
|
||||
'server.settings.is_swarm_manager' => 'required|boolean',
|
||||
'server.settings.is_swarm_worker' => 'required|boolean',
|
||||
'server.settings.is_build_server' => 'required|boolean',
|
||||
'server.settings.is_force_cleanup_enabled' => 'required|boolean',
|
||||
'server.settings.concurrent_builds' => 'required|integer|min:1',
|
||||
'server.settings.dynamic_timeout' => 'required|integer|min:1',
|
||||
'server.settings.is_metrics_enabled' => 'required|boolean',
|
||||
@@ -163,6 +164,9 @@ class Form extends Component
|
||||
|
||||
public function validateServer($install = true)
|
||||
{
|
||||
$this->server->update([
|
||||
'validation_logs' => null,
|
||||
]);
|
||||
$this->dispatch('init', $install);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,6 @@ class ByIp extends Component
|
||||
}
|
||||
$server->settings->is_build_server = $this->is_build_server;
|
||||
$server->settings->save();
|
||||
$server->addInitialNetwork();
|
||||
|
||||
return redirect()->route('server.show', $server->uuid);
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -50,7 +50,7 @@ class Deploy extends Component
|
||||
public function proxyStarted()
|
||||
{
|
||||
CheckProxy::run($this->server, true);
|
||||
$this->dispatch('success', 'Proxy started.');
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
}
|
||||
|
||||
public function proxyStatusUpdated()
|
||||
@@ -61,7 +61,7 @@ class Deploy extends Component
|
||||
public function restart()
|
||||
{
|
||||
try {
|
||||
$this->stop();
|
||||
$this->stop(forceStop: false);
|
||||
$this->dispatch('checkProxy');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -91,7 +91,7 @@ class Deploy extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function stop()
|
||||
public function stop(bool $forceStop = true)
|
||||
{
|
||||
try {
|
||||
if ($this->server->isSwarm()) {
|
||||
@@ -104,7 +104,7 @@ class Deploy extends Component
|
||||
], $this->server);
|
||||
}
|
||||
$this->server->proxy->status = 'exited';
|
||||
$this->server->proxy->force_stop = true;
|
||||
$this->server->proxy->force_stop = $forceStop;
|
||||
$this->server->save();
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -16,7 +16,10 @@ class Status extends Component
|
||||
|
||||
public int $numberOfPolls = 0;
|
||||
|
||||
protected $listeners = ['proxyStatusUpdated' => '$refresh', 'startProxyPolling'];
|
||||
protected $listeners = [
|
||||
'proxyStatusUpdated',
|
||||
'startProxyPolling',
|
||||
];
|
||||
|
||||
public function startProxyPolling()
|
||||
{
|
||||
|
||||
@@ -87,7 +87,10 @@ class ValidateAndInstall extends Component
|
||||
{
|
||||
['uptime' => $this->uptime, 'error' => $error] = $this->server->validateConnection();
|
||||
if (! $this->uptime) {
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: '.$error;
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br>Check this <a target="_blank" class="text-black underline dark:text-white" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br><div class="text-error">Error: '.$error.'</div>';
|
||||
$this->server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -99,6 +102,9 @@ class ValidateAndInstall extends Component
|
||||
$this->supported_os_type = $this->server->validateOS();
|
||||
if (! $this->supported_os_type) {
|
||||
$this->error = 'Server OS type is not supported. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$this->server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -113,6 +119,9 @@ class ValidateAndInstall extends Component
|
||||
if ($this->install) {
|
||||
if ($this->number_of_tries == $this->max_tries) {
|
||||
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$this->server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
|
||||
return;
|
||||
} else {
|
||||
@@ -126,6 +135,9 @@ class ValidateAndInstall extends Component
|
||||
}
|
||||
} else {
|
||||
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$this->server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -148,6 +160,9 @@ class ValidateAndInstall extends Component
|
||||
$this->dispatch('success', 'Server validated.');
|
||||
} else {
|
||||
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$this->server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class Create extends Component
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->storage = new S3Storage();
|
||||
$this->storage = new S3Storage;
|
||||
$this->storage->name = $this->name;
|
||||
$this->storage->description = $this->description ?? null;
|
||||
$this->storage->region = $this->region;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Tags;
|
||||
|
||||
use App\Http\Controllers\Api\Deploy;
|
||||
use App\Http\Controllers\Api\DeployController;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Url;
|
||||
@@ -51,11 +51,11 @@ class Index extends Component
|
||||
{
|
||||
try {
|
||||
$this->applications->each(function ($resource) {
|
||||
$deploy = new Deploy();
|
||||
$deploy = new DeployController;
|
||||
$deploy->deploy_resource($resource);
|
||||
});
|
||||
$this->services->each(function ($resource) {
|
||||
$deploy = new Deploy();
|
||||
$deploy = new DeployController;
|
||||
$deploy->deploy_resource($resource);
|
||||
});
|
||||
$this->dispatch('success', 'Mass deployment started.');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Tags;
|
||||
|
||||
use App\Http\Controllers\Api\Deploy;
|
||||
use App\Http\Controllers\Api\DeployController;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\Tag;
|
||||
use Livewire\Component;
|
||||
@@ -59,11 +59,11 @@ class Show extends Component
|
||||
try {
|
||||
$message = collect([]);
|
||||
$this->applications->each(function ($resource) use ($message) {
|
||||
$deploy = new Deploy();
|
||||
$deploy = new DeployController;
|
||||
$message->push($deploy->deploy_resource($resource));
|
||||
});
|
||||
$this->services->each(function ($resource) use ($message) {
|
||||
$deploy = new Deploy();
|
||||
$deploy = new DeployController;
|
||||
$message->push($deploy->deploy_resource($resource));
|
||||
});
|
||||
$this->dispatch('success', 'Mass deployment started.');
|
||||
|
||||
@@ -79,7 +79,7 @@ class InviteLink extends Component
|
||||
'via' => $sendEmail ? 'email' : 'link',
|
||||
]);
|
||||
if ($sendEmail) {
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->view('emails.invitation-link', [
|
||||
'team' => currentTeam()->name,
|
||||
'invitation_link' => $link,
|
||||
|
||||
@@ -1066,7 +1066,7 @@ class Application extends BaseModel
|
||||
if ($isInit && $this->docker_compose_raw) {
|
||||
return;
|
||||
}
|
||||
$uuid = new Cuid2();
|
||||
$uuid = new Cuid2;
|
||||
['commands' => $cloneCommand] = $this->generateGitImportCommands(deployment_uuid: $uuid, only_checkout: true, exec_in_docker: false, custom_base_dir: '.');
|
||||
$workdir = rtrim($this->base_directory, '/');
|
||||
$composeFile = $this->docker_compose_location;
|
||||
@@ -1081,45 +1081,55 @@ class Application extends BaseModel
|
||||
'git read-tree -mu HEAD',
|
||||
"cat .$workdir$composeFile",
|
||||
]);
|
||||
$composeFileContent = instant_remote_process($commands, $this->destination->server, false);
|
||||
if (! $composeFileContent) {
|
||||
try {
|
||||
$composeFileContent = instant_remote_process($commands, $this->destination->server);
|
||||
} catch (\Exception $e) {
|
||||
if (str($e->getMessage())->contains('No such file')) {
|
||||
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
}
|
||||
if (str($e->getMessage())->contains('fatal: repository') && str($e->getMessage())->contains('does not exist')) {
|
||||
if ($this->deploymentType() === 'deploy_key') {
|
||||
throw new \RuntimeException('Your deploy key does not have access to the repository. Please check your deploy key and try again.');
|
||||
}
|
||||
throw new \RuntimeException('Repository does not exist. Please check your repository URL and try again.');
|
||||
}
|
||||
throw new \RuntimeException($e->getMessage());
|
||||
} finally {
|
||||
$this->docker_compose_location = $initialDockerComposeLocation;
|
||||
$this->save();
|
||||
$commands = collect([
|
||||
"rm -rf /tmp/{$uuid}",
|
||||
]);
|
||||
instant_remote_process($commands, $this->destination->server, false);
|
||||
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
} else {
|
||||
}
|
||||
if ($composeFileContent) {
|
||||
$this->docker_compose_raw = $composeFileContent;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$commands = collect([
|
||||
"rm -rf /tmp/{$uuid}",
|
||||
]);
|
||||
instant_remote_process($commands, $this->destination->server, false);
|
||||
$parsedServices = $this->parseCompose();
|
||||
if ($this->docker_compose_domains) {
|
||||
$json = collect(json_decode($this->docker_compose_domains));
|
||||
$names = collect(data_get($parsedServices, 'services'))->keys()->toArray();
|
||||
$jsonNames = $json->keys()->toArray();
|
||||
$diff = array_diff($jsonNames, $names);
|
||||
$json = $json->filter(function ($value, $key) use ($diff) {
|
||||
return ! in_array($key, $diff);
|
||||
});
|
||||
if ($json) {
|
||||
$this->docker_compose_domains = json_encode($json);
|
||||
} else {
|
||||
$this->docker_compose_domains = null;
|
||||
$parsedServices = $this->parseCompose();
|
||||
if ($this->docker_compose_domains) {
|
||||
$json = collect(json_decode($this->docker_compose_domains));
|
||||
$names = collect(data_get($parsedServices, 'services'))->keys()->toArray();
|
||||
$jsonNames = $json->keys()->toArray();
|
||||
$diff = array_diff($jsonNames, $names);
|
||||
$json = $json->filter(function ($value, $key) use ($diff) {
|
||||
return ! in_array($key, $diff);
|
||||
});
|
||||
if ($json) {
|
||||
$this->docker_compose_domains = json_encode($json);
|
||||
} else {
|
||||
$this->docker_compose_domains = null;
|
||||
}
|
||||
$this->save();
|
||||
}
|
||||
$this->save();
|
||||
|
||||
return [
|
||||
'parsedServices' => $parsedServices,
|
||||
'initialDockerComposeLocation' => $this->docker_compose_location,
|
||||
];
|
||||
} else {
|
||||
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
}
|
||||
|
||||
return [
|
||||
'parsedServices' => $parsedServices,
|
||||
'initialDockerComposeLocation' => $this->docker_compose_location,
|
||||
];
|
||||
}
|
||||
|
||||
public function parseContainerLabels(?ApplicationPreview $preview = null)
|
||||
|
||||
@@ -52,7 +52,7 @@ class EnvironmentVariable extends Model
|
||||
{
|
||||
static::creating(function (Model $model) {
|
||||
if (! $model->uuid) {
|
||||
$model->uuid = (string) new Cuid2();
|
||||
$model->uuid = (string) new Cuid2;
|
||||
}
|
||||
});
|
||||
static::created(function (EnvironmentVariable $environment_variable) {
|
||||
|
||||
@@ -79,23 +79,29 @@ class LocalFileVolume extends BaseModel
|
||||
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
|
||||
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
|
||||
if ($isFile == 'OK' && $fileVolume->is_directory) {
|
||||
$fileVolume->is_directory = false;
|
||||
$fileVolume->save();
|
||||
throw new \Exception('The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.');
|
||||
} elseif ($isDir == 'OK' && ! $fileVolume->is_directory) {
|
||||
$fileVolume->is_directory = true;
|
||||
$fileVolume->save();
|
||||
throw new \Exception('The following file is a directory on the server, but you are trying to mark it as a file. <br><br>Please delete the directory on the server or mark it as directory.');
|
||||
}
|
||||
if (! $fileVolume->is_directory && $isDir == 'NOK') {
|
||||
if ($isDir == 'NOK' && ! $fileVolume->is_directory) {
|
||||
$chmod = data_get($fileVolume, 'chmod');
|
||||
$chown = data_get($fileVolume, 'chown');
|
||||
if ($content) {
|
||||
$content = base64_encode($content);
|
||||
$chmod = $fileVolume->chmod;
|
||||
$chown = $fileVolume->chown;
|
||||
$commands->push("echo '$content' | base64 -d | tee $path > /dev/null");
|
||||
$commands->push("chmod +x $path");
|
||||
if ($chown) {
|
||||
$commands->push("chown $chown $path");
|
||||
}
|
||||
if ($chmod) {
|
||||
$commands->push("chmod $chmod $path");
|
||||
}
|
||||
} else {
|
||||
$commands->push("touch $path");
|
||||
}
|
||||
$commands->push("chmod +x $path");
|
||||
if ($chown) {
|
||||
$commands->push("chown $chown $path");
|
||||
}
|
||||
if ($chmod) {
|
||||
$commands->push("chmod $chmod $path");
|
||||
}
|
||||
} elseif ($isDir == 'NOK' && $fileVolume->is_directory) {
|
||||
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
|
||||
|
||||
@@ -122,7 +122,7 @@ class Project extends BaseModel
|
||||
|
||||
public function resource_count()
|
||||
{
|
||||
return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count() + $this->services()->count() + $this->clickhouses()->count();
|
||||
return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count() + $this->clickhouses()->count() + $this->services()->count();
|
||||
}
|
||||
|
||||
public function databases()
|
||||
|
||||
@@ -50,7 +50,7 @@ class S3Storage extends BaseModel
|
||||
} catch (\Throwable $e) {
|
||||
$this->is_usable = false;
|
||||
if ($this->unusable_email_sent === false && is_transactional_emails_active()) {
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject('Coolify: S3 Storage Connection Error');
|
||||
$mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('storage.show', ['storage_uuid' => $this->uuid])]);
|
||||
$users = collect([]);
|
||||
|
||||
@@ -19,85 +19,23 @@ use Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
#[OA\Schema(
|
||||
description: 'Application model',
|
||||
description: 'Server model',
|
||||
type: 'object',
|
||||
properties: [
|
||||
'id' => ['type' => 'integer'],
|
||||
'repository_project_id' => ['type' => 'integer', 'nullable' => true],
|
||||
'uuid' => ['type' => 'string'],
|
||||
'name' => ['type' => 'string'],
|
||||
'fqdn' => ['type' => 'string'],
|
||||
'config_hash' => ['type' => 'string'],
|
||||
'git_repository' => ['type' => 'string'],
|
||||
'git_branch' => ['type' => 'string'],
|
||||
'git_commit_sha' => ['type' => 'string'],
|
||||
'git_full_url' => ['type' => 'string', 'nullable' => true],
|
||||
'docker_registry_image_name' => ['type' => 'string', 'nullable' => true],
|
||||
'docker_registry_image_tag' => ['type' => 'string', 'nullable' => true],
|
||||
'build_pack' => ['type' => 'string'],
|
||||
'static_image' => ['type' => 'string'],
|
||||
'install_command' => ['type' => 'string'],
|
||||
'build_command' => ['type' => 'string'],
|
||||
'start_command' => ['type' => 'string'],
|
||||
'ports_exposes' => ['type' => 'string'],
|
||||
'ports_mappings' => ['type' => 'string', 'nullable' => true],
|
||||
'base_directory' => ['type' => 'string'],
|
||||
'publish_directory' => ['type' => 'string'],
|
||||
'health_check_path' => ['type' => 'string'],
|
||||
'health_check_port' => ['type' => 'string', 'nullable' => true],
|
||||
'health_check_host' => ['type' => 'string'],
|
||||
'health_check_method' => ['type' => 'string'],
|
||||
'health_check_return_code' => ['type' => 'integer'],
|
||||
'health_check_scheme' => ['type' => 'string'],
|
||||
'health_check_response_text' => ['type' => 'string', 'nullable' => true],
|
||||
'health_check_interval' => ['type' => 'integer'],
|
||||
'health_check_timeout' => ['type' => 'integer'],
|
||||
'health_check_retries' => ['type' => 'integer'],
|
||||
'health_check_start_period' => ['type' => 'integer'],
|
||||
'limits_memory' => ['type' => 'string'],
|
||||
'limits_memory_swap' => ['type' => 'string'],
|
||||
'limits_memory_swappiness' => ['type' => 'integer'],
|
||||
'limits_memory_reservation' => ['type' => 'string'],
|
||||
'limits_cpus' => ['type' => 'string'],
|
||||
'limits_cpuset' => ['type' => 'string', 'nullable' => true],
|
||||
'limits_cpu_shares' => ['type' => 'integer'],
|
||||
'status' => ['type' => 'string'],
|
||||
'preview_url_template' => ['type' => 'string'],
|
||||
'destination_type' => ['type' => 'string'],
|
||||
'destination_id' => ['type' => 'integer'],
|
||||
'source_type' => ['type' => 'string'],
|
||||
'source_id' => ['type' => 'integer'],
|
||||
'private_key_id' => ['type' => 'integer', 'nullable' => true],
|
||||
'environment_id' => ['type' => 'integer'],
|
||||
'created_at' => ['type' => 'string', 'format' => 'date-time'],
|
||||
'updated_at' => ['type' => 'string', 'format' => 'date-time'],
|
||||
'description' => ['type' => 'string', 'nullable' => true],
|
||||
'dockerfile' => ['type' => 'string', 'nullable' => true],
|
||||
'health_check_enabled' => ['type' => 'boolean'],
|
||||
'dockerfile_location' => ['type' => 'string'],
|
||||
'custom_labels' => ['type' => 'string'],
|
||||
'dockerfile_target_build' => ['type' => 'string', 'nullable' => true],
|
||||
'manual_webhook_secret_github' => ['type' => 'string', 'nullable' => true],
|
||||
'manual_webhook_secret_gitlab' => ['type' => 'string', 'nullable' => true],
|
||||
'docker_compose_location' => ['type' => 'string'],
|
||||
'docker_compose' => ['type' => 'string', 'nullable' => true],
|
||||
'docker_compose_raw' => ['type' => 'string', 'nullable' => true],
|
||||
'docker_compose_domains' => ['type' => 'string', 'nullable' => true],
|
||||
'deleted_at' => ['type' => 'string', 'format' => 'date-time', 'nullable' => true],
|
||||
'docker_compose_custom_start_command' => ['type' => 'string', 'nullable' => true],
|
||||
'docker_compose_custom_build_command' => ['type' => 'string', 'nullable' => true],
|
||||
'swarm_replicas' => ['type' => 'integer'],
|
||||
'swarm_placement_constraints' => ['type' => 'string', 'nullable' => true],
|
||||
'manual_webhook_secret_bitbucket' => ['type' => 'string', 'nullable' => true],
|
||||
'custom_docker_run_options' => ['type' => 'string', 'nullable' => true],
|
||||
'post_deployment_command' => ['type' => 'string', 'nullable' => true],
|
||||
'post_deployment_command_container' => ['type' => 'string', 'nullable' => true],
|
||||
'pre_deployment_command' => ['type' => 'string', 'nullable' => true],
|
||||
'pre_deployment_command_container' => ['type' => 'string', 'nullable' => true],
|
||||
'watch_paths' => ['type' => 'string', 'nullable' => true],
|
||||
'custom_healthcheck_found' => ['type' => 'boolean'],
|
||||
'manual_webhook_secret_gitea' => ['type' => 'string', 'nullable' => true],
|
||||
'redirect' => ['type' => 'string'],
|
||||
'description' => ['type' => 'string'],
|
||||
'ip' => ['type' => 'string'],
|
||||
'user' => ['type' => 'string'],
|
||||
'port' => ['type' => 'integer'],
|
||||
'proxy' => ['type' => 'object'],
|
||||
'high_disk_usage_notification_sent' => ['type' => 'boolean'],
|
||||
'unreachable_notification_sent' => ['type' => 'boolean'],
|
||||
'unreachable_count' => ['type' => 'integer'],
|
||||
'validation_logs' => ['type' => 'string'],
|
||||
'log_drain_notification_sent' => ['type' => 'boolean'],
|
||||
'swarm_cluster' => ['type' => 'string'],
|
||||
]
|
||||
)]
|
||||
|
||||
@@ -123,6 +61,37 @@ class Server extends BaseModel
|
||||
ServerSetting::create([
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
if ($server->id === 0) {
|
||||
if ($server->isSwarm()) {
|
||||
SwarmDocker::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify-overlay',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if ($server->isSwarm()) {
|
||||
SwarmDocker::create([
|
||||
'name' => 'coolify-overlay',
|
||||
'network' => 'coolify-overlay',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
static::deleting(function ($server) {
|
||||
$server->destinations()->each(function ($destination) {
|
||||
@@ -176,41 +145,6 @@ class Server extends BaseModel
|
||||
return $this->hasOne(ServerSetting::class);
|
||||
}
|
||||
|
||||
public function addInitialNetwork()
|
||||
{
|
||||
if ($this->id === 0) {
|
||||
if ($this->isSwarm()) {
|
||||
SwarmDocker::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify-overlay',
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if ($this->isSwarm()) {
|
||||
SwarmDocker::create([
|
||||
'name' => 'coolify-overlay',
|
||||
'network' => 'coolify-overlay',
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setupDefault404Redirect()
|
||||
{
|
||||
$dynamic_conf_path = $this->proxyPath().'/dynamic';
|
||||
|
||||
@@ -24,6 +24,7 @@ use Symfony\Component\Yaml\Yaml;
|
||||
'destination_id' => ['type' => 'integer', 'description' => 'The unique identifier of the destination where the service is running.'],
|
||||
'connect_to_docker_network' => ['type' => 'boolean', 'description' => 'The flag to connect the service to the predefined Docker network.'],
|
||||
'is_container_label_escape_enabled' => ['type' => 'boolean', 'description' => 'The flag to enable the container label escape.'],
|
||||
'is_container_label_readonly_enabled' => ['type' => 'boolean', 'description' => 'The flag to enable the container label readonly.'],
|
||||
'config_hash' => ['type' => 'string', 'description' => 'The hash of the service configuration.'],
|
||||
'service_type' => ['type' => 'string', 'description' => 'The type of the service.'],
|
||||
'created_at' => ['type' => 'string', 'description' => 'The date and time when the service was created.'],
|
||||
|
||||
@@ -120,7 +120,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
|
||||
public function sendVerificationEmail()
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$url = Url::temporarySignedRoute(
|
||||
'verify.verify',
|
||||
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
|
||||
|
||||
@@ -53,7 +53,7 @@ class DeploymentFailed extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$pull_request_id = data_get($this->preview, 'pull_request_id', 0);
|
||||
$fqdn = $this->fqdn;
|
||||
if ($pull_request_id === 0) {
|
||||
|
||||
@@ -59,7 +59,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$pull_request_id = data_get($this->preview, 'pull_request_id', 0);
|
||||
$fqdn = $this->fqdn;
|
||||
if ($pull_request_id === 0) {
|
||||
|
||||
@@ -43,7 +43,7 @@ class StatusChanged extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$fqdn = $this->fqdn;
|
||||
$mail->subject("Coolify: {$this->resource_name} has been stopped");
|
||||
$mail->view('emails.application-status-changes', [
|
||||
|
||||
@@ -23,7 +23,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}");
|
||||
$mail->view('emails.container-restarted', [
|
||||
'containerName' => $this->name,
|
||||
|
||||
@@ -23,7 +23,7 @@ class ContainerStopped extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: A resource has been stopped unexpectedly on {$this->server->name}");
|
||||
$mail->view('emails.container-stopped', [
|
||||
'containerName' => $this->name,
|
||||
|
||||
@@ -33,7 +33,7 @@ class BackupFailed extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: [ACTION REQUIRED] Backup FAILED for {$this->database->name}");
|
||||
$mail->view('emails.backup-failed', [
|
||||
'name' => $this->name,
|
||||
|
||||
@@ -33,7 +33,7 @@ class BackupSuccess extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: Backup successfully done for {$this->database->name}");
|
||||
$mail->view('emails.backup-success', [
|
||||
'name' => $this->name,
|
||||
|
||||
@@ -25,7 +25,7 @@ class DailyBackup extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject('Coolify: Daily backup statuses');
|
||||
$mail->view('emails.daily-backup', [
|
||||
'databases' => $this->databases,
|
||||
|
||||
@@ -35,7 +35,7 @@ class TaskFailed extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: [ACTION REQUIRED] Scheduled task ({$this->task->name}) failed.");
|
||||
$mail->view('emails.scheduled-task-failed', [
|
||||
'task' => $this->task,
|
||||
|
||||
@@ -41,7 +41,7 @@ class ForceDisabled extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: Server ({$this->server->name}) disabled because it is not paid!");
|
||||
$mail->view('emails.server-force-disabled', [
|
||||
'name' => $this->server->name,
|
||||
|
||||
@@ -41,7 +41,7 @@ class ForceEnabled extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: Server ({$this->server->name}) enabled again!");
|
||||
$mail->view('emails.server-force-enabled', [
|
||||
'name' => $this->server->name,
|
||||
|
||||
@@ -41,7 +41,7 @@ class HighDiskUsage extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: Server ({$this->server->name}) high disk usage detected!");
|
||||
$mail->view('emails.high-disk-usage', [
|
||||
'name' => $this->server->name,
|
||||
|
||||
@@ -50,7 +50,7 @@ class Revived extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: Server ({$this->server->name}) revived.");
|
||||
$mail->view('emails.server-revived', [
|
||||
'name' => $this->server->name,
|
||||
|
||||
@@ -41,7 +41,7 @@ class Unreachable extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject("Coolify: Your server ({$this->server->name}) is unreachable.");
|
||||
$mail->view('emails.server-lost-connection', [
|
||||
'name' => $this->server->name,
|
||||
|
||||
@@ -22,7 +22,7 @@ class Test extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject('Coolify: Test Email');
|
||||
$mail->view('emails.test');
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class InvitationLink extends Notification implements ShouldQueue
|
||||
$invitation = TeamInvitation::whereEmail($this->user->email)->first();
|
||||
$invitation_team = Team::find($invitation->team->id);
|
||||
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject('Coolify: Invitation for '.$invitation_team->name);
|
||||
$mail->view('emails.invitation-link', [
|
||||
'team' => $invitation_team->name,
|
||||
|
||||
@@ -53,7 +53,7 @@ class ResetPassword extends Notification
|
||||
|
||||
protected function buildMailMessage($url)
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject('Coolify: Reset Password');
|
||||
$mail->view('emails.reset-password', ['url' => $url, 'count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class Test extends Notification implements ShouldQueue
|
||||
|
||||
public function toMail(): MailMessage
|
||||
{
|
||||
$mail = new MailMessage();
|
||||
$mail = new MailMessage;
|
||||
$mail->subject('Coolify: Test Email');
|
||||
$mail->view('emails.test');
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ function create_standalone_postgresql($environmentId, $destinationUuid, ?array $
|
||||
if (! $destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
$database = new StandalonePostgresql();
|
||||
$database = new StandalonePostgresql;
|
||||
$database->name = generate_database_name('postgresql');
|
||||
$database->postgres_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
$database->environment_id = $environmentId;
|
||||
@@ -45,7 +45,7 @@ function create_standalone_redis($environment_id, $destination_uuid, ?array $oth
|
||||
if (! $destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
$database = new StandaloneRedis();
|
||||
$database = new StandaloneRedis;
|
||||
$database->name = generate_database_name('redis');
|
||||
$database->redis_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
$database->environment_id = $environment_id;
|
||||
@@ -65,7 +65,7 @@ function create_standalone_mongodb($environment_id, $destination_uuid, ?array $o
|
||||
if (! $destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
$database = new StandaloneMongodb();
|
||||
$database = new StandaloneMongodb;
|
||||
$database->name = generate_database_name('mongodb');
|
||||
$database->mongo_initdb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
$database->environment_id = $environment_id;
|
||||
@@ -84,7 +84,7 @@ function create_standalone_mysql($environment_id, $destination_uuid, ?array $oth
|
||||
if (! $destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
$database = new StandaloneMysql();
|
||||
$database = new StandaloneMysql;
|
||||
$database->name = generate_database_name('mysql');
|
||||
$database->mysql_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
$database->mysql_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
@@ -104,7 +104,7 @@ function create_standalone_mariadb($environment_id, $destination_uuid, ?array $o
|
||||
if (! $destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
$database = new StandaloneMariadb();
|
||||
$database = new StandaloneMariadb;
|
||||
$database->name = generate_database_name('mariadb');
|
||||
$database->mariadb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
$database->mariadb_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
@@ -125,7 +125,7 @@ function create_standalone_keydb($environment_id, $destination_uuid, ?array $oth
|
||||
if (! $destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
$database = new StandaloneKeydb();
|
||||
$database = new StandaloneKeydb;
|
||||
$database->name = generate_database_name('keydb');
|
||||
$database->keydb_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
$database->environment_id = $environment_id;
|
||||
@@ -145,7 +145,7 @@ function create_standalone_dragonfly($environment_id, $destination_uuid, ?array
|
||||
if (! $destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
$database = new StandaloneDragonfly();
|
||||
$database = new StandaloneDragonfly;
|
||||
$database->name = generate_database_name('dragonfly');
|
||||
$database->dragonfly_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
$database->environment_id = $environment_id;
|
||||
@@ -164,7 +164,7 @@ function create_standalone_clickhouse($environment_id, $destination_uuid, ?array
|
||||
if (! $destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
$database = new StandaloneClickhouse();
|
||||
$database = new StandaloneClickhouse;
|
||||
$database->name = generate_database_name('clickhouse');
|
||||
$database->clickhouse_admin_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
$database->environment_id = $environment_id;
|
||||
|
||||
@@ -48,9 +48,13 @@ function format_docker_command_output_to_json($rawOutput): Collection
|
||||
$outputLines = collect($outputLines);
|
||||
}
|
||||
|
||||
return $outputLines
|
||||
->reject(fn ($line) => empty($line))
|
||||
->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR));
|
||||
try {
|
||||
return $outputLines
|
||||
->reject(fn ($line) => empty($line))
|
||||
->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR));
|
||||
} catch (\Throwable $e) {
|
||||
return collect([]);
|
||||
}
|
||||
}
|
||||
|
||||
function format_docker_labels_to_json(string|array $rawOutput): Collection
|
||||
|
||||
@@ -14,9 +14,9 @@ use Lcobucci\JWT\Token\Builder;
|
||||
function generate_github_installation_token(GithubApp $source)
|
||||
{
|
||||
$signingKey = InMemory::plainText($source->privateKey->private_key);
|
||||
$algorithm = new Sha256();
|
||||
$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
|
||||
$now = new DateTimeImmutable();
|
||||
$algorithm = new Sha256;
|
||||
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
|
||||
$now = new DateTimeImmutable;
|
||||
$now = $now->setTime($now->format('H'), $now->format('i'));
|
||||
$issuedToken = $tokenBuilder
|
||||
->issuedBy($source->app_id)
|
||||
@@ -38,9 +38,9 @@ function generate_github_installation_token(GithubApp $source)
|
||||
function generate_github_jwt_token(GithubApp $source)
|
||||
{
|
||||
$signingKey = InMemory::plainText($source->privateKey->private_key);
|
||||
$algorithm = new Sha256();
|
||||
$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
|
||||
$now = new DateTimeImmutable();
|
||||
$algorithm = new Sha256;
|
||||
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
|
||||
$now = new DateTimeImmutable;
|
||||
$now = $now->setTime($now->format('H'), $now->format('i'));
|
||||
$issuedToken = $tokenBuilder
|
||||
->issuedBy($source->app_id)
|
||||
|
||||
@@ -132,7 +132,7 @@ function generate_default_proxy_configuration(Server $server)
|
||||
'services' => [
|
||||
'traefik' => [
|
||||
'container_name' => 'coolify-proxy',
|
||||
'image' => 'traefik:v2.10',
|
||||
'image' => 'traefik:v2.11',
|
||||
'restart' => RESTART_MODE,
|
||||
'extra_hosts' => [
|
||||
'host.docker.internal:host-gateway',
|
||||
|
||||
@@ -73,6 +73,13 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
|
||||
"echo '$content' | base64 -d | tee $fileLocation",
|
||||
], $server);
|
||||
} elseif ($isFile == 'NOK' && $isDir == 'NOK' && $fileVolume->is_directory && $isInit) {
|
||||
// Does not exists (no dir or file), flagged as directory, is init
|
||||
$fileVolume->content = null;
|
||||
$fileVolume->is_directory = true;
|
||||
$fileVolume->save();
|
||||
instant_remote_process(["mkdir -p $fileLocation"], $server);
|
||||
} elseif ($isFile == 'NOK' && $isDir == 'NOK' && ! $fileVolume->is_directory && $isInit && ! $content) {
|
||||
// Does not exists (no dir or file), not flagged as directory, is init, has no content => create directory
|
||||
$fileVolume->content = null;
|
||||
$fileVolume->is_directory = true;
|
||||
$fileVolume->save();
|
||||
@@ -88,6 +95,9 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
|
||||
try {
|
||||
$name = data_get($resource, 'name');
|
||||
$dockerComposeRaw = data_get($resource, 'service.docker_compose_raw');
|
||||
if (! $dockerComposeRaw) {
|
||||
throw new \Exception('No compose file found or not a valid YAML file.');
|
||||
}
|
||||
$dockerCompose = Yaml::parse($dockerComposeRaw);
|
||||
|
||||
// Switch Image
|
||||
@@ -118,7 +128,6 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
|
||||
if ($port) {
|
||||
$variableName = $variableName."_$port";
|
||||
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
||||
// ray($generatedEnv);
|
||||
if ($generatedEnv) {
|
||||
$generatedEnv->value = $fqdn.$path;
|
||||
$generatedEnv->save();
|
||||
|
||||
@@ -196,7 +196,7 @@ function generate_random_name(?string $cuid = null): string
|
||||
{
|
||||
$generator = new \Nubs\RandomNameGenerator\All(
|
||||
[
|
||||
new \Nubs\RandomNameGenerator\Alliteration(),
|
||||
new \Nubs\RandomNameGenerator\Alliteration,
|
||||
]
|
||||
);
|
||||
if (is_null($cuid)) {
|
||||
@@ -977,6 +977,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$target = str($volume)->after(':')->beforeLast(':');
|
||||
if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) {
|
||||
$type = str('bind');
|
||||
// By default, we cannot determine if the bind is a directory or not, so we set it to directory
|
||||
$isDirectory = true;
|
||||
} else {
|
||||
$type = str('volume');
|
||||
}
|
||||
@@ -985,14 +987,19 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$source = data_get_str($volume, 'source');
|
||||
$target = data_get_str($volume, 'target');
|
||||
$content = data_get($volume, 'content');
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', false) || (bool) data_get($volume, 'is_directory', false);
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
||||
$foundConfig = $savedService->fileStorages()->whereMountPath($target)->first();
|
||||
if ($foundConfig) {
|
||||
$contentNotNull = data_get($foundConfig, 'content');
|
||||
if ($contentNotNull) {
|
||||
$content = $contentNotNull;
|
||||
}
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', false) || (bool) data_get($volume, 'is_directory', false);
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
||||
}
|
||||
if (is_null($isDirectory) && is_null($content)) {
|
||||
// if isDirectory is not set & content is also not set, we assume it is a directory
|
||||
ray('setting isDirectory to true');
|
||||
$isDirectory = true;
|
||||
}
|
||||
}
|
||||
if ($type?->value() === 'bind') {
|
||||
@@ -1058,30 +1065,26 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
data_set($service, 'volumes', $serviceVolumes->toArray());
|
||||
}
|
||||
|
||||
// Add env_file with at least .env to the service
|
||||
// $envFile = collect(data_get($service, 'env_file', []));
|
||||
// if ($envFile->count() > 0) {
|
||||
// if (!$envFile->contains('.env')) {
|
||||
// $envFile->push('.env');
|
||||
// }
|
||||
// } else {
|
||||
// $envFile = collect(['.env']);
|
||||
// }
|
||||
// data_set($service, 'env_file', $envFile->toArray());
|
||||
|
||||
// Get variables from the service
|
||||
foreach ($serviceVariables as $variableName => $variable) {
|
||||
if (is_numeric($variableName)) {
|
||||
$variable = str($variable);
|
||||
if ($variable->contains('=')) {
|
||||
// - SESSION_SECRET=123
|
||||
// - SESSION_SECRET=
|
||||
$key = $variable->before('=');
|
||||
$value = $variable->after('=');
|
||||
if (is_array($variable)) {
|
||||
// - SESSION_SECRET: 123
|
||||
// - SESSION_SECRET:
|
||||
$key = str(collect($variable)->keys()->first());
|
||||
$value = str(collect($variable)->values()->first());
|
||||
} else {
|
||||
// - SESSION_SECRET
|
||||
$key = $variable;
|
||||
$value = null;
|
||||
$variable = str($variable);
|
||||
if ($variable->contains('=')) {
|
||||
// - SESSION_SECRET=123
|
||||
// - SESSION_SECRET=
|
||||
$key = $variable->before('=');
|
||||
$value = $variable->after('=');
|
||||
} else {
|
||||
// - SESSION_SECRET
|
||||
$key = $variable;
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SESSION_SECRET: 123
|
||||
@@ -1688,7 +1691,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$read_only = data_get($volume, 'read_only');
|
||||
if ($source && $target) {
|
||||
$uuid = $resource->uuid;
|
||||
if ((str($source)->startsWith('.') || str($source)->startsWith('~'))) {
|
||||
if ((str($source)->startsWith('.') || str($source)->startsWith('~') || str($source)->startsWith('/'))) {
|
||||
$dir = base_configuration_dir().'/applications/'.$resource->uuid;
|
||||
if (str($source, '.')) {
|
||||
$source = str($source)->replaceFirst('.', $dir);
|
||||
@@ -1696,11 +1699,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
if (str($source, '~')) {
|
||||
$source = str($source)->replaceFirst('~', $dir);
|
||||
}
|
||||
if ($pull_request_id === 0) {
|
||||
$source = $uuid."-$source";
|
||||
} else {
|
||||
$source = $uuid."-$source-pr-$pull_request_id";
|
||||
}
|
||||
if ($read_only) {
|
||||
data_set($volume, 'source', $source.':'.$target.':ro');
|
||||
} else {
|
||||
@@ -1740,6 +1738,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
if (is_array($volume)) {
|
||||
return data_get($volume, 'source');
|
||||
}
|
||||
dispatch(new ServerFilesFromServerJob($resource));
|
||||
|
||||
return $volume->value();
|
||||
});
|
||||
@@ -1841,16 +1840,23 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
// Get variables from the service
|
||||
foreach ($serviceVariables as $variableName => $variable) {
|
||||
if (is_numeric($variableName)) {
|
||||
$variable = str($variable);
|
||||
if ($variable->contains('=')) {
|
||||
// - SESSION_SECRET=123
|
||||
// - SESSION_SECRET=
|
||||
$key = $variable->before('=');
|
||||
$value = $variable->after('=');
|
||||
if (is_array($variable)) {
|
||||
// - SESSION_SECRET: 123
|
||||
// - SESSION_SECRET:
|
||||
$key = str(collect($variable)->keys()->first());
|
||||
$value = str(collect($variable)->values()->first());
|
||||
} else {
|
||||
// - SESSION_SECRET
|
||||
$key = $variable;
|
||||
$value = null;
|
||||
$variable = str($variable);
|
||||
if ($variable->contains('=')) {
|
||||
// - SESSION_SECRET=123
|
||||
// - SESSION_SECRET=
|
||||
$key = $variable->before('=');
|
||||
$value = $variable->after('=');
|
||||
} else {
|
||||
// - SESSION_SECRET
|
||||
$key = $variable;
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SESSION_SECRET: 123
|
||||
@@ -2192,9 +2198,9 @@ function generateEnvValue(string $command, ?Service $service = null)
|
||||
$signingKey = $signingKey->value;
|
||||
}
|
||||
$key = InMemory::plainText($signingKey);
|
||||
$algorithm = new Sha256();
|
||||
$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
|
||||
$now = new DateTimeImmutable();
|
||||
$algorithm = new Sha256;
|
||||
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
|
||||
$now = new DateTimeImmutable;
|
||||
$now = $now->setTime($now->format('H'), $now->format('i'));
|
||||
$token = $tokenBuilder
|
||||
->issuedBy('supabase')
|
||||
@@ -2212,9 +2218,9 @@ function generateEnvValue(string $command, ?Service $service = null)
|
||||
$signingKey = $signingKey->value;
|
||||
}
|
||||
$key = InMemory::plainText($signingKey);
|
||||
$algorithm = new Sha256();
|
||||
$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
|
||||
$now = new DateTimeImmutable();
|
||||
$algorithm = new Sha256;
|
||||
$tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
|
||||
$now = new DateTimeImmutable;
|
||||
$now = $now->setTime($now->format('H'), $now->format('i'));
|
||||
$token = $tokenBuilder
|
||||
->issuedBy('supabase')
|
||||
|
||||
@@ -7,7 +7,7 @@ return [
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.313',
|
||||
'release' => '4.0.0-beta.319',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.313';
|
||||
return '4.0.0-beta.319';
|
||||
|
||||
@@ -38,7 +38,7 @@ return new class extends Migration
|
||||
|
||||
EnvironmentVariable::all()->each(function (EnvironmentVariable $environmentVariable) {
|
||||
$environmentVariable->update([
|
||||
'uuid' => (string) new Cuid2(),
|
||||
'uuid' => (string) new Cuid2,
|
||||
]);
|
||||
});
|
||||
Schema::table('environment_variables', function (Blueprint $table) {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->boolean('is_container_label_readonly_enabled')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('is_container_label_readonly_enabled');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->boolean('is_preserve_repository_enabled')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('application_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('is_preserve_repository_enabled');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('server_settings', function (Blueprint $table) {
|
||||
$table->boolean('is_force_cleanup_enabled')->default(false)->after('is_sentinel_enabled');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('server_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('is_force_cleanup_enabled');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('applications', function (Blueprint $table) {
|
||||
$table->boolean('health_check_enabled')->default(false)->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('applications', function (Blueprint $table) {
|
||||
$table->boolean('health_check_enabled')->default(true)->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('servers', function (Blueprint $table) {
|
||||
$table->text('validation_logs')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('servers', function (Blueprint $table) {
|
||||
$table->dropColumn('validation_logs');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -178,7 +178,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
|
||||
get_public_ips();
|
||||
|
||||
$oauth_settings_seeder = new OauthSettingSeeder();
|
||||
$oauth_settings_seeder = new OauthSettingSeeder;
|
||||
$oauth_settings_seeder->run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('services', function (Blueprint $table) {
|
||||
$table->string('git_repository')->nullable();
|
||||
$table->string('git_branch')->nullable();
|
||||
$table->nullableMorphs('source');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('services', function (Blueprint $table) {
|
||||
$table->dropColumn('git_repository');
|
||||
$table->dropColumn('git_branch');
|
||||
$table->dropMorphs('source');
|
||||
});
|
||||
}
|
||||
};
|
||||
486
openapi.yaml
486
openapi.yaml
@@ -3010,6 +3010,44 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
post:
|
||||
tags:
|
||||
- Projects
|
||||
summary: Create
|
||||
description: 'Create Project.'
|
||||
operationId: cf067eb7cf18216cda3239329a2eeadb
|
||||
requestBody:
|
||||
description: 'Project created.'
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
uuid:
|
||||
type: string
|
||||
description: 'The name of the project.'
|
||||
description:
|
||||
type: string
|
||||
description: 'The description of the project.'
|
||||
type: object
|
||||
responses:
|
||||
'201':
|
||||
description: 'Project created.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
uuid: { type: string, example: og888os, description: 'The UUID of the project.' }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
'/projects/{uuid}':
|
||||
get:
|
||||
tags:
|
||||
@@ -3041,6 +3079,79 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
delete:
|
||||
tags:
|
||||
- Projects
|
||||
summary: Delete
|
||||
description: 'Delete project by UUID.'
|
||||
operationId: f668a936f505b4401948c74b6a663029
|
||||
parameters:
|
||||
-
|
||||
name: uuid
|
||||
in: path
|
||||
description: 'UUID of the application.'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
'200':
|
||||
description: 'Project deleted.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
message: { type: string, example: 'Project deleted.' }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
patch:
|
||||
tags:
|
||||
- Projects
|
||||
summary: Update
|
||||
description: 'Update Project.'
|
||||
operationId: 2db343bd6fc14c658cb51a2b73b2f842
|
||||
requestBody:
|
||||
description: 'Project updated.'
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: 'The name of the project.'
|
||||
description:
|
||||
type: string
|
||||
description: 'The description of the project.'
|
||||
type: object
|
||||
responses:
|
||||
'201':
|
||||
description: 'Project updated.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
uuid: { type: string, example: og888os }
|
||||
name: { type: string, example: 'Project Name' }
|
||||
description: { type: string, example: 'Project Description' }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
'/projects/{uuid}/{environment_name}':
|
||||
get:
|
||||
tags:
|
||||
@@ -3288,6 +3399,70 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
post:
|
||||
tags:
|
||||
- Servers
|
||||
summary: Create
|
||||
description: 'Create Server.'
|
||||
operationId: fa44b42490379e428ba5b8747716a8d9
|
||||
requestBody:
|
||||
description: 'Server created.'
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: 'My Server'
|
||||
description: 'The name of the server.'
|
||||
description:
|
||||
type: string
|
||||
example: 'My Server Description'
|
||||
description: 'The description of the server.'
|
||||
ip:
|
||||
type: string
|
||||
example: 127.0.0.1
|
||||
description: 'The IP of the server.'
|
||||
port:
|
||||
type: integer
|
||||
example: 22
|
||||
description: 'The port of the server.'
|
||||
user:
|
||||
type: string
|
||||
example: root
|
||||
description: 'The user of the server.'
|
||||
private_key_uuid:
|
||||
type: string
|
||||
example: og888os
|
||||
description: 'The UUID of the private key.'
|
||||
is_build_server:
|
||||
type: boolean
|
||||
example: false
|
||||
description: 'Is build server.'
|
||||
instant_validate:
|
||||
type: boolean
|
||||
example: false
|
||||
description: 'Instant validate.'
|
||||
type: object
|
||||
responses:
|
||||
'201':
|
||||
description: 'Server created.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
uuid: { type: string, example: og888os, description: 'The UUID of the server.' }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
'/servers/{uuid}':
|
||||
get:
|
||||
tags:
|
||||
@@ -3319,6 +3494,95 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
delete:
|
||||
tags:
|
||||
- Servers
|
||||
summary: Delete
|
||||
description: 'Delete server by UUID.'
|
||||
operationId: 0231fe0134f0306b21f006ce51b0a3dc
|
||||
parameters:
|
||||
-
|
||||
name: uuid
|
||||
in: path
|
||||
description: 'UUID of the server.'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
'200':
|
||||
description: 'Server deleted.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
message: { type: string, example: 'Server deleted.' }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
patch:
|
||||
tags:
|
||||
- Servers
|
||||
summary: Update
|
||||
description: 'Update Server.'
|
||||
operationId: 41bbdaf79eb1938592494fc5494442a0
|
||||
requestBody:
|
||||
description: 'Server updated.'
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: 'The name of the server.'
|
||||
description:
|
||||
type: string
|
||||
description: 'The description of the server.'
|
||||
ip:
|
||||
type: string
|
||||
description: 'The IP of the server.'
|
||||
port:
|
||||
type: integer
|
||||
description: 'The port of the server.'
|
||||
user:
|
||||
type: string
|
||||
description: 'The user of the server.'
|
||||
private_key_uuid:
|
||||
type: string
|
||||
description: 'The UUID of the private key.'
|
||||
is_build_server:
|
||||
type: boolean
|
||||
description: 'Is build server.'
|
||||
instant_validate:
|
||||
type: boolean
|
||||
description: 'Instant validate.'
|
||||
type: object
|
||||
responses:
|
||||
'201':
|
||||
description: 'Server updated.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Server'
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
'/servers/{uuid}/resources':
|
||||
get:
|
||||
tags:
|
||||
@@ -3383,6 +3647,39 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
'/servers/{uuid}/validate':
|
||||
get:
|
||||
tags:
|
||||
- Servers
|
||||
summary: Validate
|
||||
description: 'Validate server by UUID.'
|
||||
operationId: a543a12ef2cbc7a3dd22c3dbe6cbee89
|
||||
parameters:
|
||||
-
|
||||
name: uuid
|
||||
in: path
|
||||
description: 'Server UUID'
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
responses:
|
||||
'201':
|
||||
description: 'Server validation started.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
message: { type: string, example: 'Validation started.' }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
/services:
|
||||
get:
|
||||
tags:
|
||||
@@ -4189,191 +4486,35 @@ components:
|
||||
$ref: '#/components/schemas/Environment'
|
||||
type: object
|
||||
Server:
|
||||
description: 'Application model'
|
||||
description: 'Server model'
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
repository_project_id:
|
||||
type: integer
|
||||
nullable: true
|
||||
uuid:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
fqdn:
|
||||
type: string
|
||||
config_hash:
|
||||
type: string
|
||||
git_repository:
|
||||
type: string
|
||||
git_branch:
|
||||
type: string
|
||||
git_commit_sha:
|
||||
type: string
|
||||
git_full_url:
|
||||
type: string
|
||||
nullable: true
|
||||
docker_registry_image_name:
|
||||
type: string
|
||||
nullable: true
|
||||
docker_registry_image_tag:
|
||||
type: string
|
||||
nullable: true
|
||||
build_pack:
|
||||
type: string
|
||||
static_image:
|
||||
type: string
|
||||
install_command:
|
||||
type: string
|
||||
build_command:
|
||||
type: string
|
||||
start_command:
|
||||
type: string
|
||||
ports_exposes:
|
||||
type: string
|
||||
ports_mappings:
|
||||
type: string
|
||||
nullable: true
|
||||
base_directory:
|
||||
type: string
|
||||
publish_directory:
|
||||
type: string
|
||||
health_check_path:
|
||||
type: string
|
||||
health_check_port:
|
||||
type: string
|
||||
nullable: true
|
||||
health_check_host:
|
||||
type: string
|
||||
health_check_method:
|
||||
type: string
|
||||
health_check_return_code:
|
||||
type: integer
|
||||
health_check_scheme:
|
||||
type: string
|
||||
health_check_response_text:
|
||||
type: string
|
||||
nullable: true
|
||||
health_check_interval:
|
||||
type: integer
|
||||
health_check_timeout:
|
||||
type: integer
|
||||
health_check_retries:
|
||||
type: integer
|
||||
health_check_start_period:
|
||||
type: integer
|
||||
limits_memory:
|
||||
type: string
|
||||
limits_memory_swap:
|
||||
type: string
|
||||
limits_memory_swappiness:
|
||||
type: integer
|
||||
limits_memory_reservation:
|
||||
type: string
|
||||
limits_cpus:
|
||||
type: string
|
||||
limits_cpuset:
|
||||
type: string
|
||||
nullable: true
|
||||
limits_cpu_shares:
|
||||
type: integer
|
||||
status:
|
||||
type: string
|
||||
preview_url_template:
|
||||
type: string
|
||||
destination_type:
|
||||
type: string
|
||||
destination_id:
|
||||
type: integer
|
||||
source_type:
|
||||
type: string
|
||||
source_id:
|
||||
type: integer
|
||||
private_key_id:
|
||||
type: integer
|
||||
nullable: true
|
||||
environment_id:
|
||||
type: integer
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description:
|
||||
type: string
|
||||
nullable: true
|
||||
dockerfile:
|
||||
ip:
|
||||
type: string
|
||||
nullable: true
|
||||
health_check_enabled:
|
||||
type: boolean
|
||||
dockerfile_location:
|
||||
user:
|
||||
type: string
|
||||
custom_labels:
|
||||
type: string
|
||||
dockerfile_target_build:
|
||||
type: string
|
||||
nullable: true
|
||||
manual_webhook_secret_github:
|
||||
type: string
|
||||
nullable: true
|
||||
manual_webhook_secret_gitlab:
|
||||
type: string
|
||||
nullable: true
|
||||
docker_compose_location:
|
||||
type: string
|
||||
docker_compose:
|
||||
type: string
|
||||
nullable: true
|
||||
docker_compose_raw:
|
||||
type: string
|
||||
nullable: true
|
||||
docker_compose_domains:
|
||||
type: string
|
||||
nullable: true
|
||||
deleted_at:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
docker_compose_custom_start_command:
|
||||
type: string
|
||||
nullable: true
|
||||
docker_compose_custom_build_command:
|
||||
type: string
|
||||
nullable: true
|
||||
swarm_replicas:
|
||||
port:
|
||||
type: integer
|
||||
swarm_placement_constraints:
|
||||
type: string
|
||||
nullable: true
|
||||
manual_webhook_secret_bitbucket:
|
||||
type: string
|
||||
nullable: true
|
||||
custom_docker_run_options:
|
||||
type: string
|
||||
nullable: true
|
||||
post_deployment_command:
|
||||
type: string
|
||||
nullable: true
|
||||
post_deployment_command_container:
|
||||
type: string
|
||||
nullable: true
|
||||
pre_deployment_command:
|
||||
type: string
|
||||
nullable: true
|
||||
pre_deployment_command_container:
|
||||
type: string
|
||||
nullable: true
|
||||
watch_paths:
|
||||
type: string
|
||||
nullable: true
|
||||
custom_healthcheck_found:
|
||||
proxy:
|
||||
type: object
|
||||
high_disk_usage_notification_sent:
|
||||
type: boolean
|
||||
manual_webhook_secret_gitea:
|
||||
unreachable_notification_sent:
|
||||
type: boolean
|
||||
unreachable_count:
|
||||
type: integer
|
||||
validation_logs:
|
||||
type: string
|
||||
nullable: true
|
||||
redirect:
|
||||
log_drain_notification_sent:
|
||||
type: boolean
|
||||
swarm_cluster:
|
||||
type: string
|
||||
type: object
|
||||
ServerSetting:
|
||||
@@ -4480,6 +4621,9 @@ components:
|
||||
is_container_label_escape_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to enable the container label escape.'
|
||||
is_container_label_readonly_enabled:
|
||||
type: boolean
|
||||
description: 'The flag to enable the container label readonly.'
|
||||
config_hash:
|
||||
type: string
|
||||
description: 'The hash of the service configuration.'
|
||||
|
||||
BIN
other/logos/branddev.png
Normal file
BIN
other/logos/branddev.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
public/svgs/docmost.png
Normal file
BIN
public/svgs/docmost.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
1
public/svgs/drupal.svg
Normal file
1
public/svgs/drupal.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="220.03239mm" height="167.50914mm" viewBox="0 0 623.71387 474.82906"><title>Risorsa 28</title><g id="Livello_2" data-name="Livello 2"><g id="Livello_1-2" data-name="Livello 1"><path d="M292.12129,345.00234h-26.212l.04275,49.04508c0,14.33211-6.14488,25.92692-20.477,25.92692-14.33924,0-20.54117-11.59481-20.54117-25.92692V345.04868H198.78953l-.00713,48.99874c0,28.32213,18.20655,51.27636,46.53223,51.27636,28.31849,0,46.79948-22.95423,46.79948-51.27636Z" style="fill:#009cde"/><rect x="528.48024" y="315.11614" width="26.08367" height="127.67755" style="fill:#009cde"/><polygon points="573.823 319.826 573.823 315.239 594.15 315.239 594.15 319.826 586.715 319.826 586.715 340.043 581.261 340.043 581.261 319.826 573.823 319.826" style="fill:#009cde"/><polygon points="604.673 315.24 610.476 332.295 610.544 332.295 616.029 315.24 623.714 315.24 623.714 340.044 618.606 340.044 618.606 322.465 618.535 322.465 612.458 340.044 608.252 340.044 602.175 322.64 602.103 322.64 602.103 340.044 596.999 340.044 596.999 315.24 604.673 315.24" style="fill:#009cde"/><path d="M177.46784,343.93624c-22.48375-5.18962-36.66617,17.148-37.25785,18.34557-.28871.58814-.2994.93035-1.29384.90533-.82337-.01773-.916-.90533-.916-.90533l-2.79086-17.08726H111.84157v97.51614h26.19419v-52.784c0-4.3128,11.61256-24.993,34.11766-19.67153,11.38088,2.69467,16.21054,7.52436,16.21054,7.52436V348.07087a41.85,41.85,0,0,0-10.89612-4.13463" style="fill:#009cde"/><path d="M353.05258,368.64446a26.13539,26.13539,0,1,1-26.13,26.13,26.13748,26.13748,0,0,1,26.13-26.13M327.664,474.82906V439.74191l.00712.00718.00713-13.14169s.03921-1.05141.98729-1.06218c.84474-.01066,1.03368.549,1.24041,1.06218,1.98529,4.94369,12.90641,23.76689,37.14374,17.86435A51.631,51.631,0,1,0,301.402,394.77446v80.0546Z" style="fill:#009cde"/><path d="M492.37655,394.77479a26.13539,26.13539,0,1,1-26.1336-26.13,26.139,26.139,0,0,1,26.1336,26.13m-.74494,47.9793H517.8935v-47.9793a51.62918,51.62918,0,1,0-65.64764,49.69381c24.23739,5.906,35.15845-12.92066,37.14374-17.86087.20673-.5132.39208-1.07284,1.24041-1.06566.94808.01425.98729,1.06566.98729,1.06566" style="fill:#009cde"/><path d="M36.905,337.16373h-10.529v83.26605l10.81417.278c22.18076,0,36.46656-2.01733,36.46656-41.90208,0-38.24519-12.61057-41.642-36.75172-41.642m-7.1108,105.38619H0V315.0649H31.96836c38.70852,0,68.06785,7.10374,68.06785,63.74083,0,56.09518-31.09872,63.74419-70.24206,63.74419" style="fill:#009cde"/><path d="M336.513,60.04433C316.67656,40.21728,297.75071,21.31654,292.11812,0c-5.63286,21.31654-24.56176,40.21728-44.39487,60.04433-29.74983,29.731-63.47995,63.42669-63.47995,113.96434a107.878,107.878,0,1,0,215.756,0c0-50.53435-33.72736-84.23329-63.48628-113.96434M230.09481,199.146c-6.61462-.22461-31.02654-42.30194,14.26152-87.1038l29.96891,32.73593a2.56174,2.56174,0,0,1-.20005,3.82276c-7.15132,7.33453-37.63207,37.90055-41.42061,48.46955-.782,2.18152-1.92407,2.099-2.60977,2.07556m62.02662,55.45668a37.10175,37.10175,0,0,1-37.102-37.102c0-9.39381,3.73446-17.765,9.24757-24.50685,6.68995-8.18054,27.84947-31.18907,27.84947-31.18907s20.83558,23.34627,27.79953,31.111a36.28369,36.28369,0,0,1,9.30744,24.58494,37.10209,37.10209,0,0,1-37.102,37.102m71.01314-60.16628c-.79965,1.74885-2.61363,4.66848-5.062,4.75761-4.36413.15894-4.83045-2.07721-8.05609-6.8511-7.08179-10.47987-68.88406-75.07043-80.44365-87.56214-10.16779-10.987-1.43181-18.733,2.62052-22.79219,5.084-5.09314,19.92417-19.92418,19.92417-19.92418s44.24974,41.984,62.6825,70.67071,12.08027,53.50972,8.33451,61.70129" style="fill:#009cde"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
6
public/svgs/plane.svg
Normal file
6
public/svgs/plane.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="155" height="155" viewBox="0 0 155 155" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="37" y="64" width="27" height="27" fill="#4075FC"/>
|
||||
<rect x="91" y="64" width="27" height="27" fill="#4075FC"/>
|
||||
<rect x="64" y="91" width="27" height="27" fill="#4075FC"/>
|
||||
<rect x="64" y="36" width="54" height="28" fill="#4075FC"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 346 B |
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
@if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
|
||||
@if (str($status)->contains('unhealthy'))
|
||||
<x-helper helper="Unhealthy state. <span class='dark:text-warning text-coollabs'>This doesn't mean that the resource is malfunctioning.</span><br><br>- If the resource is accessible, it indicates that no health check is configured - it is not mandatory.<br>- If the resource is not accessible (returning 404 or 503), it may indicate that a health check is needed and has not passed. <span class='dark:text-warning text-coollabs'>Your action is required.</span>" >
|
||||
<x-helper helper="Unhealthy state. <span class='dark:text-warning text-coollabs'>This doesn't mean that the resource is malfunctioning.</span><br><br>- If the resource is accessible, it indicates that no health check is configured - it is not mandatory.<br>- If the resource is not accessible (returning 404 or 503), it may indicate that a health check is needed and has not passed. <span class='dark:text-warning text-coollabs'>Your action is required.</span><br><br>More details in the <a href='https://coolify.io/docs/knowledge-base/healthchecks' class='underline dark:text-warning text-coollabs' target='_blank'>documentation</a>." >
|
||||
<x-slot:icon>
|
||||
<svg class="hidden w-4 h-4 dark:text-warning lg:block" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor" d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16"></path>
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
<x-forms.checkbox
|
||||
helper="Your application will be available only on https if your domain starts with https://..."
|
||||
instantSave id="is_force_https_enabled" label="Force Https" />
|
||||
<x-forms.checkbox label="Enable gzip compression"
|
||||
<x-forms.checkbox label="Enable Gzip Compression"
|
||||
helper="You can disable gzip compression if you want. Some services are compressing data by default. In this case, you do not need this."
|
||||
instantSave id="is_gzip_enabled" />
|
||||
<x-forms.checkbox helper="Strip Prefix is used to remove prefixes from paths. Like /api/ to /api."
|
||||
instantSave id="is_stripprefix_enabled" label="Strip Prefixes" />
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
<h3>Docker Compose</h3>
|
||||
<x-forms.checkbox instantSave id="application.settings.is_raw_compose_deployment_enabled"
|
||||
label="Raw Compose Deployment"
|
||||
helper="WARNING: Advanced use cases only. Your docker compose file will be deployed as-is. Nothing is modified by Coolify. You need to configure the proxy parts. More info in the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/compose#raw-docker-compose-deployment'>documentation.</a>" />
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
<livewire:project.shared.destination :resource="$application" :servers="$servers" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'storages'">
|
||||
<livewire:project.service.storage :resource="$application" />
|
||||
<livewire:project.service.storage :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'webhooks'">
|
||||
<livewire:project.shared.webhooks :resource="$application" lazy />
|
||||
|
||||
@@ -30,13 +30,7 @@
|
||||
</x-forms.select>
|
||||
@endif
|
||||
</div>
|
||||
@if ($application->could_set_build_commands())
|
||||
<div class="w-64">
|
||||
<x-forms.checkbox instantSave id="application.settings.is_static"
|
||||
label="Is it a static site?"
|
||||
helper="If your application is a static site or the final build assets should be served as a static site, enable this." />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
@if (
|
||||
!is_null($parsedServices) &&
|
||||
@@ -57,6 +51,7 @@
|
||||
@endforeach
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@endif
|
||||
@if ($application->build_pack !== 'dockercompose')
|
||||
@@ -129,99 +124,127 @@
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($application->build_pack !== 'dockerimage')
|
||||
<h3 class="pt-8">Build</h3>
|
||||
@if ($application->build_pack !== 'dockercompose')
|
||||
<div class="max-w-96">
|
||||
<x-forms.checkbox
|
||||
helper="Use a build server to build your application. You can configure your build server in the Server settings. This is experimental. For more info, check the <a href='https://coolify.io/docs/knowledge-base/server/build-server' class='underline' target='_blank'>documentation</a>."
|
||||
instantSave id="application.settings.is_build_server_enabled"
|
||||
label="Use a Build Server? (experimental)" />
|
||||
</div>
|
||||
@endif
|
||||
@if ($application->could_set_build_commands())
|
||||
@if ($application->build_pack === 'nixpacks')
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
<x-forms.input placeholder="If you modify this, you probably need to have a nixpacks.toml"
|
||||
id="application.install_command" label="Install Command" />
|
||||
<x-forms.input placeholder="If you modify this, you probably need to have a nixpacks.toml"
|
||||
id="application.build_command" label="Build Command" />
|
||||
<x-forms.input placeholder="If you modify this, you probably need to have a nixpacks.toml"
|
||||
id="application.start_command" label="Start Command" />
|
||||
</div>
|
||||
<div class="pb-4 text-xs">Nixpacks will detect the required configuration automatically.
|
||||
<a class="underline" href="https://coolify.io/docs/resources/introduction">Framework
|
||||
Specific Docs</a>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
<div class="flex flex-col gap-2" x-init="$wire.dispatch('loadCompose', true)">
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/"
|
||||
id="application.base_directory" label="Base Directory"
|
||||
helper="Directory to use as root. Useful for monorepos." />
|
||||
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/docker-compose.yaml"
|
||||
id="application.docker_compose_location" label="Docker Compose Location"
|
||||
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
|
||||
</div>
|
||||
<div class="pt-4">The following commands are for advanced use cases. Only modify them if you
|
||||
know what are
|
||||
you doing.</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input placeholder="docker compose build" x-bind:disabled="initLoadingCompose"
|
||||
id="application.docker_compose_custom_build_command"
|
||||
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} build</span>"
|
||||
label="Custom Build Command" />
|
||||
<x-forms.input placeholder="docker compose up -d" x-bind:disabled="initLoadingCompose"
|
||||
id="application.docker_compose_custom_start_command"
|
||||
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} up -d</span>"
|
||||
label="Custom Start Command" />
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
||||
helper="Directory to use as root. Useful for monorepos." />
|
||||
@if ($application->build_pack === 'dockerfile' && !$application->dockerfile)
|
||||
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
|
||||
label="Dockerfile Location"
|
||||
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}</span>" />
|
||||
@endif
|
||||
|
||||
@if ($application->build_pack === 'dockerfile')
|
||||
<x-forms.input id="application.dockerfile_target_build" label="Docker Build Stage Target"
|
||||
helper="Useful if you have multi-staged dockerfile." />
|
||||
@endif
|
||||
@if ($application->could_set_build_commands())
|
||||
@if ($application->settings->is_static)
|
||||
<x-forms.input placeholder="/dist" id="application.publish_directory"
|
||||
label="Publish Directory" required />
|
||||
@else
|
||||
<x-forms.input placeholder="/" id="application.publish_directory"
|
||||
label="Publish Directory" />
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@if ($this->application->is_github_based() && !$this->application->is_public_repository())
|
||||
<div class="pb-4">
|
||||
<x-forms.textarea helper="Gitignore-style rules to filter Git based webhook deployments."
|
||||
placeholder="src/pages/**" id="application.watch_paths" label="Watch Paths" />
|
||||
</div>
|
||||
@endif
|
||||
<div class="py-4 border-b dark:border-coolgray-200">
|
||||
<h3>Build</h3>
|
||||
@if ($application->build_pack === 'dockerimage')
|
||||
<x-forms.input
|
||||
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"
|
||||
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
|
||||
id="application.custom_docker_run_options" label="Custom Docker Options" />
|
||||
@else
|
||||
@if ($application->could_set_build_commands())
|
||||
@if ($application->build_pack === 'nixpacks')
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
<x-forms.input helper="If you modify this, you probably need to have a nixpacks.toml"
|
||||
id="application.install_command" label="Install Command" />
|
||||
<x-forms.input helper="If you modify this, you probably need to have a nixpacks.toml"
|
||||
id="application.build_command" label="Build Command" />
|
||||
<x-forms.input helper="If you modify this, you probably need to have a nixpacks.toml"
|
||||
id="application.start_command" label="Start Command" />
|
||||
</div>
|
||||
<div class="pt-1 text-xs">Nixpacks will detect the required configuration
|
||||
automatically.
|
||||
<a class="underline"
|
||||
href="https://coolify.io/docs/resources/applications/index">Framework
|
||||
Specific Docs</a>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@endif
|
||||
<div class="flex flex-col gap-2 pt-6 pb-10">
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
<div class="flex flex-col gap-2" x-init="$wire.dispatch('loadCompose', true)">
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/"
|
||||
id="application.base_directory" label="Base Directory"
|
||||
helper="Directory to use as root. Useful for monorepos." />
|
||||
<x-forms.input x-bind:disabled="initLoadingCompose"
|
||||
placeholder="/docker-compose.yaml" id="application.docker_compose_location"
|
||||
label="Docker Compose Location"
|
||||
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
|
||||
</div>
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave
|
||||
id="application.settings.is_preserve_repository_enabled"
|
||||
label="Preserve Repository During Deployment"
|
||||
helper="Git repository (based on the base directory settings) will be copied to the deployment directory." />
|
||||
</div>
|
||||
<div class="pt-4">The following commands are for advanced use cases.
|
||||
Only
|
||||
modify them if you
|
||||
know what are
|
||||
you doing.</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input placeholder="docker compose build"
|
||||
x-bind:disabled="initLoadingCompose"
|
||||
id="application.docker_compose_custom_build_command"
|
||||
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} build</span>"
|
||||
label="Custom Build Command" />
|
||||
<x-forms.input placeholder="docker compose up -d"
|
||||
x-bind:disabled="initLoadingCompose"
|
||||
id="application.docker_compose_custom_start_command"
|
||||
helper="If you use this, you need to specify paths relatively and should use the same compose file in the custom command, otherwise the automatically configured labels / etc won't work.<br><br>So in your case, use: <span class='dark:text-warning'>docker compose -f .{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }} up -d</span>"
|
||||
label="Custom Start Command" />
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
||||
helper="Directory to use as root. Useful for monorepos." />
|
||||
@if ($application->build_pack === 'dockerfile' && !$application->dockerfile)
|
||||
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
|
||||
label="Dockerfile Location"
|
||||
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}</span>" />
|
||||
@endif
|
||||
|
||||
@if ($application->build_pack === 'dockerfile')
|
||||
<x-forms.input id="application.dockerfile_target_build"
|
||||
label="Docker Build Stage Target"
|
||||
helper="Useful if you have multi-staged dockerfile." />
|
||||
@endif
|
||||
@if ($application->could_set_build_commands())
|
||||
@if ($application->settings->is_static)
|
||||
<x-forms.input placeholder="/dist" id="application.publish_directory"
|
||||
label="Publish Directory" required />
|
||||
@else
|
||||
<x-forms.input placeholder="/" id="application.publish_directory"
|
||||
label="Publish Directory" />
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</div>
|
||||
@if ($this->application->is_github_based() && !$this->application->is_public_repository())
|
||||
<div class="pb-4">
|
||||
<x-forms.textarea
|
||||
helper="Gitignore-style rules to filter Git based webhook deployments."
|
||||
placeholder="src/pages/**" id="application.watch_paths"
|
||||
label="Watch Paths" />
|
||||
</div>
|
||||
@endif
|
||||
<x-forms.input
|
||||
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"
|
||||
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
|
||||
id="application.custom_docker_run_options" label="Custom Docker Options" />
|
||||
|
||||
@if ($application->build_pack !== 'dockercompose')
|
||||
<div class="pt-2 w-96">
|
||||
<x-forms.checkbox
|
||||
helper="Use a build server to build your application. You can configure your build server in the Server settings. This is experimental. For more info, check the <a href='https://coolify.io/docs/knowledge-base/server/build-server' class='underline' target='_blank'>documentation</a>."
|
||||
instantSave id="application.settings.is_build_server_enabled"
|
||||
label="Use a Build Server? (experimental)" />
|
||||
</div>
|
||||
@endif
|
||||
@if ($application->could_set_build_commands())
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave id="application.settings.is_static"
|
||||
label="Is it a static site?"
|
||||
helper="If your application is a static site or the final build assets should be served as a static site, enable this." />
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<x-forms.input
|
||||
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"
|
||||
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
|
||||
id="application.custom_docker_run_options" label="Custom Docker Options" />
|
||||
@endif
|
||||
</div>
|
||||
@if ($application->build_pack === 'dockercompose')
|
||||
<x-forms.button wire:target='initLoadingCompose'
|
||||
x-on:click="$wire.dispatch('loadCompose', false)">Reload Compose File</x-forms.button>
|
||||
@@ -235,13 +258,15 @@
|
||||
label="Docker Compose Content" helper="You need to modify the docker compose file."
|
||||
monacoEditorLanguage="yaml" useMonacoEditor />
|
||||
@endif
|
||||
<div class="w-72">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox label="Escape special characters in labels?"
|
||||
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
|
||||
id="application.settings.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
|
||||
<x-forms.checkbox label="Readonly labels"
|
||||
helper="If you know what are you doing, you can enable this to edit the labels directly. Coolify won't update labels automatically. <br><br>Be careful, it could break the proxy configuration after you restart the container."
|
||||
id="application.settings.is_container_label_readonly_enabled" instantSave></x-forms.checkbox>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($application->dockerfile)
|
||||
<x-forms.textarea label="Dockerfile" id="application.dockerfile" monacoEditorLanguage="dockerfile"
|
||||
useMonacoEditor rows="6"> </x-forms.textarea>
|
||||
@@ -264,10 +289,13 @@
|
||||
|
||||
<x-forms.textarea label="Container Labels" rows="15" id="customLabels"
|
||||
monacoEditorLanguage="ini" useMonacoEditor></x-forms.textarea>
|
||||
<div class="w-72">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox label="Escape special characters in labels?"
|
||||
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
|
||||
id="application.settings.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
|
||||
<x-forms.checkbox label="Readonly labels"
|
||||
helper="If you know what are you doing, you can enable this to edit the labels directly. Coolify won't update labels automatically. <br><br>Be careful, it could break the proxy configuration after you restart the container."
|
||||
id="application.settings.is_container_label_readonly_enabled" instantSave></x-forms.checkbox>
|
||||
</div>
|
||||
<x-modal-confirmation buttonFullWidth action="resetDefaultLabels"
|
||||
buttonTitle="Reset to Coolify Generated Labels">
|
||||
|
||||
@@ -48,8 +48,7 @@
|
||||
@if ($current_step === 'repository')
|
||||
<h2 class="pb-4">Select a repository</h2>
|
||||
<form class="flex flex-col gap-2 pt-2" wire:submit='submit'>
|
||||
<x-forms.input id="repository_url" required label="Repository Url (https:// or git@)"
|
||||
helper="{!! __('repository.url') !!}" />
|
||||
<x-forms.input id="repository_url" required label="Repository Url (https:// or git@)" />
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="branch" required label="Branch" />
|
||||
<x-forms.select wire:model.live="build_pack" label="Build Pack" required>
|
||||
@@ -62,6 +61,15 @@
|
||||
<x-forms.input id="publish_directory" required label="Publish Directory" />
|
||||
@endif
|
||||
</div>
|
||||
@if ($build_pack === 'dockercompose')
|
||||
<x-forms.input placeholder="/" wire:model.blur="base_directory" label="Base Directory"
|
||||
helper="Directory to use as root. Useful for monorepos." />
|
||||
<x-forms.input placeholder="/docker-compose.yaml" id="docker_compose_location"
|
||||
label="Docker Compose Location"
|
||||
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>" />
|
||||
Compose file location in your repository:<span
|
||||
class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>
|
||||
@endif
|
||||
@if ($show_is_static)
|
||||
<x-forms.input type="number" required id="port" label="Port" :readonly="$is_static || $build_pack === 'static'" />
|
||||
<div class="w-52">
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
@endif
|
||||
</div>
|
||||
<div class="pb-4">Deploy any public or private Git repositories through a GitHub App.</div>
|
||||
|
||||
@if ($github_apps->count() !== 0)
|
||||
<h2 class="pt-4 pb-4">Select a Github App</h2>
|
||||
<div class="flex flex-col gap-2">
|
||||
@@ -92,6 +91,16 @@
|
||||
helper="If there is a build process involved (like Svelte, React, Next, etc..), please specify the output directory for the build assets." />
|
||||
@endif
|
||||
</div>
|
||||
@if ($build_pack === 'dockercompose')
|
||||
<x-forms.input placeholder="/" wire:model.blur="base_directory"
|
||||
label="Base Directory"
|
||||
helper="Directory to use as root. Useful for monorepos." />
|
||||
<x-forms.input placeholder="/docker-compose.yaml" id="docker_compose_location"
|
||||
label="Docker Compose Location"
|
||||
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>" />
|
||||
Compose file location in your repository:<span
|
||||
class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>
|
||||
@endif
|
||||
@if ($show_is_static)
|
||||
<x-forms.input type="number" id="port" label="Port" :readonly="$is_static || $build_pack === 'static'"
|
||||
helper="The port your application listens on." />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div>
|
||||
<h1>Create a new Application</h1>
|
||||
<div class="pb-4">Deploy any public Git repositories.</div>
|
||||
<form class="flex flex-col gap-2" wire:submit='load_branch'>
|
||||
<form class="flex flex-col gap-2" wire:submit='loadBranch'>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-end gap-2">
|
||||
@@ -11,19 +11,12 @@
|
||||
Check repository
|
||||
</x-forms.button>
|
||||
</div>
|
||||
@if (!$branch_found)
|
||||
<div class="px-2 pt-4">
|
||||
<div class="flex flex-col pb-4">
|
||||
<div>Preselect branch (eg: main):</div>
|
||||
<div class='text-helper'>https://github.com/coollabsio/coolify-examples/tree/main</div>
|
||||
</div>
|
||||
<div>
|
||||
For example application deployments, checkout <a class="underline dark:text-white"
|
||||
href="https://github.com/coollabsio/coolify-examples/" target="_blank">Coolify
|
||||
Examples</a>.
|
||||
</div>
|
||||
@endif
|
||||
@if ($branch_found)
|
||||
<div>
|
||||
For example application deployments, checkout <a class="underline dark:text-white"
|
||||
href="https://github.com/coollabsio/coolify-examples/" target="_blank">Coolify
|
||||
Examples</a>.
|
||||
</div>
|
||||
@if ($branchFound)
|
||||
@if ($rate_limit_remaining && $rate_limit_reset)
|
||||
<div class="flex gap-2 py-2">
|
||||
<div>Rate Limit</div>
|
||||
@@ -46,23 +39,34 @@
|
||||
<option value="dockerfile">Dockerfile</option>
|
||||
<option value="dockercompose">Docker Compose</option>
|
||||
</x-forms.select>
|
||||
@if ($is_static)
|
||||
@if ($isStatic)
|
||||
<x-forms.input id="publish_directory" label="Publish Directory"
|
||||
helper="If there is a build process involved (like Svelte, React, Next, etc..), please specify the output directory for the build assets." />
|
||||
@endif
|
||||
</div>
|
||||
@if ($build_pack === 'dockercompose')
|
||||
<x-forms.input placeholder="/" wire:model.blur="base_directory" label="Base Directory"
|
||||
helper="Directory to use as root. Useful for monorepos." />
|
||||
<x-forms.input placeholder="/docker-compose.yaml" id="docker_compose_location"
|
||||
label="Docker Compose Location"
|
||||
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>" />
|
||||
Compose file location in your repository:<span
|
||||
class='dark:text-warning'>{{ Str::start($base_directory . $docker_compose_location, '/') }}</span>
|
||||
@endif
|
||||
@if ($show_is_static)
|
||||
<x-forms.input type="number" id="port" label="Port" :readonly="$is_static || $build_pack === 'static'"
|
||||
<x-forms.input type="number" id="port" label="Port" :readonly="$isStatic || $build_pack === 'static'"
|
||||
helper="The port your application listens on." />
|
||||
<div class="w-52">
|
||||
<x-forms.checkbox instantSave id="is_static" label="Is it a static site?"
|
||||
<x-forms.checkbox instantSave id="isStatic" label="Is it a static site?"
|
||||
helper="If your application is a static site or the final build assets should be served as a static site, enable this." />
|
||||
</div>
|
||||
@endif
|
||||
@if ($build_pack === 'dockercompose' && isDev())
|
||||
<div class="dark:text-warning">If you choose Docker Compose based deployments, you cannot change it afterwards.</div>
|
||||
<x-forms.checkbox instantSave label="New Compose Services (only in dev mode)" id="new_compose_services"></x-forms.checkbox>
|
||||
@endif
|
||||
{{-- @if ($build_pack === 'dockercompose' && isDev())
|
||||
<div class="dark:text-warning">If you choose Docker Compose based deployments, you cannot
|
||||
change it afterwards.</div>
|
||||
<x-forms.checkbox instantSave label="New Compose Services (only in dev mode)"
|
||||
id="new_compose_services"></x-forms.checkbox>
|
||||
@endif --}}
|
||||
</div>
|
||||
<x-forms.button wire:click.prevent='submit'>
|
||||
Continue
|
||||
|
||||
@@ -175,15 +175,15 @@
|
||||
<div class="pb-4 dark:text-warning text-coollabs">If you would like to add a volume, you must add it to
|
||||
your compose file (General tab).</div>
|
||||
@foreach ($applications as $application)
|
||||
<livewire:project.service.storage wire:key="application-{{ $application->id }}"
|
||||
:resource="$application" />
|
||||
<livewire:project.service.storage wire:key="application-{{ $application->id }}" :resource="$application"
|
||||
lazy />
|
||||
@endforeach
|
||||
@foreach ($databases as $database)
|
||||
<livewire:project.service.storage wire:key="database-{{ $database->id }}" :resource="$database" />
|
||||
<livewire:project.service.storage wire:key="database-{{ $database->id }}" :resource="$database" lazy />
|
||||
@endforeach
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'scheduled-tasks'">
|
||||
<livewire:project.shared.scheduled-task.all :resource="$service" />
|
||||
<livewire:project.shared.scheduled-task.all :resource="$service" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'webhooks'">
|
||||
<livewire:project.shared.webhooks :resource="$service" />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user