mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-12 12:13:41 +00:00
Merge branch 'next' into wrong-time-during-a-failed-deployment
This commit is contained in:
167 files changed
+5222
-2470
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/)!
|
||||
<a href="https://www.quantcdn.io/?utm_source=coolify.io"><img src="https://github.com/quantcdn.png" width="60px" alt="QuantCDN"/></a>
|
||||
<a href="https://www.runpod.io/?utm_source=coolify.io">
|
||||
<svg style="width:60px;height:60px;background:#fff;" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 200 200"><g><path d="M74.5 51.1c-25.4 14.9-27 16-29.6 20.2-1.8 3-1.9 5.3-1.9 32.3 0 21.7.3 29.4 1.3 30.6 1.9 2.5 46.7 27.9 48.5 27.6 1.5-.3 1.7-3.1 2-27.7.2-21.9 0-27.8-1.1-29.5-.8-1.2-9.9-6.8-20.2-12.6-10.3-5.8-19.4-11.5-20.2-12.7-1.8-2.6-.9-5.9 1.8-7.4 1.6-.8 6.3 0 21.8 4C87.8 78.7 98 81 99.6 81c4.4 0 49.9-25.9 49.9-28.4 0-1.6-3.4-2.8-24-8.2-13.2-3.5-25.1-6.3-26.5-6.3-1.4.1-12.4 5.9-24.5 13z"></path><path d="m137.2 68.1-3.3 2.1 6.3 3.7c3.5 2 6.3 4.3 6.3 5.1 0 .9-8 6.1-19.4 12.6-10.6 6-20 11.9-20.7 12.9-1.2 1.6-1.4 7.2-1.2 29.4.3 24.8.5 27.6 2 27.9 1.8.3 46.6-25.1 48.6-27.6.9-1.2 1.2-8.8 1.2-30.2s-.3-29-1.2-30.2c-1.6-1.9-12.1-7.8-13.9-7.8-.8 0-2.9 1-4.7 2.1z"></path></g></svg></a>
|
||||
<a href="https://lightspeed.run/?utm_source=coolify.io"><img src="https://github.com/lightspeedrun.png" width="60px" alt="Lightspeed.run"/></a>
|
||||
<a href="https://www.flint.sh/en/home?utm_source=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></a>
|
||||
<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
|
||||
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
||||
<a href="https://x.com/mrsmith9ja?utm_source=coolify.io"><img width="60px" alt="Thompson Edolo" src="https://github.com/verygreenboi.png"/></a>
|
||||
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
|
||||
<a href="https://github.com/Flowko"><img src="https://barrad.me/_ipx/f_webp&s_300x300/younes.jpg" width="60px" alt="Younes Barrad" /></a>
|
||||
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Automaze" /></a>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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([]);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Webhook;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class Gitea extends Controller
|
||||
{
|
||||
public function manual(Request $request)
|
||||
{
|
||||
try {
|
||||
$return_payloads = collect([]);
|
||||
$x_gitea_delivery = request()->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ class Github extends Controller
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Invalid token.',
|
||||
'message' => 'Invalid signature.',
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class PullTemplatesFromCDN implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $timeout = 10;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
if (!isDev()) {
|
||||
ray('PullTemplatesAndVersions service-templates');
|
||||
$response = Http::retry(3, 1000)->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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class PullVersionsFromCDN implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $timeout = 10;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
if (!isDev() && !isCloud()) {
|
||||
ray('PullTemplatesAndVersions versions.json');
|
||||
$response = Http::retry(3, 1000)->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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
$showToaster && $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.<br><br>$domain->{$this->application->destination->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> 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.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
$showToaster && $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.<br><br>$domain->{$this->application->destination->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
}
|
||||
check_domain_usage(resource: $this->application);
|
||||
}
|
||||
|
||||
@@ -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.<br><br>$preview->fqdn->{$this->application->destination->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> 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.<br><br>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()) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
@@ -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'];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -30,7 +30,6 @@ class Navbar extends Component
|
||||
$userId = auth()->user()->id;
|
||||
return [
|
||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
|
||||
"updateStatus"=> '$refresh',
|
||||
];
|
||||
}
|
||||
public function serviceStarted()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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', [
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -129,6 +129,9 @@ class ValidateAndInstall extends Component
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->server->isBuildServer()) {
|
||||
return;
|
||||
}
|
||||
$this->dispatch('startProxy');
|
||||
}
|
||||
public function render()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.<br><br>Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
$this->dispatch('error', "Validating DNS failed.<br><br>Make sure you have added the DNS records correctly.<br><br>{$this->settings->fqdn}->{$this->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
$error_show = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 . ".");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+42
-20
@@ -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<br><br>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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+68
-2
@@ -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'));
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
|
||||
class ApplicationPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, Application $application): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, Application $application): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, Application $application): bool
|
||||
{
|
||||
if ($user->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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Service;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
|
||||
class ServicePolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, Service $service): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, Service $service): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, Service $service): bool
|
||||
{
|
||||
if ($user->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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+106
-28
@@ -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')
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -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",
|
||||
|
||||
Generated
+579
-527
File diff suppressed because it is too large.
Load diff
@@ -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,
|
||||
|
||||
+1
-1
@@ -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'),
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.285';
|
||||
return '4.0.0-beta.294';
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('applications', function (Blueprint $table) {
|
||||
$table->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();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('applications', function (Blueprint $table) {
|
||||
$table->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');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -2,7 +2,7 @@ services:
|
||||
coolify:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./docker/dev-ssu/Dockerfile
|
||||
dockerfile: ./docker/dev/Dockerfile
|
||||
ports:
|
||||
- "${APP_PORT:-8000}:80"
|
||||
environment:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 167 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user