mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-27 12:33:54 +00:00
Compare commits
149 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 | ||
|
|
6ada6d145c | ||
|
|
0f55e83591 | ||
|
|
4017ea7b65 | ||
|
|
a85066c644 | ||
|
|
b08d38f339 | ||
|
|
d4f4632461 | ||
|
|
666aa041f4 | ||
|
|
1c565fd502 | ||
|
|
7de2b8cbd7 | ||
|
|
852e906736 | ||
|
|
5778466947 | ||
|
|
7006239b0d | ||
|
|
49d011574d | ||
|
|
046a358ae0 | ||
|
|
d23f5af957 | ||
|
|
20a3f4b200 | ||
|
|
73acda833e | ||
|
|
fa895db76e | ||
|
|
88f33be5b6 | ||
|
|
21612cccf7 | ||
|
|
39a7332343 | ||
|
|
21825876fb | ||
|
|
aaee887d3e | ||
|
|
cb44373eff | ||
|
|
4e6ea4f584 | ||
|
|
62a93d3e51 | ||
|
|
f60c281e80 | ||
|
|
43c40cdb09 | ||
|
|
c851262d81 | ||
|
|
91783ccc3e | ||
|
|
6ba3d5f86e | ||
|
|
a9a20755a9 | ||
|
|
d2693c1ac8 | ||
|
|
aaa6f434a9 | ||
|
|
314a3ac83f | ||
|
|
36e177479e | ||
|
|
0c40c0d795 | ||
|
|
25f0a8f0b7 | ||
|
|
65a618d019 | ||
|
|
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
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
)
|
||||
|
||||
[](https://console.algora.io/org/coollabsio/bounties/new)
|
||||
[](https://console.algora.io/org/coollabsio/bounties?status=open)
|
||||
[](https://console.algora.io/org/coollabsio/bounties?status=completed)
|
||||
|
||||
# About the Project
|
||||
|
||||
@@ -49,6 +47,8 @@ Special thanks to our biggest sponsors!
|
||||
<a href="https://coolify.ad.vin/?ref=coolify.io" target="_blank"><img src="./other/logos/advin.png" alt="advin logo" width="250"/></a>
|
||||
<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,
|
||||
]);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Actions\Server\StopSentinel;
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Jobs\CleanupHelperContainersJob;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
@@ -23,6 +24,16 @@ class Init extends Command
|
||||
{
|
||||
$this->alive();
|
||||
get_public_ips();
|
||||
if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
|
||||
$servers = Server::all();
|
||||
foreach ($servers as $server) {
|
||||
$server->settings->update(['is_metrics_enabled' => false]);
|
||||
if ($server->isFunctional()) {
|
||||
StopSentinel::dispatch($server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$full_cleanup = $this->option('full-cleanup');
|
||||
$cleanup_deployments = $this->option('cleanup-deployments');
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class ServiceStatusChanged implements ShouldBroadcast
|
||||
|
||||
public function broadcastOn(): ?array
|
||||
{
|
||||
if ($this->userId) {
|
||||
if (! is_null($this->userId)) {
|
||||
return [
|
||||
new PrivateChannel("user.{$this->userId}"),
|
||||
];
|
||||
|
||||
@@ -65,7 +65,7 @@ class Handler extends ExceptionHandler
|
||||
if ($e instanceof RuntimeException) {
|
||||
return;
|
||||
}
|
||||
$this->settings = InstanceSettings::get();
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
if ($this->settings->do_not_track) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use OpenApi\Attributes as OA;
|
||||
@@ -85,7 +84,7 @@ class OtherController extends Controller
|
||||
if ($teamId !== '0') {
|
||||
return response()->json(['message' => 'You are not allowed to enable the API.'], 403);
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings->update(['is_api_enabled' => true]);
|
||||
|
||||
return response()->json(['message' => 'API enabled.'], 200);
|
||||
@@ -136,7 +135,7 @@ class OtherController extends Controller
|
||||
if ($teamId !== '0') {
|
||||
return response()->json(['message' => 'You are not allowed to disable the API.'], 403);
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings->update(['is_api_enabled' => false]);
|
||||
|
||||
return response()->json(['message' => 'API disabled.'], 200);
|
||||
|
||||
@@ -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,9 +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\InstanceSettings;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server as ModelsServer;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -76,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;
|
||||
|
||||
@@ -301,7 +304,7 @@ class ServersController extends Controller
|
||||
$projects = Project::where('team_id', $teamId)->get();
|
||||
$domains = collect();
|
||||
$applications = $projects->pluck('applications')->flatten();
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
if ($applications->count() > 0) {
|
||||
foreach ($applications as $application) {
|
||||
$ip = $application->destination->server->ip;
|
||||
@@ -393,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();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -15,7 +14,7 @@ class ApiAllowed
|
||||
if (isCloud()) {
|
||||
return $next($request);
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
if ($settings->is_api_enabled === false) {
|
||||
return response()->json(['success' => true, 'message' => 'API is disabled.'], 403);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
@@ -36,7 +35,7 @@ class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$latest_version = get_latest_version_of_coolify();
|
||||
instant_remote_process(["docker pull -q ghcr.io/coollabsio/coolify:{$latest_version}"], $server, false);
|
||||
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$current_version = config('version');
|
||||
if (! $settings->is_auto_update_enabled) {
|
||||
return;
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
@@ -39,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',
|
||||
[
|
||||
@@ -48,7 +47,7 @@ class Help extends Component
|
||||
]
|
||||
);
|
||||
$mail->subject("[HELP]: {$this->subject}");
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
if (! $type) {
|
||||
$url = 'https://app.coolify.io/api/feedback';
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Livewire\Notifications;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Component;
|
||||
@@ -173,7 +172,7 @@ class Email extends Component
|
||||
|
||||
public function copyFromInstanceSettings()
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
if ($settings->smtp_enabled) {
|
||||
$team = currentTeam();
|
||||
$team->update([
|
||||
|
||||
@@ -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,8 +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();
|
||||
@@ -364,6 +370,7 @@ class General extends Component
|
||||
}
|
||||
}
|
||||
$this->validate();
|
||||
|
||||
if ($this->ports_exposes !== $this->application->ports_exposes || $this->is_container_label_escape_enabled !== $this->application->settings->is_container_label_escape_enabled) {
|
||||
$this->resetDefaultLabels();
|
||||
}
|
||||
@@ -390,6 +397,7 @@ class General extends Component
|
||||
}
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||
|
||||
foreach ($this->parsedServiceDomains as $serviceName => $service) {
|
||||
$domain = data_get($service, 'domain');
|
||||
if ($domain) {
|
||||
@@ -399,6 +407,9 @@ class General extends Component
|
||||
check_domain_usage(resource: $this->application);
|
||||
}
|
||||
}
|
||||
if ($this->application->isDirty('docker_compose_domains')) {
|
||||
$this->resetDefaultLabels();
|
||||
}
|
||||
}
|
||||
$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();
|
||||
|
||||
@@ -3,32 +3,63 @@
|
||||
namespace App\Livewire\Project\Shared;
|
||||
|
||||
use App\Models\Tag;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
// Refactored ✅
|
||||
class Tags extends Component
|
||||
{
|
||||
public $resource = null;
|
||||
|
||||
public ?string $new_tag = null;
|
||||
#[Validate('required|string|min:2')]
|
||||
public string $newTags;
|
||||
|
||||
public $tags = [];
|
||||
|
||||
protected $listeners = [
|
||||
'refresh' => '$refresh',
|
||||
];
|
||||
|
||||
protected $rules = [
|
||||
'resource.tags.*.name' => 'required|string|min:2',
|
||||
'new_tag' => 'required|string|min:2',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'new_tag' => 'tag',
|
||||
];
|
||||
public $filteredTags = [];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->loadTags();
|
||||
}
|
||||
|
||||
public function loadTags()
|
||||
{
|
||||
$this->tags = Tag::ownedByCurrentTeam()->get();
|
||||
$this->filteredTags = $this->tags->filter(function ($tag) {
|
||||
return ! $this->resource->tags->contains($tag);
|
||||
});
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$tags = str($this->newTags)->trim()->explode(' ');
|
||||
foreach ($tags as $tag) {
|
||||
if (strlen($tag) < 2) {
|
||||
$this->dispatch('error', 'Invalid tag.', "Tag <span class='dark:text-warning'>$tag</span> is invalid. Min length is 2.");
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($this->resource->tags()->where('name', $tag)->exists()) {
|
||||
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='dark:text-warning'>$tag</span> already added.");
|
||||
|
||||
continue;
|
||||
}
|
||||
$found = Tag::ownedByCurrentTeam()->where(['name' => $tag])->exists();
|
||||
if (! $found) {
|
||||
$found = Tag::create([
|
||||
'name' => $tag,
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
}
|
||||
$this->resource->tags()->attach($found->id);
|
||||
}
|
||||
$this->refresh();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function addTag(string $id, string $name)
|
||||
@@ -39,8 +70,9 @@ class Tags extends Component
|
||||
|
||||
return;
|
||||
}
|
||||
$this->resource->tags()->syncWithoutDetaching($id);
|
||||
$this->resource->tags()->attach($id);
|
||||
$this->refresh();
|
||||
$this->dispatch('success', 'Tag added.');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -50,12 +82,12 @@ class Tags extends Component
|
||||
{
|
||||
try {
|
||||
$this->resource->tags()->detach($id);
|
||||
|
||||
$found_more_tags = Tag::where(['id' => $id, 'team_id' => currentTeam()->id])->first();
|
||||
if ($found_more_tags->applications()->count() == 0 && $found_more_tags->services()->count() == 0) {
|
||||
$found_more_tags = Tag::ownedByCurrentTeam()->find($id);
|
||||
if ($found_more_tags && $found_more_tags->applications()->count() == 0 && $found_more_tags->services()->count() == 0) {
|
||||
$found_more_tags->delete();
|
||||
}
|
||||
$this->refresh();
|
||||
$this->dispatch('success', 'Tag deleted.');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -63,41 +95,8 @@ class Tags extends Component
|
||||
|
||||
public function refresh()
|
||||
{
|
||||
$this->resource->load(['tags']);
|
||||
$this->tags = Tag::ownedByCurrentTeam()->get();
|
||||
$this->new_tag = null;
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'new_tag' => 'required|string|min:2',
|
||||
]);
|
||||
$tags = str($this->new_tag)->trim()->explode(' ');
|
||||
foreach ($tags as $tag) {
|
||||
if ($this->resource->tags()->where('name', $tag)->exists()) {
|
||||
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='dark:text-warning'>$tag</span> already added.");
|
||||
|
||||
continue;
|
||||
}
|
||||
$found = Tag::where(['name' => $tag, 'team_id' => currentTeam()->id])->first();
|
||||
if (! $found) {
|
||||
$found = Tag::create([
|
||||
'name' => $tag,
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
}
|
||||
$this->resource->tags()->syncWithoutDetaching($found->id);
|
||||
}
|
||||
$this->refresh();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.shared.tags');
|
||||
$this->resource->refresh(); // Remove this when legacy_model_binding is false
|
||||
$this->loadTags();
|
||||
$this->reset('newTags');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,49 +4,61 @@ namespace App\Livewire\Project\Shared;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
// Refactored ✅
|
||||
class Webhooks extends Component
|
||||
{
|
||||
public $resource;
|
||||
|
||||
public ?string $deploywebhook = null;
|
||||
public ?string $deploywebhook;
|
||||
|
||||
public ?string $githubManualWebhook = null;
|
||||
public ?string $githubManualWebhook;
|
||||
|
||||
public ?string $gitlabManualWebhook = null;
|
||||
public ?string $gitlabManualWebhook;
|
||||
|
||||
public ?string $bitbucketManualWebhook = null;
|
||||
public ?string $bitbucketManualWebhook;
|
||||
|
||||
public ?string $giteaManualWebhook = null;
|
||||
public ?string $giteaManualWebhook;
|
||||
|
||||
protected $rules = [
|
||||
'resource.manual_webhook_secret_github' => 'nullable|string',
|
||||
'resource.manual_webhook_secret_gitlab' => 'nullable|string',
|
||||
'resource.manual_webhook_secret_bitbucket' => 'nullable|string',
|
||||
'resource.manual_webhook_secret_gitea' => 'nullable|string',
|
||||
];
|
||||
public ?string $githubManualWebhookSecret = null;
|
||||
|
||||
public function saveSecret()
|
||||
public ?string $gitlabManualWebhookSecret = null;
|
||||
|
||||
public ?string $bitbucketManualWebhookSecret = null;
|
||||
|
||||
public ?string $giteaManualWebhookSecret = null;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
// ray()->clearAll();
|
||||
// ray()->showQueries();
|
||||
$this->deploywebhook = generateDeployWebhook($this->resource);
|
||||
|
||||
$this->githubManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_github');
|
||||
$this->githubManualWebhook = generateGitManualWebhook($this->resource, 'github');
|
||||
|
||||
$this->gitlabManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_gitlab');
|
||||
$this->gitlabManualWebhook = generateGitManualWebhook($this->resource, 'gitlab');
|
||||
|
||||
$this->bitbucketManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_bitbucket');
|
||||
$this->bitbucketManualWebhook = generateGitManualWebhook($this->resource, 'bitbucket');
|
||||
|
||||
$this->giteaManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_gitea');
|
||||
$this->giteaManualWebhook = generateGitManualWebhook($this->resource, 'gitea');
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->resource->save();
|
||||
$this->authorize('update', $this->resource);
|
||||
$this->resource->update([
|
||||
'manual_webhook_secret_github' => $this->githubManualWebhookSecret,
|
||||
'manual_webhook_secret_gitlab' => $this->gitlabManualWebhookSecret,
|
||||
'manual_webhook_secret_bitbucket' => $this->bitbucketManualWebhookSecret,
|
||||
'manual_webhook_secret_gitea' => $this->giteaManualWebhookSecret,
|
||||
]);
|
||||
$this->dispatch('success', 'Secret Saved.');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->deploywebhook = generateDeployWebhook($this->resource);
|
||||
$this->githubManualWebhook = generateGitManualWebhook($this->resource, 'github');
|
||||
$this->gitlabManualWebhook = generateGitManualWebhook($this->resource, 'gitlab');
|
||||
$this->bitbucketManualWebhook = generateGitManualWebhook($this->resource, 'bitbucket');
|
||||
$this->giteaManualWebhook = generateGitManualWebhook($this->resource, 'gitea');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.shared.webhooks');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class Index extends Component
|
||||
public function mount()
|
||||
{
|
||||
if (isInstanceAdmin()) {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$database = StandalonePostgresql::whereName('coolify-db')->first();
|
||||
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
|
||||
if ($database) {
|
||||
|
||||
@@ -29,7 +29,7 @@ class License extends Component
|
||||
abort(404);
|
||||
}
|
||||
$this->instance_id = config('app.id');
|
||||
$this->settings = InstanceSettings::get();
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Livewire\Source\Github;
|
||||
|
||||
use App\Jobs\GithubAppPermissionJob;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -100,7 +99,7 @@ class Change extends Component
|
||||
return redirect()->route('source.all');
|
||||
}
|
||||
$this->applications = $this->github_app->applications;
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
|
||||
$this->name = str($this->github_app->name)->kebab();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -23,7 +23,7 @@ class Index extends Component
|
||||
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
|
||||
return redirect()->route('subscription.show');
|
||||
}
|
||||
$this->settings = InstanceSettings::get();
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
@@ -318,7 +252,7 @@ respond 404
|
||||
|
||||
public function setupDynamicProxyConfiguration()
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$dynamic_config_path = $this->proxyPath().'/dynamic';
|
||||
if ($this->proxyType() === 'TRAEFIK_V2') {
|
||||
$file = "$dynamic_config_path/coolify.yaml";
|
||||
|
||||
@@ -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', [
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Notifications\Channels;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Mail\Message;
|
||||
@@ -14,7 +13,7 @@ class TransactionalEmailChannel
|
||||
{
|
||||
public function send(User $notifiable, Notification $notification): void
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
if (! data_get($settings, 'smtp_enabled') && ! data_get($settings, 'resend_enabled')) {
|
||||
Log::info('SMTP/Resend not enabled');
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -18,7 +18,7 @@ class ResetPassword extends Notification
|
||||
|
||||
public function __construct($token)
|
||||
{
|
||||
$this->settings = InstanceSettings::get();
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\PersonalAccessToken;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
@@ -14,6 +16,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
public function boot(): void
|
||||
{
|
||||
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
|
||||
|
||||
Http::macro('github', function (string $api_url, ?string $github_access_token = null) {
|
||||
if ($github_access_token) {
|
||||
return Http::withHeaders([
|
||||
@@ -27,5 +30,9 @@ class AppServiceProvider extends ServiceProvider
|
||||
])->baseUrl($api_url);
|
||||
}
|
||||
});
|
||||
// if (! env('CI')) {
|
||||
// View::share('instanceSettings', InstanceSettings::get());
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\Actions\Fortify\CreateNewUser;
|
||||
use App\Actions\Fortify\ResetUserPassword;
|
||||
use App\Actions\Fortify\UpdateUserPassword;
|
||||
use App\Actions\Fortify\UpdateUserProfileInformation;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\OauthSetting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
@@ -45,7 +44,7 @@ class FortifyServiceProvider extends ServiceProvider
|
||||
{
|
||||
Fortify::createUsersUsing(CreateNewUser::class);
|
||||
Fortify::registerView(function () {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
if (! $settings->is_registration_enabled) {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
@@ -57,7 +56,7 @@ class FortifyServiceProvider extends ServiceProvider
|
||||
});
|
||||
|
||||
Fortify::loginView(function () {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$enabled_oauth_providers = OauthSetting::where('enabled', true)->get();
|
||||
$users = User::count();
|
||||
if ($users == 0) {
|
||||
|
||||
@@ -40,6 +40,7 @@ const SUPPORTED_OS = [
|
||||
'ubuntu debian raspbian',
|
||||
'centos fedora rhel ol rocky amzn almalinux',
|
||||
'sles opensuse-leap opensuse-tumbleweed',
|
||||
'arch',
|
||||
];
|
||||
|
||||
const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment'];
|
||||
|
||||
@@ -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)) {
|
||||
@@ -244,13 +244,13 @@ function generate_application_name(string $git_repository, string $git_branch, ?
|
||||
|
||||
function is_transactional_emails_active(): bool
|
||||
{
|
||||
return isEmailEnabled(InstanceSettings::get());
|
||||
return isEmailEnabled(\App\Models\InstanceSettings::get());
|
||||
}
|
||||
|
||||
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string
|
||||
{
|
||||
if (! $settings) {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
}
|
||||
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
|
||||
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
|
||||
@@ -284,7 +284,7 @@ function base_ip(): string
|
||||
if (isDev()) {
|
||||
return 'localhost';
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
if ($settings->public_ipv4) {
|
||||
return "$settings->public_ipv4";
|
||||
}
|
||||
@@ -312,7 +312,7 @@ function getFqdnWithoutPort(string $fqdn)
|
||||
*/
|
||||
function base_url(bool $withPort = true): string
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
if ($settings->fqdn) {
|
||||
return $settings->fqdn;
|
||||
}
|
||||
@@ -379,7 +379,7 @@ function send_internal_notification(string $message): void
|
||||
}
|
||||
function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null): void
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
if (! $type) {
|
||||
throw new Exception('No email settings found.');
|
||||
@@ -774,6 +774,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$allServices = get_service_templates();
|
||||
$topLevelVolumes = collect(data_get($yaml, 'volumes', []));
|
||||
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
|
||||
$topLevelConfigs = collect(data_get($yaml, 'configs', []));
|
||||
$topLevelSecrets = collect(data_get($yaml, 'secrets', []));
|
||||
$services = data_get($yaml, 'services');
|
||||
|
||||
$generatedServiceFQDNS = collect([]);
|
||||
@@ -975,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');
|
||||
}
|
||||
@@ -983,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') {
|
||||
@@ -1056,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
|
||||
@@ -1402,6 +1407,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
'services' => $services->toArray(),
|
||||
'volumes' => $topLevelVolumes->toArray(),
|
||||
'networks' => $topLevelNetworks->toArray(),
|
||||
'configs' => $topLevelConfigs->toArray(),
|
||||
'secrets' => $topLevelSecrets->toArray(),
|
||||
];
|
||||
$yaml = data_forget($yaml, 'services.*.volumes.*.content');
|
||||
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||
@@ -1441,6 +1448,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
|
||||
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
|
||||
$topLevelConfigs = collect(data_get($yaml, 'configs', []));
|
||||
$topLevelSecrets = collect(data_get($yaml, 'secrets', []));
|
||||
$services = data_get($yaml, 'services');
|
||||
|
||||
$generatedServiceFQDNS = collect([]);
|
||||
@@ -1682,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);
|
||||
@@ -1690,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 {
|
||||
@@ -1734,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();
|
||||
});
|
||||
@@ -1835,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
|
||||
@@ -2027,14 +2039,20 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
domains: $fqdns,
|
||||
serviceLabels: $serviceLabels,
|
||||
generate_unique_uuid: $resource->build_pack === 'dockercompose',
|
||||
image: data_get($service, 'image')
|
||||
image: data_get($service, 'image'),
|
||||
is_force_https_enabled: $resource->isForceHttpsEnabled(),
|
||||
is_gzip_enabled: $resource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $resource->isStripprefixEnabled(),
|
||||
));
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
||||
network: $resource->destination->network,
|
||||
uuid: $resource->uuid,
|
||||
domains: $fqdns,
|
||||
serviceLabels: $serviceLabels,
|
||||
image: data_get($service, 'image')
|
||||
image: data_get($service, 'image'),
|
||||
is_force_https_enabled: $resource->isForceHttpsEnabled(),
|
||||
is_gzip_enabled: $resource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $resource->isStripprefixEnabled(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -2080,6 +2098,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
'services' => $services->toArray(),
|
||||
'volumes' => $topLevelVolumes->toArray(),
|
||||
'networks' => $topLevelNetworks->toArray(),
|
||||
'configs' => $topLevelConfigs->toArray(),
|
||||
'secrets' => $topLevelSecrets->toArray(),
|
||||
];
|
||||
if ($isSameDockerComposeFile) {
|
||||
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||
@@ -2178,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')
|
||||
@@ -2198,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')
|
||||
@@ -2244,7 +2264,7 @@ function validate_dns_entry(string $fqdn, Server $server)
|
||||
if (str($host)->contains('sslip.io')) {
|
||||
return true;
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$is_dns_validation_enabled = data_get($settings, 'is_dns_validation_enabled');
|
||||
if (! $is_dns_validation_enabled) {
|
||||
return true;
|
||||
@@ -2364,7 +2384,7 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId =
|
||||
if ($domainFound) {
|
||||
return true;
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
if (data_get($settings, 'fqdn')) {
|
||||
$domain = data_get($settings, 'fqdn');
|
||||
if (str($domain)->endsWith('/')) {
|
||||
@@ -2381,7 +2401,6 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
|
||||
if ($resource) {
|
||||
if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose') {
|
||||
$domains = data_get(json_decode($resource->docker_compose_domains, true), '*.domain');
|
||||
ray($domains);
|
||||
$domains = collect($domains);
|
||||
} else {
|
||||
$domains = collect($resource->fqdns);
|
||||
@@ -2437,7 +2456,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
|
||||
}
|
||||
}
|
||||
if ($resource) {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
if (data_get($settings, 'fqdn')) {
|
||||
$domain = data_get($settings, 'fqdn');
|
||||
if (str($domain)->endsWith('/')) {
|
||||
@@ -2512,7 +2531,7 @@ function get_public_ips()
|
||||
{
|
||||
try {
|
||||
echo "Refreshing public ips!\n";
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
[$first, $second] = Process::concurrently(function (Pool $pool) {
|
||||
$pool->path(__DIR__)->command('curl -4s https://ifconfig.io');
|
||||
$pool->path(__DIR__)->command('curl -6s https://ifconfig.io');
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"poliander/cron": "^3.0",
|
||||
"purplepixie/phpdns": "^2.1",
|
||||
"pusher/pusher-php-server": "^7.2",
|
||||
"resend/resend-laravel": "^0.13.0",
|
||||
"sentry/sentry-laravel": "^4.6",
|
||||
"socialiteproviders/microsoft-azure": "^5.1",
|
||||
"spatie/laravel-activitylog": "^4.7.3",
|
||||
@@ -106,4 +107,4 @@
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true
|
||||
}
|
||||
}
|
||||
|
||||
128
composer.lock
generated
128
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "c7c9cc002a9765c2395717c69ba8bfc6",
|
||||
"content-hash": "cb17445966de6094aef5a92ee59d1d77",
|
||||
"packages": [
|
||||
{
|
||||
"name": "amphp/amp",
|
||||
@@ -7129,6 +7129,132 @@
|
||||
],
|
||||
"time": "2024-07-01T14:24:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "resend/resend-laravel",
|
||||
"version": "v0.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/resend/resend-laravel.git",
|
||||
"reference": "23aed22df0d0b23c2952da2aaed6a8b88d301a8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/resend/resend-laravel/zipball/23aed22df0d0b23c2952da2aaed6a8b88d301a8a",
|
||||
"reference": "23aed22df0d0b23c2952da2aaed6a8b88d301a8a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/http": "^10.0|^11.0",
|
||||
"illuminate/support": "^10.0|^11.0",
|
||||
"php": "^8.1",
|
||||
"resend/resend-php": "^0.12.0",
|
||||
"symfony/mailer": "^6.2|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.14",
|
||||
"mockery/mockery": "^1.5",
|
||||
"orchestra/testbench": "^8.17|^9.0",
|
||||
"pestphp/pest": "^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Resend\\Laravel\\ResendServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Resend\\Laravel\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Resend and contributors",
|
||||
"homepage": "https://github.com/resend/resend-laravel/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Resend for Laravel",
|
||||
"homepage": "https://resend.com/",
|
||||
"keywords": [
|
||||
"api",
|
||||
"client",
|
||||
"laravel",
|
||||
"php",
|
||||
"resend",
|
||||
"sdk"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/resend/resend-laravel/issues",
|
||||
"source": "https://github.com/resend/resend-laravel/tree/v0.13.0"
|
||||
},
|
||||
"time": "2024-07-08T18:51:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "resend/resend-php",
|
||||
"version": "v0.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/resend/resend-php.git",
|
||||
"reference": "37fb79bb8160ce2de521bf37484ba59e89236521"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/resend/resend-php/zipball/37fb79bb8160ce2de521bf37484ba59e89236521",
|
||||
"reference": "37fb79bb8160ce2de521bf37484ba59e89236521",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^7.5",
|
||||
"php": "^8.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.13",
|
||||
"mockery/mockery": "^1.6",
|
||||
"pestphp/pest": "^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/Resend.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Resend\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Resend and contributors",
|
||||
"homepage": "https://github.com/resend/resend-php/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Resend PHP library.",
|
||||
"homepage": "https://resend.com/",
|
||||
"keywords": [
|
||||
"api",
|
||||
"client",
|
||||
"php",
|
||||
"resend",
|
||||
"sdk"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/resend/resend-php/issues",
|
||||
"source": "https://github.com/resend/resend-php/tree/v0.12.0"
|
||||
},
|
||||
"time": "2024-03-04T03:16:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "revolt/event-loop",
|
||||
"version": "v1.0.6",
|
||||
|
||||
@@ -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.309',
|
||||
'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.309';
|
||||
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');
|
||||
});
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user