diff --git a/.github/workflows/development-build.yml b/.github/workflows/development-build.yml
index d64af3b59..268b885ac 100644
--- a/.github/workflows/development-build.yml
+++ b/.github/workflows/development-build.yml
@@ -26,7 +26,7 @@ jobs:
uses: docker/build-push-action@v5
with:
context: .
- file: docker/prod-ssu/Dockerfile
+ file: docker/prod/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
@@ -47,7 +47,7 @@ jobs:
uses: docker/build-push-action@v5
with:
context: .
- file: docker/prod-ssu/Dockerfile
+ file: docker/prod/Dockerfile
platforms: linux/aarch64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
diff --git a/.github/workflows/production-build.yml b/.github/workflows/production-build.yml
index 3709b5b46..e4bad6a65 100644
--- a/.github/workflows/production-build.yml
+++ b/.github/workflows/production-build.yml
@@ -29,7 +29,7 @@ jobs:
uses: docker/build-push-action@v5
with:
context: .
- file: docker/prod-ssu/Dockerfile
+ file: docker/prod/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
@@ -51,7 +51,7 @@ jobs:
uses: docker/build-push-action@v5
with:
context: .
- file: docker/prod-ssu/Dockerfile
+ file: docker/prod/Dockerfile
platforms: linux/aarch64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
diff --git a/README.md b/README.md
index 97580b875..c4e575e16 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,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
Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc.
@@ -39,9 +42,11 @@ Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
+
+
diff --git a/app/Actions/CoolifyTask/RunRemoteProcess.php b/app/Actions/CoolifyTask/RunRemoteProcess.php
index 2c90750e6..16924476b 100644
--- a/app/Actions/CoolifyTask/RunRemoteProcess.php
+++ b/app/Actions/CoolifyTask/RunRemoteProcess.php
@@ -60,7 +60,7 @@ class RunRemoteProcess
$decoded = json_decode(
data_get($activity, 'description'),
associative: true,
- flags: JSON_THROW_ON_ERROR
+ flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE
);
} catch (\JsonException $exception) {
return '';
@@ -164,8 +164,7 @@ class RunRemoteProcess
public function encodeOutput($type, $output)
{
- $outputStack = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR);
-
+ $outputStack = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
$outputStack[] = [
'type' => $type,
'output' => $output,
@@ -174,12 +173,12 @@ class RunRemoteProcess
'order' => $this->getLatestCounter(),
];
- return json_encode($outputStack, flags: JSON_THROW_ON_ERROR);
+ return json_encode($outputStack, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
}
protected function getLatestCounter(): int
{
- $description = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR);
+ $description = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
if ($description === null || count($description) === 0) {
return 1;
}
diff --git a/app/Actions/Database/StartClickhouse.php b/app/Actions/Database/StartClickhouse.php
index 5f567802f..414d6b407 100644
--- a/app/Actions/Database/StartClickhouse.php
+++ b/app/Actions/Database/StartClickhouse.php
@@ -29,6 +29,7 @@ class StartClickhouse
];
$persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->database->fileStorages()->get();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
$environment_variables = $this->generate_environment_variables();
@@ -93,6 +94,11 @@ class StartClickhouse
if (count($persistent_storages) > 0) {
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
}
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
diff --git a/app/Actions/Database/StartDragonfly.php b/app/Actions/Database/StartDragonfly.php
index 92daf195d..04348c40a 100644
--- a/app/Actions/Database/StartDragonfly.php
+++ b/app/Actions/Database/StartDragonfly.php
@@ -32,6 +32,7 @@ class StartDragonfly
];
$persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->database->fileStorages()->get();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
$environment_variables = $this->generate_environment_variables();
@@ -94,6 +95,11 @@ class StartDragonfly
if (count($persistent_storages) > 0) {
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
}
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
diff --git a/app/Actions/Database/StartKeydb.php b/app/Actions/Database/StartKeydb.php
index 8c833efd5..672308d89 100644
--- a/app/Actions/Database/StartKeydb.php
+++ b/app/Actions/Database/StartKeydb.php
@@ -32,6 +32,7 @@ class StartKeydb
];
$persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->database->fileStorages()->get();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
$environment_variables = $this->generate_environment_variables();
$this->add_custom_keydb();
@@ -92,6 +93,11 @@ class StartKeydb
if (count($persistent_storages) > 0) {
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
}
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php
index c79df0dc5..652d8fa29 100644
--- a/app/Actions/Database/StartMariadb.php
+++ b/app/Actions/Database/StartMariadb.php
@@ -28,6 +28,7 @@ class StartMariadb
];
$persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->database->fileStorages()->get();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
$environment_variables = $this->generate_environment_variables();
$this->add_custom_mysql();
@@ -86,6 +87,11 @@ class StartMariadb
if (count($persistent_storages) > 0) {
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
}
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php
index 46b426ad8..38e2621bd 100644
--- a/app/Actions/Database/StartMongodb.php
+++ b/app/Actions/Database/StartMongodb.php
@@ -30,6 +30,7 @@ class StartMongodb
];
$persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->database->fileStorages()->get();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
$environment_variables = $this->generate_environment_variables();
$this->add_custom_mongo_conf();
@@ -94,6 +95,11 @@ class StartMongodb
if (count($persistent_storages) > 0) {
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
}
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php
index 6fdc8cdad..604e72fde 100644
--- a/app/Actions/Database/StartMysql.php
+++ b/app/Actions/Database/StartMysql.php
@@ -28,6 +28,7 @@ class StartMysql
];
$persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->database->fileStorages()->get();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
$environment_variables = $this->generate_environment_variables();
$this->add_custom_mysql();
@@ -86,6 +87,11 @@ class StartMysql
if (count($persistent_storages) > 0) {
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
}
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php
index 8db874ea6..554e347d9 100644
--- a/app/Actions/Database/StartPostgresql.php
+++ b/app/Actions/Database/StartPostgresql.php
@@ -29,6 +29,7 @@ class StartPostgresql
];
$persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->database->fileStorages()->get();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
$environment_variables = $this->generate_environment_variables();
$this->generate_init_scripts();
@@ -92,6 +93,11 @@ class StartPostgresql
if (count($persistent_storages) > 0) {
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
}
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php
index 5b6ab2999..055d82600 100644
--- a/app/Actions/Database/StartRedis.php
+++ b/app/Actions/Database/StartRedis.php
@@ -32,6 +32,7 @@ class StartRedis
];
$persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->database->fileStorages()->get();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
$environment_variables = $this->generate_environment_variables();
$this->add_custom_redis();
@@ -96,6 +97,11 @@ class StartRedis
if (count($persistent_storages) > 0) {
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
}
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php
index f0e1de8f6..a8a9185ba 100644
--- a/app/Actions/Docker/GetContainersStatus.php
+++ b/app/Actions/Docker/GetContainersStatus.php
@@ -221,7 +221,19 @@ class GetContainersStatus
}
$name = data_get($exitedService, 'name');
$fqdn = data_get($exitedService, 'fqdn');
- $containerName = $name ? "$name, available at $fqdn" : $fqdn;
+ if ($name) {
+ if ($fqdn) {
+ $containerName = "$name, available at $fqdn";
+ } else {
+ $containerName = $name;
+ }
+ } else {
+ if ($fqdn) {
+ $containerName = $fqdn;
+ } else {
+ $containerName = null;
+ }
+ }
$projectUuid = data_get($service, 'environment.project.uuid');
$serviceUuid = data_get($service, 'uuid');
$environmentName = data_get($service, 'environment.name');
@@ -231,7 +243,7 @@ class GetContainersStatus
} else {
$url = null;
}
- $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
$exitedService->update(['status' => 'exited']);
}
@@ -258,7 +270,7 @@ class GetContainersStatus
$url = null;
}
- $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
foreach ($notRunningApplicationPreviews as $previewId) {
@@ -283,7 +295,7 @@ class GetContainersStatus
$url = null;
}
- $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
foreach ($notRunningDatabases as $database) {
@@ -307,7 +319,7 @@ class GetContainersStatus
} else {
$url = null;
}
- $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
// Check if proxy is running
@@ -539,7 +551,19 @@ class GetContainersStatus
}
$name = data_get($exitedService, 'name');
$fqdn = data_get($exitedService, 'fqdn');
- $containerName = $name ? "$name, available at $fqdn" : $fqdn;
+ if ($name) {
+ if ($fqdn) {
+ $containerName = "$name, available at $fqdn";
+ } else {
+ $containerName = $name;
+ }
+ } else {
+ if ($fqdn) {
+ $containerName = $fqdn;
+ } else {
+ $containerName = null;
+ }
+ }
$projectUuid = data_get($service, 'environment.project.uuid');
$serviceUuid = data_get($service, 'uuid');
$environmentName = data_get($service, 'environment.name');
@@ -549,7 +573,7 @@ class GetContainersStatus
} else {
$url = null;
}
- $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
$exitedService->update(['status' => 'exited']);
}
@@ -576,7 +600,7 @@ class GetContainersStatus
$url = null;
}
- $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
foreach ($notRunningApplicationPreviews as $previewId) {
@@ -601,7 +625,7 @@ class GetContainersStatus
$url = null;
}
- $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
foreach ($notRunningDatabases as $database) {
@@ -625,7 +649,7 @@ class GetContainersStatus
} else {
$url = null;
}
- $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
// Check if proxy is running
diff --git a/app/Actions/Proxy/CheckProxy.php b/app/Actions/Proxy/CheckProxy.php
index 9323ca1d1..b1fb6a3cb 100644
--- a/app/Actions/Proxy/CheckProxy.php
+++ b/app/Actions/Proxy/CheckProxy.php
@@ -10,7 +10,18 @@ class CheckProxy
use AsAction;
public function handle(Server $server, $fromUI = false)
{
- if ($server->proxyType() === 'NONE') {
+ if (!$server->isFunctional()) {
+ return false;
+ }
+ if ($server->isBuildServer()) {
+ if ($server->proxy) {
+ $server->proxy = null;
+ $server->save();
+ }
+ return false;
+ }
+ $proxyType = $server->proxyType();
+ if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop) {
return false;
}
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php
index 5b1ffa409..92a5e5b56 100644
--- a/app/Actions/Proxy/StartProxy.php
+++ b/app/Actions/Proxy/StartProxy.php
@@ -15,7 +15,7 @@ class StartProxy
{
try {
$proxyType = $server->proxyType();
- if (is_null($proxyType) || $proxyType === 'NONE') {
+ if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop || $server->isBuildServer()) {
return 'OK';
}
$commands = collect([]);
diff --git a/app/Actions/Server/UpdateCoolify.php b/app/Actions/Server/UpdateCoolify.php
index ac548a951..c992ed786 100644
--- a/app/Actions/Server/UpdateCoolify.php
+++ b/app/Actions/Server/UpdateCoolify.php
@@ -12,12 +12,10 @@ class UpdateCoolify
public ?Server $server = null;
public ?string $latestVersion = null;
public ?string $currentVersion = null;
- public bool $async = false;
- public function handle(bool $force = false, bool $async = false)
+ public function handle($manual_update = false)
{
try {
- $this->async = $async;
$settings = InstanceSettings::get();
ray('Running InstanceAutoUpdateJob');
$this->server = Server::find(0);
@@ -27,24 +25,18 @@ class UpdateCoolify
CleanupDocker::run($this->server, false);
$this->latestVersion = get_latest_version_of_coolify();
$this->currentVersion = config('version');
- // if ($settings->next_channel) {
- // ray('next channel enabled');
- // $this->latestVersion = 'next';
- // }
- if ($force) {
- $this->update();
- } else {
+ if (!$manual_update) {
if (!$settings->is_auto_update_enabled) {
- return 'Auto update is disabled';
+ return;
}
if ($this->latestVersion === $this->currentVersion) {
- return 'Already on latest version';
+ return;
}
if (version_compare($this->latestVersion, $this->currentVersion, '<')) {
- return 'Latest version is lower than current version?!';
+ return;
}
- $this->update();
}
+ $this->update();
} catch (\Throwable $e) {
ray('InstanceAutoUpdateJob failed');
ray($e->getMessage());
@@ -56,34 +48,16 @@ class UpdateCoolify
private function update()
{
if (isDev()) {
- ray("Running update on local docker container. Updating to $this->latestVersion");
- if ($this->async) {
- ray('Running async update');
- remote_process([
- "sleep 10"
- ], $this->server);
- } else {
- instant_remote_process([
- "sleep 10"
- ], $this->server);
- }
- ray('Update done');
- return;
- } else {
- ray('Running update on production server');
- if ($this->async) {
- remote_process([
- "curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
- "bash /data/coolify/source/upgrade.sh $this->latestVersion"
- ], $this->server);
- } else {
- instant_remote_process([
- "curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
- "bash /data/coolify/source/upgrade.sh $this->latestVersion"
- ], $this->server);
- }
- send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
+ remote_process([
+ "sleep 10"
+ ], $this->server);
return;
}
+ remote_process([
+ "curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
+ "bash /data/coolify/source/upgrade.sh $this->latestVersion"
+ ], $this->server);
+ send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
+ return;
}
}
diff --git a/app/Console/Commands/ServicesGenerate.php b/app/Console/Commands/ServicesGenerate.php
index 7619a1d85..d96d4743c 100644
--- a/app/Console/Commands/ServicesGenerate.php
+++ b/app/Console/Commands/ServicesGenerate.php
@@ -40,7 +40,7 @@ class ServicesGenerate extends Command
$serviceTemplatesJson[$name] = $parsed;
}
}
- $serviceTemplatesJson = json_encode($serviceTemplatesJson, JSON_PRETTY_PRINT);
+ $serviceTemplatesJson = json_encode($serviceTemplatesJson);
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson);
}
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index 23289f90e..ab8794877 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -10,6 +10,9 @@ use App\Jobs\InstanceAutoUpdateJob;
use App\Jobs\ContainerStatusJob;
use App\Jobs\PullHelperImageJob;
use App\Jobs\PullSentinelImageJob;
+use App\Jobs\PullTemplatesAndVersions;
+use App\Jobs\PullTemplatesFromCDN;
+use App\Jobs\PullVersionsFromCDN;
use App\Jobs\ServerStatusJob;
use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
@@ -29,6 +32,8 @@ class Kernel extends ConsoleKernel
// Instance Jobs
$schedule->command('horizon:snapshot')->everyMinute();
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
+ $schedule->job(new PullVersionsFromCDN)->everyTenMinutes()->onOneServer();
+ $schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
// Server Jobs
$this->check_scheduled_backups($schedule);
@@ -41,7 +46,8 @@ class Kernel extends ConsoleKernel
// Instance Jobs
$schedule->command('horizon:snapshot')->everyFiveMinutes();
$schedule->command('cleanup:unreachable-servers')->daily();
-
+ $schedule->job(new PullVersionsFromCDN)->everyTenMinutes()->onOneServer();
+ $schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
diff --git a/app/Http/Controllers/Webhook/Bitbucket.php b/app/Http/Controllers/Webhook/Bitbucket.php
index 7b569c278..1fc7ea453 100644
--- a/app/Http/Controllers/Webhook/Bitbucket.php
+++ b/app/Http/Controllers/Webhook/Bitbucket.php
@@ -82,7 +82,7 @@ class Bitbucket extends Controller
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
- 'message' => 'Invalid token.',
+ 'message' => 'Invalid signature.',
]);
ray('Invalid signature');
continue;
diff --git a/app/Http/Controllers/Webhook/Gitea.php b/app/Http/Controllers/Webhook/Gitea.php
new file mode 100644
index 000000000..775e2c17e
--- /dev/null
+++ b/app/Http/Controllers/Webhook/Gitea.php
@@ -0,0 +1,222 @@
+header('X-Gitea-Delivery');
+ if (app()->isDownForMaintenance()) {
+ ray('Maintenance mode is on');
+ $epoch = now()->valueOf();
+ $files = Storage::disk('webhooks-during-maintenance')->files();
+ $gitea_delivery_found = collect($files)->filter(function ($file) use ($x_gitea_delivery) {
+ return Str::contains($file, $x_gitea_delivery);
+ })->first();
+ if ($gitea_delivery_found) {
+ ray('Webhook already found');
+ return;
+ }
+ $data = [
+ 'attributes' => $request->attributes->all(),
+ 'request' => $request->request->all(),
+ 'query' => $request->query->all(),
+ 'server' => $request->server->all(),
+ 'files' => $request->files->all(),
+ 'cookies' => $request->cookies->all(),
+ 'headers' => $request->headers->all(),
+ 'content' => $request->getContent(),
+ ];
+ $json = json_encode($data);
+ Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitea::manual_{$x_gitea_delivery}", $json);
+ return;
+ }
+ $x_gitea_event = Str::lower($request->header('X-Gitea-Event'));
+ $x_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256=');
+ $content_type = $request->header('Content-Type');
+ $payload = $request->collect();
+ if ($x_gitea_event === 'ping') {
+ // Just pong
+ return response('pong');
+ }
+
+ if ($content_type !== 'application/json') {
+ $payload = json_decode(data_get($payload, 'payload'), true);
+ }
+ if ($x_gitea_event === 'push') {
+ $branch = data_get($payload, 'ref');
+ $full_name = data_get($payload, 'repository.full_name');
+ if (Str::isMatch('/refs\/heads\/*/', $branch)) {
+ $branch = Str::after($branch, 'refs/heads/');
+ }
+ $added_files = data_get($payload, 'commits.*.added');
+ $removed_files = data_get($payload, 'commits.*.removed');
+ $modified_files = data_get($payload, 'commits.*.modified');
+ $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
+ ray($changed_files);
+ ray('Manual Webhook Gitea Push Event with branch: ' . $branch);
+ }
+ if ($x_gitea_event === 'pull_request') {
+ $action = data_get($payload, 'action');
+ $full_name = data_get($payload, 'repository.full_name');
+ $pull_request_id = data_get($payload, 'number');
+ $pull_request_html_url = data_get($payload, 'pull_request.html_url');
+ $branch = data_get($payload, 'pull_request.head.ref');
+ $base_branch = data_get($payload, 'pull_request.base.ref');
+ ray('Webhook Gitea Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
+ }
+ if (!$branch) {
+ return response('Nothing to do. No branch found in the request.');
+ }
+ $applications = Application::where('git_repository', 'like', "%$full_name%");
+ if ($x_gitea_event === 'push') {
+ $applications = $applications->where('git_branch', $branch)->get();
+ if ($applications->isEmpty()) {
+ return response("Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.");
+ }
+ }
+ if ($x_gitea_event === 'pull_request') {
+ $applications = $applications->where('git_branch', $base_branch)->get();
+ if ($applications->isEmpty()) {
+ return response("Nothing to do. No applications found with branch '$base_branch'.");
+ }
+ }
+ foreach ($applications as $application) {
+ $webhook_secret = data_get($application, 'manual_webhook_secret_gitea');
+ $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
+ if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) {
+ ray('Invalid signature');
+ $return_payloads->push([
+ 'application' => $application->name,
+ 'status' => 'failed',
+ 'message' => 'Invalid signature.',
+ ]);
+ continue;
+ }
+ $isFunctional = $application->destination->server->isFunctional();
+ if (!$isFunctional) {
+ $return_payloads->push([
+ 'application' => $application->name,
+ 'status' => 'failed',
+ 'message' => 'Server is not functional.',
+ ]);
+ continue;
+ }
+ if ($x_gitea_event === 'push') {
+ if ($application->isDeployable()) {
+ $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
+ if ($is_watch_path_triggered || is_null($application->watch_paths)) {
+ ray('Deploying ' . $application->name . ' with branch ' . $branch);
+ $deployment_uuid = new Cuid2(7);
+ queue_application_deployment(
+ application: $application,
+ deployment_uuid: $deployment_uuid,
+ force_rebuild: false,
+ commit: data_get($payload, 'after', 'HEAD'),
+ is_webhook: true,
+ );
+ $return_payloads->push([
+ 'status' => 'success',
+ 'message' => 'Deployment queued.',
+ 'application_uuid' => $application->uuid,
+ 'application_name' => $application->name,
+ ]);
+ } else {
+ $paths = str($application->watch_paths)->explode("\n");
+ $return_payloads->push([
+ 'status' => 'failed',
+ 'message' => 'Changed files do not match watch paths. Ignoring deployment.',
+ 'application_uuid' => $application->uuid,
+ 'application_name' => $application->name,
+ 'details' => [
+ 'changed_files' => $changed_files,
+ 'watch_paths' => $paths,
+ ],
+ ]);
+ }
+ } else {
+ $return_payloads->push([
+ 'status' => 'failed',
+ 'message' => 'Deployments disabled.',
+ 'application_uuid' => $application->uuid,
+ 'application_name' => $application->name,
+ ]);
+ }
+ }
+ if ($x_gitea_event === 'pull_request') {
+ if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
+ if ($application->isPRDeployable()) {
+ $deployment_uuid = new Cuid2(7);
+ $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
+ if (!$found) {
+ ApplicationPreview::create([
+ 'git_type' => 'gitea',
+ 'application_id' => $application->id,
+ 'pull_request_id' => $pull_request_id,
+ 'pull_request_html_url' => $pull_request_html_url,
+ ]);
+ }
+ queue_application_deployment(
+ application: $application,
+ pull_request_id: $pull_request_id,
+ deployment_uuid: $deployment_uuid,
+ force_rebuild: false,
+ commit: data_get($payload, 'head.sha', 'HEAD'),
+ is_webhook: true,
+ git_type: 'gitea'
+ );
+ $return_payloads->push([
+ 'application' => $application->name,
+ 'status' => 'success',
+ 'message' => 'Preview deployment queued.',
+ ]);
+ } else {
+ $return_payloads->push([
+ 'application' => $application->name,
+ 'status' => 'failed',
+ 'message' => 'Preview deployments disabled.',
+ ]);
+ }
+ }
+ if ($action === 'closed') {
+ $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
+ if ($found) {
+ $found->delete();
+ $container_name = generateApplicationContainerName($application, $pull_request_id);
+ // ray('Stopping container: ' . $container_name);
+ instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
+ $return_payloads->push([
+ 'application' => $application->name,
+ 'status' => 'success',
+ 'message' => 'Preview deployment closed.',
+ ]);
+ } else {
+ $return_payloads->push([
+ 'application' => $application->name,
+ 'status' => 'failed',
+ 'message' => 'No preview deployment found.',
+ ]);
+ }
+ }
+ }
+ }
+ ray($return_payloads);
+ return response($return_payloads);
+ } catch (Exception $e) {
+ ray($e->getMessage());
+ return handleError($e);
+ }
+ }
+}
diff --git a/app/Http/Controllers/Webhook/Github.php b/app/Http/Controllers/Webhook/Github.php
index baa23deec..bddfaff92 100644
--- a/app/Http/Controllers/Webhook/Github.php
+++ b/app/Http/Controllers/Webhook/Github.php
@@ -106,7 +106,7 @@ class Github extends Controller
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
- 'message' => 'Invalid token.',
+ 'message' => 'Invalid signature.',
]);
continue;
}
diff --git a/app/Http/Controllers/Webhook/Gitlab.php b/app/Http/Controllers/Webhook/Gitlab.php
index dfa9394eb..a36929781 100644
--- a/app/Http/Controllers/Webhook/Gitlab.php
+++ b/app/Http/Controllers/Webhook/Gitlab.php
@@ -109,7 +109,7 @@ class Gitlab extends Controller
$return_payloads->push([
'application' => $application->name,
'status' => 'failed',
- 'message' => 'Invalid token.',
+ 'message' => 'Invalid signature.',
]);
ray('Invalid signature');
continue;
diff --git a/app/Http/Controllers/Webhook/Stripe.php b/app/Http/Controllers/Webhook/Stripe.php
index 7d6721252..200d3dd1c 100644
--- a/app/Http/Controllers/Webhook/Stripe.php
+++ b/app/Http/Controllers/Webhook/Stripe.php
@@ -150,6 +150,10 @@ class Stripe extends Controller
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
}
if (!$subscription) {
+ if ($status === 'incomplete_expired') {
+ send_internal_notification('Subscription incomplete expired for customer: ' . $customerId);
+ return response("Subscription incomplete expired", 200);
+ }
send_internal_notification('No subscription found for: ' . $customerId);
return response("No subscription found", 400);
}
@@ -166,9 +170,11 @@ class Stripe extends Controller
$quantity = data_get($data, 'items.data.0.quantity', 10);
}
$team = data_get($subscription, 'team');
- $team->update([
- 'custom_server_limit' => $quantity,
- ]);
+ if ($team) {
+ $team->update([
+ 'custom_server_limit' => $quantity,
+ ]);
+ }
ServerLimitCheckJob::dispatch($team);
}
$subscription->update([
@@ -210,7 +216,9 @@ class Stripe extends Controller
$customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
$team = data_get($subscription, 'team');
- $team->trialEnded();
+ if ($team) {
+ $team->trialEnded();
+ }
$subscription->update([
'stripe_subscription_id' => null,
'stripe_plan_id' => null,
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 47319ac10..e60bfac90 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -67,6 +67,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
// Save original server between phases
private Server $original_server;
private Server $mainServer;
+ private bool $is_this_additional_server = false;
private ?ApplicationPreview $preview = null;
private ?string $git_type = null;
private bool $only_this_server = false;
@@ -112,6 +113,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
public $tries = 1;
public function __construct(int $application_deployment_queue_id)
{
+ ray()->clearAll();
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
$this->application = Application::find($this->application_deployment_queue->application_id);
$this->build_pack = data_get($this->application, 'build_pack');
@@ -123,6 +125,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->rollback = $this->application_deployment_queue->rollback;
$this->force_rebuild = $this->application_deployment_queue->force_rebuild;
$this->restart_only = $this->application_deployment_queue->restart_only;
+ $this->restart_only = $this->restart_only && $this->application->build_pack !== 'dockerimage' && $this->application->build_pack !== 'dockerfile';
$this->only_this_server = $this->application_deployment_queue->only_this_server;
$this->git_type = data_get($this->application_deployment_queue, 'git_type');
@@ -136,6 +139,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->destination = $this->server->destinations()->where('id', $this->application_deployment_queue->destination_id)->first();
$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->basedir = $this->application->generateBaseDir($this->deployment_uuid);
$this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/');
$this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}";
@@ -149,28 +154,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
// Set preview fqdn
if ($this->pull_request_id !== 0) {
- $this->preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pull_request_id);
- if ($this->application->fqdn) {
- if (str($this->application->fqdn)->contains(',')) {
- $url = Url::fromString(str($this->application->fqdn)->explode(',')[0]);
- $preview_fqdn = getFqdnWithoutPort(str($this->application->fqdn)->explode(',')[0]);
- } else {
- $url = Url::fromString($this->application->fqdn);
- if (data_get($this->preview, 'fqdn')) {
- $preview_fqdn = getFqdnWithoutPort(data_get($this->preview, 'fqdn'));
- }
- }
- $template = $this->application->preview_url_template;
- $host = $url->getHost();
- $schema = $url->getScheme();
- $random = new Cuid2(7);
- $preview_fqdn = str_replace('{{random}}', $random, $template);
- $preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
- $preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);
- $preview_fqdn = "$schema://$preview_fqdn";
- $this->preview->fqdn = $preview_fqdn;
- $this->preview->save();
- }
+ $this->preview = $this->application->generate_preview_fqdn($this->pull_request_id);
if ($this->application->is_github_based()) {
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS);
}
@@ -184,6 +168,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
public function handle(): void
{
+ $this->application_deployment_queue->update([
+ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
+ ]);
if (!$this->server->isFunctional()) {
$this->application_deployment_queue->addLogEntry("Server is not functional.");
$this->fail("Server is not functional.");
@@ -281,7 +268,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
private function decide_what_to_do()
{
- if ($this->restart_only && $this->application->build_pack !== 'dockerimage' && $this->application->build_pack !== 'dockerfile') {
+ if ($this->restart_only) {
$this->just_restart();
return;
} else if ($this->pull_request_id !== 0) {
@@ -331,18 +318,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
],
);
$this->generate_image_names();
-
- // Always rebuild dockerfile based container.
- // if (!$this->force_rebuild) {
- // $this->check_image_locally_or_remotely();
- // if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
- // $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
- // $this->generate_compose_file();
- // $this->push_to_docker_registry();
- // $this->rolling_update();
- // return;
- // }
- // }
$this->generate_compose_file();
$this->generate_build_env_variables();
$this->add_build_env_variables_to_dockerfile();
@@ -390,15 +365,29 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ($this->application->settings->is_raw_compose_deployment_enabled) {
$this->application->parseRawCompose();
$yaml = $composeFile = $this->application->docker_compose_raw;
+ $this->save_environment_variables();
} else {
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
+ $this->save_environment_variables();
+ if (!is_null($this->env_filename)) {
+ $services = collect($composeFile['services']);
+ $services = $services->map(function ($service, $name) {
+ $service['env_file'] = [$this->env_filename];
+ return $service;
+ });
+ $composeFile['services'] = $services->toArray();
+ }
+ if (is_null($composeFile)) {
+ $this->application_deployment_queue->addLogEntry("Failed to parse docker-compose file.");
+ $this->fail("Failed to parse docker-compose file.");
+ return;
+ }
$yaml = Yaml::dump($composeFile->toArray(), 10);
}
$this->docker_compose_base64 = base64_encode($yaml);
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"), "hidden" => true
]);
- $this->save_environment_variables();
// Build new container to limit downtime.
$this->application_deployment_queue->addLogEntry("Pulling & building required images.");
@@ -407,13 +396,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_build_command}"), "hidden" => true],
);
} else {
+ $command = "{$this->coolify_variables} docker compose";
+ if ($this->env_filename) {
+ $command .= " --env-file {$this->workdir}/{$this->env_filename}";
+ }
+ $command .= " --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build";
$this->execute_remote_command(
- [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
+ [executeInDocker($this->deployment_uuid, $command), "hidden" => true],
);
}
$this->stop_running_container(force: true);
-
+ $this->application_deployment_queue->addLogEntry("Starting new application.");
$networkId = $this->application->uuid;
if ($this->pull_request_id !== 0) {
$networkId = "{$this->application->uuid}-{$this->pull_request_id}";
@@ -422,7 +416,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
// TODO
} else {
$this->execute_remote_command([
- "docker network create --attachable '{$networkId}' >/dev/null || true", "hidden" => true, "ignore_errors" => true
+ "docker network inspect '{$networkId}' >/dev/null 2>&1 || docker network create --attachable '{$networkId}' >/dev/null || true", "hidden" => true, "ignore_errors" => true
], [
"docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
]);
@@ -438,9 +432,15 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
} else {
$this->write_deployment_configurations();
$server_workdir = $this->application->workdir();
- ray("{$this->coolify_variables} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d");
+
+ $command = "{$this->coolify_variables} docker compose";
+ if ($this->env_filename) {
+ $command .= " --env-file {$this->workdir}/{$this->env_filename}";
+ }
+ $command .= " --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
+
$this->execute_remote_command(
- ["{$this->coolify_variables} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d", "hidden" => true],
+ ["command" => $command, "hidden" => true],
);
}
} else {
@@ -450,8 +450,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
);
$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-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
$this->execute_remote_command(
- [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
+ [executeInDocker($this->deployment_uuid, $command), "hidden" => true],
);
$this->write_deployment_configurations();
}
@@ -470,16 +475,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
$this->prepare_builder_image();
$this->check_git_if_build_needed();
- $this->set_base_dir();
$this->generate_image_names();
$this->clone_repository();
if (!$this->force_rebuild) {
$this->check_image_locally_or_remotely();
- if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
- $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
- $this->generate_compose_file();
- $this->push_to_docker_registry();
- $this->rolling_update();
+ if ($this->should_skip_build()) {
return;
}
}
@@ -499,21 +499,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
$this->prepare_builder_image();
$this->check_git_if_build_needed();
- $this->set_base_dir();
$this->generate_image_names();
if (!$this->force_rebuild) {
$this->check_image_locally_or_remotely();
- if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
- $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
- $this->generate_compose_file();
- ray('pushing to docker registry');
- $this->push_to_docker_registry();
- $this->rolling_update();
+ if ($this->should_skip_build()) {
return;
}
- if ($this->application->isConfigurationChanged()) {
- $this->application_deployment_queue->addLogEntry("Configuration changed. Rebuilding image.");
- }
}
$this->clone_repository();
$this->cleanup_git();
@@ -532,15 +523,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
$this->prepare_builder_image();
$this->check_git_if_build_needed();
- $this->set_base_dir();
$this->generate_image_names();
if (!$this->force_rebuild) {
$this->check_image_locally_or_remotely();
- if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
- $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
- $this->generate_compose_file();
- $this->push_to_docker_registry();
- $this->rolling_update();
+ if ($this->should_skip_build()) {
return;
}
}
@@ -608,7 +594,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
ray('additional_servers');
$forceFail = true;
}
- if ($this->application->additional_servers()->wherePivot('server_id', $this->server->id)->count() > 0) {
+ if ($this->is_this_additional_server) {
ray('this is an additional_servers, no pushy pushy');
return;
}
@@ -623,8 +609,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
],
);
if ($this->application->docker_registry_image_tag) {
- // Tag image with latest
- $this->application_deployment_queue->addLogEntry("Tagging and pushing image with latest tag.");
+ // Tag image with docker_registry_image_tag
+ $this->application_deployment_queue->addLogEntry("Tagging and pushing image with {$this->application->docker_registry_image_tag} tag.");
$this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true
@@ -634,7 +620,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
],
);
}
- $this->application_deployment_queue->addLogEntry("Image pushed to docker registry.");
} catch (Exception $e) {
$this->application_deployment_queue->addLogEntry("Failed to push image to docker registry. Please check debug logs for more information.");
if ($forceFail) {
@@ -665,9 +650,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
} else {
$this->dockerImageTag = str($this->commit)->substr(0, 128);
- if ($this->application->docker_registry_image_tag) {
- $this->dockerImageTag = $this->application->docker_registry_image_tag;
- }
+ // if ($this->application->docker_registry_image_tag) {
+ // $this->dockerImageTag = $this->application->docker_registry_image_tag;
+ // }
if ($this->application->docker_registry_image_name) {
$this->build_image_name = "{$this->application->docker_registry_image_name}:{$this->dockerImageTag}-build";
$this->production_image_name = "{$this->application->docker_registry_image_name}:{$this->dockerImageTag}";
@@ -682,19 +667,42 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->application_deployment_queue->addLogEntry("Restarting {$this->customRepository}:{$this->application->git_branch} on {$this->server->name}.");
$this->prepare_builder_image();
$this->check_git_if_build_needed();
- $this->set_base_dir();
$this->generate_image_names();
$this->check_image_locally_or_remotely();
+ if ($this->should_skip_build()) {
+ return;
+ }
+ }
+ private function should_skip_build()
+ {
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
- $this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.");
- $this->generate_compose_file();
- $this->rolling_update();
- $this->post_deployment();
+ if ($this->is_this_additional_server) {
+ $this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
+ $this->generate_compose_file();
+ $this->push_to_docker_registry();
+ $this->rolling_update();
+ if ($this->restart_only) {
+ $this->post_deployment();
+ }
+ return true;
+ }
+ if (!$this->application->isConfigurationChanged()) {
+ $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
+ $this->generate_compose_file();
+ $this->push_to_docker_registry();
+ $this->rolling_update();
+ return true;
+ } else {
+ $this->application_deployment_queue->addLogEntry("Configuration changed. Rebuilding image.");
+ }
} else {
- $this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Redeploying the application.");
+ $this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Building new image.");
+ }
+ if ($this->restart_only) {
$this->restart_only = false;
$this->decide_what_to_do();
}
+ return false;
}
private function check_image_locally_or_remotely()
{
@@ -814,16 +822,29 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->env_filename = null;
if ($this->use_build_server) {
$this->server = $this->original_server;
- }
- $this->execute_remote_command(
- [
- "command" => "rm -f $this->configuration_dir/{$this->env_filename}",
- "hidden" => true,
- "ignore_errors" => true
- ]
- );
- if ($this->use_build_server) {
+ $this->execute_remote_command(
+ [
+ "command" => "rm -f $this->configuration_dir/{$this->env_filename}",
+ "hidden" => true,
+ "ignore_errors" => true
+ ]
+ );
$this->server = $this->build_server;
+ $this->execute_remote_command(
+ [
+ "command" => "rm -f $this->configuration_dir/{$this->env_filename}",
+ "hidden" => true,
+ "ignore_errors" => true
+ ]
+ );
+ } else {
+ $this->execute_remote_command(
+ [
+ "command" => "rm -f $this->configuration_dir/{$this->env_filename}",
+ "hidden" => true,
+ "ignore_errors" => true
+ ]
+ );
}
} else {
$envs_base64 = base64_encode($envs->implode("\n"));
@@ -835,38 +856,20 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
);
if ($this->use_build_server) {
$this->server = $this->original_server;
- }
- $this->execute_remote_command(
- [
- "echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null"
- ]
- );
- if ($this->use_build_server) {
+ $this->execute_remote_command(
+ [
+ "echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null"
+ ]
+ );
$this->server = $this->build_server;
+ } else {
+ $this->execute_remote_command(
+ [
+ "echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null"
+ ]
+ );
}
}
- // $this->execute_remote_command([
- // executeInDocker($this->deployment_uuid, "cat $this->workdir/.env 2>/dev/null || true"),
- // "hidden" => true,
- // "save" => "dotenv"
- // ]);
- // if (str($this->saved_outputs->get('dotenv'))->isNotEmpty()) {
- // $base64_dotenv = base64_encode($this->saved_outputs->get('dotenv')->value());
- // $this->execute_remote_command(
- // [
- // "echo '{$base64_dotenv}' | base64 -d | tee $this->configuration_dir/.env > /dev/null"
- // ]
- // );
- // } else {
- // $this->execute_remote_command(
- // [
- // "command" => "rm -f $this->configuration_dir/.env",
- // "hidden" => true,
- // "ignore_errors" => true
- // ]
- // );
- // }
-
}
@@ -988,6 +991,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'unhealthy') {
$this->newVersionIsHealthy = false;
+ $this->query_logs();
break;
}
$counter++;
@@ -997,9 +1001,25 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$sleeptime++;
}
}
+ if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'starting') {
+ $this->query_logs();
+ }
}
}
}
+ private function query_logs()
+ {
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ $this->application_deployment_queue->addLogEntry("Container logs:");
+ $this->execute_remote_command(
+ [
+ "command" => "docker logs -n 100 {$this->container_name}",
+ "type" => "stderr",
+ "ignore_errors" => true,
+ ],
+ );
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ }
private function deploy_pull_request()
{
if ($this->application->build_pack === 'dockercompose') {
@@ -1015,7 +1035,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->prepare_builder_image();
$this->check_git_if_build_needed();
$this->clone_repository();
- $this->set_base_dir();
$this->cleanup_git();
if ($this->application->build_pack === 'nixpacks') {
$this->generate_nixpacks_confs();
@@ -1032,14 +1051,32 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
private function create_workdir()
{
- $this->execute_remote_command(
- [
- "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
- ],
- [
- "command" => "mkdir -p {$this->configuration_dir}"
- ],
- );
+ if ($this->use_build_server) {
+ $this->server = $this->original_server;
+ $this->execute_remote_command(
+ [
+ "command" => "mkdir -p {$this->configuration_dir}"
+ ],
+ );
+ $this->server = $this->build_server;
+ $this->execute_remote_command(
+ [
+ "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
+ ],
+ [
+ "command" => "mkdir -p {$this->configuration_dir}"
+ ],
+ );
+ } else {
+ $this->execute_remote_command(
+ [
+ "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
+ ],
+ [
+ "command" => "mkdir -p {$this->configuration_dir}"
+ ],
+ );
+ }
}
private function prepare_builder_image()
{
@@ -1119,10 +1156,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
]));
}
}
- private function set_base_dir()
- {
- $this->application_deployment_queue->addLogEntry("Setting base directory to {$this->workdir}.");
- }
private function set_coolify_variables()
{
$this->coolify_variables = "SOURCE_COMMIT={$this->commit} ";
@@ -1175,7 +1208,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
],
);
}
- ray("GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$local_branch}");
if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) {
$this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t");
$this->application_deployment_queue->commit = $this->commit;
@@ -1191,7 +1223,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ($this->pull_request_id !== 0) {
$this->application_deployment_queue->addLogEntry("Checking out tag pull/{$this->pull_request_id}/head.");
}
- ray($importCommands);
$this->execute_remote_command(
[
$importCommands, "hidden" => true
@@ -1205,8 +1236,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
"save" => "commit_message"
]
);
- ray($this->saved_outputs->get('commit_message'));
- raY($this->commit);
if ($this->saved_outputs->get('commit_message')) {
$commit_message = str($this->saved_outputs->get('commit_message'))->limit(47);
$this->application_deployment_queue->commit_message = $commit_message->value();
@@ -1257,8 +1286,21 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
// Do any modifications here
$this->generate_env_variables();
$merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', [])));
+ $aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []);
+ if (count($aptPkgs) === 0) {
+ data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']);
+ } else {
+ if (!in_array('curl', $aptPkgs)) {
+ $aptPkgs[] = 'curl';
+ }
+ if (!in_array('wget', $aptPkgs)) {
+ $aptPkgs[] = 'wget';
+ }
+ data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs);
+ }
data_set($parsed, 'variables', $merged_envs->toArray());
$this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT);
+ $this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true);
}
}
}
@@ -1326,6 +1368,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$onlyPort = $ports[0];
}
$persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->application->fileStorages()->get();
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
// $environment_variables = $this->generate_environment_variables($ports);
$this->save_environment_variables();
@@ -1526,6 +1569,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if (count($persistent_storages) > 0) {
$docker_compose['services'][$this->container_name]['volumes'] = $persistent_storages;
}
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$this->container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
@@ -1764,7 +1812,11 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
} else {
// Pure Dockerfile based deployment
if ($this->application->dockerfile) {
- $build_command = "docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
+ if ($this->force_rebuild) {
+ $build_command = "docker build --no-cache --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
+ } else {
+ $build_command = "docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
+ }
$base64_build_command = base64_encode($build_command);
$this->execute_remote_command(
[
@@ -1935,16 +1987,16 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if ($containers->count() == 0) {
return;
}
- $this->application_deployment_queue->addLogEntry("Executing pre-deployment command (see debug log for output): {$this->application->pre_deployment_command}");
+ $this->application_deployment_queue->addLogEntry("Executing pre-deployment command (see debug log for output).");
foreach ($containers as $container) {
$containerName = data_get($container, 'Names');
if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container . '-' . $this->application->uuid)) {
- $cmd = 'sh -c "' . str_replace('"', '\"', $this->application->pre_deployment_command) . '"';
+ $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->pre_deployment_command) . "'";
$exec = "docker exec {$containerName} {$cmd}";
$this->execute_remote_command(
[
- executeInDocker($this->deployment_uuid, $exec), 'hidden' => true
+ 'command' => $exec, 'hidden' => true
],
);
return;
@@ -1958,17 +2010,17 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if (empty($this->application->post_deployment_command)) {
return;
}
- $this->application_deployment_queue->addLogEntry("Executing post-deployment command (see debug log for output): {$this->application->post_deployment_command}");
+ $this->application_deployment_queue->addLogEntry("Executing post-deployment command (see debug log for output).");
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
foreach ($containers as $container) {
$containerName = data_get($container, 'Names');
if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container . '-' . $this->application->uuid)) {
- $cmd = 'sh -c "' . str_replace('"', '\"', $this->application->post_deployment_command) . '"';
+ $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->post_deployment_command) . "'";
$exec = "docker exec {$containerName} {$cmd}";
$this->execute_remote_command(
[
- executeInDocker($this->deployment_uuid, $exec), 'hidden' => true
+ 'command' => $exec, 'hidden' => true
],
);
return;
diff --git a/app/Jobs/ApplicationDeploymentJobNew.php b/app/Jobs/ApplicationDeploymentJobNew.php
new file mode 100644
index 000000000..dedd736da
--- /dev/null
+++ b/app/Jobs/ApplicationDeploymentJobNew.php
@@ -0,0 +1,2153 @@
+application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
+ $this->application = Application::find($this->application_deployment_queue->application_id);
+ $this->build_pack = data_get($this->application, 'build_pack');
+
+ $this->application_deployment_queue_id = $application_deployment_queue_id;
+ $this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
+ $this->pull_request_id = $this->application_deployment_queue->pull_request_id;
+ $this->is_pull_request = $this->pull_request_id !== 0;
+
+ $this->commit = $this->application_deployment_queue->commit;
+ $this->rollback = $this->application_deployment_queue->rollback;
+ $this->force_rebuild = $this->application_deployment_queue->force_rebuild;
+ $this->restart_only = $this->application_deployment_queue->restart_only;
+ $this->only_this_server = $this->application_deployment_queue->only_this_server;
+
+ $this->git_type = data_get($this->application_deployment_queue, 'git_type');
+
+ $source = data_get($this->application, 'source');
+ if ($source) {
+ $this->source = $source->getMorphClass()::where('id', $this->application->source->id)->first();
+ }
+ $this->server = Server::find($this->application_deployment_queue->server_id);
+ $this->timeout = $this->server->settings->dynamic_timeout;
+ $this->destination = $this->server->destinations()->where('id', $this->application_deployment_queue->destination_id)->first();
+ $this->server = $this->mainServer = $this->destination->server;
+ $this->serverUser = $this->server->user;
+ $this->basedir = $this->application->generateBaseDir($this->deployment_uuid);
+ $this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/');
+ $this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}";
+ $this->is_debug_enabled = $this->application->settings->is_debug_enabled;
+
+ $this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
+ ray('New container name: ', $this->container_name);
+ }
+
+ public function handle(): void
+ {
+ $this->application_deployment_queue->update([
+ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
+ ]);
+ if (!$this->server->isFunctional()) {
+ $this->application_deployment_queue->addLogEntry("Server is not functional.");
+ $this->fail("Server is not functional.");
+ return;
+ }
+ savePrivateKeyToFs($this->server);
+ $this->saved_outputs = collect();
+
+ // Set preview fqdn
+ if ($this->is_pull_request) {
+ $this->preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pull_request_id);
+ if ($this->application->fqdn) {
+ if (str($this->application->fqdn)->contains(',')) {
+ $url = Url::fromString(str($this->application->fqdn)->explode(',')[0]);
+ $preview_fqdn = getFqdnWithoutPort(str($this->application->fqdn)->explode(',')[0]);
+ } else {
+ $url = Url::fromString($this->application->fqdn);
+ if (data_get($this->preview, 'fqdn')) {
+ $preview_fqdn = getFqdnWithoutPort(data_get($this->preview, 'fqdn'));
+ }
+ }
+ $template = $this->application->preview_url_template;
+ $host = $url->getHost();
+ $schema = $url->getScheme();
+ $random = new Cuid2(7);
+ $preview_fqdn = str_replace('{{random}}', $random, $template);
+ $preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
+ $preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);
+ $preview_fqdn = "$schema://$preview_fqdn";
+ $this->preview->fqdn = $preview_fqdn;
+ $this->preview->save();
+ }
+ if ($this->application->is_github_based()) {
+ ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS);
+ }
+ if ($this->application->build_pack === 'dockerfile') {
+ if (data_get($this->application, 'dockerfile_location')) {
+ $this->dockerfile_location = $this->application->dockerfile_location;
+ }
+ }
+ }
+ try {
+ // Generate custom host<->ip mapping
+ $allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
+
+ if (!is_null($allContainers)) {
+ $allContainers = format_docker_command_output_to_json($allContainers);
+ $ips = collect([]);
+ if (count($allContainers) > 0) {
+ $allContainers = $allContainers[0];
+ $allContainers = collect($allContainers)->sort()->values();
+ foreach ($allContainers as $container) {
+ $containerName = data_get($container, 'Name');
+ if ($containerName === 'coolify-proxy') {
+ continue;
+ }
+ if (preg_match('/-(\d{12})/', $containerName)) {
+ continue;
+ }
+ $containerIp = data_get($container, 'IPv4Address');
+ if ($containerName && $containerIp) {
+ $containerIp = str($containerIp)->before('/');
+ $ips->put($containerName, $containerIp->value());
+ }
+ }
+ }
+ $this->addHosts = $ips->map(function ($ip, $name) {
+ return "--add-host $name:$ip";
+ })->implode(' ');
+ }
+
+ if ($this->application->dockerfile_target_build) {
+ $this->buildTarget = " --target {$this->application->dockerfile_target_build} ";
+ }
+
+ // Check custom port
+ ['repository' => $this->customRepository, 'port' => $this->customPort] = $this->application->customRepository();
+
+ if (data_get($this->application, 'settings.is_build_server_enabled')) {
+ $teamId = data_get($this->application, 'environment.project.team.id');
+ $buildServers = Server::buildServers($teamId)->get();
+ if ($buildServers->count() === 0) {
+ $this->application_deployment_queue->addLogEntry("No suitable build server found. Using the deployment server.");
+ $this->build_server = $this->server;
+ $this->original_server = $this->server;
+ } else {
+ $this->build_server = $buildServers->random();
+ $this->application_deployment_queue->addLogEntry("Found a suitable build server ({$this->build_server->name}).");
+ $this->original_server = $this->server;
+ $this->use_build_server = true;
+ }
+ } else {
+ // Set build server & original_server to the same as deployment server
+ $this->build_server = $this->server;
+ $this->original_server = $this->server;
+ }
+ $this->decide_what_to_do();
+ } catch (Exception $e) {
+ if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
+ ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
+ }
+ ray($e);
+ $this->fail($e);
+ throw $e;
+ } finally {
+ if ($this->use_build_server) {
+ $this->server = $this->build_server;
+ } else {
+ $this->write_deployment_configurations();
+ }
+ $this->execute_remote_command(
+ [
+ "docker rm -f {$this->deployment_uuid} >/dev/null 2>&1",
+ "hidden" => true,
+ "ignore_errors" => true,
+ ]
+ );
+
+
+ // $this->execute_remote_command(
+ // [
+ // "docker image prune -f >/dev/null 2>&1",
+ // "hidden" => true,
+ // "ignore_errors" => true,
+ // ]
+ // );
+
+
+ ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
+ }
+ }
+ private function execute_remote_command(...$commands)
+ {
+ static::$batch_counter++;
+ if ($commands instanceof Collection) {
+ $commandsText = $commands;
+ } else {
+ $commandsText = collect($commands);
+ }
+ if ($this->server instanceof Server === false) {
+ throw new \RuntimeException('Server is not set or is not an instance of Server model');
+ }
+ $commandsText->each(function ($single_command) {
+ $command = data_get($single_command, 'command') ?? $single_command[0] ?? null;
+ if ($command === null) {
+ throw new \RuntimeException('Command is not set');
+ }
+ $hidden = data_get($single_command, 'hidden', false);
+ $customType = data_get($single_command, 'type');
+ $ignore_errors = data_get($single_command, 'ignore_errors', false);
+ $append = data_get($single_command, 'append', true);
+ $this->save = data_get($single_command, 'save');
+ if ($this->server->isNonRoot()) {
+ if (str($command)->startsWith('docker exec')) {
+ $command = str($command)->replace('docker exec', 'sudo docker exec');
+ } else {
+ $command = parseLineForSudo($command, $this->server);
+ }
+ }
+ $remote_command = generateSshCommand($this->server, $command);
+ $process = Process::timeout(3600)->idleTimeout(3600)->start($remote_command, function (string $type, string $output) use ($command, $hidden, $customType, $append) {
+ $output = Str::of($output)->trim();
+ if ($output->startsWith('â•”')) {
+ $output = "\n" . $output;
+ }
+ $new_log_entry = [
+ 'command' => remove_iip($command),
+ 'output' => remove_iip($output),
+ 'type' => $customType ?? $type === 'err' ? 'stderr' : 'stdout',
+ 'timestamp' => Carbon::now('UTC'),
+ 'hidden' => $hidden,
+ 'batch' => static::$batch_counter,
+ ];
+ if (!$this->application_deployment_queue->logs) {
+ $new_log_entry['order'] = 1;
+ } else {
+ $previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR);
+ $new_log_entry['order'] = count($previous_logs) + 1;
+ }
+ $previous_logs[] = $new_log_entry;
+ $this->application_deployment_queue->logs = json_encode($previous_logs, flags: JSON_THROW_ON_ERROR);
+ $this->application_deployment_queue->save();
+
+ if ($this->save) {
+ if (data_get($this->saved_outputs, $this->save, null) === null) {
+ data_set($this->saved_outputs, $this->save, str());
+ }
+ if ($append) {
+ $this->saved_outputs[$this->save] .= str($output)->trim();
+ $this->saved_outputs[$this->save] = str($this->saved_outputs[$this->save]);
+ } else {
+ $this->saved_outputs[$this->save] = str($output)->trim();
+ }
+ }
+ });
+ $this->application_deployment_queue->update([
+ 'current_process_id' => $process->id(),
+ ]);
+
+ $process_result = $process->wait();
+ if ($process_result->exitCode() !== 0) {
+ if (!$ignore_errors) {
+ $this->application_deployment_queue->status = ApplicationDeploymentStatus::FAILED->value;
+ $this->application_deployment_queue->save();
+ throw new \RuntimeException($process_result->errorOutput());
+ }
+ }
+ });
+ }
+ private function decide_what_to_do()
+ {
+ if ($this->restart_only && $this->application->build_pack !== 'dockerimage' && $this->application->build_pack !== 'dockerfile') {
+ $this->just_restart();
+ return;
+ } else if ($this->is_pull_request) {
+ $this->deploy_pull_request();
+ } else if ($this->application->dockerfile) {
+ $this->deploy_simple_dockerfile();
+ } else if ($this->application->build_pack === 'dockercompose') {
+ $this->deploy_docker_compose_buildpack();
+ } else if ($this->application->build_pack === 'dockerimage') {
+ $this->deploy_dockerimage_buildpack();
+ } else if ($this->application->build_pack === 'dockerfile') {
+ $this->deploy_dockerfile_buildpack();
+ } else if ($this->application->build_pack === 'static') {
+ $this->deploy_static_buildpack();
+ } else {
+ $this->deploy_nixpacks_buildpack();
+ }
+ $this->post_deployment();
+ }
+ private function post_deployment()
+ {
+
+ if ($this->server->isProxyShouldRun()) {
+ GetContainersStatus::dispatch($this->server);
+ // dispatch(new ContainerStatusJob($this->server));
+ }
+ $this->next(ApplicationDeploymentStatus::FINISHED->value);
+ if ($this->is_pull_request) {
+ if ($this->application->is_github_based()) {
+ ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
+ }
+ }
+ $this->run_post_deployment_command();
+ $this->application->isConfigurationChanged(true);
+ }
+ private function deploy_simple_dockerfile()
+ {
+ if ($this->use_build_server) {
+ $this->server = $this->build_server;
+ }
+ $dockerfile_base64 = base64_encode($this->application->dockerfile);
+ $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name} to {$this->server->name}.");
+ $this->prepare_builder_image();
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null")
+ ],
+ );
+ $this->generate_image_names();
+
+ // Always rebuild dockerfile based container.
+ // if (!$this->force_rebuild) {
+ // $this->check_image_locally_or_remotely();
+ // if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
+ // $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
+ // $this->generate_compose_file();
+ // $this->push_to_docker_registry();
+ // $this->rolling_update();
+ // return;
+ // }
+ // }
+ $this->generate_compose_file();
+ $this->generate_build_env_variables();
+ $this->add_build_env_variables_to_dockerfile();
+ $this->build_image();
+ $this->push_to_docker_registry();
+ $this->rolling_update();
+ }
+ private function deploy_dockerimage_buildpack()
+ {
+ $this->dockerImage = $this->application->docker_registry_image_name;
+ if (str($this->application->docker_registry_image_tag)->isEmpty()) {
+ $this->dockerImageTag = 'latest';
+ } else {
+ $this->dockerImageTag = $this->application->docker_registry_image_tag;
+ }
+ ray("echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.'");
+ $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.");
+ $this->generate_image_names();
+ $this->prepare_builder_image();
+ $this->generate_compose_file();
+ $this->rolling_update();
+ }
+ private function deploy_docker_compose_buildpack()
+ {
+ if (data_get($this->application, 'docker_compose_location')) {
+ $this->docker_compose_location = $this->application->docker_compose_location;
+ }
+ if (data_get($this->application, 'docker_compose_custom_start_command')) {
+ $this->docker_compose_custom_start_command = $this->application->docker_compose_custom_start_command;
+ }
+ if (data_get($this->application, 'docker_compose_custom_build_command')) {
+ $this->docker_compose_custom_build_command = $this->application->docker_compose_custom_build_command;
+ }
+ if ($this->pull_request_id === 0) {
+ $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->application->name} to {$this->server->name}.");
+ } else {
+ $this->application_deployment_queue->addLogEntry("Starting pull request (#{$this->pull_request_id}) deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
+ }
+ $this->prepare_builder_image();
+ $this->check_git_if_build_needed();
+ $this->clone_repository();
+ $this->generate_image_names();
+ $this->cleanup_git();
+ $this->application->loadComposeFile(isInit: false);
+ if ($this->application->settings->is_raw_compose_deployment_enabled) {
+ $this->application->parseRawCompose();
+ $yaml = $composeFile = $this->application->docker_compose_raw;
+ } else {
+ $composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
+ $yaml = Yaml::dump($composeFile->toArray(), 10);
+ }
+ $this->docker_compose_base64 = base64_encode($yaml);
+ $this->execute_remote_command([
+ executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"), "hidden" => true
+ ]);
+ $this->save_environment_variables();
+ // Build new container to limit downtime.
+ $this->application_deployment_queue->addLogEntry("Pulling & building required images.");
+
+ if ($this->docker_compose_custom_build_command) {
+ $this->execute_remote_command(
+ [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_build_command}"), "hidden" => true],
+ );
+ } else {
+ $this->execute_remote_command(
+ [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} build"), "hidden" => true],
+ );
+ }
+
+ $this->stop_running_container(force: true);
+
+ $networkId = $this->application->uuid;
+ if ($this->is_pull_request) {
+ $networkId = "{$this->application->uuid}-{$this->pull_request_id}";
+ }
+ if ($this->server->isSwarm()) {
+ // TODO
+ } else {
+ $this->execute_remote_command([
+ "docker network inspect '{$networkId}' >/dev/null 2>&1 || docker network create --attachable '{$networkId}' >/dev/null || true", "hidden" => true, "ignore_errors" => true
+ ], [
+ "docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
+ ]);
+ }
+
+ // Start compose file
+ if ($this->application->settings->is_raw_compose_deployment_enabled) {
+ if ($this->docker_compose_custom_start_command) {
+ $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();
+ ray("{$this->coolify_variables} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d");
+ $this->execute_remote_command(
+ ["{$this->coolify_variables} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d", "hidden" => true],
+ );
+ }
+ } else {
+ if ($this->docker_compose_custom_start_command) {
+ $this->execute_remote_command(
+ [executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
+ );
+ $this->write_deployment_configurations();
+ } else {
+ $this->execute_remote_command(
+ [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
+ );
+ $this->write_deployment_configurations();
+ }
+ }
+
+ $this->application_deployment_queue->addLogEntry("New container started.");
+ }
+ private function deploy_dockerfile_buildpack()
+ {
+ $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
+ if ($this->use_build_server) {
+ $this->server = $this->build_server;
+ }
+ if (data_get($this->application, 'dockerfile_location')) {
+ $this->dockerfile_location = $this->application->dockerfile_location;
+ }
+ $this->prepare_builder_image();
+ $this->check_git_if_build_needed();
+ $this->set_base_dir();
+ $this->generate_image_names();
+ $this->clone_repository();
+ if (!$this->force_rebuild) {
+ $this->check_image_locally_or_remotely();
+ if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
+ $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
+ $this->generate_compose_file();
+ $this->push_to_docker_registry();
+ $this->rolling_update();
+ return;
+ }
+ }
+ $this->cleanup_git();
+ $this->generate_compose_file();
+ $this->generate_build_env_variables();
+ $this->add_build_env_variables_to_dockerfile();
+ $this->build_image();
+ $this->push_to_docker_registry();
+ $this->rolling_update();
+ }
+ private function deploy_nixpacks_buildpack()
+ {
+ if ($this->use_build_server) {
+ $this->server = $this->build_server;
+ }
+ $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
+ $this->prepare_builder_image();
+ $this->check_git_if_build_needed();
+ $this->set_base_dir();
+ $this->generate_image_names();
+ if (!$this->force_rebuild) {
+ $this->check_image_locally_or_remotely();
+ if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
+ $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
+ $this->generate_compose_file();
+ ray('pushing to docker registry');
+ $this->push_to_docker_registry();
+ $this->rolling_update();
+ return;
+ }
+ if ($this->application->isConfigurationChanged()) {
+ $this->application_deployment_queue->addLogEntry("Configuration changed. Rebuilding image.");
+ }
+ }
+ $this->clone_repository();
+ $this->cleanup_git();
+ $this->generate_nixpacks_confs();
+ $this->generate_compose_file();
+ $this->generate_build_env_variables();
+ $this->build_image();
+ $this->push_to_docker_registry();
+ $this->rolling_update();
+ }
+ private function deploy_static_buildpack()
+ {
+ if ($this->use_build_server) {
+ $this->server = $this->build_server;
+ }
+ $this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
+ $this->prepare_builder_image();
+ $this->check_git_if_build_needed();
+ $this->set_base_dir();
+ $this->generate_image_names();
+ if (!$this->force_rebuild) {
+ $this->check_image_locally_or_remotely();
+ if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
+ $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
+ $this->generate_compose_file();
+ $this->push_to_docker_registry();
+ $this->rolling_update();
+ return;
+ }
+ }
+ $this->clone_repository();
+ $this->cleanup_git();
+ $this->generate_compose_file();
+ $this->build_image();
+ $this->push_to_docker_registry();
+ $this->rolling_update();
+ }
+
+ private function write_deployment_configurations()
+ {
+ if (isset($this->docker_compose_base64)) {
+ if ($this->use_build_server) {
+ $this->server = $this->original_server;
+ }
+ $readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at);
+ if ($this->pull_request_id === 0) {
+ $composeFileName = "$this->configuration_dir/docker-compose.yml";
+ } else {
+ $composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yml";
+ $this->docker_compose_location = "/docker-compose-pr-{$this->pull_request_id}.yml";
+ }
+ $this->execute_remote_command(
+ [
+ "mkdir -p $this->configuration_dir"
+ ],
+ [
+ "echo '{$this->docker_compose_base64}' | base64 -d | tee $composeFileName > /dev/null",
+ ],
+ [
+ "echo '{$readme}' > $this->configuration_dir/README.md",
+ ]
+ );
+ if ($this->use_build_server) {
+ $this->server = $this->build_server;
+ }
+ }
+ }
+ private function push_to_docker_registry()
+ {
+ $forceFail = true;
+ if (str($this->application->docker_registry_image_name)->isEmpty()) {
+ ray('empty docker_registry_image_name');
+ return;
+ }
+ if ($this->restart_only) {
+ ray('restart_only');
+ return;
+ }
+ if ($this->application->build_pack === 'dockerimage') {
+ ray('dockerimage');
+ return;
+ }
+ if ($this->use_build_server) {
+ ray('use_build_server');
+ $forceFail = true;
+ }
+ if ($this->server->isSwarm() && $this->build_pack !== 'dockerimage') {
+ ray('isSwarm');
+ $forceFail = true;
+ }
+ if ($this->application->additional_servers->count() > 0) {
+ ray('additional_servers');
+ $forceFail = true;
+ }
+ if ($this->application->additional_servers()->wherePivot('server_id', $this->server->id)->count() > 0) {
+ ray('this is an additional_servers, no pushy pushy');
+ return;
+ }
+ ray('push_to_docker_registry noww: ' . $this->production_image_name);
+ try {
+ instant_remote_process(["docker images --format '{{json .}}' {$this->production_image_name}"], $this->server);
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ $this->application_deployment_queue->addLogEntry("Pushing image to docker registry ({$this->production_image_name}).");
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "docker push {$this->production_image_name}"), 'hidden' => true
+ ],
+ );
+ if ($this->application->docker_registry_image_tag) {
+ // Tag image with latest
+ $this->application_deployment_queue->addLogEntry("Tagging and pushing image with latest tag.");
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "docker push {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true
+ ],
+ );
+ }
+ } catch (Exception $e) {
+ $this->application_deployment_queue->addLogEntry("Failed to push image to docker registry. Please check debug logs for more information.");
+ if ($forceFail) {
+ throw new RuntimeException($e->getMessage(), 69420);
+ }
+ ray($e);
+ }
+ }
+ private function generate_image_names()
+ {
+ if ($this->application->dockerfile) {
+ if ($this->application->docker_registry_image_name) {
+ $this->build_image_name = "{$this->application->docker_registry_image_name}:build";
+ $this->production_image_name = "{$this->application->docker_registry_image_name}:latest";
+ } else {
+ $this->build_image_name = "{$this->application->uuid}:build";
+ $this->production_image_name = "{$this->application->uuid}:latest";
+ }
+ } else if ($this->application->build_pack === 'dockerimage') {
+ $this->production_image_name = "{$this->dockerImage}:{$this->dockerImageTag}";
+ } else if ($this->is_pull_request) {
+ if ($this->application->docker_registry_image_name) {
+ $this->build_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}-build";
+ $this->production_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}";
+ } else {
+ $this->build_image_name = "{$this->application->uuid}:pr-{$this->pull_request_id}-build";
+ $this->production_image_name = "{$this->application->uuid}:pr-{$this->pull_request_id}";
+ }
+ } else {
+ $this->dockerImageTag = str($this->commit)->substr(0, 128);
+ if ($this->application->docker_registry_image_tag) {
+ $this->dockerImageTag = $this->application->docker_registry_image_tag;
+ }
+ if ($this->application->docker_registry_image_name) {
+ $this->build_image_name = "{$this->application->docker_registry_image_name}:{$this->dockerImageTag}-build";
+ $this->production_image_name = "{$this->application->docker_registry_image_name}:{$this->dockerImageTag}";
+ } else {
+ $this->build_image_name = "{$this->application->uuid}:{$this->dockerImageTag}-build";
+ $this->production_image_name = "{$this->application->uuid}:{$this->dockerImageTag}";
+ }
+ }
+ }
+ private function just_restart()
+ {
+ $this->application_deployment_queue->addLogEntry("Restarting {$this->customRepository}:{$this->application->git_branch} on {$this->server->name}.");
+ $this->prepare_builder_image();
+ $this->check_git_if_build_needed();
+ $this->set_base_dir();
+ $this->generate_image_names();
+ $this->check_image_locally_or_remotely();
+ if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
+ $this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.");
+ $this->generate_compose_file();
+ $this->rolling_update();
+ $this->post_deployment();
+ } else {
+ $this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Redeploying the application.");
+ $this->restart_only = false;
+ $this->decide_what_to_do();
+ }
+ }
+ private function check_image_locally_or_remotely()
+ {
+ $this->execute_remote_command([
+ "docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found"
+ ]);
+ if (str($this->saved_outputs->get('local_image_found'))->isEmpty() && $this->application->docker_registry_image_name) {
+ $this->execute_remote_command([
+ "docker pull {$this->production_image_name} 2>/dev/null", "ignore_errors" => true, "hidden" => true
+ ]);
+ $this->execute_remote_command([
+ "docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found"
+ ]);
+ }
+ }
+ private function save_environment_variables()
+ {
+ $envs = collect([]);
+ $local_branch = $this->branch;
+ if ($this->is_pull_request) {
+ $local_branch = "pull/{$this->pull_request_id}/head";
+ }
+ $sort = $this->application->settings->is_env_sorting_enabled;
+ if ($sort) {
+ $sorted_environment_variables = $this->application->environment_variables->sortBy('key');
+ $sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('key');
+ } else {
+ $sorted_environment_variables = $this->application->environment_variables->sortBy('id');
+ $sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('id');
+ }
+ $ports = $this->application->main_port();
+ if ($this->is_pull_request) {
+ $this->env_filename = ".env-pr-$this->pull_request_id";
+ // Add SOURCE_COMMIT if not exists
+ if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) {
+ if (!is_null($this->commit)) {
+ $envs->push("SOURCE_COMMIT={$this->commit}");
+ } else {
+ $envs->push("SOURCE_COMMIT=unknown");
+ }
+ }
+ if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
+ $envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
+ }
+ if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
+ $url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
+ $envs->push("COOLIFY_URL={$url}");
+ }
+ if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
+ $envs->push("COOLIFY_BRANCH={$local_branch}");
+ }
+ foreach ($sorted_environment_variables_preview as $env) {
+ $real_value = $env->real_value;
+ if ($env->version === '4.0.0-beta.239') {
+ $real_value = $env->real_value;
+ } else {
+ if ($env->is_literal) {
+ $real_value = '\'' . $real_value . '\'';
+ } else {
+ $real_value = escapeEnvVariables($env->real_value);
+ }
+ }
+ $envs->push($env->key . '=' . $real_value);
+ }
+ // Add PORT if not exists, use the first port as default
+ if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
+ $envs->push("PORT={$ports[0]}");
+ }
+ // Add HOST if not exists
+ if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
+ $envs->push("HOST=0.0.0.0");
+ }
+ } else {
+ $this->env_filename = ".env";
+ // Add SOURCE_COMMIT if not exists
+ if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) {
+ if (!is_null($this->commit)) {
+ $envs->push("SOURCE_COMMIT={$this->commit}");
+ } else {
+ $envs->push("SOURCE_COMMIT=unknown");
+ }
+ }
+ if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
+ $envs->push("COOLIFY_FQDN={$this->application->fqdn}");
+ }
+ if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
+ $url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
+ $envs->push("COOLIFY_URL={$url}");
+ }
+ if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
+ $envs->push("COOLIFY_BRANCH={$local_branch}");
+ }
+ foreach ($sorted_environment_variables as $env) {
+ $real_value = $env->real_value;
+ if ($env->version === '4.0.0-beta.239') {
+ $real_value = $env->real_value;
+ } else {
+ if ($env->is_literal) {
+ $real_value = '\'' . $real_value . '\'';
+ } else {
+ $real_value = escapeEnvVariables($env->real_value);
+ }
+ }
+ $envs->push($env->key . '=' . $real_value);
+ }
+ // Add PORT if not exists, use the first port as default
+ if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) {
+ $envs->push("PORT={$ports[0]}");
+ }
+ // Add HOST if not exists
+ if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
+ $envs->push("HOST=0.0.0.0");
+ }
+ }
+
+ if ($envs->isEmpty()) {
+ $this->env_filename = null;
+ if ($this->use_build_server) {
+ $this->server = $this->original_server;
+ $this->execute_remote_command(
+ [
+ "command" => "rm -f $this->configuration_dir/{$this->env_filename}",
+ "hidden" => true,
+ "ignore_errors" => true
+ ]
+ );
+ $this->server = $this->build_server;
+ $this->execute_remote_command(
+ [
+ "command" => "rm -f $this->configuration_dir/{$this->env_filename}",
+ "hidden" => true,
+ "ignore_errors" => true
+ ]
+ );
+ } else {
+ $this->execute_remote_command(
+ [
+ "command" => "rm -f $this->configuration_dir/{$this->env_filename}",
+ "hidden" => true,
+ "ignore_errors" => true
+ ]
+ );
+ }
+ } else {
+ $envs_base64 = base64_encode($envs->implode("\n"));
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee $this->workdir/{$this->env_filename} > /dev/null")
+ ],
+
+ );
+ if ($this->use_build_server) {
+ $this->server = $this->original_server;
+ $this->execute_remote_command(
+ [
+ "echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null"
+ ]
+ );
+ $this->server = $this->build_server;
+ } else {
+ $this->execute_remote_command(
+ [
+ "echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null"
+ ]
+ );
+ }
+ }
+ }
+
+
+ private function framework_based_notification()
+ {
+ // Laravel old env variables
+ if ($this->pull_request_id === 0) {
+ $nixpacks_php_fallback_path = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
+ $nixpacks_php_root_dir = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
+ } else {
+ $nixpacks_php_fallback_path = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
+ $nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
+ }
+ if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) {
+ $this->application_deployment_queue->addLogEntry("There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/resources/laravel", 'stderr');
+ }
+ }
+ private function rolling_update()
+ {
+ if ($this->server->isSwarm()) {
+ $this->application_deployment_queue->addLogEntry("Rolling update started.");
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "docker stack deploy --with-registry-auth -c {$this->workdir}{$this->docker_compose_location} {$this->application->uuid}")
+ ],
+ );
+ $this->application_deployment_queue->addLogEntry("Rolling update completed.");
+ } else {
+ if ($this->use_build_server) {
+ $this->write_deployment_configurations();
+ $this->server = $this->original_server;
+ }
+ if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name) || $this->is_pull_request || str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) {
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ if (count($this->application->ports_mappings_array) > 0) {
+ $this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
+ }
+ 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)) {
+ $this->application_deployment_queue->addLogEntry("Custom internal name is set, rolling update is not supported.");
+ }
+ if ($this->is_pull_request) {
+ $this->application->settings->is_consistent_container_name_enabled = true;
+ $this->application_deployment_queue->addLogEntry("Pull request deployment, rolling update is not supported.");
+ }
+ if (str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) {
+ $this->application_deployment_queue->addLogEntry("Custom IP address is set, rolling update is not supported.");
+ }
+ $this->stop_running_container(force: true);
+ $this->start_by_compose_file();
+ } else {
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ $this->application_deployment_queue->addLogEntry("Rolling update started.");
+ $this->start_by_compose_file();
+ $this->health_check();
+ $this->stop_running_container();
+ $this->application_deployment_queue->addLogEntry("Rolling update completed.");
+ }
+ }
+ $this->framework_based_notification();
+ }
+ private function health_check()
+ {
+ if ($this->server->isSwarm()) {
+ // Implement healthcheck for swarm
+ } else {
+ if ($this->application->isHealthcheckDisabled() && $this->application->custom_healthcheck_found === false) {
+ $this->newVersionIsHealthy = true;
+ return;
+ }
+ if ($this->application->custom_healthcheck_found) {
+ $this->application_deployment_queue->addLogEntry("Custom healthcheck found, skipping default healthcheck.");
+ }
+ // ray('New container name: ', $this->container_name);
+ if ($this->container_name) {
+ $counter = 1;
+ $this->application_deployment_queue->addLogEntry("Waiting for healthcheck to pass on the new container.");
+ if ($this->full_healthcheck_url) {
+ $this->application_deployment_queue->addLogEntry("Healthcheck URL (inside the container): {$this->full_healthcheck_url}");
+ }
+ $this->application_deployment_queue->addLogEntry("Waiting for the start period ({$this->application->health_check_start_period} seconds) before starting healthcheck.");
+ $sleeptime = 0;
+ while ($sleeptime < $this->application->health_check_start_period) {
+ Sleep::for(1)->seconds();
+ $sleeptime++;
+ }
+ while ($counter <= $this->application->health_check_retries) {
+ $this->execute_remote_command(
+ [
+ "docker inspect --format='{{json .State.Health.Status}}' {$this->container_name}",
+ "hidden" => true,
+ "save" => "health_check",
+ "append" => false
+ ],
+ [
+ "docker inspect --format='{{json .State.Health.Log}}' {$this->container_name}",
+ "hidden" => true,
+ "save" => "health_check_logs",
+ "append" => false
+ ],
+ );
+ $this->application_deployment_queue->addLogEntry("Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}");
+ $health_check_logs = data_get(collect(json_decode($this->saved_outputs->get('health_check_logs')))->last(), 'Output', '(no logs)');
+ if (empty($health_check_logs)) {
+ $health_check_logs = '(no logs)';
+ }
+ $health_check_return_code = data_get(collect(json_decode($this->saved_outputs->get('health_check_logs')))->last(), 'ExitCode', '(no return code)');
+ if ($health_check_logs !== '(no logs)' || $health_check_return_code !== '(no return code)') {
+ $this->application_deployment_queue->addLogEntry("Healthcheck logs: {$health_check_logs} | Return code: {$health_check_return_code}");
+ }
+
+ if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') {
+ $this->newVersionIsHealthy = true;
+ $this->application->update(['status' => 'running']);
+ $this->application_deployment_queue->addLogEntry("New container is healthy.");
+ break;
+ }
+ if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'unhealthy') {
+ $this->newVersionIsHealthy = false;
+ $this->query_logs();
+ break;
+ }
+ $counter++;
+ $sleeptime = 0;
+ while ($sleeptime < $this->application->health_check_interval) {
+ Sleep::for(1)->seconds();
+ $sleeptime++;
+ }
+ }
+ if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'starting') {
+ $this->query_logs();
+ }
+ }
+ }
+ }
+ private function query_logs()
+ {
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ $this->application_deployment_queue->addLogEntry("Container logs:");
+ $this->execute_remote_command(
+ [
+ "command" => "docker logs -n 100 {$this->container_name}",
+ "type" => "stderr",
+ "ignore_errors" => true,
+ ],
+ );
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ }
+ private function deploy_pull_request()
+ {
+ if ($this->application->build_pack === 'dockercompose') {
+ $this->deploy_docker_compose_buildpack();
+ return;
+ }
+ if ($this->use_build_server) {
+ $this->server = $this->build_server;
+ }
+ $this->newVersionIsHealthy = true;
+ $this->generate_image_names();
+ $this->application_deployment_queue->addLogEntry("Starting pull request (#{$this->pull_request_id}) deployment of {$this->customRepository}:{$this->application->git_branch}.");
+ $this->prepare_builder_image();
+ $this->check_git_if_build_needed();
+ $this->clone_repository();
+ $this->set_base_dir();
+ $this->cleanup_git();
+ if ($this->application->build_pack === 'nixpacks') {
+ $this->generate_nixpacks_confs();
+ }
+ $this->generate_compose_file();
+ $this->generate_build_env_variables();
+ if ($this->application->build_pack === 'dockerfile') {
+ $this->add_build_env_variables_to_dockerfile();
+ }
+ $this->build_image();
+ $this->push_to_docker_registry();
+ // $this->stop_running_container();
+ $this->rolling_update();
+ }
+ private function create_workdir()
+ {
+ if ($this->use_build_server) {
+ $this->server = $this->original_server;
+ $this->execute_remote_command(
+ [
+ "command" => "mkdir -p {$this->configuration_dir}"
+ ],
+ );
+ $this->server = $this->build_server;
+ $this->execute_remote_command(
+ [
+ "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
+ ],
+ [
+ "command" => "mkdir -p {$this->configuration_dir}"
+ ],
+ );
+ } else {
+ $this->execute_remote_command(
+ [
+ "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
+ ],
+ [
+ "command" => "mkdir -p {$this->configuration_dir}"
+ ],
+ );
+ }
+ }
+ private function prepare_builder_image()
+ {
+ $helperImage = config('coolify.helper_image');
+ // Get user home directory
+ $this->serverUserHomeDir = instant_remote_process(["echo \$HOME"], $this->server);
+ $this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
+ if ($this->use_build_server) {
+ if ($this->dockerConfigFileExists === 'NOK') {
+ throw new RuntimeException('Docker config file (~/.docker/config.json) not found on the build server. Please run "docker login" to login to the docker registry on the server.');
+ }
+ $runCommand = "docker run -d --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
+ } else {
+ if ($this->dockerConfigFileExists === 'OK') {
+ $runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
+ } else {
+ $runCommand = "docker run -d --network {$this->destination->network} --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
+ }
+ }
+ $this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
+ $this->execute_remote_command(
+ [
+ "command" => "docker rm -f {$this->deployment_uuid}",
+ "ignore_errors" => true,
+ "hidden" => true
+ ]
+ );
+ $this->execute_remote_command(
+ [
+ $runCommand,
+ "hidden" => true,
+ ],
+ [
+ "command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->basedir}")
+ ],
+ );
+ $this->run_pre_deployment_command();
+ }
+ private function deploy_to_additional_destinations()
+ {
+ if ($this->application->additional_networks->count() === 0) {
+ return;
+ }
+ if ($this->is_pull_request) {
+ return;
+ }
+ $destination_ids = $this->application->additional_networks->pluck('id');
+ if ($this->server->isSwarm()) {
+ $this->application_deployment_queue->addLogEntry("Additional destinations are not supported in swarm mode.");
+ return;
+ }
+ if ($destination_ids->contains($this->destination->id)) {
+ ray('Same destination found in additional destinations. Skipping.');
+ return;
+ }
+ foreach ($destination_ids as $destination_id) {
+ $destination = StandaloneDocker::find($destination_id);
+ $server = $destination->server;
+ if ($server->team_id !== $this->mainServer->team_id) {
+ $this->application_deployment_queue->addLogEntry("Skipping deployment to {$server->name}. Not in the same team?!");
+ continue;
+ }
+ // ray('Deploying to additional destination: ', $server->name);
+ $deployment_uuid = new Cuid2();
+ queue_application_deployment(
+ deployment_uuid: $deployment_uuid,
+ application: $this->application,
+ server: $server,
+ destination: $destination,
+ no_questions_asked: true,
+ );
+ $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: " . route('project.application.deployment.show', [
+ 'project_uuid' => data_get($this->application, 'environment.project.uuid'),
+ 'application_uuid' => data_get($this->application, 'uuid'),
+ 'deployment_uuid' => $deployment_uuid,
+ 'environment_name' => data_get($this->application, 'environment.name'),
+ ]));
+ }
+ }
+ private function set_base_dir()
+ {
+ $this->application_deployment_queue->addLogEntry("Setting base directory to {$this->workdir}.");
+ }
+ private function set_coolify_variables()
+ {
+ $this->coolify_variables = "SOURCE_COMMIT={$this->commit} ";
+ if ($this->pull_request_id === 0) {
+ $fqdn = $this->application->fqdn;
+ } else {
+ $fqdn = $this->preview->fqdn;
+ }
+ if (isset($fqdn)) {
+ $this->coolify_variables .= "COOLIFY_FQDN={$fqdn} ";
+ $url = str($fqdn)->replace('http://', '')->replace('https://', '');
+ $this->coolify_variables .= "COOLIFY_URL={$url} ";
+ }
+ if (isset($this->application->git_branch)) {
+ $this->coolify_variables .= "COOLIFY_BRANCH={$this->application->git_branch} ";
+ }
+ }
+ private function check_git_if_build_needed()
+ {
+ $this->generate_git_import_commands();
+ $local_branch = $this->branch;
+ if ($this->is_pull_request) {
+ $local_branch = "pull/{$this->pull_request_id}/head";
+ }
+ $private_key = data_get($this->application, 'private_key.private_key');
+ if ($private_key) {
+ $private_key = base64_encode($private_key);
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh")
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d | tee /root/.ssh/id_rsa > /dev/null")
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa")
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->fullRepoUrl} {$local_branch}"),
+ "hidden" => true,
+ "save" => "git_commit_sha"
+ ],
+ );
+ } else {
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$local_branch}"),
+ "hidden" => true,
+ "save" => "git_commit_sha"
+ ],
+ );
+ }
+ if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) {
+ $this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t");
+ $this->application_deployment_queue->commit = $this->commit;
+ $this->application_deployment_queue->save();
+ }
+ $this->set_coolify_variables();
+ }
+ private function clone_repository()
+ {
+ $importCommands = $this->generate_git_import_commands();
+ $this->application_deployment_queue->addLogEntry("\n----------------------------------------");
+ $this->application_deployment_queue->addLogEntry("Importing {$this->customRepository}:{$this->application->git_branch} (commit sha {$this->application->git_commit_sha}) to {$this->basedir}.");
+ if ($this->is_pull_request) {
+ $this->application_deployment_queue->addLogEntry("Checking out tag pull/{$this->pull_request_id}/head.");
+ }
+ $this->execute_remote_command(
+ [
+ $importCommands, "hidden" => true
+ ]
+ );
+ $this->create_workdir();
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "cd {$this->workdir} && git log -1 {$this->commit} --pretty=%B"),
+ "hidden" => true,
+ "save" => "commit_message"
+ ]
+ );
+ if ($this->saved_outputs->get('commit_message')) {
+ $commit_message = str($this->saved_outputs->get('commit_message'))->limit(47);
+ $this->application_deployment_queue->commit_message = $commit_message->value();
+ ApplicationDeploymentQueue::whereCommit($this->commit)->whereApplicationId($this->application->id)->update(
+ ['commit_message' => $commit_message->value()]
+ );
+ }
+ }
+
+ private function generate_git_import_commands()
+ {
+ ['commands' => $commands, 'branch' => $this->branch, 'fullRepoUrl' => $this->fullRepoUrl] = $this->application->generateGitImportCommands(
+ deployment_uuid: $this->deployment_uuid,
+ pull_request_id: $this->pull_request_id,
+ git_type: $this->git_type,
+ commit: $this->commit
+ );
+ return $commands;
+ }
+
+ private function cleanup_git()
+ {
+ $this->execute_remote_command(
+ [executeInDocker($this->deployment_uuid, "rm -fr {$this->basedir}/.git")],
+ );
+ }
+
+ private function generate_nixpacks_confs()
+ {
+ $nixpacks_command = $this->nixpacks_build_cmd();
+ $this->application_deployment_queue->addLogEntry("Generating nixpacks configuration with: $nixpacks_command");
+ $this->execute_remote_command(
+ [executeInDocker($this->deployment_uuid, $nixpacks_command), "save" => "nixpacks_plan", "hidden" => true],
+ [executeInDocker($this->deployment_uuid, "nixpacks detect {$this->workdir}"), "save" => "nixpacks_type", "hidden" => true],
+ );
+ if ($this->saved_outputs->get('nixpacks_type')) {
+ $this->nixpacks_type = $this->saved_outputs->get('nixpacks_type');
+ if (str($this->nixpacks_type)->isEmpty()) {
+ throw new RuntimeException('Nixpacks failed to detect the application type. Please check the documentation of Nixpacks: https://nixpacks.com/docs/providers');
+ }
+ }
+ if ($this->saved_outputs->get('nixpacks_plan')) {
+ $this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan');
+ if ($this->nixpacks_plan) {
+ $this->application_deployment_queue->addLogEntry("Found application type: {$this->nixpacks_type}.");
+ $this->application_deployment_queue->addLogEntry("If you need further customization, please check the documentation of Nixpacks: https://nixpacks.com/docs/providers/{$this->nixpacks_type}");
+ $parsed = Toml::Parse($this->nixpacks_plan);
+ // Do any modifications here
+ $this->generate_env_variables();
+ $merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', [])));
+ $aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []);
+ if (count($aptPkgs) === 0) {
+ data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']);
+ } else {
+ if (!in_array('curl', $aptPkgs)) {
+ $aptPkgs[] = 'curl';
+ }
+ if (!in_array('wget', $aptPkgs)) {
+ $aptPkgs[] = 'wget';
+ }
+ data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs);
+ }
+ data_set($parsed, 'variables', $merged_envs->toArray());
+ $this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT);
+ $this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true);
+ }
+ }
+ }
+
+ private function nixpacks_build_cmd()
+ {
+ $this->generate_nixpacks_env_variables();
+ $nixpacks_command = "nixpacks plan -f toml {$this->env_nixpacks_args}";
+ if ($this->application->build_command) {
+ $nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
+ }
+ if ($this->application->start_command) {
+ $nixpacks_command .= " --start-cmd \"{$this->application->start_command}\"";
+ }
+ if ($this->application->install_command) {
+ $nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
+ }
+ $nixpacks_command .= " {$this->workdir}";
+ return $nixpacks_command;
+ }
+ private function generate_nixpacks_env_variables()
+ {
+ $this->env_nixpacks_args = collect([]);
+ if ($this->pull_request_id === 0) {
+ foreach ($this->application->nixpacks_environment_variables as $env) {
+ if (!is_null($env->real_value)) {
+ $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
+ }
+ }
+ } else {
+ foreach ($this->application->nixpacks_environment_variables_preview as $env) {
+ if (!is_null($env->real_value)) {
+ $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
+ }
+ }
+ }
+
+ $this->env_nixpacks_args = $this->env_nixpacks_args->implode(' ');
+ }
+ private function generate_env_variables()
+ {
+ $this->env_args = collect([]);
+ $this->env_args->put('SOURCE_COMMIT', $this->commit);
+ if ($this->pull_request_id === 0) {
+ foreach ($this->application->build_environment_variables as $env) {
+ if (!is_null($env->real_value)) {
+ $this->env_args->put($env->key, $env->real_value);
+ }
+ }
+ } else {
+ foreach ($this->application->build_environment_variables_preview as $env) {
+ if (!is_null($env->real_value)) {
+ $this->env_args->put($env->key, $env->real_value);
+ }
+ }
+ }
+ }
+
+ private function generate_compose_file()
+ {
+ $this->create_workdir();
+ $ports = $this->application->main_port();
+ $onlyPort = null;
+ if (count($ports) > 0) {
+ $onlyPort = $ports[0];
+ }
+ $persistent_storages = $this->generate_local_persistent_volumes();
+ $persistent_file_volumes = $this->application->fileStorages()->get();
+ $volume_names = $this->generate_local_persistent_volumes_only_volume_names();
+ // $environment_variables = $this->generate_environment_variables($ports);
+ $this->save_environment_variables();
+ if (data_get($this->application, 'custom_labels')) {
+ $this->application->parseContainerLabels();
+ $labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels)));
+ $labels = $labels->filter(function ($value, $key) {
+ return !Str::startsWith($value, 'coolify.');
+ });
+ $found_caddy_labels = $labels->filter(function ($value, $key) {
+ return Str::startsWith($value, 'caddy_');
+ });
+ if ($found_caddy_labels->count() === 0) {
+ if ($this->is_pull_request) {
+ $domains = str(data_get($this->preview, 'fqdn'))->explode(',');
+ } else {
+ $domains = str(data_get($this->application, 'fqdn'))->explode(',');
+ }
+ $labels = $labels->merge(fqdnLabelsForCaddy(
+ network: $this->application->destination->network,
+ uuid: $this->application->uuid,
+ domains: $domains,
+ onlyPort: $onlyPort,
+ is_force_https_enabled: $this->application->isForceHttpsEnabled(),
+ is_gzip_enabled: $this->application->isGzipEnabled(),
+ is_stripprefix_enabled: $this->application->isStripprefixEnabled()
+ ));
+ }
+ $this->application->custom_labels = base64_encode($labels->implode("\n"));
+ $this->application->save();
+ } else {
+ $labels = collect(generateLabelsApplication($this->application, $this->preview));
+ }
+ if ($this->is_pull_request) {
+ $labels = collect(generateLabelsApplication($this->application, $this->preview));
+ }
+ if ($this->application->settings->is_container_label_escape_enabled) {
+ $labels = $labels->map(function ($value, $key) {
+ return escapeDollarSign($value);
+ });
+ }
+ $labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
+
+ // Check for custom HEALTHCHECK
+ if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
+ $this->execute_remote_command([
+ executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile_from_repo', "ignore_errors" => true
+ ]);
+ $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
+ $this->application->parseHealthcheckFromDockerfile($dockerfile);
+ }
+ $docker_compose = [
+ 'services' => [
+ $this->container_name => [
+ 'image' => $this->production_image_name,
+ 'container_name' => $this->container_name,
+ 'restart' => RESTART_MODE,
+ 'expose' => $ports,
+ 'networks' => [
+ $this->destination->network => [
+ 'aliases' => [
+ $this->container_name
+ ]
+ ]
+ ],
+ 'mem_limit' => $this->application->limits_memory,
+ 'memswap_limit' => $this->application->limits_memory_swap,
+ 'mem_swappiness' => $this->application->limits_memory_swappiness,
+ 'mem_reservation' => $this->application->limits_memory_reservation,
+ 'cpus' => (float) $this->application->limits_cpus,
+ 'cpu_shares' => $this->application->limits_cpu_shares,
+ ]
+ ],
+ 'networks' => [
+ $this->destination->network => [
+ 'external' => true,
+ 'name' => $this->destination->network,
+ 'attachable' => true
+ ]
+ ]
+ ];
+ if (isset($this->application->settings->custom_internal_name)) {
+ $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['aliases'][] = $this->application->settings->custom_internal_name;
+ }
+ // if (str($this->saved_outputs->get('dotenv'))->isNotEmpty()) {
+ // if (data_get($docker_compose, "services.{$this->container_name}.env_file")) {
+ // $docker_compose['services'][$this->container_name]['env_file'][] = '.env';
+ // } else {
+ // $docker_compose['services'][$this->container_name]['env_file'] = ['.env'];
+ // }
+ // }
+ // if ($this->env_filename) {
+ // if (data_get($docker_compose, "services.{$this->container_name}.env_file")) {
+ // $docker_compose['services'][$this->container_name]['env_file'][] = $this->env_filename;
+ // } else {
+ // $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename];
+ // }
+ // }
+ if (!is_null($this->env_filename)) {
+ $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename];
+ }
+ $docker_compose['services'][$this->container_name]['healthcheck'] = [
+ 'test' => [
+ 'CMD-SHELL',
+ $this->generate_healthcheck_commands()
+ ],
+ 'interval' => $this->application->health_check_interval . 's',
+ 'timeout' => $this->application->health_check_timeout . 's',
+ 'retries' => $this->application->health_check_retries,
+ 'start_period' => $this->application->health_check_start_period . 's'
+ ];
+
+ if (!is_null($this->application->limits_cpuset)) {
+ data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset);
+ }
+ if ($this->server->isSwarm()) {
+ data_forget($docker_compose, 'services.' . $this->container_name . '.container_name');
+ data_forget($docker_compose, 'services.' . $this->container_name . '.expose');
+ data_forget($docker_compose, 'services.' . $this->container_name . '.restart');
+
+ data_forget($docker_compose, 'services.' . $this->container_name . '.mem_limit');
+ data_forget($docker_compose, 'services.' . $this->container_name . '.memswap_limit');
+ data_forget($docker_compose, 'services.' . $this->container_name . '.mem_swappiness');
+ data_forget($docker_compose, 'services.' . $this->container_name . '.mem_reservation');
+ data_forget($docker_compose, 'services.' . $this->container_name . '.cpus');
+ data_forget($docker_compose, 'services.' . $this->container_name . '.cpuset');
+ data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares');
+
+ $docker_compose['services'][$this->container_name]['deploy'] = [
+ 'mode' => 'replicated',
+ 'replicas' => data_get($this->application, 'swarm_replicas', 1),
+ 'update_config' => [
+ 'order' => 'start-first'
+ ],
+ 'rollback_config' => [
+ 'order' => 'start-first'
+ ],
+ 'labels' => $labels,
+ 'resources' => [
+ 'limits' => [
+ 'cpus' => $this->application->limits_cpus,
+ 'memory' => $this->application->limits_memory,
+ ],
+ 'reservations' => [
+ 'cpus' => $this->application->limits_cpus,
+ 'memory' => $this->application->limits_memory,
+ ]
+ ]
+ ];
+ if (data_get($this->application, 'settings.is_swarm_only_worker_nodes')) {
+ $docker_compose['services'][$this->container_name]['deploy']['placement'] = [
+ 'constraints' => [
+ 'node.role == worker'
+ ]
+ ];
+ }
+ if ($this->is_pull_request) {
+ $docker_compose['services'][$this->container_name]['deploy']['replicas'] = 1;
+ }
+ } else {
+ $docker_compose['services'][$this->container_name]['labels'] = $labels;
+ }
+ if ($this->server->isLogDrainEnabled() && $this->application->isLogDrainEnabled()) {
+ $docker_compose['services'][$this->container_name]['logging'] = [
+ 'driver' => 'fluentd',
+ 'options' => [
+ 'fluentd-address' => "tcp://127.0.0.1:24224",
+ 'fluentd-async' => "true",
+ 'fluentd-sub-second-precision' => "true",
+ ]
+ ];
+ }
+ if ($this->application->settings->is_gpu_enabled) {
+ $docker_compose['services'][$this->container_name]['deploy']['resources']['reservations']['devices'] = [
+ [
+ 'driver' => data_get($this->application, 'settings.gpu_driver', 'nvidia'),
+ 'capabilities' => ['gpu'],
+ 'options' => data_get($this->application, 'settings.gpu_options', [])
+ ]
+ ];
+ if (data_get($this->application, 'settings.gpu_count')) {
+ $count = data_get($this->application, 'settings.gpu_count');
+ if ($count === 'all') {
+ $docker_compose['services'][$this->container_name]['deploy']['resources']['reservations']['devices'][0]['count'] = $count;
+ } else {
+ $docker_compose['services'][$this->container_name]['deploy']['resources']['reservations']['devices'][0]['count'] = (int) $count;
+ }
+ } else if (data_get($this->application, 'settings.gpu_device_ids')) {
+ $docker_compose['services'][$this->container_name]['deploy']['resources']['reservations']['devices'][0]['ids'] = data_get($this->application, 'settings.gpu_device_ids');
+ }
+ }
+ if ($this->application->isHealthcheckDisabled()) {
+ data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck');
+ }
+ if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) {
+ $docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array;
+ }
+ if (count($persistent_storages) > 0) {
+ $docker_compose['services'][$this->container_name]['volumes'] = $persistent_storages;
+ }
+ if (count($persistent_file_volumes) > 0) {
+ $docker_compose['services'][$this->container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
+ return "$item->fs_path:$item->mount_path";
+ })->toArray();
+ }
+ if (count($volume_names) > 0) {
+ $docker_compose['volumes'] = $volume_names;
+ }
+ // if ($this->build_pack === 'dockerfile') {
+ // $docker_compose['services'][$this->container_name]['build'] = [
+ // 'context' => $this->workdir,
+ // 'dockerfile' => $this->workdir . $this->dockerfile_location,
+ // ];
+ // }
+
+ if ($this->pull_request_id === 0) {
+ $custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
+ if ((bool)$this->application->settings->is_consistent_container_name_enabled) {
+ $docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
+ if (count($custom_compose) > 0) {
+ $ipv4 = data_get($custom_compose, 'ip.0');
+ $ipv6 = data_get($custom_compose, 'ip6.0');
+ data_forget($custom_compose, 'ip');
+ data_forget($custom_compose, 'ip6');
+ if ($ipv4 || $ipv6) {
+ data_forget($docker_compose['services'][$this->application->uuid], 'networks');
+ }
+ if ($ipv4) {
+ $docker_compose['services'][$this->application->uuid]['networks'][$this->destination->network]['ipv4_address'] = $ipv4;
+ }
+ if ($ipv6) {
+ $docker_compose['services'][$this->application->uuid]['networks'][$this->destination->network]['ipv6_address'] = $ipv6;
+ }
+ $docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
+ }
+ } else {
+ if (count($custom_compose) > 0) {
+ $ipv4 = data_get($custom_compose, 'ip.0');
+ $ipv6 = data_get($custom_compose, 'ip6.0');
+ data_forget($custom_compose, 'ip');
+ data_forget($custom_compose, 'ip6');
+ if ($ipv4 || $ipv6) {
+ data_forget($docker_compose['services'][$this->container_name], 'networks');
+ }
+ if ($ipv4) {
+ $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv4_address'] = $ipv4;
+ }
+ if ($ipv6) {
+ $docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv6_address'] = $ipv6;
+ }
+ $docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
+ }
+ }
+ }
+
+ $this->docker_compose = Yaml::dump($docker_compose, 10);
+ $this->docker_compose_base64 = base64_encode($this->docker_compose);
+ $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}/docker-compose.yml > /dev/null"), "hidden" => true]);
+ }
+
+ private function generate_local_persistent_volumes()
+ {
+ $local_persistent_volumes = [];
+ foreach ($this->application->persistentStorages as $persistentStorage) {
+ if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
+ $volume_name = $persistentStorage->host_path;
+ } else {
+ $volume_name = $persistentStorage->name;
+ }
+ if ($this->is_pull_request) {
+ $volume_name = $volume_name . '-pr-' . $this->pull_request_id;
+ }
+ $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
+ }
+ return $local_persistent_volumes;
+ }
+
+ private function generate_local_persistent_volumes_only_volume_names()
+ {
+ $local_persistent_volumes_names = [];
+ foreach ($this->application->persistentStorages as $persistentStorage) {
+ if ($persistentStorage->host_path) {
+ continue;
+ }
+ $name = $persistentStorage->name;
+
+ if ($this->is_pull_request) {
+ $name = $name . '-pr-' . $this->pull_request_id;
+ }
+
+ $local_persistent_volumes_names[$name] = [
+ 'name' => $name,
+ 'external' => false,
+ ];
+ }
+ return $local_persistent_volumes_names;
+ }
+
+ private function generate_healthcheck_commands()
+ {
+ if (!$this->application->health_check_port) {
+ $health_check_port = $this->application->ports_exposes_array[0];
+ } else {
+ $health_check_port = $this->application->health_check_port;
+ }
+ if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
+ $health_check_port = 80;
+ }
+ if ($this->application->health_check_path) {
+ $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path}";
+ $generated_healthchecks_commands = [
+ "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null || wget -q -O- {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}{$this->application->health_check_path} > /dev/null || exit 1"
+ ];
+ } else {
+ $this->full_healthcheck_url = "{$this->application->health_check_method}: {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/";
+ $generated_healthchecks_commands = [
+ "curl -s -X {$this->application->health_check_method} -f {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/ > /dev/null || wget -q -O- {$this->application->health_check_scheme}://{$this->application->health_check_host}:{$health_check_port}/ > /dev/null || exit 1"
+ ];
+ }
+ return implode(' ', $generated_healthchecks_commands);
+ }
+ private function pull_latest_image($image)
+ {
+ $this->application_deployment_queue->addLogEntry("Pulling latest image ($image) from the registry.");
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "docker pull {$image}"), "hidden" => true
+ ]
+ );
+ }
+ private function build_image()
+ {
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ if ($this->application->build_pack === 'static') {
+ $this->application_deployment_queue->addLogEntry("Static deployment. Copying static assets to the image.");
+ } else {
+ $this->application_deployment_queue->addLogEntry("Building docker image started.");
+ $this->application_deployment_queue->addLogEntry("To check the current progress, click on Show Debug Logs.");
+ }
+
+ if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
+ if ($this->application->static_image) {
+ $this->pull_latest_image($this->application->static_image);
+ $this->application_deployment_queue->addLogEntry("Continuing with the building process.");
+ }
+ if ($this->application->build_pack === 'static') {
+ $dockerfile = base64_encode("FROM {$this->application->static_image}
+WORKDIR /usr/share/nginx/html/
+LABEL coolify.deploymentId={$this->deployment_uuid}
+COPY . .
+RUN rm -f /usr/share/nginx/html/nginx.conf
+RUN rm -f /usr/share/nginx/html/Dockerfile
+COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
+ $nginx_config = base64_encode("server {
+ listen 80;
+ listen [::]:80;
+ server_name localhost;
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html;
+ try_files \$uri \$uri.html \$uri/index.html \$uri/ /index.html =404;
+ }
+
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+ }");
+ } else {
+ if ($this->application->build_pack === 'nixpacks') {
+ $this->nixpacks_plan = base64_encode($this->nixpacks_plan);
+ $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), "hidden" => true]);
+ if ($this->force_rebuild) {
+ $this->execute_remote_command([
+ executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), "hidden" => true
+ ]);
+ } else {
+ $this->execute_remote_command([
+ executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), "hidden" => true
+ ]);
+ }
+ $this->execute_remote_command([executeInDocker($this->deployment_uuid, "rm /artifacts/thegameplan.json"), "hidden" => true]);
+ } else {
+ if ($this->force_rebuild) {
+ $build_command = "docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}";
+ $base64_build_command = base64_encode($build_command);
+ } else {
+ $build_command = "docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}";
+ $base64_build_command = base64_encode($build_command);
+ }
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
+ ]
+ );
+ }
+
+ $dockerfile = base64_encode("FROM {$this->application->static_image}
+WORKDIR /usr/share/nginx/html/
+LABEL coolify.deploymentId={$this->deployment_uuid}
+COPY --from=$this->build_image_name /app/{$this->application->publish_directory} .
+COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
+
+ $nginx_config = base64_encode("server {
+ listen 80;
+ listen [::]:80;
+ server_name localhost;
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html;
+ try_files \$uri \$uri.html \$uri/index.html \$uri/ /index.html =404;
+ }
+
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+ }");
+ }
+ $build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
+ $base64_build_command = base64_encode($build_command);
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null")
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null")
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
+ ]
+ );
+ } else {
+ // Pure Dockerfile based deployment
+ if ($this->application->dockerfile) {
+ $build_command = "docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
+ $base64_build_command = base64_encode($build_command);
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
+ ]
+ );
+ } else {
+ if ($this->application->build_pack === 'nixpacks') {
+ $this->nixpacks_plan = base64_encode($this->nixpacks_plan);
+ $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), "hidden" => true]);
+ if ($this->force_rebuild) {
+ $this->execute_remote_command([
+ executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), "hidden" => true
+ ]);
+ } else {
+ $this->execute_remote_command([
+ executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), "hidden" => true
+ ]);
+ }
+ $this->execute_remote_command([executeInDocker($this->deployment_uuid, "rm /artifacts/thegameplan.json"), "hidden" => true]);
+ } else {
+ if ($this->force_rebuild) {
+ $build_command = "docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
+ $base64_build_command = base64_encode($build_command);
+ } else {
+ $build_command = "docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
+ $base64_build_command = base64_encode($build_command);
+ }
+ $this->execute_remote_command(
+ [
+ executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), "hidden" => true
+ ],
+ [
+ executeInDocker($this->deployment_uuid, "bash /artifacts/build.sh"), "hidden" => true
+ ]
+ );
+ }
+ }
+ }
+ $this->application_deployment_queue->addLogEntry("Building docker image completed.");
+ }
+
+ private function stop_running_container(bool $force = false)
+ {
+ $this->application_deployment_queue->addLogEntry("Removing old containers.");
+ if ($this->newVersionIsHealthy || $force) {
+ $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
+ if ($this->pull_request_id === 0) {
+ $containers = $containers->filter(function ($container) {
+ return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name . '-pr-' . $this->pull_request_id;
+ });
+ }
+ $containers->each(function ($container) {
+ $containerName = data_get($container, 'Names');
+ $this->execute_remote_command(
+ ["docker rm -f $containerName >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
+ );
+ });
+ if ($this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name)) {
+ $this->execute_remote_command(
+ ["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
+ );
+ }
+ } else {
+ if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') {
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ $this->application_deployment_queue->addLogEntry("WARNING: Dockerfile or Docker Image based deployment detected. The healthcheck needs a curl or wget command to check the health of the application. Please make sure that it is available in the image or turn off healthcheck on Coolify's UI.");
+ $this->application_deployment_queue->addLogEntry("----------------------------------------");
+ }
+ $this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
+ $this->application_deployment_queue->update([
+ 'status' => ApplicationDeploymentStatus::FAILED->value,
+ ]);
+ $this->execute_remote_command(
+ ["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
+ );
+ }
+ }
+
+ 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-directory {$this->workdir} pull"), "hidden" => true],
+ [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} build"), "hidden" => true],
+ );
+ } else {
+ $this->execute_remote_command(
+ [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --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') {
+ $this->application_deployment_queue->addLogEntry("Pulling latest images from the registry.");
+ $this->execute_remote_command(
+ [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} pull"), "hidden" => true],
+ [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
+ );
+ } else {
+ if ($this->use_build_server) {
+ $this->execute_remote_command(
+ ["{$this->coolify_variables} docker compose --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", "hidden" => true],
+ );
+ } else {
+ $this->execute_remote_command(
+ [executeInDocker($this->deployment_uuid, "{$this->coolify_variables} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true],
+ );
+ }
+ }
+ $this->application_deployment_queue->addLogEntry("New container started.");
+ }
+
+ private function generate_build_env_variables()
+ {
+ $this->build_args = collect(["--build-arg SOURCE_COMMIT=\"{$this->commit}\""]);
+ if ($this->pull_request_id === 0) {
+ foreach ($this->application->build_environment_variables as $env) {
+ $value = escapeshellarg($env->real_value);
+ $this->build_args->push("--build-arg {$env->key}={$value}");
+ }
+ } else {
+ foreach ($this->application->build_environment_variables_preview as $env) {
+ $value = escapeshellarg($env->real_value);
+ $this->build_args->push("--build-arg {$env->key}={$value}");
+ }
+ }
+
+ $this->build_args = $this->build_args->implode(' ');
+ }
+
+ private function add_build_env_variables_to_dockerfile()
+ {
+ $this->execute_remote_command([
+ executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile'
+ ]);
+ $dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
+ if ($this->pull_request_id === 0) {
+ foreach ($this->application->build_environment_variables as $env) {
+ $dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
+ }
+ } else {
+ foreach ($this->application->build_environment_variables_preview as $env) {
+ $dockerfile->splice(1, 0, "ARG {$env->key}={$env->real_value}");
+ }
+ }
+ $dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
+ $this->execute_remote_command([
+ executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d | tee {$this->workdir}{$this->dockerfile_location} > /dev/null"),
+ "hidden" => true
+ ]);
+ }
+
+ private function run_pre_deployment_command()
+ {
+ if (empty($this->application->pre_deployment_command)) {
+ return;
+ }
+ $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
+ if ($containers->count() == 0) {
+ return;
+ }
+ $this->application_deployment_queue->addLogEntry("Executing pre-deployment command (see debug log for output).");
+
+ foreach ($containers as $container) {
+ $containerName = data_get($container, 'Names');
+ if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container . '-' . $this->application->uuid)) {
+ $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->pre_deployment_command) . "'";
+ $exec = "docker exec {$containerName} {$cmd}";
+ $this->execute_remote_command(
+ [
+ 'command' => $exec, 'hidden' => true
+ ],
+ );
+ return;
+ }
+ }
+ throw new RuntimeException('Pre-deployment command: Could not find a valid container. Is the container name correct?');
+ }
+
+ private function run_post_deployment_command()
+ {
+ if (empty($this->application->post_deployment_command)) {
+ return;
+ }
+ $this->application_deployment_queue->addLogEntry("Executing post-deployment command (see debug log for output).");
+
+ $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
+ foreach ($containers as $container) {
+ $containerName = data_get($container, 'Names');
+ if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container . '-' . $this->application->uuid)) {
+ $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->post_deployment_command) . "'";
+ $exec = "docker exec {$containerName} {$cmd}";
+ $this->execute_remote_command(
+ [
+ 'command' => $exec, 'hidden' => true
+ ],
+ );
+ return;
+ }
+ }
+ throw new RuntimeException('Post-deployment command: Could not find a valid container. Is the container name correct?');
+ }
+
+ private function next(string $status)
+ {
+ queue_next_deployment($this->application);
+ // If the deployment is cancelled by the user, don't update the status
+ if (
+ $this->application_deployment_queue->status !== ApplicationDeploymentStatus::CANCELLED_BY_USER->value && $this->application_deployment_queue->status !== ApplicationDeploymentStatus::FAILED->value
+ ) {
+ $this->application_deployment_queue->update([
+ 'status' => $status,
+ ]);
+ }
+ if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::FAILED->value) {
+ $this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
+ return;
+ }
+ if ($status === ApplicationDeploymentStatus::FINISHED->value) {
+ if (!$this->only_this_server) {
+ $this->deploy_to_additional_destinations();
+ }
+ $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
+ }
+ }
+
+ public function failed(Throwable $exception): void
+ {
+ $this->next(ApplicationDeploymentStatus::FAILED->value);
+ $this->application_deployment_queue->addLogEntry("Oops something is not okay, are you okay? 😢", 'stderr');
+ if (str($exception->getMessage())->isNotEmpty()) {
+ $this->application_deployment_queue->addLogEntry($exception->getMessage(), 'stderr');
+ }
+
+ if ($this->application->build_pack !== 'dockercompose') {
+ $code = $exception->getCode();
+ ray($code);
+ if ($code !== 69420) {
+ // 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
+ $this->application_deployment_queue->addLogEntry("Deployment failed. Removing the new version of your application.", 'stderr');
+ $this->execute_remote_command(
+ ["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true]
+ );
+ }
+ }
+ }
+}
diff --git a/app/Jobs/CheckLogDrainContainerJob.php b/app/Jobs/CheckLogDrainContainerJob.php
index 4da49b988..8776b67c3 100644
--- a/app/Jobs/CheckLogDrainContainerJob.php
+++ b/app/Jobs/CheckLogDrainContainerJob.php
@@ -70,7 +70,7 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
}
if (!$this->server->log_drain_notification_sent) {
ray('Log drain container still unhealthy. Sending notification...');
- $this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
+ // $this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
$this->server->update(['log_drain_notification_sent' => true]);
}
} else {
diff --git a/app/Jobs/InstanceAutoUpdateJob.php b/app/Jobs/InstanceAutoUpdateJob.php
index dc35aa2b1..ae629dab9 100644
--- a/app/Jobs/InstanceAutoUpdateJob.php
+++ b/app/Jobs/InstanceAutoUpdateJob.php
@@ -18,12 +18,12 @@ class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncr
public $timeout = 600;
public $tries = 1;
- public function __construct(private bool $force = false)
+ public function __construct()
{
}
public function handle(): void
{
- UpdateCoolify::run(force: $this->force, async: false);
+ UpdateCoolify::run();
}
}
diff --git a/app/Jobs/PullTemplatesFromCDN.php b/app/Jobs/PullTemplatesFromCDN.php
new file mode 100644
index 000000000..66e7611a7
--- /dev/null
+++ b/app/Jobs/PullTemplatesFromCDN.php
@@ -0,0 +1,42 @@
+get(config('constants.services.official'));
+ if ($response->successful()) {
+ $services = $response->json();
+ File::put(base_path('templates/service-templates.json'), json_encode($services));
+ } else {
+ send_internal_notification('PullTemplatesAndVersions failed with: ' . $response->status() . ' ' . $response->body());
+ }
+ }
+ } catch (\Throwable $e) {
+ send_internal_notification('PullTemplatesAndVersions failed with: ' . $e->getMessage());
+ ray($e->getMessage());
+ }
+ }
+}
diff --git a/app/Jobs/PullVersionsFromCDN.php b/app/Jobs/PullVersionsFromCDN.php
new file mode 100644
index 000000000..0d4084a30
--- /dev/null
+++ b/app/Jobs/PullVersionsFromCDN.php
@@ -0,0 +1,41 @@
+get('https://cdn.coollabs.io/coolify/versions.json');
+ if ($response->successful()) {
+ $versions = $response->json();
+ File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT));
+ } else {
+ send_internal_notification('PullTemplatesAndVersions failed with: ' . $response->status() . ' ' . $response->body());
+ }
+ }
+ } catch (\Throwable $e) {
+ send_internal_notification('PullTemplatesAndVersions failed with: ' . $e->getMessage());
+ ray($e->getMessage());
+ }
+ }
+}
diff --git a/app/Jobs/ServerStatusJob.php b/app/Jobs/ServerStatusJob.php
index 449ab85a0..d104185c0 100644
--- a/app/Jobs/ServerStatusJob.php
+++ b/app/Jobs/ServerStatusJob.php
@@ -43,7 +43,7 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
try {
if ($this->server->isFunctional()) {
$this->cleanup(notify: false);
- $this->removeCoolifyYaml();
+ $this->remove_unnecessary_coolify_yaml();
if (config('coolify.is_sentinel_enabled')) {
$this->server->checkSentinel();
}
@@ -53,8 +53,44 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
ray($e->getMessage());
return handleError($e);
}
+ try {
+ // $this->check_docker_engine();
+ } catch (\Throwable $e) {
+ // Do nothing
+ }
}
- private function removeCoolifyYaml()
+ 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()
{
// This will remote the coolify.yaml file from the server as it is not needed on cloud servers
if (isCloud() && $this->server->id !== 0) {
diff --git a/app/Listeners/ProxyStartedNotification.php b/app/Listeners/ProxyStartedNotification.php
index e6be605ce..1a4fe97bb 100644
--- a/app/Listeners/ProxyStartedNotification.php
+++ b/app/Listeners/ProxyStartedNotification.php
@@ -17,5 +17,7 @@ class ProxyStartedNotification
$this->server = data_get($event, 'data');
$this->server->setupDefault404Redirect();
$this->server->setupDynamicProxyConfiguration();
+ $this->server->proxy->force_stop = false;
+ $this->server->save();
}
}
diff --git a/app/Livewire/Dev/Compose.php b/app/Livewire/Dev/Compose.php
index ec2c4f54d..8c361ba2a 100644
--- a/app/Livewire/Dev/Compose.php
+++ b/app/Livewire/Dev/Compose.php
@@ -10,7 +10,7 @@ class Compose extends Component
public string $base64 = '';
public $services;
public function mount() {
- $this->services = getServiceTemplates();
+ $this->services = get_service_templates();
}
public function setService(string $selected) {
$this->base64 = data_get($this->services, $selected . '.compose');
diff --git a/app/Livewire/ForcePasswordReset.php b/app/Livewire/ForcePasswordReset.php
index e4a66ebd6..7bbec9d32 100644
--- a/app/Livewire/ForcePasswordReset.php
+++ b/app/Livewire/ForcePasswordReset.php
@@ -24,7 +24,7 @@ class ForcePasswordReset extends Component
}
public function render()
{
- return view('livewire.force-password-reset');
+ return view('livewire.force-password-reset')->layout('layouts.simple');
}
public function submit()
{
diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php
index 718312d2d..58a5ee267 100644
--- a/app/Livewire/Project/Application/General.php
+++ b/app/Livewire/Project/Application/General.php
@@ -141,7 +141,7 @@ class General extends Component
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
if ($this->application->build_pack === 'dockercompose' && !$this->application->docker_compose_raw) {
$this->initLoadingCompose = true;
- $this->dispatch('info', 'Loading docker compose file...');
+ $this->dispatch('info', 'Loading docker compose file.');
}
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
@@ -287,7 +287,7 @@ class General extends Component
if ($this->application->additional_servers->count() === 0) {
foreach ($domains as $domain) {
if (!validate_dns_entry($domain, $this->application->destination->server)) {
- $showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.
Check this documentation for further help.");
+ $showToaster && $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.
$domain->{$this->application->destination->server->ip}
Check this documentation for further help.");
}
}
}
@@ -352,7 +352,7 @@ class General extends Component
$domain = data_get($service, 'domain');
if ($domain) {
if (!validate_dns_entry($domain, $this->application->destination->server)) {
- $showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.
Check this documentation for further help.");
+ $showToaster && $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.
$domain->{$this->application->destination->server->ip}
Check this documentation for further help.");
}
check_domain_usage(resource: $this->application);
}
diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php
index d057479ea..1f4a144a9 100644
--- a/app/Livewire/Project/Application/Previews.php
+++ b/app/Livewire/Project/Application/Previews.php
@@ -2,10 +2,12 @@
namespace App\Livewire\Project\Application;
+use App\Actions\Docker\GetContainersStatus;
use App\Models\Application;
use App\Models\ApplicationPreview;
use Illuminate\Support\Collection;
use Livewire\Component;
+use Spatie\Url\Url;
use Visus\Cuid2\Cuid2;
class Previews extends Component
@@ -16,6 +18,9 @@ class Previews extends Component
public Collection $pull_requests;
public int $rate_limit_remaining;
+ protected $rules = [
+ 'application.previews.*.fqdn' => 'string|nullable',
+ ];
public function mount()
{
$this->pull_requests = collect();
@@ -33,7 +38,71 @@ class Previews extends Component
return handleError($e, $this);
}
}
+ public function save_preview($preview_id)
+ {
+ try {
+ $success = true;
+ $preview = $this->application->previews->find($preview_id);
+ if (isset($preview->fqdn)) {
+ $preview->fqdn = str($preview->fqdn)->replaceEnd(',', '')->trim();
+ $preview->fqdn = str($preview->fqdn)->replaceStart(',', '')->trim();
+ $preview->fqdn = str($preview->fqdn)->trim()->lower();
+ if (!validate_dns_entry($preview->fqdn, $this->application->destination->server)) {
+ $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.
$preview->fqdn->{$this->application->destination->server->ip}
Check this documentation for further help.");
+ $success = false;
+ }
+ check_domain_usage(resource: $this->application, domain: $preview->fqdn);
+ }
+ if (!$preview) {
+ throw new \Exception('Preview not found');
+ }
+ $success && $preview->save();
+ $success && $this->dispatch('success', 'Preview saved.
Do not forget to redeploy the preview to apply the changes.');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+ public function generate_preview($preview_id)
+ {
+ $preview = $this->application->previews->find($preview_id);
+ if (!$preview) {
+ $this->dispatch('error', 'Preview not found.');
+ return;
+ }
+ $fqdn = generateFqdn($this->application->destination->server, $this->application->uuid);
+
+ $url = Url::fromString($fqdn);
+ $template = $this->application->preview_url_template;
+ $host = $url->getHost();
+ $schema = $url->getScheme();
+ $random = new Cuid2(7);
+ $preview_fqdn = str_replace('{{random}}', $random, $template);
+ $preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
+ $preview_fqdn = str_replace('{{pr_id}}', $preview_id, $preview_fqdn);
+ $preview_fqdn = "$schema://$preview_fqdn";
+ $preview->fqdn = $preview_fqdn;
+ $preview->save();
+ $this->dispatch('success', 'Domain generated.');
+ }
+ public function add(int $pull_request_id, string|null $pull_request_html_url = null)
+ {
+ try {
+ $this->setDeploymentUuid();
+ $found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
+ if (!$found && !is_null($pull_request_html_url)) {
+ ApplicationPreview::create([
+ 'application_id' => $this->application->id,
+ 'pull_request_id' => $pull_request_id,
+ 'pull_request_html_url' => $pull_request_html_url
+ ]);
+ }
+ $this->application->generate_preview_fqdn($pull_request_id);
+ $this->application->refresh();
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
public function deploy(int $pull_request_id, string|null $pull_request_html_url = null)
{
try {
@@ -71,6 +140,25 @@ class Previews extends Component
}
public function stop(int $pull_request_id)
+ {
+ try {
+ if ($this->application->destination->server->isSwarm()) {
+ instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
+ } else {
+ $containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
+ foreach ($containers as $container) {
+ $name = str_replace('/', '', $container['Names']);
+ instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
+ }
+ }
+ GetContainersStatus::dispatchSync($this->application->destination->server);
+ $this->application->refresh();
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function delete(int $pull_request_id)
{
try {
if ($this->application->destination->server->isSwarm()) {
diff --git a/app/Livewire/Project/Database/Backup/Execution.php b/app/Livewire/Project/Database/Backup/Execution.php
index 000b6fb2b..ed015dbbf 100644
--- a/app/Livewire/Project/Database/Backup/Execution.php
+++ b/app/Livewire/Project/Database/Backup/Execution.php
@@ -2,12 +2,13 @@
namespace App\Livewire\Project\Database\Backup;
+use App\Models\ScheduledDatabaseBackup;
use Livewire\Component;
class Execution extends Component
{
public $database;
- public $backup;
+ public ?ScheduledDatabaseBackup $backup;
public $executions;
public $s3s;
public function mount()
diff --git a/app/Livewire/Project/Database/BackupEdit.php b/app/Livewire/Project/Database/BackupEdit.php
index d7f7f5503..90eadfe43 100644
--- a/app/Livewire/Project/Database/BackupEdit.php
+++ b/app/Livewire/Project/Database/BackupEdit.php
@@ -2,12 +2,13 @@
namespace App\Livewire\Project\Database;
+use App\Models\ScheduledDatabaseBackup;
use Livewire\Component;
use Spatie\Url\Url;
class BackupEdit extends Component
{
- public $backup;
+ public ?ScheduledDatabaseBackup $backup;
public $s3s;
public ?string $status = null;
public array $parameters;
@@ -36,7 +37,7 @@ class BackupEdit extends Component
{
$this->parameters = get_route_parameters();
if (is_null(data_get($this->backup, 's3_storage_id'))) {
- $this->backup->s3_storage_id = 'default';
+ data_set($this->backup, 's3_storage_id', 'default');
}
}
diff --git a/app/Livewire/Project/Database/BackupExecutions.php b/app/Livewire/Project/Database/BackupExecutions.php
index 101bb4593..5e9319cfd 100644
--- a/app/Livewire/Project/Database/BackupExecutions.php
+++ b/app/Livewire/Project/Database/BackupExecutions.php
@@ -2,11 +2,12 @@
namespace App\Livewire\Project\Database;
+use App\Models\ScheduledDatabaseBackup;
use Livewire\Component;
class BackupExecutions extends Component
{
- public $backup;
+ public ?ScheduledDatabaseBackup $backup = null;
public $executions = [];
public $setDeletableBackup;
public function getListeners()
@@ -20,8 +21,11 @@ class BackupExecutions extends Component
public function cleanupFailed()
{
- $this->backup?->executions()->where('status', 'failed')->delete();
- $this->refreshBackupExecutions();
+ if ($this->backup) {
+ $this->backup->executions()->where('status', 'failed')->delete();
+ $this->refreshBackupExecutions();
+ $this->dispatch('success', 'Failed backups cleaned up.');
+ }
}
public function deleteBackup($exeuctionId)
{
@@ -45,6 +49,8 @@ class BackupExecutions extends Component
}
public function refreshBackupExecutions(): void
{
- $this->executions = $this->backup->executions()->get()->sortByDesc('created_at');
+ if ($this->backup) {
+ $this->executions = $this->backup->executions()->get()->sortByDesc('created_at');
+ }
}
}
diff --git a/app/Livewire/Project/Database/CreateScheduledBackup.php b/app/Livewire/Project/Database/CreateScheduledBackup.php
index e58ea9df3..2b9aa987b 100644
--- a/app/Livewire/Project/Database/CreateScheduledBackup.php
+++ b/app/Livewire/Project/Database/CreateScheduledBackup.php
@@ -3,6 +3,7 @@
namespace App\Livewire\Project\Database;
use App\Models\ScheduledDatabaseBackup;
+use Illuminate\Support\Collection;
use Livewire\Component;
class CreateScheduledBackup extends Component
@@ -12,7 +13,7 @@ class CreateScheduledBackup extends Component
public bool $enabled = true;
public bool $save_s3 = false;
public $s3_storage_id;
- public $s3s;
+ public Collection $s3s;
protected $rules = [
'frequency' => 'required|string',
diff --git a/app/Livewire/Project/Database/ScheduledBackups.php b/app/Livewire/Project/Database/ScheduledBackups.php
index 399fddac4..61c2a3bb1 100644
--- a/app/Livewire/Project/Database/ScheduledBackups.php
+++ b/app/Livewire/Project/Database/ScheduledBackups.php
@@ -2,6 +2,7 @@
namespace App\Livewire\Project\Database;
+use App\Models\ScheduledDatabaseBackup;
use Livewire\Component;
class ScheduledBackups extends Component
@@ -9,7 +10,7 @@ class ScheduledBackups extends Component
public $database;
public $parameters;
public $type;
- public $selectedBackup;
+ public ?ScheduledDatabaseBackup $selectedBackup;
public $selectedBackupId;
public $s3s;
protected $listeners = ['refreshScheduledBackups'];
diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php
index d4deebeb4..2b5ef9eca 100644
--- a/app/Livewire/Project/New/Select.php
+++ b/app/Livewire/Project/New/Select.php
@@ -15,10 +15,10 @@ class Select extends Component
public string $type;
public string $server_id;
public string $destination_uuid;
- public Countable|array|Server $allServers = [];
- public Countable|array|Server $servers = [];
- public Collection|array $standaloneDockers = [];
- public Collection|array $swarmDockers = [];
+ public Collection|null|Server $allServers;
+ public Collection|null|Server $servers;
+ public ?Collection $standaloneDockers;
+ public ?Collection $swarmDockers;
public array $parameters;
public Collection|array $services = [];
public Collection|array $allServices = [];
@@ -91,7 +91,7 @@ class Select extends Component
});
} else {
$this->search = null;
- $this->allServices = getServiceTemplates();
+ $this->allServices = get_service_templates($force);
$this->services = $this->allServices->filter(function ($service, $key) {
return str_contains(strtolower($key), strtolower($this->search));
});
@@ -107,7 +107,11 @@ class Select extends Component
if ($this->includeSwarm) {
$this->servers = $this->allServers;
} else {
- $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
+ if ($this->allServers instanceof Collection) {
+ $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
+ } else {
+ $this->servers = $this->allServers;
+ }
}
}
public function setType(string $type)
@@ -126,13 +130,21 @@ class Select extends Component
case 'mongodb':
$this->isDatabase = true;
$this->includeSwarm = false;
- $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
+ if ($this->allServers instanceof Collection) {
+ $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
+ } else {
+ $this->servers = $this->allServers;
+ }
break;
}
if (str($type)->startsWith('one-click-service') || str($type)->startsWith('docker-compose-empty')) {
$this->isDatabase = true;
$this->includeSwarm = false;
- $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
+ if ($this->allServers instanceof Collection) {
+ $this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
+ } else {
+ $this->servers = $this->allServers;
+ }
}
if ($type === "existing-postgresql") {
$this->current_step = $type;
diff --git a/app/Livewire/Project/Resource/Create.php b/app/Livewire/Project/Resource/Create.php
index 8ea77950e..48c5b107d 100644
--- a/app/Livewire/Project/Resource/Create.php
+++ b/app/Livewire/Project/Resource/Create.php
@@ -25,7 +25,7 @@ class Create extends Component
return redirect()->route('dashboard');
}
if (isset($type) && isset($destination_uuid) && isset($server_id)) {
- $services = getServiceTemplates();
+ $services = get_service_templates();
if (in_array($type, DATABASE_TYPES)) {
if ($type->value() === "postgresql") {
diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php
index 86c9a8a31..eaa794a93 100644
--- a/app/Livewire/Project/Service/Configuration.php
+++ b/app/Livewire/Project/Service/Configuration.php
@@ -67,7 +67,6 @@ class Configuration extends Component
GetContainersStatus::run($this->service->server);
// dispatch_sync(new ContainerStatusJob($this->service->server));
$this->dispatch('refresh')->self();
- $this->dispatch('updateStatus');
} catch (\Exception $e) {
return handleError($e, $this);
}
diff --git a/app/Livewire/Project/Service/FileStorage.php b/app/Livewire/Project/Service/FileStorage.php
index 1703caf8f..f10c49794 100644
--- a/app/Livewire/Project/Service/FileStorage.php
+++ b/app/Livewire/Project/Service/FileStorage.php
@@ -7,13 +7,20 @@ use App\Models\LocalFileVolume;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
use App\Models\StandaloneClickhouse;
+use App\Models\StandaloneDragonfly;
+use App\Models\StandaloneKeydb;
+use App\Models\StandaloneMariadb;
+use App\Models\StandaloneMongodb;
+use App\Models\StandaloneMysql;
+use App\Models\StandalonePostgresql;
+use App\Models\StandaloneRedis;
use Livewire\Component;
use Illuminate\Support\Str;
class FileStorage extends Component
{
public LocalFileVolume $fileStorage;
- public ServiceApplication|ServiceDatabase|StandaloneClickhouse|Application $resource;
+ public ServiceApplication|StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase|Application $resource;
public string $fs_path;
public ?string $workdir = null;
@@ -27,7 +34,7 @@ class FileStorage extends Component
{
$this->resource = $this->fileStorage->service;
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
- $this->workdir = $this->resource->service->workdir();
+ $this->workdir = $this->resource->service?->workdir();
$this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
} else {
$this->workdir = null;
diff --git a/app/Livewire/Project/Service/Navbar.php b/app/Livewire/Project/Service/Navbar.php
index 141859ed4..392178633 100644
--- a/app/Livewire/Project/Service/Navbar.php
+++ b/app/Livewire/Project/Service/Navbar.php
@@ -30,7 +30,6 @@ class Navbar extends Component
$userId = auth()->user()->id;
return [
"echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
- "updateStatus"=> '$refresh',
];
}
public function serviceStarted()
diff --git a/app/Livewire/Project/Service/StackForm.php b/app/Livewire/Project/Service/StackForm.php
index 03bf99245..7eca5bf2d 100644
--- a/app/Livewire/Project/Service/StackForm.php
+++ b/app/Livewire/Project/Service/StackForm.php
@@ -42,7 +42,7 @@ class StackForm extends Component
$this->validationAttributes["fields.$key.value"] = $fieldKey;
}
}
- $this->fields = $this->fields->sortDesc();
+ $this->fields = $this->fields->sortBy('name');
}
public function saveCompose($raw)
{
@@ -52,7 +52,7 @@ class StackForm extends Component
public function instantSave()
{
$this->service->save();
- $this->dispatch('success', 'Service settings saved.');
+ $this->dispatch('success', 'Service settings saved.');
}
public function submit()
diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php
index 2ed764cd1..158549b06 100644
--- a/app/Livewire/Project/Shared/Danger.php
+++ b/app/Livewire/Project/Shared/Danger.php
@@ -24,6 +24,7 @@ class Danger extends Component
public function delete()
{
try {
+ // $this->authorize('delete', $this->resource);
$this->resource->delete();
DeleteResourceJob::dispatch($this->resource, $this->delete_configurations);
return redirect()->route('project.resource.index', [
diff --git a/app/Livewire/Project/Shared/GetLogs.php b/app/Livewire/Project/Shared/GetLogs.php
index e14cd6113..0060fa16e 100644
--- a/app/Livewire/Project/Shared/GetLogs.php
+++ b/app/Livewire/Project/Shared/GetLogs.php
@@ -43,6 +43,11 @@ class GetLogs extends Component
$this->showTimeStamps = $this->resource->is_include_timestamps;
}
}
+ if ($this->resource?->getMorphClass() === 'App\Models\Application') {
+ if (str($this->container)->contains('-pr-')) {
+ $this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value();
+ }
+ }
}
}
public function doSomethingWithThisChunkOfOutput($output)
@@ -77,13 +82,6 @@ class GetLogs extends Component
if (!$this->server->isFunctional()) {
return;
}
- if ($this->resource?->getMorphClass() === 'App\Models\Application') {
- if (str($this->container)->contains('-pr-')) {
- $this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value();
- } else {
- $this->pull_request = 'branch';
- }
- }
if (!$refresh && ($this->resource?->getMorphClass() === 'App\Models\Service' || str($this->container)->contains('-pr-'))) return;
if (!$this->numberOfLines) {
$this->numberOfLines = 1000;
diff --git a/app/Livewire/Project/Shared/Logs.php b/app/Livewire/Project/Shared/Logs.php
index f1d70bf28..52a7b568d 100644
--- a/app/Livewire/Project/Shared/Logs.php
+++ b/app/Livewire/Project/Shared/Logs.php
@@ -103,6 +103,14 @@ class Logs extends Component
}
}
$this->containers = $this->containers->sort();
+ if (data_get($this->query,'pull_request_id')) {
+ $this->containers = $this->containers->filter(function ($container) {
+ return str_contains($container, $this->query['pull_request_id']);
+ });
+ ray($this->containers);
+
+ }
+
$this->loadMetrics();
} catch (\Exception $e) {
return handleError($e, $this);
diff --git a/app/Livewire/Project/Shared/Storages/Add.php b/app/Livewire/Project/Shared/Storages/Add.php
index 2bfbf7baf..156078805 100644
--- a/app/Livewire/Project/Shared/Storages/Add.php
+++ b/app/Livewire/Project/Shared/Storages/Add.php
@@ -3,21 +3,31 @@
namespace App\Livewire\Project\Shared\Storages;
use App\Models\Application;
+use App\Models\LocalFileVolume;
use Livewire\Component;
class Add extends Component
{
+ public $resource;
public $uuid;
public $parameters;
public $isSwarm = false;
public string $name;
public string $mount_path;
public ?string $host_path = null;
+ public string $file_storage_path;
+ public ?string $file_storage_content = null;
+ public string $file_storage_directory_source;
+ public string $file_storage_directory_destination;
public $rules = [
'name' => 'required|string',
'mount_path' => 'required|string',
'host_path' => 'string|nullable',
+ 'file_storage_path' => 'string',
+ 'file_storage_content' => 'nullable|string',
+ 'file_storage_directory_source' => 'string',
+ 'file_storage_directory_destination' => 'string',
];
protected $listeners = ['clearAddStorage' => 'clear'];
@@ -26,10 +36,16 @@ class Add extends Component
'name' => 'name',
'mount_path' => 'mount',
'host_path' => 'host',
+ 'file_storage_path' => 'file storage path',
+ 'file_storage_content' => 'file storage content',
+ 'file_storage_directory_source' => 'file storage directory source',
+ 'file_storage_directory_destination' => 'file storage directory destination',
];
public function mount()
{
+ $this->file_storage_directory_source = application_configuration_dir() . "/{$this->resource->uuid}";
+ $this->uuid = $this->resource->uuid;
$this->parameters = get_route_parameters();
if (data_get($this->parameters, 'application_uuid')) {
$applicationUuid = $this->parameters['application_uuid'];
@@ -43,18 +59,75 @@ class Add extends Component
}
}
}
-
- public function submit()
+ public function submitFileStorage()
{
try {
- $this->validate($this->rules);
+ $this->validate([
+ 'file_storage_path' => 'string',
+ 'file_storage_content' => 'nullable|string',
+ ]);
+ $this->file_storage_path = trim($this->file_storage_path);
+ $this->file_storage_path = str($this->file_storage_path)->start('/')->value();
+ if ($this->resource->getMorphClass() === 'App\Models\Application') {
+ $fs_path = application_configuration_dir() . '/' . $this->resource->uuid . $this->file_storage_path;
+ }
+ LocalFileVolume::create(
+ [
+ 'fs_path' => $fs_path,
+ 'mount_path' => $this->file_storage_path,
+ 'content' => $this->file_storage_content,
+ 'is_directory' => false,
+ 'resource_id' => $this->resource->id,
+ 'resource_type' => get_class($this->resource)
+ ],
+ );
+ $this->dispatch('refresh_storages');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+
+ }
+ public function submitFileStorageDirectory()
+ {
+ try {
+ $this->validate([
+ 'file_storage_directory_source' => 'string',
+ 'file_storage_directory_destination' => 'string',
+ ]);
+ $this->file_storage_directory_source = trim($this->file_storage_directory_source);
+ $this->file_storage_directory_source = str($this->file_storage_directory_source)->start('/')->value();
+ $this->file_storage_directory_destination = trim($this->file_storage_directory_destination);
+ $this->file_storage_directory_destination = str($this->file_storage_directory_destination)->start('/')->value();
+ LocalFileVolume::create(
+ [
+ 'fs_path' => $this->file_storage_directory_source,
+ 'mount_path' => $this->file_storage_directory_destination,
+ 'is_directory' => true,
+ 'resource_id' => $this->resource->id,
+ 'resource_type' => get_class($this->resource)
+ ],
+ );
+ $this->dispatch('refresh_storages');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+
+ }
+ public function submitPersistentVolume()
+ {
+ try {
+ $this->validate([
+ 'name' => 'required|string',
+ 'mount_path' => 'required|string',
+ 'host_path' => 'string|nullable',
+ ]);
$name = $this->uuid . '-' . $this->name;
$this->dispatch('addNewVolume', [
'name' => $name,
'mount_path' => $this->mount_path,
'host_path' => $this->host_path,
]);
- $this->dispatch('closeStorageModal');
+
} catch (\Throwable $e) {
return handleError($e, $this);
}
diff --git a/app/Livewire/Project/Shared/Storages/Show.php b/app/Livewire/Project/Shared/Storages/Show.php
index 5e35b796a..283930174 100644
--- a/app/Livewire/Project/Shared/Storages/Show.php
+++ b/app/Livewire/Project/Shared/Storages/Show.php
@@ -12,6 +12,8 @@ class Show extends Component
public bool $isReadOnly = false;
public ?string $modalId = null;
public bool $isFirst = true;
+ public bool $isService = false;
+ public ?string $startedAt = null;
protected $rules = [
'storage.name' => 'required|string',
diff --git a/app/Livewire/Project/Shared/Webhooks.php b/app/Livewire/Project/Shared/Webhooks.php
index 6bb9428d5..35a383ece 100644
--- a/app/Livewire/Project/Shared/Webhooks.php
+++ b/app/Livewire/Project/Shared/Webhooks.php
@@ -11,10 +11,12 @@ class Webhooks extends Component
public ?string $githubManualWebhook = null;
public ?string $gitlabManualWebhook = null;
public ?string $bitbucketManualWebhook = null;
+ public ?string $giteaManualWebhook = null;
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 function saveSecret()
{
@@ -32,6 +34,7 @@ class Webhooks extends Component
$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()
{
diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php
index c1dcd34ce..44f016aca 100644
--- a/app/Livewire/Server/Form.php
+++ b/app/Livewire/Server/Form.php
@@ -58,6 +58,12 @@ class Form extends Component
$this->server->refresh();
$this->server->settings->refresh();
}
+ public function updatedServerSettingsIsBuildServer()
+ {
+ $this->dispatch('serverInstalled');
+ $this->dispatch('serverRefresh');
+ $this->dispatch('proxyStatusUpdated');
+ }
public function instantSave()
{
try {
diff --git a/app/Livewire/Server/New/ByIp.php b/app/Livewire/Server/New/ByIp.php
index 1ce3df273..c56e9bec6 100644
--- a/app/Livewire/Server/New/ByIp.php
+++ b/app/Livewire/Server/New/ByIp.php
@@ -97,6 +97,9 @@ class ByIp extends Component
if ($this->is_swarm_worker) {
$payload['swarm_cluster'] = $this->selected_swarm_cluster;
}
+ if ($this->is_build_server) {
+ data_forget($payload, 'proxy');
+ }
$server = Server::create($payload);
if ($this->is_build_server) {
$this->is_swarm_manager = false;
diff --git a/app/Livewire/Server/Proxy/Deploy.php b/app/Livewire/Server/Proxy/Deploy.php
index 82925c396..5587451a4 100644
--- a/app/Livewire/Server/Proxy/Deploy.php
+++ b/app/Livewire/Server/Proxy/Deploy.php
@@ -72,6 +72,8 @@ class Deploy extends Component
public function startProxy()
{
try {
+ $this->server->proxy->force_stop = false;
+ $this->server->save();
$activity = StartProxy::run($this->server);
$this->dispatch('activityMonitor', $activity->id, ProxyStatusChanged::class);
} catch (\Throwable $e) {
@@ -86,17 +88,15 @@ class Deploy extends Component
instant_remote_process([
"docker service rm coolify-proxy_traefik",
], $this->server);
- $this->server->proxy->status = 'exited';
- $this->server->save();
- $this->dispatch('proxyStatusUpdated');
} else {
instant_remote_process([
"docker rm -f coolify-proxy",
], $this->server);
- $this->server->proxy->status = 'exited';
- $this->server->save();
- $this->dispatch('proxyStatusUpdated');
}
+ $this->server->proxy->status = 'exited';
+ $this->server->proxy->force_stop = true;
+ $this->server->save();
+ $this->dispatch('proxyStatusUpdated');
} catch (\Throwable $e) {
return handleError($e, $this);
}
diff --git a/app/Livewire/Server/ValidateAndInstall.php b/app/Livewire/Server/ValidateAndInstall.php
index b148fe2c4..aef7b800c 100644
--- a/app/Livewire/Server/ValidateAndInstall.php
+++ b/app/Livewire/Server/ValidateAndInstall.php
@@ -129,6 +129,9 @@ class ValidateAndInstall extends Component
}
}
+ if ($this->server->isBuildServer()) {
+ return;
+ }
$this->dispatch('startProxy');
}
public function render()
diff --git a/app/Livewire/Settings/Backup.php b/app/Livewire/Settings/Backup.php
index 121b73af8..82b3075c0 100644
--- a/app/Livewire/Settings/Backup.php
+++ b/app/Livewire/Settings/Backup.php
@@ -36,7 +36,7 @@ class Backup extends Component
public function mount()
{
- $this->backup = $this->database?->scheduledBackups->first() ?? [];
+ $this->backup = $this->database?->scheduledBackups->first() ?? null;
$this->executions = $this->backup?->executions ?? [];
}
public function add_coolify_database()
diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php
index 54dbe1bdb..68dc59a7f 100644
--- a/app/Livewire/Settings/Configuration.php
+++ b/app/Livewire/Settings/Configuration.php
@@ -70,9 +70,8 @@ class Configuration extends Component
$this->validate();
if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) {
- ray('asdf');
if (!validate_dns_entry($this->settings->fqdn, $this->server)) {
- $this->dispatch('error', "Validating DNS ({$this->settings->fqdn}) failed.
Make sure you have added the DNS records correctly.
Check this documentation for further help.");
+ $this->dispatch('error', "Validating DNS failed.
Make sure you have added the DNS records correctly.
{$this->settings->fqdn}->{$this->server->ip}
Check this documentation for further help.");
$error_show = true;
}
}
diff --git a/app/Livewire/Team/AdminView.php b/app/Livewire/Team/AdminView.php
index 12546ff1b..97bc6c04f 100644
--- a/app/Livewire/Team/AdminView.php
+++ b/app/Livewire/Team/AdminView.php
@@ -10,6 +10,8 @@ class AdminView extends Component
{
public $users;
public ?string $search = "";
+ public bool $lots_of_users = false;
+ private $number_of_users_to_show = 20;
public function mount()
{
if (!isInstanceAdmin()) {
@@ -32,8 +34,14 @@ class AdminView extends Component
}
public function getUsers()
{
- $this->users = User::where('id', '!=', auth()->id())->get();
- // $this->users = User::all();
+ $users = User::where('id', '!=', auth()->id())->get();
+ if ($users->count() > $this->number_of_users_to_show) {
+ $this->lots_of_users = true;
+ $this->users = $users->take($this->number_of_users_to_show);
+ } else {
+ $this->lots_of_users = false;
+ $this->users = $users;
+ }
}
private function finalizeDeletion(User $user, Team $team)
{
@@ -59,6 +67,9 @@ class AdminView extends Component
}
public function delete($id)
{
+ if (!auth()->user()->isInstanceAdmin()) {
+ return $this->dispatch('error', 'You are not authorized to delete users');
+ }
$user = User::find($id);
$teams = $user->teams;
foreach ($teams as $team) {
diff --git a/app/Livewire/Team/InviteLink.php b/app/Livewire/Team/InviteLink.php
index cc9054888..c03bb0c45 100644
--- a/app/Livewire/Team/InviteLink.php
+++ b/app/Livewire/Team/InviteLink.php
@@ -17,6 +17,10 @@ class InviteLink extends Component
public string $email;
public string $role = 'member';
+ protected $rules = [
+ 'email' => 'required|email',
+ 'role' => 'required|string',
+ ];
public function mount()
{
$this->email = isDev() ? 'test3@example.com' : '';
@@ -34,6 +38,7 @@ class InviteLink extends Component
private function generate_invite_link(bool $sendEmail = false)
{
try {
+ $this->validate();
$member_emails = currentTeam()->members()->get()->pluck('email');
if ($member_emails->contains($this->email)) {
return handleError(livewire: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . ".");
diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php
index 5ef966f43..e81ee93e6 100644
--- a/app/Livewire/Upgrade.php
+++ b/app/Livewire/Upgrade.php
@@ -5,11 +5,9 @@ namespace App\Livewire;
use App\Actions\Server\UpdateCoolify;
use Livewire\Component;
-use DanHarrin\LivewireRateLimiting\WithRateLimiting;
class Upgrade extends Component
{
- use WithRateLimiting;
public bool $showProgress = false;
public bool $updateInProgress = false;
public bool $isUpgradeAvailable = false;
@@ -31,9 +29,8 @@ class Upgrade extends Component
if ($this->updateInProgress) {
return;
}
- $this->rateLimit(1, 60);
$this->updateInProgress = true;
- UpdateCoolify::run(force: true, async: true);
+ UpdateCoolify::run(manual_update: true);
} catch (\Throwable $e) {
return handleError($e, $this);
}
diff --git a/app/Models/Application.php b/app/Models/Application.php
index 0f3425dd6..e0ed328f9 100644
--- a/app/Models/Application.php
+++ b/app/Models/Application.php
@@ -10,6 +10,7 @@ use Illuminate\Support\Collection;
use Spatie\Activitylog\Models\Activity;
use Illuminate\Support\Str;
use RuntimeException;
+use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml;
use Visus\Cuid2\Cuid2;
@@ -213,6 +214,13 @@ class Application extends BaseModel
$git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
return "https://{$git_repository}/commit/{$link}";
}
+ if (str($this->git_repository)->contains('bitbucket')) {
+ $git_repository = str_replace('.git', '', $this->git_repository);
+ $url = Url::fromString($git_repository);
+ $url = $url->withUserInfo('');
+ $url = $url->withPath($url->getPath() . '/commits/' . $link);
+ return $url->__toString();
+ }
return $this->git_repository;
}
public function dockerfileLocation(): Attribute
@@ -661,7 +669,9 @@ class Application extends BaseModel
$git_clone_command = "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository} {$baseDir}";
$fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}";
}
- $git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: false);
+ if (!$only_checkout) {
+ $git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command, public: false);
+ }
if ($exec_in_docker) {
$commands->push(executeInDocker($deployment_uuid, $git_clone_command));
} else {
@@ -880,7 +890,8 @@ class Application extends BaseModel
// }
$commands = collect([
"rm -rf /tmp/{$uuid}",
- "mkdir -p /tmp/{$uuid} && cd /tmp/{$uuid}",
+ "mkdir -p /tmp/{$uuid}",
+ "cd /tmp/{$uuid}",
$cloneCommand,
"git sparse-checkout init --cone",
"git sparse-checkout set {$fileList->implode(' ')}",
@@ -891,29 +902,15 @@ class Application extends BaseModel
if (!$composeFileContent) {
$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
Check if you used the right extension (.yaml or .yml) in the compose file name.");
} else {
$this->docker_compose_raw = $composeFileContent;
$this->save();
}
- // if ($composeFile === $prComposeFile) {
- // $this->docker_compose_pr_raw = $composeFileContent;
- // $this->save();
- // } else {
- // $commands = collect([
- // "cd /tmp/{$uuid}",
- // "cat .$workdir$prComposeFile",
- // ]);
- // $composePrFileContent = instant_remote_process($commands, $this->destination->server, false);
- // if (!$composePrFileContent) {
- // $this->docker_compose_pr_location = $initialDockerComposePrLocation;
- // $this->save();
- // throw new \Exception("Could not load compose file from $workdir$prComposeFile");
- // } else {
- // $this->docker_compose_pr_raw = $composePrFileContent;
- // $this->save();
- // }
- // }
$commands = collect([
"rm -rf /tmp/{$uuid}",
@@ -1055,4 +1052,29 @@ class Application extends BaseModel
}
}
}
+ function generate_preview_fqdn(int $pull_request_id) {
+ $preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->id, $pull_request_id);
+ if (is_null(data_get($preview, 'fqdn')) && $this->fqdn) {
+ if (str($this->fqdn)->contains(',')) {
+ $url = Url::fromString(str($this->fqdn)->explode(',')[0]);
+ $preview_fqdn = getFqdnWithoutPort(str($this->fqdn)->explode(',')[0]);
+ } else {
+ $url = Url::fromString($this->fqdn);
+ if (data_get($preview, 'fqdn')) {
+ $preview_fqdn = getFqdnWithoutPort(data_get($preview, 'fqdn'));
+ }
+ }
+ $template = $this->preview_url_template;
+ $host = $url->getHost();
+ $schema = $url->getScheme();
+ $random = new Cuid2(7);
+ $preview_fqdn = str_replace('{{random}}', $random, $template);
+ $preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
+ $preview_fqdn = str_replace('{{pr_id}}', $pull_request_id, $preview_fqdn);
+ $preview_fqdn = "$schema://$preview_fqdn";
+ $preview->fqdn = $preview_fqdn;
+ $preview->save();
+ }
+ return $preview;
+ }
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 2f4c29080..38c427dc4 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -525,7 +525,7 @@ $schema://$host {
// Reached max number of retries
if ($this->unreachable_notification_sent === false) {
ray('Server unreachable, sending notification...');
- $this->team?->notify(new Unreachable($this));
+ // $this->team?->notify(new Unreachable($this));
$this->update(['unreachable_notification_sent' => true]);
}
if ($this->settings->is_reachable === true) {
@@ -825,7 +825,7 @@ $schema://$host {
'unreachable_count' => 0,
]);
if (data_get($server, 'unreachable_notification_sent') === true) {
- $server->team?->notify(new Revived($server));
+ // $server->team?->notify(new Revived($server));
$server->update(['unreachable_notification_sent' => false]);
}
return ['uptime' => true, 'error' => null];
@@ -897,7 +897,9 @@ $schema://$host {
}
public function validateDockerEngineVersion()
{
- $dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this, false);
+ $dockerVersionRaw = instant_remote_process(["docker version --format json"], $this, false);
+ $dockerVersionJson = json_decode($dockerVersionRaw, true);
+ $dockerVersion = data_get($dockerVersionJson, 'Server.Version', '0.0.0');
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
if (is_null($dockerVersion)) {
$this->settings->is_usable = false;
@@ -927,4 +929,7 @@ $schema://$host {
}
return $this->user !== 'root';
}
+ public function isBuildServer() {
+ return $this->settings->is_build_server;
+ }
}
diff --git a/app/Models/Service.php b/app/Models/Service.php
index 4c20b71de..ac7c15dcf 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -472,6 +472,72 @@ class Service extends BaseModel
}
$fields->put('Admin', $data->toArray());
break;
+ case str($image)?->contains('vaultwarden'):
+ $data = collect([]);
+
+ $DATABASE_URL = $this->environment_variables()->where('key', 'DATABASE_URL')->first();
+ $ADMIN_TOKEN = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_64_ADMIN')->first();
+ $SIGNUP_ALLOWED = $this->environment_variables()->where('key', 'SIGNUP_ALLOWED')->first();
+ $PUSH_ENABLED = $this->environment_variables()->where('key', 'PUSH_ENABLED')->first();
+ $PUSH_INSTALLATION_ID = $this->environment_variables()->where('key', 'PUSH_SERVICE_ID')->first();
+ $PUSH_INSTALLATION_KEY = $this->environment_variables()->where('key', 'PUSH_SERVICE_KEY')->first();
+
+ if ($DATABASE_URL) {
+ $data = $data->merge([
+ 'Database URL' => [
+ 'key' => data_get($DATABASE_URL, 'key'),
+ 'value' => data_get($DATABASE_URL, 'value'),
+ ],
+ ]);
+ }
+ if ($ADMIN_TOKEN) {
+ $data = $data->merge([
+ 'Admin Password' => [
+ 'key' => data_get($ADMIN_TOKEN, 'key'),
+ 'value' => data_get($ADMIN_TOKEN, 'value'),
+ 'isPassword' => true,
+ ],
+ ]);
+ }
+ if ($SIGNUP_ALLOWED) {
+ $data = $data->merge([
+ 'Signup Allowed' => [
+ 'key' => data_get($SIGNUP_ALLOWED, 'key'),
+ 'value' => data_get($SIGNUP_ALLOWED, 'value'),
+ 'rules' => 'required|string|in:true,false',
+ ],
+ ]);
+ }
+
+ if ($PUSH_ENABLED) {
+ $data = $data->merge([
+ 'Push Enabled' => [
+ 'key' => data_get($PUSH_ENABLED, 'key'),
+ 'value' => data_get($PUSH_ENABLED, 'value'),
+ 'rules' => 'required|string|in:true,false',
+ ],
+ ]);
+ }
+ if ($PUSH_INSTALLATION_ID) {
+ $data = $data->merge([
+ 'Push Installation ID' => [
+ 'key' => data_get($PUSH_INSTALLATION_ID, 'key'),
+ 'value' => data_get($PUSH_INSTALLATION_ID, 'value'),
+ ],
+ ]);
+ }
+ if ($PUSH_INSTALLATION_KEY) {
+ $data = $data->merge([
+ 'Push Installation Key' => [
+ 'key' => data_get($PUSH_INSTALLATION_KEY, 'key'),
+ 'value' => data_get($PUSH_INSTALLATION_KEY, 'value'),
+ 'isPassword' => true,
+ ],
+ ]);
+ }
+
+ $fields->put('Vaultwarden', $data);
+ break;
}
}
$databases = $this->databases()->get();
@@ -659,7 +725,7 @@ class Service extends BaseModel
return route('project.service.scheduled-tasks', [
'project_uuid' => data_get($this, 'environment.project.uuid'),
'environment_name' => data_get($this, 'environment.name'),
- 'application_uuid' => data_get($this, 'uuid'),
+ 'service_uuid' => data_get($this, 'uuid'),
'task_uuid' => $task_uuid
]);
}
@@ -667,7 +733,7 @@ class Service extends BaseModel
}
public function documentation()
{
- $services = getServiceTemplates();
+ $services = get_service_templates();
$service = data_get($services, str($this->name)->beforeLast('-')->value, []);
return data_get($service, 'documentation', config('constants.docs.base_url'));
}
diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php
index 820ef6fee..f8fcda004 100644
--- a/app/Models/ServiceApplication.php
+++ b/app/Models/ServiceApplication.php
@@ -40,6 +40,13 @@ class ServiceApplication extends BaseModel
{
return 'service';
}
+ public function team()
+ {
+ return data_get($this, 'environment.project.team');
+ }
+ public function workdir() {
+ return service_configuration_dir() . "/{$this->service->uuid}";
+ }
public function serviceType()
{
$found = str(collect(SPECIFIC_SERVICES)->filter(function ($service) {
diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php
index 76c174d08..9d90641e1 100644
--- a/app/Models/ServiceDatabase.php
+++ b/app/Models/ServiceDatabase.php
@@ -59,6 +59,13 @@ class ServiceDatabase extends BaseModel
}
return "{$realIp}:{$port}";
}
+ public function team()
+ {
+ return data_get($this, 'environment.project.team');
+ }
+ public function workdir() {
+ return service_configuration_dir() . "/{$this->service->uuid}";
+ }
public function service()
{
return $this->belongsTo(Service::class);
diff --git a/app/Policies/ApplicationPolicy.php b/app/Policies/ApplicationPolicy.php
new file mode 100644
index 000000000..860479a94
--- /dev/null
+++ b/app/Policies/ApplicationPolicy.php
@@ -0,0 +1,69 @@
+isAdmin()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether the user can restore the model.
+ */
+ public function restore(User $user, Application $application): bool
+ {
+ return true;
+ }
+
+ /**
+ * Determine whether the user can permanently delete the model.
+ */
+ public function forceDelete(User $user, Application $application): bool
+ {
+ return true;
+ }
+}
diff --git a/app/Policies/ServicePolicy.php b/app/Policies/ServicePolicy.php
new file mode 100644
index 000000000..93882be9a
--- /dev/null
+++ b/app/Policies/ServicePolicy.php
@@ -0,0 +1,79 @@
+isAdmin()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether the user can restore the model.
+ */
+ public function restore(User $user, Service $service): bool
+ {
+ return true;
+ }
+
+ /**
+ * Determine whether the user can permanently delete the model.
+ */
+ public function forceDelete(User $user, Service $service): bool
+ {
+ if ($user->isAdmin()) {
+ return true;
+ }
+ return false;
+ }
+ public function stop(User $user, Service $service): bool
+ {
+ if ($user->isAdmin()) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/bootstrap/helpers/applications.php b/bootstrap/helpers/applications.php
index a1995c645..39d21bcca 100644
--- a/bootstrap/helpers/applications.php
+++ b/bootstrap/helpers/applications.php
@@ -43,16 +43,10 @@ function queue_application_deployment(Application $application, string $deployme
]);
if ($no_questions_asked) {
- $deployment->update([
- 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
- ]);
dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $deployment->id,
));
} else if (next_queuable($server_id, $application_id)) {
- $deployment->update([
- 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
- ]);
dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $deployment->id,
));
@@ -63,6 +57,7 @@ function force_start_deployment(ApplicationDeploymentQueue $deployment)
$deployment->update([
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
]);
+
dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $deployment->id,
));
@@ -75,6 +70,7 @@ function queue_next_deployment(Application $application)
$next_found->update([
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
]);
+
dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $next_found->id,
));
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index a087c92c5..0ce578758 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -173,14 +173,14 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
{
if ($resource->getMorphClass() === 'App\Models\ServiceApplication') {
- $uuid = $resource->uuid;
- $server = $resource->service->server;
- $environment_variables = $resource->service->environment_variables;
+ $uuid = data_get($resource, 'uuid');
+ $server = data_get($resource, 'service.server');
+ $environment_variables = data_get($resource, 'service.environment_variables');
$type = $resource->serviceType();
} else if ($resource->getMorphClass() === 'App\Models\Application') {
- $uuid = $resource->uuid;
- $server = $resource->destination->server;
- $environment_variables = $resource->environment_variables;
+ $uuid = data_get($resource, 'uuid');
+ $server = data_get($resource, 'destination.server');
+ $environment_variables = data_get($resource, 'environment_variables');
$type = $resource->serviceType();
}
if (is_null($server) || is_null($type)) {
@@ -234,7 +234,7 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
}
return $payload;
}
-function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null)
+function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null)
{
$labels = collect([]);
if ($serviceLabels) {
@@ -247,7 +247,6 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
$url = Url::fromString($domain);
$host = $url->getHost();
$path = $url->getPath();
- // $stripped_path = str($path)->replaceEnd('/', '');
$schema = $url->getScheme();
$port = $url->getPort();
@@ -273,7 +272,7 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
}
return $labels->sort();
}
-function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false)
+function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, bool $generate_unique_uuid = false, ?string $image = null)
{
$labels = collect([]);
$labels->push('traefik.enable=true');
@@ -331,7 +330,10 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$http_label = "http-{$loop}-{$uuid}-{$service_name}";
$https_label = "https-{$loop}-{$uuid}-{$service_name}";
}
-
+ if (str($image)->contains('ghost')) {
+ $labels->push("traefik.http.middlewares.redir-ghost.redirectregex.regex=^{$path}/(.*)");
+ $labels->push("traefik.http.middlewares.redir-ghost.redirectregex.replacement=/$1");
+ }
if ($schema === 'https') {
// Set labels for https
$labels->push("traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
@@ -341,9 +343,10 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port");
}
if ($path !== '/') {
- if ($is_stripprefix_enabled) {
+ $middlewares = collect([]);
+ if ($is_stripprefix_enabled && !str($image)->contains('ghost')) {
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
- $middlewares = collect(["{$https_label}-stripprefix"]);
+ $middlewares->push("{$https_label}-stripprefix");
}
if ($is_gzip_enabled) {
$middlewares->push('gzip');
@@ -354,6 +357,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if ($redirect && $redirect_middleware) {
$middlewares->push($redirect_middleware);
}
+ if (str($image)->contains('ghost')) {
+ $middlewares->push('redir-ghost');
+ }
if ($middlewares->isNotEmpty()) {
$middlewares = $middlewares->join(',');
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
@@ -369,6 +375,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if ($redirect && $redirect_middleware) {
$middlewares->push($redirect_middleware);
}
+ if (str($image)->contains('ghost')) {
+ $middlewares->push('redir-ghost');
+ }
if ($middlewares->isNotEmpty()) {
$middlewares = $middlewares->join(',');
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
@@ -396,9 +405,10 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
}
if ($path !== '/') {
- if ($is_stripprefix_enabled) {
+ $middlewares = collect([]);
+ if ($is_stripprefix_enabled && !str($image)->contains('ghost')) {
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
- $middlewares = collect(["{$http_label}-stripprefix"]);
+ $middlewares->push("{$https_label}-stripprefix");
}
if ($is_gzip_enabled) {
$middlewares->push('gzip');
@@ -409,6 +419,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if ($redirect && $redirect_middleware) {
$middlewares->push($redirect_middleware);
}
+ if (str($image)->contains('ghost')) {
+ $middlewares->push('redir-ghost');
+ }
if ($middlewares->isNotEmpty()) {
$middlewares = $middlewares->join(',');
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
@@ -424,6 +437,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
if ($redirect && $redirect_middleware) {
$middlewares->push($redirect_middleware);
}
+ if (str($image)->contains('ghost')) {
+ $middlewares->push('redir-ghost');
+ }
if ($middlewares->isNotEmpty()) {
$middlewares = $middlewares->join(',');
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
@@ -449,13 +465,32 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
$appUuid = $appUuid . '-pr-' . $pull_request_id;
}
$labels = collect([]);
- if ($application->fqdn) {
- if ($pull_request_id !== 0) {
- $domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
- } else {
+ if ($pull_request_id === 0) {
+ if ($application->fqdn) {
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
+ $labels = $labels->merge(fqdnLabelsForTraefik(
+ uuid: $appUuid,
+ domains: $domains,
+ onlyPort: $onlyPort,
+ is_force_https_enabled: $application->isForceHttpsEnabled(),
+ is_gzip_enabled: $application->isGzipEnabled(),
+ is_stripprefix_enabled: $application->isStripprefixEnabled()
+ ));
+ // Add Caddy labels
+ $labels = $labels->merge(fqdnLabelsForCaddy(
+ network: $application->destination->network,
+ uuid: $appUuid,
+ domains: $domains,
+ onlyPort: $onlyPort,
+ is_force_https_enabled: $application->isForceHttpsEnabled(),
+ is_gzip_enabled: $application->isGzipEnabled(),
+ is_stripprefix_enabled: $application->isStripprefixEnabled()
+ ));
+ }
+ } else {
+ if ($preview->fqdn) {
+ $domains = Str::of(data_get($preview, 'fqdn'))->explode(',');
}
- // Add Traefik labels
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
@@ -474,6 +509,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
+
}
return $labels->all();
}
diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php
index 26a69222a..5021071d8 100644
--- a/bootstrap/helpers/services.php
+++ b/bootstrap/helpers/services.php
@@ -110,16 +110,18 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$fqdn = Url::fromString($resourceFqdns);
$port = $fqdn->getPort();
+ $path = $fqdn->getPath();
$fqdn = $fqdn->getScheme() . '://' . $fqdn->getHost();
if ($generatedEnv) {
- $generatedEnv->value = $fqdn;
+ $generatedEnv->value = $fqdn . $path;
$generatedEnv->save();
}
if ($port) {
$variableName = $variableName . "_$port";
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
+ // ray($generatedEnv);
if ($generatedEnv) {
- $generatedEnv->value = $fqdn . ':' . $port;
+ $generatedEnv->value = $fqdn . $path;
$generatedEnv->save();
}
}
@@ -127,17 +129,18 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$url = Url::fromString($fqdn);
$port = $url->getPort();
+ $path = $url->getPath();
$url = $url->getHost();
if ($generatedEnv) {
$url = Str::of($fqdn)->after('://');
- $generatedEnv->value = $url;
+ $generatedEnv->value = $url . $path;
$generatedEnv->save();
}
if ($port) {
$variableName = $variableName . "_$port";
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
if ($generatedEnv) {
- $generatedEnv->value = $url . ':' . $port;
+ $generatedEnv->value = $url . $path;
$generatedEnv->save();
}
}
@@ -146,6 +149,7 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$host = Url::fromString($fqdn);
$port = $host->getPort();
$url = $host->getHost();
+ $path = $host->getPath();
$host = $host->getScheme() . '://' . $host->getHost();
if ($port) {
$port_envs = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'like', "SERVICE_FQDN_%_$port")->get();
@@ -153,10 +157,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$service_fqdn = str($port_env->key)->beforeLast('_')->after('SERVICE_FQDN_');
$env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_FQDN_' . $service_fqdn)->first();
if ($env) {
- $env->value = $host;
+ $env->value = $host . $path;
$env->save();
}
- $port_env->value = $host . ':' . $port;
+ $port_env->value = $host . $path;
$port_env->save();
}
$port_envs_url = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'like', "SERVICE_URL_%_$port")->get();
@@ -164,17 +168,17 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$service_url = str($port_env_url->key)->beforeLast('_')->after('SERVICE_URL_');
$env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_URL_' . $service_url)->first();
if ($env) {
- $env->value = $url;
+ $env->value = $url . $path;
$env->save();
}
- $port_env_url->value = $url . ':' . $port;
+ $port_env_url->value = $url . $path;
$port_env_url->save();
}
} else {
$variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper()->replace('-', '');
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$fqdn = Url::fromString($fqdn);
- $fqdn = $fqdn->getScheme() . '://' . $fqdn->getHost();
+ $fqdn = $fqdn->getScheme() . '://' . $fqdn->getHost() . $fqdn->getPath();
if ($generatedEnv) {
$generatedEnv->value = $fqdn;
$generatedEnv->save();
@@ -182,7 +186,7 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$variableName = "SERVICE_URL_" . Str::of($resource->name)->upper()->replace('-', '');
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$url = Url::fromString($fqdn);
- $url = $url->getHost();
+ $url = $url->getHost() . $url->getPath();
if ($generatedEnv) {
$url = Str::of($fqdn)->after('://');
$generatedEnv->value = $url;
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 6453108eb..5c65eb218 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -165,9 +165,12 @@ function get_latest_sentinel_version(): string
function get_latest_version_of_coolify(): string
{
try {
- $response = Http::get('https://cdn.coollabs.io/coolify/versions.json');
- $versions = $response->json();
+ $versions = File::get(base_path('versions.json'));
+ $versions = json_decode($versions, true);
return data_get($versions, 'coolify.v4.version');
+ // $response = Http::get('https://cdn.coollabs.io/coolify/versions.json');
+ // $versions = $response->json();
+ // return data_get($versions, 'coolify.v4.version');
} catch (\Throwable $e) {
//throw $e;
ray($e->getMessage());
@@ -462,24 +465,24 @@ function sslip(Server $server)
return "http://{$server->ip}.sslip.io";
}
-function getServiceTemplates()
+function get_service_templates(bool $force = false): Collection
{
- if (isDev()) {
- $services = File::get(base_path('templates/service-templates.json'));
- $services = collect(json_decode($services))->sortKeys();
- } else {
+ if ($force) {
try {
$response = Http::retry(3, 50)->get(config('constants.services.official'));
if ($response->failed()) {
return collect([]);
}
$services = $response->json();
- $services = collect($services)->sortKeys();
+ return collect($services);
} catch (\Throwable $e) {
- $services = collect([]);
+ $services = File::get(base_path('templates/service-templates.json'));
+ return collect(json_decode($services))->sortKeys();
}
+ } else {
+ $services = File::get(base_path('templates/service-templates.json'));
+ return collect(json_decode($services))->sortKeys();
}
- return $services;
}
function getResourceByUuid(string $uuid, ?int $teamId = null)
@@ -573,6 +576,13 @@ function getTopLevelNetworks(Service|Application $resource)
// Collect/create/update networks
if ($serviceNetworks->count() > 0) {
foreach ($serviceNetworks as $networkName => $networkDetails) {
+ if ($networkName === 'default') {
+ continue;
+ }
+ // ignore alias
+ if ($networkDetails['aliases'] ?? false) {
+ continue;
+ }
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
return $value == $networkName || $key == $networkName;
});
@@ -615,6 +625,13 @@ function getTopLevelNetworks(Service|Application $resource)
// Collect/create/update networks
if ($serviceNetworks->count() > 0) {
foreach ($serviceNetworks as $networkName => $networkDetails) {
+ if ($networkName === 'default') {
+ continue;
+ }
+ // ignore alias
+ if ($networkDetails['aliases'] ?? false) {
+ continue;
+ }
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
return $value == $networkName || $key == $networkName;
});
@@ -639,9 +656,10 @@ function getTopLevelNetworks(Service|Application $resource)
return $topLevelNetworks->keys();
}
}
+
function parseDockerComposeFile(Service|Application $resource, bool $isNew = false, int $pull_request_id = 0, bool $is_pr = false)
{
- // ray()->clearAll();
+ ray()->clearAll();
if ($resource->getMorphClass() === 'App\Models\Service') {
if ($resource->docker_compose_raw) {
try {
@@ -649,7 +667,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
- $allServices = getServiceTemplates();
+ $allServices = get_service_templates();
$topLevelVolumes = collect(data_get($yaml, 'volumes', []));
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
$services = data_get($yaml, 'services');
@@ -663,6 +681,16 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
}
}
$definedNetwork = collect([$resource->uuid]);
+ if ($topLevelVolumes->count() > 0) {
+ $tempTopLevelVolumes = collect([]);
+ foreach ($topLevelVolumes as $volumeName => $volume) {
+ if (is_null($volume)) {
+ continue;
+ }
+ $tempTopLevelVolumes->put($volumeName, $volume);
+ }
+ $topLevelVolumes = collect($tempTopLevelVolumes);
+ }
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource, $allServices) {
// Workarounds for beta users.
if ($serviceName === 'registry') {
@@ -761,6 +789,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
// Collect/create/update networks
if ($serviceNetworks->count() > 0) {
foreach ($serviceNetworks as $networkName => $networkDetails) {
+ if ($networkName === 'default') {
+ continue;
+ }
+ // ignore alias
+ if ($networkDetails['aliases'] ?? false) {
+ continue;
+ }
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
return $value == $networkName || $key == $networkName;
});
@@ -812,7 +847,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
// default:
// ipv4_address: 192.168.203.254
// $networks->put($serviceNetwork, null);
- ray($key);
$networks->put($key, $serviceNetwork);
}
}
@@ -876,6 +910,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
]
);
} else if ($type->value() === 'volume') {
+ if ($topLevelVolumes->has($source->value())) {
+ return $volume;
+ }
$slugWithoutUuid = Str::slug($source, '-');
$name = "{$savedService->service->uuid}_{$slugWithoutUuid}";
if (is_string($volume)) {
@@ -1007,7 +1044,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'service_id' => $resource->id,
])->first();
if ($env) {
-
$env_url = Url::fromString($savedService->fqdn);
$env_port = $env_url->getPort();
if ($env_port !== $predefinedPort) {
@@ -1049,6 +1085,17 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
}
if ($foundEnv) {
$fqdn = data_get($foundEnv, 'value');
+ // if ($savedService->fqdn) {
+ // $savedServiceFqdn = Url::fromString($savedService->fqdn);
+ // $parsedFqdn = Url::fromString($fqdn);
+ // $savedServicePath = $savedServiceFqdn->getPath();
+ // $parsedFqdnPath = $parsedFqdn->getPath();
+ // if ($savedServicePath != $parsedFqdnPath) {
+ // $fqdn = $parsedFqdn->withPath($savedServicePath)->__toString();
+ // $foundEnv->value = $fqdn;
+ // $foundEnv->save();
+ // }
+ // }
} else {
if ($command->value() === 'URL') {
$fqdn = Str::of($fqdn)->after('://')->value();
@@ -1153,7 +1200,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
serviceLabels: $serviceLabels,
is_gzip_enabled: $savedService->isGzipEnabled(),
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
- service_name: $serviceName
+ service_name: $serviceName,
+ image: data_get($service, 'image')
));
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
network: $resource->destination->network,
@@ -1163,7 +1211,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
serviceLabels: $serviceLabels,
is_gzip_enabled: $savedService->isGzipEnabled(),
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
- service_name: $serviceName
+ service_name: $serviceName,
+ image: data_get($service, 'image')
));
}
}
@@ -1189,13 +1238,14 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if (!data_get($service, 'restart')) {
data_set($service, 'restart', RESTART_MODE);
}
- if (data_get($service, 'restart') === 'no') {
+ if (data_get($service, 'restart') === 'no' || data_get($service, 'exclude_from_hc')) {
$savedService->update(['exclude_from_status' => true]);
}
data_set($service, 'container_name', $containerName);
data_forget($service, 'volumes.*.content');
data_forget($service, 'volumes.*.isDirectory');
data_forget($service, 'volumes.*.is_directory');
+ data_forget($service, 'exclude_from_hc');
// Remove unnecessary variables from service.environment
// $withoutServiceEnvs = collect([]);
@@ -1217,6 +1267,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'volumes' => $topLevelVolumes->toArray(),
'networks' => $topLevelNetworks->toArray(),
];
+ $yaml = data_forget($yaml, 'services.*.volumes.*.content');
$resource->docker_compose_raw = Yaml::dump($yaml, 10, 2);
$resource->docker_compose = Yaml::dump($finalServices, 10, 2);
$resource->save();
@@ -1249,6 +1300,18 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($pull_request_id !== 0) {
$topLevelVolumes = collect([]);
}
+
+ if ($topLevelVolumes->count() > 0) {
+ $tempTopLevelVolumes = collect([]);
+ foreach ($topLevelVolumes as $volumeName => $volume) {
+ if (is_null($volume)) {
+ continue;
+ }
+ $tempTopLevelVolumes->put($volumeName, $volume);
+ }
+ $topLevelVolumes = collect($tempTopLevelVolumes);
+ }
+
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
$services = data_get($yaml, 'services');
@@ -1312,13 +1375,17 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($pull_request_id !== 0) {
$name = $name . "-pr-$pull_request_id";
$volume = str("$name:$mount");
- $topLevelVolumes->put($name, [
- 'name' => $name,
- ]);
+ if (!$topLevelVolumes->has($name)) {
+ $topLevelVolumes->put($name, [
+ 'name' => $name,
+ ]);
+ }
} else {
- $topLevelVolumes->put($name->value(), [
- 'name' => $name->value(),
- ]);
+ if (!$topLevelVolumes->has($name->value())) {
+ $topLevelVolumes->put($name->value(), [
+ 'name' => $name->value(),
+ ]);
+ }
}
}
} else {
@@ -1362,9 +1429,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
data_set($volume, 'source', $source . ':' . $target);
}
if (!str($source)->startsWith('/')) {
- $topLevelVolumes->put($source, [
- 'name' => $source,
- ]);
+ if (!$topLevelVolumes->has($source)) {
+ $topLevelVolumes->put($source, [
+ 'name' => $source,
+ ]);
+ }
}
}
}
@@ -1391,6 +1460,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
// Collect/create/update networks
if ($serviceNetworks->count() > 0) {
foreach ($serviceNetworks as $networkName => $networkDetails) {
+ if ($networkName === 'default') {
+ continue;
+ }
+ // ignore alias
+ if ($networkDetails['aliases'] ?? false) {
+ continue;
+ }
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
return $value == $networkName || $key == $networkName;
});
@@ -1642,13 +1718,15 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
uuid: $resource->uuid,
domains: $fqdns,
serviceLabels: $serviceLabels,
- generate_unique_uuid: $resource->build_pack === 'dockercompose'
+ generate_unique_uuid: $resource->build_pack === 'dockercompose',
+ image: data_get($service, 'image')
));
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
network: $resource->destination->network,
uuid: $resource->uuid,
domains: $fqdns,
- serviceLabels: $serviceLabels
+ serviceLabels: $serviceLabels,
+ image: data_get($service, 'image')
));
}
}
diff --git a/composer.json b/composer.json
index cb98eba57..b8ab3baa1 100644
--- a/composer.json
+++ b/composer.json
@@ -14,16 +14,16 @@
"guzzlehttp/guzzle": "^7.5.0",
"laravel/fortify": "^v1.16.0",
"laravel/framework": "^v10.7.1",
- "laravel/horizon": "^5.15",
+ "laravel/horizon": "^5.23.1",
"laravel/prompts": "^0.1.6",
"laravel/sanctum": "^v3.2.1",
- "laravel/socialite": "^5.12",
+ "laravel/socialite": "^v5.14.0",
"laravel/tinker": "^v2.8.1",
"laravel/ui": "^4.2",
"lcobucci/jwt": "^5.0.0",
"league/flysystem-aws-s3-v3": "^3.0",
"league/flysystem-sftp-v3": "^3.0",
- "livewire/livewire": "^3.0",
+ "livewire/livewire": "3.4.9",
"lorisleiva/laravel-actions": "^2.7",
"nubs/random-name-generator": "^2.2",
"phpseclib/phpseclib": "~3.0",
diff --git a/composer.lock b/composer.lock
index 55cd48f95..bcc419441 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "e6fd1d5c5183226a78df717b52343393",
+ "content-hash": "bbef91ebb38f73fc712a7d3173bf7b39",
"packages": [
{
"name": "amphp/amp",
- "version": "v3.0.0",
+ "version": "v3.0.2",
"source": {
"type": "git",
"url": "https://github.com/amphp/amp.git",
- "reference": "aaf0ec1d5a2c20b523258995a10e80c1fb765871"
+ "reference": "138801fb68cfc9c329da8a7b39d01ce7291ee4b0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/amp/zipball/aaf0ec1d5a2c20b523258995a10e80c1fb765871",
- "reference": "aaf0ec1d5a2c20b523258995a10e80c1fb765871",
+ "url": "https://api.github.com/repos/amphp/amp/zipball/138801fb68cfc9c329da8a7b39d01ce7291ee4b0",
+ "reference": "138801fb68cfc9c329da8a7b39d01ce7291ee4b0",
"shasum": ""
},
"require": {
@@ -27,7 +27,7 @@
"require-dev": {
"amphp/php-cs-fixer-config": "^2",
"phpunit/phpunit": "^9",
- "psalm/phar": "^4.13"
+ "psalm/phar": "5.23.1"
},
"type": "library",
"autoload": {
@@ -77,7 +77,7 @@
],
"support": {
"issues": "https://github.com/amphp/amp/issues",
- "source": "https://github.com/amphp/amp/tree/v3.0.0"
+ "source": "https://github.com/amphp/amp/tree/v3.0.2"
},
"funding": [
{
@@ -85,7 +85,7 @@
"type": "github"
}
],
- "time": "2022-12-18T16:52:44+00:00"
+ "time": "2024-05-10T21:37:46+00:00"
},
{
"name": "amphp/byte-stream",
@@ -164,16 +164,16 @@
},
{
"name": "amphp/cache",
- "version": "v2.0.0",
+ "version": "v2.0.1",
"source": {
"type": "git",
"url": "https://github.com/amphp/cache.git",
- "reference": "218bb3888d380eb9dd926cd06f803573c84391d3"
+ "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/cache/zipball/218bb3888d380eb9dd926cd06f803573c84391d3",
- "reference": "218bb3888d380eb9dd926cd06f803573c84391d3",
+ "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c",
+ "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c",
"shasum": ""
},
"require": {
@@ -217,7 +217,7 @@
"homepage": "https://amphp.org/cache",
"support": {
"issues": "https://github.com/amphp/cache/issues",
- "source": "https://github.com/amphp/cache/tree/v2.0.0"
+ "source": "https://github.com/amphp/cache/tree/v2.0.1"
},
"funding": [
{
@@ -225,20 +225,20 @@
"type": "github"
}
],
- "time": "2023-01-09T21:04:12+00:00"
+ "time": "2024-04-19T03:38:06+00:00"
},
{
"name": "amphp/dns",
- "version": "v2.1.1",
+ "version": "v2.1.2",
"source": {
"type": "git",
"url": "https://github.com/amphp/dns.git",
- "reference": "3e3f413fbbaacd9632b1612941b363fa26a72e52"
+ "reference": "04c88e67bef804203df934703bd422ea72f46b0e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/dns/zipball/3e3f413fbbaacd9632b1612941b363fa26a72e52",
- "reference": "3e3f413fbbaacd9632b1612941b363fa26a72e52",
+ "url": "https://api.github.com/repos/amphp/dns/zipball/04c88e67bef804203df934703bd422ea72f46b0e",
+ "reference": "04c88e67bef804203df934703bd422ea72f46b0e",
"shasum": ""
},
"require": {
@@ -305,7 +305,7 @@
],
"support": {
"issues": "https://github.com/amphp/dns/issues",
- "source": "https://github.com/amphp/dns/tree/v2.1.1"
+ "source": "https://github.com/amphp/dns/tree/v2.1.2"
},
"funding": [
{
@@ -313,7 +313,7 @@
"type": "github"
}
],
- "time": "2024-01-30T23:25:30+00:00"
+ "time": "2024-04-19T03:49:29+00:00"
},
{
"name": "amphp/parallel",
@@ -530,16 +530,16 @@
},
{
"name": "amphp/process",
- "version": "v2.0.2",
+ "version": "v2.0.3",
"source": {
"type": "git",
"url": "https://github.com/amphp/process.git",
- "reference": "a79dc87100be857db2c4bbfd5369585a6d1e658c"
+ "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/process/zipball/a79dc87100be857db2c4bbfd5369585a6d1e658c",
- "reference": "a79dc87100be857db2c4bbfd5369585a6d1e658c",
+ "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d",
+ "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d",
"shasum": ""
},
"require": {
@@ -586,7 +586,7 @@
"homepage": "https://amphp.org/process",
"support": {
"issues": "https://github.com/amphp/process/issues",
- "source": "https://github.com/amphp/process/tree/v2.0.2"
+ "source": "https://github.com/amphp/process/tree/v2.0.3"
},
"funding": [
{
@@ -594,7 +594,7 @@
"type": "github"
}
],
- "time": "2024-02-13T20:38:21+00:00"
+ "time": "2024-04-19T03:13:44+00:00"
},
{
"name": "amphp/serialization",
@@ -656,16 +656,16 @@
},
{
"name": "amphp/socket",
- "version": "v2.3.0",
+ "version": "v2.3.1",
"source": {
"type": "git",
"url": "https://github.com/amphp/socket.git",
- "reference": "acc0a2f65ab498025ba5641f7cce499c4b1ed4b5"
+ "reference": "58e0422221825b79681b72c50c47a930be7bf1e1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/amphp/socket/zipball/acc0a2f65ab498025ba5641f7cce499c4b1ed4b5",
- "reference": "acc0a2f65ab498025ba5641f7cce499c4b1ed4b5",
+ "url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1",
+ "reference": "58e0422221825b79681b72c50c47a930be7bf1e1",
"shasum": ""
},
"require": {
@@ -728,7 +728,7 @@
],
"support": {
"issues": "https://github.com/amphp/socket/issues",
- "source": "https://github.com/amphp/socket/tree/v2.3.0"
+ "source": "https://github.com/amphp/socket/tree/v2.3.1"
},
"funding": [
{
@@ -736,7 +736,7 @@
"type": "github"
}
],
- "time": "2024-03-19T20:01:53+00:00"
+ "time": "2024-04-21T14:33:03+00:00"
},
{
"name": "amphp/sync",
@@ -867,16 +867,16 @@
},
{
"name": "aws/aws-crt-php",
- "version": "v1.2.4",
+ "version": "v1.2.5",
"source": {
"type": "git",
"url": "https://github.com/awslabs/aws-crt-php.git",
- "reference": "eb0c6e4e142224a10b08f49ebf87f32611d162b2"
+ "reference": "0ea1f04ec5aa9f049f97e012d1ed63b76834a31b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/eb0c6e4e142224a10b08f49ebf87f32611d162b2",
- "reference": "eb0c6e4e142224a10b08f49ebf87f32611d162b2",
+ "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/0ea1f04ec5aa9f049f97e012d1ed63b76834a31b",
+ "reference": "0ea1f04ec5aa9f049f97e012d1ed63b76834a31b",
"shasum": ""
},
"require": {
@@ -915,22 +915,22 @@
],
"support": {
"issues": "https://github.com/awslabs/aws-crt-php/issues",
- "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.4"
+ "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.5"
},
- "time": "2023-11-08T00:42:13+00:00"
+ "time": "2024-04-19T21:30:56+00:00"
},
{
"name": "aws/aws-sdk-php",
- "version": "3.301.6",
+ "version": "3.308.4",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "18c0ebd71d3071304f1ea02aa9af75f95863177a"
+ "reference": "c88e9df7e076b6e2c652a1c87d2c3af0a9ac30b6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/18c0ebd71d3071304f1ea02aa9af75f95863177a",
- "reference": "18c0ebd71d3071304f1ea02aa9af75f95863177a",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c88e9df7e076b6e2c652a1c87d2c3af0a9ac30b6",
+ "reference": "c88e9df7e076b6e2c652a1c87d2c3af0a9ac30b6",
"shasum": ""
},
"require": {
@@ -1010,34 +1010,34 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
- "source": "https://github.com/aws/aws-sdk-php/tree/3.301.6"
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.308.4"
},
- "time": "2024-03-22T18:05:21+00:00"
+ "time": "2024-05-28T18:05:38+00:00"
},
{
"name": "bacon/bacon-qr-code",
- "version": "2.0.8",
+ "version": "v3.0.0",
"source": {
"type": "git",
"url": "https://github.com/Bacon/BaconQrCode.git",
- "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22"
+ "reference": "510de6eca6248d77d31b339d62437cc995e2fb41"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22",
- "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22",
+ "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/510de6eca6248d77d31b339d62437cc995e2fb41",
+ "reference": "510de6eca6248d77d31b339d62437cc995e2fb41",
"shasum": ""
},
"require": {
"dasprid/enum": "^1.0.3",
"ext-iconv": "*",
- "php": "^7.1 || ^8.0"
+ "php": "^8.1"
},
"require-dev": {
- "phly/keep-a-changelog": "^2.1",
- "phpunit/phpunit": "^7 | ^8 | ^9",
- "spatie/phpunit-snapshot-assertions": "^4.2.9",
- "squizlabs/php_codesniffer": "^3.4"
+ "phly/keep-a-changelog": "^2.12",
+ "phpunit/phpunit": "^10.5.11 || 11.0.4",
+ "spatie/phpunit-snapshot-assertions": "^5.1.5",
+ "squizlabs/php_codesniffer": "^3.9"
},
"suggest": {
"ext-imagick": "to generate QR code images"
@@ -1064,31 +1064,31 @@
"homepage": "https://github.com/Bacon/BaconQrCode",
"support": {
"issues": "https://github.com/Bacon/BaconQrCode/issues",
- "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8"
+ "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.0"
},
- "time": "2022-12-07T17:46:57+00:00"
+ "time": "2024-04-18T11:16:25+00:00"
},
{
"name": "brick/math",
- "version": "0.11.0",
+ "version": "0.12.1",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
- "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478"
+ "reference": "f510c0a40911935b77b86859eb5223d58d660df1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478",
- "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478",
+ "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1",
+ "reference": "f510c0a40911935b77b86859eb5223d58d660df1",
"shasum": ""
},
"require": {
- "php": "^8.0"
+ "php": "^8.1"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.2",
- "phpunit/phpunit": "^9.0",
- "vimeo/psalm": "5.0.0"
+ "phpunit/phpunit": "^10.1",
+ "vimeo/psalm": "5.16.0"
},
"type": "library",
"autoload": {
@@ -1108,12 +1108,17 @@
"arithmetic",
"bigdecimal",
"bignum",
+ "bignumber",
"brick",
- "math"
+ "decimal",
+ "integer",
+ "math",
+ "mathematics",
+ "rational"
],
"support": {
"issues": "https://github.com/brick/math/issues",
- "source": "https://github.com/brick/math/tree/0.11.0"
+ "source": "https://github.com/brick/math/tree/0.12.1"
},
"funding": [
{
@@ -1121,7 +1126,7 @@
"type": "github"
}
],
- "time": "2023-01-15T23:15:59+00:00"
+ "time": "2023-11-29T23:19:16+00:00"
},
{
"name": "carbonphp/carbon-doctrine-types",
@@ -1260,16 +1265,16 @@
},
{
"name": "danharrin/livewire-rate-limiting",
- "version": "v1.3.0",
+ "version": "v1.3.1",
"source": {
"type": "git",
"url": "https://github.com/danharrin/livewire-rate-limiting.git",
- "reference": "bf16003f0d977b5a41071526d697eec94ac41735"
+ "reference": "1a1b299e20de61f88ed6e94ea0bbcfc33aab1ddb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/danharrin/livewire-rate-limiting/zipball/bf16003f0d977b5a41071526d697eec94ac41735",
- "reference": "bf16003f0d977b5a41071526d697eec94ac41735",
+ "url": "https://api.github.com/repos/danharrin/livewire-rate-limiting/zipball/1a1b299e20de61f88ed6e94ea0bbcfc33aab1ddb",
+ "reference": "1a1b299e20de61f88ed6e94ea0bbcfc33aab1ddb",
"shasum": ""
},
"require": {
@@ -1310,7 +1315,7 @@
"type": "github"
}
],
- "time": "2024-01-21T14:53:34+00:00"
+ "time": "2024-05-06T09:10:03+00:00"
},
{
"name": "dasprid/enum",
@@ -1364,21 +1369,21 @@
},
{
"name": "daverandom/libdns",
- "version": "v2.0.3",
+ "version": "v2.1.0",
"source": {
"type": "git",
"url": "https://github.com/DaveRandom/LibDNS.git",
- "reference": "42c2d700d1178c9f9e78664793463f7f1aea248c"
+ "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/42c2d700d1178c9f9e78664793463f7f1aea248c",
- "reference": "42c2d700d1178c9f9e78664793463f7f1aea248c",
+ "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a",
+ "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a",
"shasum": ""
},
"require": {
"ext-ctype": "*",
- "php": ">=7.0"
+ "php": ">=7.1"
},
"suggest": {
"ext-intl": "Required for IDN support"
@@ -1402,9 +1407,9 @@
],
"support": {
"issues": "https://github.com/DaveRandom/LibDNS/issues",
- "source": "https://github.com/DaveRandom/LibDNS/tree/v2.0.3"
+ "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0"
},
- "time": "2022-09-20T18:15:38+00:00"
+ "time": "2024-04-12T12:12:48+00:00"
},
{
"name": "dflydev/dot-access-data",
@@ -1576,16 +1581,16 @@
},
{
"name": "doctrine/dbal",
- "version": "3.8.3",
+ "version": "3.8.4",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
- "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c"
+ "reference": "b05e48a745f722801f55408d0dbd8003b403dbbd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/dbal/zipball/db922ba9436b7b18a23d1653a0b41ff2369ca41c",
- "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c",
+ "url": "https://api.github.com/repos/doctrine/dbal/zipball/b05e48a745f722801f55408d0dbd8003b403dbbd",
+ "reference": "b05e48a745f722801f55408d0dbd8003b403dbbd",
"shasum": ""
},
"require": {
@@ -1669,7 +1674,7 @@
],
"support": {
"issues": "https://github.com/doctrine/dbal/issues",
- "source": "https://github.com/doctrine/dbal/tree/3.8.3"
+ "source": "https://github.com/doctrine/dbal/tree/3.8.4"
},
"funding": [
{
@@ -1685,7 +1690,7 @@
"type": "tidelift"
}
],
- "time": "2024-03-03T15:55:06+00:00"
+ "time": "2024-04-25T07:04:44+00:00"
},
{
"name": "doctrine/deprecations",
@@ -1736,16 +1741,16 @@
},
{
"name": "doctrine/event-manager",
- "version": "2.0.0",
+ "version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/event-manager.git",
- "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32"
+ "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/event-manager/zipball/750671534e0241a7c50ea5b43f67e23eb5c96f32",
- "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32",
+ "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e",
+ "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e",
"shasum": ""
},
"require": {
@@ -1755,10 +1760,10 @@
"doctrine/common": "<2.9"
},
"require-dev": {
- "doctrine/coding-standard": "^10",
+ "doctrine/coding-standard": "^12",
"phpstan/phpstan": "^1.8.8",
- "phpunit/phpunit": "^9.5",
- "vimeo/psalm": "^4.28"
+ "phpunit/phpunit": "^10.5",
+ "vimeo/psalm": "^5.24"
},
"type": "library",
"autoload": {
@@ -1807,7 +1812,7 @@
],
"support": {
"issues": "https://github.com/doctrine/event-manager/issues",
- "source": "https://github.com/doctrine/event-manager/tree/2.0.0"
+ "source": "https://github.com/doctrine/event-manager/tree/2.0.1"
},
"funding": [
{
@@ -1823,7 +1828,7 @@
"type": "tidelift"
}
],
- "time": "2022-10-12T20:59:15+00:00"
+ "time": "2024-05-22T20:47:39+00:00"
},
{
"name": "doctrine/inflector",
@@ -2121,6 +2126,69 @@
],
"time": "2023-10-06T06:47:41+00:00"
},
+ {
+ "name": "firebase/php-jwt",
+ "version": "v6.10.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/firebase/php-jwt.git",
+ "reference": "500501c2ce893c824c801da135d02661199f60c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5",
+ "reference": "500501c2ce893c824c801da135d02661199f60c5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "guzzlehttp/guzzle": "^7.4",
+ "phpspec/prophecy-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.5",
+ "psr/cache": "^2.0||^3.0",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0"
+ },
+ "suggest": {
+ "ext-sodium": "Support EdDSA (Ed25519) signatures",
+ "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Firebase\\JWT\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Neuman Vong",
+ "email": "neuman+pear@twilio.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Anant Narayanan",
+ "email": "anant@php.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+ "homepage": "https://github.com/firebase/php-jwt",
+ "keywords": [
+ "jwt",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/firebase/php-jwt/issues",
+ "source": "https://github.com/firebase/php-jwt/tree/v6.10.1"
+ },
+ "time": "2024-05-18T18:05:11+00:00"
+ },
{
"name": "fruitcake/php-cors",
"version": "v1.3.0",
@@ -2842,24 +2910,25 @@
},
{
"name": "laravel/fortify",
- "version": "v1.21.0",
+ "version": "v1.21.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/fortify.git",
- "reference": "b34e672e1d341f6e520f81712f73e56f6cb80767"
+ "reference": "a725684d17959c4750f3b441ff2e94ecde7793a1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/fortify/zipball/b34e672e1d341f6e520f81712f73e56f6cb80767",
- "reference": "b34e672e1d341f6e520f81712f73e56f6cb80767",
+ "url": "https://api.github.com/repos/laravel/fortify/zipball/a725684d17959c4750f3b441ff2e94ecde7793a1",
+ "reference": "a725684d17959c4750f3b441ff2e94ecde7793a1",
"shasum": ""
},
"require": {
- "bacon/bacon-qr-code": "^2.0",
+ "bacon/bacon-qr-code": "^3.0",
"ext-json": "*",
"illuminate/support": "^10.0|^11.0",
"php": "^8.1",
- "pragmarx/google2fa": "^8.0"
+ "pragmarx/google2fa": "^8.0",
+ "symfony/console": "^6.0|^7.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
@@ -2902,20 +2971,20 @@
"issues": "https://github.com/laravel/fortify/issues",
"source": "https://github.com/laravel/fortify"
},
- "time": "2024-03-08T19:55:45+00:00"
+ "time": "2024-05-08T18:07:38+00:00"
},
{
"name": "laravel/framework",
- "version": "v10.48.4",
+ "version": "v10.48.12",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "7e0701bf59cb76a51f7c1f7bea51c0c0c29c0b72"
+ "reference": "590afea38e708022662629fbf5184351fa82cf08"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/7e0701bf59cb76a51f7c1f7bea51c0c0c29c0b72",
- "reference": "7e0701bf59cb76a51f7c1f7bea51c0c0c29c0b72",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/590afea38e708022662629fbf5184351fa82cf08",
+ "reference": "590afea38e708022662629fbf5184351fa82cf08",
"shasum": ""
},
"require": {
@@ -3109,20 +3178,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2024-03-21T13:36:36+00:00"
+ "time": "2024-05-28T15:46:19+00:00"
},
{
"name": "laravel/horizon",
- "version": "v5.23.1",
+ "version": "v5.24.4",
"source": {
"type": "git",
"url": "https://github.com/laravel/horizon.git",
- "reference": "7475de7eb5b465c2da84218002fe1a62b8175da0"
+ "reference": "8d31ff178bf5493efc2b2629c10612054f31f584"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/horizon/zipball/7475de7eb5b465c2da84218002fe1a62b8175da0",
- "reference": "7475de7eb5b465c2da84218002fe1a62b8175da0",
+ "url": "https://api.github.com/repos/laravel/horizon/zipball/8d31ff178bf5493efc2b2629c10612054f31f584",
+ "reference": "8d31ff178bf5493efc2b2629c10612054f31f584",
"shasum": ""
},
"require": {
@@ -3135,6 +3204,7 @@
"nesbot/carbon": "^2.17|^3.0",
"php": "^8.0",
"ramsey/uuid": "^4.0",
+ "symfony/console": "^6.0|^7.0",
"symfony/error-handler": "^6.0|^7.0",
"symfony/process": "^6.0|^7.0"
},
@@ -3185,22 +3255,22 @@
],
"support": {
"issues": "https://github.com/laravel/horizon/issues",
- "source": "https://github.com/laravel/horizon/tree/v5.23.1"
+ "source": "https://github.com/laravel/horizon/tree/v5.24.4"
},
- "time": "2024-02-20T15:14:10+00:00"
+ "time": "2024-05-03T13:34:14+00:00"
},
{
"name": "laravel/prompts",
- "version": "v0.1.16",
+ "version": "v0.1.23",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
- "reference": "ca6872ab6aec3ab61db3a61f83a6caf764ec7781"
+ "reference": "9bc4df7c699b0452c6b815e64a2d84b6d7f99400"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/prompts/zipball/ca6872ab6aec3ab61db3a61f83a6caf764ec7781",
- "reference": "ca6872ab6aec3ab61db3a61f83a6caf764ec7781",
+ "url": "https://api.github.com/repos/laravel/prompts/zipball/9bc4df7c699b0452c6b815e64a2d84b6d7f99400",
+ "reference": "9bc4df7c699b0452c6b815e64a2d84b6d7f99400",
"shasum": ""
},
"require": {
@@ -3240,11 +3310,12 @@
"license": [
"MIT"
],
+ "description": "Add beautiful and user-friendly forms to your command-line applications.",
"support": {
"issues": "https://github.com/laravel/prompts/issues",
- "source": "https://github.com/laravel/prompts/tree/v0.1.16"
+ "source": "https://github.com/laravel/prompts/tree/v0.1.23"
},
- "time": "2024-02-21T19:25:27+00:00"
+ "time": "2024-05-27T13:53:20+00:00"
},
{
"name": "laravel/sanctum",
@@ -3374,26 +3445,28 @@
},
{
"name": "laravel/socialite",
- "version": "v5.12.1",
+ "version": "v5.14.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/socialite.git",
- "reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf"
+ "reference": "c7b0193a3753a29aff8ce80aa2f511917e6ed68a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/socialite/zipball/7dae1b072573809f32ab6dcf4aebb57c8b3e8acf",
- "reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf",
+ "url": "https://api.github.com/repos/laravel/socialite/zipball/c7b0193a3753a29aff8ce80aa2f511917e6ed68a",
+ "reference": "c7b0193a3753a29aff8ce80aa2f511917e6ed68a",
"shasum": ""
},
"require": {
"ext-json": "*",
+ "firebase/php-jwt": "^6.4",
"guzzlehttp/guzzle": "^6.0|^7.0",
"illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"league/oauth1-client": "^1.10.1",
- "php": "^7.2|^8.0"
+ "php": "^7.2|^8.0",
+ "phpseclib/phpseclib": "^3.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
@@ -3440,7 +3513,7 @@
"issues": "https://github.com/laravel/socialite/issues",
"source": "https://github.com/laravel/socialite"
},
- "time": "2024-02-16T08:58:20+00:00"
+ "time": "2024-05-03T20:31:38+00:00"
},
{
"name": "laravel/tinker",
@@ -3510,16 +3583,16 @@
},
{
"name": "laravel/ui",
- "version": "v4.5.0",
+ "version": "v4.5.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/ui.git",
- "reference": "da3811f409297d13feccd5858ce748e7474b3d11"
+ "reference": "c75396f63268c95b053c8e4814eb70e0875e9628"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/ui/zipball/da3811f409297d13feccd5858ce748e7474b3d11",
- "reference": "da3811f409297d13feccd5858ce748e7474b3d11",
+ "url": "https://api.github.com/repos/laravel/ui/zipball/c75396f63268c95b053c8e4814eb70e0875e9628",
+ "reference": "c75396f63268c95b053c8e4814eb70e0875e9628",
"shasum": ""
},
"require": {
@@ -3527,7 +3600,8 @@
"illuminate/filesystem": "^9.21|^10.0|^11.0",
"illuminate/support": "^9.21|^10.0|^11.0",
"illuminate/validation": "^9.21|^10.0|^11.0",
- "php": "^8.0"
+ "php": "^8.0",
+ "symfony/console": "^6.0|^7.0"
},
"require-dev": {
"orchestra/testbench": "^7.35|^8.15|^9.0",
@@ -3566,22 +3640,22 @@
"ui"
],
"support": {
- "source": "https://github.com/laravel/ui/tree/v4.5.0"
+ "source": "https://github.com/laravel/ui/tree/v4.5.2"
},
- "time": "2024-03-04T13:58:27+00:00"
+ "time": "2024-05-08T18:07:10+00:00"
},
{
"name": "lcobucci/jwt",
- "version": "5.2.0",
+ "version": "5.3.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
- "reference": "0ba88aed12c04bd2ed9924f500673f32b67a6211"
+ "reference": "08071d8d2c7f4b00222cc4b1fb6aa46990a80f83"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/lcobucci/jwt/zipball/0ba88aed12c04bd2ed9924f500673f32b67a6211",
- "reference": "0ba88aed12c04bd2ed9924f500673f32b67a6211",
+ "url": "https://api.github.com/repos/lcobucci/jwt/zipball/08071d8d2c7f4b00222cc4b1fb6aa46990a80f83",
+ "reference": "08071d8d2c7f4b00222cc4b1fb6aa46990a80f83",
"shasum": ""
},
"require": {
@@ -3629,7 +3703,7 @@
],
"support": {
"issues": "https://github.com/lcobucci/jwt/issues",
- "source": "https://github.com/lcobucci/jwt/tree/5.2.0"
+ "source": "https://github.com/lcobucci/jwt/tree/5.3.0"
},
"funding": [
{
@@ -3641,7 +3715,7 @@
"type": "patreon"
}
],
- "time": "2023-11-20T21:17:42+00:00"
+ "time": "2024-04-11T23:07:54+00:00"
},
{
"name": "league/commonmark",
@@ -3833,16 +3907,16 @@
},
{
"name": "league/flysystem",
- "version": "3.25.1",
+ "version": "3.28.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
- "reference": "abbd664eb4381102c559d358420989f835208f18"
+ "reference": "e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/abbd664eb4381102c559d358420989f835208f18",
- "reference": "abbd664eb4381102c559d358420989f835208f18",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c",
+ "reference": "e611adab2b1ae2e3072fa72d62c62f52c2bf1f0c",
"shasum": ""
},
"require": {
@@ -3866,10 +3940,13 @@
"composer/semver": "^3.0",
"ext-fileinfo": "*",
"ext-ftp": "*",
+ "ext-mongodb": "^1.3",
"ext-zip": "*",
"friendsofphp/php-cs-fixer": "^3.5",
"google/cloud-storage": "^1.23",
+ "guzzlehttp/psr7": "^2.6",
"microsoft/azure-storage-blob": "^1.1",
+ "mongodb/mongodb": "^1.2",
"phpseclib/phpseclib": "^3.0.36",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.5.11|^10.0",
@@ -3907,32 +3984,22 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
- "source": "https://github.com/thephpleague/flysystem/tree/3.25.1"
+ "source": "https://github.com/thephpleague/flysystem/tree/3.28.0"
},
- "funding": [
- {
- "url": "https://ecologi.com/frankdejonge",
- "type": "custom"
- },
- {
- "url": "https://github.com/frankdejonge",
- "type": "github"
- }
- ],
- "time": "2024-03-16T12:53:19+00:00"
+ "time": "2024-05-22T10:09:12+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
- "version": "3.25.1",
+ "version": "3.28.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
- "reference": "6a5be0e6d6a93574e80805c9cc108a4b63c824d8"
+ "reference": "22071ef1604bc776f5ff2468ac27a752514665c8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/6a5be0e6d6a93574e80805c9cc108a4b63c824d8",
- "reference": "6a5be0e6d6a93574e80805c9cc108a4b63c824d8",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/22071ef1604bc776f5ff2468ac27a752514665c8",
+ "reference": "22071ef1604bc776f5ff2468ac27a752514665c8",
"shasum": ""
},
"require": {
@@ -3972,32 +4039,22 @@
"storage"
],
"support": {
- "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.25.1"
+ "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.28.0"
},
- "funding": [
- {
- "url": "https://ecologi.com/frankdejonge",
- "type": "custom"
- },
- {
- "url": "https://github.com/frankdejonge",
- "type": "github"
- }
- ],
- "time": "2024-03-15T19:58:44+00:00"
+ "time": "2024-05-06T20:05:52+00:00"
},
{
"name": "league/flysystem-local",
- "version": "3.25.1",
+ "version": "3.28.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-local.git",
- "reference": "61a6a90d6e999e4ddd9ce5adb356de0939060b92"
+ "reference": "13f22ea8be526ea58c2ddff9e158ef7c296e4f40"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/61a6a90d6e999e4ddd9ce5adb356de0939060b92",
- "reference": "61a6a90d6e999e4ddd9ce5adb356de0939060b92",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/13f22ea8be526ea58c2ddff9e158ef7c296e4f40",
+ "reference": "13f22ea8be526ea58c2ddff9e158ef7c296e4f40",
"shasum": ""
},
"require": {
@@ -4031,32 +4088,22 @@
"local"
],
"support": {
- "source": "https://github.com/thephpleague/flysystem-local/tree/3.25.1"
+ "source": "https://github.com/thephpleague/flysystem-local/tree/3.28.0"
},
- "funding": [
- {
- "url": "https://ecologi.com/frankdejonge",
- "type": "custom"
- },
- {
- "url": "https://github.com/frankdejonge",
- "type": "github"
- }
- ],
- "time": "2024-03-15T19:58:44+00:00"
+ "time": "2024-05-06T20:05:52+00:00"
},
{
"name": "league/flysystem-sftp-v3",
- "version": "3.25.1",
+ "version": "3.28.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-sftp-v3.git",
- "reference": "5cf169f33e4351832373f9d1b663c137589aa260"
+ "reference": "abedadd3c64d4f0e276d6ecc796ec8194d136b41"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/flysystem-sftp-v3/zipball/5cf169f33e4351832373f9d1b663c137589aa260",
- "reference": "5cf169f33e4351832373f9d1b663c137589aa260",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-sftp-v3/zipball/abedadd3c64d4f0e276d6ecc796ec8194d136b41",
+ "reference": "abedadd3c64d4f0e276d6ecc796ec8194d136b41",
"shasum": ""
},
"require": {
@@ -4090,19 +4137,9 @@
"sftp"
],
"support": {
- "source": "https://github.com/thephpleague/flysystem-sftp-v3/tree/3.25.1"
+ "source": "https://github.com/thephpleague/flysystem-sftp-v3/tree/3.28.0"
},
- "funding": [
- {
- "url": "https://ecologi.com/frankdejonge",
- "type": "custom"
- },
- {
- "url": "https://github.com/frankdejonge",
- "type": "github"
- }
- ],
- "time": "2024-03-16T11:44:18+00:00"
+ "time": "2024-05-06T20:05:52+00:00"
},
{
"name": "league/mime-type-detection",
@@ -4634,16 +4671,16 @@
},
{
"name": "monolog/monolog",
- "version": "3.5.0",
+ "version": "3.6.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
- "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448"
+ "reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c915e2634718dbc8a4a15c61b0e62e7a44e14448",
- "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/4b18b21a5527a3d5ffdac2fd35d3ab25a9597654",
+ "reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654",
"shasum": ""
},
"require": {
@@ -4666,7 +4703,7 @@
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-strict-rules": "^1.4",
- "phpunit/phpunit": "^10.1",
+ "phpunit/phpunit": "^10.5.17",
"predis/predis": "^1.1 || ^2",
"ruflin/elastica": "^7",
"symfony/mailer": "^5.4 || ^6",
@@ -4719,7 +4756,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
- "source": "https://github.com/Seldaek/monolog/tree/3.5.0"
+ "source": "https://github.com/Seldaek/monolog/tree/3.6.0"
},
"funding": [
{
@@ -4731,7 +4768,7 @@
"type": "tidelift"
}
],
- "time": "2023-10-27T15:32:31+00:00"
+ "time": "2024-04-12T21:02:21+00:00"
},
{
"name": "mtdowling/jmespath.php",
@@ -5331,16 +5368,16 @@
},
{
"name": "paragonie/constant_time_encoding",
- "version": "v2.6.3",
+ "version": "v2.7.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
- "reference": "58c3f47f650c94ec05a151692652a868995d2938"
+ "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938",
- "reference": "58c3f47f650c94ec05a151692652a868995d2938",
+ "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105",
+ "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105",
"shasum": ""
},
"require": {
@@ -5394,7 +5431,7 @@
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
- "time": "2022-06-14T06:56:20+00:00"
+ "time": "2024-05-08T12:18:48+00:00"
},
{
"name": "paragonie/random_compat",
@@ -5448,16 +5485,16 @@
},
{
"name": "paragonie/sodium_compat",
- "version": "v1.20.0",
+ "version": "v1.21.1",
"source": {
"type": "git",
"url": "https://github.com/paragonie/sodium_compat.git",
- "reference": "e592a3e06d1fa0d43988c7c7d9948ca836f644b6"
+ "reference": "bb312875dcdd20680419564fe42ba1d9564b9e37"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/e592a3e06d1fa0d43988c7c7d9948ca836f644b6",
- "reference": "e592a3e06d1fa0d43988c7c7d9948ca836f644b6",
+ "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bb312875dcdd20680419564fe42ba1d9564b9e37",
+ "reference": "bb312875dcdd20680419564fe42ba1d9564b9e37",
"shasum": ""
},
"require": {
@@ -5528,9 +5565,9 @@
],
"support": {
"issues": "https://github.com/paragonie/sodium_compat/issues",
- "source": "https://github.com/paragonie/sodium_compat/tree/v1.20.0"
+ "source": "https://github.com/paragonie/sodium_compat/tree/v1.21.1"
},
- "time": "2023-04-30T00:54:53+00:00"
+ "time": "2024-04-22T22:05:04+00:00"
},
{
"name": "php-http/client-common",
@@ -5603,16 +5640,16 @@
},
{
"name": "php-http/discovery",
- "version": "1.19.2",
+ "version": "1.19.4",
"source": {
"type": "git",
"url": "https://github.com/php-http/discovery.git",
- "reference": "61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb"
+ "reference": "0700efda8d7526335132360167315fdab3aeb599"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-http/discovery/zipball/61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb",
- "reference": "61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb",
+ "url": "https://api.github.com/repos/php-http/discovery/zipball/0700efda8d7526335132360167315fdab3aeb599",
+ "reference": "0700efda8d7526335132360167315fdab3aeb599",
"shasum": ""
},
"require": {
@@ -5636,7 +5673,8 @@
"php-http/httplug": "^1.0 || ^2.0",
"php-http/message-factory": "^1.0",
"phpspec/phpspec": "^5.1 || ^6.1 || ^7.3",
- "symfony/phpunit-bridge": "^6.2"
+ "sebastian/comparator": "^3.0.5 || ^4.0.8",
+ "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1"
},
"type": "composer-plugin",
"extra": {
@@ -5675,9 +5713,9 @@
],
"support": {
"issues": "https://github.com/php-http/discovery/issues",
- "source": "https://github.com/php-http/discovery/tree/1.19.2"
+ "source": "https://github.com/php-http/discovery/tree/1.19.4"
},
- "time": "2023-11-30T16:49:05+00:00"
+ "time": "2024-03-29T13:00:05+00:00"
},
{
"name": "php-http/httplug",
@@ -6210,16 +6248,16 @@
},
{
"name": "phpstan/phpdoc-parser",
- "version": "1.27.0",
+ "version": "1.29.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
- "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757"
+ "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/86e4d5a4b036f8f0be1464522f4c6b584c452757",
- "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc",
+ "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc",
"shasum": ""
},
"require": {
@@ -6251,22 +6289,22 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
- "source": "https://github.com/phpstan/phpdoc-parser/tree/1.27.0"
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0"
},
- "time": "2024-03-21T13:14:53+00:00"
+ "time": "2024-05-06T12:04:23+00:00"
},
{
"name": "phpstan/phpstan",
- "version": "1.10.65",
+ "version": "1.11.2",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "3c657d057a0b7ecae19cb12db446bbc99d8839c6"
+ "reference": "0d5d4294a70deb7547db655c47685d680e39cfec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3c657d057a0b7ecae19cb12db446bbc99d8839c6",
- "reference": "3c657d057a0b7ecae19cb12db446bbc99d8839c6",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0d5d4294a70deb7547db655c47685d680e39cfec",
+ "reference": "0d5d4294a70deb7547db655c47685d680e39cfec",
"shasum": ""
},
"require": {
@@ -6309,13 +6347,9 @@
{
"url": "https://github.com/phpstan",
"type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
- "type": "tidelift"
}
],
- "time": "2024-03-23T10:30:26+00:00"
+ "time": "2024-05-24T13:23:04+00:00"
},
{
"name": "pimple/pimple",
@@ -6786,20 +6820,20 @@
},
{
"name": "psr/http-factory",
- "version": "1.0.2",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
- "reference": "e616d01114759c4c489f93b099585439f795fe35"
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
- "reference": "e616d01114759c4c489f93b099585439f795fe35",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": ""
},
"require": {
- "php": ">=7.0.0",
+ "php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
@@ -6823,7 +6857,7 @@
"homepage": "https://www.php-fig.org/"
}
],
- "description": "Common interfaces for PSR-7 HTTP message factories",
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
@@ -6835,9 +6869,9 @@
"response"
],
"support": {
- "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
+ "source": "https://github.com/php-fig/http-factory"
},
- "time": "2023-04-10T20:10:41+00:00"
+ "time": "2024-04-15T12:06:14+00:00"
},
{
"name": "psr/http-message",
@@ -6995,16 +7029,16 @@
},
{
"name": "psy/psysh",
- "version": "v0.12.2",
+ "version": "v0.12.3",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
- "reference": "9185c66c2165bbf4d71de78a69dccf4974f9538d"
+ "reference": "b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/bobthecow/psysh/zipball/9185c66c2165bbf4d71de78a69dccf4974f9538d",
- "reference": "9185c66c2165bbf4d71de78a69dccf4974f9538d",
+ "url": "https://api.github.com/repos/bobthecow/psysh/zipball/b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73",
+ "reference": "b6b6cce7d3ee8fbf31843edce5e8f5a72eff4a73",
"shasum": ""
},
"require": {
@@ -7068,22 +7102,22 @@
],
"support": {
"issues": "https://github.com/bobthecow/psysh/issues",
- "source": "https://github.com/bobthecow/psysh/tree/v0.12.2"
+ "source": "https://github.com/bobthecow/psysh/tree/v0.12.3"
},
- "time": "2024-03-17T01:53:00+00:00"
+ "time": "2024-04-02T15:57:53+00:00"
},
{
"name": "purplepixie/phpdns",
- "version": "2.1.0",
+ "version": "2.1.1",
"source": {
"type": "git",
"url": "https://github.com/purplepixie/phpdns.git",
- "reference": "e1e4f18a60d01947e2aac7157325a9e2e7755bf7"
+ "reference": "18cd3a43fadcfd16e2789e3c78a264945f6cbfad"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/purplepixie/phpdns/zipball/e1e4f18a60d01947e2aac7157325a9e2e7755bf7",
- "reference": "e1e4f18a60d01947e2aac7157325a9e2e7755bf7",
+ "url": "https://api.github.com/repos/purplepixie/phpdns/zipball/18cd3a43fadcfd16e2789e3c78a264945f6cbfad",
+ "reference": "18cd3a43fadcfd16e2789e3c78a264945f6cbfad",
"shasum": ""
},
"require": {
@@ -7116,9 +7150,9 @@
"description": "PHP DNS Direct Query Module",
"support": {
"issues": "https://github.com/purplepixie/phpdns/issues",
- "source": "https://github.com/purplepixie/phpdns/tree/2.1.0"
+ "source": "https://github.com/purplepixie/phpdns/tree/2.1.1"
},
- "time": "2023-11-06T15:37:19+00:00"
+ "time": "2024-05-27T13:27:50+00:00"
},
{
"name": "pusher/pusher-php-server",
@@ -7316,20 +7350,20 @@
},
{
"name": "ramsey/uuid",
- "version": "4.7.5",
+ "version": "4.7.6",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
- "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e"
+ "reference": "91039bc1faa45ba123c4328958e620d382ec7088"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ramsey/uuid/zipball/5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
- "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
+ "reference": "91039bc1faa45ba123c4328958e620d382ec7088",
"shasum": ""
},
"require": {
- "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11",
+ "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
"ext-json": "*",
"php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0"
@@ -7392,7 +7426,7 @@
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
- "source": "https://github.com/ramsey/uuid/tree/4.7.5"
+ "source": "https://github.com/ramsey/uuid/tree/4.7.6"
},
"funding": [
{
@@ -7404,25 +7438,25 @@
"type": "tidelift"
}
],
- "time": "2023-11-08T05:53:05+00:00"
+ "time": "2024-04-27T21:32:50+00:00"
},
{
"name": "rector/rector",
- "version": "1.0.3",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/rectorphp/rector.git",
- "reference": "c59507a9090b465d65e1aceed91e5b81986e375b"
+ "reference": "556509e2dcf527369892b7d411379c4a02f31859"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/rectorphp/rector/zipball/c59507a9090b465d65e1aceed91e5b81986e375b",
- "reference": "c59507a9090b465d65e1aceed91e5b81986e375b",
+ "url": "https://api.github.com/repos/rectorphp/rector/zipball/556509e2dcf527369892b7d411379c4a02f31859",
+ "reference": "556509e2dcf527369892b7d411379c4a02f31859",
"shasum": ""
},
"require": {
"php": "^7.2|^8.0",
- "phpstan/phpstan": "^1.10.57"
+ "phpstan/phpstan": "^1.11"
},
"conflict": {
"rector/rector-doctrine": "*",
@@ -7430,6 +7464,9 @@
"rector/rector-phpunit": "*",
"rector/rector-symfony": "*"
},
+ "suggest": {
+ "ext-dom": "To manipulate phpunit.xml via the custom-rule command"
+ },
"bin": [
"bin/rector"
],
@@ -7452,7 +7489,7 @@
],
"support": {
"issues": "https://github.com/rectorphp/rector/issues",
- "source": "https://github.com/rectorphp/rector/tree/1.0.3"
+ "source": "https://github.com/rectorphp/rector/tree/1.1.0"
},
"funding": [
{
@@ -7460,7 +7497,7 @@
"type": "github"
}
],
- "time": "2024-03-14T15:04:18+00:00"
+ "time": "2024-05-18T09:40:27+00:00"
},
{
"name": "resend/resend-laravel",
@@ -7910,21 +7947,21 @@
},
{
"name": "socialiteproviders/manager",
- "version": "v4.5.1",
+ "version": "v4.6.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Manager.git",
- "reference": "a67f194f0f4c4c7616c549afc697b78df9658d44"
+ "reference": "dea5190981c31b89e52259da9ab1ca4e2b258b21"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/a67f194f0f4c4c7616c549afc697b78df9658d44",
- "reference": "a67f194f0f4c4c7616c549afc697b78df9658d44",
+ "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/dea5190981c31b89e52259da9ab1ca4e2b258b21",
+ "reference": "dea5190981c31b89e52259da9ab1ca4e2b258b21",
"shasum": ""
},
"require": {
"illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0",
- "laravel/socialite": "^5.2",
+ "laravel/socialite": "^5.5",
"php": "^8.0"
},
"require-dev": {
@@ -7980,7 +8017,7 @@
"issues": "https://github.com/socialiteproviders/manager/issues",
"source": "https://github.com/socialiteproviders/manager"
},
- "time": "2024-02-17T08:58:03+00:00"
+ "time": "2024-05-04T07:57:39+00:00"
},
{
"name": "socialiteproviders/microsoft-azure",
@@ -8035,16 +8072,16 @@
},
{
"name": "spatie/backtrace",
- "version": "1.5.3",
+ "version": "1.6.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/backtrace.git",
- "reference": "483f76a82964a0431aa836b6ed0edde0c248e3ab"
+ "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/backtrace/zipball/483f76a82964a0431aa836b6ed0edde0c248e3ab",
- "reference": "483f76a82964a0431aa836b6ed0edde0c248e3ab",
+ "url": "https://api.github.com/repos/spatie/backtrace/zipball/8373b9d51638292e3bfd736a9c19a654111b4a23",
+ "reference": "8373b9d51638292e3bfd736a9c19a654111b4a23",
"shasum": ""
},
"require": {
@@ -8052,6 +8089,7 @@
},
"require-dev": {
"ext-json": "*",
+ "laravel/serializable-closure": "^1.3",
"phpunit/phpunit": "^9.3",
"spatie/phpunit-snapshot-assertions": "^4.2",
"symfony/var-dumper": "^5.1"
@@ -8081,7 +8119,7 @@
"spatie"
],
"support": {
- "source": "https://github.com/spatie/backtrace/tree/1.5.3"
+ "source": "https://github.com/spatie/backtrace/tree/1.6.1"
},
"funding": [
{
@@ -8093,7 +8131,7 @@
"type": "other"
}
],
- "time": "2023-06-28T12:59:17+00:00"
+ "time": "2024-04-24T13:22:11+00:00"
},
{
"name": "spatie/laravel-activitylog",
@@ -8188,20 +8226,20 @@
},
{
"name": "spatie/laravel-data",
- "version": "3.11.2",
+ "version": "3.12.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-data.git",
- "reference": "21b4d115a502dfd96ab2b11c62746325e9a28924"
+ "reference": "d44e04839407bc32b029be59ba80090a5f720e91"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/laravel-data/zipball/21b4d115a502dfd96ab2b11c62746325e9a28924",
- "reference": "21b4d115a502dfd96ab2b11c62746325e9a28924",
+ "url": "https://api.github.com/repos/spatie/laravel-data/zipball/d44e04839407bc32b029be59ba80090a5f720e91",
+ "reference": "d44e04839407bc32b029be59ba80090a5f720e91",
"shasum": ""
},
"require": {
- "illuminate/contracts": "^9.30|^10.0",
+ "illuminate/contracts": "^9.30|^10.0|^11.0",
"php": "^8.1",
"phpdocumentor/type-resolver": "^1.5",
"spatie/laravel-package-tools": "^1.9.0",
@@ -8261,7 +8299,7 @@
],
"support": {
"issues": "https://github.com/spatie/laravel-data/issues",
- "source": "https://github.com/spatie/laravel-data/tree/3.11.2"
+ "source": "https://github.com/spatie/laravel-data/tree/3.12.0"
},
"funding": [
{
@@ -8269,7 +8307,7 @@
"type": "github"
}
],
- "time": "2024-02-22T08:34:10+00:00"
+ "time": "2024-04-24T09:27:45+00:00"
},
{
"name": "spatie/laravel-package-tools",
@@ -8333,16 +8371,16 @@
},
{
"name": "spatie/laravel-ray",
- "version": "1.35.1",
+ "version": "1.36.2",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-ray.git",
- "reference": "f504d3787d88c7e5de7a4290658f7ad9b1352f22"
+ "reference": "1852faa96e5aa6778ea3401ec3176eee77268718"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/f504d3787d88c7e5de7a4290658f7ad9b1352f22",
- "reference": "f504d3787d88c7e5de7a4290658f7ad9b1352f22",
+ "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/1852faa96e5aa6778ea3401ec3176eee77268718",
+ "reference": "1852faa96e5aa6778ea3401ec3176eee77268718",
"shasum": ""
},
"require": {
@@ -8371,7 +8409,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "1.29.x-dev"
+ "dev-main": "1.x-dev"
},
"laravel": {
"providers": [
@@ -8404,7 +8442,7 @@
],
"support": {
"issues": "https://github.com/spatie/laravel-ray/issues",
- "source": "https://github.com/spatie/laravel-ray/tree/1.35.1"
+ "source": "https://github.com/spatie/laravel-ray/tree/1.36.2"
},
"funding": [
{
@@ -8416,7 +8454,7 @@
"type": "other"
}
],
- "time": "2024-02-13T14:19:41+00:00"
+ "time": "2024-05-02T08:26:02+00:00"
},
{
"name": "spatie/laravel-schemaless-attributes",
@@ -8626,16 +8664,16 @@
},
{
"name": "spatie/ray",
- "version": "1.41.1",
+ "version": "1.41.2",
"source": {
"type": "git",
"url": "https://github.com/spatie/ray.git",
- "reference": "051a0facb1d2462fafef87ff77eb74d6f2d12944"
+ "reference": "c44f8cfbf82c69909b505de61d8d3f2d324e93fc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/ray/zipball/051a0facb1d2462fafef87ff77eb74d6f2d12944",
- "reference": "051a0facb1d2462fafef87ff77eb74d6f2d12944",
+ "url": "https://api.github.com/repos/spatie/ray/zipball/c44f8cfbf82c69909b505de61d8d3f2d324e93fc",
+ "reference": "c44f8cfbf82c69909b505de61d8d3f2d324e93fc",
"shasum": ""
},
"require": {
@@ -8646,7 +8684,7 @@
"spatie/backtrace": "^1.1",
"spatie/macroable": "^1.0|^2.0",
"symfony/stopwatch": "^4.0|^5.1|^6.0|^7.0",
- "symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0"
+ "symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3"
},
"require-dev": {
"illuminate/support": "6.x|^8.18|^9.0",
@@ -8662,6 +8700,11 @@
"bin/remove-ray.sh"
],
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
"autoload": {
"files": [
"src/helpers.php"
@@ -8690,7 +8733,7 @@
],
"support": {
"issues": "https://github.com/spatie/ray/issues",
- "source": "https://github.com/spatie/ray/tree/1.41.1"
+ "source": "https://github.com/spatie/ray/tree/1.41.2"
},
"funding": [
{
@@ -8702,7 +8745,7 @@
"type": "other"
}
],
- "time": "2024-01-25T10:15:50+00:00"
+ "time": "2024-04-24T14:21:46+00:00"
},
{
"name": "spatie/url",
@@ -8827,16 +8870,16 @@
},
{
"name": "symfony/console",
- "version": "v6.4.4",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "0d9e4eb5ad413075624378f474c4167ea202de78"
+ "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/0d9e4eb5ad413075624378f474c4167ea202de78",
- "reference": "0d9e4eb5ad413075624378f474c4167ea202de78",
+ "url": "https://api.github.com/repos/symfony/console/zipball/a170e64ae10d00ba89e2acbb590dc2e54da8ad8f",
+ "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f",
"shasum": ""
},
"require": {
@@ -8901,7 +8944,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v6.4.4"
+ "source": "https://github.com/symfony/console/tree/v6.4.7"
},
"funding": [
{
@@ -8917,20 +8960,20 @@
"type": "tidelift"
}
],
- "time": "2024-02-22T20:27:10+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/css-selector",
- "version": "v7.0.3",
+ "version": "v7.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "ec60a4edf94e63b0556b6a0888548bb400a3a3be"
+ "reference": "b08a4ad89e84b29cec285b7b1f781a7ae51cf4bc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/ec60a4edf94e63b0556b6a0888548bb400a3a3be",
- "reference": "ec60a4edf94e63b0556b6a0888548bb400a3a3be",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/b08a4ad89e84b29cec285b7b1f781a7ae51cf4bc",
+ "reference": "b08a4ad89e84b29cec285b7b1f781a7ae51cf4bc",
"shasum": ""
},
"require": {
@@ -8966,7 +9009,7 @@
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/css-selector/tree/v7.0.3"
+ "source": "https://github.com/symfony/css-selector/tree/v7.0.7"
},
"funding": [
{
@@ -8982,20 +9025,20 @@
"type": "tidelift"
}
],
- "time": "2024-01-23T15:02:46+00:00"
+ "time": "2024-04-18T09:29:19+00:00"
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.4.0",
+ "version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
+ "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
- "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
+ "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
@@ -9004,7 +9047,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.4-dev"
+ "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -9033,7 +9076,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
},
"funding": [
{
@@ -9049,20 +9092,20 @@
"type": "tidelift"
}
],
- "time": "2023-05-23T14:45:45+00:00"
+ "time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/error-handler",
- "version": "v6.4.4",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "c725219bdf2afc59423c32793d5019d2a904e13a"
+ "reference": "667a072466c6a53827ed7b119af93806b884cbb3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/c725219bdf2afc59423c32793d5019d2a904e13a",
- "reference": "c725219bdf2afc59423c32793d5019d2a904e13a",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/667a072466c6a53827ed7b119af93806b884cbb3",
+ "reference": "667a072466c6a53827ed7b119af93806b884cbb3",
"shasum": ""
},
"require": {
@@ -9108,7 +9151,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/error-handler/tree/v6.4.4"
+ "source": "https://github.com/symfony/error-handler/tree/v6.4.7"
},
"funding": [
{
@@ -9124,20 +9167,20 @@
"type": "tidelift"
}
],
- "time": "2024-02-22T20:27:10+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v7.0.3",
+ "version": "v7.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e"
+ "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/834c28d533dd0636f910909d01b9ff45cc094b5e",
- "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/db2a7fab994d67d92356bb39c367db115d9d30f9",
+ "reference": "db2a7fab994d67d92356bb39c367db115d9d30f9",
"shasum": ""
},
"require": {
@@ -9188,7 +9231,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.3"
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.7"
},
"funding": [
{
@@ -9204,20 +9247,20 @@
"type": "tidelift"
}
],
- "time": "2024-01-23T15:02:46+00:00"
+ "time": "2024-04-18T09:29:19+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
- "version": "v3.4.0",
+ "version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
- "reference": "a76aed96a42d2b521153fb382d418e30d18b59df"
+ "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df",
- "reference": "a76aed96a42d2b521153fb382d418e30d18b59df",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50",
+ "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50",
"shasum": ""
},
"require": {
@@ -9227,7 +9270,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.4-dev"
+ "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -9264,7 +9307,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0"
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0"
},
"funding": [
{
@@ -9280,20 +9323,20 @@
"type": "tidelift"
}
],
- "time": "2023-05-23T14:45:45+00:00"
+ "time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/finder",
- "version": "v6.4.0",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "11d736e97f116ac375a81f96e662911a34cd50ce"
+ "reference": "511c48990be17358c23bf45c5d71ab85d40fb764"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/11d736e97f116ac375a81f96e662911a34cd50ce",
- "reference": "11d736e97f116ac375a81f96e662911a34cd50ce",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/511c48990be17358c23bf45c5d71ab85d40fb764",
+ "reference": "511c48990be17358c23bf45c5d71ab85d40fb764",
"shasum": ""
},
"require": {
@@ -9328,7 +9371,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v6.4.0"
+ "source": "https://github.com/symfony/finder/tree/v6.4.7"
},
"funding": [
{
@@ -9344,27 +9387,27 @@
"type": "tidelift"
}
],
- "time": "2023-10-31T17:30:12+00:00"
+ "time": "2024-04-23T10:36:43+00:00"
},
{
"name": "symfony/http-client",
- "version": "v6.4.5",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
- "reference": "f3c86a60a3615f466333a11fd42010d4382a82c7"
+ "reference": "3683d8107cf1efdd24795cc5f7482be1eded34ac"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client/zipball/f3c86a60a3615f466333a11fd42010d4382a82c7",
- "reference": "f3c86a60a3615f466333a11fd42010d4382a82c7",
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/3683d8107cf1efdd24795cc5f7482be1eded34ac",
+ "reference": "3683d8107cf1efdd24795cc5f7482be1eded34ac",
"shasum": ""
},
"require": {
"php": ">=8.1",
"psr/log": "^1|^2|^3",
"symfony/deprecation-contracts": "^2.5|^3",
- "symfony/http-client-contracts": "^3",
+ "symfony/http-client-contracts": "^3.4.1",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
@@ -9382,7 +9425,7 @@
"amphp/http-client": "^4.2.1",
"amphp/http-tunnel": "^1.0",
"amphp/socket": "^1.1",
- "guzzlehttp/promises": "^1.4",
+ "guzzlehttp/promises": "^1.4|^2.0",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"psr/http-client": "^1.0",
@@ -9421,7 +9464,7 @@
"http"
],
"support": {
- "source": "https://github.com/symfony/http-client/tree/v6.4.5"
+ "source": "https://github.com/symfony/http-client/tree/v6.4.7"
},
"funding": [
{
@@ -9437,20 +9480,20 @@
"type": "tidelift"
}
],
- "time": "2024-03-02T12:45:30+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/http-client-contracts",
- "version": "v3.4.0",
+ "version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
- "reference": "1ee70e699b41909c209a0c930f11034b93578654"
+ "reference": "20414d96f391677bf80078aa55baece78b82647d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1ee70e699b41909c209a0c930f11034b93578654",
- "reference": "1ee70e699b41909c209a0c930f11034b93578654",
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d",
+ "reference": "20414d96f391677bf80078aa55baece78b82647d",
"shasum": ""
},
"require": {
@@ -9459,7 +9502,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.4-dev"
+ "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -9499,7 +9542,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/http-client-contracts/tree/v3.4.0"
+ "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0"
},
"funding": [
{
@@ -9515,20 +9558,20 @@
"type": "tidelift"
}
],
- "time": "2023-07-30T20:28:31+00:00"
+ "time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/http-foundation",
- "version": "v6.4.4",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "ebc713bc6e6f4b53f46539fc158be85dfcd77304"
+ "reference": "b4db6b833035477cb70e18d0ae33cb7c2b521759"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ebc713bc6e6f4b53f46539fc158be85dfcd77304",
- "reference": "ebc713bc6e6f4b53f46539fc158be85dfcd77304",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b4db6b833035477cb70e18d0ae33cb7c2b521759",
+ "reference": "b4db6b833035477cb70e18d0ae33cb7c2b521759",
"shasum": ""
},
"require": {
@@ -9576,7 +9619,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-foundation/tree/v6.4.4"
+ "source": "https://github.com/symfony/http-foundation/tree/v6.4.7"
},
"funding": [
{
@@ -9592,20 +9635,20 @@
"type": "tidelift"
}
],
- "time": "2024-02-08T15:01:18+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v6.4.5",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "f6947cb939d8efee137797382cb4db1af653ef75"
+ "reference": "b7b5e6cdef670a0c82d015a966ffc7e855861a98"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f6947cb939d8efee137797382cb4db1af653ef75",
- "reference": "f6947cb939d8efee137797382cb4db1af653ef75",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b7b5e6cdef670a0c82d015a966ffc7e855861a98",
+ "reference": "b7b5e6cdef670a0c82d015a966ffc7e855861a98",
"shasum": ""
},
"require": {
@@ -9660,6 +9703,7 @@
"symfony/translation-contracts": "^2.5|^3",
"symfony/uid": "^5.4|^6.0|^7.0",
"symfony/validator": "^6.4|^7.0",
+ "symfony/var-dumper": "^5.4|^6.4|^7.0",
"symfony/var-exporter": "^6.2|^7.0",
"twig/twig": "^2.13|^3.0.4"
},
@@ -9689,7 +9733,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-kernel/tree/v6.4.5"
+ "source": "https://github.com/symfony/http-kernel/tree/v6.4.7"
},
"funding": [
{
@@ -9705,20 +9749,20 @@
"type": "tidelift"
}
],
- "time": "2024-03-04T21:00:47+00:00"
+ "time": "2024-04-29T11:24:44+00:00"
},
{
"name": "symfony/mailer",
- "version": "v6.4.4",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailer.git",
- "reference": "791c5d31a8204cf3db0c66faab70282307f4376b"
+ "reference": "2c446d4e446995bed983c0b5bb9ff837e8de7dbd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mailer/zipball/791c5d31a8204cf3db0c66faab70282307f4376b",
- "reference": "791c5d31a8204cf3db0c66faab70282307f4376b",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/2c446d4e446995bed983c0b5bb9ff837e8de7dbd",
+ "reference": "2c446d4e446995bed983c0b5bb9ff837e8de7dbd",
"shasum": ""
},
"require": {
@@ -9769,7 +9813,7 @@
"description": "Helps sending emails",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/mailer/tree/v6.4.4"
+ "source": "https://github.com/symfony/mailer/tree/v6.4.7"
},
"funding": [
{
@@ -9785,20 +9829,20 @@
"type": "tidelift"
}
],
- "time": "2024-02-03T21:33:47+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/mime",
- "version": "v6.4.3",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
- "reference": "5017e0a9398c77090b7694be46f20eb796262a34"
+ "reference": "decadcf3865918ecfcbfa90968553994ce935a5e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mime/zipball/5017e0a9398c77090b7694be46f20eb796262a34",
- "reference": "5017e0a9398c77090b7694be46f20eb796262a34",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/decadcf3865918ecfcbfa90968553994ce935a5e",
+ "reference": "decadcf3865918ecfcbfa90968553994ce935a5e",
"shasum": ""
},
"require": {
@@ -9819,6 +9863,7 @@
"league/html-to-markdown": "^5.0",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
+ "symfony/process": "^5.4|^6.4|^7.0",
"symfony/property-access": "^5.4|^6.0|^7.0",
"symfony/property-info": "^5.4|^6.0|^7.0",
"symfony/serializer": "^6.3.2|^7.0"
@@ -9853,7 +9898,7 @@
"mime-type"
],
"support": {
- "source": "https://github.com/symfony/mime/tree/v6.4.3"
+ "source": "https://github.com/symfony/mime/tree/v6.4.7"
},
"funding": [
{
@@ -9869,20 +9914,20 @@
"type": "tidelift"
}
],
- "time": "2024-01-30T08:32:12+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/options-resolver",
- "version": "v7.0.0",
+ "version": "v7.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
- "reference": "700ff4096e346f54cb628ea650767c8130f1001f"
+ "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/options-resolver/zipball/700ff4096e346f54cb628ea650767c8130f1001f",
- "reference": "700ff4096e346f54cb628ea650767c8130f1001f",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/23cc173858776ad451e31f053b1c9f47840b2cfa",
+ "reference": "23cc173858776ad451e31f053b1c9f47840b2cfa",
"shasum": ""
},
"require": {
@@ -9920,7 +9965,7 @@
"options"
],
"support": {
- "source": "https://github.com/symfony/options-resolver/tree/v7.0.0"
+ "source": "https://github.com/symfony/options-resolver/tree/v7.0.7"
},
"funding": [
{
@@ -9936,7 +9981,7 @@
"type": "tidelift"
}
],
- "time": "2023-08-08T10:20:21+00:00"
+ "time": "2024-04-18T09:29:19+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -10731,16 +10776,16 @@
},
{
"name": "symfony/process",
- "version": "v6.4.4",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "710e27879e9be3395de2b98da3f52a946039f297"
+ "reference": "cdb1c81c145fd5aa9b0038bab694035020943381"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/710e27879e9be3395de2b98da3f52a946039f297",
- "reference": "710e27879e9be3395de2b98da3f52a946039f297",
+ "url": "https://api.github.com/repos/symfony/process/zipball/cdb1c81c145fd5aa9b0038bab694035020943381",
+ "reference": "cdb1c81c145fd5aa9b0038bab694035020943381",
"shasum": ""
},
"require": {
@@ -10772,7 +10817,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/process/tree/v6.4.4"
+ "source": "https://github.com/symfony/process/tree/v6.4.7"
},
"funding": [
{
@@ -10788,7 +10833,7 @@
"type": "tidelift"
}
],
- "time": "2024-02-20T12:31:00+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/psr-http-message-bridge",
@@ -10881,16 +10926,16 @@
},
{
"name": "symfony/routing",
- "version": "v6.4.5",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
- "reference": "7fe30068e207d9c31c0138501ab40358eb2d49a4"
+ "reference": "276e06398f71fa2a973264d94f28150f93cfb907"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/routing/zipball/7fe30068e207d9c31c0138501ab40358eb2d49a4",
- "reference": "7fe30068e207d9c31c0138501ab40358eb2d49a4",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/276e06398f71fa2a973264d94f28150f93cfb907",
+ "reference": "276e06398f71fa2a973264d94f28150f93cfb907",
"shasum": ""
},
"require": {
@@ -10944,7 +10989,7 @@
"url"
],
"support": {
- "source": "https://github.com/symfony/routing/tree/v6.4.5"
+ "source": "https://github.com/symfony/routing/tree/v6.4.7"
},
"funding": [
{
@@ -10960,25 +11005,26 @@
"type": "tidelift"
}
],
- "time": "2024-02-27T12:33:30+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v3.4.1",
+ "version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0"
+ "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0",
- "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
+ "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
"shasum": ""
},
"require": {
"php": ">=8.1",
- "psr/container": "^1.1|^2.0"
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
},
"conflict": {
"ext-psr": "<1.1|>=2"
@@ -10986,7 +11032,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.4-dev"
+ "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -11026,7 +11072,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v3.4.1"
+ "source": "https://github.com/symfony/service-contracts/tree/v3.5.0"
},
"funding": [
{
@@ -11042,20 +11088,20 @@
"type": "tidelift"
}
],
- "time": "2023-12-26T14:02:43+00:00"
+ "time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/stopwatch",
- "version": "v7.0.3",
+ "version": "v7.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
- "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112"
+ "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/stopwatch/zipball/983900d6fddf2b0cbaacacbbad07610854bd8112",
- "reference": "983900d6fddf2b0cbaacacbbad07610854bd8112",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/41a7a24aa1dc82adf46a06bc292d1923acfe6b84",
+ "reference": "41a7a24aa1dc82adf46a06bc292d1923acfe6b84",
"shasum": ""
},
"require": {
@@ -11088,7 +11134,7 @@
"description": "Provides a way to profile code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/stopwatch/tree/v7.0.3"
+ "source": "https://github.com/symfony/stopwatch/tree/v7.0.7"
},
"funding": [
{
@@ -11104,20 +11150,20 @@
"type": "tidelift"
}
],
- "time": "2024-01-23T15:02:46+00:00"
+ "time": "2024-04-18T09:29:19+00:00"
},
{
"name": "symfony/string",
- "version": "v7.0.4",
+ "version": "v7.0.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b"
+ "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b",
- "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b",
+ "url": "https://api.github.com/repos/symfony/string/zipball/e405b5424dc2528e02e31ba26b83a79fd4eb8f63",
+ "reference": "e405b5424dc2528e02e31ba26b83a79fd4eb8f63",
"shasum": ""
},
"require": {
@@ -11174,7 +11220,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v7.0.4"
+ "source": "https://github.com/symfony/string/tree/v7.0.7"
},
"funding": [
{
@@ -11190,20 +11236,20 @@
"type": "tidelift"
}
],
- "time": "2024-02-01T13:17:36+00:00"
+ "time": "2024-04-18T09:29:19+00:00"
},
{
"name": "symfony/translation",
- "version": "v6.4.4",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "bce6a5a78e94566641b2594d17e48b0da3184a8e"
+ "reference": "7495687c58bfd88b7883823747b0656d90679123"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/bce6a5a78e94566641b2594d17e48b0da3184a8e",
- "reference": "bce6a5a78e94566641b2594d17e48b0da3184a8e",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/7495687c58bfd88b7883823747b0656d90679123",
+ "reference": "7495687c58bfd88b7883823747b0656d90679123",
"shasum": ""
},
"require": {
@@ -11269,7 +11315,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/translation/tree/v6.4.4"
+ "source": "https://github.com/symfony/translation/tree/v6.4.7"
},
"funding": [
{
@@ -11285,20 +11331,20 @@
"type": "tidelift"
}
],
- "time": "2024-02-20T13:16:58+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/translation-contracts",
- "version": "v3.4.1",
+ "version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation-contracts.git",
- "reference": "06450585bf65e978026bda220cdebca3f867fde7"
+ "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7",
- "reference": "06450585bf65e978026bda220cdebca3f867fde7",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a",
+ "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a",
"shasum": ""
},
"require": {
@@ -11307,7 +11353,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.4-dev"
+ "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -11347,7 +11393,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1"
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0"
},
"funding": [
{
@@ -11363,20 +11409,20 @@
"type": "tidelift"
}
],
- "time": "2023-12-26T14:02:43+00:00"
+ "time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/uid",
- "version": "v6.4.3",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/uid.git",
- "reference": "1d31267211cc3a2fff32bcfc7c1818dac41b6fc0"
+ "reference": "a66efcb71d8bc3a207d9d78e0bd67f3321510355"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/uid/zipball/1d31267211cc3a2fff32bcfc7c1818dac41b6fc0",
- "reference": "1d31267211cc3a2fff32bcfc7c1818dac41b6fc0",
+ "url": "https://api.github.com/repos/symfony/uid/zipball/a66efcb71d8bc3a207d9d78e0bd67f3321510355",
+ "reference": "a66efcb71d8bc3a207d9d78e0bd67f3321510355",
"shasum": ""
},
"require": {
@@ -11421,7 +11467,7 @@
"uuid"
],
"support": {
- "source": "https://github.com/symfony/uid/tree/v6.4.3"
+ "source": "https://github.com/symfony/uid/tree/v6.4.7"
},
"funding": [
{
@@ -11437,20 +11483,20 @@
"type": "tidelift"
}
],
- "time": "2024-01-23T14:51:35+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/var-dumper",
- "version": "v6.4.4",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "b439823f04c98b84d4366c79507e9da6230944b1"
+ "reference": "7a9cd977cd1c5fed3694bee52990866432af07d7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b439823f04c98b84d4366c79507e9da6230944b1",
- "reference": "b439823f04c98b84d4366c79507e9da6230944b1",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7a9cd977cd1c5fed3694bee52990866432af07d7",
+ "reference": "7a9cd977cd1c5fed3694bee52990866432af07d7",
"shasum": ""
},
"require": {
@@ -11506,7 +11552,7 @@
"dump"
],
"support": {
- "source": "https://github.com/symfony/var-dumper/tree/v6.4.4"
+ "source": "https://github.com/symfony/var-dumper/tree/v6.4.7"
},
"funding": [
{
@@ -11522,20 +11568,20 @@
"type": "tidelift"
}
],
- "time": "2024-02-15T11:23:52+00:00"
+ "time": "2024-04-18T09:22:46+00:00"
},
{
"name": "symfony/yaml",
- "version": "v6.4.3",
+ "version": "v6.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "d75715985f0f94f978e3a8fa42533e10db921b90"
+ "reference": "53e8b1ef30a65f78eac60fddc5ee7ebbbdb1dee0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/d75715985f0f94f978e3a8fa42533e10db921b90",
- "reference": "d75715985f0f94f978e3a8fa42533e10db921b90",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/53e8b1ef30a65f78eac60fddc5ee7ebbbdb1dee0",
+ "reference": "53e8b1ef30a65f78eac60fddc5ee7ebbbdb1dee0",
"shasum": ""
},
"require": {
@@ -11578,7 +11624,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/yaml/tree/v6.4.3"
+ "source": "https://github.com/symfony/yaml/tree/v6.4.7"
},
"funding": [
{
@@ -11594,7 +11640,7 @@
"type": "tidelift"
}
],
- "time": "2024-01-23T14:51:35+00:00"
+ "time": "2024-04-28T10:28:08+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@@ -11706,7 +11752,6 @@
"issues": "https://github.com/visus-io/php-cuid2/issues",
"source": "https://github.com/visus-io/php-cuid2/tree/2.0.0"
},
- "abandoned": true,
"time": "2023-03-23T19:18:36+00:00"
},
{
@@ -12037,16 +12082,16 @@
},
{
"name": "zbateson/mail-mime-parser",
- "version": "2.4.0",
+ "version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/zbateson/mail-mime-parser.git",
- "reference": "20b3e48eb799537683780bc8782fbbe9bc25934a"
+ "reference": "ff49e02f6489b38f7cc3d1bd3971adc0f872569c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zbateson/mail-mime-parser/zipball/20b3e48eb799537683780bc8782fbbe9bc25934a",
- "reference": "20b3e48eb799537683780bc8782fbbe9bc25934a",
+ "url": "https://api.github.com/repos/zbateson/mail-mime-parser/zipball/ff49e02f6489b38f7cc3d1bd3971adc0f872569c",
+ "reference": "ff49e02f6489b38f7cc3d1bd3971adc0f872569c",
"shasum": ""
},
"require": {
@@ -12108,7 +12153,7 @@
"type": "github"
}
],
- "time": "2023-02-14T22:58:03+00:00"
+ "time": "2024-04-28T00:58:54+00:00"
},
{
"name": "zbateson/mb-wrapper",
@@ -12662,16 +12707,16 @@
},
{
"name": "laravel/pint",
- "version": "v1.14.0",
+ "version": "v1.16.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
- "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e"
+ "reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/pint/zipball/6b127276e3f263f7bb17d5077e9e0269e61b2a0e",
- "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e",
+ "url": "https://api.github.com/repos/laravel/pint/zipball/1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98",
+ "reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98",
"shasum": ""
},
"require": {
@@ -12682,13 +12727,13 @@
"php": "^8.1.0"
},
"require-dev": {
- "friendsofphp/php-cs-fixer": "^3.49.0",
- "illuminate/view": "^10.43.0",
- "larastan/larastan": "^2.8.1",
- "laravel-zero/framework": "^10.3.0",
- "mockery/mockery": "^1.6.7",
+ "friendsofphp/php-cs-fixer": "^3.57.1",
+ "illuminate/view": "^10.48.10",
+ "larastan/larastan": "^2.9.6",
+ "laravel-zero/framework": "^10.4.0",
+ "mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^1.15.1",
- "pestphp/pest": "^2.33.6"
+ "pestphp/pest": "^2.34.7"
},
"bin": [
"builds/pint"
@@ -12724,20 +12769,20 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
- "time": "2024-02-20T17:38:05+00:00"
+ "time": "2024-05-21T18:08:25+00:00"
},
{
"name": "mockery/mockery",
- "version": "1.6.11",
+ "version": "1.6.12",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
- "reference": "81a161d0b135df89951abd52296adf97deb0723d"
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/mockery/mockery/zipball/81a161d0b135df89951abd52296adf97deb0723d",
- "reference": "81a161d0b135df89951abd52296adf97deb0723d",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
"shasum": ""
},
"require": {
@@ -12807,7 +12852,7 @@
"security": "https://github.com/mockery/mockery/security/advisories",
"source": "https://github.com/mockery/mockery"
},
- "time": "2024-03-21T18:34:15+00:00"
+ "time": "2024-05-16T03:13:13+00:00"
},
{
"name": "myclabs/deep-copy",
@@ -12966,16 +13011,16 @@
},
{
"name": "pestphp/pest",
- "version": "v2.34.5",
+ "version": "v2.34.7",
"source": {
"type": "git",
"url": "https://github.com/pestphp/pest.git",
- "reference": "863a0cc83744c677ffdb28a6a2b841dd049e57ce"
+ "reference": "a7a3e4240e341d0fee1c54814ce18adc26ce5a76"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/pestphp/pest/zipball/863a0cc83744c677ffdb28a6a2b841dd049e57ce",
- "reference": "863a0cc83744c677ffdb28a6a2b841dd049e57ce",
+ "url": "https://api.github.com/repos/pestphp/pest/zipball/a7a3e4240e341d0fee1c54814ce18adc26ce5a76",
+ "reference": "a7a3e4240e341d0fee1c54814ce18adc26ce5a76",
"shasum": ""
},
"require": {
@@ -12985,10 +13030,10 @@
"pestphp/pest-plugin": "^2.1.1",
"pestphp/pest-plugin-arch": "^2.7.0",
"php": "^8.1.0",
- "phpunit/phpunit": "^10.5.15"
+ "phpunit/phpunit": "^10.5.17"
},
"conflict": {
- "phpunit/phpunit": ">10.5.15",
+ "phpunit/phpunit": ">10.5.17",
"sebastian/exporter": "<5.1.0",
"webmozart/assert": "<1.11.0"
},
@@ -13058,7 +13103,7 @@
],
"support": {
"issues": "https://github.com/pestphp/pest/issues",
- "source": "https://github.com/pestphp/pest/tree/v2.34.5"
+ "source": "https://github.com/pestphp/pest/tree/v2.34.7"
},
"funding": [
{
@@ -13070,7 +13115,7 @@
"type": "github"
}
],
- "time": "2024-03-22T08:44:19+00:00"
+ "time": "2024-04-05T07:44:17+00:00"
},
{
"name": "pestphp/pest-plugin",
@@ -13399,28 +13444,35 @@
},
{
"name": "phpdocumentor/reflection-docblock",
- "version": "5.3.0",
+ "version": "5.4.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
+ "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
- "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
+ "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c",
"shasum": ""
},
"require": {
+ "doctrine/deprecations": "^1.1",
"ext-filter": "*",
- "php": "^7.2 || ^8.0",
+ "php": "^7.4 || ^8.0",
"phpdocumentor/reflection-common": "^2.2",
- "phpdocumentor/type-resolver": "^1.3",
+ "phpdocumentor/type-resolver": "^1.7",
+ "phpstan/phpdoc-parser": "^1.7",
"webmozart/assert": "^1.9.1"
},
"require-dev": {
- "mockery/mockery": "~1.3.2",
- "psalm/phar": "^4.8"
+ "mockery/mockery": "~1.3.5",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-webmozart-assert": "^1.2",
+ "phpunit/phpunit": "^9.5",
+ "vimeo/psalm": "^5.13"
},
"type": "library",
"extra": {
@@ -13444,15 +13496,15 @@
},
{
"name": "Jaap van Otterdijk",
- "email": "account@ijaap.nl"
+ "email": "opensource@ijaap.nl"
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
- "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1"
},
- "time": "2021-10-19T17:43:47+00:00"
+ "time": "2024-05-21T05:55:05+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -13777,16 +13829,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "10.5.15",
+ "version": "10.5.17",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "86376e05e8745ed81d88232ff92fee868247b07b"
+ "reference": "c1f736a473d21957ead7e94fcc029f571895abf5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86376e05e8745ed81d88232ff92fee868247b07b",
- "reference": "86376e05e8745ed81d88232ff92fee868247b07b",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c1f736a473d21957ead7e94fcc029f571895abf5",
+ "reference": "c1f736a473d21957ead7e94fcc029f571895abf5",
"shasum": ""
},
"require": {
@@ -13858,7 +13910,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.15"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.17"
},
"funding": [
{
@@ -13874,7 +13926,7 @@
"type": "tidelift"
}
],
- "time": "2024-03-22T04:17:47+00:00"
+ "time": "2024-04-05T04:39:01+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -14839,16 +14891,16 @@
},
{
"name": "spatie/flare-client-php",
- "version": "1.4.4",
+ "version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/flare-client-php.git",
- "reference": "17082e780752d346c2db12ef5d6bee8e835e399c"
+ "reference": "220a7c8745e9fa427d54099f47147c4b97fe6462"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/17082e780752d346c2db12ef5d6bee8e835e399c",
- "reference": "17082e780752d346c2db12ef5d6bee8e835e399c",
+ "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/220a7c8745e9fa427d54099f47147c4b97fe6462",
+ "reference": "220a7c8745e9fa427d54099f47147c4b97fe6462",
"shasum": ""
},
"require": {
@@ -14896,7 +14948,7 @@
],
"support": {
"issues": "https://github.com/spatie/flare-client-php/issues",
- "source": "https://github.com/spatie/flare-client-php/tree/1.4.4"
+ "source": "https://github.com/spatie/flare-client-php/tree/1.6.0"
},
"funding": [
{
@@ -14904,20 +14956,20 @@
"type": "github"
}
],
- "time": "2024-01-31T14:18:45+00:00"
+ "time": "2024-05-22T09:45:39+00:00"
},
{
"name": "spatie/ignition",
- "version": "1.12.0",
+ "version": "1.14.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/ignition.git",
- "reference": "5b6f801c605a593106b623e45ca41496a6e7d56d"
+ "reference": "c23cc018c5f423d2f413b99f84655fceb6549811"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/ignition/zipball/5b6f801c605a593106b623e45ca41496a6e7d56d",
- "reference": "5b6f801c605a593106b623e45ca41496a6e7d56d",
+ "url": "https://api.github.com/repos/spatie/ignition/zipball/c23cc018c5f423d2f413b99f84655fceb6549811",
+ "reference": "c23cc018c5f423d2f413b99f84655fceb6549811",
"shasum": ""
},
"require": {
@@ -14987,20 +15039,20 @@
"type": "github"
}
],
- "time": "2024-01-03T15:49:39+00:00"
+ "time": "2024-05-03T15:56:16+00:00"
},
{
"name": "spatie/laravel-ignition",
- "version": "2.4.2",
+ "version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-ignition.git",
- "reference": "351504f4570e32908839fc5a2dc53bf77d02f85e"
+ "reference": "f52124d50122611e8a40f628cef5c19ff6cc5b57"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/351504f4570e32908839fc5a2dc53bf77d02f85e",
- "reference": "351504f4570e32908839fc5a2dc53bf77d02f85e",
+ "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/f52124d50122611e8a40f628cef5c19ff6cc5b57",
+ "reference": "f52124d50122611e8a40f628cef5c19ff6cc5b57",
"shasum": ""
},
"require": {
@@ -15009,8 +15061,8 @@
"ext-mbstring": "*",
"illuminate/support": "^10.0|^11.0",
"php": "^8.1",
- "spatie/flare-client-php": "^1.3.5",
- "spatie/ignition": "^1.9",
+ "spatie/flare-client-php": "^1.5",
+ "spatie/ignition": "^1.14",
"symfony/console": "^6.2.3|^7.0",
"symfony/var-dumper": "^6.2.3|^7.0"
},
@@ -15018,11 +15070,11 @@
"livewire/livewire": "^2.11|^3.3.5",
"mockery/mockery": "^1.5.1",
"openai-php/client": "^0.8.1",
- "orchestra/testbench": "^8.0|^9.0",
- "pestphp/pest": "^2.30",
- "phpstan/extension-installer": "^1.2",
+ "orchestra/testbench": "8.22.3|^9.0",
+ "pestphp/pest": "^2.34",
+ "phpstan/extension-installer": "^1.3.1",
"phpstan/phpstan-deprecation-rules": "^1.1.1",
- "phpstan/phpstan-phpunit": "^1.3.3",
+ "phpstan/phpstan-phpunit": "^1.3.16",
"vlucas/phpdotenv": "^5.5"
},
"suggest": {
@@ -15079,7 +15131,7 @@
"type": "github"
}
],
- "time": "2024-02-09T16:08:40+00:00"
+ "time": "2024-05-02T13:42:49+00:00"
},
{
"name": "ta-tikoma/phpunit-architecture-test",
diff --git a/config/constants.php b/config/constants.php
index 53f43ae5a..51bc63b7b 100644
--- a/config/constants.php
+++ b/config/constants.php
@@ -21,8 +21,8 @@ return [
],
'services' => [
// Temporary disabled until cache is implemented
- // 'official' => 'https://cdn.coollabs.io/coolify/service-templates.json',
- 'official' => 'https://raw.githubusercontent.com/coollabsio/coolify/main/templates/service-templates.json',
+ 'official' => 'https://cdn.coollabs.io/coolify/service-templates.json',
+ // 'official' => 'https://raw.githubusercontent.com/coollabsio/coolify/main/templates/service-templates.json',
],
'limits' => [
'trial_period' => 0,
diff --git a/config/sentry.php b/config/sentry.php
index 693c33c3d..ad068446a 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -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.285',
+ 'release' => '4.0.0-beta.294',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),
diff --git a/config/version.php b/config/version.php
index e09c4fd6a..c57853f01 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
text('post_deployment_command')->nullable()->change();
+ $table->text('pre_deployment_command')->nullable()->change();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('applications', function (Blueprint $table) {
+ $table->string('post_deployment_command')->nullable()->change();
+ $table->string('pre_deployment_command')->nullable()->change();
+ });
+ }
+};
diff --git a/database/migrations/2024_05_23_091713_add_gitea_webhook_to_applications.php b/database/migrations/2024_05_23_091713_add_gitea_webhook_to_applications.php
new file mode 100644
index 000000000..716f1f44c
--- /dev/null
+++ b/database/migrations/2024_05_23_091713_add_gitea_webhook_to_applications.php
@@ -0,0 +1,29 @@
+string('manual_webhook_secret_gitea')->nullable();
+
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('applications', function (Blueprint $table) {
+ $table->dropColumn('manual_webhook_secret_gitea');
+ });
+ }
+};
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 91e90b989..7eda14d41 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -2,7 +2,7 @@ services:
coolify:
build:
context: .
- dockerfile: ./docker/dev-ssu/Dockerfile
+ dockerfile: ./docker/dev/Dockerfile
ports:
- "${APP_PORT:-8000}:80"
environment:
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index f3dda9748..357138ca1 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -13,6 +13,7 @@ services:
- /data/coolify/backups:/var/www/html/storage/app/backups
- /data/coolify/webhooks-during-maintenance:/var/www/html/storage/app/webhooks-during-maintenance
environment:
+ - PHP_MEMORY_LIMIT
- APP_ID
- APP_ENV=production
- APP_DEBUG
diff --git a/docker/dev-ssu/Dockerfile b/docker/dev/Dockerfile
similarity index 93%
rename from docker/dev-ssu/Dockerfile
rename to docker/dev/Dockerfile
index f0e353d28..f75a0ff1e 100644
--- a/docker/dev-ssu/Dockerfile
+++ b/docker/dev/Dockerfile
@@ -18,9 +18,9 @@ RUN apt-get install postgresql-client-$POSTGRES_VERSION -y
# Coolify requirements
RUN apt-get install -y php8.2-pgsql openssh-client git git-lfs jq lsof
RUN apt-get -y autoremove && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
-COPY --chmod=755 docker/dev-ssu/etc/s6-overlay/ /etc/s6-overlay/
+COPY --chmod=755 docker/dev/etc/s6-overlay/ /etc/s6-overlay/
-COPY docker/dev-ssu/nginx.conf /etc/nginx/conf.d/custom.conf
+COPY docker/dev/nginx.conf /etc/nginx/conf.d/custom.conf
RUN echo "alias ll='ls -al'" >>/etc/bash.bashrc
RUN echo "alias a='php artisan'" >>/etc/bash.bashrc
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/horizon/dependencies.d/init-setup b/docker/dev/etc/s6-overlay/s6-rc.d/horizon/dependencies.d/init-setup
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/horizon/dependencies.d/init-setup
rename to docker/dev/etc/s6-overlay/s6-rc.d/horizon/dependencies.d/init-setup
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/horizon/run b/docker/dev/etc/s6-overlay/s6-rc.d/horizon/run
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/horizon/run
rename to docker/dev/etc/s6-overlay/s6-rc.d/horizon/run
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/horizon/type b/docker/dev/etc/s6-overlay/s6-rc.d/horizon/type
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/horizon/type
rename to docker/dev/etc/s6-overlay/s6-rc.d/horizon/type
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/type b/docker/dev/etc/s6-overlay/s6-rc.d/init-setup/type
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/type
rename to docker/dev/etc/s6-overlay/s6-rc.d/init-setup/type
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/up b/docker/dev/etc/s6-overlay/s6-rc.d/init-setup/up
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/up
rename to docker/dev/etc/s6-overlay/s6-rc.d/init-setup/up
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/dependencies.d/init-setup b/docker/dev/etc/s6-overlay/s6-rc.d/scheduler-worker/dependencies.d/init-setup
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/dependencies.d/init-setup
rename to docker/dev/etc/s6-overlay/s6-rc.d/scheduler-worker/dependencies.d/init-setup
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/run b/docker/dev/etc/s6-overlay/s6-rc.d/scheduler-worker/run
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/run
rename to docker/dev/etc/s6-overlay/s6-rc.d/scheduler-worker/run
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/type b/docker/dev/etc/s6-overlay/s6-rc.d/scheduler-worker/type
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/type
rename to docker/dev/etc/s6-overlay/s6-rc.d/scheduler-worker/type
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/horizon b/docker/dev/etc/s6-overlay/s6-rc.d/user/contents.d/horizon
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/horizon
rename to docker/dev/etc/s6-overlay/s6-rc.d/user/contents.d/horizon
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/init-setup b/docker/dev/etc/s6-overlay/s6-rc.d/user/contents.d/init-setup
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/init-setup
rename to docker/dev/etc/s6-overlay/s6-rc.d/user/contents.d/init-setup
diff --git a/docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker b/docker/dev/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker
similarity index 100%
rename from docker/dev-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker
rename to docker/dev/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker
diff --git a/docker/dev-ssu/nginx.conf b/docker/dev/nginx.conf
similarity index 100%
rename from docker/dev-ssu/nginx.conf
rename to docker/dev/nginx.conf
diff --git a/docker/prod-ssu/Dockerfile b/docker/prod/Dockerfile
similarity index 94%
rename from docker/prod-ssu/Dockerfile
rename to docker/prod/Dockerfile
index 2192f4f0e..4ee3fade2 100644
--- a/docker/prod-ssu/Dockerfile
+++ b/docker/prod/Dockerfile
@@ -31,10 +31,10 @@ RUN apt-get update
RUN apt-get install postgresql-client-$POSTGRES_VERSION -y
# Coolify requirements
-RUN apt-get install -y php8.2-pgsql openssh-client git git-lfs jq lsof
+RUN apt-get install -y php8.2-pgsql openssh-client git git-lfs jq lsof vim
RUN apt-get -y autoremove && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
-COPY docker/prod-ssu/nginx.conf /etc/nginx/conf.d/custom.conf
+COPY docker/prod/nginx.conf /etc/nginx/conf.d/custom.conf
COPY --from=base --chown=9999:9999 /var/www/html .
@@ -42,7 +42,7 @@ COPY --chown=9999:9999 . .
RUN composer dump-autoload
COPY --from=static-assets --chown=9999:9999 /app/public/build ./public/build
-COPY --chmod=755 docker/prod-ssu/etc/s6-overlay/ /etc/s6-overlay/
+COPY --chmod=755 docker/prod/etc/s6-overlay/ /etc/s6-overlay/
RUN php artisan route:cache
RUN php artisan view:cache
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/db-migration/type b/docker/prod/etc/s6-overlay/s6-rc.d/db-migration/type
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/db-migration/type
rename to docker/prod/etc/s6-overlay/s6-rc.d/db-migration/type
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/db-migration/up b/docker/prod/etc/s6-overlay/s6-rc.d/db-migration/up
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/db-migration/up
rename to docker/prod/etc/s6-overlay/s6-rc.d/db-migration/up
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/horizon/run b/docker/prod/etc/s6-overlay/s6-rc.d/horizon/run
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/horizon/run
rename to docker/prod/etc/s6-overlay/s6-rc.d/horizon/run
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/horizon/type b/docker/prod/etc/s6-overlay/s6-rc.d/horizon/type
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/horizon/type
rename to docker/prod/etc/s6-overlay/s6-rc.d/horizon/type
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/dependencies.d/init-seeder b/docker/prod/etc/s6-overlay/s6-rc.d/init-script/dependencies.d/init-seeder
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/dependencies.d/init-seeder
rename to docker/prod/etc/s6-overlay/s6-rc.d/init-script/dependencies.d/init-seeder
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/type b/docker/prod/etc/s6-overlay/s6-rc.d/init-script/type
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/type
rename to docker/prod/etc/s6-overlay/s6-rc.d/init-script/type
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/up b/docker/prod/etc/s6-overlay/s6-rc.d/init-script/up
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-script/up
rename to docker/prod/etc/s6-overlay/s6-rc.d/init-script/up
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-seeder/dependencies.d/db-migration b/docker/prod/etc/s6-overlay/s6-rc.d/init-seeder/dependencies.d/db-migration
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-seeder/dependencies.d/db-migration
rename to docker/prod/etc/s6-overlay/s6-rc.d/init-seeder/dependencies.d/db-migration
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-seeder/type b/docker/prod/etc/s6-overlay/s6-rc.d/init-seeder/type
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-seeder/type
rename to docker/prod/etc/s6-overlay/s6-rc.d/init-seeder/type
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-seeder/up b/docker/prod/etc/s6-overlay/s6-rc.d/init-seeder/up
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/init-seeder/up
rename to docker/prod/etc/s6-overlay/s6-rc.d/init-seeder/up
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/run b/docker/prod/etc/s6-overlay/s6-rc.d/scheduler-worker/run
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/run
rename to docker/prod/etc/s6-overlay/s6-rc.d/scheduler-worker/run
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/type b/docker/prod/etc/s6-overlay/s6-rc.d/scheduler-worker/type
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/scheduler-worker/type
rename to docker/prod/etc/s6-overlay/s6-rc.d/scheduler-worker/type
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/db-migration b/docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/db-migration
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/db-migration
rename to docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/db-migration
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/horizon b/docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/horizon
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/horizon
rename to docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/horizon
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/init-script b/docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/init-script
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/init-script
rename to docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/init-script
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/init-seeder b/docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/init-seeder
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/init-seeder
rename to docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/init-seeder
diff --git a/docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker b/docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker
similarity index 100%
rename from docker/prod-ssu/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker
rename to docker/prod/etc/s6-overlay/s6-rc.d/user/contents.d/scheduler-worker
diff --git a/docker/prod-ssu/nginx.conf b/docker/prod/nginx.conf
similarity index 100%
rename from docker/prod-ssu/nginx.conf
rename to docker/prod/nginx.conf
diff --git a/package-lock.json b/package-lock.json
index 17804b797..0010d87fa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,22 +6,22 @@
"": {
"dependencies": {
"@tailwindcss/forms": "0.5.7",
- "@tailwindcss/typography": "0.5.12",
- "alpinejs": "3.13.8",
- "ioredis": "5.3.2",
+ "@tailwindcss/typography": "0.5.13",
+ "alpinejs": "3.14.0",
+ "ioredis": "5.4.1",
"tailwindcss-scrollbar": "0.1.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "4.5.1",
"autoprefixer": "10.4.19",
- "axios": "1.6.8",
- "laravel-echo": "1.16.0",
+ "axios": "1.7.2",
+ "laravel-echo": "1.16.1",
"laravel-vite-plugin": "0.8.1",
"postcss": "8.4.38",
"pusher-js": "8.4.0-rc2",
"tailwindcss": "3.4.3",
"vite": "4.5.3",
- "vue": "3.4.21"
+ "vue": "3.4.27"
}
},
"node_modules/@alloc/quick-lru": {
@@ -36,9 +36,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.24.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz",
- "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==",
+ "version": "7.24.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
+ "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@@ -496,9 +496,9 @@
}
},
"node_modules/@tailwindcss/typography": {
- "version": "0.5.12",
- "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.12.tgz",
- "integrity": "sha512-CNwpBpconcP7ppxmuq3qvaCxiRWnbhANpY/ruH4L5qs2GCiVDJXde/pjj2HWPV1+Q4G9+V/etrwUYopdcjAlyg==",
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.13.tgz",
+ "integrity": "sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==",
"dependencies": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
@@ -535,77 +535,77 @@
}
},
"node_modules/@vue/compiler-core": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz",
- "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
+ "integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
"dev": true,
"dependencies": {
- "@babel/parser": "^7.23.9",
- "@vue/shared": "3.4.21",
+ "@babel/parser": "^7.24.4",
+ "@vue/shared": "3.4.27",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
- "source-map-js": "^1.0.2"
+ "source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-core/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/@vue/compiler-dom": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz",
- "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
+ "integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
"dev": true,
"dependencies": {
- "@vue/compiler-core": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/compiler-core": "3.4.27",
+ "@vue/shared": "3.4.27"
}
},
"node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz",
- "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
+ "integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
"dev": true,
"dependencies": {
- "@babel/parser": "^7.23.9",
- "@vue/compiler-core": "3.4.21",
- "@vue/compiler-dom": "3.4.21",
- "@vue/compiler-ssr": "3.4.21",
- "@vue/shared": "3.4.21",
+ "@babel/parser": "^7.24.4",
+ "@vue/compiler-core": "3.4.27",
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/compiler-ssr": "3.4.27",
+ "@vue/shared": "3.4.27",
"estree-walker": "^2.0.2",
- "magic-string": "^0.30.7",
- "postcss": "^8.4.35",
- "source-map-js": "^1.0.2"
+ "magic-string": "^0.30.10",
+ "postcss": "^8.4.38",
+ "source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz",
- "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
+ "integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
"dev": true,
"dependencies": {
- "@vue/compiler-dom": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/shared": "3.4.27"
}
},
"node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/@vue/reactivity": {
@@ -617,64 +617,64 @@
}
},
"node_modules/@vue/runtime-core": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz",
- "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
+ "integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
"dev": true,
"dependencies": {
- "@vue/reactivity": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/reactivity": "3.4.27",
+ "@vue/shared": "3.4.27"
}
},
"node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz",
- "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz",
+ "integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
"dev": true,
"dependencies": {
- "@vue/shared": "3.4.21"
+ "@vue/shared": "3.4.27"
}
},
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/@vue/runtime-dom": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz",
- "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
+ "integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
"dev": true,
"dependencies": {
- "@vue/runtime-core": "3.4.21",
- "@vue/shared": "3.4.21",
+ "@vue/runtime-core": "3.4.27",
+ "@vue/shared": "3.4.27",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/@vue/server-renderer": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz",
- "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
+ "integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
"dev": true,
"dependencies": {
- "@vue/compiler-ssr": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/compiler-ssr": "3.4.27",
+ "@vue/shared": "3.4.27"
},
"peerDependencies": {
- "vue": "3.4.21"
+ "vue": "3.4.27"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/@vue/shared": {
@@ -683,9 +683,9 @@
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
},
"node_modules/alpinejs": {
- "version": "3.13.8",
- "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.8.tgz",
- "integrity": "sha512-XolbBJryCndomtaHd/KHQjQeD/L72FJxy/YhLLFD4Lr7zzGcpcbg+UgXteMR2pYg1KhRUr6V4O3GfN1zJAmRWw==",
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.0.tgz",
+ "integrity": "sha512-YCWF95PMJqePe9ll6KMyDt/nLhh2R7RhqBf4loEmLzIskcHque4Br/9UgAa6cw13H0Cm3FM9e1hzDwP5z5wlDA==",
"dependencies": {
"@vue/reactivity": "~3.1.1"
}
@@ -756,9 +756,9 @@
}
},
"node_modules/axios": {
- "version": "1.6.8",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
- "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
+ "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -1238,9 +1238,9 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ioredis": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz",
- "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==",
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz",
+ "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==",
"dependencies": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
@@ -1318,9 +1318,9 @@
}
},
"node_modules/laravel-echo": {
- "version": "1.16.0",
- "resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-1.16.0.tgz",
- "integrity": "sha512-BJGUa4tcKvYmTkzTmcBGMHiO2tq+k7Do5wPmLbRswWfzKwyfZEUR+J5iwBTPEfLLwNPZlA9Kjo6R/NV6pmyIpg==",
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-1.16.1.tgz",
+ "integrity": "sha512-++Ylb6M3ariC9Rk5WE5gZjj6wcEV5kvLF8b+geJ5/rRIfdoOA+eG6b9qJPrarMD9rY28Apx+l3eelIrCc2skVg==",
"dev": true,
"engines": {
"node": ">=10"
@@ -1381,15 +1381,12 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"node_modules/magic-string": {
- "version": "0.30.8",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
- "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
+ "version": "0.30.10",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
+ "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
- },
- "engines": {
- "node": ">=12"
}
},
"node_modules/merge2": {
@@ -2085,16 +2082,16 @@
}
},
"node_modules/vue": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz",
- "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz",
+ "integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
"dev": true,
"dependencies": {
- "@vue/compiler-dom": "3.4.21",
- "@vue/compiler-sfc": "3.4.21",
- "@vue/runtime-dom": "3.4.21",
- "@vue/server-renderer": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/compiler-sfc": "3.4.27",
+ "@vue/runtime-dom": "3.4.27",
+ "@vue/server-renderer": "3.4.27",
+ "@vue/shared": "3.4.27"
},
"peerDependencies": {
"typescript": "*"
@@ -2106,9 +2103,9 @@
}
},
"node_modules/vue/node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/wrappy": {
diff --git a/package.json b/package.json
index ba733a457..4d6b321c8 100644
--- a/package.json
+++ b/package.json
@@ -8,20 +8,20 @@
"devDependencies": {
"@vitejs/plugin-vue": "4.5.1",
"autoprefixer": "10.4.19",
- "axios": "1.6.8",
- "laravel-echo": "1.16.0",
+ "axios": "1.7.2",
+ "laravel-echo": "1.16.1",
"laravel-vite-plugin": "0.8.1",
"postcss": "8.4.38",
"pusher-js": "8.4.0-rc2",
"tailwindcss": "3.4.3",
"vite": "4.5.3",
- "vue": "3.4.21"
+ "vue": "3.4.27"
},
"dependencies": {
"@tailwindcss/forms": "0.5.7",
- "@tailwindcss/typography": "0.5.12",
- "alpinejs": "3.13.8",
- "ioredis": "5.3.2",
+ "@tailwindcss/typography": "0.5.13",
+ "alpinejs": "3.14.0",
+ "ioredis": "5.4.1",
"tailwindcss-scrollbar": "0.1.0"
}
}
diff --git a/public/svgs/chatwoot.svg b/public/svgs/chatwoot.svg
new file mode 100644
index 000000000..1a7bf0bfd
--- /dev/null
+++ b/public/svgs/chatwoot.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/svgs/docuseal.png b/public/svgs/docuseal.png
new file mode 100644
index 000000000..3f1aed12c
Binary files /dev/null and b/public/svgs/docuseal.png differ
diff --git a/public/svgs/glance.png b/public/svgs/glance.png
new file mode 100644
index 000000000..8323483bf
Binary files /dev/null and b/public/svgs/glance.png differ
diff --git a/public/svgs/mediawiki.ico b/public/svgs/mediawiki.ico
new file mode 100644
index 000000000..525171399
Binary files /dev/null and b/public/svgs/mediawiki.ico differ
diff --git a/resources/css/app.css b/resources/css/app.css
index cae83b0de..42496cffe 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -4,6 +4,7 @@
html,
body {
+ zoom: 0.95;
@apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400;
}
diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php
index 27bc161af..395b03ff4 100644
--- a/resources/views/components/modal-confirmation.blade.php
+++ b/resources/views/components/modal-confirmation.blade.php
@@ -1,54 +1,68 @@
@props([
'title' => 'Are you sure?',
- 'buttonTitle' => 'Open Modal',
'isErrorButton' => false,
+ 'buttonTitle' => 'REWRITE THIS BUTTON TITLE PLEASSSSEEEE',
'buttonFullWidth' => false,
+ 'customButton' => null,
'disabled' => false,
'action' => 'delete',
'content' => null,
])