mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 20:59:24 +00:00
Compare commits
112 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9427d2ec1 | ||
|
|
332a0b9e04 | ||
|
|
18e98aaf52 | ||
|
|
a7f9fad627 | ||
|
|
b01f6ac414 | ||
|
|
e1bc2cc406 | ||
|
|
74830b12f3 | ||
|
|
56a977c676 | ||
|
|
a0bb5733e6 | ||
|
|
516e10ddf2 | ||
|
|
2976c72e09 | ||
|
|
7377e9e415 | ||
|
|
d77c55148b | ||
|
|
ad7aa2eed6 | ||
|
|
5cec50efbe | ||
|
|
ef8686d4da | ||
|
|
581cc73cd4 | ||
|
|
358fbf6b3d | ||
|
|
ca0535c285 | ||
|
|
9007a645a6 | ||
|
|
68b1b9774d | ||
|
|
b9b4c23d5b | ||
|
|
149fee2452 | ||
|
|
87af9e46a6 | ||
|
|
d6f87d3fb6 | ||
|
|
493af61233 | ||
|
|
ab03908f1d | ||
|
|
9ef7cf3c12 | ||
|
|
eab7fd44d4 | ||
|
|
0d1d25a945 | ||
|
|
534372c29c | ||
|
|
1ccb239797 | ||
|
|
66287b43d0 | ||
|
|
143e4e0d23 | ||
|
|
73f3a09157 | ||
|
|
5ce449aa08 | ||
|
|
6203804713 | ||
|
|
0858faf628 | ||
|
|
a84f3e0577 | ||
|
|
8d571a5eab | ||
|
|
7a117c61c4 | ||
|
|
8b034f15fc | ||
|
|
b4a6499c83 | ||
|
|
c083acaeef | ||
|
|
9c6d8320d8 | ||
|
|
0e7a304610 | ||
|
|
83993cbbb2 | ||
|
|
6840ddd3e6 | ||
|
|
2c6ece62bb | ||
|
|
3f8514050e | ||
|
|
a4a653603e | ||
|
|
b6d8851c99 | ||
|
|
bcd7697f50 | ||
|
|
f1da735c40 | ||
|
|
01331c287b | ||
|
|
3320de787a | ||
|
|
2bddb09384 | ||
|
|
6f673d7a07 | ||
|
|
0a5a101ef4 | ||
|
|
88590fbf0f | ||
|
|
90291b2edf | ||
|
|
070573f0df | ||
|
|
e583beb753 | ||
|
|
d31683df61 | ||
|
|
0a83ed82fa | ||
|
|
4031e477ee | ||
|
|
0c1991d1de | ||
|
|
05b697b18c | ||
|
|
061aeba605 | ||
|
|
f446e784cc | ||
|
|
72fe24d98e | ||
|
|
0cd3a3d848 | ||
|
|
126b2dc65b | ||
|
|
a0031efce0 | ||
|
|
3bffe3f010 | ||
|
|
b9a37233a2 | ||
|
|
8ae18f49dc | ||
|
|
4feb99cbe0 | ||
|
|
aab122d97e | ||
|
|
658d608f55 | ||
|
|
0838343841 | ||
|
|
b557ea1e1d | ||
|
|
4520070df3 | ||
|
|
be8ea78b1b | ||
|
|
1175d68ab5 | ||
|
|
f56d373ed2 | ||
|
|
c6253658ca | ||
|
|
4249aec936 | ||
|
|
25f80aba5f | ||
|
|
4550983761 | ||
|
|
b0238372a2 | ||
|
|
a021b71496 | ||
|
|
e3958d9626 | ||
|
|
55891d7001 | ||
|
|
b12ac8bb29 | ||
|
|
728a9f88eb | ||
|
|
57267c3ee0 | ||
|
|
d3d133ed1f | ||
|
|
f5240abbe5 | ||
|
|
abf5840f97 | ||
|
|
7925228f97 | ||
|
|
a7dc62aaa0 | ||
|
|
632dbd155b | ||
|
|
fe092bb7a5 | ||
|
|
722ff15fbd | ||
|
|
fcd0d8d359 | ||
|
|
3fcac0ac35 | ||
|
|
fcc8a7f0ed | ||
|
|
6950ead041 | ||
|
|
f78c49fc82 | ||
|
|
5f2581020b | ||
|
|
2fb674ae85 |
2
.github/workflows/development-build.yml
vendored
2
.github/workflows/development-build.yml
vendored
@@ -13,7 +13,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
amd64:
|
amd64:
|
||||||
runs-on: [self-hosted, x64]
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
|
|||||||
2
.github/workflows/production-build.yml
vendored
2
.github/workflows/production-build.yml
vendored
@@ -10,7 +10,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
amd64:
|
amd64:
|
||||||
runs-on: [self-hosted, x64]
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
|
|||||||
28
.tinkerwell/snippets/SendEmail.php
Normal file
28
.tinkerwell/snippets/SendEmail.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @label Send Email
|
||||||
|
* @description Send email to all users
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
set_transanctional_email_settings();
|
||||||
|
|
||||||
|
$users = User::whereEmail('andras.bacsai@gmail.com');
|
||||||
|
foreach ($users as $user) {
|
||||||
|
Mail::send([], [], function ($message) use ($user) {
|
||||||
|
$message
|
||||||
|
->to($user->email)
|
||||||
|
->subject("Testing")
|
||||||
|
->text(
|
||||||
|
<<<EOF
|
||||||
|
Hello,
|
||||||
|
|
||||||
|
Welcome to Coolify Cloud.
|
||||||
|
Here is your user id: $user->id
|
||||||
|
|
||||||
|
EOF
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -4,26 +4,26 @@ namespace App\Actions\License;
|
|||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
|
||||||
class CheckResaleLicense
|
class CheckResaleLicense
|
||||||
{
|
{
|
||||||
public function __invoke()
|
use AsAction;
|
||||||
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
$settings->update([
|
|
||||||
'is_resale_license_active' => false,
|
|
||||||
]);
|
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
|
$settings->update([
|
||||||
|
'is_resale_license_active' => true,
|
||||||
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!$settings->resale_license) {
|
// if (!$settings->resale_license) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
$base_url = config('coolify.license_url');
|
$base_url = config('coolify.license_url');
|
||||||
if (isDev()) {
|
|
||||||
$base_url = 'http://host.docker.internal:8787';
|
|
||||||
}
|
|
||||||
$instance_id = config('app.id');
|
$instance_id = config('app.id');
|
||||||
|
|
||||||
ray("Checking license key against $base_url/lemon/validate");
|
ray("Checking license key against $base_url/lemon/validate");
|
||||||
|
|||||||
@@ -16,17 +16,17 @@ class StartService
|
|||||||
$commands[] = "cd " . $service->workdir();
|
$commands[] = "cd " . $service->workdir();
|
||||||
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
||||||
$commands[] = "echo '####### Creating Docker network.'";
|
$commands[] = "echo '####### Creating Docker network.'";
|
||||||
$commands[] = "docker network create --attachable {$service->uuid} >/dev/null 2>/dev/null || true";
|
$commands[] = "docker network create --attachable '{$service->uuid}' >/dev/null || true";
|
||||||
$commands[] = "echo '####### Starting service {$service->name} on {$service->server->name}.'";
|
$commands[] = "echo '####### Starting service {$service->name} on {$service->server->name}.'";
|
||||||
$commands[] = "echo '####### Pulling images.'";
|
$commands[] = "echo '####### Pulling images.'";
|
||||||
$commands[] = "docker compose pull";
|
$commands[] = "docker compose pull";
|
||||||
$commands[] = "echo '####### Starting containers.'";
|
$commands[] = "echo '####### Starting containers.'";
|
||||||
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
||||||
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
$commands[] = "docker network connect $service->uuid coolify-proxy || true";
|
||||||
$compose = data_get($service,'docker_compose',[]);
|
$compose = data_get($service,'docker_compose',[]);
|
||||||
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
|
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
|
||||||
foreach($serviceNames as $serviceName => $serviceConfig){
|
foreach($serviceNames as $serviceName => $serviceConfig){
|
||||||
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} 2>/dev/null || true";
|
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} || true";
|
||||||
}
|
}
|
||||||
$activity = remote_process($commands, $service->server);
|
$activity = remote_process($commands, $service->server);
|
||||||
return $activity;
|
return $activity;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ namespace App\Actions\Service;
|
|||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Notifications\Application\StatusChanged;
|
|
||||||
|
|
||||||
class StopService
|
class StopService
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ use App\Enums\ApplicationDeploymentStatus;
|
|||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
@@ -15,15 +18,18 @@ use Illuminate\Support\Facades\Storage;
|
|||||||
|
|
||||||
class Init extends Command
|
class Init extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'app:init';
|
protected $signature = 'app:init {--cleanup}';
|
||||||
protected $description = 'Cleanup instance related stuffs';
|
protected $description = 'Cleanup instance related stuffs';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
ray()->clearAll();
|
ray()->clearAll();
|
||||||
|
$cleanup = $this->option('cleanup');
|
||||||
|
if ($cleanup) {
|
||||||
|
$this->cleanup_stucked_resources();
|
||||||
|
$this->cleanup_ssh();
|
||||||
|
}
|
||||||
$this->cleanup_in_progress_application_deployments();
|
$this->cleanup_in_progress_application_deployments();
|
||||||
$this->cleanup_stucked_resources();
|
|
||||||
// $this->cleanup_ssh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanup_ssh()
|
private function cleanup_ssh()
|
||||||
@@ -38,7 +44,7 @@ class Init extends Command
|
|||||||
Storage::delete($file);
|
Storage::delete($file);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error: {$e->getMessage()}\n";
|
echo "Error in cleaning ssh: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function cleanup_in_progress_application_deployments()
|
private function cleanup_in_progress_application_deployments()
|
||||||
@@ -61,77 +67,129 @@ class Init extends Command
|
|||||||
try {
|
try {
|
||||||
$applications = Application::all();
|
$applications = Application::all();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
if (!$application->environment) {
|
if (!data_get($application, 'environment')) {
|
||||||
ray('Application without environment', $application->name);
|
ray('Application without environment', $application->name);
|
||||||
$application->delete();
|
$application->delete();
|
||||||
}
|
}
|
||||||
|
if (!data_get($application, 'destination.server')) {
|
||||||
|
ray('Application without server', $application->name);
|
||||||
|
$application->delete();
|
||||||
|
}
|
||||||
if (!$application->destination()) {
|
if (!$application->destination()) {
|
||||||
ray('Application without destination', $application->name);
|
ray('Application without destination', $application->name);
|
||||||
$application->delete();
|
$application->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in application: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
$postgresqls = StandalonePostgresql::all();
|
$postgresqls = StandalonePostgresql::all();
|
||||||
foreach ($postgresqls as $postgresql) {
|
foreach ($postgresqls as $postgresql) {
|
||||||
if (!$postgresql->environment) {
|
if (!data_get($postgresql, 'environment')) {
|
||||||
ray('Postgresql without environment', $postgresql->name);
|
ray('Postgresql without environment', $postgresql->name);
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
}
|
}
|
||||||
|
if (!data_get($postgresql, 'destination.server')) {
|
||||||
|
ray('Postgresql without server', $postgresql->name);
|
||||||
|
$postgresql->delete();
|
||||||
|
}
|
||||||
if (!$postgresql->destination()) {
|
if (!$postgresql->destination()) {
|
||||||
ray('Postgresql without destination', $postgresql->name);
|
ray('Postgresql without destination', $postgresql->name);
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in postgresql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
$redis = StandaloneRedis::all();
|
$redis = StandaloneRedis::all();
|
||||||
foreach ($redis as $redis) {
|
foreach ($redis as $redis) {
|
||||||
if (!$redis->environment) {
|
if (!data_get($redis, 'environment')) {
|
||||||
ray('Redis without environment', $redis->name);
|
ray('Redis without environment', $redis->name);
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
}
|
}
|
||||||
|
if (!data_get($redis, 'destination.server')) {
|
||||||
|
ray('Redis without server', $redis->name);
|
||||||
|
$redis->delete();
|
||||||
|
}
|
||||||
if (!$redis->destination()) {
|
if (!$redis->destination()) {
|
||||||
ray('Redis without destination', $redis->name);
|
ray('Redis without destination', $redis->name);
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in redis: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$mongodbs = StandaloneMongodb::all();
|
$mongodbs = StandaloneMongodb::all();
|
||||||
foreach ($mongodbs as $mongodb) {
|
foreach ($mongodbs as $mongodb) {
|
||||||
if (!$mongodb->environment) {
|
if (!data_get($mongodb, 'environment')) {
|
||||||
ray('Mongodb without environment', $mongodb->name);
|
ray('Mongodb without environment', $mongodb->name);
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
}
|
}
|
||||||
|
if (!data_get($mongodb, 'destination.server')) {
|
||||||
|
ray('Mongodb without server', $mongodb->name);
|
||||||
|
$mongodb->delete();
|
||||||
|
}
|
||||||
if (!$mongodb->destination()) {
|
if (!$mongodb->destination()) {
|
||||||
ray('Mongodb without destination', $mongodb->name);
|
ray('Mongodb without destination', $mongodb->name);
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in mongodb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$mysqls = StandaloneMysql::all();
|
$mysqls = StandaloneMysql::all();
|
||||||
foreach ($mysqls as $mysql) {
|
foreach ($mysqls as $mysql) {
|
||||||
if (!$mysql->environment) {
|
if (!data_get($mysql, 'environment')) {
|
||||||
ray('Mysql without environment', $mysql->name);
|
ray('Mysql without environment', $mysql->name);
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
}
|
}
|
||||||
|
if (!data_get($mysql, 'destination.server')) {
|
||||||
|
ray('Mysql without server', $mysql->name);
|
||||||
|
$mysql->delete();
|
||||||
|
}
|
||||||
if (!$mysql->destination()) {
|
if (!$mysql->destination()) {
|
||||||
ray('Mysql without destination', $mysql->name);
|
ray('Mysql without destination', $mysql->name);
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$mariadbs = StandaloneMysql::all();
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in mysql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$mariadbs = StandaloneMariadb::all();
|
||||||
foreach ($mariadbs as $mariadb) {
|
foreach ($mariadbs as $mariadb) {
|
||||||
if (!$mariadb->environment) {
|
if (!data_get($mariadb, 'environment')) {
|
||||||
ray('Mariadb without environment', $mariadb->name);
|
ray('Mariadb without environment', $mariadb->name);
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
}
|
}
|
||||||
|
if (!data_get($mariadb, 'destination.server')) {
|
||||||
|
ray('Mariadb without server', $mariadb->name);
|
||||||
|
$mariadb->delete();
|
||||||
|
}
|
||||||
if (!$mariadb->destination()) {
|
if (!$mariadb->destination()) {
|
||||||
ray('Mariadb without destination', $mariadb->name);
|
ray('Mariadb without destination', $mariadb->name);
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in mariadb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$services = Service::all();
|
$services = Service::all();
|
||||||
foreach ($services as $service) {
|
foreach ($services as $service) {
|
||||||
if (!$service->environment) {
|
if (!data_get($service, 'environment')) {
|
||||||
ray('Service without environment', $service->name);
|
ray('Service without environment', $service->name);
|
||||||
$service->delete();
|
$service->delete();
|
||||||
}
|
}
|
||||||
if (!$service->server) {
|
if (!data_get($service, 'server')) {
|
||||||
ray('Service without server', $service->name);
|
ray('Service without server', $service->name);
|
||||||
$service->delete();
|
$service->delete();
|
||||||
}
|
}
|
||||||
@@ -141,7 +199,29 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error: {$e->getMessage()}\n";
|
echo "Error in service: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceApplications = ServiceApplication::all();
|
||||||
|
foreach ($serviceApplications as $service) {
|
||||||
|
if (!data_get($service, 'service')) {
|
||||||
|
ray('ServiceApplication without service', $service->name);
|
||||||
|
$service->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in serviceApplications: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceDatabases = ServiceDatabase::all();
|
||||||
|
foreach ($serviceDatabases as $service) {
|
||||||
|
if (!data_get($service, 'service')) {
|
||||||
|
ray('ServiceDatabase without service', $service->name);
|
||||||
|
$service->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in ServiceDatabases: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,12 +61,14 @@ class ResourcesDelete extends Command
|
|||||||
|
|
||||||
foreach ($serversToDelete as $server) {
|
foreach ($serversToDelete as $server) {
|
||||||
$toDelete = $servers->where('id', $server)->first();
|
$toDelete = $servers->where('id', $server)->first();
|
||||||
$this->info($toDelete);
|
if ($toDelete) {
|
||||||
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
$this->info($toDelete);
|
||||||
if (!$confirmed) {
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
break;
|
if (!$confirmed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$toDelete->delete();
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function deleteApplication()
|
private function deleteApplication()
|
||||||
@@ -82,14 +84,15 @@ class ResourcesDelete extends Command
|
|||||||
);
|
);
|
||||||
|
|
||||||
foreach ($applicationsToDelete as $application) {
|
foreach ($applicationsToDelete as $application) {
|
||||||
ray($application);
|
|
||||||
$toDelete = $applications->where('id', $application)->first();
|
$toDelete = $applications->where('id', $application)->first();
|
||||||
$this->info($toDelete);
|
if ($toDelete) {
|
||||||
$confirmed = confirm("Are you sure you want to delete all selected resources? ");
|
$this->info($toDelete);
|
||||||
if (!$confirmed) {
|
$confirmed = confirm("Are you sure you want to delete all selected resources? ");
|
||||||
break;
|
if (!$confirmed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$toDelete->delete();
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function deleteDatabase()
|
private function deleteDatabase()
|
||||||
@@ -106,12 +109,14 @@ class ResourcesDelete extends Command
|
|||||||
|
|
||||||
foreach ($databasesToDelete as $database) {
|
foreach ($databasesToDelete as $database) {
|
||||||
$toDelete = $databases->where('id', $database)->first();
|
$toDelete = $databases->where('id', $database)->first();
|
||||||
$this->info($toDelete);
|
if ($toDelete) {
|
||||||
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
$this->info($toDelete);
|
||||||
if (!$confirmed) {
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
return;
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$toDelete->delete();
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function deleteService()
|
private function deleteService()
|
||||||
@@ -128,12 +133,14 @@ class ResourcesDelete extends Command
|
|||||||
|
|
||||||
foreach ($servicesToDelete as $service) {
|
foreach ($servicesToDelete as $service) {
|
||||||
$toDelete = $services->where('id', $service)->first();
|
$toDelete = $services->where('id', $service)->first();
|
||||||
$this->info($toDelete);
|
if ($toDelete) {
|
||||||
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
$this->info($toDelete);
|
||||||
if (!$confirmed) {
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
return;
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$toDelete->delete();
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class Kernel extends ConsoleKernel
|
|||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
$schedule->command('horizon:snapshot')->everyMinute();
|
$schedule->command('horizon:snapshot')->everyMinute();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
||||||
|
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
// Server Jobs
|
// Server Jobs
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
@@ -34,7 +34,7 @@ class Kernel extends ConsoleKernel
|
|||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||||
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
|
|
||||||
// Server Jobs
|
// Server Jobs
|
||||||
$this->instance_auto_update($schedule);
|
$this->instance_auto_update($schedule);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class ApplicationController extends Controller
|
|||||||
if (!$application) {
|
if (!$application) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 8);
|
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
|
||||||
return view('project.application.deployments', ['application' => $application, 'deployments' => $deployments, 'deployments_count' => $count]);
|
return view('project.application.deployments', ['application' => $application, 'deployments' => $deployments, 'deployments_count' => $count]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,14 @@ class MagicController extends Controller
|
|||||||
|
|
||||||
public function environments()
|
public function environments()
|
||||||
{
|
{
|
||||||
|
$project = Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first();
|
||||||
|
if (!$project) {
|
||||||
|
return response()->json([
|
||||||
|
'environments' => []
|
||||||
|
]);
|
||||||
|
}
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'environments' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first()->environments
|
'environments' => $project->environments
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ class ProjectController extends Controller
|
|||||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||||
if ($oneClickDotEnvs) {
|
if ($oneClickDotEnvs) {
|
||||||
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
|
||||||
|
return !empty($value);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if ($oneClickService) {
|
if ($oneClickService) {
|
||||||
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
{
|
{
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'remoteServerName' => 'required',
|
'remoteServerName' => 'required',
|
||||||
'remoteServerHost' => 'required|ip',
|
'remoteServerHost' => 'required',
|
||||||
'remoteServerPort' => 'required|integer',
|
'remoteServerPort' => 'required|integer',
|
||||||
'remoteServerUser' => 'required',
|
'remoteServerUser' => 'required',
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class CheckLicense extends Component
|
|||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
if ($this->settings->resale_license) {
|
if ($this->settings->resale_license) {
|
||||||
try {
|
try {
|
||||||
resolve(CheckResaleLicense::class)();
|
CheckResaleLicense::run();
|
||||||
$this->emit('reloadWindow');
|
$this->emit('reloadWindow');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class Form extends Component
|
|||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'destination.name' => 'name',
|
'destination.name' => 'name',
|
||||||
'destination.network' => 'network',
|
'destination.network' => 'network',
|
||||||
'destination.server.ip' => 'IP Address',
|
'destination.server.ip' => 'IP Address/Domain',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
|
|||||||
@@ -3,24 +3,31 @@
|
|||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Http\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Deployments extends Component
|
class Deployments extends Component
|
||||||
{
|
{
|
||||||
public Application $application;
|
public Application $application;
|
||||||
public $deployments = [];
|
public Array|Collection $deployments = [];
|
||||||
public int $deployments_count = 0;
|
public int $deployments_count = 0;
|
||||||
public string $current_url;
|
public string $current_url;
|
||||||
public int $skip = 0;
|
public int $skip = 0;
|
||||||
public int $default_take = 8;
|
public int $default_take = 40;
|
||||||
public bool $show_next = false;
|
public bool $show_next = false;
|
||||||
|
public ?string $pull_request_id = null;
|
||||||
|
protected $queryString = ['pull_request_id'];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->current_url = url()->current();
|
$this->current_url = url()->current();
|
||||||
|
$this->show_pull_request_only();
|
||||||
$this->show_more();
|
$this->show_more();
|
||||||
}
|
}
|
||||||
|
private function show_pull_request_only() {
|
||||||
|
if ($this->pull_request_id) {
|
||||||
|
$this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
private function show_more()
|
private function show_more()
|
||||||
{
|
{
|
||||||
if (count($this->deployments) !== 0) {
|
if (count($this->deployments) !== 0) {
|
||||||
@@ -47,6 +54,7 @@ class Deployments extends Component
|
|||||||
['deployments' => $deployments, 'count' => $count] = $this->application->deployments($this->skip, $take);
|
['deployments' => $deployments, 'count' => $count] = $this->application->deployments($this->skip, $take);
|
||||||
$this->deployments = $deployments;
|
$this->deployments = $deployments;
|
||||||
$this->deployments_count = $count;
|
$this->deployments_count = $count;
|
||||||
|
$this->show_pull_request_only();
|
||||||
$this->show_more();
|
$this->show_more();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class General extends Component
|
|||||||
'application.docker_registry_image_tag' => 'nullable',
|
'application.docker_registry_image_tag' => 'nullable',
|
||||||
'application.dockerfile_location' => 'nullable',
|
'application.dockerfile_location' => 'nullable',
|
||||||
'application.custom_labels' => 'nullable',
|
'application.custom_labels' => 'nullable',
|
||||||
|
'application.dockerfile_target_build' => 'nullable',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@@ -77,6 +78,7 @@ class General extends Component
|
|||||||
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
||||||
'application.dockerfile_location' => 'Dockerfile location',
|
'application.dockerfile_location' => 'Dockerfile location',
|
||||||
'application.custom_labels' => 'Custom labels',
|
'application.custom_labels' => 'Custom labels',
|
||||||
|
'application.dockerfile_target_build' => 'Dockerfile target build',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -152,7 +154,7 @@ class General extends Component
|
|||||||
$fqdn = generateFqdn($server, $this->application->uuid);
|
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||||
$this->application->fqdn = $fqdn;
|
$this->application->fqdn = $fqdn;
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->updatedApplicationFqdn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function resetDefaultLabels($showToaster = true)
|
public function resetDefaultLabels($showToaster = true)
|
||||||
|
|||||||
@@ -65,4 +65,18 @@ class Heading extends Component
|
|||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
}
|
}
|
||||||
|
public function restart() {
|
||||||
|
$this->setDeploymentUuid();
|
||||||
|
queue_application_deployment(
|
||||||
|
application_id: $this->application->id,
|
||||||
|
deployment_uuid: $this->deploymentUuid,
|
||||||
|
restart_only: true,
|
||||||
|
);
|
||||||
|
return redirect()->route('project.application.deployment', [
|
||||||
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ class CloneProject extends Component
|
|||||||
$uuid = (string)new Cuid2(7);
|
$uuid = (string)new Cuid2(7);
|
||||||
$newDatabase = $database->replicate()->fill([
|
$newDatabase = $database->replicate()->fill([
|
||||||
'uuid' => $uuid,
|
'uuid' => $uuid,
|
||||||
|
'status' => 'exited',
|
||||||
'environment_id' => $newEnvironment->id,
|
'environment_id' => $newEnvironment->id,
|
||||||
'destination_id' => $this->selectedServer,
|
'destination_id' => $this->selectedServer,
|
||||||
]);
|
]);
|
||||||
@@ -111,15 +112,15 @@ class CloneProject extends Component
|
|||||||
$environmentVaribles = $database->environment_variables()->get();
|
$environmentVaribles = $database->environment_variables()->get();
|
||||||
foreach ($environmentVaribles as $environmentVarible) {
|
foreach ($environmentVaribles as $environmentVarible) {
|
||||||
$payload = [];
|
$payload = [];
|
||||||
if ($database->type() === 'standalone-postgres') {
|
if ($database->type() === 'standalone-postgresql') {
|
||||||
$payload['standalone_postgresql_id'] = $newDatabase->id;
|
$payload['standalone_postgresql_id'] = $newDatabase->id;
|
||||||
} else if ($database->type() === 'standalone_redis') {
|
} else if ($database->type() === 'standalone-redis') {
|
||||||
$payload['standalone_redis_id'] = $newDatabase->id;
|
$payload['standalone_redis_id'] = $newDatabase->id;
|
||||||
} else if ($database->type() === 'standalone_mongodb') {
|
} else if ($database->type() === 'standalone-mongodb') {
|
||||||
$payload['standalone_mongodb_id'] = $newDatabase->id;
|
$payload['standalone_mongodb_id'] = $newDatabase->id;
|
||||||
} else if ($database->type() === 'standalone_mysql') {
|
} else if ($database->type() === 'standalone-mysql') {
|
||||||
$payload['standalone_mysql_id'] = $newDatabase->id;
|
$payload['standalone_mysql_id'] = $newDatabase->id;
|
||||||
}else if ($database->type() === 'standalone_mariadb') {
|
} else if ($database->type() === 'standalone-mariadb') {
|
||||||
$payload['standalone_mariadb_id'] = $newDatabase->id;
|
$payload['standalone_mariadb_id'] = $newDatabase->id;
|
||||||
}
|
}
|
||||||
$newEnvironmentVariable = $environmentVarible->replicate()->fill($payload);
|
$newEnvironmentVariable = $environmentVarible->replicate()->fill($payload);
|
||||||
@@ -134,6 +135,16 @@ class CloneProject extends Component
|
|||||||
'destination_id' => $this->selectedServer,
|
'destination_id' => $this->selectedServer,
|
||||||
]);
|
]);
|
||||||
$newService->save();
|
$newService->save();
|
||||||
|
foreach ($newService->applications() as $application) {
|
||||||
|
$application->update([
|
||||||
|
'status' => 'exited',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
foreach ($newService->databases() as $database) {
|
||||||
|
$database->update([
|
||||||
|
'status' => 'exited',
|
||||||
|
]);
|
||||||
|
}
|
||||||
$newService->parse();
|
$newService->parse();
|
||||||
}
|
}
|
||||||
return redirect()->route('project.resources', [
|
return redirect()->route('project.resources', [
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Http\Livewire\Project\Database;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Spatie\Url\Url;
|
||||||
|
|
||||||
class BackupEdit extends Component
|
class BackupEdit extends Component
|
||||||
{
|
{
|
||||||
@@ -43,14 +44,23 @@ class BackupEdit extends Component
|
|||||||
{
|
{
|
||||||
// TODO: Delete backup from server and add a confirmation modal
|
// TODO: Delete backup from server and add a confirmation modal
|
||||||
$this->backup->delete();
|
$this->backup->delete();
|
||||||
redirect()->route('project.database.backups.all', $this->parameters);
|
if ($this->backup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
|
$previousUrl = url()->previous();
|
||||||
|
$url = Url::fromString($previousUrl);
|
||||||
|
$url = $url->withoutQueryParameter('selectedBackupId');
|
||||||
|
$url = $url->withFragment('backups');
|
||||||
|
$url = $url->getPath() . "#{$url->getFragment()}";
|
||||||
|
return redirect()->to($url);
|
||||||
|
} else {
|
||||||
|
redirect()->route('project.database.backups.all', $this->parameters);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->custom_validate();
|
$this->custom_validate();
|
||||||
|
|
||||||
$this->backup->save();
|
$this->backup->save();
|
||||||
$this->backup->refresh();
|
$this->backup->refresh();
|
||||||
$this->emit('success', 'Backup updated successfully');
|
$this->emit('success', 'Backup updated successfully');
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ class BackupExecutions extends Component
|
|||||||
$this->emit('error', 'Backup execution not found.');
|
$this->emit('error', 'Backup execution not found.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
|
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
|
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->service->destination->server);
|
||||||
|
} else {
|
||||||
|
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
|
||||||
|
}
|
||||||
$execution->delete();
|
$execution->delete();
|
||||||
$this->emit('success', 'Backup deleted successfully.');
|
$this->emit('success', 'Backup deleted successfully.');
|
||||||
$this->emit('refreshBackupExecutions');
|
$this->emit('refreshBackupExecutions');
|
||||||
@@ -33,7 +37,11 @@ class BackupExecutions extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = data_get($execution, 'filename');
|
$filename = data_get($execution, 'filename');
|
||||||
$server = $execution->scheduledDatabaseBackup->database->destination->server;
|
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
|
$server = $execution->scheduledDatabaseBackup->database->service->destination->server;
|
||||||
|
} else {
|
||||||
|
$server = $execution->scheduledDatabaseBackup->database->destination->server;
|
||||||
|
}
|
||||||
$privateKeyLocation = savePrivateKeyToFs($server);
|
$privateKeyLocation = savePrivateKeyToFs($server);
|
||||||
$disk = Storage::build([
|
$disk = Storage::build([
|
||||||
'driver' => 'sftp',
|
'driver' => 'sftp',
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ class CreateScheduledBackup extends Component
|
|||||||
'frequency' => 'Backup Frequency',
|
'frequency' => 'Backup Frequency',
|
||||||
'save_s3' => 'Save to S3',
|
'save_s3' => 'Save to S3',
|
||||||
];
|
];
|
||||||
public function mount() {
|
public function mount()
|
||||||
|
{
|
||||||
if ($this->s3s->count() > 0) {
|
if ($this->s3s->count() > 0) {
|
||||||
$this->s3_storage_id = $this->s3s->first()->id;
|
$this->s3_storage_id = $this->s3s->first()->id;
|
||||||
}
|
}
|
||||||
@@ -50,11 +51,16 @@ class CreateScheduledBackup extends Component
|
|||||||
$payload['databases_to_backup'] = $this->database->postgres_db;
|
$payload['databases_to_backup'] = $this->database->postgres_db;
|
||||||
} else if ($this->database->type() === 'standalone-mysql') {
|
} else if ($this->database->type() === 'standalone-mysql') {
|
||||||
$payload['databases_to_backup'] = $this->database->mysql_database;
|
$payload['databases_to_backup'] = $this->database->mysql_database;
|
||||||
}else if ($this->database->type() === 'standalone-mariadb') {
|
} else if ($this->database->type() === 'standalone-mariadb') {
|
||||||
$payload['databases_to_backup'] = $this->database->mariadb_database;
|
$payload['databases_to_backup'] = $this->database->mariadb_database;
|
||||||
}
|
}
|
||||||
ScheduledDatabaseBackup::create($payload);
|
|
||||||
$this->emit('refreshScheduledBackups');
|
$databaseBackup = ScheduledDatabaseBackup::create($payload);
|
||||||
|
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
|
$this->emit('refreshScheduledBackups', $databaseBackup->id);
|
||||||
|
} else {
|
||||||
|
$this->emit('refreshScheduledBackups');
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
handleError($e, $this);
|
handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -8,13 +8,33 @@ class ScheduledBackups extends Component
|
|||||||
{
|
{
|
||||||
public $database;
|
public $database;
|
||||||
public $parameters;
|
public $parameters;
|
||||||
|
public $type;
|
||||||
|
public $selectedBackup;
|
||||||
|
public $selectedBackupId;
|
||||||
|
public $s3s;
|
||||||
protected $listeners = ['refreshScheduledBackups'];
|
protected $listeners = ['refreshScheduledBackups'];
|
||||||
|
protected $queryString = ['selectedBackupId'];
|
||||||
|
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
{
|
{
|
||||||
|
if ($this->selectedBackupId) {
|
||||||
|
$this->setSelectedBackup($this->selectedBackupId);
|
||||||
|
}
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
|
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
|
$this->type = 'service-database';
|
||||||
|
} else {
|
||||||
|
$this->type = 'database';
|
||||||
|
}
|
||||||
|
$this->s3s = currentTeam()->s3s;
|
||||||
|
}
|
||||||
|
public function setSelectedBackup($backupId) {
|
||||||
|
$this->selectedBackupId = $backupId;
|
||||||
|
$this->selectedBackup = $this->database->scheduledBackups->find($this->selectedBackupId);
|
||||||
|
if (is_null($this->selectedBackup)) {
|
||||||
|
$this->selectedBackupId = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($scheduled_backup_id): void
|
public function delete($scheduled_backup_id): void
|
||||||
{
|
{
|
||||||
$this->database->scheduledBackups->find($scheduled_backup_id)->delete();
|
$this->database->scheduledBackups->find($scheduled_backup_id)->delete();
|
||||||
@@ -22,9 +42,11 @@ class ScheduledBackups extends Component
|
|||||||
$this->refreshScheduledBackups();
|
$this->refreshScheduledBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function refreshScheduledBackups(): void
|
public function refreshScheduledBackups(?int $id = null): void
|
||||||
{
|
{
|
||||||
ray('refreshScheduledBackups');
|
|
||||||
$this->database->refresh();
|
$this->database->refresh();
|
||||||
|
if ($id) {
|
||||||
|
$this->setSelectedBackup($id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use App\Traits\SaveFromRedirect;
|
|||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
|
||||||
|
|
||||||
class GithubPrivateRepository extends Component
|
class GithubPrivateRepository extends Component
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Index extends Component
|
|||||||
public $databases;
|
public $databases;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
protected $listeners = ["refreshStacks","checkStatus"];
|
protected $listeners = ["refreshStacks", "checkStatus"];
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.project.service.index');
|
return view('livewire.project.service.index');
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ namespace App\Http\Livewire\Project\Service;
|
|||||||
|
|
||||||
use App\Actions\Service\StartService;
|
use App\Actions\Service\StartService;
|
||||||
use App\Actions\Service\StopService;
|
use App\Actions\Service\StopService;
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -13,15 +12,14 @@ class Navbar extends Component
|
|||||||
public Service $service;
|
public Service $service;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
|
protected $listeners = ["checkStatus"];
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.project.service.navbar');
|
return view('livewire.project.service.navbar');
|
||||||
}
|
}
|
||||||
|
public function checkStatus() {
|
||||||
public function checkStatus()
|
$this->service->refresh();
|
||||||
{
|
|
||||||
$this->emit('checkStatus');
|
|
||||||
}
|
}
|
||||||
public function deploy()
|
public function deploy()
|
||||||
{
|
{
|
||||||
@@ -29,11 +27,15 @@ class Navbar extends Component
|
|||||||
$activity = StartService::run($this->service);
|
$activity = StartService::run($this->service);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
}
|
}
|
||||||
public function stop()
|
public function stop(bool $forceCleanup = false)
|
||||||
{
|
{
|
||||||
StopService::run($this->service);
|
StopService::run($this->service);
|
||||||
$this->service->refresh();
|
$this->service->refresh();
|
||||||
$this->emit('success', 'Service stopped successfully.');
|
if ($forceCleanup) {
|
||||||
$this->checkStatus();
|
$this->emit('success', 'Force cleanup service successfully.');
|
||||||
|
} else {
|
||||||
|
$this->emit('success', 'Service stopped successfully.');
|
||||||
|
}
|
||||||
|
$this->emit('checkStatus');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class Show extends Component
|
|||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
public Collection $services;
|
public Collection $services;
|
||||||
|
public $s3s;
|
||||||
|
|
||||||
protected $listeners = ['generateDockerCompose'];
|
protected $listeners = ['generateDockerCompose'];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -33,6 +35,7 @@ class Show extends Component
|
|||||||
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
||||||
$this->serviceDatabase->getFilesFromServer();
|
$this->serviceDatabase->getFilesFromServer();
|
||||||
}
|
}
|
||||||
|
$this->s3s = currentTeam()->s3s;
|
||||||
} catch(\Throwable $e) {
|
} catch(\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
use App\Jobs\StopResourceJob;
|
use App\Jobs\DeleteResourceJob;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ class Danger extends Component
|
|||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
StopResourceJob::dispatchSync($this->resource);
|
DeleteResourceJob::dispatchSync($this->resource);
|
||||||
return redirect()->route('project.resources', [
|
return redirect()->route('project.resources', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'environment_name' => $this->parameters['environment_name']
|
'environment_name' => $this->parameters['environment_name']
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class GetLogs extends Component
|
|||||||
public int $numberOfLines = 100;
|
public int $numberOfLines = 100;
|
||||||
public function doSomethingWithThisChunkOfOutput($output)
|
public function doSomethingWithThisChunkOfOutput($output)
|
||||||
{
|
{
|
||||||
$this->outputs .= $output;
|
$this->outputs .= removeAnsiColors($output);
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
@@ -36,6 +36,13 @@ class GetLogs extends Component
|
|||||||
Process::run($sshCommand, function (string $type, string $output) {
|
Process::run($sshCommand, function (string $type, string $output) {
|
||||||
$this->doSomethingWithThisChunkOfOutput($output);
|
$this->doSomethingWithThisChunkOfOutput($output);
|
||||||
});
|
});
|
||||||
|
if ($this->showTimeStamps) {
|
||||||
|
$this->outputs = str($this->outputs)->split('/\n/')->sort(function ($a, $b) {
|
||||||
|
$a = explode(' ', $a);
|
||||||
|
$b = explode(' ', $b);
|
||||||
|
return $a[0] <=> $b[0];
|
||||||
|
})->join("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
|
|||||||
31
app/Http/Livewire/Server/Delete.php
Normal file
31
app/Http/Livewire/Server/Delete.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Server;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Delete extends Component
|
||||||
|
{
|
||||||
|
use AuthorizesRequests;
|
||||||
|
|
||||||
|
public $server;
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->authorize('delete', $this->server);
|
||||||
|
if ($this->server->hasDefinedResources()) {
|
||||||
|
$this->emit('error', 'Server has defined resources. Please delete them first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->server->delete();
|
||||||
|
return redirect()->route('server.all');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.delete');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,10 @@ namespace App\Http\Livewire\Server;
|
|||||||
|
|
||||||
use App\Actions\Server\InstallDocker;
|
use App\Actions\Server\InstallDocker;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Form extends Component
|
class Form extends Component
|
||||||
{
|
{
|
||||||
use AuthorizesRequests;
|
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public bool $isValidConnection = false;
|
public bool $isValidConnection = false;
|
||||||
public bool $isValidDocker = false;
|
public bool $isValidDocker = false;
|
||||||
@@ -32,7 +30,7 @@ class Form extends Component
|
|||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'server.name' => 'Name',
|
'server.name' => 'Name',
|
||||||
'server.description' => 'Description',
|
'server.description' => 'Description',
|
||||||
'server.ip' => 'IP address',
|
'server.ip' => 'IP address/Domain',
|
||||||
'server.user' => 'User',
|
'server.user' => 'User',
|
||||||
'server.port' => 'Port',
|
'server.port' => 'Port',
|
||||||
'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel',
|
'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel',
|
||||||
@@ -106,26 +104,12 @@ class Form extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->authorize('delete', $this->server);
|
|
||||||
if (!$this->server->isEmpty()) {
|
|
||||||
$this->emit('error', 'Server has defined resources. Please delete them first.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->server->delete();
|
|
||||||
return redirect()->route('server.all');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
if(isCloud() && !isDev()) {
|
if (isCloud() && !isDev()) {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'server.ip' => 'required|ip',
|
'server.ip' => 'required',
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ class ByIp extends Component
|
|||||||
protected $rules = [
|
protected $rules = [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'description' => 'nullable|string',
|
'description' => 'nullable|string',
|
||||||
'ip' => 'required|ip',
|
'ip' => 'required',
|
||||||
'user' => 'required|string',
|
'user' => 'required|string',
|
||||||
'port' => 'required|integer',
|
'port' => 'required|integer',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'name' => 'Name',
|
'name' => 'Name',
|
||||||
'description' => 'Description',
|
'description' => 'Description',
|
||||||
'ip' => 'IP Address',
|
'ip' => 'IP Address/Domain',
|
||||||
'user' => 'User',
|
'user' => 'User',
|
||||||
'port' => 'Port',
|
'port' => 'Port',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ class Backup extends Component
|
|||||||
]);
|
]);
|
||||||
$this->database->refresh();
|
$this->database->refresh();
|
||||||
$this->backup->refresh();
|
$this->backup->refresh();
|
||||||
ray($this->backup);
|
|
||||||
$this->s3s = S3Storage::whereTeamId(0)->get();
|
$this->s3s = S3Storage::whereTeamId(0)->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,17 +3,18 @@
|
|||||||
namespace App\Http\Livewire\Source\Github;
|
namespace App\Http\Livewire\Source\Github;
|
||||||
|
|
||||||
use App\Models\GithubApp;
|
use App\Models\GithubApp;
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Change extends Component
|
class Change extends Component
|
||||||
{
|
{
|
||||||
public string $webhook_endpoint;
|
public string $webhook_endpoint;
|
||||||
public string|null $ipv4;
|
public ?string $ipv4;
|
||||||
public string|null $ipv6;
|
public ?string $ipv6;
|
||||||
public string|null $fqdn;
|
public ?string $fqdn;
|
||||||
|
|
||||||
public bool|null $default_permissions = true;
|
public ?bool $default_permissions = true;
|
||||||
public bool|null $preview_deployment_permissions = true;
|
public ?bool $preview_deployment_permissions = true;
|
||||||
|
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public GithubApp $github_app;
|
public GithubApp $github_app;
|
||||||
@@ -28,29 +29,68 @@ class Change extends Component
|
|||||||
'github_app.custom_user' => 'required|string',
|
'github_app.custom_user' => 'required|string',
|
||||||
'github_app.custom_port' => 'required|int',
|
'github_app.custom_port' => 'required|int',
|
||||||
'github_app.app_id' => 'required|int',
|
'github_app.app_id' => 'required|int',
|
||||||
'github_app.installation_id' => 'nullable',
|
'github_app.installation_id' => 'required|int',
|
||||||
'github_app.client_id' => 'nullable',
|
'github_app.client_id' => 'required|string',
|
||||||
'github_app.client_secret' => 'nullable',
|
'github_app.client_secret' => 'required|string',
|
||||||
'github_app.webhook_secret' => 'nullable',
|
'github_app.webhook_secret' => 'required|string',
|
||||||
'github_app.is_system_wide' => 'required|bool',
|
'github_app.is_system_wide' => 'required|bool',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
$github_app_uuid = request()->github_app_uuid;
|
||||||
|
$this->github_app = GithubApp::where('uuid', $github_app_uuid)->first();
|
||||||
|
if (!$this->github_app) {
|
||||||
|
return redirect()->route('source.all');
|
||||||
|
}
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||||
|
|
||||||
|
$this->name = str($this->github_app->name)->kebab();
|
||||||
|
$this->fqdn = $settings->fqdn;
|
||||||
|
|
||||||
|
if ($settings->public_ipv4) {
|
||||||
|
$this->ipv4 = 'http://' . $settings->public_ipv4 . ':' . config('app.port');
|
||||||
|
}
|
||||||
|
if ($settings->public_ipv6) {
|
||||||
|
$this->ipv6 = 'http://' . $settings->public_ipv6 . ':' . config('app.port');
|
||||||
|
}
|
||||||
|
if ($this->github_app->installation_id && session('from')) {
|
||||||
|
$source_id = data_get(session('from'), 'source_id');
|
||||||
|
if (!$source_id || $this->github_app->id !== $source_id) {
|
||||||
|
session()->forget('from');
|
||||||
|
} else {
|
||||||
|
$parameters = data_get(session('from'), 'parameters');
|
||||||
|
$back = data_get(session('from'), 'back');
|
||||||
|
$environment_name = data_get($parameters, 'environment_name');
|
||||||
|
$project_uuid = data_get($parameters, 'project_uuid');
|
||||||
|
$type = data_get($parameters, 'type');
|
||||||
|
$destination = data_get($parameters, 'destination');
|
||||||
|
session()->forget('from');
|
||||||
|
return redirect()->route($back, [
|
||||||
|
'environment_name' => $environment_name,
|
||||||
|
'project_uuid' => $project_uuid,
|
||||||
|
'type' => $type,
|
||||||
|
'destination' => $destination,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
if (isCloud() && !isDev()) {
|
if (isCloud() && !isDev()) {
|
||||||
$this->webhook_endpoint = config('app.url');
|
$this->webhook_endpoint = config('app.url');
|
||||||
} else {
|
} else {
|
||||||
$this->webhook_endpoint = $this->ipv4;
|
$this->webhook_endpoint = $this->ipv4;
|
||||||
$this->is_system_wide = $this->github_app->is_system_wide;
|
$this->is_system_wide = $this->github_app->is_system_wide;
|
||||||
}
|
}
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->github_app->save();
|
$this->github_app->save();
|
||||||
|
$this->emit('success', 'Github App updated successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -58,6 +98,7 @@ class Change extends Component
|
|||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
|
$this->submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private int $pull_request_id;
|
private int $pull_request_id;
|
||||||
private string $commit;
|
private string $commit;
|
||||||
private bool $force_rebuild;
|
private bool $force_rebuild;
|
||||||
|
private bool $restart_only;
|
||||||
|
|
||||||
private ?string $dockerImage = null;
|
private ?string $dockerImage = null;
|
||||||
private ?string $dockerImageTag = null;
|
private ?string $dockerImageTag = null;
|
||||||
@@ -68,6 +69,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private $docker_compose_base64;
|
private $docker_compose_base64;
|
||||||
private string $dockerfile_location = '/Dockerfile';
|
private string $dockerfile_location = '/Dockerfile';
|
||||||
private ?string $addHosts = null;
|
private ?string $addHosts = null;
|
||||||
|
private ?string $buildTarget = null;
|
||||||
private $log_model;
|
private $log_model;
|
||||||
private Collection $saved_outputs;
|
private Collection $saved_outputs;
|
||||||
|
|
||||||
@@ -94,6 +96,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->pull_request_id = $this->application_deployment_queue->pull_request_id;
|
$this->pull_request_id = $this->application_deployment_queue->pull_request_id;
|
||||||
$this->commit = $this->application_deployment_queue->commit;
|
$this->commit = $this->application_deployment_queue->commit;
|
||||||
$this->force_rebuild = $this->application_deployment_queue->force_rebuild;
|
$this->force_rebuild = $this->application_deployment_queue->force_rebuild;
|
||||||
|
$this->restart_only = $this->application_deployment_queue->restart_only;
|
||||||
|
|
||||||
$source = data_get($this->application, 'source');
|
$source = data_get($this->application, 'source');
|
||||||
if ($source) {
|
if ($source) {
|
||||||
@@ -136,9 +139,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
// ray()->measure();
|
// ray()->measure();
|
||||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id);
|
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
|
||||||
if ($containers->count() > 0) {
|
if ($containers->count() === 1) {
|
||||||
$this->currently_running_container_name = data_get($containers[0], 'Names');
|
$this->currently_running_container_name = data_get($containers[0], 'Names');
|
||||||
|
} else {
|
||||||
|
$foundContainer = $containers->filter(function ($container) {
|
||||||
|
return !str(data_get($container, 'Names'))->startsWith("{$this->application->uuid}-pr-");
|
||||||
|
})->first();
|
||||||
|
if ($foundContainer) {
|
||||||
|
$this->currently_running_container_name = data_get($foundContainer, 'Names');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($this->pull_request_id !== 0 && $this->pull_request_id !== null) {
|
if ($this->pull_request_id !== 0 && $this->pull_request_id !== null) {
|
||||||
$this->currently_running_container_name = $this->container_name;
|
$this->currently_running_container_name = $this->container_name;
|
||||||
@@ -169,6 +179,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
return "--add-host $name:$ip";
|
return "--add-host $name:$ip";
|
||||||
})->implode(' ');
|
})->implode(' ');
|
||||||
|
|
||||||
|
if ($this->application->dockerfile_target_build) {
|
||||||
|
$this->buildTarget = " --target {$this->application->dockerfile_target_build} ";
|
||||||
|
}
|
||||||
|
|
||||||
// Get user home directory
|
// Get user home directory
|
||||||
$this->serverUserHomeDir = instant_remote_process(["echo \$HOME"], $this->server);
|
$this->serverUserHomeDir = instant_remote_process(["echo \$HOME"], $this->server);
|
||||||
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
|
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
|
||||||
@@ -182,7 +196,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->application->git_repository = "$gitHost:$gitRepo";
|
$this->application->git_repository = "$gitHost:$gitRepo";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if ($this->application->dockerfile) {
|
if ($this->restart_only) {
|
||||||
|
$this->just_restart();
|
||||||
|
} else if ($this->application->dockerfile) {
|
||||||
$this->deploy_simple_dockerfile();
|
$this->deploy_simple_dockerfile();
|
||||||
} else if ($this->application->build_pack === 'dockerimage') {
|
} else if ($this->application->build_pack === 'dockerimage') {
|
||||||
$this->deploy_dockerimage_buildpack();
|
$this->deploy_dockerimage_buildpack();
|
||||||
@@ -264,6 +280,48 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
// [executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
private function generate_image_names()
|
||||||
|
{
|
||||||
|
if ($this->application->dockerfile) {
|
||||||
|
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
||||||
|
$this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
||||||
|
} else if ($this->application->build_pack === 'dockerimage') {
|
||||||
|
$this->production_image_name = Str::lower("{$this->dockerImage}:{$this->dockerImageTag}");
|
||||||
|
} else if ($this->pull_request_id !== 0) {
|
||||||
|
$this->build_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}-build");
|
||||||
|
$this->production_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}");
|
||||||
|
} else {
|
||||||
|
$tag = Str::of("{$this->commit}-{$this->application->id}-{$this->pull_request_id}");
|
||||||
|
if (strlen($tag) > 128) {
|
||||||
|
$tag = $tag->substr(0, 128);
|
||||||
|
}
|
||||||
|
$this->build_image_name = Str::lower("{$this->application->git_repository}:{$tag}-build");
|
||||||
|
$this->production_image_name = Str::lower("{$this->application->uuid}:{$tag}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function just_restart()
|
||||||
|
{
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}.'"
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$this->prepare_builder_image();
|
||||||
|
$this->check_git_if_build_needed();
|
||||||
|
$this->set_base_dir();
|
||||||
|
$this->generate_image_names();
|
||||||
|
$this->execute_remote_command([
|
||||||
|
"docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found"
|
||||||
|
]);
|
||||||
|
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
||||||
|
$this->generate_compose_file();
|
||||||
|
$this->rolling_update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->execute_remote_command([
|
||||||
|
"echo 'Cannot find image {$this->production_image_name} locally. Please redeploy the application.'",
|
||||||
|
]);
|
||||||
|
}
|
||||||
private function save_environment_variables()
|
private function save_environment_variables()
|
||||||
{
|
{
|
||||||
$envs = collect([]);
|
$envs = collect([]);
|
||||||
@@ -291,9 +349,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > $this->workdir/Dockerfile")
|
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > $this->workdir/Dockerfile")
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
$this->generate_image_names();
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
|
||||||
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->generate_build_env_variables();
|
$this->generate_build_env_variables();
|
||||||
$this->add_build_env_variables_to_dockerfile();
|
$this->add_build_env_variables_to_dockerfile();
|
||||||
@@ -311,7 +367,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
"echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag}.'"
|
"echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag}.'"
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$this->production_image_name = Str::lower("{$this->dockerImage}:{$this->dockerImageTag}");
|
$this->generate_image_names();
|
||||||
$this->prepare_builder_image();
|
$this->prepare_builder_image();
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->rolling_update();
|
$this->rolling_update();
|
||||||
@@ -328,16 +384,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
$this->prepare_builder_image();
|
$this->prepare_builder_image();
|
||||||
|
$this->check_git_if_build_needed();
|
||||||
$this->clone_repository();
|
$this->clone_repository();
|
||||||
$this->set_base_dir();
|
$this->set_base_dir();
|
||||||
$tag = Str::of("{$this->commit}-{$this->application->id}-{$this->pull_request_id}");
|
$this->generate_image_names();
|
||||||
if (strlen($tag) > 128) {
|
|
||||||
$tag = $tag->substr(0, 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->build_image_name = Str::lower("{$this->application->git_repository}:{$tag}-build");
|
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:{$tag}");
|
|
||||||
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
|
||||||
$this->cleanup_git();
|
$this->cleanup_git();
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->generate_build_env_variables();
|
$this->generate_build_env_variables();
|
||||||
@@ -355,15 +405,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->prepare_builder_image();
|
$this->prepare_builder_image();
|
||||||
$this->check_git_if_build_needed();
|
$this->check_git_if_build_needed();
|
||||||
$this->set_base_dir();
|
$this->set_base_dir();
|
||||||
$tag = Str::of("{$this->commit}-{$this->application->id}-{$this->pull_request_id}");
|
$this->generate_image_names();
|
||||||
if (strlen($tag) > 128) {
|
|
||||||
$tag = $tag->substr(0, 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->build_image_name = Str::lower("{$this->application->git_repository}:{$tag}-build");
|
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:{$tag}");
|
|
||||||
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
|
||||||
|
|
||||||
if (!$this->force_rebuild) {
|
if (!$this->force_rebuild) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found"
|
"docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found"
|
||||||
@@ -396,7 +438,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
if (count($this->application->ports_mappings_array) > 0) {
|
if (count($this->application->ports_mappings_array) > 0) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Application has ports mapped to the host system, rolling update is not supported. Stopping current container.'"],
|
["echo -n 'Application has ports mapped to the host system, rolling update is not supported.'"],
|
||||||
);
|
);
|
||||||
$this->stop_running_container(force: true);
|
$this->stop_running_container(force: true);
|
||||||
$this->start_by_compose_file();
|
$this->start_by_compose_file();
|
||||||
@@ -457,9 +499,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
private function deploy_pull_request()
|
private function deploy_pull_request()
|
||||||
{
|
{
|
||||||
$this->build_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}-build");
|
$this->generate_image_names();
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}");
|
|
||||||
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"echo 'Starting pull request (#{$this->pull_request_id}) deployment of {$this->application->git_repository}:{$this->application->git_branch}.'",
|
"echo 'Starting pull request (#{$this->pull_request_id}) deployment of {$this->application->git_repository}:{$this->application->git_branch}.'",
|
||||||
]);
|
]);
|
||||||
@@ -475,7 +515,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// $this->generate_build_env_variables();
|
// $this->generate_build_env_variables();
|
||||||
// $this->add_build_env_variables_to_dockerfile();
|
// $this->add_build_env_variables_to_dockerfile();
|
||||||
$this->build_image();
|
$this->build_image();
|
||||||
$this->stop_running_container();
|
if ($this->currently_running_container_name) {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Removing old version of your application.'"],
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Starting preview deployment.'"],
|
["echo -n 'Starting preview deployment.'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
||||||
@@ -516,24 +561,38 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function check_git_if_build_needed()
|
private function check_git_if_build_needed()
|
||||||
{
|
{
|
||||||
$this->generate_git_import_commands();
|
$this->generate_git_import_commands();
|
||||||
$private_key = base64_encode($this->application->private_key->private_key);
|
$private_key = data_get($this->application, 'private_key.private_key');
|
||||||
$this->execute_remote_command(
|
if ($private_key) {
|
||||||
[
|
$private_key = base64_encode($private_key);
|
||||||
executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh")
|
$this->execute_remote_command(
|
||||||
],
|
[
|
||||||
[
|
executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh")
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa")
|
],
|
||||||
],
|
[
|
||||||
[
|
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa")
|
||||||
executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa")
|
],
|
||||||
],
|
[
|
||||||
[
|
executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa")
|
||||||
executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->fullRepoUrl} {$this->branch}"),
|
],
|
||||||
"hidden" => true,
|
[
|
||||||
"save" => "git_commit_sha"
|
executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->fullRepoUrl} {$this->branch}"),
|
||||||
],
|
"hidden" => true,
|
||||||
);
|
"save" => "git_commit_sha"
|
||||||
$this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t");
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$this->branch}"),
|
||||||
|
"hidden" => true,
|
||||||
|
"save" => "git_commit_sha"
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->saved_outputs->get('git_commit_sha')) {
|
||||||
|
$this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private function clone_repository()
|
private function clone_repository()
|
||||||
{
|
{
|
||||||
@@ -584,7 +643,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
if ($this->application->deploymentType() === 'deploy_key') {
|
if ($this->application->deploymentType() === 'deploy_key') {
|
||||||
$this->fullRepoUrl = $this->application->git_repository;
|
$this->fullRepoUrl = $this->application->git_repository;
|
||||||
$private_key = base64_encode($this->application->private_key->private_key);
|
$private_key = data_get($this->application, 'private_key.private_key');
|
||||||
|
if (is_null($private_key)) {
|
||||||
|
throw new Exception('Private key not found. Please add a private key to the application and try again.');
|
||||||
|
}
|
||||||
|
$private_key = base64_encode($private_key);
|
||||||
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->application->git_repository} {$this->basedir}";
|
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->application->git_repository} {$this->basedir}";
|
||||||
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
||||||
$commands = collect([
|
$commands = collect([
|
||||||
@@ -647,7 +710,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function nixpacks_build_cmd()
|
private function nixpacks_build_cmd()
|
||||||
{
|
{
|
||||||
$this->generate_env_variables();
|
$this->generate_env_variables();
|
||||||
$nixpacks_command = "nixpacks build --no-cache -o {$this->workdir} {$this->env_args} --no-error-without-start";
|
$nixpacks_command = "nixpacks build --cache-key '{$this->application->uuid}' -o {$this->workdir} {$this->env_args} --no-error-without-start";
|
||||||
if ($this->application->build_command) {
|
if ($this->application->build_command) {
|
||||||
$nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
|
$nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
|
||||||
}
|
}
|
||||||
@@ -695,6 +758,23 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} else {
|
} else {
|
||||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||||
}
|
}
|
||||||
|
if ($this->pull_request_id !== 0) {
|
||||||
|
$newLabels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||||
|
$newHostLabel = $newLabels->filter(function ($label) {
|
||||||
|
return str($label)->contains('Host');
|
||||||
|
});
|
||||||
|
$labels = $labels->reject(function ($label) {
|
||||||
|
return str($label)->contains('Host');
|
||||||
|
});
|
||||||
|
|
||||||
|
$labels = $labels->map(function ($label) {
|
||||||
|
$pattern = '/([a-zA-Z0-9]+)-(\d+)-(http|https)/';
|
||||||
|
$replacement = "$1-pr-{$this->pull_request_id}-$2-$3";
|
||||||
|
$newLabel = preg_replace($pattern, $replacement, $label);
|
||||||
|
return $newLabel;
|
||||||
|
});
|
||||||
|
$labels = $labels->merge($newHostLabel);
|
||||||
|
}
|
||||||
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
|
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
'version' => '3.8',
|
||||||
@@ -846,7 +926,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
if ($this->application->settings->is_static) {
|
if ($this->application->settings->is_static) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}/{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}/{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||||
@@ -884,7 +964,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -895,12 +975,12 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
if ($this->newVersionIsHealthy || $force) {
|
if ($this->newVersionIsHealthy || $force) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Removing old version of your application.'"],
|
["echo -n 'Removing old version of your application.'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'New version is not healthy, rolling back to the old version.'"],
|
["echo -n 'New version is not healthy, rolling back to the old version.'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class CheckResaleLicenseJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
resolve(CheckResaleLicense::class)();
|
CheckResaleLicense::run();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('CheckResaleLicenseJob failed with: ' . $e->getMessage());
|
send_internal_notification('CheckResaleLicenseJob failed with: ' . $e->getMessage());
|
||||||
ray($e);
|
ray($e);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ use Illuminate\Queue\InteractsWithQueue;
|
|||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
@@ -26,6 +25,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server)
|
||||||
{
|
{
|
||||||
|
$this->handle();
|
||||||
}
|
}
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
@@ -58,6 +58,23 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->server->update([
|
$this->server->update([
|
||||||
'unreachable_count' => 0,
|
'unreachable_count' => 0,
|
||||||
]);
|
]);
|
||||||
|
// Update all applications, databases and services to exited
|
||||||
|
foreach ($this->server->applications() as $application) {
|
||||||
|
$application->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
foreach ($this->server->databases() as $database) {
|
||||||
|
$database->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
foreach ($this->server->services() as $service) {
|
||||||
|
$apps = $service->applications()->get();
|
||||||
|
$dbs = $service->databases()->get();
|
||||||
|
foreach ($apps as $app) {
|
||||||
|
$app->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
foreach ($dbs as $db) {
|
||||||
|
$db->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$result = $this->server->validateConnection();
|
$result = $this->server->validateConnection();
|
||||||
@@ -138,11 +155,10 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$containerStatus = "$containerStatus ($containerHealth)";
|
$containerStatus = "$containerStatus ($containerHealth)";
|
||||||
$labels = data_get($container, 'Config.Labels');
|
$labels = data_get($container, 'Config.Labels');
|
||||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||||
$labelId = data_get($labels, 'coolify.applicationId');
|
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||||
if ($labelId) {
|
if ($applicationId) {
|
||||||
if (str_contains($labelId, '-pr-')) {
|
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||||
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
if ($pullRequestId) {
|
||||||
$applicationId = (int) Str::before($labelId, '-pr-');
|
|
||||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||||
if ($preview) {
|
if ($preview) {
|
||||||
$foundApplicationPreviews[] = $preview->id;
|
$foundApplicationPreviews[] = $preview->id;
|
||||||
@@ -154,7 +170,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
//Notify user that this container should not be there.
|
//Notify user that this container should not be there.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$application = $applications->where('id', $labelId)->first();
|
$application = $applications->where('id', $applicationId)->first();
|
||||||
if ($application) {
|
if ($application) {
|
||||||
$foundApplications[] = $application->id;
|
$foundApplications[] = $application->id;
|
||||||
$statusFromDb = $application->status;
|
$statusFromDb = $application->status;
|
||||||
@@ -230,10 +246,13 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$name = data_get($exitedService, 'name');
|
$name = data_get($exitedService, 'name');
|
||||||
$fqdn = data_get($exitedService, 'fqdn');
|
$fqdn = data_get($exitedService, 'fqdn');
|
||||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
$project = data_get($service, 'environment.project');
|
$projectUuid = data_get($service, 'environment.project.uuid');
|
||||||
$environment = data_get($service, 'environment');
|
$serviceUuid = data_get($service, 'uuid');
|
||||||
|
$environmentName = data_get($service, 'environment.name');
|
||||||
|
|
||||||
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/service/" . $service->uuid;
|
if ($projectUuid && $serviceUuid && $environmentName) {
|
||||||
|
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid;
|
||||||
|
}
|
||||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
$exitedService->update(['status' => 'exited']);
|
$exitedService->update(['status' => 'exited']);
|
||||||
}
|
}
|
||||||
@@ -251,10 +270,13 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
|
||||||
$project = data_get($application, 'environment.project');
|
$projectUuid = data_get($application, 'environment.project.uuid');
|
||||||
$environment = data_get($application, 'environment');
|
$applicationUuid = data_get($application, 'uuid');
|
||||||
|
$environment = data_get($application, 'environment.name');
|
||||||
|
|
||||||
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/application/" . $application->uuid;
|
if ($projectUuid && $applicationUuid && $environment) {
|
||||||
|
$url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid;
|
||||||
|
}
|
||||||
|
|
||||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
}
|
}
|
||||||
@@ -271,10 +293,14 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
|
||||||
$project = data_get($preview, 'application.environment.project');
|
$projectUuid = data_get($preview, 'application.environment.project.uuid');
|
||||||
$environment = data_get($preview, 'application.environment');
|
$environmentName = data_get($preview, 'application.environment.name');
|
||||||
|
$applicationUuid = data_get($preview, 'application.uuid');
|
||||||
|
|
||||||
|
if ($projectUuid && $applicationUuid && $environmentName) {
|
||||||
|
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid;
|
||||||
|
}
|
||||||
|
|
||||||
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/application/" . $preview->application->uuid;
|
|
||||||
$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);
|
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
||||||
@@ -290,10 +316,13 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
$containerName = $name;
|
$containerName = $name;
|
||||||
|
|
||||||
$project = data_get($database, 'environment.project');
|
$projectUuid = data_get($database, 'environment.project.uuid');
|
||||||
$environment = data_get($database, 'environment');
|
$environmentName = data_get($database, 'environment.name');
|
||||||
|
$databaseUuid = data_get($database, 'uuid');
|
||||||
|
|
||||||
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/database/" . $database->uuid;
|
if ($projectUuid && $databaseUuid && $environmentName) {
|
||||||
|
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid;
|
||||||
|
}
|
||||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Actions\Database\StopDatabase;
|
||||||
use App\Models\S3Storage;
|
use App\Models\S3Storage;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledDatabaseBackupExecution;
|
use App\Models\ScheduledDatabaseBackupExecution;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -22,6 +24,7 @@ use Illuminate\Queue\InteractsWithQueue;
|
|||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
@@ -30,7 +33,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public ?Team $team = null;
|
public ?Team $team = null;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public ScheduledDatabaseBackup $backup;
|
public ScheduledDatabaseBackup $backup;
|
||||||
public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database;
|
public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database;
|
||||||
|
|
||||||
public ?string $container_name = null;
|
public ?string $container_name = null;
|
||||||
public ?ScheduledDatabaseBackupExecution $backup_log = null;
|
public ?ScheduledDatabaseBackupExecution $backup_log = null;
|
||||||
@@ -46,9 +49,15 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
$this->backup = $backup;
|
$this->backup = $backup;
|
||||||
$this->team = Team::find($backup->team_id);
|
$this->team = Team::find($backup->team_id);
|
||||||
$this->database = data_get($this->backup, 'database');
|
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||||
$this->server = $this->database->destination->server;
|
$this->database = data_get($this->backup, 'database');
|
||||||
$this->s3 = $this->backup->s3;
|
$this->server = $this->database->service->server;
|
||||||
|
$this->s3 = $this->backup->s3;
|
||||||
|
} else {
|
||||||
|
$this->database = data_get($this->backup, 'database');
|
||||||
|
$this->server = $this->database->destination->server;
|
||||||
|
$this->s3 = $this->backup->s3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
@@ -64,13 +73,45 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
// Check if team is exists
|
||||||
|
if (is_null($this->team)) {
|
||||||
|
$this->backup->update(['status' => 'failed']);
|
||||||
|
StopDatabase::run($this->database);
|
||||||
|
$this->database->delete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
$status = Str::of(data_get($this->database, 'status'));
|
$status = Str::of(data_get($this->database, 'status'));
|
||||||
if (!$status->startsWith('running') && $this->database->id !== 0) {
|
if (!$status->startsWith('running') && $this->database->id !== 0) {
|
||||||
ray('database not running');
|
ray('database not running');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$databaseType = $this->database->type();
|
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||||
$databasesToBackup = data_get($this->backup, 'databases_to_backup');
|
$databaseType = $this->database->databaseType();
|
||||||
|
$serviceUuid = $this->database->service->uuid;
|
||||||
|
if ($databaseType === 'standalone-postgresql') {
|
||||||
|
$this->container_name = "postgresql-$serviceUuid";
|
||||||
|
$commands[] = "docker exec $this->container_name env | grep POSTGRES_";
|
||||||
|
$envs = instant_remote_process($commands, $this->server);
|
||||||
|
$databasesToBackup = Str::of($envs)->after('POSTGRES_DB=')->before("\n")->value();
|
||||||
|
$this->database->postgres_user = Str::of($envs)->after('POSTGRES_USER=')->before("\n")->value();
|
||||||
|
} else if ($databaseType === 'standalone-mysql') {
|
||||||
|
$this->container_name = "mysql-$serviceUuid";
|
||||||
|
$commands[] = "docker exec $this->container_name env | grep MYSQL_";
|
||||||
|
$envs = instant_remote_process($commands, $this->server);
|
||||||
|
$databasesToBackup = Str::of($envs)->after('MYSQL_DATABASE=')->before("\n")->value();
|
||||||
|
$this->database->mysql_root_password = Str::of($envs)->after('MYSQL_ROOT_PASSWORD=')->before("\n")->value();
|
||||||
|
} else if ($databaseType === 'standalone-mariadb') {
|
||||||
|
$this->container_name = "mariadb-$serviceUuid";
|
||||||
|
$commands[] = "docker exec $this->container_name env | grep MARIADB_";
|
||||||
|
$envs = instant_remote_process($commands, $this->server);
|
||||||
|
$databasesToBackup = Str::of($envs)->after('MARIADB_DATABASE=')->before("\n")->value();
|
||||||
|
$this->database->mysql_root_password = Str::of($envs)->after('MARIADB_ROOT_PASSWORD=')->before("\n")->value();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->container_name = $this->database->uuid;
|
||||||
|
$databaseType = $this->database->type();
|
||||||
|
$databasesToBackup = data_get($this->backup, 'databases_to_backup');
|
||||||
|
}
|
||||||
|
|
||||||
if (is_null($databasesToBackup)) {
|
if (is_null($databasesToBackup)) {
|
||||||
if ($databaseType === 'standalone-postgresql') {
|
if ($databaseType === 'standalone-postgresql') {
|
||||||
@@ -106,7 +147,6 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->container_name = $this->database->uuid;
|
|
||||||
$this->backup_dir = backup_dir() . "/databases/" . Str::of($this->team->name)->slug() . '-' . $this->team->id . '/' . $this->container_name;
|
$this->backup_dir = backup_dir() . "/databases/" . Str::of($this->team->name)->slug() . '-' . $this->team->id . '/' . $this->container_name;
|
||||||
|
|
||||||
if ($this->database->name === 'coolify-db') {
|
if ($this->database->name === 'coolify-db') {
|
||||||
@@ -304,7 +344,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->backup->number_of_backups_locally === 0) {
|
if ($this->backup->number_of_backups_locally === 0) {
|
||||||
$deletable = $this->backup->executions()->where('status', 'success');
|
$deletable = $this->backup->executions()->where('status', 'success');
|
||||||
} else {
|
} else {
|
||||||
$deletable = $this->backup->executions()->where('status', 'success')->orderByDesc('created_at')->skip($this->backup->number_of_backups_locally);
|
$deletable = $this->backup->executions()->where('status', 'success')->orderByDesc('created_at')->skip($this->backup->number_of_backups_locally - 1);
|
||||||
}
|
}
|
||||||
foreach ($deletable->get() as $execution) {
|
foreach ($deletable->get() as $execution) {
|
||||||
delete_backup_locally($execution->filename, $this->server);
|
delete_backup_locally($execution->filename, $this->server);
|
||||||
@@ -324,8 +364,12 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$bucket = $this->s3->bucket;
|
$bucket = $this->s3->bucket;
|
||||||
$endpoint = $this->s3->endpoint;
|
$endpoint = $this->s3->endpoint;
|
||||||
$this->s3->testConnection();
|
$this->s3->testConnection();
|
||||||
$commands[] = "docker run --pull=always -d --network {$this->database->destination->network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper >/dev/null 2>&1";
|
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||||
|
$network = $this->database->service->destination->network;
|
||||||
|
} else {
|
||||||
|
$network = $this->database->destination->network;
|
||||||
|
}
|
||||||
|
$commands[] = "docker run --pull=always -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
|
||||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
||||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
||||||
instant_remote_process($commands, $this->server);
|
instant_remote_process($commands, $this->server);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
|||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class StopResourceJob implements ShouldQueue, ShouldBeEncrypted
|
class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ class StopResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
try {
|
try {
|
||||||
$server = $this->resource->destination->server;
|
$server = $this->resource->destination->server;
|
||||||
if (!$server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
|
$this->resource->delete();
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
switch ($this->resource->type()) {
|
switch ($this->resource->type()) {
|
||||||
@@ -57,11 +58,10 @@ class StopResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
StopService::run($this->resource);
|
StopService::run($this->resource);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
$this->resource->delete();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
} finally {
|
|
||||||
$this->resource->delete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
@@ -22,7 +21,7 @@ class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
|
return [(new WithoutOverlapping($this->server->uuid))];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uniqueId(): string
|
public function uniqueId(): string
|
||||||
|
|||||||
@@ -46,11 +46,12 @@ class SendMessageToTelegramJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if (!empty($this->buttons)) {
|
if (!empty($this->buttons)) {
|
||||||
foreach ($this->buttons as $button) {
|
foreach ($this->buttons as $button) {
|
||||||
$buttonUrl = data_get($button, 'url');
|
$buttonUrl = data_get($button, 'url');
|
||||||
|
$text = data_get($button, 'text', 'Click here');
|
||||||
if ($buttonUrl && Str::contains($buttonUrl, 'http://localhost')) {
|
if ($buttonUrl && Str::contains($buttonUrl, 'http://localhost')) {
|
||||||
$buttonUrl = str_replace('http://localhost', config('app.url'), $buttonUrl);
|
$buttonUrl = str_replace('http://localhost', config('app.url'), $buttonUrl);
|
||||||
}
|
}
|
||||||
$inlineButtons[] = [
|
$inlineButtons[] = [
|
||||||
'text' => $button['text'],
|
'text' => $text,
|
||||||
'url' => $buttonUrl,
|
'url' => $buttonUrl,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,11 @@ class Application extends BaseModel
|
|||||||
static::deleting(function ($application) {
|
static::deleting(function ($application) {
|
||||||
$application->settings()->delete();
|
$application->settings()->delete();
|
||||||
$storages = $application->persistentStorages()->get();
|
$storages = $application->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
$server = data_get($application, 'destination.server');
|
||||||
instant_remote_process(["docker volume rm -f $storage->name"], $application->destination->server, false);
|
if ($server) {
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$application->persistentStorages()->delete();
|
$application->persistentStorages()->delete();
|
||||||
$application->environment_variables()->delete();
|
$application->environment_variables()->delete();
|
||||||
|
|||||||
@@ -109,11 +109,12 @@ class Server extends BaseModel
|
|||||||
return $this->proxy->modelScope();
|
return $this->proxy->modelScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEmpty()
|
public function hasDefinedResources()
|
||||||
{
|
{
|
||||||
$applications = $this->applications()->count() === 0;
|
$applications = $this->applications()->count() === 0;
|
||||||
$databases = $this->databases()->count() === 0;
|
$databases = $this->databases()->count() === 0;
|
||||||
if ($applications && $databases) {
|
$services = $this->services()->count() === 0;
|
||||||
|
if ($applications || $databases || $services) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ namespace App\Models;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
@@ -23,24 +22,23 @@ class Service extends BaseModel
|
|||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
}
|
}
|
||||||
$application->persistentStorages()->delete();
|
|
||||||
}
|
}
|
||||||
foreach ($service->databases()->get() as $database) {
|
foreach ($service->databases()->get() as $database) {
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
}
|
}
|
||||||
$database->persistentStorages()->delete();
|
|
||||||
}
|
}
|
||||||
$service->environment_variables()->delete();
|
$service->environment_variables()->delete();
|
||||||
$service->applications()->delete();
|
$service->applications()->delete();
|
||||||
$service->databases()->delete();
|
$service->databases()->delete();
|
||||||
if ($storagesToDelete->count() > 0) {
|
|
||||||
$storagesToDelete->each(function ($storage) use ($service) {
|
$server = data_get($service, 'server');
|
||||||
instant_remote_process(["docker volume rm -f $storage->name"], $service->server, false);
|
if ($server && $storagesToDelete->count() > 0) {
|
||||||
|
$storagesToDelete->each(function ($storage) use ($server) {
|
||||||
|
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
instant_remote_process(["docker network rm {$service->uuid}"], $service->server, false);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public function type()
|
public function type()
|
||||||
@@ -90,6 +88,10 @@ class Service extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
|
return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
|
||||||
}
|
}
|
||||||
|
public function environment_variables_preview(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderBy('key', 'asc');
|
||||||
|
}
|
||||||
public function workdir()
|
public function workdir()
|
||||||
{
|
{
|
||||||
return service_configuration_dir() . "/{$this->uuid}";
|
return service_configuration_dir() . "/{$this->uuid}";
|
||||||
@@ -115,7 +117,7 @@ class Service extends BaseModel
|
|||||||
|
|
||||||
public function parse(bool $isNew = false): Collection
|
public function parse(bool $isNew = false): Collection
|
||||||
{
|
{
|
||||||
ray()->clearAll();
|
// ray()->clearAll();
|
||||||
if ($this->docker_compose_raw) {
|
if ($this->docker_compose_raw) {
|
||||||
try {
|
try {
|
||||||
$yaml = Yaml::parse($this->docker_compose_raw);
|
$yaml = Yaml::parse($this->docker_compose_raw);
|
||||||
@@ -254,13 +256,25 @@ class Service extends BaseModel
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$networks = $serviceNetworks->toArray();
|
$networks = collect();
|
||||||
foreach ($definedNetwork as $key => $network) {
|
foreach ($serviceNetworks as $key =>$serviceNetwork) {
|
||||||
$networks = array_merge($networks, [
|
if (gettype($serviceNetwork) === 'string') {
|
||||||
$network
|
// networks:
|
||||||
]);
|
// - appwrite
|
||||||
|
$networks->put($serviceNetwork, null);
|
||||||
|
} else if (gettype($serviceNetwork) === 'array') {
|
||||||
|
// networks:
|
||||||
|
// default:
|
||||||
|
// ipv4_address: 192.168.203.254
|
||||||
|
// $networks->put($serviceNetwork, null);
|
||||||
|
ray($key);
|
||||||
|
$networks->put($key,$serviceNetwork);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data_set($service, 'networks', $networks);
|
foreach ($definedNetwork as $key => $network) {
|
||||||
|
$networks->put($network, null);
|
||||||
|
}
|
||||||
|
data_set($service, 'networks', $networks->toArray());
|
||||||
|
|
||||||
// Collect/create/update volumes
|
// Collect/create/update volumes
|
||||||
if ($serviceVolumes->count() > 0) {
|
if ($serviceVolumes->count() > 0) {
|
||||||
|
|||||||
@@ -11,6 +11,13 @@ class ServiceApplication extends BaseModel
|
|||||||
use HasFactory;
|
use HasFactory;
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
protected static function booted()
|
||||||
|
{
|
||||||
|
static::deleting(function ($service) {
|
||||||
|
$service->persistentStorages()->delete();
|
||||||
|
$service->fileStorages()->delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
public function type()
|
public function type()
|
||||||
{
|
{
|
||||||
return 'service';
|
return 'service';
|
||||||
|
|||||||
@@ -9,10 +9,25 @@ class ServiceDatabase extends BaseModel
|
|||||||
use HasFactory;
|
use HasFactory;
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
|
protected static function booted()
|
||||||
|
{
|
||||||
|
static::deleting(function ($service) {
|
||||||
|
$service->persistentStorages()->delete();
|
||||||
|
$service->fileStorages()->delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
public function type()
|
public function type()
|
||||||
{
|
{
|
||||||
return 'service';
|
return 'service';
|
||||||
}
|
}
|
||||||
|
public function databaseType()
|
||||||
|
{
|
||||||
|
$image = str($this->image)->before(':');
|
||||||
|
if ($image->value() === 'postgres') {
|
||||||
|
$image = 'postgresql';
|
||||||
|
}
|
||||||
|
return "standalone-$image";
|
||||||
|
}
|
||||||
public function service()
|
public function service()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Service::class);
|
return $this->belongsTo(Service::class);
|
||||||
@@ -29,4 +44,8 @@ class ServiceDatabase extends BaseModel
|
|||||||
{
|
{
|
||||||
getFilesystemVolumesFromServer($this, $isInit);
|
getFilesystemVolumesFromServer($this, $isInit);
|
||||||
}
|
}
|
||||||
|
public function scheduledBackups()
|
||||||
|
{
|
||||||
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,6 @@ class StandaloneDocker extends BaseModel
|
|||||||
|
|
||||||
public function attachedTo()
|
public function attachedTo()
|
||||||
{
|
{
|
||||||
return $this->applications?->count() > 0 || $this->databases?->count() > 0;
|
return $this->applications?->count() > 0 || $this->databases()->count() > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,11 @@ class StandaloneMariadb extends BaseModel
|
|||||||
});
|
});
|
||||||
static::deleting(function ($database) {
|
static::deleting(function ($database) {
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
$server = data_get($database, 'destination.server');
|
||||||
instant_remote_process(["docker volume rm -f $storage->name"], $database->destination->server, false);
|
if ($server) {
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$database->scheduledBackups()->delete();
|
$database->scheduledBackups()->delete();
|
||||||
$database->persistentStorages()->delete();
|
$database->persistentStorages()->delete();
|
||||||
|
|||||||
@@ -32,11 +32,14 @@ class StandaloneMongodb extends BaseModel
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
static::deleting(function ($database) {
|
static::deleting(function ($database) {
|
||||||
$database->scheduledBackups()->delete();
|
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
$server = data_get($database, 'destination.server');
|
||||||
instant_remote_process(["docker volume rm -f $storage->name"], $database->destination->server, false);
|
if ($server) {
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
$database->scheduledBackups()->delete();
|
||||||
$database->persistentStorages()->delete();
|
$database->persistentStorages()->delete();
|
||||||
$database->environment_variables()->delete();
|
$database->environment_variables()->delete();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,8 +30,11 @@ class StandaloneMysql extends BaseModel
|
|||||||
});
|
});
|
||||||
static::deleting(function ($database) {
|
static::deleting(function ($database) {
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
$server = data_get($database, 'destination.server');
|
||||||
instant_remote_process(["docker volume rm -f $storage->name"], $database->destination->server, false);
|
if ($server) {
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$database->scheduledBackups()->delete();
|
$database->scheduledBackups()->delete();
|
||||||
$database->persistentStorages()->delete();
|
$database->persistentStorages()->delete();
|
||||||
|
|||||||
@@ -30,8 +30,11 @@ class StandalonePostgresql extends BaseModel
|
|||||||
});
|
});
|
||||||
static::deleting(function ($database) {
|
static::deleting(function ($database) {
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
$server = data_get($database, 'destination.server');
|
||||||
instant_remote_process(["docker volume rm -f $storage->name"], $database->destination->server, false);
|
if ($server) {
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$database->scheduledBackups()->delete();
|
$database->scheduledBackups()->delete();
|
||||||
$database->persistentStorages()->delete();
|
$database->persistentStorages()->delete();
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ class StandaloneRedis extends BaseModel
|
|||||||
static::deleting(function ($database) {
|
static::deleting(function ($database) {
|
||||||
$database->scheduledBackups()->delete();
|
$database->scheduledBackups()->delete();
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
$server = data_get($database, 'destination.server');
|
||||||
instant_remote_process(["docker volume rm -f $storage->name"], $database->destination->server, false);
|
if ($server) {
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$database->persistentStorages()->delete();
|
$database->persistentStorages()->delete();
|
||||||
$database->environment_variables()->delete();
|
$database->environment_variables()->delete();
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class EmailChannel
|
|||||||
if (isset($recepients)) {
|
if (isset($recepients)) {
|
||||||
$message .= implode(', ', $recepients);
|
$message .= implode(', ', $recepients);
|
||||||
}
|
}
|
||||||
$message .= " with subject: {$mailMessage->subject}";
|
if (isset($mailMessage)) {
|
||||||
|
$message .= " with subject: {$mailMessage->subject}";
|
||||||
|
}
|
||||||
send_internal_notification($message);
|
send_internal_notification($message);
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
@@ -49,8 +51,8 @@ class EmailChannel
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
config()->set('mail.from.address', data_get($notifiable, 'smtp_from_address'));
|
config()->set('mail.from.address', data_get($notifiable, 'smtp_from_address', 'test@example.com'));
|
||||||
config()->set('mail.from.name', data_get($notifiable, 'smtp_from_name'));
|
config()->set('mail.from.name', data_get($notifiable, 'smtp_from_name', 'Test'));
|
||||||
if (data_get($notifiable, 'resend_enabled')) {
|
if (data_get($notifiable, 'resend_enabled')) {
|
||||||
config()->set('mail.default', 'resend');
|
config()->set('mail.default', 'resend');
|
||||||
config()->set('resend.api_key', data_get($notifiable, 'resend_api_key'));
|
config()->set('resend.api_key', data_get($notifiable, 'resend_api_key'));
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use App\Jobs\ApplicationDeploymentJob;
|
|||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
|
||||||
function queue_application_deployment(int $application_id, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false)
|
function queue_application_deployment(int $application_id, string $deployment_uuid, int | null $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false)
|
||||||
{
|
{
|
||||||
$deployment = ApplicationDeploymentQueue::create([
|
$deployment = ApplicationDeploymentQueue::create([
|
||||||
'application_id' => $application_id,
|
'application_id' => $application_id,
|
||||||
@@ -12,6 +12,7 @@ function queue_application_deployment(int $application_id, string $deployment_uu
|
|||||||
'pull_request_id' => $pull_request_id,
|
'pull_request_id' => $pull_request_id,
|
||||||
'force_rebuild' => $force_rebuild,
|
'force_rebuild' => $force_rebuild,
|
||||||
'is_webhook' => $is_webhook,
|
'is_webhook' => $is_webhook,
|
||||||
|
'restart_only' => $restart_only,
|
||||||
'commit' => $commit,
|
'commit' => $commit,
|
||||||
]);
|
]);
|
||||||
$queued_deployments = ApplicationDeploymentQueue::where('application_id', $application_id)->where('status', 'queued')->get()->sortByDesc('created_at');
|
$queued_deployments = ApplicationDeploymentQueue::where('application_id', $application_id)->where('status', 'queued')->get()->sortByDesc('created_at');
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ use Illuminate\Support\Str;
|
|||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
function getCurrentApplicationContainerStatus(Server $server, int $id): Collection
|
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null): Collection
|
||||||
{
|
{
|
||||||
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
|
if ($pullRequestId) {
|
||||||
|
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --filter='label=coolify.pullRequestId={$pullRequestId}' --format '{{json .}}' "], $server);
|
||||||
|
} else {
|
||||||
|
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}'"], $server);
|
||||||
|
}
|
||||||
if (!$containers) {
|
if (!$containers) {
|
||||||
return collect([]);
|
return collect([]);
|
||||||
}
|
}
|
||||||
@@ -77,20 +81,6 @@ function executeInDocker(string $containerId, string $command)
|
|||||||
// return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1; [ \$PIPESTATUS -eq 0 ] || exit \$PIPESTATUS'";
|
// return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1; [ \$PIPESTATUS -eq 0 ] || exit \$PIPESTATUS'";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getApplicationContainerStatus(Application $application)
|
|
||||||
{
|
|
||||||
$server = data_get($application, 'destination.server');
|
|
||||||
$id = $application->id;
|
|
||||||
if (!$server) {
|
|
||||||
return 'exited';
|
|
||||||
}
|
|
||||||
$containers = getCurrentApplicationContainerStatus($server, $id);
|
|
||||||
if ($containers->count() > 0) {
|
|
||||||
$status = data_get($containers[0], 'State', 'exited');
|
|
||||||
return $status;
|
|
||||||
}
|
|
||||||
return 'exited';
|
|
||||||
}
|
|
||||||
function getContainerStatus(Server $server, string $container_id, bool $all_data = false, bool $throwError = false)
|
function getContainerStatus(Server $server, string $container_id, bool $all_data = false, bool $throwError = false)
|
||||||
{
|
{
|
||||||
$container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError);
|
$container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError);
|
||||||
@@ -152,6 +142,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
|||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
$labels->push('traefik.enable=true');
|
$labels->push('traefik.enable=true');
|
||||||
foreach ($domains as $loop => $domain) {
|
foreach ($domains as $loop => $domain) {
|
||||||
|
$uuid = new Cuid2(7);
|
||||||
$url = Url::fromString($domain);
|
$url = Url::fromString($domain);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$path = $url->getPath();
|
$path = $url->getPath();
|
||||||
@@ -212,9 +203,9 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
|||||||
$onlyPort = $ports[0];
|
$onlyPort = $ports[0];
|
||||||
}
|
}
|
||||||
$pull_request_id = data_get($preview, 'pull_request_id', 0);
|
$pull_request_id = data_get($preview, 'pull_request_id', 0);
|
||||||
$appId = $application->id;
|
$appUuid = $application->uuid;
|
||||||
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
if ($pull_request_id !== 0) {
|
||||||
$appId = $appId . '-pr-' . $pull_request_id;
|
$appUuid = $appUuid . '-pr-' . $pull_request_id;
|
||||||
}
|
}
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
if ($application->fqdn) {
|
if ($application->fqdn) {
|
||||||
@@ -224,7 +215,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
|||||||
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
||||||
}
|
}
|
||||||
// Add Traefik labels no matter which proxy is selected
|
// Add Traefik labels no matter which proxy is selected
|
||||||
$labels = $labels->merge(fqdnLabelsForTraefik($application->uuid, $domains, $application->settings->is_force_https_enabled, $onlyPort));
|
$labels = $labels->merge(fqdnLabelsForTraefik($appUuid, $domains, $application->settings->is_force_https_enabled, $onlyPort));
|
||||||
}
|
}
|
||||||
return $labels->all();
|
return $labels->all();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,8 +174,11 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
|
|||||||
return $formatted;
|
return $formatted;
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh_server_connection(PrivateKey $private_key)
|
function refresh_server_connection(?PrivateKey $private_key = null)
|
||||||
{
|
{
|
||||||
|
if (is_null($private_key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
foreach ($private_key->servers as $server) {
|
foreach ($private_key->servers as $server) {
|
||||||
Storage::disk('ssh-mux')->delete($server->muxFilename());
|
Storage::disk('ssh-mux')->delete($server->muxFilename());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -501,3 +501,6 @@ function generateDeployWebhook($resource) {
|
|||||||
$url = $api . $endpoint . "?uuid=$uuid&force=false";
|
$url = $api . $endpoint . "?uuid=$uuid&force=false";
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
function removeAnsiColors($text) {
|
||||||
|
return preg_replace('/\e[[][A-Za-z0-9];?[0-9]*m?/', '', $text);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ return [
|
|||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.107',
|
'release' => '4.0.0-beta.112',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.107';
|
return '4.0.0-beta.112';
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('application_deployment_queues', function (Blueprint $table) {
|
||||||
|
$table->boolean('restart_only')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('application_deployment_queues', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('restart_only');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->string('dockerfile_target_build')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('dockerfile_target_build');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -26,7 +26,7 @@ class ApplicationSeeder extends Seeder
|
|||||||
'environment_id' => 1,
|
'environment_id' => 1,
|
||||||
'destination_id' => 0,
|
'destination_id' => 0,
|
||||||
'destination_type' => StandaloneDocker::class,
|
'destination_type' => StandaloneDocker::class,
|
||||||
'source_id' => 0,
|
'source_id' => 1,
|
||||||
'source_type' => GithubApp::class
|
'source_type' => GithubApp::class
|
||||||
]);
|
]);
|
||||||
Application::create([
|
Application::create([
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ services:
|
|||||||
- /data/coolify/_volumes/redis/:/data
|
- /data/coolify/_volumes/redis/:/data
|
||||||
# - coolify-redis-data-dev:/data
|
# - coolify-redis-data-dev:/data
|
||||||
vite:
|
vite:
|
||||||
image: node:19
|
image: node:20
|
||||||
working_dir: /var/www/html
|
working_dir: /var/www/html
|
||||||
ports:
|
ports:
|
||||||
- "${VITE_PORT:-5173}:${VITE_PORT:-5173}"
|
- "${VITE_PORT:-5173}:${VITE_PORT:-5173}"
|
||||||
@@ -60,6 +60,14 @@ services:
|
|||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- /data/coolify/:/data/coolify
|
- /data/coolify/:/data/coolify
|
||||||
# - coolify-data-dev:/data/coolify
|
# - coolify-data-dev:/data/coolify
|
||||||
|
remote-host:
|
||||||
|
<<: *testing-host-base
|
||||||
|
container_name: coolify-remote-host
|
||||||
|
volumes:
|
||||||
|
- /:/host
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /data/coolify/:/data/coolify
|
||||||
|
# - coolify-data-dev:/data/coolify
|
||||||
mailpit:
|
mailpit:
|
||||||
image: "axllent/mailpit:latest"
|
image: "axllent/mailpit:latest"
|
||||||
container_name: coolify-mail
|
container_name: coolify-mail
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ ARG DOCKER_BUILDX_VERSION=0.11.2
|
|||||||
# https://github.com/buildpacks/pack/releases
|
# https://github.com/buildpacks/pack/releases
|
||||||
ARG PACK_VERSION=0.31.0
|
ARG PACK_VERSION=0.31.0
|
||||||
# https://github.com/railwayapp/nixpacks/releases
|
# https://github.com/railwayapp/nixpacks/releases
|
||||||
ARG NIXPACKS_VERSION=1.17.0
|
ARG NIXPACKS_VERSION=1.18.0
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
WORKDIR /artifacts
|
WORKDIR /artifacts
|
||||||
|
|||||||
@@ -121,3 +121,6 @@ tr td:first-child {
|
|||||||
.subtitle {
|
.subtitle {
|
||||||
@apply pt-2 pb-10;
|
@apply pt-2 pb-10;
|
||||||
}
|
}
|
||||||
|
.fullscreen {
|
||||||
|
@apply fixed top-0 left-0 w-full h-full z-[9999] bg-coolgray-100 overflow-y-auto scrollbar pb-4 ;
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
@if ($application->status !== 'exited')
|
@if ($application->status !== 'exited')
|
||||||
<button title="With rolling update if possible" wire:click='deploy' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
<button title="With rolling update if possible" wire:click='deploy' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24" stroke-width="2"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-orange-400" viewBox="0 0 24 24" stroke-width="2"
|
||||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||||
<path
|
<path
|
||||||
@@ -27,6 +27,15 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Redeploy
|
Redeploy
|
||||||
</button>
|
</button>
|
||||||
|
<button title="Restart without rebuilding" wire:click='restart' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
|
<svg class="w-5 h-5 text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||||
|
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747"/>
|
||||||
|
<path d="M20 4v5h-5"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Restart
|
||||||
|
</button>
|
||||||
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
|
||||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<li class="inline-flex items-center">
|
<li class="inline-flex items-center">
|
||||||
<a class="text-xs truncate lg:text-sm"
|
<a class="text-xs truncate lg:text-sm"
|
||||||
href="{{ route('project.show', ['project_uuid' => $this->parameters['project_uuid']]) }}">
|
href="{{ route('project.show', ['project_uuid' => $this->parameters['project_uuid']]) }}">
|
||||||
{{ $resource->environment->project->name }}</a>
|
{{ data_get($resource, 'environment.project.name', 'Undefined Name') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
|||||||
@@ -38,13 +38,13 @@
|
|||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
@if (serviceStatus($service) === 'exited')
|
@if (serviceStatus($service) === 'exited')
|
||||||
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
<button wire:click='stop(true)' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
<svg class="w-5 h-5 " viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-5 h-5 " viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
|
<path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
|
||||||
<path fill="red"
|
<path fill="red"
|
||||||
d="M17.003 20a4.895 4.895 0 0 0-2.404-4.173L22 3l-1.73-1l-7.577 13.126a5.699 5.699 0 0 0-5.243 1.503C3.706 20.24 3.996 28.682 4.01 29.04a1 1 0 0 0 1 .96h14.991a1 1 0 0 0 .6-1.8c-3.54-2.656-3.598-8.146-3.598-8.2Zm-5.073-3.003A3.11 3.11 0 0 1 15.004 20c0 .038.002.208.017.469l-5.9-2.624a3.8 3.8 0 0 1 2.809-.848ZM15.45 28A5.2 5.2 0 0 1 14 25h-2a6.5 6.5 0 0 0 .968 3h-2.223A16.617 16.617 0 0 1 10 24H8a17.342 17.342 0 0 0 .665 4H6c.031-1.836.29-5.892 1.803-8.553l7.533 3.35A13.025 13.025 0 0 0 17.596 28Z" />
|
d="M17.003 20a4.895 4.895 0 0 0-2.404-4.173L22 3l-1.73-1l-7.577 13.126a5.699 5.699 0 0 0-5.243 1.503C3.706 20.24 3.996 28.682 4.01 29.04a1 1 0 0 0 1 .96h14.991a1 1 0 0 0 .6-1.8c-3.54-2.656-3.598-8.146-3.598-8.2Zm-5.073-3.003A3.11 3.11 0 0 1 15.004 20c0 .038.002.208.017.469l-5.9-2.624a3.8 3.8 0 0 1 2.809-.848ZM15.45 28A5.2 5.2 0 0 1 14 25h-2a6.5 6.5 0 0 0 .968 3h-2.223A16.617 16.617 0 0 1 10 24H8a17.342 17.342 0 0 0 .665 4H6c.031-1.836.29-5.892 1.803-8.553l7.533 3.35A13.025 13.025 0 0 0 17.596 28Z" />
|
||||||
</svg>
|
</svg>
|
||||||
Force cleanup containers
|
Force Cleanup Containers
|
||||||
</button>
|
</button>
|
||||||
<button wire:click='deploy' onclick="startService.showModal()"
|
<button wire:click='deploy' onclick="startService.showModal()"
|
||||||
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="pt-4">
|
<div class="pt-4" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }">
|
||||||
<livewire:project.application.deployment-navbar :application_deployment_queue="$application_deployment_queue" />
|
<livewire:project.application.deployment-navbar :application_deployment_queue="$application_deployment_queue" />
|
||||||
@if (data_get($application_deployment_queue, 'status') === 'in_progress')
|
@if (data_get($application_deployment_queue, 'status') === 'in_progress')
|
||||||
<div class="flex items-center gap-1 pt-2 ">Deployment is
|
<div class="flex items-center gap-1 pt-2 ">Deployment is
|
||||||
@@ -6,31 +6,94 @@
|
|||||||
</div>
|
</div>
|
||||||
<x-loading class="loading-ring" />
|
<x-loading class="loading-ring" />
|
||||||
</div>
|
</div>
|
||||||
<div class="">Logs will be updated automatically.</div>
|
{{-- <div class="">Logs will be updated automatically.</div> --}}
|
||||||
@else
|
@else
|
||||||
<div class="pt-2 ">Deployment is <span
|
<div class="pt-2 ">Deployment is <span
|
||||||
class="text-warning">{{ Str::headline(data_get($application_deployment_queue, 'status')) }}</span>.
|
class="text-warning">{{ Str::headline(data_get($application_deployment_queue, 'status')) }}</span>.
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div @if ($isKeepAliveOn) wire:poll.2000ms="polling" @endif
|
<div id="screen" :class="fullscreen ? 'fullscreen' : ''">
|
||||||
class="scrollbar flex flex-col-reverse w-full overflow-y-auto border border-dotted rounded border-coolgray-400 max-h-[32rem] p-2 px-4 mt-4 text-xs">
|
<div @if ($isKeepAliveOn) wire:poll.2000ms="polling" @endif
|
||||||
<span class="flex flex-col">
|
class="relative flex flex-col-reverse w-full p-2 px-4 mt-4 overflow-y-auto scrollbar border-coolgray-400"
|
||||||
@if (decode_remote_command_output($application_deployment_queue)->count() > 0)
|
:class="fullscreen ? '' : 'max-h-[40rem] border border-dotted rounded'">
|
||||||
@foreach (decode_remote_command_output($application_deployment_queue) as $line)
|
<button title="Minimize" x-show="fullscreen" class="fixed top-4 right-4" x-on:click="makeFullscreen"><svg
|
||||||
<div @class([
|
class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
'font-mono whitespace-pre-line',
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
'text-neutral-400' => $line['type'] == 'stdout',
|
stroke-width="2" d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
|
||||||
'text-error' => $line['type'] == 'stderr',
|
</svg></button>
|
||||||
'text-warning' => $line['hidden'],
|
<button title="Go Top" x-show="fullscreen" class="fixed top-4 right-28" x-on:click="goTop"> <svg
|
||||||
])>[{{ $line['timestamp'] }}] @if ($line['hidden'])
|
class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<br>COMMAND: <br>{{ $line['command'] }} <br><br>OUTPUT:
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
@endif{{ $line['output'] }}@if ($line['hidden'])
|
stroke-width="2" d="M12 5v14m4-10l-4-4M8 9l4-4" />
|
||||||
@endif
|
</svg></button>
|
||||||
</div>
|
<button title="Follow Logs" x-show="fullscreen" :class="alwaysScroll ? 'text-warning' : ''"
|
||||||
@endforeach
|
class="fixed top-4 right-16" x-on:click="toggleScroll"><svg class="icon" viewBox="0 0 24 24"
|
||||||
@else
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<span class="font-mono text-neutral-400">No logs yet.</span>
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
@endif
|
stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
|
||||||
</span>
|
</svg></button>
|
||||||
|
|
||||||
|
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-2 right-8"
|
||||||
|
x-on:click="makeFullscreen"><svg class="fixed icon" viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="none">
|
||||||
|
<path
|
||||||
|
d="M24 0v24H0V0h24ZM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018Zm.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022Zm-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01l-.184-.092Z" />
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="M9.793 12.793a1 1 0 0 1 1.497 1.32l-.083.094L6.414 19H9a1 1 0 0 1 .117 1.993L9 21H4a1 1 0 0 1-.993-.883L3 20v-5a1 1 0 0 1 1.993-.117L5 15v2.586l4.793-4.793ZM20 3a1 1 0 0 1 .993.883L21 4v5a1 1 0 0 1-1.993.117L19 9V6.414l-4.793 4.793a1 1 0 0 1-1.497-1.32l.083-.094L17.586 5H15a1 1 0 0 1-.117-1.993L15 3h5Z" />
|
||||||
|
</g>
|
||||||
|
</svg></button>
|
||||||
|
<div id="logs" class="flex flex-col">
|
||||||
|
@if (decode_remote_command_output($application_deployment_queue)->count() > 0)
|
||||||
|
@foreach (decode_remote_command_output($application_deployment_queue) as $line)
|
||||||
|
<div @class([
|
||||||
|
'font-mono whitespace-pre-line',
|
||||||
|
'text-neutral-400' => $line['type'] == 'stdout',
|
||||||
|
'text-error' => $line['type'] == 'stderr',
|
||||||
|
'text-warning' => $line['hidden'],
|
||||||
|
])>[{{ $line['timestamp'] }}] @if ($line['hidden'])
|
||||||
|
<br>COMMAND: <br>{{ $line['command'] }} <br><br>OUTPUT:
|
||||||
|
@endif{{ $line['output'] }}@if ($line['hidden'])
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
@else
|
||||||
|
<span class="font-mono text-neutral-400">No logs yet.</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
function makeFullscreen() {
|
||||||
|
this.fullscreen = !this.fullscreen;
|
||||||
|
if (this.fullscreen === false) {
|
||||||
|
this.alwaysScroll = false;
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleScroll() {
|
||||||
|
this.alwaysScroll = !this.alwaysScroll;
|
||||||
|
|
||||||
|
if (this.alwaysScroll) {
|
||||||
|
this.intervalId = setInterval(() => {
|
||||||
|
const screen = document.getElementById('screen');
|
||||||
|
const logs = document.getElementById('logs');
|
||||||
|
if (screen.scrollTop !== logs.scrollHeight) {
|
||||||
|
screen.scrollTop = logs.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
} else {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
this.intervalId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goTop() {
|
||||||
|
this.alwaysScroll = false;
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
const screen = document.getElementById('screen');
|
||||||
|
screen.scrollTop = 0;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
<div class="flex flex-col gap-2" @if ($skip == 0) wire:poll.5000ms='reload_deployments' @endif>
|
<div class="flex flex-col gap-2 pb-10" @if ($skip == 0) wire:poll.5000ms='reload_deployments' @endif>
|
||||||
<h2 class="pt-4">Deployments <span class="text-xs">({{ $deployments_count }})</span></h2>
|
<div class="flex items-end gap-2 pt-4">
|
||||||
@if ($show_next)
|
<h2>Deployments <span class="text-xs">({{ $deployments_count }})</span></h2>
|
||||||
<x-forms.button wire:click="load_deployments({{ $default_take }})">Show More
|
@if ($show_next)
|
||||||
</x-forms.button>
|
<x-forms.button wire:click="load_deployments({{ $default_take }})">Next Page
|
||||||
@endif
|
</x-forms.button>
|
||||||
@foreach ($deployments as $deployment)
|
@endif
|
||||||
|
</div>
|
||||||
|
<form class="flex items-end gap-2">
|
||||||
|
<x-forms.input id="pull_request_id" label="Pull Request"></x-forms.input>
|
||||||
|
<x-forms.button type="submit">Filter</x-forms.button>
|
||||||
|
</form>
|
||||||
|
@forelse ($deployments as $deployment)
|
||||||
<a @class([
|
<a @class([
|
||||||
'bg-coolgray-200 p-2 border-l border-dashed transition-colors hover:no-underline',
|
'bg-coolgray-200 p-2 border-l border-dashed transition-colors hover:no-underline',
|
||||||
'cursor-not-allowed hover:bg-coolgray-200' =>
|
'cursor-not-allowed hover:bg-coolgray-200' =>
|
||||||
@@ -16,45 +22,45 @@
|
|||||||
data_get($deployment, 'status') === 'error',
|
data_get($deployment, 'status') === 'error',
|
||||||
'border-success hover:bg-success' =>
|
'border-success hover:bg-success' =>
|
||||||
data_get($deployment, 'status') === 'finished',
|
data_get($deployment, 'status') === 'finished',
|
||||||
]) @if (data_get($deployment, 'status') !== 'cancelled by system' && data_get($deployment, 'status') !== 'queued')
|
]) href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}"
|
||||||
href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}"
|
class="hover:no-underline">
|
||||||
@endif
|
<div class="flex flex-col justify-start">
|
||||||
class="hover:no-underline">
|
<div class="flex gap-1">
|
||||||
<div class="flex flex-col justify-start">
|
{{ $deployment->created_at }} UTC
|
||||||
<div>
|
<span class=" text-warning">></span>
|
||||||
{{ $deployment->id }} <span class=" text-warning">></span> {{ $deployment->deployment_uuid }}
|
{{ $deployment->status }}
|
||||||
<span class=" text-warning">></span>
|
</div>
|
||||||
{{ $deployment->status }}
|
@if (data_get($deployment, 'pull_request_id'))
|
||||||
</div>
|
<div>
|
||||||
@if (data_get($deployment, 'pull_request_id'))
|
<span class=" text-warning">></span>
|
||||||
<div>
|
Pull Request #{{ data_get($deployment, 'pull_request_id') }}
|
||||||
Pull Request #{{ data_get($deployment, 'pull_request_id') }}
|
@if (data_get($deployment, 'is_webhook'))
|
||||||
@if (data_get($deployment, 'is_webhook'))
|
(Webhook)
|
||||||
(Webhook)
|
@endif
|
||||||
|
Webhook (SHA
|
||||||
|
@if (data_get($deployment, 'commit'))
|
||||||
|
{{ data_get($deployment, 'commit') }})
|
||||||
|
@else
|
||||||
|
HEAD)
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@elseif (data_get($deployment, 'is_webhook'))
|
|
||||||
<div>Webhook (sha
|
<div class="flex flex-col" x-data="elapsedTime('{{ $deployment->deployment_uuid }}', '{{ $deployment->status }}', '{{ $deployment->created_at }}', '{{ $deployment->updated_at }}')">
|
||||||
@if (data_get($deployment, 'commit'))
|
<div>
|
||||||
{{ data_get($deployment, 'commit') }})
|
@if ($deployment->status !== 'in_progress')
|
||||||
@else
|
Finished <span x-text="measure_since_started()">0s</span> in
|
||||||
HEAD)
|
@else
|
||||||
@endif
|
Running for
|
||||||
|
@endif
|
||||||
|
<span class="font-bold" x-text="measure_finished_time()">0s</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
</a>
|
||||||
<div class="flex flex-col" x-data="elapsedTime('{{ $deployment->deployment_uuid }}', '{{ $deployment->status }}', '{{ $deployment->created_at }}', '{{ $deployment->updated_at }}')">
|
@empty
|
||||||
<div>
|
<div class="">No deployments found</div>
|
||||||
@if ($deployment->status !== 'in_progress')
|
@endforelse
|
||||||
Finished <span x-text="measure_since_started()">0s</span> in
|
|
||||||
@else
|
|
||||||
Running for
|
|
||||||
@endif
|
|
||||||
<span class="font-bold" x-text="measure_finished_time()">0s</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
@endforeach
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/utc.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/utc.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/relativeTime.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/relativeTime.js"></script>
|
||||||
|
|||||||
@@ -49,14 +49,22 @@
|
|||||||
@if ($application->build_pack !== 'dockerimage')
|
@if ($application->build_pack !== 'dockerimage')
|
||||||
<h3>Build</h3>
|
<h3>Build</h3>
|
||||||
@if ($application->could_set_build_commands())
|
@if ($application->could_set_build_commands())
|
||||||
<div class="flex flex-col gap-2 xl:flex-row">
|
@if ($application->build_pack === 'nixpacks')
|
||||||
<x-forms.input placeholder="pnpm install" id="application.install_command"
|
<div>Nixpacks will detect your package manager/configurations: <a class="underline"
|
||||||
label="Install Command" />
|
href="https://nixpacks.com/docs/providers">Nixpacks documentation</a></div>
|
||||||
<x-forms.input placeholder="pnpm build" id="application.build_command" label="Build Command" />
|
<div class="text-warning">You probably do not need to modify the commands below.</div>
|
||||||
<x-forms.input placeholder="pnpm start" id="application.start_command" label="Start Command" />
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
</div>
|
<x-forms.input placeholder="If you modify this, you probably need to have a nixpacks.toml"
|
||||||
|
id="application.install_command" label="Install Command" />
|
||||||
|
<x-forms.input placeholder="If you modify this, you probably need to have a nixpacks.toml"
|
||||||
|
id="application.build_command" label="Build Command" />
|
||||||
|
<x-forms.input placeholder="If you modify this, you probably need to have a nixpacks.toml"
|
||||||
|
id="application.start_command" label="Start Command" />
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 xl:flex-row">
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
||||||
helper="Directory to use as root. Useful for monorepos." />
|
helper="Directory to use as root. Useful for monorepos." />
|
||||||
@@ -64,6 +72,7 @@
|
|||||||
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
|
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
|
||||||
label="Dockerfile Location"
|
label="Dockerfile Location"
|
||||||
helper="It is calculated together with the Base Directory: {{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}" />
|
helper="It is calculated together with the Base Directory: {{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}" />
|
||||||
|
<x-forms.input id="application.dockerfile_target_build" label="Docker Build Stage Target" helper="Useful if you have multi-staged dockerfile." />
|
||||||
@endif
|
@endif
|
||||||
@if ($application->could_set_build_commands())
|
@if ($application->could_set_build_commands())
|
||||||
@if ($application->settings->is_static)
|
@if ($application->settings->is_static)
|
||||||
@@ -96,11 +105,7 @@
|
|||||||
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
||||||
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
||||||
</div>
|
</div>
|
||||||
@if ($labelsChanged)
|
<x-forms.textarea label="Container Labels" rows="15" id="customLabels"></x-forms.textarea>
|
||||||
<x-forms.textarea label="Custom Labels" rows="15" id="customLabels"></x-forms.textarea>
|
|
||||||
@else
|
|
||||||
<x-forms.textarea label="Coolify Generated Labels" rows="15" id="customLabels"></x-forms.textarea>
|
|
||||||
@endif
|
|
||||||
<x-forms.button wire:click="resetDefaultLabels">Reset to Coolify Generated Labels</x-forms.button>
|
<x-forms.button wire:click="resetDefaultLabels">Reset to Coolify Generated Labels</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
<h3>Advanced</h3>
|
<h3>Advanced</h3>
|
||||||
|
|||||||
@@ -78,11 +78,15 @@
|
|||||||
Redeploy
|
Redeploy
|
||||||
@endif
|
@endif
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@if (data_get($preview, 'status') !== 'exited')
|
<x-forms.button wire:click="stop({{ data_get($preview, 'pull_request_id') }})">Remove
|
||||||
<x-forms.button wire:click="stop({{ data_get($preview, 'pull_request_id') }})">Remove
|
Preview
|
||||||
Preview
|
</x-forms.button>
|
||||||
|
<a
|
||||||
|
href="{{ route('project.application.deployments', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
|
||||||
|
<x-forms.button>
|
||||||
|
Get Deployment Logs
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@endif
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|||||||
@@ -1,12 +1,36 @@
|
|||||||
<div class="flex flex-wrap gap-2">
|
<div>
|
||||||
@forelse($database->scheduledBackups as $backup)
|
<div class="flex flex-wrap gap-2">
|
||||||
<a class="flex flex-col box"
|
@forelse($database->scheduledBackups as $backup)
|
||||||
href="{{ route('project.database.backups.executions', [...$parameters, 'backup_uuid' => $backup->uuid]) }}">
|
@if ($type === 'database')
|
||||||
<div>Frequency: {{ $backup->frequency }}</div>
|
<a class="flex flex-col box"
|
||||||
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
href="{{ route('project.database.backups.executions', [...$parameters, 'backup_uuid' => $backup->uuid]) }}">
|
||||||
<div>Number of backups to keep (locally): {{ $backup->number_of_backups_locally }}</div>
|
<div>Frequency: {{ $backup->frequency }}</div>
|
||||||
</a>
|
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
||||||
@empty
|
<div>Number of backups to keep (locally): {{ $backup->number_of_backups_locally }}</div>
|
||||||
<div>No scheduled backups configured.</div>
|
</a>
|
||||||
@endforelse
|
@else
|
||||||
|
<div @class([
|
||||||
|
'border-coollabs' =>
|
||||||
|
data_get($backup, 'id') === data_get($selectedBackup, 'id'),
|
||||||
|
'flex flex-col box border-l-2 border-transparent',
|
||||||
|
])
|
||||||
|
wire:click="setSelectedBackup('{{ data_get($backup, 'id') }}')">
|
||||||
|
<div>Frequency: {{ $backup->frequency }}</div>
|
||||||
|
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
||||||
|
<div>Number of backups to keep (locally): {{ $backup->number_of_backups_locally }}</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@empty
|
||||||
|
<div>No scheduled backups configured.</div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
@if ($type === 'service-database' && $selectedBackup)
|
||||||
|
<div class="pt-10">
|
||||||
|
<livewire:project.database.backup-edit key="{{ $selectedBackup->id }}" :backup="$selectedBackup" :s3s="$s3s"
|
||||||
|
:status="data_get($database, 'status')" />
|
||||||
|
<h3 class="py-4">Executions</h3>
|
||||||
|
<livewire:project.database.backup-executions key="{{ $selectedBackup->id }}" :backup="$selectedBackup"
|
||||||
|
:executions="$selectedBackup->executions" />
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div x-data="{ raw: true, activeTab: window.location.hash ? window.location.hash.substring(1) : 'service-stack' }" wire:poll.15000ms="checkStatus">
|
<div x-data="{ raw: true, activeTab: window.location.hash ? window.location.hash.substring(1) : 'service-stack' }" x-init="$wire.checkStatus" wire:poll.10000ms="checkStatus">
|
||||||
<livewire:project.service.navbar :service="$service" :parameters="$parameters" :query="$query" />
|
<livewire:project.service.navbar :service="$service" :parameters="$parameters" :query="$query" />
|
||||||
<livewire:project.service.compose-modal :raw="$service->docker_compose_raw" :actual="$service->docker_compose" />
|
<livewire:project.service.compose-modal :raw="$service->docker_compose_raw" :actual="$service->docker_compose" />
|
||||||
<div class="flex h-full pt-6">
|
<div class="flex h-full pt-6">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div x-init="$wire.checkStatus" wire:poll.2500ms='checkStatus'>
|
<div>
|
||||||
<livewire:project.service.modal />
|
<livewire:project.service.modal />
|
||||||
<h1>Configuration</h1>
|
<h1>Configuration</h1>
|
||||||
<x-resources.breadcrumbs :resource="$service" :parameters="$parameters" />
|
<x-resources.breadcrumbs :resource="$service" :parameters="$parameters" />
|
||||||
|
|||||||
@@ -7,10 +7,14 @@
|
|||||||
<button><- Back</button>
|
<button><- Back</button>
|
||||||
</a>
|
</a>
|
||||||
<a :class="activeTab === 'general' && 'text-white'"
|
<a :class="activeTab === 'general' && 'text-white'"
|
||||||
@click.prevent="activeTab = 'general'; window.location.hash = 'general'" href="#">General</a>
|
@click.prevent="activeTab = 'general'; window.location.hash = 'general'; if(window.location.search) window.location.search = ''"
|
||||||
|
href="#">General</a>
|
||||||
<a :class="activeTab === 'storages' && 'text-white'"
|
<a :class="activeTab === 'storages' && 'text-white'"
|
||||||
@click.prevent="activeTab = 'storages'; window.location.hash = 'storages'" href="#">Storages
|
@click.prevent="activeTab = 'storages'; window.location.hash = 'storages'; if(window.location.search) window.location.search = ''"
|
||||||
|
href="#">Storages
|
||||||
</a>
|
</a>
|
||||||
|
<a :class="activeTab === 'backups' && 'text-white'"
|
||||||
|
@click.prevent="activeTab = 'backups'; window.location.hash = 'backups'" href="#">Backups</a>
|
||||||
@if (data_get($parameters, 'service_name'))
|
@if (data_get($parameters, 'service_name'))
|
||||||
<a class="{{ request()->routeIs('project.service.logs') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.service.logs') ? 'text-white' : '' }}"
|
||||||
href="{{ route('project.service.logs', $parameters) }}">
|
href="{{ route('project.service.logs', $parameters) }}">
|
||||||
@@ -43,8 +47,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pb-4">Persistent storage to preserve data between deployments.</div>
|
<div class="pb-4">Persistent storage to preserve data between deployments.</div>
|
||||||
<span class="text-warning">Please modify storage layout in your Docker Compose file.</span>
|
<span class="text-warning">Please modify storage layout in your Docker Compose file.</span>
|
||||||
<livewire:project.service.storage wire:key="application-{{ $serviceDatabase->id }}"
|
<livewire:project.service.storage wire:key="application-{{ $serviceDatabase->id }}" :resource="$serviceDatabase" />
|
||||||
:resource="$serviceDatabase" />
|
</div>
|
||||||
|
<div x-cloak x-show="activeTab === 'backups'">
|
||||||
|
<div class="flex gap-2 ">
|
||||||
|
<h2 class="pb-4">Scheduled Backups</h2>
|
||||||
|
<x-forms.button onclick="createScheduledBackup.showModal()">+ Add</x-forms.button>
|
||||||
|
</div>
|
||||||
|
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" :s3s="$s3s" />
|
||||||
|
<livewire:project.database.scheduled-backups :database="$serviceDatabase" />
|
||||||
</div>
|
</div>
|
||||||
@endisset
|
@endisset
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,12 +13,70 @@
|
|||||||
<x-forms.input label="Only Show Number of Lines" placeholder="1000" required id="numberOfLines"></x-forms.input>
|
<x-forms.input label="Only Show Number of Lines" placeholder="1000" required id="numberOfLines"></x-forms.input>
|
||||||
<x-forms.button type="submit">Refresh</x-forms.button>
|
<x-forms.button type="submit">Refresh</x-forms.button>
|
||||||
</form>
|
</form>
|
||||||
|
<div id="screen" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }" :class="fullscreen ? 'fullscreen' : 'container w-full pt-4 mx-auto'">
|
||||||
|
<div class="relative flex flex-col-reverse w-full p-4 pt-6 overflow-y-auto text-white scrollbar border-coolgray-300"
|
||||||
|
:class="fullscreen ? '' : 'max-h-[40rem] border border-solid rounded'">
|
||||||
|
<button title="Minimize" x-show="fullscreen" class="fixed top-4 right-4" x-on:click="makeFullscreen"><svg
|
||||||
|
class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2" d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
|
||||||
|
</svg></button>
|
||||||
|
<button title="Go Top" x-show="fullscreen" class="fixed top-4 right-28" x-on:click="goTop"> <svg
|
||||||
|
class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2" d="M12 5v14m4-10l-4-4M8 9l4-4" />
|
||||||
|
</svg></button>
|
||||||
|
<button title="Follow Logs" x-show="fullscreen" :class="alwaysScroll ? 'text-warning' : ''"
|
||||||
|
class="fixed top-4 right-16" x-on:click="toggleScroll"><svg class="icon" viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
|
||||||
|
</svg></button>
|
||||||
|
|
||||||
<div class="container w-full pt-4 mx-auto">
|
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-2 right-8"
|
||||||
<div
|
x-on:click="makeFullscreen"><svg class="fixed icon" viewBox="0 0 24 24"
|
||||||
class="scrollbar flex flex-col-reverse w-full overflow-y-auto border border-solid rounded border-coolgray-300 max-h-[32rem] p-4 pt-6 text-xs text-white">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="none">
|
||||||
<pre class="font-mono whitespace-pre-wrap">{{ $outputs }}</pre>
|
<path
|
||||||
|
d="M24 0v24H0V0h24ZM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018Zm.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022Zm-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01l-.184-.092Z" />
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="M9.793 12.793a1 1 0 0 1 1.497 1.32l-.083.094L6.414 19H9a1 1 0 0 1 .117 1.993L9 21H4a1 1 0 0 1-.993-.883L3 20v-5a1 1 0 0 1 1.993-.117L5 15v2.586l4.793-4.793ZM20 3a1 1 0 0 1 .993.883L21 4v5a1 1 0 0 1-1.993.117L19 9V6.414l-4.793 4.793a1 1 0 0 1-1.497-1.32l.083-.094L17.586 5H15a1 1 0 0 1-.117-1.993L15 3h5Z" />
|
||||||
|
</g>
|
||||||
|
</svg></button>
|
||||||
|
<pre id="logs" class="font-mono whitespace-pre-wrap">{{ $outputs }}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
function makeFullscreen() {
|
||||||
|
this.fullscreen = !this.fullscreen;
|
||||||
|
if (this.fullscreen === false) {
|
||||||
|
this.alwaysScroll = false;
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleScroll() {
|
||||||
|
this.alwaysScroll = !this.alwaysScroll;
|
||||||
|
|
||||||
|
if (this.alwaysScroll) {
|
||||||
|
this.intervalId = setInterval(() => {
|
||||||
|
const screen = document.getElementById('screen');
|
||||||
|
const logs = document.getElementById('logs');
|
||||||
|
if (screen.scrollTop !== logs.scrollHeight) {
|
||||||
|
screen.scrollTop = logs.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
} else {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
this.intervalId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goTop() {
|
||||||
|
this.alwaysScroll = false;
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
const screen = document.getElementById('screen');
|
||||||
|
screen.scrollTop = 0;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
22
resources/views/livewire/server/delete.blade.php
Normal file
22
resources/views/livewire/server/delete.blade.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<div>
|
||||||
|
<x-modal yesOrNo modalId="deleteServer" modalTitle="Delete Server">
|
||||||
|
<x-slot:modalBody>
|
||||||
|
<p>This server will be deleted. It is not reversible. <br>Please think again..</p>
|
||||||
|
</x-slot:modalBody>
|
||||||
|
</x-modal>
|
||||||
|
@if ($server->id !== 0)
|
||||||
|
<h2 class="pt-4">Danger Zone</h2>
|
||||||
|
<div class="">Woah. I hope you know what are you doing.</div>
|
||||||
|
<h4 class="pt-4">Delete Server</h4>
|
||||||
|
<div class="pb-4">This will remove this server from Coolify. Beware! There is no coming
|
||||||
|
back!
|
||||||
|
</div>
|
||||||
|
@if ($server->hasDefinedResources())
|
||||||
|
<div class="text-warning">Please delete all resources before deleting this server.</div>
|
||||||
|
@else
|
||||||
|
<x-forms.button isError isModal modalId="deleteServer">
|
||||||
|
Delete
|
||||||
|
</x-forms.button>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
<div x-init="$wire.validateServer(false)">
|
<div x-init="$wire.validateServer(false)">
|
||||||
<x-modal yesOrNo modalId="deleteServer" modalTitle="Delete Server">
|
|
||||||
<x-slot:modalBody>
|
|
||||||
<p>This server will be deleted. It is not reversible. <br>Please think again..</p>
|
|
||||||
</x-slot:modalBody>
|
|
||||||
</x-modal>
|
|
||||||
<x-modal yesOrNo modalId="changeLocalhost" modalTitle="Change Localhost" action="submit">
|
<x-modal yesOrNo modalId="changeLocalhost" modalTitle="Change Localhost" action="submit">
|
||||||
<x-slot:modalBody>
|
<x-slot:modalBody>
|
||||||
<p>You could lost a lot of functionalities if you change the server details of the server where Coolify is
|
<p>You could lost a lot of functionalities if you change the server details of the server where Coolify is
|
||||||
@@ -47,7 +42,8 @@
|
|||||||
label="Is it part of a Swarm cluster?" /> --}}
|
label="Is it part of a Swarm cluster?" /> --}}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full gap-2 lg:flex-row">
|
<div class="flex flex-col w-full gap-2 lg:flex-row">
|
||||||
<x-forms.input id="server.ip" label="IP Address" required />
|
<x-forms.input id="server.ip" label="IP Address/Domain"
|
||||||
|
helper="An IP Address (127.0.0.1) or domain (example.com)." required />
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.input id="server.user" label="User" required />
|
<x-forms.input id="server.user" label="User" required />
|
||||||
<x-forms.input type="number" id="server.port" label="Port" required />
|
<x-forms.input type="number" id="server.port" label="Port" required />
|
||||||
@@ -66,21 +62,8 @@
|
|||||||
helper="Disk cleanup job will be executed if disk usage is more than this number." />
|
helper="Disk cleanup job will be executed if disk usage is more than this number." />
|
||||||
@endif
|
@endif
|
||||||
</form>
|
</form>
|
||||||
@if ($server->id !== 0)
|
|
||||||
<h2 class="pt-4">Danger Zone</h2>
|
|
||||||
<div class="">Woah. I hope you know what are you doing.</div>
|
|
||||||
<h4 class="pt-4">Delete Server</h4>
|
|
||||||
<div class="pb-4">This will remove this server from Coolify. Beware! There is no coming
|
|
||||||
back!
|
|
||||||
</div>
|
|
||||||
<x-forms.button isError isModal modalId="deleteServer">
|
|
||||||
Delete
|
|
||||||
</x-forms.button>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
Livewire.on('installDocker', () => {
|
Livewire.on('installDocker', () => {
|
||||||
console.log('asd');
|
|
||||||
installDocker.showModal();
|
installDocker.showModal();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
<x-forms.input id="description" label="Description" />
|
<x-forms.input id="description" label="Description" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.input id="ip" label="IP Address" required
|
<x-forms.input id="ip" label="IP Address/Domain" required
|
||||||
helper="An IP Address (127.0.0.1). No domain names." />
|
helper="An IP Address (127.0.0.1) or domain (example.com)." />
|
||||||
<x-forms.input id="user" label ="User" required />
|
<x-forms.input id="user" label ="User" required />
|
||||||
<x-forms.input type="number" id="port" label="Port" required />
|
<x-forms.input type="number" id="port" label="Port" required />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,4 +11,5 @@
|
|||||||
</x-modal>
|
</x-modal>
|
||||||
<x-server.navbar :server="$server" :parameters="$parameters" />
|
<x-server.navbar :server="$server" :parameters="$parameters" />
|
||||||
<livewire:server.form :server="$server" />
|
<livewire:server.form :server="$server" />
|
||||||
|
<livewire:server.delete :server="$server" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,32 +4,26 @@
|
|||||||
<p>This source will be deleted. It is not reversible. <br>Please think again.</p>
|
<p>This source will be deleted. It is not reversible. <br>Please think again.</p>
|
||||||
</x-slot:modalBody>
|
</x-slot:modalBody>
|
||||||
</x-modal>
|
</x-modal>
|
||||||
<form wire:submit.prevent='submit' x-data>
|
@if (data_get($github_app, 'app_id'))
|
||||||
<div class="flex items-center gap-2">
|
<form wire:submit.prevent='submit'>
|
||||||
<h1>GitHub App</h1>
|
<div class="flex items-center gap-2">
|
||||||
<div class="flex gap-2">
|
<h1>GitHub App</h1>
|
||||||
@if ($github_app->app_id)
|
<div class="flex gap-2">
|
||||||
<x-forms.button type="submit">Save</x-forms.button>
|
|
||||||
|
|
||||||
@if (data_get($github_app, 'installation_id'))
|
@if (data_get($github_app, 'installation_id'))
|
||||||
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
<a href="{{ get_installation_path($github_app) }}">
|
<a href="{{ get_installation_path($github_app) }}">
|
||||||
<x-forms.button>
|
<x-forms.button>
|
||||||
|
|
||||||
Update Repositories
|
Update Repositories
|
||||||
<x-external-link />
|
<x-external-link />
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
@else
|
<x-forms.button isError isModal modalId="deleteSource">
|
||||||
<x-forms.button disabled type="submit">Save</x-forms.button>
|
Delete
|
||||||
@endif
|
</x-forms.button>
|
||||||
<x-forms.button isError isModal modalId="deleteSource">
|
</div>
|
||||||
Delete
|
|
||||||
</x-forms.button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="subtitle">Your Private GitHub App for private repositories.</div>
|
||||||
<div class="subtitle">Your Private GitHub App for private repositories.</div>
|
|
||||||
@if (data_get($github_app, 'app_id'))
|
|
||||||
@if (!data_get($github_app, 'installation_id'))
|
@if (!data_get($github_app, 'installation_id'))
|
||||||
<div class="mb-10 rounded alert alert-warning">
|
<div class="mb-10 rounded alert alert-warning">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
||||||
@@ -39,7 +33,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<span>You must complete this step before you can use this source!</span>
|
<span>You must complete this step before you can use this source!</span>
|
||||||
</div>
|
</div>
|
||||||
<a class="justify-center box" href="{{ get_installation_path($github_app) }}">
|
<a class="items-center justify-center box" href="{{ get_installation_path($github_app) }}">
|
||||||
Install Repositories on GitHub
|
Install Repositories on GitHub
|
||||||
</a>
|
</a>
|
||||||
@else
|
@else
|
||||||
@@ -47,7 +41,7 @@
|
|||||||
<div class="w-48">
|
<div class="w-48">
|
||||||
<x-forms.checkbox label="System Wide?"
|
<x-forms.checkbox label="System Wide?"
|
||||||
helper="If checked, this GitHub App will be available for everyone in this Coolify instance."
|
helper="If checked, this GitHub App will be available for everyone in this Coolify instance."
|
||||||
instantSave id="is_system_wide" />
|
instantSave id="github_app.is_system_wide" />
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@@ -78,110 +72,118 @@
|
|||||||
<x-forms.input id="github_app.webhook_secret" label="Webhook Secret" type="password" />
|
<x-forms.input id="github_app.webhook_secret" label="Webhook Secret" type="password" />
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@else
|
</form>
|
||||||
<div class="mb-10 rounded alert alert-warning">
|
@else
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
<div class="flex items-center gap-2 pb-4">
|
||||||
viewBox="0 0 24 24">
|
<h1>GitHub App</h1>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
<div class="flex gap-2">
|
||||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
<x-forms.button isError isModal modalId="deleteSource">
|
||||||
</svg>
|
Delete
|
||||||
<span>You must complete this step before you can use this source!</span>
|
</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
<form class="flex gap-4">
|
</div>
|
||||||
<h2>Register a GitHub App</h2>
|
<div class="mb-10 rounded alert alert-warning">
|
||||||
<div class="pt-1 pb-2 ">You need to register a GitHub App before using this source.</div>
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
||||||
<div class="pt-2 pb-10">
|
viewBox="0 0 24 24">
|
||||||
@if (!isCloud() || isDev())
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
<div class="flex items-end gap-2">
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
<x-forms.select wire:model='webhook_endpoint' label="Webhook Endpoint"
|
</svg>
|
||||||
helper="All Git webhooks will be sent to this endpoint. <br><br>If you would like to use domain instead of IP address, set your Coolify instance's FQDN in the Settings menu.">
|
<span>You must complete this step before you can use this source!</span>
|
||||||
@if ($ipv4)
|
</div>
|
||||||
<option value="{{ $ipv4 }}">Use {{ $ipv4 }}</option>
|
<div class="flex flex-col">
|
||||||
@endif
|
<h2 >Register a GitHub App</h2>
|
||||||
@if ($ipv6)
|
<div >You need to register a GitHub App before using this source.</div>
|
||||||
<option value="{{ $ipv6 }}">Use {{ $ipv6 }}</option>
|
<div class="py-10">
|
||||||
@endif
|
@if (!isCloud() || isDev())
|
||||||
@if ($fqdn)
|
<div class="flex items-end gap-2">
|
||||||
<option value="{{ $fqdn }}">Use {{ $fqdn }}</option>
|
<x-forms.select wire:model='webhook_endpoint' label="Webhook Endpoint"
|
||||||
@endif
|
helper="All Git webhooks will be sent to this endpoint. <br><br>If you would like to use domain instead of IP address, set your Coolify instance's FQDN in the Settings menu.">
|
||||||
@if (config('app.url'))
|
@if ($ipv4)
|
||||||
<option value="{{ config('app.url') }}">Use {{ config('app.url') }}</option>
|
<option value="{{ $ipv4 }}">Use {{ $ipv4 }}</option>
|
||||||
@endif
|
@endif
|
||||||
</x-forms.select>
|
@if ($ipv6)
|
||||||
<x-forms.button
|
<option value="{{ $ipv6 }}">Use {{ $ipv6 }}</option>
|
||||||
x-on:click.prevent="createGithubApp('{{ $webhook_endpoint }}','{{ $preview_deployment_permissions }}')">
|
@endif
|
||||||
Register
|
@if ($fqdn)
|
||||||
</x-forms.button>
|
<option value="{{ $fqdn }}">Use {{ $fqdn }}</option>
|
||||||
</div>
|
@endif
|
||||||
@else
|
@if (config('app.url'))
|
||||||
|
<option value="{{ config('app.url') }}">Use {{ config('app.url') }}</option>
|
||||||
|
@endif
|
||||||
|
</x-forms.select>
|
||||||
<x-forms.button
|
<x-forms.button
|
||||||
x-on:click.prevent="createGithubApp('{{ $webhook_endpoint }}','{{ $preview_deployment_permissions }}')">
|
x-on:click.prevent="createGithubApp('{{ $webhook_endpoint }}','{{ $preview_deployment_permissions }}')">
|
||||||
Register Now
|
Register
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@endif
|
|
||||||
<div class="flex flex-col gap-2 pt-4">
|
|
||||||
<x-forms.checkbox disabled instantSave id="default_permissions" label="Default Permissions"
|
|
||||||
helper="Contents: read<br>Metadata: read<br>Email: read" />
|
|
||||||
<x-forms.checkbox instantSave id="preview_deployment_permissions"
|
|
||||||
label="Preview Deployments Permission"
|
|
||||||
helper="Necessary for updating pull requests with useful comments (deployment status, links, etc.)<br><br>Pull Request: read & write" />
|
|
||||||
</div>
|
</div>
|
||||||
|
@else
|
||||||
|
<x-forms.button
|
||||||
|
x-on:click.prevent="createGithubApp('{{ $webhook_endpoint }}','{{ $preview_deployment_permissions }}')">
|
||||||
|
Register Now
|
||||||
|
</x-forms.button>
|
||||||
|
@endif
|
||||||
|
<div class="flex flex-col gap-2 pt-4">
|
||||||
|
<x-forms.checkbox disabled instantSave id="default_permissions" label="Default Permissions"
|
||||||
|
helper="Contents: read<br>Metadata: read<br>Email: read" />
|
||||||
|
<x-forms.checkbox instantSave id="preview_deployment_permissions"
|
||||||
|
label="Preview Deployments Permission"
|
||||||
|
helper="Necessary for updating pull requests with useful comments (deployment status, links, etc.)<br><br>Pull Request: read & write" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
<script>
|
</div>
|
||||||
function createGithubApp(webhook_endpoint, preview_deployment_permissions) {
|
<script>
|
||||||
const {
|
function createGithubApp(webhook_endpoint, preview_deployment_permissions) {
|
||||||
organization,
|
const {
|
||||||
uuid,
|
organization,
|
||||||
html_url
|
uuid,
|
||||||
} = @json($github_app);
|
html_url
|
||||||
let baseUrl = webhook_endpoint;
|
} = @json($github_app);
|
||||||
const name = @js($name);
|
let baseUrl = webhook_endpoint;
|
||||||
const isDev = @js(config('app.env')) ===
|
const name = @js($name);
|
||||||
'local';
|
const isDev = @js(config('app.env')) ===
|
||||||
const devWebhook = @js(config('coolify.dev_webhook'));
|
'local';
|
||||||
if (isDev && devWebhook) {
|
const devWebhook = @js(config('coolify.dev_webhook'));
|
||||||
baseUrl = devWebhook;
|
if (isDev && devWebhook) {
|
||||||
}
|
baseUrl = devWebhook;
|
||||||
const webhookBaseUrl = `${baseUrl}/webhooks`;
|
|
||||||
const path = organization ? `organizations/${organization}/settings/apps/new` : 'settings/apps/new';
|
|
||||||
const default_permissions = {
|
|
||||||
contents: 'read',
|
|
||||||
metadata: 'read',
|
|
||||||
emails: 'read'
|
|
||||||
};
|
|
||||||
if (preview_deployment_permissions) {
|
|
||||||
default_permissions.pull_requests = 'write';
|
|
||||||
}
|
|
||||||
const data = {
|
|
||||||
name,
|
|
||||||
url: baseUrl,
|
|
||||||
hook_attributes: {
|
|
||||||
url: `${webhookBaseUrl}/source/github/events`,
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
redirect_url: `${webhookBaseUrl}/source/github/redirect`,
|
|
||||||
callback_urls: [`${baseUrl}/login/github/app`],
|
|
||||||
public: false,
|
|
||||||
request_oauth_on_install: false,
|
|
||||||
setup_url: `${webhookBaseUrl}/source/github/install?source=${uuid}`,
|
|
||||||
setup_on_update: true,
|
|
||||||
default_permissions,
|
|
||||||
default_events: ['pull_request', 'push']
|
|
||||||
};
|
|
||||||
const form = document.createElement('form');
|
|
||||||
form.setAttribute('method', 'post');
|
|
||||||
form.setAttribute('action', `${html_url}/${path}?state=${uuid}`);
|
|
||||||
const input = document.createElement('input');
|
|
||||||
input.setAttribute('id', 'manifest');
|
|
||||||
input.setAttribute('name', 'manifest');
|
|
||||||
input.setAttribute('type', 'hidden');
|
|
||||||
input.setAttribute('value', JSON.stringify(data));
|
|
||||||
form.appendChild(input);
|
|
||||||
document.getElementsByTagName('body')[0].appendChild(form);
|
|
||||||
form.submit();
|
|
||||||
}
|
}
|
||||||
</script>
|
const webhookBaseUrl = `${baseUrl}/webhooks`;
|
||||||
@endif
|
const path = organization ? `organizations/${organization}/settings/apps/new` : 'settings/apps/new';
|
||||||
</form>
|
const default_permissions = {
|
||||||
|
contents: 'read',
|
||||||
|
metadata: 'read',
|
||||||
|
emails: 'read'
|
||||||
|
};
|
||||||
|
if (preview_deployment_permissions) {
|
||||||
|
default_permissions.pull_requests = 'write';
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
name,
|
||||||
|
url: baseUrl,
|
||||||
|
hook_attributes: {
|
||||||
|
url: `${webhookBaseUrl}/source/github/events`,
|
||||||
|
active: true,
|
||||||
|
},
|
||||||
|
redirect_url: `${webhookBaseUrl}/source/github/redirect`,
|
||||||
|
callback_urls: [`${baseUrl}/login/github/app`],
|
||||||
|
public: false,
|
||||||
|
request_oauth_on_install: false,
|
||||||
|
setup_url: `${webhookBaseUrl}/source/github/install?source=${uuid}`,
|
||||||
|
setup_on_update: true,
|
||||||
|
default_permissions,
|
||||||
|
default_events: ['pull_request', 'push']
|
||||||
|
};
|
||||||
|
const form = document.createElement('form');
|
||||||
|
form.setAttribute('method', 'post');
|
||||||
|
form.setAttribute('action', `${html_url}/${path}?state=${uuid}`);
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.setAttribute('id', 'manifest');
|
||||||
|
input.setAttribute('name', 'manifest');
|
||||||
|
input.setAttribute('type', 'hidden');
|
||||||
|
input.setAttribute('value', JSON.stringify(data));
|
||||||
|
form.appendChild(input);
|
||||||
|
document.getElementsByTagName('body')[0].appendChild(form);
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.input id="custom_user" label="Custom Git User" required />
|
<x-forms.input id="custom_user" label="Custom Git User" required />
|
||||||
<x-forms.input id="custom_port" label="Custom Git Port" required />
|
<x-forms.input id="custom_port" type="number" label="Custom Git Port" required />
|
||||||
</div>
|
</div>
|
||||||
@if (!isCloud())
|
@if (!isCloud())
|
||||||
<x-forms.checkbox class="pt-2" id="is_system_wide" label="System Wide" />
|
<x-forms.checkbox class="pt-2" id="is_system_wide" label="System Wide" />
|
||||||
|
|||||||
@@ -11,8 +11,7 @@
|
|||||||
<div>This is the default team. You can't delete it.</div>
|
<div>This is the default team. You can't delete it.</div>
|
||||||
@elseif(auth()->user()->teams()->get()->count() === 1)
|
@elseif(auth()->user()->teams()->get()->count() === 1)
|
||||||
<div>You can't delete your last team.</div>
|
<div>You can't delete your last team.</div>
|
||||||
@elseif(currentTeam()->subscription &&
|
@elseif(currentTeam()->subscription && currentTeam()->subscription?->lemon_status !== 'cancelled')
|
||||||
currentTeam()->subscription?->lemon_status !== 'cancelled')
|
|
||||||
<div>Please cancel your subscription before delete this team (Manage My Subscription).</div>
|
<div>Please cancel your subscription before delete this team (Manage My Subscription).</div>
|
||||||
@else
|
@else
|
||||||
@if (currentTeam()->isEmpty())
|
@if (currentTeam()->isEmpty())
|
||||||
@@ -23,30 +22,38 @@
|
|||||||
@else
|
@else
|
||||||
<div>
|
<div>
|
||||||
<div class="pb-4">You need to delete the following resources to be able to delete the team:</div>
|
<div class="pb-4">You need to delete the following resources to be able to delete the team:</div>
|
||||||
<h4 class="pb-4">Projects:</h4>
|
@if (currentTeam()->projects()->count() > 0)
|
||||||
<ul class="pl-8 list-disc">
|
<h4 class="pb-4">Projects:</h4>
|
||||||
@foreach (currentTeam()->projects as $resource)
|
<ul class="pl-8 list-disc">
|
||||||
<li>{{ $resource->name }}</li>
|
@foreach (currentTeam()->projects as $resource)
|
||||||
@endforeach
|
<li>{{ $resource->name }}</li>
|
||||||
</ul>
|
@endforeach
|
||||||
<h4 class="py-4">Servers:</h4>
|
</ul>
|
||||||
<ul class="pl-8 list-disc">
|
@endif
|
||||||
@foreach (currentTeam()->servers as $resource)
|
@if (currentTeam()->servers()->count() > 0)
|
||||||
<li>{{ $resource->name }}</li>
|
<h4 class="py-4">Servers:</h4>
|
||||||
@endforeach
|
<ul class="pl-8 list-disc">
|
||||||
</ul>
|
@foreach (currentTeam()->servers as $resource)
|
||||||
<h4 class="py-4">Private Keys:</h4>
|
<li>{{ $resource->name }}</li>
|
||||||
<ul class="pl-8 list-disc">
|
@endforeach
|
||||||
@foreach (currentTeam()->privateKeys as $resource)
|
</ul>
|
||||||
<li>{{ $resource->name }}</li>
|
@endif
|
||||||
@endforeach
|
@if (currentTeam()->privateKeys()->count() > 0)
|
||||||
</ul>
|
<h4 class="py-4">Private Keys:</h4>
|
||||||
<h4 class="py-4">Sources:</h4>
|
<ul class="pl-8 list-disc">
|
||||||
<ul class="pl-8 list-disc">
|
@foreach (currentTeam()->privateKeys as $resource)
|
||||||
@foreach (currentTeam()->sources() as $resource)
|
<li>{{ $resource->name }}</li>
|
||||||
<li>{{ $resource->name }}</li>
|
@endforeach
|
||||||
@endforeach
|
</ul>
|
||||||
</ul>
|
@endif
|
||||||
|
@if (currentTeam()->sources()->count() > 0)
|
||||||
|
<h4 class="py-4">Sources:</h4>
|
||||||
|
<ul class="pl-8 list-disc">
|
||||||
|
@foreach (currentTeam()->sources() as $resource)
|
||||||
|
<li>{{ $resource->name }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<div class="text-xs truncate subtitle lg:text-sm">{{ $project->name }}</div>
|
<div class="text-xs truncate subtitle lg:text-sm">{{ $project->name }}</div>
|
||||||
<div class="grid gap-2 lg:grid-cols-2">
|
<div class="grid gap-2 lg:grid-cols-2">
|
||||||
@forelse ($project->environments as $environment)
|
@forelse ($project->environments as $environment)
|
||||||
<a class="items-center justify-center box description" href="{{ route('project.resources', [$project->uuid, $environment->name]) }}">
|
<a class="items-center justify-center font-bold box" href="{{ route('project.resources', [$project->uuid, $environment->name]) }}">
|
||||||
{{ $environment->name }}
|
{{ $environment->name }}
|
||||||
</a>
|
</a>
|
||||||
@empty
|
@empty
|
||||||
|
|||||||
@@ -20,11 +20,10 @@ use App\Http\Livewire\Server\PrivateKey\Show as PrivateKeyShow;
|
|||||||
use App\Http\Livewire\Server\Proxy\Show as ProxyShow;
|
use App\Http\Livewire\Server\Proxy\Show as ProxyShow;
|
||||||
use App\Http\Livewire\Server\Proxy\Logs as ProxyLogs;
|
use App\Http\Livewire\Server\Proxy\Logs as ProxyLogs;
|
||||||
use App\Http\Livewire\Server\Show;
|
use App\Http\Livewire\Server\Show;
|
||||||
|
use App\Http\Livewire\Source\Github\Change as GitHubChange;
|
||||||
use App\Http\Livewire\Subscription\Show as SubscriptionShow;
|
use App\Http\Livewire\Subscription\Show as SubscriptionShow;
|
||||||
use App\Http\Livewire\Waitlist\Index as WaitlistIndex;
|
use App\Http\Livewire\Waitlist\Index as WaitlistIndex;
|
||||||
use App\Models\GithubApp;
|
|
||||||
use App\Models\GitlabApp;
|
use App\Models\GitlabApp;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
@@ -178,49 +177,7 @@ Route::middleware(['auth'])->group(function () {
|
|||||||
'sources' => $sources,
|
'sources' => $sources,
|
||||||
]);
|
]);
|
||||||
})->name('source.all');
|
})->name('source.all');
|
||||||
Route::get('/source/github/{github_app_uuid}', function (Request $request) {
|
Route::get('/source/github/{github_app_uuid}', GitHubChange::class)->name('source.github.show');
|
||||||
$github_app = GithubApp::where('uuid', request()->github_app_uuid)->first();
|
|
||||||
if (!$github_app) {
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
$github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
|
||||||
$settings = InstanceSettings::get();
|
|
||||||
$name = Str::of(Str::kebab($github_app->name));
|
|
||||||
if ($settings->public_ipv4) {
|
|
||||||
$ipv4 = 'http://' . $settings->public_ipv4 . ':' . config('app.port');
|
|
||||||
}
|
|
||||||
if ($settings->public_ipv6) {
|
|
||||||
$ipv6 = 'http://' . $settings->public_ipv6 . ':' . config('app.port');
|
|
||||||
}
|
|
||||||
if ($github_app->installation_id && session('from')) {
|
|
||||||
$source_id = data_get(session('from'), 'source_id');
|
|
||||||
if (!$source_id || $github_app->id !== $source_id) {
|
|
||||||
session()->forget('from');
|
|
||||||
} else {
|
|
||||||
$parameters = data_get(session('from'), 'parameters');
|
|
||||||
$back = data_get(session('from'), 'back');
|
|
||||||
$environment_name = data_get($parameters, 'environment_name');
|
|
||||||
$project_uuid = data_get($parameters, 'project_uuid');
|
|
||||||
$type = data_get($parameters, 'type');
|
|
||||||
$destination = data_get($parameters, 'destination');
|
|
||||||
session()->forget('from');
|
|
||||||
return redirect()->route($back, [
|
|
||||||
'environment_name' => $environment_name,
|
|
||||||
'project_uuid' => $project_uuid,
|
|
||||||
'type' => $type,
|
|
||||||
'destination' => $destination,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return view('source.github.show', [
|
|
||||||
'github_app' => $github_app,
|
|
||||||
'name' => $name,
|
|
||||||
'ipv4' => $ipv4 ?? null,
|
|
||||||
'ipv6' => $ipv6 ?? null,
|
|
||||||
'fqdn' => $settings->fqdn,
|
|
||||||
]);
|
|
||||||
})->name('source.github.show');
|
|
||||||
|
|
||||||
Route::get('/source/gitlab/{gitlab_app_uuid}', function (Request $request) {
|
Route::get('/source/gitlab/{gitlab_app_uuid}', function (Request $request) {
|
||||||
$gitlab_app = GitlabApp::where('uuid', request()->gitlab_app_uuid)->first();
|
$gitlab_app = GitlabApp::where('uuid', request()->gitlab_app_uuid)->first();
|
||||||
return view('source.gitlab.show', [
|
return view('source.gitlab.show', [
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use App\Models\Waitlist;
|
|||||||
use App\Models\Webhook;
|
use App\Models\Webhook;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Illuminate\Support\Sleep;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ Route::get('/source/github/install', function () {
|
|||||||
});
|
});
|
||||||
Route::post('/source/github/events', function () {
|
Route::post('/source/github/events', function () {
|
||||||
try {
|
try {
|
||||||
|
$id = null;
|
||||||
$x_github_delivery = request()->header('X-GitHub-Delivery');
|
$x_github_delivery = request()->header('X-GitHub-Delivery');
|
||||||
$x_github_event = Str::lower(request()->header('X-GitHub-Event'));
|
$x_github_event = Str::lower(request()->header('X-GitHub-Event'));
|
||||||
$x_github_hook_installation_target_id = request()->header('X-GitHub-Hook-Installation-Target-Id');
|
$x_github_hook_installation_target_id = request()->header('X-GitHub-Hook-Installation-Target-Id');
|
||||||
@@ -73,7 +75,7 @@ Route::post('/source/github/events', function () {
|
|||||||
// Just pong
|
// Just pong
|
||||||
return response('pong');
|
return response('pong');
|
||||||
}
|
}
|
||||||
if ($x_github_event === 'installation') {
|
if ($x_github_event === 'installation' || $x_github_event === 'installation_repositories') {
|
||||||
// Installation handled by setup redirect url. Repositories queried on-demand.
|
// Installation handled by setup redirect url. Repositories queried on-demand.
|
||||||
return response('cool');
|
return response('cool');
|
||||||
}
|
}
|
||||||
@@ -87,7 +89,6 @@ Route::post('/source/github/events', function () {
|
|||||||
return response('not cool');
|
return response('not cool');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($x_github_event === 'push') {
|
if ($x_github_event === 'push') {
|
||||||
$id = data_get($payload, 'repository.id');
|
$id = data_get($payload, 'repository.id');
|
||||||
$branch = data_get($payload, 'ref');
|
$branch = data_get($payload, 'ref');
|
||||||
@@ -281,7 +282,11 @@ Route::post('/payments/stripe/events', function () {
|
|||||||
break;
|
break;
|
||||||
case 'invoice.paid':
|
case 'invoice.paid':
|
||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
if (!$subscription) {
|
||||||
|
Sleep::for(5)->seconds();
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
|
}
|
||||||
$planId = data_get($data, 'lines.data.0.plan.id');
|
$planId = data_get($data, 'lines.data.0.plan.id');
|
||||||
$subscription->update([
|
$subscription->update([
|
||||||
'stripe_plan_id' => $planId,
|
'stripe_plan_id' => $planId,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
## Always run "php artisan app:sync-to-bunny-cdn --env=secrets" or "scripts/run sync-bunny" if you update this file.
|
## Always run "php artisan app:sync-to-bunny-cdn --env=secrets" or "scripts/run sync-bunny" if you update this file.
|
||||||
###########
|
###########
|
||||||
|
|
||||||
VERSION="1.0.1"
|
VERSION="1.0.3"
|
||||||
DOCKER_VERSION="24.0"
|
DOCKER_VERSION="24.0"
|
||||||
|
|
||||||
CDN="https://cdn.coollabs.io/coolify"
|
CDN="https://cdn.coollabs.io/coolify"
|
||||||
@@ -46,7 +46,14 @@ apt install -y curl wget git jq jc >/dev/null 2>&1
|
|||||||
if ! [ -x "$(command -v docker)" ]; then
|
if ! [ -x "$(command -v docker)" ]; then
|
||||||
echo "Docker is not installed. Installing Docker..."
|
echo "Docker is not installed. Installing Docker..."
|
||||||
curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh
|
curl https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh
|
||||||
echo "Docker installed successfully"
|
if [ -x "$(command -v docker)" ]; then
|
||||||
|
echo "Docker installed successfully."
|
||||||
|
else
|
||||||
|
echo "Docker installation failed."
|
||||||
|
echo "Maybe your OS is not supported."
|
||||||
|
echo "Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
echo -e "-------------"
|
echo -e "-------------"
|
||||||
echo -e "Check Docker Configuration..."
|
echo -e "Check Docker Configuration..."
|
||||||
@@ -94,8 +101,8 @@ mkdir -p /data/coolify/ssh/mux
|
|||||||
mkdir -p /data/coolify/source
|
mkdir -p /data/coolify/source
|
||||||
mkdir -p /data/coolify/proxy/dynamic
|
mkdir -p /data/coolify/proxy/dynamic
|
||||||
|
|
||||||
chown -R 9999:root /data
|
chown -R 9999:root /data/coolify
|
||||||
chmod -R 700 /data
|
chmod -R 700 /data/coolify
|
||||||
|
|
||||||
echo "Downloading required files from CDN..."
|
echo "Downloading required files from CDN..."
|
||||||
curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
|
curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ _APP_LOCALE=en
|
|||||||
_APP_OPTIONS_ABUSE=enabled
|
_APP_OPTIONS_ABUSE=enabled
|
||||||
_APP_OPTIONS_FORCE_HTTPS=disabled
|
_APP_OPTIONS_FORCE_HTTPS=disabled
|
||||||
_APP_OPENSSL_KEY_V1=
|
_APP_OPENSSL_KEY_V1=
|
||||||
|
_APP_DOMAIN=
|
||||||
|
_APP_DOMAIN_TARGET=
|
||||||
_APP_DOMAIN_FUNCTIONS=
|
_APP_DOMAIN_FUNCTIONS=
|
||||||
_APP_CONSOLE_WHITELIST_ROOT=enabled
|
_APP_CONSOLE_WHITELIST_ROOT=enabled
|
||||||
_APP_CONSOLE_WHITELIST_EMAILS=
|
_APP_CONSOLE_WHITELIST_EMAILS=
|
||||||
@@ -18,19 +20,19 @@ _APP_USAGE_AGGREGATION_INTERVAL=30
|
|||||||
_APP_USAGE_TIMESERIES_INTERVAL=30
|
_APP_USAGE_TIMESERIES_INTERVAL=30
|
||||||
_APP_USAGE_DATABASE_INTERVAL=900
|
_APP_USAGE_DATABASE_INTERVAL=900
|
||||||
_APP_WORKER_PER_CORE=6
|
_APP_WORKER_PER_CORE=6
|
||||||
_APP_REDIS_HOST=redis
|
_APP_REDIS_HOST=appwrite-redis
|
||||||
_APP_REDIS_PORT=6379
|
_APP_REDIS_PORT=6379
|
||||||
_APP_REDIS_USER=
|
_APP_REDIS_USER=
|
||||||
_APP_REDIS_PASS=
|
_APP_REDIS_PASS=
|
||||||
_APP_DB_HOST=mariadb
|
_APP_DB_HOST=appwrite-mariadb
|
||||||
_APP_DB_PORT=3306
|
_APP_DB_PORT=3306
|
||||||
_APP_DB_SCHEMA=appwrite
|
_APP_DB_SCHEMA=appwrite
|
||||||
_APP_DB_USER=$SERVICE_USER_MYSQL
|
_APP_DB_USER=$SERVICE_USER_MYSQL
|
||||||
_APP_DB_PASS=$SERVICE_PASSWORD_MYSQL
|
_APP_DB_PASS=$SERVICE_PASSWORD_MYSQL
|
||||||
_APP_DB_ROOT_PASS=$SERVICE_PASSWORD_ROOTMYSQL
|
_APP_DB_ROOT_PASS=$SERVICE_PASSWORD_ROOTMYSQL
|
||||||
_APP_INFLUXDB_HOST=influxdb
|
_APP_INFLUXDB_HOST=appwrite-influxdb
|
||||||
_APP_INFLUXDB_PORT=8086
|
_APP_INFLUXDB_PORT=8086
|
||||||
_APP_STATSD_HOST=telegraf
|
_APP_STATSD_HOST=appwrite-telegraf
|
||||||
_APP_STATSD_PORT=8125
|
_APP_STATSD_PORT=8125
|
||||||
_APP_SMTP_HOST=
|
_APP_SMTP_HOST=
|
||||||
_APP_SMTP_PORT=
|
_APP_SMTP_PORT=
|
||||||
@@ -42,7 +44,7 @@ _APP_SMS_FROM=
|
|||||||
_APP_STORAGE_LIMIT=30000000
|
_APP_STORAGE_LIMIT=30000000
|
||||||
_APP_STORAGE_PREVIEW_LIMIT=20000000
|
_APP_STORAGE_PREVIEW_LIMIT=20000000
|
||||||
_APP_STORAGE_ANTIVIRUS=disabled
|
_APP_STORAGE_ANTIVIRUS=disabled
|
||||||
_APP_STORAGE_ANTIVIRUS_HOST=clamav
|
_APP_STORAGE_ANTIVIRUS_HOST=appwrite-clamav
|
||||||
_APP_STORAGE_ANTIVIRUS_PORT=3310
|
_APP_STORAGE_ANTIVIRUS_PORT=3310
|
||||||
_APP_STORAGE_DEVICE=local
|
_APP_STORAGE_DEVICE=local
|
||||||
_APP_STORAGE_S3_ACCESS_KEY=
|
_APP_STORAGE_S3_ACCESS_KEY=
|
||||||
@@ -72,11 +74,10 @@ _APP_FUNCTIONS_CONTAINERS=10
|
|||||||
_APP_FUNCTIONS_CPUS=0
|
_APP_FUNCTIONS_CPUS=0
|
||||||
_APP_FUNCTIONS_MEMORY=0
|
_APP_FUNCTIONS_MEMORY=0
|
||||||
_APP_FUNCTIONS_MEMORY_SWAP=0
|
_APP_FUNCTIONS_MEMORY_SWAP=0
|
||||||
_APP_FUNCTIONS_RUNTIMES=node-16.0,php-8.0,python-3.9,ruby-3.0
|
_APP_FUNCTIONS_RUNTIMES=node-20.0,php-8.2,python-3.11,ruby-3.2
|
||||||
_APP_EXECUTOR_SECRET=your-secret-key
|
_APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
|
||||||
_APP_EXECUTOR_HOST=http://appwrite-executor/v1
|
_APP_EXECUTOR_HOST=http://appwrite-executor/v1
|
||||||
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes
|
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes
|
||||||
_APP_FUNCTIONS_ENVS=node-16.0,php-7.4,python-3.9,ruby-3.0
|
|
||||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD=60
|
_APP_FUNCTIONS_INACTIVE_THRESHOLD=60
|
||||||
DOCKERHUB_PULL_USERNAME=
|
DOCKERHUB_PULL_USERNAME=
|
||||||
DOCKERHUB_PULL_PASSWORD=
|
DOCKERHUB_PULL_PASSWORD=
|
||||||
|
|||||||
@@ -17,19 +17,6 @@ services:
|
|||||||
image: appwrite/appwrite:1.4
|
image: appwrite/appwrite:1.4
|
||||||
container_name: appwrite
|
container_name: appwrite
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
labels:
|
|
||||||
- traefik.constraint-label-stack=appwrite
|
|
||||||
- traefik.docker.network=appwrite
|
|
||||||
- traefik.http.services.appwrite_api.loadbalancer.server.port=80
|
|
||||||
#http
|
|
||||||
- traefik.http.routers.appwrite_api_http.entrypoints=web
|
|
||||||
- traefik.http.routers.appwrite_api_http.rule=PathPrefix(`/`)
|
|
||||||
- traefik.http.routers.appwrite_api_http.service=appwrite_api
|
|
||||||
# https
|
|
||||||
- traefik.http.routers.appwrite_api_https.entrypoints=websecure
|
|
||||||
- traefik.http.routers.appwrite_api_https.rule=PathPrefix(`/`)
|
|
||||||
- traefik.http.routers.appwrite_api_https.service=appwrite_api
|
|
||||||
- traefik.http.routers.appwrite_api_https.tls=true
|
|
||||||
volumes:
|
volumes:
|
||||||
- appwrite-uploads:/storage/uploads:rw
|
- appwrite-uploads:/storage/uploads:rw
|
||||||
- appwrite-cache:/storage/cache:rw
|
- appwrite-cache:/storage/cache:rw
|
||||||
@@ -37,10 +24,10 @@ services:
|
|||||||
- appwrite-certificates:/storage/certificates:rw
|
- appwrite-certificates:/storage/certificates:rw
|
||||||
- appwrite-functions:/storage/functions:rw
|
- appwrite-functions:/storage/functions:rw
|
||||||
depends_on:
|
depends_on:
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
- redis
|
- appwrite-redis
|
||||||
# - clamav
|
# - appwrite-clamav
|
||||||
- influxdb
|
- appwrite-influxdb
|
||||||
environment:
|
environment:
|
||||||
- SERVICE_FQDN_APPWRITE=/
|
- SERVICE_FQDN_APPWRITE=/
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
@@ -56,9 +43,9 @@ services:
|
|||||||
- _APP_OPTIONS_ABUSE
|
- _APP_OPTIONS_ABUSE
|
||||||
- _APP_OPTIONS_FORCE_HTTPS
|
- _APP_OPTIONS_FORCE_HTTPS
|
||||||
- _APP_OPENSSL_KEY_V1
|
- _APP_OPENSSL_KEY_V1
|
||||||
- _APP_DOMAIN
|
- _APP_DOMAIN=$SERVICE_FQDN_APPWRITE
|
||||||
- _APP_DOMAIN_TARGET
|
- _APP_DOMAIN_TARGET=$SERVICE_FQDN_APPWRITE
|
||||||
- _APP_DOMAIN_FUNCTIONS
|
- _APP_DOMAIN_FUNCTIONS=$SERVICE_FQDN_APPWRITE
|
||||||
- _APP_REDIS_HOST
|
- _APP_REDIS_HOST
|
||||||
- _APP_REDIS_PORT
|
- _APP_REDIS_PORT
|
||||||
- _APP_REDIS_USER
|
- _APP_REDIS_USER
|
||||||
@@ -137,29 +124,12 @@ services:
|
|||||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
- _APP_ASSISTANT_OPENAI_API_KEY
|
||||||
|
|
||||||
appwrite-realtime:
|
appwrite-realtime:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: realtime
|
entrypoint: realtime
|
||||||
container_name: appwrite-realtime
|
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
labels:
|
|
||||||
- "traefik.constraint-label-stack=appwrite"
|
|
||||||
- "traefik.docker.network=appwrite"
|
|
||||||
- "traefik.http.services.appwrite_realtime.loadbalancer.server.port=80"
|
|
||||||
#ws
|
|
||||||
- traefik.http.routers.appwrite_realtime_ws.entrypoints=web
|
|
||||||
- traefik.http.routers.appwrite_realtime_ws.rule=PathPrefix(`/v1/realtime`)
|
|
||||||
- traefik.http.routers.appwrite_realtime_ws.service=appwrite_realtime
|
|
||||||
# wss
|
|
||||||
- traefik.http.routers.appwrite_realtime_wss.entrypoints=websecure
|
|
||||||
- traefik.http.routers.appwrite_realtime_wss.rule=PathPrefix(`/v1/realtime`)
|
|
||||||
- traefik.http.routers.appwrite_realtime_wss.service=appwrite_realtime
|
|
||||||
- traefik.http.routers.appwrite_realtime_wss.tls=true
|
|
||||||
- traefik.http.routers.appwrite_realtime_wss.tls.certresolver=dns
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
- redis
|
- appwrite-redis
|
||||||
environment:
|
environment:
|
||||||
- SERVICE_FQDN_APPWRITE=/v1/realtime
|
- SERVICE_FQDN_APPWRITE=/v1/realtime
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
@@ -180,16 +150,13 @@ services:
|
|||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
appwrite-worker-audits:
|
appwrite-worker-audits:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-audits
|
entrypoint: worker-audits
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-audits
|
container_name: appwrite-worker-audits
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
@@ -207,16 +174,13 @@ services:
|
|||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
appwrite-worker-webhooks:
|
appwrite-worker-webhooks:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-webhooks
|
entrypoint: worker-webhooks
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-webhooks
|
container_name: appwrite-worker-webhooks
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
@@ -230,16 +194,13 @@ services:
|
|||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
appwrite-worker-deletes:
|
appwrite-worker-deletes:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-deletes
|
entrypoint: worker-deletes
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-deletes
|
container_name: appwrite-worker-deletes
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
volumes:
|
volumes:
|
||||||
- appwrite-uploads:/storage/uploads:rw
|
- appwrite-uploads:/storage/uploads:rw
|
||||||
- appwrite-cache:/storage/cache:rw
|
- appwrite-cache:/storage/cache:rw
|
||||||
@@ -286,16 +247,13 @@ services:
|
|||||||
- _APP_EXECUTOR_HOST
|
- _APP_EXECUTOR_HOST
|
||||||
|
|
||||||
appwrite-worker-databases:
|
appwrite-worker-databases:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-databases
|
entrypoint: worker-databases
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-databases
|
container_name: appwrite-worker-databases
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
@@ -313,16 +271,13 @@ services:
|
|||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
appwrite-worker-builds:
|
appwrite-worker-builds:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-builds
|
entrypoint: worker-builds
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-builds
|
container_name: appwrite-worker-builds
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
volumes:
|
volumes:
|
||||||
- appwrite-functions:/storage/functions:rw
|
- appwrite-functions:/storage/functions:rw
|
||||||
- appwrite-builds:/storage/builds:rw
|
- appwrite-builds:/storage/builds:rw
|
||||||
@@ -375,16 +330,13 @@ services:
|
|||||||
- _APP_STORAGE_WASABI_BUCKET
|
- _APP_STORAGE_WASABI_BUCKET
|
||||||
|
|
||||||
appwrite-worker-certificates:
|
appwrite-worker-certificates:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-certificates
|
entrypoint: worker-certificates
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-certificates
|
container_name: appwrite-worker-certificates
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
volumes:
|
volumes:
|
||||||
- appwrite-config:/storage/config:rw
|
- appwrite-config:/storage/config:rw
|
||||||
- appwrite-certificates:/storage/certificates:rw
|
- appwrite-certificates:/storage/certificates:rw
|
||||||
@@ -409,16 +361,13 @@ services:
|
|||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
appwrite-worker-functions:
|
appwrite-worker-functions:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-functions
|
entrypoint: worker-functions
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-functions
|
container_name: appwrite-worker-functions
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
- openruntimes-executor
|
- openruntimes-executor
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
@@ -446,15 +395,12 @@ services:
|
|||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
|
|
||||||
appwrite-worker-mails:
|
appwrite-worker-mails:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-mails
|
entrypoint: worker-mails
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-mails
|
container_name: appwrite-worker-mails
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
@@ -474,15 +420,12 @@ services:
|
|||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
appwrite-worker-messaging:
|
appwrite-worker-messaging:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-messaging
|
entrypoint: worker-messaging
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-messaging
|
container_name: appwrite-worker-messaging
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
@@ -496,15 +439,12 @@ services:
|
|||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
appwrite-worker-migrations:
|
appwrite-worker-migrations:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: worker-migrations
|
entrypoint: worker-migrations
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-migrations
|
container_name: appwrite-worker-migrations
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
@@ -527,15 +467,12 @@ services:
|
|||||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||||
|
|
||||||
appwrite-maintenance:
|
appwrite-maintenance:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: maintenance
|
entrypoint: maintenance
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-maintenance
|
container_name: appwrite-maintenance
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- appwrite-redis
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
@@ -561,16 +498,13 @@ services:
|
|||||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||||
|
|
||||||
appwrite-usage:
|
appwrite-usage:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: usage
|
entrypoint: usage
|
||||||
container_name: appwrite-usage
|
container_name: appwrite-usage
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- influxdb
|
- appwrite-influxdb
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
@@ -592,16 +526,13 @@ services:
|
|||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
|
|
||||||
appwrite-schedule:
|
appwrite-schedule:
|
||||||
image: appwrite/appwrite:1.4.3
|
image: appwrite/appwrite:1.4
|
||||||
entrypoint: schedule
|
entrypoint: schedule
|
||||||
container_name: appwrite-schedule
|
container_name: appwrite-schedule
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- mariadb
|
- appwrite-mariadb
|
||||||
- redis
|
- appwrite-redis
|
||||||
environment:
|
environment:
|
||||||
- _APP_ENV
|
- _APP_ENV
|
||||||
- _APP_WORKER_PER_CORE
|
- _APP_WORKER_PER_CORE
|
||||||
@@ -616,15 +547,13 @@ services:
|
|||||||
- _APP_DB_USER
|
- _APP_DB_USER
|
||||||
- _APP_DB_PASS
|
- _APP_DB_PASS
|
||||||
|
|
||||||
appwrite-assistant:
|
# appwrite-assistant:
|
||||||
image: appwrite/assistant:0.2.1
|
# image: appwrite/assistant:0.2.1
|
||||||
container_name: appwrite-assistant
|
# container_name: appwrite-assistant
|
||||||
<<: *x-logging
|
# <<: *x-logging
|
||||||
restart: unless-stopped
|
#
|
||||||
networks:
|
# environment:
|
||||||
- appwrite
|
# - _APP_ASSISTANT_OPENAI_API_KEY
|
||||||
environment:
|
|
||||||
- _APP_ASSISTANT_OPENAI_API_KEY
|
|
||||||
|
|
||||||
openruntimes-executor:
|
openruntimes-executor:
|
||||||
container_name: openruntimes-executor
|
container_name: openruntimes-executor
|
||||||
@@ -632,9 +561,6 @@ services:
|
|||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
stop_signal: SIGINT
|
stop_signal: SIGINT
|
||||||
image: openruntimes/executor:0.4.1
|
image: openruntimes/executor:0.4.1
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
- runtimes
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- appwrite-builds:/storage/builds:rw
|
- appwrite-builds:/storage/builds:rw
|
||||||
@@ -675,13 +601,10 @@ services:
|
|||||||
- OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION
|
- OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION
|
||||||
- OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET
|
- OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET
|
||||||
|
|
||||||
mariadb:
|
appwrite-mariadb:
|
||||||
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
|
image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p
|
||||||
container_name: appwrite-mariadb
|
container_name: appwrite-mariadb
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
volumes:
|
volumes:
|
||||||
- appwrite-mariadb:/var/lib/mysql:rw
|
- appwrite-mariadb:/var/lib/mysql:rw
|
||||||
environment:
|
environment:
|
||||||
@@ -691,59 +614,40 @@ services:
|
|||||||
- MYSQL_PASSWORD=${_APP_DB_PASS}
|
- MYSQL_PASSWORD=${_APP_DB_PASS}
|
||||||
command: 'mysqld --innodb-flush-method=fsync'
|
command: 'mysqld --innodb-flush-method=fsync'
|
||||||
|
|
||||||
redis:
|
appwrite-redis:
|
||||||
image: redis:7.0.4-alpine
|
image: redis:7.0.4-alpine
|
||||||
container_name: appwrite-redis
|
container_name: appwrite-redis
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
restart: unless-stopped
|
|
||||||
command: >
|
command: >
|
||||||
redis-server
|
redis-server
|
||||||
--maxmemory 512mb
|
--maxmemory 512mb
|
||||||
--maxmemory-policy allkeys-lru
|
--maxmemory-policy allkeys-lru
|
||||||
--maxmemory-samples 5
|
--maxmemory-samples 5
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
volumes:
|
volumes:
|
||||||
- appwrite-redis:/data:rw
|
- appwrite-redis:/data:rw
|
||||||
|
|
||||||
# clamav:
|
# appwrite-clamav:
|
||||||
# image: appwrite/clamav:1.2.0
|
# image: appwrite/clamav:1.2.0
|
||||||
# container_name: appwrite-clamav
|
# container_name: appwrite-clamav
|
||||||
# restart: unless-stopped
|
#
|
||||||
# networks:
|
|
||||||
# - appwrite
|
|
||||||
# volumes:
|
# volumes:
|
||||||
# - appwrite-uploads:/storage/uploads
|
# - appwrite-uploads:/storage/uploads
|
||||||
|
|
||||||
influxdb:
|
appwrite-influxdb:
|
||||||
image: appwrite/influxdb:1.5.0
|
image: appwrite/influxdb:1.5.0
|
||||||
container_name: appwrite-influxdb
|
container_name: appwrite-influxdb
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
volumes:
|
volumes:
|
||||||
- appwrite-influxdb:/var/lib/influxdb:rw
|
- appwrite-influxdb:/var/lib/influxdb:rw
|
||||||
|
|
||||||
telegraf:
|
appwrite-telegraf:
|
||||||
image: appwrite/telegraf:1.4.0
|
image: appwrite/telegraf:1.4.0
|
||||||
container_name: appwrite-telegraf
|
container_name: appwrite-telegraf
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- appwrite
|
|
||||||
environment:
|
environment:
|
||||||
- _APP_INFLUXDB_HOST
|
- _APP_INFLUXDB_HOST
|
||||||
- _APP_INFLUXDB_PORT
|
- _APP_INFLUXDB_PORT
|
||||||
|
|
||||||
networks:
|
|
||||||
gateway:
|
|
||||||
name: gateway
|
|
||||||
appwrite:
|
|
||||||
name: appwrite
|
|
||||||
runtimes:
|
|
||||||
name: runtimes
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
appwrite-mariadb:
|
appwrite-mariadb:
|
||||||
appwrite-redis:
|
appwrite-redis:
|
||||||
|
|||||||
19
templates/compose/budge.yaml
Normal file
19
templates/compose/budge.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# documentation: https://github.com/linuxserver/budge
|
||||||
|
# slogan: BudgE is an open-source 'budgeting with envelopes' personal finance app, helping you manage your finances effectively.
|
||||||
|
# tags: personal finance, budgeting, expense tracking
|
||||||
|
|
||||||
|
services:
|
||||||
|
budge:
|
||||||
|
image: lscr.io/linuxserver/budge:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_BUDGE
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Madrid
|
||||||
|
volumes:
|
||||||
|
- budge-config:/config
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 15
|
||||||
20
templates/compose/duplicati.yaml
Normal file
20
templates/compose/duplicati.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# documentation: https://duplicati.readthedocs.io/en/latest/02-installation/
|
||||||
|
# slogan: Duplicati is an open-source backup solution, allowing you to safeguard your data with ease through scheduled backups and encryption.
|
||||||
|
# tags: backup, encryption
|
||||||
|
|
||||||
|
services:
|
||||||
|
duplicati:
|
||||||
|
image: lscr.io/linuxserver/duplicati:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_DUPLICATI
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Madrid
|
||||||
|
volumes:
|
||||||
|
- duplicati-config:/config
|
||||||
|
- duplicati-backups:/backups
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8200"]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 15
|
||||||
20
templates/compose/filebrowser.yaml
Normal file
20
templates/compose/filebrowser.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# documentation: https://filebrowser.org/configuration
|
||||||
|
# slogan: FileBrowser is a self-hosted, web-based file manager and file explorer with a user-friendly interface. It allows you to manage and organize your files and directories directly from your web browser.
|
||||||
|
# tags: file-management, storage-access, data-organization, file-utilization, administration-tool
|
||||||
|
|
||||||
|
services:
|
||||||
|
filebrowser:
|
||||||
|
image: filebrowser/filebrowser:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_FILEBROWSER
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
volumes:
|
||||||
|
- filebrowser-srv:/srv
|
||||||
|
- filebrowser-database:/database/filebrowser.db
|
||||||
|
- filebrowser-config:/config/settings.json
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:80"]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 15
|
||||||
45
templates/compose/gitea-with-mariadb.yaml
Normal file
45
templates/compose/gitea-with-mariadb.yaml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# documentation: https://docs.gitea.com
|
||||||
|
# slogan: Gitea (with MariaDB) is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.
|
||||||
|
# tags: version control, collaboration, code, hosting, lightweight, mariadb
|
||||||
|
|
||||||
|
services:
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_GITEA_3000
|
||||||
|
- USER_UID=1000
|
||||||
|
- USER_GID=1000
|
||||||
|
- GITEA__database__DB_TYPE=mysql
|
||||||
|
- GITEA__database__HOST=mariadb
|
||||||
|
- GITEA__database__NAME=${MYSQL_DATABASE-gitea}
|
||||||
|
- GITEA__database__USER=$SERVICE_USER_MYSQL
|
||||||
|
- GITEA__database__PASSWD=$SERVICE_PASSWORD_MYSQL
|
||||||
|
volumes:
|
||||||
|
- gitea-data:/var/lib/gitea
|
||||||
|
- gitea-timezone:/etc/timezone:ro
|
||||||
|
- gitea-localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- 22222:22
|
||||||
|
depends_on:
|
||||||
|
mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 15
|
||||||
|
|
||||||
|
mariadb:
|
||||||
|
image: mariadb:11
|
||||||
|
volumes:
|
||||||
|
- gitea-mariadb-data:/var/lib/mysql
|
||||||
|
environment:
|
||||||
|
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
||||||
|
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
||||||
|
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
||||||
|
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 10
|
||||||
45
templates/compose/gitea-with-mysql.yaml
Normal file
45
templates/compose/gitea-with-mysql.yaml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# documentation: https://docs.gitea.com
|
||||||
|
# slogan: Gitea (with MySQL) is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.
|
||||||
|
# tags: version control, collaboration, code, hosting, lightweight, mysql
|
||||||
|
|
||||||
|
services:
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_GITEA_3000
|
||||||
|
- USER_UID=1000
|
||||||
|
- USER_GID=1000
|
||||||
|
- GITEA__database__DB_TYPE=mysql
|
||||||
|
- GITEA__database__HOST=mysql
|
||||||
|
- GITEA__database__NAME=${MYSQL_DATABASE-gitea}
|
||||||
|
- GITEA__database__USER=$SERVICE_USER_MYSQL
|
||||||
|
- GITEA__database__PASSWD=$SERVICE_PASSWORD_MYSQL
|
||||||
|
volumes:
|
||||||
|
- gitea-data:/var/lib/gitea
|
||||||
|
- gitea-timezone:/etc/timezone:ro
|
||||||
|
- gitea-localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- 22222:22
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 15
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
volumes:
|
||||||
|
- gitea-mysql-data:/var/lib/mysql
|
||||||
|
environment:
|
||||||
|
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
||||||
|
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
||||||
|
- MYSQL_DATABASE=${MYSQL_DATABASE}
|
||||||
|
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 10
|
||||||
44
templates/compose/gitea-with-postgresql.yaml
Normal file
44
templates/compose/gitea-with-postgresql.yaml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# documentation: https://docs.gitea.com
|
||||||
|
# slogan: Gitea (with PostgreSQL)vis a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.
|
||||||
|
# tags: version control, collaboration, code, hosting, lightweight, postgresql
|
||||||
|
|
||||||
|
services:
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_GITEA_3000
|
||||||
|
- USER_UID=1000
|
||||||
|
- USER_GID=1000
|
||||||
|
- GITEA__database__DB_TYPE=postgres
|
||||||
|
- GITEA__database__HOST=postgresql
|
||||||
|
- GITEA__database__NAME=${POSTGRESQL_DATABASE-gitea}
|
||||||
|
- GITEA__database__USER=$SERVICE_USER_POSTGRESQL
|
||||||
|
- GITEA__database__PASSWD=$SERVICE_PASSWORD_POSTGRESQL
|
||||||
|
volumes:
|
||||||
|
- gitea-data:/var/lib/gitea
|
||||||
|
- gitea-timezone:/etc/timezone:ro
|
||||||
|
- gitea-localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- 22222:22
|
||||||
|
depends_on:
|
||||||
|
postgresql:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 15
|
||||||
|
|
||||||
|
postgresql:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
volumes:
|
||||||
|
- gitea-postgresql-data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=${SERVICE_USER_POSTGRESQL}
|
||||||
|
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
|
||||||
|
- POSTGRES_DB=${POSTGRESQL_DATABASE}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 10
|
||||||
22
templates/compose/gitea.yaml
Normal file
22
templates/compose/gitea.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# documentation: https://docs.gitea.com
|
||||||
|
# slogan: Gitea is a self-hosted, lightweight Git service, offering version control, collaboration, and code hosting.
|
||||||
|
# tags: version control, collaboration, code, hosting, lightweight
|
||||||
|
|
||||||
|
services:
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_GITEA_3000
|
||||||
|
- USER_UID=1000
|
||||||
|
- USER_GID=1000
|
||||||
|
ports:
|
||||||
|
- 22222:22
|
||||||
|
volumes:
|
||||||
|
- gitea-data:/var/lib/gitea
|
||||||
|
- gitea-timezone:/etc/timezone:ro
|
||||||
|
- gitea-localtime:/etc/localtime:ro
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 15
|
||||||
22
templates/compose/jellyfin.yaml
Normal file
22
templates/compose/jellyfin.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# documentation: https://jellyfin.org/docs/
|
||||||
|
# slogan: Jellyfin is an open-source media server for hosting and streaming your media collection, providing an alternative to proprietary media platforms.
|
||||||
|
# tags: media, server, movies, tv, music
|
||||||
|
|
||||||
|
services:
|
||||||
|
jellyfin:
|
||||||
|
image: lscr.io/linuxserver/jellyfin:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_JELLYFIN
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Madrid
|
||||||
|
- JELLYFIN_PublishedServerUrl=$SERVICE_FQDN_JELLYFIN
|
||||||
|
volumes:
|
||||||
|
- jellyfin-config:/config
|
||||||
|
- jellyfin-tvshows:/data/tvshows
|
||||||
|
- jellyfin-movies:/data/movies
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:8096"]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 15
|
||||||
60
templates/compose/kuzzle.yaml
Normal file
60
templates/compose/kuzzle.yaml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# documentation: https://docs.kuzzle.io/
|
||||||
|
# slogan: Kuzzle is a generic backend offering the basic building blocks common to every application.
|
||||||
|
# tags: backend, api, realtime, websocket, mqtt, rest, sdk, iot, geofencing, low-code
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:6.2.4
|
||||||
|
command: redis-server --appendonly yes
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "redis-cli", "ping" ]
|
||||||
|
interval: 1s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 30
|
||||||
|
|
||||||
|
elasticsearch:
|
||||||
|
image: kuzzleio/elasticsearch:7
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "curl", "-f", "http://localhost:9200" ]
|
||||||
|
interval: 2s
|
||||||
|
timeout: 2s
|
||||||
|
retries: 10
|
||||||
|
ulimits:
|
||||||
|
nofile: 65536
|
||||||
|
|
||||||
|
kuzzle:
|
||||||
|
image: kuzzleio/kuzzle:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_KUZZLE_7512
|
||||||
|
- kuzzle_services__storageEngine__client__node=http://elasticsearch:9200
|
||||||
|
- kuzzle_services__storageEngine__commonMapping__dynamic=true
|
||||||
|
- kuzzle_services__internalCache__node__host=redis
|
||||||
|
- kuzzle_services__memoryStorage__node__host=redis
|
||||||
|
- kuzzle_server__protocols__mqtt__enabled=true
|
||||||
|
- kuzzle_server__protocols__mqtt__developmentMode=false
|
||||||
|
- kuzzle_limits__loginsPerSecond=50
|
||||||
|
- NODE_ENV=production
|
||||||
|
# - DEBUG=${DEBUG:-kuzzle:*,-kuzzle:network:protocols:websocket,-kuzzle:events}
|
||||||
|
- DEBUG=${DEBUG:-kuzzle:cluster:sync}
|
||||||
|
- DEBUG_DEPTH=${DEBUG_DEPTH:-0}
|
||||||
|
- DEBUG_MAX_ARRAY_LENGTH=${DEBUG_MAX_ARRAY:-100}
|
||||||
|
- DEBUG_EXPAND=${DEBUG_EXPAND:-off}
|
||||||
|
- DEBUG_SHOW_HIDDEN={$DEBUG_SHOW_HIDDEN:-on}
|
||||||
|
- DEBUG_COLORS=${DEBUG_COLORS:-on}
|
||||||
|
cap_add:
|
||||||
|
- SYS_PTRACE
|
||||||
|
ulimits:
|
||||||
|
nofile: 65536
|
||||||
|
sysctls:
|
||||||
|
- net.core.somaxconn=8192
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD", "curl", "-f", "http://localhost:7512/_healthcheck" ]
|
||||||
|
timeout: 1s
|
||||||
|
interval: 2s
|
||||||
|
retries: 30
|
||||||
|
depends_on:
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
elasticsearch:
|
||||||
|
condition: service_healthy
|
||||||
37
templates/compose/moodle.yaml
Normal file
37
templates/compose/moodle.yaml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# documentation: https://moodle.org
|
||||||
|
# slogan: Moodle is the world’s most customisable and trusted eLearning solution that empowers educators to improve our world.
|
||||||
|
# tags: moodle, elearning, education, lms, cms, open, source, low, code
|
||||||
|
|
||||||
|
services:
|
||||||
|
mariadb:
|
||||||
|
image: mariadb:11.1
|
||||||
|
environment:
|
||||||
|
- ALLOW_EMPTY_PASSWORD=no
|
||||||
|
- MYSQL_ROOT_PASSWORD=$SERVICE_PASSWORD_ROOT
|
||||||
|
- MYSQL_DATABASE=bitnami_moodle
|
||||||
|
- MYSQL_USER=$SERVICE_USER_MARIADB
|
||||||
|
- MYSQL_PASSWORD=$SERVICE_PASSWORD_MARIADB
|
||||||
|
- MARIADB_CHARACTER_SET=utf8mb4
|
||||||
|
- MARIADB_COLLATE=utf8mb4_unicode_ci
|
||||||
|
volumes:
|
||||||
|
- mariadb-data:/var/lib/mysql
|
||||||
|
|
||||||
|
moodle:
|
||||||
|
image: docker.io/bitnami/moodle:4.3
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_MOODLE
|
||||||
|
- MOODLE_DATABASE_HOST=mariadb
|
||||||
|
- MOODLE_DATABASE_PORT_NUMBER=3306
|
||||||
|
- MOODLE_DATABASE_USER=$SERVICE_USER_MARIADB
|
||||||
|
- MOODLE_DATABASE_NAME=bitnami_moodle
|
||||||
|
- MOODLE_DATABASE_PASSWORD=$SERVICE_PASSWORD_MARIADB
|
||||||
|
- ALLOW_EMPTY_PASSWORD=no
|
||||||
|
- MOODLE_USERNAME=${MOODLE_USERNAME:-user}
|
||||||
|
- MOODLE_PASSWORD=$SERVICE_PASSWORD_MOODLE
|
||||||
|
- MOODLE_EMAIL=user@example.com
|
||||||
|
- MOODLE_SITE_NAME=${MOODLE_SITE_NAME:-New Site}
|
||||||
|
volumes:
|
||||||
|
- moodle-data:/bitnami/moodle
|
||||||
|
- moodledata-data:/bitnami/moodledata
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user