mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 20:59:24 +00:00
Compare commits
143 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dd00dd91a | ||
|
|
fd97c5085b | ||
|
|
fa6a249fb4 | ||
|
|
0aa9b1735b | ||
|
|
6027bee3b8 | ||
|
|
4d72787c83 | ||
|
|
c6740cfea0 | ||
|
|
9c1d585c43 | ||
|
|
863acf988e | ||
|
|
a6b3beafbb | ||
|
|
2ffc3f497b | ||
|
|
f3a279be26 | ||
|
|
9ad6631747 | ||
|
|
0131f5e341 | ||
|
|
b5ab9a8da6 | ||
|
|
57fa2709da | ||
|
|
96c6a198d7 | ||
|
|
d106d4bd4e | ||
|
|
53cd3091f7 | ||
|
|
f1e7b870aa | ||
|
|
99fe076b5a | ||
|
|
65fcaa17d9 | ||
|
|
89c6563e00 | ||
|
|
76b0bef32e | ||
|
|
c20aa0b256 | ||
|
|
b4908cfcb4 | ||
|
|
4fb5b04d27 | ||
|
|
8385bbb0a0 | ||
|
|
cee6b54033 | ||
|
|
0dd591a5ff | ||
|
|
62278126e4 | ||
|
|
0aa85a3701 | ||
|
|
0e1ba64836 | ||
|
|
f7e1ce8656 | ||
|
|
5030c14dc2 | ||
|
|
1333cd1d84 | ||
|
|
112c259d27 | ||
|
|
130d1e1756 | ||
|
|
a7df9fa625 | ||
|
|
9064aedc89 | ||
|
|
fda5d23d32 | ||
|
|
60be51dbe0 | ||
|
|
e9f451339f | ||
|
|
4d8ffd05a9 | ||
|
|
b630105572 | ||
|
|
9fa71f847f | ||
|
|
f70a9c6974 | ||
|
|
a4d173c733 | ||
|
|
2eb7712e09 | ||
|
|
54923b7640 | ||
|
|
bb927505fe | ||
|
|
5e66e314d2 | ||
|
|
6fe791c1f1 | ||
|
|
860c537f81 | ||
|
|
a352e4cbf7 | ||
|
|
5322d446bd | ||
|
|
604ab0afd8 | ||
|
|
3d87a88d3d | ||
|
|
10f9e22a8e | ||
|
|
8edda0cdda | ||
|
|
21047afc02 | ||
|
|
2e9793ffb2 | ||
|
|
fcd100df39 | ||
|
|
dfd564a3a4 | ||
|
|
a43c916009 | ||
|
|
c8332ca9bf | ||
|
|
e98170f921 | ||
|
|
b8f25406cd | ||
|
|
76dcc12b13 | ||
|
|
baa2228c9b | ||
|
|
5275ae8e9c | ||
|
|
c71e1e107e | ||
|
|
8ab72c7e10 | ||
|
|
a8970df91b | ||
|
|
2468251f56 | ||
|
|
6e74f3e40e | ||
|
|
407f84a4bb | ||
|
|
91632f0adb | ||
|
|
af3c575d84 | ||
|
|
bf1475441d | ||
|
|
9268f9db1d | ||
|
|
600c43827a | ||
|
|
74092ea95b | ||
|
|
b67abe58e8 | ||
|
|
678647f39a | ||
|
|
453956172b | ||
|
|
b550c32f9b | ||
|
|
f6b886adbc | ||
|
|
9642453052 | ||
|
|
64fca99c26 | ||
|
|
c7da43f50d | ||
|
|
6efa2dd9ba | ||
|
|
5e980c5fe0 | ||
|
|
c8c7a415ea | ||
|
|
c3cfb8d23b | ||
|
|
1b055f0316 | ||
|
|
1fcbf0b363 | ||
|
|
61dbc81765 | ||
|
|
b8b76dfa40 | ||
|
|
297b314904 | ||
|
|
55dd1ab0a1 | ||
|
|
8c803f1c4b | ||
|
|
3b942049a2 | ||
|
|
f78fd212bb | ||
|
|
f931ebece8 | ||
|
|
ea0a9763bf | ||
|
|
b59e47dcf9 | ||
|
|
ce09ef8848 | ||
|
|
188727daba | ||
|
|
1150633fef | ||
|
|
62ae845f4b | ||
|
|
0757fd741e | ||
|
|
a07fa8ccd2 | ||
|
|
c77f32e696 | ||
|
|
4391771416 | ||
|
|
01ab820459 | ||
|
|
c7218f2856 | ||
|
|
592221b4bf | ||
|
|
836458ad85 | ||
|
|
7233c86f3d | ||
|
|
154b1b05e4 | ||
|
|
4d88638d4d | ||
|
|
9403986643 | ||
|
|
ad48610b2f | ||
|
|
63487cf3ec | ||
|
|
4ae2087c2e | ||
|
|
5179129a6b | ||
|
|
6a00d8c88c | ||
|
|
50f43f9396 | ||
|
|
6f205f8931 | ||
|
|
3776ffa49b | ||
|
|
318b5beac1 | ||
|
|
344c5d6d12 | ||
|
|
4d319a8caa | ||
|
|
74b24a0690 | ||
|
|
1ca0464957 | ||
|
|
f7ebc8a88c | ||
|
|
a102099ac1 | ||
|
|
59ac22aa05 | ||
|
|
cd7244b3d7 | ||
|
|
f81b676abe | ||
|
|
234b154053 | ||
|
|
a1d09ad574 |
@@ -33,7 +33,9 @@ Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and
|
|||||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
||||||
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
||||||
|
|
||||||
## Github Sponsors ($15+)
|
## Github Sponsors ($40+)
|
||||||
|
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
||||||
|
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
||||||
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
|
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
|
||||||
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
|
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ class StartProxy
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$proxyType = $server->proxyType();
|
$proxyType = $server->proxyType();
|
||||||
|
if ($proxyType === 'NONE') {
|
||||||
|
return 'OK';
|
||||||
|
}
|
||||||
$commands = collect([]);
|
$commands = collect([]);
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
$configuration = CheckConfiguration::run($server);
|
$configuration = CheckConfiguration::run($server);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ 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 inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid >/dev/null 2>&1 || true";
|
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid";
|
||||||
$commands[] = "echo Starting service.";
|
$commands[] = "echo Starting service.";
|
||||||
$commands[] = "echo 'Pulling images.'";
|
$commands[] = "echo 'Pulling images.'";
|
||||||
$commands[] = "docker compose pull";
|
$commands[] = "docker compose pull";
|
||||||
|
|||||||
25
app/Console/Commands/CleanupApplicationDeploymentQueue.php
Normal file
25
app/Console/Commands/CleanupApplicationDeploymentQueue.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class CleanupApplicationDeploymentQueue extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
|
||||||
|
protected $description = 'CleanupApplicationDeploymentQueue';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$team_id = $this->option('team-id');
|
||||||
|
$servers = \App\Models\Server::where('team_id', $team_id)->get();
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$deployments = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->where("server_id", $server->id)->get();
|
||||||
|
foreach ($deployments as $deployment) {
|
||||||
|
$deployment->update(['status' => 'failed']);
|
||||||
|
instant_remote_process(['docker rm -f ' . $deployment->deployment_uuid], $server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
app/Console/Commands/CleanupDatabase.php
Normal file
59
app/Console/Commands/CleanupDatabase.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class CleanupDatabase extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:database {--yes}';
|
||||||
|
protected $description = 'Cleanup database';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
echo "Running database cleanup...\n";
|
||||||
|
$keep_days = 60;
|
||||||
|
|
||||||
|
// Cleanup failed jobs table
|
||||||
|
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(7));
|
||||||
|
$count = $failed_jobs->count();
|
||||||
|
echo "Delete $count entries from failed_jobs.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$failed_jobs->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup sessions table
|
||||||
|
$sessions = DB::table('sessions')->where('last_activity', '<', now()->subDays($keep_days)->timestamp);
|
||||||
|
$count = $sessions->count();
|
||||||
|
echo "Delete $count entries from sessions.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$sessions->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup activity_log table
|
||||||
|
$activity_log = DB::table('activity_log')->where('created_at', '<', now()->subDays($keep_days));
|
||||||
|
$count = $activity_log->count();
|
||||||
|
echo "Delete $count entries from activity_log.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$activity_log->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup application_deployment_queues table
|
||||||
|
$application_deployment_queues = DB::table('application_deployment_queues')->where('created_at', '<', now()->subDays($keep_days));
|
||||||
|
$count = $application_deployment_queues->count();
|
||||||
|
echo "Delete $count entries from application_deployment_queues.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$application_deployment_queues->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup webhooks table
|
||||||
|
$webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days));
|
||||||
|
$count = $webhooks->count();
|
||||||
|
echo "Delete $count entries from webhooks.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$webhooks->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,15 +8,16 @@ use Illuminate\Console\Command;
|
|||||||
class CleanupUnreachableServers extends Command
|
class CleanupUnreachableServers extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'cleanup:unreachable-servers';
|
protected $signature = 'cleanup:unreachable-servers';
|
||||||
protected $description = 'Cleanup Unreachable Servers (3 days)';
|
protected $description = 'Cleanup Unreachable Servers (7 days)';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
echo "Running unreachable server cleanup...\n";
|
echo "Running unreachable server cleanup...\n";
|
||||||
$servers = Server::where('unreachable_count', 3)->where('unreachable_notification_sent', true)->where('updated_at', '<', now()->subDays(3))->get();
|
$servers = Server::where('unreachable_count', 3)->where('unreachable_notification_sent', true)->where('updated_at', '<', now()->subDays(7))->get();
|
||||||
if ($servers->count() > 0) {
|
if ($servers->count() > 0) {
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
echo "Cleanup unreachable server ($server->id) with name $server->name";
|
echo "Cleanup unreachable server ($server->id) with name $server->name";
|
||||||
|
send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up...");
|
||||||
$server->update([
|
$server->update([
|
||||||
'ip' => '1.2.3.4'
|
'ip' => '1.2.3.4'
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\DatabaseBackupStatusJob;
|
||||||
use App\Jobs\SendConfirmationForWaitlistJob;
|
use App\Jobs\SendConfirmationForWaitlistJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
|
use App\Models\ScheduledDatabaseBackupExecution;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
@@ -15,6 +17,7 @@ use App\Notifications\Application\DeploymentSuccess;
|
|||||||
use App\Notifications\Application\StatusChanged;
|
use App\Notifications\Application\StatusChanged;
|
||||||
use App\Notifications\Database\BackupFailed;
|
use App\Notifications\Database\BackupFailed;
|
||||||
use App\Notifications\Database\BackupSuccess;
|
use App\Notifications\Database\BackupSuccess;
|
||||||
|
use App\Notifications\Database\DailyBackup;
|
||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
@@ -54,6 +57,8 @@ class Emails extends Command
|
|||||||
options: [
|
options: [
|
||||||
'updates' => 'Send Update Email to all users',
|
'updates' => 'Send Update Email to all users',
|
||||||
'emails-test' => 'Test',
|
'emails-test' => 'Test',
|
||||||
|
'database-backup-statuses-daily' => 'Database - Backup Statuses (Daily)',
|
||||||
|
'application-deployment-success-daily' => 'Application - Deployment Success (Daily)',
|
||||||
'application-deployment-success' => 'Application - Deployment Success',
|
'application-deployment-success' => 'Application - Deployment Success',
|
||||||
'application-deployment-failed' => 'Application - Deployment Failed',
|
'application-deployment-failed' => 'Application - Deployment Failed',
|
||||||
'application-status-changed' => 'Application - Status Changed',
|
'application-status-changed' => 'Application - Status Changed',
|
||||||
@@ -67,8 +72,12 @@ class Emails extends Command
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
||||||
if (!in_array($type, $emailsGathered)) {
|
if (isDev()) {
|
||||||
$this->email = text('Email Address to send to');
|
$this->email = "test@example.com";
|
||||||
|
} else {
|
||||||
|
if (!in_array($type, $emailsGathered)) {
|
||||||
|
$this->email = text('Email Address to send to:');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set_transanctional_email_settings();
|
set_transanctional_email_settings();
|
||||||
|
|
||||||
@@ -102,7 +111,7 @@ class Emails extends Command
|
|||||||
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
||||||
'token' => encrypt($email),
|
'token' => encrypt($email),
|
||||||
]);
|
]);
|
||||||
$this->mail->view('emails.updates',["unsubscribeUrl" => $unsubscribeUrl]);
|
$this->mail->view('emails.updates', ["unsubscribeUrl" => $unsubscribeUrl]);
|
||||||
$this->sendEmail($email);
|
$this->sendEmail($email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,6 +120,35 @@ class Emails extends Command
|
|||||||
$this->mail = (new Test())->toMail();
|
$this->mail = (new Test())->toMail();
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
break;
|
break;
|
||||||
|
case 'database-backup-statuses-daily':
|
||||||
|
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||||
|
$databases = collect();
|
||||||
|
foreach ($scheduled_backups as $scheduled_backup) {
|
||||||
|
$last_days_backups = $scheduled_backup->get_last_days_backup_status();
|
||||||
|
if ($last_days_backups->isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$failed = $last_days_backups->where('status', 'failed');
|
||||||
|
$database = $scheduled_backup->database;
|
||||||
|
$databases->put($database->name, [
|
||||||
|
'failed_count' => $failed->count(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->mail = (new DailyBackup($databases))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
break;
|
||||||
|
case 'application-deployment-success-daily':
|
||||||
|
$applications = Application::all();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$deployments = $application->get_last_days_deployments();
|
||||||
|
ray($deployments);
|
||||||
|
if ($deployments->isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'application-deployment-success':
|
case 'application-deployment-success':
|
||||||
$application = Application::all()->first();
|
$application = Application::all()->first();
|
||||||
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
||||||
|
|||||||
@@ -73,6 +73,18 @@ class ServicesGenerate extends Command
|
|||||||
} else {
|
} else {
|
||||||
$slogan = str($file)->headline()->value();
|
$slogan = str($file)->headline()->value();
|
||||||
}
|
}
|
||||||
|
$logo = collect(preg_grep('/^# logo:/', explode("\n", $content)))->values();
|
||||||
|
if ($logo->count() > 0) {
|
||||||
|
$logo = str($logo[0])->after('# logo:')->trim()->value();
|
||||||
|
} else {
|
||||||
|
$logo = 'svgs/unknown.svg';
|
||||||
|
}
|
||||||
|
$minversion = collect(preg_grep('/^# minversion:/', explode("\n", $content)))->values();
|
||||||
|
if ($minversion->count() > 0) {
|
||||||
|
$minversion = str($minversion[0])->after('# minversion:')->trim()->value();
|
||||||
|
} else {
|
||||||
|
$minversion = '0.0.0';
|
||||||
|
}
|
||||||
$env_file = collect(preg_grep('/^# env_file:/', explode("\n", $content)))->values();
|
$env_file = collect(preg_grep('/^# env_file:/', explode("\n", $content)))->values();
|
||||||
if ($env_file->count() > 0) {
|
if ($env_file->count() > 0) {
|
||||||
$env_file = str($env_file[0])->after('# env_file:')->trim()->value();
|
$env_file = str($env_file[0])->after('# env_file:')->trim()->value();
|
||||||
@@ -96,6 +108,8 @@ class ServicesGenerate extends Command
|
|||||||
'slogan' => $slogan,
|
'slogan' => $slogan,
|
||||||
'compose' => $yaml,
|
'compose' => $yaml,
|
||||||
'tags' => $tags,
|
'tags' => $tags,
|
||||||
|
'logo' => $logo,
|
||||||
|
'minversion' => $minversion,
|
||||||
];
|
];
|
||||||
if ($env_file) {
|
if ($env_file) {
|
||||||
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
|
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ namespace App\Console;
|
|||||||
|
|
||||||
use App\Jobs\CheckLogDrainContainerJob;
|
use App\Jobs\CheckLogDrainContainerJob;
|
||||||
use App\Jobs\CleanupInstanceStuffsJob;
|
use App\Jobs\CleanupInstanceStuffsJob;
|
||||||
use App\Jobs\ComplexContainerStatusJob;
|
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
use App\Jobs\ScheduledTaskJob;
|
use App\Jobs\ScheduledTaskJob;
|
||||||
use App\Jobs\InstanceAutoUpdateJob;
|
use App\Jobs\InstanceAutoUpdateJob;
|
||||||
@@ -48,6 +47,10 @@ class Kernel extends ConsoleKernel
|
|||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->pull_helper_image($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
$this->check_scheduled_tasks($schedule);
|
$this->check_scheduled_tasks($schedule);
|
||||||
|
|
||||||
|
if (!isCloud()) {
|
||||||
|
$schedule->command('cleanup:database --yes')->daily();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function pull_helper_image($schedule)
|
private function pull_helper_image($schedule)
|
||||||
@@ -77,6 +80,35 @@ class Kernel extends ConsoleKernel
|
|||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
}
|
}
|
||||||
|
// Delayed Jobs
|
||||||
|
// foreach ($containerServers as $server) {
|
||||||
|
// $schedule
|
||||||
|
// ->call(function () use ($server) {
|
||||||
|
// $randomSeconds = rand(1, 40);
|
||||||
|
// $job = new ContainerStatusJob($server);
|
||||||
|
// $job->delay($randomSeconds);
|
||||||
|
// ray('dispatching container status job in ' . $randomSeconds . ' seconds');
|
||||||
|
// dispatch($job);
|
||||||
|
// })->name('container-status-' . $server->id)->everyMinute()->onOneServer();
|
||||||
|
// if ($server->isLogDrainEnabled()) {
|
||||||
|
// $schedule
|
||||||
|
// ->call(function () use ($server) {
|
||||||
|
// $randomSeconds = rand(1, 40);
|
||||||
|
// $job = new CheckLogDrainContainerJob($server);
|
||||||
|
// $job->delay($randomSeconds);
|
||||||
|
// dispatch($job);
|
||||||
|
// })->name('log-drain-container-check-' . $server->id)->everyMinute()->onOneServer();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// foreach ($servers as $server) {
|
||||||
|
// $schedule
|
||||||
|
// ->call(function () use ($server) {
|
||||||
|
// $randomSeconds = rand(1, 40);
|
||||||
|
// $job = new ServerStatusJob($server);
|
||||||
|
// $job->delay($randomSeconds);
|
||||||
|
// dispatch($job);
|
||||||
|
// })->name('server-status-job-' . $server->id)->everyMinute()->onOneServer();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
private function instance_auto_update($schedule)
|
private function instance_auto_update($schedule)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ class Handler extends ExceptionHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
if (str($e->getMessage())->contains('No space left on device')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ray('reporting to sentry');
|
ray('reporting to sentry');
|
||||||
Integration::captureUnhandledException($e);
|
Integration::captureUnhandledException($e);
|
||||||
});
|
});
|
||||||
|
|||||||
186
app/Http/Controllers/Webhook/Bitbucket.php
Normal file
186
app/Http/Controllers/Webhook/Bitbucket.php
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Livewire\Project\Service\Storage;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Bitbucket extends Controller
|
||||||
|
{
|
||||||
|
public function manual(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$data = [
|
||||||
|
'attributes' => $request->attributes->all(),
|
||||||
|
'request' => $request->request->all(),
|
||||||
|
'query' => $request->query->all(),
|
||||||
|
'server' => $request->server->all(),
|
||||||
|
'files' => $request->files->all(),
|
||||||
|
'cookies' => $request->cookies->all(),
|
||||||
|
'headers' => $request->headers->all(),
|
||||||
|
'content' => $request->getContent(),
|
||||||
|
];
|
||||||
|
$json = json_encode($data);
|
||||||
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Bitbicket::manual_bitbucket", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$return_payloads = collect([]);
|
||||||
|
$payload = $request->collect();
|
||||||
|
$headers = $request->headers->all();
|
||||||
|
$x_bitbucket_token = data_get($headers, 'x-hub-signature.0', "");
|
||||||
|
$x_bitbucket_event = data_get($headers, 'x-event-key.0', "");
|
||||||
|
$handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']);
|
||||||
|
if (!$handled_events->contains($x_bitbucket_event)) {
|
||||||
|
return response([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Nothing to do. Event not handled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($x_bitbucket_event === 'repo:push') {
|
||||||
|
$branch = data_get($payload, 'push.changes.0.new.name');
|
||||||
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
|
|
||||||
|
if (!$branch) {
|
||||||
|
return response([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
ray('Manual webhook bitbucket push event with branch: ' . $branch);
|
||||||
|
}
|
||||||
|
if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
|
||||||
|
$branch = data_get($payload, 'pullrequest.destination.branch.name');
|
||||||
|
$base_branch = data_get($payload, 'pullrequest.source.branch.name');
|
||||||
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
|
$pull_request_id = data_get($payload, 'pullrequest.id');
|
||||||
|
$pull_request_html_url = data_get($payload, 'pullrequest.links.html.href');
|
||||||
|
$commit = data_get($payload, 'pullrequest.source.commit.hash');
|
||||||
|
}
|
||||||
|
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
||||||
|
$applications = $applications->where('git_branch', $branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
return response([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => "Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$webhook_secret = data_get($application, 'manual_webhook_secret_bitbucket');
|
||||||
|
$payload = $request->getContent();
|
||||||
|
|
||||||
|
list($algo, $hash) = explode('=', $x_bitbucket_token, 2);
|
||||||
|
$payloadHash = hash_hmac($algo, $payload, $webhook_secret);
|
||||||
|
if (!hash_equals($hash, $payloadHash) && !isDev()) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Invalid token.',
|
||||||
|
]);
|
||||||
|
ray('Invalid signature');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
|
if (!$isFunctional) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Server is not functional.',
|
||||||
|
]);
|
||||||
|
ray('Server is not functional: ' . $application->destination->server->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($x_bitbucket_event === 'repo:push') {
|
||||||
|
if ($application->isDeployable()) {
|
||||||
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Auto deployment disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_bitbucket_event === 'pullrequest:created') {
|
||||||
|
if ($application->isPRDeployable()) {
|
||||||
|
ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if (!$found) {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'bitbucket',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
pull_request_id: $pull_request_id,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
commit: $commit,
|
||||||
|
is_webhook: true,
|
||||||
|
git_type: 'bitbucket'
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Preview deployments disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
|
||||||
|
ray('Pull request rejected');
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if ($found) {
|
||||||
|
$found->delete();
|
||||||
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment closed.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'No preview deployment found.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ray($return_payloads);
|
||||||
|
return response($return_payloads);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
ray($e);
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
459
app/Http/Controllers/Webhook/Github.php
Normal file
459
app/Http/Controllers/Webhook/Github.php
Normal file
@@ -0,0 +1,459 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Enums\ProcessStatus;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Jobs\ApplicationPullRequestUpdateJob;
|
||||||
|
use App\Jobs\GithubAppPermissionJob;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
|
use App\Models\GithubApp;
|
||||||
|
use App\Models\PrivateKey;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Github extends Controller
|
||||||
|
{
|
||||||
|
public function manual(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ray($request);
|
||||||
|
$return_payloads = collect([]);
|
||||||
|
$x_github_delivery = request()->header('X-GitHub-Delivery');
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$files = Storage::disk('webhooks-during-maintenance')->files();
|
||||||
|
$github_delivery_found = collect($files)->filter(function ($file) use ($x_github_delivery) {
|
||||||
|
return Str::contains($file, $x_github_delivery);
|
||||||
|
})->first();
|
||||||
|
if ($github_delivery_found) {
|
||||||
|
ray('Webhook already found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$data = [
|
||||||
|
'attributes' => $request->attributes->all(),
|
||||||
|
'request' => $request->request->all(),
|
||||||
|
'query' => $request->query->all(),
|
||||||
|
'server' => $request->server->all(),
|
||||||
|
'files' => $request->files->all(),
|
||||||
|
'cookies' => $request->cookies->all(),
|
||||||
|
'headers' => $request->headers->all(),
|
||||||
|
'content' => $request->getContent(),
|
||||||
|
];
|
||||||
|
$json = json_encode($data);
|
||||||
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::manual_{$x_github_delivery}", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$x_github_event = Str::lower($request->header('X-GitHub-Event'));
|
||||||
|
$x_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256=');
|
||||||
|
$content_type = $request->header('Content-Type');
|
||||||
|
$payload = $request->collect();
|
||||||
|
if ($x_github_event === 'ping') {
|
||||||
|
// Just pong
|
||||||
|
return response('pong');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($content_type !== 'application/json') {
|
||||||
|
$payload = json_decode(data_get($payload, 'payload'), true);
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
$branch = data_get($payload, 'ref');
|
||||||
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
|
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||||
|
$branch = Str::after($branch, 'refs/heads/');
|
||||||
|
}
|
||||||
|
ray('Manual Webhook GitHub Push Event with branch: ' . $branch);
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'pull_request') {
|
||||||
|
$action = data_get($payload, 'action');
|
||||||
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
|
$pull_request_id = data_get($payload, 'number');
|
||||||
|
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
||||||
|
$branch = data_get($payload, 'pull_request.head.ref');
|
||||||
|
$base_branch = data_get($payload, 'pull_request.base.ref');
|
||||||
|
ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
|
||||||
|
}
|
||||||
|
if (!$branch) {
|
||||||
|
return response('Nothing to do. No branch found in the request.');
|
||||||
|
}
|
||||||
|
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
$applications = $applications->where('git_branch', $branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
return response("Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'pull_request') {
|
||||||
|
$applications = $applications->where('git_branch', $base_branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
return response("Nothing to do. No applications found with branch '$base_branch'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$webhook_secret = data_get($application, 'manual_webhook_secret_github');
|
||||||
|
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
|
||||||
|
if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) {
|
||||||
|
ray('Invalid signature');
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Invalid token.',
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
|
if (!$isFunctional) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Server is not functional.',
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
if ($application->isDeployable()) {
|
||||||
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true,
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Deployments disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'pull_request') {
|
||||||
|
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
||||||
|
if ($application->isPRDeployable()) {
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if (!$found) {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'github',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
pull_request_id: $pull_request_id,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true,
|
||||||
|
git_type: 'github'
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Preview deployments disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($action === 'closed') {
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if ($found) {
|
||||||
|
$found->delete();
|
||||||
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
|
// ray('Stopping container: ' . $container_name);
|
||||||
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment closed.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'No preview deployment found.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ray($return_payloads);
|
||||||
|
return response($return_payloads);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function normal(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$return_payloads = collect([]);
|
||||||
|
$id = null;
|
||||||
|
$x_github_delivery = $request->header('X-GitHub-Delivery');
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$files = Storage::disk('webhooks-during-maintenance')->files();
|
||||||
|
$github_delivery_found = collect($files)->filter(function ($file) use ($x_github_delivery) {
|
||||||
|
return Str::contains($file, $x_github_delivery);
|
||||||
|
})->first();
|
||||||
|
if ($github_delivery_found) {
|
||||||
|
ray('Webhook already found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$data = [
|
||||||
|
'attributes' => $request->attributes->all(),
|
||||||
|
'request' => $request->request->all(),
|
||||||
|
'query' => $request->query->all(),
|
||||||
|
'server' => $request->server->all(),
|
||||||
|
'files' => $request->files->all(),
|
||||||
|
'cookies' => $request->cookies->all(),
|
||||||
|
'headers' => $request->headers->all(),
|
||||||
|
'content' => $request->getContent(),
|
||||||
|
];
|
||||||
|
$json = json_encode($data);
|
||||||
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::normal_{$x_github_delivery}", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$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_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256=');
|
||||||
|
$payload = $request->collect();
|
||||||
|
if ($x_github_event === 'ping') {
|
||||||
|
// Just pong
|
||||||
|
return response('pong');
|
||||||
|
}
|
||||||
|
$github_app = GithubApp::where('app_id', $x_github_hook_installation_target_id)->first();
|
||||||
|
if (is_null($github_app)) {
|
||||||
|
return response('Nothing to do. No GitHub App found.');
|
||||||
|
}
|
||||||
|
$webhook_secret = data_get($github_app, 'webhook_secret');
|
||||||
|
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
|
||||||
|
if (config('app.env') !== 'local') {
|
||||||
|
if (!hash_equals($x_hub_signature_256, $hmac)) {
|
||||||
|
return response('Invalid signature.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'installation' || $x_github_event === 'installation_repositories') {
|
||||||
|
// Installation handled by setup redirect url. Repositories queried on-demand.
|
||||||
|
$action = data_get($payload, 'action');
|
||||||
|
if ($action === 'new_permissions_accepted') {
|
||||||
|
GithubAppPermissionJob::dispatch($github_app);
|
||||||
|
}
|
||||||
|
return response('cool');
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
$id = data_get($payload, 'repository.id');
|
||||||
|
$branch = data_get($payload, 'ref');
|
||||||
|
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||||
|
$branch = Str::after($branch, 'refs/heads/');
|
||||||
|
}
|
||||||
|
ray('Webhook GitHub Push Event: ' . $id . ' with branch: ' . $branch);
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'pull_request') {
|
||||||
|
$action = data_get($payload, 'action');
|
||||||
|
$id = data_get($payload, 'repository.id');
|
||||||
|
$pull_request_id = data_get($payload, 'number');
|
||||||
|
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
||||||
|
$branch = data_get($payload, 'pull_request.head.ref');
|
||||||
|
$base_branch = data_get($payload, 'pull_request.base.ref');
|
||||||
|
ray('Webhook GitHub Pull Request Event: ' . $id . ' with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
|
||||||
|
}
|
||||||
|
if (!$id || !$branch) {
|
||||||
|
return response('Nothing to do. No id or branch found.');
|
||||||
|
}
|
||||||
|
$applications = Application::where('repository_project_id', $id)->whereRelation('source', 'is_public', false);
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
$applications = $applications->where('git_branch', $branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
return response("Nothing to do. No applications found with branch '$branch'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'pull_request') {
|
||||||
|
$applications = $applications->where('git_branch', $base_branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
return response("Nothing to do. No applications found with branch '$base_branch'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
|
if (!$isFunctional) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Server is not functional.',
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
if ($application->isDeployable()) {
|
||||||
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Deployments disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'pull_request') {
|
||||||
|
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
||||||
|
if ($application->isPRDeployable()) {
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if (!$found) {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'github',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
pull_request_id: $pull_request_id,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true,
|
||||||
|
git_type: 'github'
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Preview deployments disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($action === 'closed' || $action === 'close') {
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if ($found) {
|
||||||
|
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
|
||||||
|
$found->delete();
|
||||||
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
|
// ray('Stopping container: ' . $container_name);
|
||||||
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment closed.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'No preview deployment found.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ray($return_payloads);
|
||||||
|
return response($return_payloads);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function redirect(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$code = $request->get('code');
|
||||||
|
$state = $request->get('state');
|
||||||
|
$github_app = GithubApp::where('uuid', $state)->firstOrFail();
|
||||||
|
$api_url = data_get($github_app, 'api_url');
|
||||||
|
$data = Http::withBody(null)->accept('application/vnd.github+json')->post("$api_url/app-manifests/$code/conversions")->throw()->json();
|
||||||
|
$id = data_get($data, 'id');
|
||||||
|
$slug = data_get($data, 'slug');
|
||||||
|
$client_id = data_get($data, 'client_id');
|
||||||
|
$client_secret = data_get($data, 'client_secret');
|
||||||
|
$private_key = data_get($data, 'pem');
|
||||||
|
$webhook_secret = data_get($data, 'webhook_secret');
|
||||||
|
$private_key = PrivateKey::create([
|
||||||
|
'name' => $slug,
|
||||||
|
'private_key' => $private_key,
|
||||||
|
'team_id' => $github_app->team_id,
|
||||||
|
'is_git_related' => true,
|
||||||
|
]);
|
||||||
|
$github_app->name = $slug;
|
||||||
|
$github_app->app_id = $id;
|
||||||
|
$github_app->client_id = $client_id;
|
||||||
|
$github_app->client_secret = $client_secret;
|
||||||
|
$github_app->webhook_secret = $webhook_secret;
|
||||||
|
$github_app->private_key_id = $private_key->id;
|
||||||
|
$github_app->save();
|
||||||
|
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function install(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$installation_id = $request->get('installation_id');
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$data = [
|
||||||
|
'attributes' => $request->attributes->all(),
|
||||||
|
'request' => $request->request->all(),
|
||||||
|
'query' => $request->query->all(),
|
||||||
|
'server' => $request->server->all(),
|
||||||
|
'files' => $request->files->all(),
|
||||||
|
'cookies' => $request->cookies->all(),
|
||||||
|
'headers' => $request->headers->all(),
|
||||||
|
'content' => $request->getContent(),
|
||||||
|
];
|
||||||
|
$json = json_encode($data);
|
||||||
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::install_{$installation_id}", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$source = $request->get('source');
|
||||||
|
$setup_action = $request->get('setup_action');
|
||||||
|
$github_app = GithubApp::where('uuid', $source)->firstOrFail();
|
||||||
|
if ($setup_action === 'install') {
|
||||||
|
$github_app->installation_id = $installation_id;
|
||||||
|
$github_app->save();
|
||||||
|
}
|
||||||
|
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
202
app/Http/Controllers/Webhook/Gitlab.php
Normal file
202
app/Http/Controllers/Webhook/Gitlab.php
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Gitlab extends Controller
|
||||||
|
{
|
||||||
|
public function manual(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$data = [
|
||||||
|
'attributes' => $request->attributes->all(),
|
||||||
|
'request' => $request->request->all(),
|
||||||
|
'query' => $request->query->all(),
|
||||||
|
'server' => $request->server->all(),
|
||||||
|
'files' => $request->files->all(),
|
||||||
|
'cookies' => $request->cookies->all(),
|
||||||
|
'headers' => $request->headers->all(),
|
||||||
|
'content' => $request->getContent(),
|
||||||
|
];
|
||||||
|
$json = json_encode($data);
|
||||||
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitlab::manual_gitlab", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$return_payloads = collect([]);
|
||||||
|
$payload = $request->collect();
|
||||||
|
$headers = $request->headers->all();
|
||||||
|
$x_gitlab_token = data_get($headers, 'x-gitlab-token.0');
|
||||||
|
$x_gitlab_event = data_get($payload, 'object_kind');
|
||||||
|
if ($x_gitlab_event === 'push') {
|
||||||
|
$branch = data_get($payload, 'ref');
|
||||||
|
$full_name = data_get($payload, 'project.path_with_namespace');
|
||||||
|
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||||
|
$branch = Str::after($branch, 'refs/heads/');
|
||||||
|
}
|
||||||
|
if (!$branch) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
|
]);
|
||||||
|
return response($return_payloads);
|
||||||
|
}
|
||||||
|
ray('Manual Webhook GitLab Push Event with branch: ' . $branch);
|
||||||
|
}
|
||||||
|
if ($x_gitlab_event === 'merge_request') {
|
||||||
|
$action = data_get($payload, 'object_attributes.action');
|
||||||
|
$branch = data_get($payload, 'object_attributes.source_branch');
|
||||||
|
$base_branch = data_get($payload, 'object_attributes.target_branch');
|
||||||
|
$full_name = data_get($payload, 'project.path_with_namespace');
|
||||||
|
$pull_request_id = data_get($payload, 'object_attributes.iid');
|
||||||
|
$pull_request_html_url = data_get($payload, 'object_attributes.url');
|
||||||
|
if (!$branch) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
|
]);
|
||||||
|
return response($return_payloads);
|
||||||
|
}
|
||||||
|
ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
|
||||||
|
}
|
||||||
|
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
||||||
|
if ($x_gitlab_event === 'push') {
|
||||||
|
$applications = $applications->where('git_branch', $branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => "Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.",
|
||||||
|
]);
|
||||||
|
return response($return_payloads);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_gitlab_event === 'merge_request') {
|
||||||
|
$applications = $applications->where('git_branch', $base_branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => "Nothing to do. No applications found with branch '$base_branch'.",
|
||||||
|
]);
|
||||||
|
return response($return_payloads);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$webhook_secret = data_get($application, 'manual_webhook_secret_gitlab');
|
||||||
|
if ($webhook_secret !== $x_gitlab_token) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Invalid token.',
|
||||||
|
]);
|
||||||
|
ray('Invalid signature');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
|
if (!$isFunctional) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Server is not functional',
|
||||||
|
]);
|
||||||
|
ray('Server is not functional: ' . $application->destination->server->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($x_gitlab_event === 'push') {
|
||||||
|
if ($application->isDeployable()) {
|
||||||
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Deployments disabled',
|
||||||
|
]);
|
||||||
|
ray('Deployments disabled for ' . $application->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_gitlab_event === 'merge_request') {
|
||||||
|
if ($action === 'open' || $action === 'opened' || $action === 'synchronize' || $action === 'reopened' || $action === 'reopen' || $action === 'update') {
|
||||||
|
if ($application->isPRDeployable()) {
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if (!$found) {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'gitlab',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
pull_request_id: $pull_request_id,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true,
|
||||||
|
git_type: 'gitlab'
|
||||||
|
);
|
||||||
|
ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview Deployment queued',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Preview deployments disabled',
|
||||||
|
]);
|
||||||
|
ray('Preview deployments disabled for ' . $application->name);
|
||||||
|
}
|
||||||
|
} else if ($action === 'closed' || $action === 'close') {
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if ($found) {
|
||||||
|
$found->delete();
|
||||||
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
|
// ray('Stopping container: ' . $container_name);
|
||||||
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview Deployment closed',
|
||||||
|
]);
|
||||||
|
return response($return_payloads);
|
||||||
|
}
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'No Preview Deployment found',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'No action found. Contact us for debugging.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response($return_payloads);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
258
app/Http/Controllers/Webhook/Stripe.php
Normal file
258
app/Http/Controllers/Webhook/Stripe.php
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Jobs\ServerLimitCheckJob;
|
||||||
|
use App\Jobs\SubscriptionInvoiceFailedJob;
|
||||||
|
use App\Jobs\SubscriptionTrialEndedJob;
|
||||||
|
use App\Jobs\SubscriptionTrialEndsSoonJob;
|
||||||
|
use App\Models\Subscription;
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Models\Webhook;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Sleep;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class Stripe extends Controller
|
||||||
|
{
|
||||||
|
public function events(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$data = [
|
||||||
|
'attributes' => $request->attributes->all(),
|
||||||
|
'request' => $request->request->all(),
|
||||||
|
'query' => $request->query->all(),
|
||||||
|
'server' => $request->server->all(),
|
||||||
|
'files' => $request->files->all(),
|
||||||
|
'cookies' => $request->cookies->all(),
|
||||||
|
'headers' => $request->headers->all(),
|
||||||
|
'content' => $request->getContent(),
|
||||||
|
];
|
||||||
|
$json = json_encode($data);
|
||||||
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Stripe::events_stripe", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$webhookSecret = config('subscription.stripe_webhook_secret');
|
||||||
|
$signature = $request->header('Stripe-Signature');
|
||||||
|
$excludedPlans = config('subscription.stripe_excluded_plans');
|
||||||
|
$event = \Stripe\Webhook::constructEvent(
|
||||||
|
$request->getContent(),
|
||||||
|
$signature,
|
||||||
|
$webhookSecret
|
||||||
|
);
|
||||||
|
$webhook = Webhook::create([
|
||||||
|
'type' => 'stripe',
|
||||||
|
'payload' => $request->getContent()
|
||||||
|
]);
|
||||||
|
$type = data_get($event, 'type');
|
||||||
|
$data = data_get($event, 'data.object');
|
||||||
|
switch ($type) {
|
||||||
|
case 'checkout.session.completed':
|
||||||
|
$clientReferenceId = data_get($data, 'client_reference_id');
|
||||||
|
if (is_null($clientReferenceId)) {
|
||||||
|
send_internal_notification('Checkout session completed without client reference id.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$userId = Str::before($clientReferenceId, ':');
|
||||||
|
$teamId = Str::after($clientReferenceId, ':');
|
||||||
|
$subscriptionId = data_get($data, 'subscription');
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$team = Team::find($teamId);
|
||||||
|
$found = $team->members->where('id', $userId)->first();
|
||||||
|
if (!$found->isAdmin()) {
|
||||||
|
send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
|
||||||
|
throw new Exception("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
|
||||||
|
}
|
||||||
|
$subscription = Subscription::where('team_id', $teamId)->first();
|
||||||
|
if ($subscription) {
|
||||||
|
send_internal_notification('Old subscription activated for team: ' . $teamId);
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_subscription_id' => $subscriptionId,
|
||||||
|
'stripe_customer_id' => $customerId,
|
||||||
|
'stripe_invoice_paid' => true,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
send_internal_notification('New subscription for team: ' . $teamId);
|
||||||
|
Subscription::create([
|
||||||
|
'team_id' => $teamId,
|
||||||
|
'stripe_subscription_id' => $subscriptionId,
|
||||||
|
'stripe_customer_id' => $customerId,
|
||||||
|
'stripe_invoice_paid' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'invoice.paid':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$planId = data_get($data, 'lines.data.0.plan.id');
|
||||||
|
if (Str::contains($excludedPlans, $planId)) {
|
||||||
|
send_internal_notification('Subscription excluded.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
if (!$subscription) {
|
||||||
|
Sleep::for(5)->seconds();
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
|
}
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_invoice_paid' => true,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case 'invoice.payment_failed':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
if (!$subscription) {
|
||||||
|
send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: ' . $customerId);
|
||||||
|
return response('No subscription found in Coolify.');
|
||||||
|
}
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
if (!$team) {
|
||||||
|
send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: ' . $customerId);
|
||||||
|
return response('No team found in Coolify.');
|
||||||
|
}
|
||||||
|
if (!$subscription->stripe_invoice_paid) {
|
||||||
|
SubscriptionInvoiceFailedJob::dispatch($team);
|
||||||
|
send_internal_notification('Invoice payment failed: ' . $customerId);
|
||||||
|
} else {
|
||||||
|
send_internal_notification('Invoice payment failed but already paid: ' . $customerId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'payment_intent.payment_failed':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
if (!$subscription) {
|
||||||
|
send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: ' . $customerId);
|
||||||
|
return response('No subscription found in Coolify.');
|
||||||
|
}
|
||||||
|
if ($subscription->stripe_invoice_paid) {
|
||||||
|
send_internal_notification('payment_intent.payment_failed but invoice is active for customer: ' . $customerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send_internal_notification('Subscription payment failed for customer: ' . $customerId);
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.updated':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$status = data_get($data, 'status');
|
||||||
|
$subscriptionId = data_get($data, 'items.data.0.subscription');
|
||||||
|
$planId = data_get($data, 'items.data.0.plan.id');
|
||||||
|
if (Str::contains($excludedPlans, $planId)) {
|
||||||
|
send_internal_notification('Subscription excluded.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
if (!$subscription) {
|
||||||
|
Sleep::for(5)->seconds();
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
}
|
||||||
|
if (!$subscription) {
|
||||||
|
send_internal_notification('No subscription found for: ' . $customerId);
|
||||||
|
return response("No subscription found", 400);
|
||||||
|
}
|
||||||
|
$trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended');
|
||||||
|
$cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end');
|
||||||
|
$alreadyCancelAtPeriodEnd = data_get($subscription, 'stripe_cancel_at_period_end');
|
||||||
|
$feedback = data_get($data, 'cancellation_details.feedback');
|
||||||
|
$comment = data_get($data, 'cancellation_details.comment');
|
||||||
|
$lookup_key = data_get($data, 'items.data.0.price.lookup_key');
|
||||||
|
if (str($lookup_key)->contains('ultimate')) {
|
||||||
|
$quantity = data_get($data, 'items.data.0.quantity', 10);
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
$team->update([
|
||||||
|
'custom_server_limit' => $quantity,
|
||||||
|
]);
|
||||||
|
ServerLimitCheckJob::dispatch($team);
|
||||||
|
}
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_feedback' => $feedback,
|
||||||
|
'stripe_comment' => $comment,
|
||||||
|
'stripe_plan_id' => $planId,
|
||||||
|
'stripe_cancel_at_period_end' => $cancelAtPeriodEnd,
|
||||||
|
]);
|
||||||
|
if ($status === 'paused' || $status === 'incomplete_expired') {
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_invoice_paid' => false,
|
||||||
|
]);
|
||||||
|
send_internal_notification('Subscription paused or incomplete for customer: ' . $customerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trial ended but subscribed, reactive servers
|
||||||
|
if ($trialEndedAlready && $status === 'active') {
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
$team->trialEndedButSubscribed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($feedback) {
|
||||||
|
$reason = "Cancellation feedback for {$customerId}: '" . $feedback . "'";
|
||||||
|
if ($comment) {
|
||||||
|
$reason .= ' with comment: \'' . $comment . "'";
|
||||||
|
}
|
||||||
|
send_internal_notification($reason);
|
||||||
|
}
|
||||||
|
if ($alreadyCancelAtPeriodEnd !== $cancelAtPeriodEnd) {
|
||||||
|
if ($cancelAtPeriodEnd) {
|
||||||
|
// send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id);
|
||||||
|
} else {
|
||||||
|
send_internal_notification('customer.subscription.updated for customer: ' . $customerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.deleted':
|
||||||
|
// End subscription
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
$team->trialEnded();
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_subscription_id' => null,
|
||||||
|
'stripe_plan_id' => null,
|
||||||
|
'stripe_cancel_at_period_end' => false,
|
||||||
|
'stripe_invoice_paid' => false,
|
||||||
|
'stripe_trial_already_ended' => true,
|
||||||
|
]);
|
||||||
|
send_internal_notification('customer.subscription.deleted for customer: ' . $customerId);
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.trial_will_end':
|
||||||
|
// Not used for now
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
if (!$team) {
|
||||||
|
throw new Exception('No team found for subscription: ' . $subscription->id);
|
||||||
|
}
|
||||||
|
SubscriptionTrialEndsSoonJob::dispatch($team);
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.paused':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
if (!$team) {
|
||||||
|
throw new Exception('No team found for subscription: ' . $subscription->id);
|
||||||
|
}
|
||||||
|
$team->trialEnded();
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_trial_already_ended' => true,
|
||||||
|
'stripe_invoice_paid' => false,
|
||||||
|
]);
|
||||||
|
SubscriptionTrialEndedJob::dispatch($team);
|
||||||
|
send_internal_notification('Subscription paused for customer: ' . $customerId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unhandled event type
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if ($type !== 'payment_intent.payment_failed') {
|
||||||
|
send_internal_notification("Subscription webhook ($type) failed: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
$webhook->update([
|
||||||
|
'status' => 'failed',
|
||||||
|
'failure_reason' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
return response($e->getMessage(), 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/Http/Controllers/Webhook/Waitlist.php
Normal file
58
app/Http/Controllers/Webhook/Waitlist.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Waitlist as ModelsWaitlist;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Waitlist extends Controller
|
||||||
|
{
|
||||||
|
public function confirm(Request $request)
|
||||||
|
{
|
||||||
|
$email = request()->get('email');
|
||||||
|
$confirmation_code = request()->get('confirmation_code');
|
||||||
|
ray($email, $confirmation_code);
|
||||||
|
try {
|
||||||
|
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
|
||||||
|
if ($found) {
|
||||||
|
if (!$found->verified) {
|
||||||
|
if ($found->created_at > now()->subMinutes(config('constants.waitlist.expiration'))) {
|
||||||
|
$found->verified = true;
|
||||||
|
$found->save();
|
||||||
|
send_internal_notification('Waitlist confirmed: ' . $email);
|
||||||
|
return 'Thank you for confirming your email address. We will notify you when you are next in line.';
|
||||||
|
} else {
|
||||||
|
$found->delete();
|
||||||
|
send_internal_notification('Waitlist expired: ' . $email);
|
||||||
|
return 'Your confirmation code has expired. Please sign up again.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
send_internal_notification('Waitlist confirmation failed: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function cancel(Request $request)
|
||||||
|
{
|
||||||
|
$email = request()->get('email');
|
||||||
|
$confirmation_code = request()->get('confirmation_code');
|
||||||
|
try {
|
||||||
|
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
|
||||||
|
if ($found && !$found->verified) {
|
||||||
|
$found->delete();
|
||||||
|
send_internal_notification('Waitlist cancelled: ' . $email);
|
||||||
|
return 'Your email address has been removed from the waitlist.';
|
||||||
|
}
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
send_internal_notification('Waitlist cancellation failed: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,10 @@ class DecideWhatToDoWithUser
|
|||||||
{
|
{
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
if (auth()?->user()?->teams?->count() === 0) {
|
||||||
|
$currentTeam = auth()->user()?->recreate_personal_team();
|
||||||
|
refreshSession($currentTeam);
|
||||||
|
}
|
||||||
if(auth()?->user()?->currentTeam()){
|
if(auth()?->user()?->currentTeam()){
|
||||||
refreshSession(auth()->user()->currentTeam());
|
refreshSession(auth()->user()->currentTeam());
|
||||||
}
|
}
|
||||||
@@ -44,7 +48,7 @@ class DecideWhatToDoWithUser
|
|||||||
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
|
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
|
||||||
return redirect(RouteServiceProvider::HOME);
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
if (isSubscriptionActive() && $request->path() === 'subscription') {
|
if (isSubscriptionActive() && $request->routeIs('subscription.index')) {
|
||||||
return redirect(RouteServiceProvider::HOME);
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class PreventRequestsDuringMaintenance extends Middleware
|
|||||||
* @var array<int, string>
|
* @var array<int, string>
|
||||||
*/
|
*/
|
||||||
protected $except = [
|
protected $except = [
|
||||||
//
|
'webhooks/*',
|
||||||
|
'/api/health'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private Server $mainServer;
|
private Server $mainServer;
|
||||||
private ?ApplicationPreview $preview = null;
|
private ?ApplicationPreview $preview = null;
|
||||||
private ?string $git_type = null;
|
private ?string $git_type = null;
|
||||||
|
private bool $only_this_server = false;
|
||||||
|
|
||||||
private string $container_name;
|
private string $container_name;
|
||||||
private ?string $currently_running_container_name = null;
|
private ?string $currently_running_container_name = null;
|
||||||
@@ -115,6 +116,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$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;
|
$this->restart_only = $this->application_deployment_queue->restart_only;
|
||||||
|
$this->only_this_server = $this->application_deployment_queue->only_this_server;
|
||||||
|
|
||||||
$this->git_type = data_get($this->application_deployment_queue, 'git_type');
|
$this->git_type = data_get($this->application_deployment_queue, 'git_type');
|
||||||
|
|
||||||
@@ -165,65 +167,71 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->application->is_github_based()) {
|
if ($this->application->is_github_based()) {
|
||||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS);
|
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS);
|
||||||
}
|
}
|
||||||
|
if ($this->application->build_pack === 'dockerfile') {
|
||||||
|
if (data_get($this->application, 'dockerfile_location')) {
|
||||||
|
$this->dockerfile_location = $this->application->dockerfile_location;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
// Generate custom host<->ip mapping
|
try {
|
||||||
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
// Generate custom host<->ip mapping
|
||||||
if (!is_null($allContainers)) {
|
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
||||||
$allContainers = format_docker_command_output_to_json($allContainers);
|
|
||||||
$ips = collect([]);
|
if (!is_null($allContainers)) {
|
||||||
if (count($allContainers) > 0) {
|
$allContainers = format_docker_command_output_to_json($allContainers);
|
||||||
$allContainers = $allContainers[0];
|
$ips = collect([]);
|
||||||
$allContainers = collect($allContainers)->sort()->values();
|
if (count($allContainers) > 0) {
|
||||||
foreach ($allContainers as $container) {
|
$allContainers = $allContainers[0];
|
||||||
$containerName = data_get($container, 'Name');
|
$allContainers = collect($allContainers)->sort()->values();
|
||||||
if ($containerName === 'coolify-proxy') {
|
foreach ($allContainers as $container) {
|
||||||
continue;
|
$containerName = data_get($container, 'Name');
|
||||||
}
|
if ($containerName === 'coolify-proxy') {
|
||||||
if (preg_match('/-(\d{12})/', $containerName)) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
if (preg_match('/-(\d{12})/', $containerName)) {
|
||||||
$containerIp = data_get($container, 'IPv4Address');
|
continue;
|
||||||
if ($containerName && $containerIp) {
|
}
|
||||||
$containerIp = str($containerIp)->before('/');
|
$containerIp = data_get($container, 'IPv4Address');
|
||||||
$ips->put($containerName, $containerIp->value());
|
if ($containerName && $containerIp) {
|
||||||
|
$containerIp = str($containerIp)->before('/');
|
||||||
|
$ips->put($containerName, $containerIp->value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->addHosts = $ips->map(function ($ip, $name) {
|
||||||
|
return "--add-host $name:$ip";
|
||||||
|
})->implode(' ');
|
||||||
}
|
}
|
||||||
$this->addHosts = $ips->map(function ($ip, $name) {
|
|
||||||
return "--add-host $name:$ip";
|
|
||||||
})->implode(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->application->dockerfile_target_build) {
|
if ($this->application->dockerfile_target_build) {
|
||||||
$this->buildTarget = " --target {$this->application->dockerfile_target_build} ";
|
$this->buildTarget = " --target {$this->application->dockerfile_target_build} ";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check custom port
|
// Check custom port
|
||||||
['repository' => $this->customRepository, 'port' => $this->customPort] = $this->application->customRepository();
|
['repository' => $this->customRepository, 'port' => $this->customPort] = $this->application->customRepository();
|
||||||
|
|
||||||
if (data_get($this->application, 'settings.is_build_server_enabled')) {
|
if (data_get($this->application, 'settings.is_build_server_enabled')) {
|
||||||
$teamId = data_get($this->application, 'environment.project.team.id');
|
$teamId = data_get($this->application, 'environment.project.team.id');
|
||||||
$buildServers = Server::buildServers($teamId)->get();
|
$buildServers = Server::buildServers($teamId)->get();
|
||||||
if ($buildServers->count() === 0) {
|
if ($buildServers->count() === 0) {
|
||||||
$this->application_deployment_queue->addLogEntry("Build server feature activated, but no suitable build server found. Using the deployment server.");
|
$this->application_deployment_queue->addLogEntry("No suitable build server found. Using the deployment server.");
|
||||||
|
$this->build_server = $this->server;
|
||||||
|
$this->original_server = $this->server;
|
||||||
|
} else {
|
||||||
|
$this->build_server = $buildServers->random();
|
||||||
|
$this->application_deployment_queue->addLogEntry("Found a suitable build server ({$this->build_server->name}).");
|
||||||
|
$this->original_server = $this->server;
|
||||||
|
$this->use_build_server = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set build server & original_server to the same as deployment server
|
||||||
$this->build_server = $this->server;
|
$this->build_server = $this->server;
|
||||||
$this->original_server = $this->server;
|
$this->original_server = $this->server;
|
||||||
} else {
|
|
||||||
$this->application_deployment_queue->addLogEntry("Build server feature activated and found a suitable build server. Using it to build your application - if needed.");
|
|
||||||
$this->build_server = $buildServers->random();
|
|
||||||
$this->original_server = $this->server;
|
|
||||||
$this->use_build_server = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Set build server & original_server to the same as deployment server
|
|
||||||
$this->build_server = $this->server;
|
|
||||||
$this->original_server = $this->server;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if ($this->restart_only && $this->application->build_pack !== 'dockerimage') {
|
if ($this->restart_only && $this->application->build_pack !== 'dockerimage') {
|
||||||
$this->just_restart();
|
$this->just_restart();
|
||||||
if ($this->server->isProxyShouldRun()) {
|
if ($this->server->isProxyShouldRun()) {
|
||||||
@@ -331,7 +339,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function deploy_dockerimage_buildpack()
|
private function deploy_dockerimage_buildpack()
|
||||||
{
|
{
|
||||||
$this->dockerImage = $this->application->docker_registry_image_name;
|
$this->dockerImage = $this->application->docker_registry_image_name;
|
||||||
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
if (str($this->application->docker_registry_image_tag)->isEmpty()) {
|
||||||
|
$this->dockerImageTag = 'latest';
|
||||||
|
} else {
|
||||||
|
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
||||||
|
}
|
||||||
ray("echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.'");
|
ray("echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.'");
|
||||||
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.");
|
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->dockerImage}:{$this->dockerImageTag} to {$this->server->name}.");
|
||||||
$this->generate_image_names();
|
$this->generate_image_names();
|
||||||
@@ -362,6 +374,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->cleanup_git();
|
$this->cleanup_git();
|
||||||
$this->application->loadComposeFile(isInit: false);
|
$this->application->loadComposeFile(isInit: false);
|
||||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||||
|
$this->application->parseRawCompose();
|
||||||
$yaml = $composeFile = $this->application->docker_compose_raw;
|
$yaml = $composeFile = $this->application->docker_compose_raw;
|
||||||
} else {
|
} else {
|
||||||
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
|
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
|
||||||
@@ -401,27 +414,44 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$this->write_deployment_configurations();
|
$this->write_deployment_configurations();
|
||||||
|
|
||||||
// Start compose file
|
// Start compose file
|
||||||
if ($this->docker_compose_custom_start_command) {
|
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||||
$this->execute_remote_command(
|
if ($this->docker_compose_custom_start_command) {
|
||||||
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
|
$this->execute_remote_command(
|
||||||
);
|
["cd {$this->basedir} && {$this->docker_compose_custom_start_command}", "hidden" => true],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$server_workdir = $this->application->workdir();
|
||||||
|
ray("SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d");
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d", "hidden" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command(
|
if ($this->docker_compose_custom_start_command) {
|
||||||
[executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
|
$this->execute_remote_command(
|
||||||
);
|
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$this->application_deployment_queue->addLogEntry("New container started.");
|
$this->application_deployment_queue->addLogEntry("New container started.");
|
||||||
}
|
}
|
||||||
private function deploy_dockerfile_buildpack()
|
private function deploy_dockerfile_buildpack()
|
||||||
{
|
{
|
||||||
|
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
|
||||||
if ($this->use_build_server) {
|
if ($this->use_build_server) {
|
||||||
$this->server = $this->build_server;
|
$this->server = $this->build_server;
|
||||||
}
|
}
|
||||||
if (data_get($this->application, 'dockerfile_location')) {
|
if (data_get($this->application, 'dockerfile_location')) {
|
||||||
$this->dockerfile_location = $this->application->dockerfile_location;
|
$this->dockerfile_location = $this->application->dockerfile_location;
|
||||||
}
|
}
|
||||||
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
|
|
||||||
$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();
|
||||||
@@ -516,9 +546,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->server = $this->original_server;
|
$this->server = $this->original_server;
|
||||||
}
|
}
|
||||||
$readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at);
|
$readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at);
|
||||||
$composeFileName = "$this->configuration_dir/docker-compose.yml";
|
if ($this->pull_request_id === 0) {
|
||||||
if ($this->pull_request_id !== 0) {
|
$composeFileName = "$this->configuration_dir/docker-compose.yml";
|
||||||
|
} else {
|
||||||
$composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yml";
|
$composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yml";
|
||||||
|
$this->docker_compose_location = "/docker-compose-pr-{$this->pull_request_id}.yml";
|
||||||
}
|
}
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
@@ -713,7 +745,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->write_deployment_configurations();
|
$this->write_deployment_configurations();
|
||||||
$this->server = $this->original_server;
|
$this->server = $this->original_server;
|
||||||
}
|
}
|
||||||
if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled) {
|
if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || $this->pull_request_id !== 0) {
|
||||||
$this->application_deployment_queue->addLogEntry("----------------------------------------");
|
$this->application_deployment_queue->addLogEntry("----------------------------------------");
|
||||||
if (count($this->application->ports_mappings_array) > 0) {
|
if (count($this->application->ports_mappings_array) > 0) {
|
||||||
$this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
|
$this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
|
||||||
@@ -721,6 +753,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
|
if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
|
||||||
$this->application_deployment_queue->addLogEntry("Consistent container name feature enabled, rolling update is not supported.");
|
$this->application_deployment_queue->addLogEntry("Consistent container name feature enabled, rolling update is not supported.");
|
||||||
}
|
}
|
||||||
|
if ($this->pull_request_id !== 0) {
|
||||||
|
$this->application->settings->is_consistent_container_name_enabled = true;
|
||||||
|
$this->application_deployment_queue->addLogEntry("Pull request deployment, 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();
|
||||||
} else {
|
} else {
|
||||||
@@ -798,26 +834,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->add_build_env_variables_to_dockerfile();
|
$this->add_build_env_variables_to_dockerfile();
|
||||||
}
|
}
|
||||||
$this->build_image();
|
$this->build_image();
|
||||||
$this->stop_running_container();
|
$this->push_to_docker_registry();
|
||||||
if ($this->application->destination->server->isSwarm()) {
|
// $this->stop_running_container();
|
||||||
$this->push_to_docker_registry();
|
$this->rolling_update();
|
||||||
$this->execute_remote_command(
|
|
||||||
[
|
|
||||||
executeInDocker($this->deployment_uuid, "docker stack deploy --with-registry-auth -c {$this->workdir}{$this->docker_compose_location} {$this->application->uuid}-{$this->pull_request_id}")
|
|
||||||
],
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->application_deployment_queue->addLogEntry("Starting preview deployment.");
|
|
||||||
if ($this->use_build_server) {
|
|
||||||
$this->execute_remote_command(
|
|
||||||
["SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->configuration_dir} -f {$this->configuration_dir}{$this->docker_compose_location} up --build -d", "hidden" => true],
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->execute_remote_command(
|
|
||||||
[executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up --build -d"), "hidden" => true],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private function create_workdir()
|
private function create_workdir()
|
||||||
{
|
{
|
||||||
@@ -887,7 +906,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
destination: $destination,
|
destination: $destination,
|
||||||
no_questions_asked: true,
|
no_questions_asked: true,
|
||||||
);
|
);
|
||||||
$this->application_deployment_queue->addLogEntry("Deploying to additional server: {$server->name}. Click here to see the deployment status: " . route('project.application.deployment.show', [
|
$this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: " . route('project.application.deployment.show', [
|
||||||
'project_uuid' => data_get($this->application, 'environment.project.uuid'),
|
'project_uuid' => data_get($this->application, 'environment.project.uuid'),
|
||||||
'application_uuid' => data_get($this->application, 'uuid'),
|
'application_uuid' => data_get($this->application, 'uuid'),
|
||||||
'deployment_uuid' => $deployment_uuid,
|
'deployment_uuid' => $deployment_uuid,
|
||||||
@@ -1214,17 +1233,45 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// ];
|
// ];
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if ((bool)$this->application->settings->is_consistent_container_name_enabled) {
|
if ($this->pull_request_id === 0) {
|
||||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
if ((bool)$this->application->settings->is_consistent_container_name_enabled) {
|
||||||
if (count($custom_compose) > 0) {
|
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||||
$docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
|
if (count($custom_compose) > 0) {
|
||||||
}
|
$ipv4 = data_get($custom_compose, 'ip.0');
|
||||||
} else {
|
$ipv6 = data_get($custom_compose, 'ip6.0');
|
||||||
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
data_forget($custom_compose, 'ip');
|
||||||
data_forget($docker_compose, 'services.' . $this->container_name);
|
data_forget($custom_compose, 'ip6');
|
||||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
if ($ipv4 || $ipv6) {
|
||||||
if (count($custom_compose) > 0) {
|
data_forget($docker_compose['services'][$this->container_name], 'networks');
|
||||||
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
}
|
||||||
|
if ($ipv4) {
|
||||||
|
$docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv4_address'] = $ipv4;
|
||||||
|
}
|
||||||
|
if ($ipv6) {
|
||||||
|
$docker_compose['services'][$this->container_name]['networks'][$this->destination->network]['ipv6_address'] = $ipv6;
|
||||||
|
}
|
||||||
|
$docker_compose['services'][$this->container_name] = array_merge_recursive($docker_compose['services'][$this->container_name], $custom_compose);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
||||||
|
data_forget($docker_compose, 'services.' . $this->container_name);
|
||||||
|
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||||
|
if (count($custom_compose) > 0) {
|
||||||
|
$ipv4 = data_get($custom_compose, 'ip.0');
|
||||||
|
$ipv6 = data_get($custom_compose, 'ip6.0');
|
||||||
|
data_forget($custom_compose, 'ip');
|
||||||
|
data_forget($custom_compose, 'ip6');
|
||||||
|
if ($ipv4 || $ipv6) {
|
||||||
|
data_forget($docker_compose['services'][$this->application->uuid], 'networks');
|
||||||
|
}
|
||||||
|
if ($ipv4) {
|
||||||
|
$docker_compose['services'][$this->application->uuid]['networks'][$this->destination->network]['ipv4_address'] = $ipv4;
|
||||||
|
}
|
||||||
|
if ($ipv6) {
|
||||||
|
$docker_compose['services'][$this->application->uuid]['networks'][$this->destination->network]['ipv6_address'] = $ipv6;
|
||||||
|
}
|
||||||
|
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1501,18 +1548,18 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
|
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
$containers = $containers->filter(function ($container) {
|
$containers = $containers->filter(function ($container) {
|
||||||
return data_get($container, 'Names') !== $this->container_name;
|
return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name . '-pr-' . $this->pull_request_id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$containers->each(function ($container) {
|
$containers->each(function ($container) {
|
||||||
$containerName = data_get($container, 'Names');
|
$containerName = data_get($container, 'Names');
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $containerName >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
["docker rm -f $containerName >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
if ($this->application->settings->is_consistent_container_name_enabled) {
|
if ($this->application->settings->is_consistent_container_name_enabled) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1521,7 +1568,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
'status' => ApplicationDeploymentStatus::FAILED->value,
|
'status' => ApplicationDeploymentStatus::FAILED->value,
|
||||||
]);
|
]);
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1619,13 +1666,17 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($status === ApplicationDeploymentStatus::FINISHED->value) {
|
if ($status === ApplicationDeploymentStatus::FINISHED->value) {
|
||||||
$this->deploy_to_additional_destinations();
|
if (!$this->only_this_server) {
|
||||||
|
$this->deploy_to_additional_destinations();
|
||||||
|
}
|
||||||
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function failed(Throwable $exception): void
|
public function failed(Throwable $exception): void
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$this->next(ApplicationDeploymentStatus::FAILED->value);
|
||||||
$this->application_deployment_queue->addLogEntry("Oops something is not okay, are you okay? 😢", 'stderr');
|
$this->application_deployment_queue->addLogEntry("Oops something is not okay, are you okay? 😢", 'stderr');
|
||||||
if (str($exception->getMessage())->isNotEmpty()) {
|
if (str($exception->getMessage())->isNotEmpty()) {
|
||||||
$this->application_deployment_queue->addLogEntry($exception->getMessage(), 'stderr');
|
$this->application_deployment_queue->addLogEntry($exception->getMessage(), 'stderr');
|
||||||
@@ -1633,15 +1684,14 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
|
|
||||||
if ($this->application->build_pack !== 'dockercompose') {
|
if ($this->application->build_pack !== 'dockercompose') {
|
||||||
$code = $exception->getCode();
|
$code = $exception->getCode();
|
||||||
|
ray($code);
|
||||||
if ($code !== 69420) {
|
if ($code !== 69420) {
|
||||||
// 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
|
// 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
|
||||||
$this->application_deployment_queue->addLogEntry("Deployment failed. Removing the new version of your application.", 'stderr');
|
$this->application_deployment_queue->addLogEntry("Deployment failed. Removing the new version of your application.", 'stderr');
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true]
|
["docker rm -f $this->container_name >/dev/null 2>&1", "hidden" => true, "ignore_errors" => true]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->next(ApplicationDeploymentStatus::FAILED->value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
if ($this->application->is_public_repository()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ($this->status === ProcessStatus::CLOSED) {
|
if ($this->status === ProcessStatus::CLOSED) {
|
||||||
$this->delete_comment();
|
$this->delete_comment();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -43,22 +43,24 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$applications = $this->server->applications();
|
|
||||||
foreach ($applications as $application) {
|
|
||||||
if ($application->additional_servers->count() > 0) {
|
|
||||||
$is_main_server = $application->destination->server->id === $this->server->id;
|
|
||||||
if ($is_main_server) {
|
|
||||||
ComplexStatusCheck::run($application);
|
|
||||||
$applications = $applications->filter(function ($value, $key) use ($application) {
|
|
||||||
return $value->id !== $application->id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->server->isFunctional()) {
|
if (!$this->server->isFunctional()) {
|
||||||
return 'Server is not ready.';
|
return 'Server is not ready.';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$applications = $this->server->applications();
|
||||||
|
$skip_these_applications = collect([]);
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
if ($application->additional_servers->count() > 0) {
|
||||||
|
$skip_these_applications->push($application);
|
||||||
|
ComplexStatusCheck::run($application);
|
||||||
|
$applications = $applications->filter(function ($value, $key) use ($application) {
|
||||||
|
return $value->id !== $application->id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$applications = $applications->filter(function ($value, $key) use ($skip_these_applications) {
|
||||||
|
return !$skip_these_applications->pluck('id')->contains($value->id);
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ 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);
|
||||||
|
if (is_null($this->team)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||||
$this->database = data_get($this->backup, 'database');
|
$this->database = data_get($this->backup, 'database');
|
||||||
$this->server = $this->database->service->server;
|
$this->server = $this->database->service->server;
|
||||||
@@ -316,7 +319,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function backup_standalone_mongodb(string $databaseWithCollections): void
|
private function backup_standalone_mongodb(string $databaseWithCollections): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$url = $this->database->getDbUrl(useInternal: true);
|
$url = $this->database->get_db_url(useInternal: true);
|
||||||
if ($databaseWithCollections === 'all') {
|
if ($databaseWithCollections === 'all') {
|
||||||
$commands[] = "mkdir -p " . $this->backup_dir;
|
$commands[] = "mkdir -p " . $this->backup_dir;
|
||||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
|
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
|
||||||
|
|||||||
74
app/Jobs/DatabaseBackupStatusJob.php
Normal file
74
app/Jobs/DatabaseBackupStatusJob.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Notifications\Database\DailyBackup;
|
||||||
|
use App\Notifications\Server\HighDiskUsage;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class DatabaseBackupStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $tries = 1;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
// $teams = Team::all();
|
||||||
|
// foreach ($teams as $team) {
|
||||||
|
// $scheduled_backups = $team->scheduledDatabaseBackups()->get();
|
||||||
|
// if ($scheduled_backups->isEmpty()) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// foreach ($scheduled_backups as $scheduled_backup) {
|
||||||
|
// $last_days_backups = $scheduled_backup->get_last_days_backup_status();
|
||||||
|
// if ($last_days_backups->isEmpty()) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// $failed = $last_days_backups->where('status', 'failed');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// $scheduled_backups = ScheduledDatabaseBackup::all();
|
||||||
|
// $databases = collect();
|
||||||
|
// $teams = collect();
|
||||||
|
// foreach ($scheduled_backups as $scheduled_backup) {
|
||||||
|
// $last_days_backups = $scheduled_backup->get_last_days_backup_status();
|
||||||
|
// if ($last_days_backups->isEmpty()) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// $failed = $last_days_backups->where('status', 'failed');
|
||||||
|
// $database = $scheduled_backup->database;
|
||||||
|
// $team = $database->team();
|
||||||
|
// $teams->put($team->id, $team);
|
||||||
|
// $databases->put("{$team->id}:{$database->name}", [
|
||||||
|
// 'failed_count' => $failed->count(),
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
// foreach ($databases as $name => $database) {
|
||||||
|
// [$team_id, $name] = explode(':', $name);
|
||||||
|
// $team = $teams->get($team_id);
|
||||||
|
// $team?->notify(new DailyBackup($databases));
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace App\Jobs;
|
|||||||
|
|
||||||
use App\Actions\Server\CleanupDocker;
|
use App\Actions\Server\CleanupDocker;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Notifications\Server\DockerCleanup;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
@@ -47,8 +48,9 @@ class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
CleanupDocker::run($this->server);
|
CleanupDocker::run($this->server);
|
||||||
$usageAfter = $this->server->getDiskUsage();
|
$usageAfter = $this->server->getDiskUsage();
|
||||||
if ($usageAfter < $this->usageBefore) {
|
if ($usageAfter < $this->usageBefore) {
|
||||||
ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
$this->server->team?->notify(new DockerCleanup($this->server, 'Saved ' . ($this->usageBefore - $usageAfter) . '% disk space.'));
|
||||||
send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
// ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||||
|
// send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||||
Log::info('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
Log::info('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||||
} else {
|
} else {
|
||||||
Log::info('DockerCleanupJob failed to save disk space on ' . $this->server->name);
|
Log::info('DockerCleanupJob failed to save disk space on ' . $this->server->name);
|
||||||
|
|||||||
59
app/Jobs/GithubAppPermissionJob.php
Normal file
59
app/Jobs/GithubAppPermissionJob.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\GithubApp;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
class GithubAppPermissionJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $tries = 4;
|
||||||
|
public function backoff(): int
|
||||||
|
{
|
||||||
|
return isDev() ? 1 : 3;
|
||||||
|
}
|
||||||
|
public function __construct(public GithubApp $github_app)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function middleware(): array
|
||||||
|
{
|
||||||
|
return [(new WithoutOverlapping($this->github_app->uuid))];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uniqueId(): int
|
||||||
|
{
|
||||||
|
return $this->github_app->uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$github_access_token = generate_github_jwt_token($this->github_app);
|
||||||
|
$response = Http::withHeaders([
|
||||||
|
'Authorization' => "Bearer $github_access_token",
|
||||||
|
'Accept' => 'application/vnd.github+json'
|
||||||
|
])->get("{$this->github_app->api_url}/app");
|
||||||
|
$response = $response->json();
|
||||||
|
$permissions = data_get($response, 'permissions');
|
||||||
|
$this->github_app->contents = data_get($permissions, 'contents');
|
||||||
|
$this->github_app->metadata = data_get($permissions, 'metadata');
|
||||||
|
$this->github_app->pull_requests = data_get($permissions, 'pull_requests');
|
||||||
|
$this->github_app->administration = data_get($permissions, 'administration');
|
||||||
|
$this->github_app->save();
|
||||||
|
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
send_internal_notification('GithubAppPermissionJob failed with: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
app/Jobs/ServerLimitCheckJob.php
Normal file
68
app/Jobs/ServerLimitCheckJob.php
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Notifications\Server\ForceDisabled;
|
||||||
|
use App\Notifications\Server\ForceEnabled;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ServerLimitCheckJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $tries = 4;
|
||||||
|
public function backoff(): int
|
||||||
|
{
|
||||||
|
return isDev() ? 1 : 3;
|
||||||
|
}
|
||||||
|
public function __construct(public Team $team)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function middleware(): array
|
||||||
|
{
|
||||||
|
return [(new WithoutOverlapping($this->team->uuid))];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uniqueId(): int
|
||||||
|
{
|
||||||
|
return $this->team->uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$servers = $this->team->servers;
|
||||||
|
$servers_count = $servers->count();
|
||||||
|
$limit = $this->team->limits['serverLimit'];
|
||||||
|
$number_of_servers_to_disable = $servers_count - $limit;
|
||||||
|
ray('ServerLimitCheckJob', $this->team->uuid, $servers_count, $limit, $number_of_servers_to_disable);
|
||||||
|
if ($number_of_servers_to_disable > 0) {
|
||||||
|
ray('Disabling servers');
|
||||||
|
$servers = $servers->sortbyDesc('created_at');
|
||||||
|
$servers_to_disable = $servers->take($number_of_servers_to_disable);
|
||||||
|
$servers_to_disable->each(function ($server) {
|
||||||
|
$server->forceDisableServer();
|
||||||
|
$this->team->notify(new ForceDisabled($server));
|
||||||
|
});
|
||||||
|
} else if ($number_of_servers_to_disable === 0) {
|
||||||
|
$servers->each(function ($server) {
|
||||||
|
if ($server->isForceDisabled()) {
|
||||||
|
$server->forceEnableServer();
|
||||||
|
$this->team->notify(new ForceEnabled($server));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
send_internal_notification('ServerLimitCheckJob failed with: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,15 +41,6 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
throw new \RuntimeException('Server is not ready.');
|
throw new \RuntimeException('Server is not ready.');
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
// $this->server->validateConnection();
|
|
||||||
// $this->server->validateOS();
|
|
||||||
// $docker_installed = $this->server->validateDockerEngine();
|
|
||||||
// if (!$docker_installed) {
|
|
||||||
// $this->server->installDocker();
|
|
||||||
// $this->server->validateDockerEngine();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// $this->server->validateDockerEngineVersion();
|
|
||||||
if ($this->server->isFunctional()) {
|
if ($this->server->isFunctional()) {
|
||||||
$this->cleanup(notify: false);
|
$this->cleanup(notify: false);
|
||||||
}
|
}
|
||||||
|
|||||||
52
app/Listeners/MaintenanceModeDisabledNotification.php
Normal file
52
app/Listeners/MaintenanceModeDisabledNotification.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\MaintenanceModeDisabled as EventsMaintenanceModeDisabled;
|
||||||
|
use Illuminate\Support\Facades\Request;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
||||||
|
|
||||||
|
class MaintenanceModeDisabledNotification
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(EventsMaintenanceModeDisabled $event): void
|
||||||
|
{
|
||||||
|
ray('Maintenance mode disabled!');
|
||||||
|
$files = Storage::disk('webhooks-during-maintenance')->files();
|
||||||
|
$files = collect($files);
|
||||||
|
$files = $files->sort();
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$content = Storage::disk('webhooks-during-maintenance')->get($file);
|
||||||
|
$data = json_decode($content, true);
|
||||||
|
$symfonyRequest = new SymfonyRequest(
|
||||||
|
$data['query'],
|
||||||
|
$data['request'],
|
||||||
|
$data['attributes'],
|
||||||
|
$data['cookies'],
|
||||||
|
$data['files'],
|
||||||
|
$data['server'],
|
||||||
|
$data['content']
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($data['headers'] as $key => $value) {
|
||||||
|
$symfonyRequest->headers->set($key, $value);
|
||||||
|
}
|
||||||
|
$request = Request::createFromBase($symfonyRequest);
|
||||||
|
$endpoint = str($file)->after('_')->beforeLast('_')->value();
|
||||||
|
$class = "App\Http\Controllers\Webhook\\" . ucfirst(str($endpoint)->before('::')->value());
|
||||||
|
$method = str($endpoint)->after('::')->value();
|
||||||
|
try {
|
||||||
|
$instance = new $class();
|
||||||
|
$instance->$method($request);
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
ray($th);
|
||||||
|
} finally {
|
||||||
|
Storage::disk('webhooks-during-maintenance')->delete($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/Listeners/MaintenanceModeEnabledNotification.php
Normal file
27
app/Listeners/MaintenanceModeEnabledNotification.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use App\Events\MaintenanceModeEnabled;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Events\MaintenanceModeEnabled as EventsMaintenanceModeEnabled;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
|
||||||
|
class MaintenanceModeEnabledNotification
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create the event listener.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*/
|
||||||
|
public function handle(EventsMaintenanceModeEnabled $event): void
|
||||||
|
{
|
||||||
|
ray('Maintenance mode enabled!');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Livewire\Admin;
|
namespace App\Livewire\Admin;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -14,28 +15,26 @@ class Index extends Component
|
|||||||
if (!isCloud()) {
|
if (!isCloud()) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
if (auth()->user()->id !== 0 && session('adminToken') === null) {
|
if (auth()->user()->id !== 0) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$this->users = User::whereHas('teams', function ($query) {
|
$this->users = User::whereHas('teams', function ($query) {
|
||||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||||
})->get();
|
})->get()->filter(function ($user) {
|
||||||
|
return $user->id !== 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
public function switchUser(int $user_id)
|
public function switchUser(int $user_id)
|
||||||
{
|
{
|
||||||
$user = User::find($user_id);
|
if (auth()->user()->id !== 0) {
|
||||||
auth()->login($user);
|
return redirect()->route('dashboard');
|
||||||
|
|
||||||
if ($user_id === 0) {
|
|
||||||
session()->forget('adminToken');
|
|
||||||
} else {
|
|
||||||
$token_payload = [
|
|
||||||
'valid' => true,
|
|
||||||
];
|
|
||||||
$token = Crypt::encrypt($token_payload);
|
|
||||||
session(['adminToken' => $token]);
|
|
||||||
}
|
}
|
||||||
return refreshSession();
|
$user = User::find($user_id);
|
||||||
|
$team_to_switch_to = $user->teams->first();
|
||||||
|
Cache::forget("team:{$user->id}");
|
||||||
|
auth()->login($user);
|
||||||
|
refreshSession($team_to_switch_to);
|
||||||
|
return redirect(request()->header('Referer'));
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
}
|
}
|
||||||
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
|
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
|
||||||
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
||||||
$this->installServer();
|
$this->currentState = 'validate-server';
|
||||||
}
|
}
|
||||||
public function getProxyType()
|
public function getProxyType()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ class Dashboard extends Component
|
|||||||
public function cleanup_queue()
|
public function cleanup_queue()
|
||||||
{
|
{
|
||||||
$this->dispatch('success', 'Cleanup started.');
|
$this->dispatch('success', 'Cleanup started.');
|
||||||
Artisan::queue('app:init', [
|
Artisan::queue('cleanup:application-deployment-queue', [
|
||||||
'--cleanup-deployments' => 'true'
|
'--team-id' => currentTeam()->id
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
public function get_deployments()
|
public function get_deployments()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace App\Livewire;
|
|||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Sponsorship extends Component
|
class LayoutPopups extends Component
|
||||||
{
|
{
|
||||||
public function getListeners()
|
public function getListeners()
|
||||||
{
|
{
|
||||||
@@ -23,6 +23,6 @@ class Sponsorship extends Component
|
|||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.sponsorship');
|
return view('livewire.layout-popups');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->dispatch('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ class EmailSettings extends Component
|
|||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team?->notify(new Test($this->emails));
|
$this->team?->notify(new Test($this->emails));
|
||||||
$this->dispatch('success', 'Test Email sent successfully.');
|
$this->dispatch('success', 'Test Email sent.');
|
||||||
}
|
}
|
||||||
public function instantSaveInstance()
|
public function instantSaveInstance()
|
||||||
{
|
{
|
||||||
@@ -83,7 +83,7 @@ class EmailSettings extends Component
|
|||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->dispatch('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->dispatch('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -148,7 +148,7 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->dispatch('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class Index extends Component
|
|||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->dispatch('success', 'Profile updated successfully.');
|
$this->dispatch('success', 'Profile updated');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ class Advanced extends Component
|
|||||||
{
|
{
|
||||||
public Application $application;
|
public Application $application;
|
||||||
public bool $is_force_https_enabled;
|
public bool $is_force_https_enabled;
|
||||||
|
public bool $is_gzip_enabled;
|
||||||
|
public bool $is_stripprefix_enabled;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'application.settings.is_git_submodules_enabled' => 'boolean|required',
|
'application.settings.is_git_submodules_enabled' => 'boolean|required',
|
||||||
'application.settings.is_git_lfs_enabled' => 'boolean|required',
|
'application.settings.is_git_lfs_enabled' => 'boolean|required',
|
||||||
@@ -19,13 +21,17 @@ class Advanced extends Component
|
|||||||
'application.settings.is_gpu_enabled' => 'boolean|required',
|
'application.settings.is_gpu_enabled' => 'boolean|required',
|
||||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||||
'application.settings.is_consistent_container_name_enabled' => 'boolean|required',
|
'application.settings.is_consistent_container_name_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_gzip_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_stripprefix_enabled' => 'boolean|required',
|
||||||
'application.settings.gpu_driver' => 'string|required',
|
'application.settings.gpu_driver' => 'string|required',
|
||||||
'application.settings.gpu_count' => 'string|required',
|
'application.settings.gpu_count' => 'string|required',
|
||||||
'application.settings.gpu_device_ids' => 'string|required',
|
'application.settings.gpu_device_ids' => 'string|required',
|
||||||
'application.settings.gpu_options' => 'string|required',
|
'application.settings.gpu_options' => 'string|required',
|
||||||
];
|
];
|
||||||
public function mount() {
|
public function mount() {
|
||||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
$this->is_force_https_enabled = $this->application->isForceHttpsEnabled();
|
||||||
|
$this->is_gzip_enabled = $this->application->isGzipEnabled();
|
||||||
|
$this->is_stripprefix_enabled = $this->application->isStripprefixEnabled();
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
@@ -40,6 +46,14 @@ class Advanced extends Component
|
|||||||
$this->application->settings->is_force_https_enabled = $this->is_force_https_enabled;
|
$this->application->settings->is_force_https_enabled = $this->is_force_https_enabled;
|
||||||
$this->dispatch('resetDefaultLabels', false);
|
$this->dispatch('resetDefaultLabels', false);
|
||||||
}
|
}
|
||||||
|
if ($this->application->settings->is_gzip_enabled !== $this->is_gzip_enabled) {
|
||||||
|
$this->application->settings->is_gzip_enabled = $this->is_gzip_enabled;
|
||||||
|
$this->dispatch('resetDefaultLabels', false);
|
||||||
|
}
|
||||||
|
if ($this->application->settings->is_stripprefix_enabled !== $this->is_stripprefix_enabled) {
|
||||||
|
$this->application->settings->is_stripprefix_enabled = $this->is_stripprefix_enabled;
|
||||||
|
$this->dispatch('resetDefaultLabels', false);
|
||||||
|
}
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
$this->dispatch('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class Configuration extends Component
|
|||||||
{
|
{
|
||||||
public Application $application;
|
public Application $application;
|
||||||
public $servers;
|
public $servers;
|
||||||
protected $listeners = ['build_pack_updated' => '$refresh'];
|
protected $listeners = ['buildPackUpdated' => '$refresh'];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ class General extends Component
|
|||||||
$this->resetDefaultLabels(false);
|
$this->resetDefaultLabels(false);
|
||||||
}
|
}
|
||||||
$this->submit();
|
$this->submit();
|
||||||
$this->dispatch('build_pack_updated');
|
$this->dispatch('buildPackUpdated');
|
||||||
}
|
}
|
||||||
public function getWildcardDomain()
|
public function getWildcardDomain()
|
||||||
{
|
{
|
||||||
@@ -232,7 +232,6 @@ class General extends Component
|
|||||||
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'application.docker_registry_image_name' => 'required',
|
'application.docker_registry_image_name' => 'required',
|
||||||
'application.docker_registry_image_tag' => 'required',
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if (data_get($this->application, 'fqdn')) {
|
if (data_get($this->application, 'fqdn')) {
|
||||||
@@ -264,7 +263,11 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
if ($this->application->build_pack === 'dockercompose') {
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||||
$this->parsedServices = $this->application->parseCompose();
|
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||||
|
$this->application->parseRawCompose();
|
||||||
|
} else {
|
||||||
|
$this->parsedServices = $this->application->parseCompose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
|
|||||||
@@ -33,15 +33,11 @@ class Heading extends Component
|
|||||||
{
|
{
|
||||||
if ($this->application->destination->server->isFunctional()) {
|
if ($this->application->destination->server->isFunctional()) {
|
||||||
dispatch(new ContainerStatusJob($this->application->destination->server));
|
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||||
// $this->application->refresh();
|
|
||||||
// $this->application->previews->each(function ($preview) {
|
|
||||||
// $preview->refresh();
|
|
||||||
// });
|
|
||||||
} else {
|
} else {
|
||||||
dispatch(new ServerStatusJob($this->application->destination->server));
|
dispatch(new ServerStatusJob($this->application->destination->server));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($showNotification) $this->dispatch('success', "Application status updated.");
|
if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function force_deploy_without_cache()
|
public function force_deploy_without_cache()
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class Form extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
$this->application->preview_url_template = str_replace(' ', '', $this->application->preview_url_template);
|
$this->application->preview_url_template = str_replace(' ', '', $this->application->preview_url_template);
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->dispatch('success', 'Preview url template updated successfully.');
|
$this->dispatch('success', 'Preview url template updated.');
|
||||||
$this->generate_real_url();
|
$this->generate_real_url();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class BackupExecutions extends Component
|
|||||||
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
|
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
|
||||||
}
|
}
|
||||||
$execution->delete();
|
$execution->delete();
|
||||||
$this->dispatch('success', 'Backup deleted successfully.');
|
$this->dispatch('success', 'Backup deleted.');
|
||||||
$this->dispatch('refreshBackupExecutions');
|
$this->dispatch('refreshBackupExecutions');
|
||||||
}
|
}
|
||||||
public function download($exeuctionId)
|
public function download($exeuctionId)
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ class Import extends Component
|
|||||||
public string $container;
|
public string $container;
|
||||||
public array $importCommands = [];
|
public array $importCommands = [];
|
||||||
public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB';
|
public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB';
|
||||||
public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p $MYSQL_PASSWORD $MYSQL_DATABASE';
|
public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE';
|
||||||
public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p $MARIADB_PASSWORD $MARIADB_DATABASE';
|
public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE';
|
||||||
|
|
||||||
public function getListeners()
|
public function getListeners()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ class General extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->db_url = $this->database->getDbUrl(true);
|
$this->db_url = $this->database->get_db_url(true);
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSaveAdvanced() {
|
public function instantSaveAdvanced() {
|
||||||
@@ -59,7 +59,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -73,7 +73,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ class General extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->db_url = $this->database->getDbUrl(true);
|
$this->db_url = $this->database->get_db_url(true);
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSaveAdvanced()
|
public function instantSaveAdvanced()
|
||||||
@@ -58,7 +58,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -75,7 +75,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ class General extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->db_url = $this->database->getDbUrl(true);
|
$this->db_url = $this->database->get_db_url(true);
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSaveAdvanced()
|
public function instantSaveAdvanced()
|
||||||
@@ -60,7 +60,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -74,7 +74,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ class General extends Component
|
|||||||
];
|
];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->db_url = $this->database->getDbUrl(true);
|
$this->db_url = $this->database->get_db_url(true);
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSaveAdvanced() {
|
public function instantSaveAdvanced() {
|
||||||
@@ -66,7 +66,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -87,7 +87,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
@@ -105,7 +105,7 @@ class General extends Component
|
|||||||
$this->database->init_scripts = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']);
|
$this->database->init_scripts = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']);
|
||||||
$this->database->init_scripts = array_merge($this->database->init_scripts, [$script]);
|
$this->database->init_scripts = array_merge($this->database->init_scripts, [$script]);
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Init script saved successfully.');
|
$this->dispatch('success', 'Init script saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_init_script($script)
|
public function delete_init_script($script)
|
||||||
@@ -116,7 +116,7 @@ class General extends Component
|
|||||||
$this->database->init_scripts = $collection->filter(fn ($s) => $s['filename'] !== $script['filename'])->toArray();
|
$this->database->init_scripts = $collection->filter(fn ($s) => $s['filename'] !== $script['filename'])->toArray();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->refresh();
|
$this->refresh();
|
||||||
$this->dispatch('success', 'Init script deleted successfully.');
|
$this->dispatch('success', 'Init script deleted.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ class General extends Component
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Init script added successfully.');
|
$this->dispatch('success', 'Init script added.');
|
||||||
$this->new_content = '';
|
$this->new_content = '';
|
||||||
$this->new_filename = '';
|
$this->new_filename = '';
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ class General extends Component
|
|||||||
];
|
];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->db_url = $this->database->getDbUrl(true);
|
$this->db_url = $this->database->get_db_url(true);
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSaveAdvanced() {
|
public function instantSaveAdvanced() {
|
||||||
@@ -52,7 +52,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -66,7 +66,7 @@ class General extends Component
|
|||||||
$this->database->redis_conf = null;
|
$this->database->redis_conf = null;
|
||||||
}
|
}
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'Database updated successfully.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ class General extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StartDatabaseProxy::run($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->db_url_public = $this->database->getDbUrl();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
StopDatabaseProxy::run($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class ScheduledBackups extends Component
|
|||||||
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();
|
||||||
$this->dispatch('success', 'Scheduled backup deleted successfully.');
|
$this->dispatch('success', 'Scheduled backup deleted.');
|
||||||
$this->refreshScheduledBackups();
|
$this->refreshScheduledBackups();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,10 @@ class GithubPrivateRepository extends Component
|
|||||||
$this->loadRepositoryByPage();
|
$this->loadRepositoryByPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->selected_repository_id = $this->repositories[0]['id'];
|
$this->repositories = $this->repositories->sortBy('name');
|
||||||
|
if ($this->repositories->count() > 0) {
|
||||||
|
$this->selected_repository_id = data_get($this->repositories->first(), 'id');
|
||||||
|
}
|
||||||
$this->current_step = 'repository';
|
$this->current_step = 'repository';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,10 +71,10 @@ class Select extends Component
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public function loadServices()
|
public function loadServices(bool $force = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (count($this->allServices) > 0) {
|
if (count($this->allServices) > 0 && !$force) {
|
||||||
if (!$this->search) {
|
if (!$this->search) {
|
||||||
$this->services = $this->allServices;
|
$this->services = $this->allServices;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ use Livewire\Component;
|
|||||||
class Create extends Component
|
class Create extends Component
|
||||||
{
|
{
|
||||||
public $type;
|
public $type;
|
||||||
public function mount() {
|
public function mount()
|
||||||
|
{
|
||||||
$services = getServiceTemplates();
|
$services = getServiceTemplates();
|
||||||
$type = str(request()->query('type'));
|
$type = str(request()->query('type'));
|
||||||
$destination_uuid = request()->query('destination');
|
$destination_uuid = request()->query('destination');
|
||||||
@@ -70,7 +71,7 @@ class Create extends Component
|
|||||||
$generatedValue = $value;
|
$generatedValue = $value;
|
||||||
if ($value->contains('SERVICE_')) {
|
if ($value->contains('SERVICE_')) {
|
||||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
$generatedValue = generateEnvValue($command->value());
|
$generatedValue = generateEnvValue($command->value(), $service);
|
||||||
}
|
}
|
||||||
EnvironmentVariable::create([
|
EnvironmentVariable::create([
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
|
|||||||
@@ -17,9 +17,8 @@ class Configuration extends Component
|
|||||||
{
|
{
|
||||||
$userId = auth()->user()->id;
|
$userId = auth()->user()->id;
|
||||||
return [
|
return [
|
||||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'checkStatus',
|
"echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
|
||||||
"refreshStacks",
|
"check_status"
|
||||||
"checkStatus",
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
@@ -37,21 +36,10 @@ class Configuration extends Component
|
|||||||
$this->applications = $this->service->applications->sort();
|
$this->applications = $this->service->applications->sort();
|
||||||
$this->databases = $this->service->databases->sort();
|
$this->databases = $this->service->databases->sort();
|
||||||
}
|
}
|
||||||
public function checkStatus()
|
public function check_status()
|
||||||
{
|
{
|
||||||
dispatch_sync(new ContainerStatusJob($this->service->server));
|
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||||
$this->refreshStacks();
|
$this->dispatch('refresh')->self();
|
||||||
$this->dispatch('serviceStatusChanged');
|
$this->dispatch('serviceStatusChanged');
|
||||||
}
|
}
|
||||||
public function refreshStacks()
|
|
||||||
{
|
|
||||||
$this->applications = $this->service->applications->sort();
|
|
||||||
$this->applications->each(function ($application) {
|
|
||||||
$application->refresh();
|
|
||||||
});
|
|
||||||
$this->databases = $this->service->databases->sort();
|
|
||||||
$this->databases->each(function ($database) {
|
|
||||||
$database->refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class Database extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
updateCompose($this->database);
|
updateCompose($this->database);
|
||||||
$this->dispatch('success', 'Database saved successfully.');
|
$this->dispatch('success', 'Database saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class FileStorage extends Component
|
|||||||
}
|
}
|
||||||
$this->fileStorage->save();
|
$this->fileStorage->save();
|
||||||
$this->fileStorage->saveStorageOnServer();
|
$this->fileStorage->saveStorageOnServer();
|
||||||
$this->dispatch('success', 'File updated successfully.');
|
$this->dispatch('success', 'File updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->fileStorage->setRawAttributes($original);
|
$this->fileStorage->setRawAttributes($original);
|
||||||
$this->fileStorage->save();
|
$this->fileStorage->save();
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use App\Actions\Shared\PullImage;
|
|||||||
use App\Actions\Service\StartService;
|
use App\Actions\Service\StartService;
|
||||||
use App\Actions\Service\StopService;
|
use App\Actions\Service\StopService;
|
||||||
use App\Events\ServiceStatusChanged;
|
use App\Events\ServiceStatusChanged;
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
@@ -17,7 +16,24 @@ class Navbar extends Component
|
|||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
public $isDeploymentProgress = false;
|
public $isDeploymentProgress = false;
|
||||||
|
public function getListeners()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"serviceStatusChanged"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
public function serviceStatusChanged()
|
||||||
|
{
|
||||||
|
$this->dispatch('refresh')->self();
|
||||||
|
}
|
||||||
|
public function check_status() {
|
||||||
|
$this->dispatch('check_status');
|
||||||
|
$this->dispatch('success', 'Service status updated.');
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.navbar');
|
||||||
|
}
|
||||||
public function checkDeployments()
|
public function checkDeployments()
|
||||||
{
|
{
|
||||||
$activity = Activity::where('properties->type_uuid', $this->service->uuid)->latest()->first();
|
$activity = Activity::where('properties->type_uuid', $this->service->uuid)->latest()->first();
|
||||||
@@ -28,26 +44,6 @@ class Navbar extends Component
|
|||||||
$this->isDeploymentProgress = false;
|
$this->isDeploymentProgress = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function getListeners()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
"serviceStatusChanged"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
public function serviceStatusChanged()
|
|
||||||
{
|
|
||||||
$this->service->refresh();
|
|
||||||
}
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.navbar');
|
|
||||||
}
|
|
||||||
public function check_status($showNotification = false)
|
|
||||||
{
|
|
||||||
dispatch_sync(new ContainerStatusJob($this->service->destination->server));
|
|
||||||
$this->service->refresh();
|
|
||||||
if ($showNotification) $this->dispatch('success', 'Service status updated.');
|
|
||||||
}
|
|
||||||
public function deploy()
|
public function deploy()
|
||||||
{
|
{
|
||||||
$this->checkDeployments();
|
$this->checkDeployments();
|
||||||
@@ -62,11 +58,10 @@ class Navbar extends Component
|
|||||||
public function stop(bool $forceCleanup = false)
|
public function stop(bool $forceCleanup = false)
|
||||||
{
|
{
|
||||||
StopService::run($this->service);
|
StopService::run($this->service);
|
||||||
$this->service->refresh();
|
|
||||||
if ($forceCleanup) {
|
if ($forceCleanup) {
|
||||||
$this->dispatch('success', 'Force cleanup service successfully.');
|
$this->dispatch('success', 'Containers cleaned up.');
|
||||||
} else {
|
} else {
|
||||||
$this->dispatch('success', 'Service stopped successfully.');
|
$this->dispatch('success', 'Service stopped.');
|
||||||
}
|
}
|
||||||
ServiceStatusChanged::dispatch();
|
ServiceStatusChanged::dispatch();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class ServiceApplicationView extends Component
|
|||||||
'application.required_fqdn' => 'required|boolean',
|
'application.required_fqdn' => 'required|boolean',
|
||||||
'application.is_log_drain_enabled' => 'nullable|boolean',
|
'application.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
'application.is_gzip_enabled' => 'nullable|boolean',
|
'application.is_gzip_enabled' => 'nullable|boolean',
|
||||||
|
'application.is_stripprefix_enabled' => 'nullable|boolean',
|
||||||
];
|
];
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
@@ -41,7 +42,7 @@ class ServiceApplicationView extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->application->delete();
|
$this->application->delete();
|
||||||
$this->dispatch('success', 'Application deleted successfully.');
|
$this->dispatch('success', 'Application deleted.');
|
||||||
return redirect()->route('project.service.configuration', $this->parameters);
|
return redirect()->route('project.service.configuration', $this->parameters);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -58,7 +59,7 @@ class ServiceApplicationView extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
updateCompose($this->application);
|
updateCompose($this->application);
|
||||||
$this->dispatch('success', 'Application saved successfully.');
|
$this->dispatch('success', 'Application saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class StackForm extends Component
|
|||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
$this->service->save();
|
$this->service->save();
|
||||||
$this->dispatch('success', 'Service settings saved successfully.');
|
$this->dispatch('success', 'Service settings saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
@@ -62,7 +62,7 @@ class StackForm extends Component
|
|||||||
$this->service->saveComposeConfigs();
|
$this->service->saveComposeConfigs();
|
||||||
$this->dispatch('refreshStacks');
|
$this->dispatch('refreshStacks');
|
||||||
$this->dispatch('refreshEnvs');
|
$this->dispatch('refreshEnvs');
|
||||||
$this->dispatch('success', 'Service saved successfully.');
|
$this->dispatch('success', 'Service saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Shared;
|
|||||||
|
|
||||||
use App\Actions\Application\StopApplicationOneServer;
|
use App\Actions\Application\StopApplicationOneServer;
|
||||||
use App\Events\ApplicationStatusChanged;
|
use App\Events\ApplicationStatusChanged;
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -40,6 +41,17 @@ class Destination extends Component
|
|||||||
$this->networks = $this->networks->reject(function ($network) {
|
$this->networks = $this->networks->reject(function ($network) {
|
||||||
return $this->resource->destination->server->id == $network->server->id;
|
return $this->resource->destination->server->id == $network->server->id;
|
||||||
});
|
});
|
||||||
|
if ($this->resource?->additional_servers?->count() > 0) {
|
||||||
|
$this->networks = $this->networks->reject(function ($network) {
|
||||||
|
return $this->resource->additional_servers->pluck('id')->contains($network->server->id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function stop(int $server_id)
|
||||||
|
{
|
||||||
|
$server = Server::find($server_id);
|
||||||
|
StopApplicationOneServer::run($this->resource, $server);
|
||||||
|
$this->refreshServers();
|
||||||
}
|
}
|
||||||
public function redeploy(int $network_id, int $server_id)
|
public function redeploy(int $network_id, int $server_id)
|
||||||
{
|
{
|
||||||
@@ -55,6 +67,7 @@ class Destination extends Component
|
|||||||
application: $this->resource,
|
application: $this->resource,
|
||||||
server: $server,
|
server: $server,
|
||||||
destination: $destination,
|
destination: $destination,
|
||||||
|
only_this_server: true,
|
||||||
no_questions_asked: true,
|
no_questions_asked: true,
|
||||||
);
|
);
|
||||||
return redirect()->route('project.application.deployment.show', [
|
return redirect()->route('project.application.deployment.show', [
|
||||||
@@ -64,12 +77,29 @@ class Destination extends Component
|
|||||||
'environment_name' => data_get($this->resource, 'environment.name'),
|
'environment_name' => data_get($this->resource, 'environment.name'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
public function promote(int $network_id, int $server_id)
|
||||||
|
{
|
||||||
|
$main_destination = $this->resource->destination;
|
||||||
|
$this->resource->update([
|
||||||
|
'destination_id' => $network_id,
|
||||||
|
'destination_type' => StandaloneDocker::class,
|
||||||
|
]);
|
||||||
|
$this->resource->additional_networks()->detach($network_id, ['server_id' => $server_id]);
|
||||||
|
$this->resource->additional_networks()->attach($main_destination->id, ['server_id' => $main_destination->server->id]);
|
||||||
|
$this->refreshServers();
|
||||||
|
}
|
||||||
|
public function refreshServers()
|
||||||
|
{
|
||||||
|
ContainerStatusJob::dispatchSync($this->resource->destination->server);
|
||||||
|
$this->loadData();
|
||||||
|
$this->dispatch('refresh');
|
||||||
|
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||||
|
}
|
||||||
public function addServer(int $network_id, int $server_id)
|
public function addServer(int $network_id, int $server_id)
|
||||||
{
|
{
|
||||||
$this->resource->additional_networks()->attach($network_id, ['server_id' => $server_id]);
|
$this->resource->additional_networks()->attach($network_id, ['server_id' => $server_id]);
|
||||||
$this->resource->load(['additional_networks']);
|
|
||||||
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
|
||||||
$this->loadData();
|
$this->loadData();
|
||||||
|
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||||
}
|
}
|
||||||
public function removeServer(int $network_id, int $server_id)
|
public function removeServer(int $network_id, int $server_id)
|
||||||
{
|
{
|
||||||
@@ -80,8 +110,7 @@ class Destination extends Component
|
|||||||
$server = Server::find($server_id);
|
$server = Server::find($server_id);
|
||||||
StopApplicationOneServer::run($this->resource, $server);
|
StopApplicationOneServer::run($this->resource, $server);
|
||||||
$this->resource->additional_networks()->detach($network_id, ['server_id' => $server_id]);
|
$this->resource->additional_networks()->detach($network_id, ['server_id' => $server_id]);
|
||||||
$this->resource->load(['additional_networks']);
|
|
||||||
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
|
||||||
$this->loadData();
|
$this->loadData();
|
||||||
|
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,9 +120,9 @@ class All extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($isPreview) {
|
if ($isPreview) {
|
||||||
$this->dispatch('success', 'Preview environment variables updated successfully.');
|
$this->dispatch('success', 'Preview environment variables updated.');
|
||||||
} else {
|
} else {
|
||||||
$this->dispatch('success', 'Environment variables updated successfully.');
|
$this->dispatch('success', 'Environment variables updated.');
|
||||||
}
|
}
|
||||||
$this->refreshEnvs();
|
$this->refreshEnvs();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class Show extends Component
|
|||||||
}
|
}
|
||||||
$this->serialize();
|
$this->serialize();
|
||||||
$this->env->save();
|
$this->env->save();
|
||||||
$this->dispatch('success', 'Environment variable updated successfully.');
|
$this->dispatch('success', 'Environment variable updated.');
|
||||||
$this->dispatch('refreshEnvs');
|
$this->dispatch('refreshEnvs');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
|
|||||||
@@ -10,29 +10,21 @@ use App\Models\StandaloneMongodb;
|
|||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class ExecuteContainerCommand extends Component
|
class ExecuteContainerCommand extends Component
|
||||||
{
|
{
|
||||||
public string $command;
|
public string $command;
|
||||||
public string $container;
|
public string $container;
|
||||||
public $containers;
|
public Collection $containers;
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public $resource;
|
public $resource;
|
||||||
public string $type;
|
public string $type;
|
||||||
public string $workDir = '';
|
public string $workDir = '';
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public $servers = [];
|
public Collection $servers;
|
||||||
public function getListeners()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
"serviceStatusChanged",
|
|
||||||
];
|
|
||||||
}
|
|
||||||
public function serviceStatusChanged()
|
|
||||||
{
|
|
||||||
$this->getContainers();
|
|
||||||
}
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'server' => 'required',
|
'server' => 'required',
|
||||||
'container' => 'required',
|
'container' => 'required',
|
||||||
@@ -43,20 +35,18 @@ class ExecuteContainerCommand extends Component
|
|||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
$this->getContainers();
|
|
||||||
}
|
|
||||||
public function getContainers()
|
|
||||||
{
|
|
||||||
$this->containers = collect();
|
$this->containers = collect();
|
||||||
|
$this->servers = collect();
|
||||||
if (data_get($this->parameters, 'application_uuid')) {
|
if (data_get($this->parameters, 'application_uuid')) {
|
||||||
$this->type = 'application';
|
$this->type = 'application';
|
||||||
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
||||||
$this->server = $this->resource->destination->server;
|
if ($this->resource->destination->server->isFunctional()) {
|
||||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
|
$this->servers = $this->servers->push($this->resource->destination->server);
|
||||||
if ($containers->count() > 0) {
|
}
|
||||||
$containers->each(function ($container) {
|
foreach ($this->resource->additional_servers as $server) {
|
||||||
$this->containers->push(str_replace('/', '', $container['Names']));
|
if ($server->isFunctional()) {
|
||||||
});
|
$this->servers = $this->servers->push($server);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||||
$this->type = 'database';
|
$this->type = 'database';
|
||||||
@@ -77,44 +67,85 @@ class ExecuteContainerCommand extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->resource = $resource;
|
$this->resource = $resource;
|
||||||
$this->server = $this->resource->destination->server;
|
if ($this->resource->destination->server->isFunctional()) {
|
||||||
|
$this->servers = $this->servers->push($this->resource->destination->server);
|
||||||
|
}
|
||||||
$this->container = $this->resource->uuid;
|
$this->container = $this->resource->uuid;
|
||||||
// if (!str(data_get($this,'resource.status'))->startsWith('exited')) {
|
$this->containers->push($this->container);
|
||||||
$this->containers->push($this->container);
|
|
||||||
// }
|
|
||||||
} else if (data_get($this->parameters, 'service_uuid')) {
|
} else if (data_get($this->parameters, 'service_uuid')) {
|
||||||
$this->type = 'service';
|
$this->type = 'service';
|
||||||
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
||||||
$this->resource->applications()->get()->each(function ($application) {
|
$this->resource->applications()->get()->each(function ($application) {
|
||||||
// if (str(data_get($application, 'status'))->contains('running')) {
|
$this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
|
||||||
$this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
$this->resource->databases()->get()->each(function ($database) {
|
$this->resource->databases()->get()->each(function ($database) {
|
||||||
// if (str(data_get($database, 'status'))->contains('running')) {
|
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
|
||||||
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
|
if ($this->resource->server->isFunctional()) {
|
||||||
$this->server = $this->resource->server;
|
$this->servers = $this->servers->push($this->resource->server);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($this->containers->count() > 0) {
|
if ($this->containers->count() > 0) {
|
||||||
$this->container = $this->containers->first();
|
$this->container = $this->containers->first();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function loadContainers()
|
||||||
|
{
|
||||||
|
foreach ($this->servers as $server) {
|
||||||
|
if (data_get($this->parameters, 'application_uuid')) {
|
||||||
|
if ($server->isSwarm()) {
|
||||||
|
$containers = collect([
|
||||||
|
[
|
||||||
|
'Names' => $this->resource->uuid . '_' . $this->resource->uuid,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
|
||||||
|
}
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$payload = [
|
||||||
|
'server' => $server,
|
||||||
|
'container' => $container,
|
||||||
|
];
|
||||||
|
$this->containers = $this->containers->push($payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->containers->count() > 0) {
|
||||||
|
if (data_get($this->parameters, 'application_uuid')) {
|
||||||
|
$this->container = data_get($this->containers->first(), 'container.Names');
|
||||||
|
} elseif (data_get($this->parameters, 'database_uuid')) {
|
||||||
|
$this->container = $this->containers->first();
|
||||||
|
} elseif (data_get($this->parameters, 'service_uuid')) {
|
||||||
|
$this->container = $this->containers->first();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function runCommand()
|
public function runCommand()
|
||||||
{
|
{
|
||||||
$this->validate();
|
|
||||||
try {
|
try {
|
||||||
// Wrap command to prevent escaped execution in the host.
|
if (data_get($this->parameters, 'application_uuid')) {
|
||||||
|
$container = $this->containers->where('container.Names', $this->container)->first();
|
||||||
|
$container_name = data_get($container, 'container.Names');
|
||||||
|
if (is_null($container)) {
|
||||||
|
throw new \RuntimeException('Container not found.');
|
||||||
|
}
|
||||||
|
$server = data_get($container, 'server');
|
||||||
|
} else {
|
||||||
|
$container_name = $this->container;
|
||||||
|
$server = $this->servers->first();
|
||||||
|
}
|
||||||
|
if ($server->isForceDisabled()) {
|
||||||
|
throw new \RuntimeException('Server is disabled.');
|
||||||
|
}
|
||||||
$cmd = 'sh -c "if [ -f ~/.profile ]; then . ~/.profile; fi; ' . str_replace('"', '\"', $this->command) . '"';
|
$cmd = 'sh -c "if [ -f ~/.profile ]; then . ~/.profile; fi; ' . str_replace('"', '\"', $this->command) . '"';
|
||||||
if (!empty($this->workDir)) {
|
if (!empty($this->workDir)) {
|
||||||
$exec = "docker exec -w {$this->workDir} {$this->container} {$cmd}";
|
$exec = "docker exec -w {$this->workDir} {$container_name} {$cmd}";
|
||||||
} else {
|
} else {
|
||||||
$exec = "docker exec {$this->container} {$cmd}";
|
$exec = "docker exec {$container_name} {$cmd}";
|
||||||
}
|
}
|
||||||
$activity = remote_process([$exec], $this->server, ignore_errors: true);
|
$activity = remote_process([$exec], $server, ignore_errors: true);
|
||||||
$this->dispatch('activityMonitor', $activity->id);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class GetLogs extends Component
|
|||||||
public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
|
public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public ?string $container = null;
|
public ?string $container = null;
|
||||||
|
public ?string $pull_request = null;
|
||||||
public ?bool $streamLogs = false;
|
public ?bool $streamLogs = false;
|
||||||
public ?bool $showTimeStamps = true;
|
public ?bool $showTimeStamps = true;
|
||||||
public int $numberOfLines = 100;
|
public int $numberOfLines = 100;
|
||||||
@@ -70,7 +71,14 @@ class GetLogs extends Component
|
|||||||
}
|
}
|
||||||
public function getLogs($refresh = false)
|
public function getLogs($refresh = false)
|
||||||
{
|
{
|
||||||
if (!$refresh && $this->resource?->getMorphClass() === 'App\Models\Service') return;
|
if ($this->resource?->getMorphClass() === 'App\Models\Application') {
|
||||||
|
if (str($this->container)->contains('-pr-')) {
|
||||||
|
$this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value();
|
||||||
|
} else {
|
||||||
|
$this->pull_request = 'branch';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$refresh && ($this->resource?->getMorphClass() === 'App\Models\Service' || str($this->container)->contains('-pr-'))) return;
|
||||||
if ($this->container) {
|
if ($this->container) {
|
||||||
if ($this->showTimeStamps) {
|
if ($this->showTimeStamps) {
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
|
|||||||
@@ -10,84 +10,99 @@ use App\Models\StandaloneMongodb;
|
|||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Logs extends Component
|
class Logs extends Component
|
||||||
{
|
{
|
||||||
public ?string $type = null;
|
public ?string $type = null;
|
||||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
|
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
|
||||||
public Server $server;
|
public Collection $servers;
|
||||||
|
public Collection $containers;
|
||||||
public $container = [];
|
public $container = [];
|
||||||
public $containers;
|
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public $query;
|
public $query;
|
||||||
public $status;
|
public $status;
|
||||||
public $serviceSubType;
|
public $serviceSubType;
|
||||||
|
|
||||||
public function mount()
|
public function loadContainers($server_id)
|
||||||
{
|
{
|
||||||
$this->containers = collect();
|
try {
|
||||||
$this->parameters = get_route_parameters();
|
$server = $this->servers->firstWhere('id', $server_id);
|
||||||
$this->query = request()->query();
|
if ($server->isSwarm()) {
|
||||||
if (data_get($this->parameters, 'application_uuid')) {
|
|
||||||
$this->type = 'application';
|
|
||||||
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
|
||||||
$this->status = $this->resource->status;
|
|
||||||
$this->server = $this->resource->destination->server;
|
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
$containers = collect([
|
$containers = collect([
|
||||||
[
|
[
|
||||||
'Names' => $this->resource->uuid . '_' . $this->resource->uuid,
|
'Names' => $this->resource->uuid . '_' . $this->resource->uuid,
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
|
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
|
||||||
}
|
}
|
||||||
if ($containers->count() > 0) {
|
$server->containers = $containers;
|
||||||
$containers->each(function ($container) {
|
} catch (\Exception $e) {
|
||||||
$this->containers->push(str_replace('/', '', $container['Names']));
|
return handleError($e, $this);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
public function mount()
|
||||||
$this->type = 'database';
|
{
|
||||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
try {
|
||||||
if (is_null($resource)) {
|
$this->containers = collect();
|
||||||
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
$this->servers = collect();
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
if (data_get($this->parameters, 'application_uuid')) {
|
||||||
|
$this->type = 'application';
|
||||||
|
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
||||||
|
$this->status = $this->resource->status;
|
||||||
|
if ($this->resource->destination->server->isFunctional()) {
|
||||||
|
$this->servers = $this->servers->push($this->resource->destination->server);
|
||||||
|
}
|
||||||
|
foreach ($this->resource->additional_servers as $server) {
|
||||||
|
if ($server->isFunctional()) {
|
||||||
|
$this->servers = $this->servers->push($server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||||
|
$this->type = 'database';
|
||||||
|
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
if (is_null($resource)) {
|
if (is_null($resource)) {
|
||||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
if (is_null($resource)) {
|
if (is_null($resource)) {
|
||||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
if (is_null($resource)) {
|
if (is_null($resource)) {
|
||||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
if (is_null($resource)) {
|
if (is_null($resource)) {
|
||||||
abort(404);
|
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
|
if (is_null($resource)) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
$this->resource = $resource;
|
||||||
$this->resource = $resource;
|
$this->status = $this->resource->status;
|
||||||
$this->status = $this->resource->status;
|
if ($this->resource->destination->server->isFunctional()) {
|
||||||
$this->server = $this->resource->destination->server;
|
$this->servers = $this->servers->push($this->resource->destination->server);
|
||||||
$this->container = $this->resource->uuid;
|
}
|
||||||
// if (str(data_get($this, 'resource.status'))->startsWith('running')) {
|
$this->container = $this->resource->uuid;
|
||||||
$this->containers->push($this->container);
|
$this->containers->push($this->container);
|
||||||
// }
|
} else if (data_get($this->parameters, 'service_uuid')) {
|
||||||
} else if (data_get($this->parameters, 'service_uuid')) {
|
$this->type = 'service';
|
||||||
$this->type = 'service';
|
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
||||||
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
$this->resource->applications()->get()->each(function ($application) {
|
||||||
$this->resource->applications()->get()->each(function ($application) {
|
|
||||||
// if (str(data_get($application, 'status'))->contains('running')) {
|
|
||||||
$this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
|
$this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
|
||||||
// }
|
});
|
||||||
});
|
$this->resource->databases()->get()->each(function ($database) {
|
||||||
$this->resource->databases()->get()->each(function ($database) {
|
|
||||||
// if (str(data_get($database, 'status'))->contains('running')) {
|
|
||||||
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
|
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
|
||||||
// }
|
});
|
||||||
});
|
|
||||||
|
|
||||||
$this->server = $this->resource->server;
|
if ($this->resource->server->isFunctional()) {
|
||||||
|
$this->servers = $this->servers->push($this->resource->server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class ResourceLimits extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->resource->save();
|
$this->resource->save();
|
||||||
$this->dispatch('success', 'Resource limits updated successfully.');
|
$this->dispatch('success', 'Resource limits updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class All extends Component
|
|||||||
}
|
}
|
||||||
$task->save();
|
$task->save();
|
||||||
$this->refreshTasks();
|
$this->refreshTasks();
|
||||||
$this->dispatch('success', 'Scheduled task added successfully.');
|
$this->dispatch('success', 'Scheduled task added.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class Show extends Component
|
|||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->task->save();
|
$this->task->save();
|
||||||
$this->dispatch('success', 'Scheduled task updated successfully.');
|
$this->dispatch('success', 'Scheduled task updated.');
|
||||||
$this->dispatch('refreshTasks');
|
$this->dispatch('refreshTasks');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Livewire\Server;
|
namespace App\Livewire\Server;
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
|
use App\Models\Team;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Create extends Component
|
class Create extends Component
|
||||||
@@ -16,11 +17,7 @@ class Create extends Component
|
|||||||
$this->limit_reached = false;
|
$this->limit_reached = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$team = currentTeam();
|
$this->limit_reached = Team::serverLimitReached();
|
||||||
$servers = $team->servers->count();
|
|
||||||
['serverLimit' => $serverLimit] = $team->limits;
|
|
||||||
|
|
||||||
$this->limit_reached = $servers >= $serverLimit;
|
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class Form extends Component
|
|||||||
refresh_server_connection($this->server->privateKey);
|
refresh_server_connection($this->server->privateKey);
|
||||||
$this->validateServer(false);
|
$this->validateServer(false);
|
||||||
$this->server->settings->save();
|
$this->server->settings->save();
|
||||||
$this->dispatch('success', 'Server updated successfully.');
|
$this->dispatch('success', 'Server updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,7 @@ class Form extends Component
|
|||||||
}
|
}
|
||||||
public function validateServer($install = true)
|
public function validateServer($install = true)
|
||||||
{
|
{
|
||||||
$this->dispatch('validateServer', $install);
|
$this->dispatch('init', $install);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
@@ -113,6 +113,6 @@ class Form extends Component
|
|||||||
$this->server->settings->cleanup_after_percentage = $this->cleanup_after_percentage;
|
$this->server->settings->cleanup_after_percentage = $this->cleanup_after_percentage;
|
||||||
$this->server->settings->save();
|
$this->server->settings->save();
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
$this->dispatch('success', 'Server updated successfully.');
|
$this->dispatch('success', 'Server updated.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class LogDrains extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->dispatch('serverRefresh');
|
$this->dispatch('serverRefresh');
|
||||||
$this->dispatch('success', 'Log drain service started successfully.');
|
$this->dispatch('success', 'Log drain service started.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ class LogDrains extends Component
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$this->server->settings->save();
|
$this->server->settings->save();
|
||||||
$this->dispatch('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
return true;
|
return true;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
if ($type === 'newrelic') {
|
if ($type === 'newrelic') {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Livewire\Server\New;
|
|||||||
use App\Enums\ProxyStatus;
|
use App\Enums\ProxyStatus;
|
||||||
use App\Enums\ProxyTypes;
|
use App\Enums\ProxyTypes;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\Team;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class ByIp extends Component
|
class ByIp extends Component
|
||||||
@@ -76,6 +77,9 @@ class ByIp extends Component
|
|||||||
if (is_null($this->private_key_id)) {
|
if (is_null($this->private_key_id)) {
|
||||||
return $this->dispatch('error', 'You must select a private key');
|
return $this->dispatch('error', 'You must select a private key');
|
||||||
}
|
}
|
||||||
|
if (Team::serverLimitReached()) {
|
||||||
|
return $this->dispatch('error', 'You have reached the server limit for your subscription.');
|
||||||
|
}
|
||||||
$payload = [
|
$payload = [
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Server;
|
|||||||
|
|
||||||
use App\Actions\Proxy\CheckConfiguration;
|
use App\Actions\Proxy\CheckConfiguration;
|
||||||
use App\Actions\Proxy\SaveConfiguration;
|
use App\Actions\Proxy\SaveConfiguration;
|
||||||
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -26,7 +27,7 @@ class Proxy extends Component
|
|||||||
|
|
||||||
public function proxyStatusUpdated()
|
public function proxyStatusUpdated()
|
||||||
{
|
{
|
||||||
$this->server->refresh();
|
$this->dispatch('refresh')->self();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function change_proxy()
|
public function change_proxy()
|
||||||
@@ -41,6 +42,9 @@ class Proxy extends Component
|
|||||||
$this->server->proxy->set('type', $proxy_type);
|
$this->server->proxy->set('type', $proxy_type);
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
$this->selectedProxy = $this->server->proxy->type;
|
$this->selectedProxy = $this->server->proxy->type;
|
||||||
|
if ($this->selectedProxy !== 'NONE') {
|
||||||
|
StartProxy::run($this->server, false);
|
||||||
|
}
|
||||||
$this->dispatch('proxyStatusUpdated');
|
$this->dispatch('proxyStatusUpdated');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
28
app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
Normal file
28
app/Livewire/Server/Proxy/DynamicConfigurationNavbar.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Server\Proxy;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class DynamicConfigurationNavbar extends Component
|
||||||
|
{
|
||||||
|
public $server_id;
|
||||||
|
public $fileName = '';
|
||||||
|
public $value = '';
|
||||||
|
public $newFile = false;
|
||||||
|
public function delete(string $fileName)
|
||||||
|
{
|
||||||
|
$server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first();
|
||||||
|
$proxy_path = get_proxy_path();
|
||||||
|
$file = str_replace('|', '.', $fileName);
|
||||||
|
instant_remote_process(["rm -f {$proxy_path}/dynamic/{$file}"], $server);
|
||||||
|
$this->dispatch('success', 'File deleted.');
|
||||||
|
$this->dispatch('loadDynamicConfigurations');
|
||||||
|
$this->dispatch('refresh');
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.proxy.dynamic-configuration-navbar');
|
||||||
|
}
|
||||||
|
}
|
||||||
51
app/Livewire/Server/Proxy/DynamicConfigurations.php
Normal file
51
app/Livewire/Server/Proxy/DynamicConfigurations.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Server\Proxy;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class DynamicConfigurations extends Component
|
||||||
|
{
|
||||||
|
public ?Server $server = null;
|
||||||
|
public $parameters = [];
|
||||||
|
public Collection $contents;
|
||||||
|
protected $listeners = ['loadDynamicConfigurations', 'refresh' => '$refresh'];
|
||||||
|
protected $rules = [
|
||||||
|
'contents.*' => 'nullable|string',
|
||||||
|
];
|
||||||
|
public function loadDynamicConfigurations()
|
||||||
|
{
|
||||||
|
$proxy_path = get_proxy_path();
|
||||||
|
$files = instant_remote_process(["mkdir -p $proxy_path/dynamic && ls -1 {$proxy_path}/dynamic"], $this->server);
|
||||||
|
$files = collect(explode("\n", $files))->filter(fn ($file) => !empty($file));
|
||||||
|
$files = $files->map(fn ($file) => trim($file));
|
||||||
|
$files = $files->sort();
|
||||||
|
if ($files->contains('coolify.yaml')) {
|
||||||
|
$files = $files->filter(fn ($file) => $file !== 'coolify.yaml')->prepend('coolify.yaml');
|
||||||
|
}
|
||||||
|
$contents = collect([]);
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$without_extension = str_replace('.', '|', $file);
|
||||||
|
$contents[$without_extension] = instant_remote_process(["cat {$proxy_path}/dynamic/{$file}"], $this->server);
|
||||||
|
}
|
||||||
|
$this->contents = $contents;
|
||||||
|
}
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
try {
|
||||||
|
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||||
|
if (is_null($this->server)) {
|
||||||
|
return redirect()->route('server.index');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.proxy.dynamic-configurations');
|
||||||
|
}
|
||||||
|
}
|
||||||
74
app/Livewire/Server/Proxy/NewDynamicConfiguration.php
Normal file
74
app/Livewire/Server/Proxy/NewDynamicConfiguration.php
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Server\Proxy;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Routing\Route;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class NewDynamicConfiguration extends Component
|
||||||
|
{
|
||||||
|
public string $fileName = '';
|
||||||
|
public string $value = '';
|
||||||
|
public bool $newFile = false;
|
||||||
|
public Server $server;
|
||||||
|
public $server_id;
|
||||||
|
public $parameters = [];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
if ($this->fileName !== '') {
|
||||||
|
$this->fileName = str_replace('|', '.', $this->fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function addDynamicConfiguration()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate([
|
||||||
|
'fileName' => 'required',
|
||||||
|
'value' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (data_get($this->parameters, 'server_uuid')) {
|
||||||
|
$this->server = Server::ownedByCurrentTeam()->whereUuid(data_get($this->parameters, 'server_uuid'))->first();
|
||||||
|
}
|
||||||
|
if (!is_null($this->server_id)) {
|
||||||
|
$this->server = Server::ownedByCurrentTeam()->whereId($this->server_id)->first();
|
||||||
|
}
|
||||||
|
if (is_null($this->server)) {
|
||||||
|
return redirect()->route('server.index');
|
||||||
|
}
|
||||||
|
if (!str($this->fileName)->endsWith('.yaml') && !str($this->fileName)->endsWith('.yml')) {
|
||||||
|
$this->fileName = "{$this->fileName}.yaml";
|
||||||
|
}
|
||||||
|
if ($this->fileName === 'coolify.yaml') {
|
||||||
|
$this->dispatch('error', 'File name is reserved.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$proxy_path = get_proxy_path();
|
||||||
|
$file = "{$proxy_path}/dynamic/{$this->fileName}";
|
||||||
|
if ($this->newFile) {
|
||||||
|
$exists = instant_remote_process(["test -f $file && echo 1 || echo 0"], $this->server);
|
||||||
|
if ($exists == 1) {
|
||||||
|
$this->dispatch('error', 'File already exists');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$yaml = Yaml::parse($this->value);
|
||||||
|
$yaml = Yaml::dump($yaml, 10, 2);
|
||||||
|
$this->value = $yaml;
|
||||||
|
$base64_value = base64_encode($this->value);
|
||||||
|
instant_remote_process(["echo '{$base64_value}' | base64 -d > {$file}"], $this->server);
|
||||||
|
$this->dispatch('loadDynamicConfigurations');
|
||||||
|
$this->dispatch('dynamic-configuration-added');
|
||||||
|
$this->dispatch('success', 'Dynamic configuration saved.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.proxy.new-dynamic-configuration');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,7 +55,6 @@ class Resources extends Component
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
$this->loadUnmanagedContainers();
|
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Server;
|
namespace App\Livewire\Server;
|
||||||
|
|
||||||
|
use App\Actions\Proxy\CheckProxy;
|
||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -21,7 +22,15 @@ class ValidateAndInstall extends Component
|
|||||||
public $error = null;
|
public $error = null;
|
||||||
public bool $ask = false;
|
public bool $ask = false;
|
||||||
|
|
||||||
protected $listeners = ['validateServer' => 'init', 'validateDockerEngine', 'validateServerNow' => 'validateServer'];
|
protected $listeners = [
|
||||||
|
'init',
|
||||||
|
'validateConnection',
|
||||||
|
'validateOS',
|
||||||
|
'validateDockerEngine',
|
||||||
|
'validateDockerVersion',
|
||||||
|
'startProxy',
|
||||||
|
'refresh' => '$refresh',
|
||||||
|
];
|
||||||
|
|
||||||
public function init(bool $install = true)
|
public function init(bool $install = true)
|
||||||
{
|
{
|
||||||
@@ -35,31 +44,29 @@ class ValidateAndInstall extends Component
|
|||||||
$this->error = null;
|
$this->error = null;
|
||||||
$this->number_of_tries = 0;
|
$this->number_of_tries = 0;
|
||||||
if (!$this->ask) {
|
if (!$this->ask) {
|
||||||
$this->dispatch('validateServerNow');
|
$this->dispatch('validateConnection');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function startValidatingAfterAsking() {
|
public function startValidatingAfterAsking()
|
||||||
|
{
|
||||||
$this->ask = false;
|
$this->ask = false;
|
||||||
$this->init();
|
$this->init();
|
||||||
}
|
}
|
||||||
public function validateServer()
|
public function startProxy()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validateConnection();
|
$shouldStart = CheckProxy::run($this->server);
|
||||||
$this->validateOS();
|
if ($shouldStart) {
|
||||||
$this->validateDockerEngine();
|
$proxy = StartProxy::run($this->server, false);
|
||||||
|
if ($proxy === 'OK') {
|
||||||
if ($this->server->isSwarm()) {
|
$this->proxy_started = true;
|
||||||
$swarmInstalled = $this->server->validateDockerSwarm();
|
} else {
|
||||||
if ($swarmInstalled) {
|
throw new \Exception("Proxy could not be started.");
|
||||||
$this->dispatch('success', 'Docker Swarm is initiated.');
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$proxy = StartProxy::run($this->server);
|
$this->proxy_started = true;
|
||||||
if ($proxy) {
|
|
||||||
$this->proxy_started = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -71,6 +78,7 @@ class ValidateAndInstall extends Component
|
|||||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh">documentation</a> for further help.';
|
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh">documentation</a> for further help.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$this->dispatch('validateOS');
|
||||||
}
|
}
|
||||||
public function validateOS()
|
public function validateOS()
|
||||||
{
|
{
|
||||||
@@ -79,6 +87,7 @@ class ValidateAndInstall extends Component
|
|||||||
$this->error = 'Server OS type is not supported. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
$this->error = 'Server OS type is not supported. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$this->dispatch('validateDockerEngine');
|
||||||
}
|
}
|
||||||
public function validateDockerEngine()
|
public function validateDockerEngine()
|
||||||
{
|
{
|
||||||
@@ -90,29 +99,39 @@ class ValidateAndInstall extends Component
|
|||||||
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
$activity = $this->server->installDocker();
|
if ($this->number_of_tries == 0) {
|
||||||
$this->number_of_tries++;
|
$activity = $this->server->installDocker();
|
||||||
$this->dispatch('newActivityMonitor', $activity->id, 'validateDockerEngine');
|
$this->number_of_tries++;
|
||||||
|
$this->dispatch('newActivityMonitor', $activity->id, 'init');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$this->validateDockerVersion();
|
|
||||||
}
|
}
|
||||||
|
$this->dispatch('validateDockerVersion');
|
||||||
}
|
}
|
||||||
public function validateDockerVersion()
|
public function validateDockerVersion()
|
||||||
{
|
{
|
||||||
$this->docker_version = $this->server->validateDockerEngineVersion();
|
if ($this->server->isSwarm()) {
|
||||||
if ($this->docker_version) {
|
$swarmInstalled = $this->server->validateDockerSwarm();
|
||||||
$this->dispatch('serverInstalled');
|
if ($swarmInstalled) {
|
||||||
$this->dispatch('success', 'Server validated successfully.');
|
$this->dispatch('success', 'Docker Swarm is initiated.');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
$this->docker_version = $this->server->validateDockerEngineVersion();
|
||||||
return;
|
if ($this->docker_version) {
|
||||||
|
$this->dispatch('serverInstalled');
|
||||||
|
$this->dispatch('success', 'Server validated.');
|
||||||
|
} else {
|
||||||
|
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->dispatch('startProxy');
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -81,6 +81,6 @@ class Backup extends Component
|
|||||||
}
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->dispatch('success', 'Backup updated successfully.');
|
$this->dispatch('success', 'Backup updated.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class Email extends Component
|
|||||||
'settings.smtp_from_name' => 'required',
|
'settings.smtp_from_name' => 'required',
|
||||||
]);
|
]);
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->dispatch('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ class Email extends Component
|
|||||||
'settings.resend_api_key' => 'required'
|
'settings.resend_api_key' => 'required'
|
||||||
]);
|
]);
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->dispatch('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->settings->resend_enabled = false;
|
$this->settings->resend_enabled = false;
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -98,7 +98,7 @@ class Email extends Component
|
|||||||
'settings.smtp_timeout' => 'nullable',
|
'settings.smtp_timeout' => 'nullable',
|
||||||
]);
|
]);
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->dispatch('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Source\Github;
|
namespace App\Livewire\Source\Github;
|
||||||
|
|
||||||
|
use App\Jobs\GithubAppPermissionJob;
|
||||||
use App\Models\GithubApp;
|
use App\Models\GithubApp;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Change extends Component
|
class Change extends Component
|
||||||
@@ -15,6 +17,7 @@ class Change extends Component
|
|||||||
|
|
||||||
public ?bool $default_permissions = true;
|
public ?bool $default_permissions = true;
|
||||||
public ?bool $preview_deployment_permissions = true;
|
public ?bool $preview_deployment_permissions = true;
|
||||||
|
public ?bool $administration = false;
|
||||||
|
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public ?GithubApp $github_app;
|
public ?GithubApp $github_app;
|
||||||
@@ -34,8 +37,52 @@ class Change extends Component
|
|||||||
'github_app.client_secret' => 'required|string',
|
'github_app.client_secret' => 'required|string',
|
||||||
'github_app.webhook_secret' => 'required|string',
|
'github_app.webhook_secret' => 'required|string',
|
||||||
'github_app.is_system_wide' => 'required|bool',
|
'github_app.is_system_wide' => 'required|bool',
|
||||||
|
'github_app.contents' => 'nullable|string',
|
||||||
|
'github_app.metadata' => 'nullable|string',
|
||||||
|
'github_app.pull_requests' => 'nullable|string',
|
||||||
|
'github_app.administration' => 'nullable|string',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function checkPermissions()
|
||||||
|
{
|
||||||
|
GithubAppPermissionJob::dispatchSync($this->github_app);
|
||||||
|
$this->github_app->refresh()->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||||
|
$this->dispatch('success', 'Github App permissions updated.');
|
||||||
|
}
|
||||||
|
// public function check()
|
||||||
|
// {
|
||||||
|
|
||||||
|
// Need administration:read:write permission
|
||||||
|
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-a-repository
|
||||||
|
|
||||||
|
|
||||||
|
// $github_access_token = generate_github_installation_token($this->github_app);
|
||||||
|
// $repositories = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/installation/repositories?per_page=100");
|
||||||
|
// $runners_by_repository = collect([]);
|
||||||
|
// $repositories = $repositories->json()['repositories'];
|
||||||
|
// foreach ($repositories as $repository) {
|
||||||
|
// $runners_downloads = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/repos/{$repository['full_name']}/actions/runners/downloads");
|
||||||
|
// $runners = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/repos/{$repository['full_name']}/actions/runners");
|
||||||
|
// $token = Http::withHeaders([
|
||||||
|
// 'Authorization' => "Bearer $github_access_token",
|
||||||
|
// 'Accept' => 'application/vnd.github+json'
|
||||||
|
// ])->withBody(null)->post("{$this->github_app->api_url}/repos/{$repository['full_name']}/actions/runners/registration-token");
|
||||||
|
// $token = $token->json();
|
||||||
|
// $remove_token = Http::withHeaders([
|
||||||
|
// 'Authorization' => "Bearer $github_access_token",
|
||||||
|
// 'Accept' => 'application/vnd.github+json'
|
||||||
|
// ])->withBody(null)->post("{$this->github_app->api_url}/repos/{$repository['full_name']}/actions/runners/remove-token");
|
||||||
|
// $remove_token = $remove_token->json();
|
||||||
|
// $runners_by_repository->put($repository['full_name'], [
|
||||||
|
// 'token' => $token,
|
||||||
|
// 'remove_token' => $remove_token,
|
||||||
|
// 'runners' => $runners->json(),
|
||||||
|
// 'runners_downloads' => $runners_downloads->json()
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ray($runners_by_repository);
|
||||||
|
// }
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$github_app_uuid = request()->github_app_uuid;
|
$github_app_uuid = request()->github_app_uuid;
|
||||||
@@ -103,7 +150,7 @@ class Change extends Component
|
|||||||
'github_app.is_system_wide' => 'required|bool',
|
'github_app.is_system_wide' => 'required|bool',
|
||||||
]);
|
]);
|
||||||
$this->github_app->save();
|
$this->github_app->save();
|
||||||
$this->dispatch('success', 'Github App updated successfully.');
|
$this->dispatch('success', 'Github App updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -111,6 +158,13 @@ class Change extends Component
|
|||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
|
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||||
|
$this->github_app->save();
|
||||||
|
$this->dispatch('success', 'Github App updated.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
|
|||||||
@@ -2,11 +2,18 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Subscription;
|
namespace App\Livewire\Subscription;
|
||||||
|
|
||||||
|
use App\Models\Team;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Actions extends Component
|
class Actions extends Component
|
||||||
{
|
{
|
||||||
|
public $server_limits = 0;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->server_limits = Team::serverLimit();
|
||||||
|
}
|
||||||
public function cancel()
|
public function cancel()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -69,7 +76,8 @@ class Actions extends Component
|
|||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function stripeCustomerPortal() {
|
public function stripeCustomerPortal()
|
||||||
|
{
|
||||||
$session = getStripeCustomerPortalSession(currentTeam());
|
$session = getStripeCustomerPortalSession(currentTeam());
|
||||||
redirect($session->url);
|
redirect($session->url);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,19 @@ class Index extends Component
|
|||||||
{
|
{
|
||||||
public InstanceSettings $settings;
|
public InstanceSettings $settings;
|
||||||
public bool $alreadySubscribed = false;
|
public bool $alreadySubscribed = false;
|
||||||
public function mount() {
|
public function mount()
|
||||||
|
{
|
||||||
if (!isCloud()) {
|
if (!isCloud()) {
|
||||||
return redirect(RouteServiceProvider::HOME);
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
|
if (data_get(currentTeam(), 'subscription')) {
|
||||||
|
return redirect()->route('subscription.show');
|
||||||
|
}
|
||||||
$this->settings = InstanceSettings::get();
|
$this->settings = InstanceSettings::get();
|
||||||
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
||||||
}
|
}
|
||||||
public function stripeCustomerPortal() {
|
public function stripeCustomerPortal()
|
||||||
|
{
|
||||||
$session = getStripeCustomerPortalSession(currentTeam());
|
$session = getStripeCustomerPortalSession(currentTeam());
|
||||||
if (is_null($session)) {
|
if (is_null($session)) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ use Stripe\Checkout\Session;
|
|||||||
class PricingPlans extends Component
|
class PricingPlans extends Component
|
||||||
{
|
{
|
||||||
public bool $isTrial = false;
|
public bool $isTrial = false;
|
||||||
public function mount() {
|
public function mount()
|
||||||
$this->isTrial = !data_get(currentTeam(),'subscription.stripe_trial_already_ended');
|
{
|
||||||
|
$this->isTrial = !data_get(currentTeam(), 'subscription.stripe_trial_already_ended');
|
||||||
if (config('constants.limits.trial_period') == 0) {
|
if (config('constants.limits.trial_period') == 0) {
|
||||||
$this->isTrial = false;
|
$this->isTrial = false;
|
||||||
}
|
}
|
||||||
@@ -26,15 +27,15 @@ class PricingPlans extends Component
|
|||||||
case 'basic-yearly':
|
case 'basic-yearly':
|
||||||
$priceId = config('subscription.stripe_price_id_basic_yearly');
|
$priceId = config('subscription.stripe_price_id_basic_yearly');
|
||||||
break;
|
break;
|
||||||
case 'ultimate-monthly':
|
|
||||||
$priceId = config('subscription.stripe_price_id_ultimate_monthly');
|
|
||||||
break;
|
|
||||||
case 'pro-monthly':
|
case 'pro-monthly':
|
||||||
$priceId = config('subscription.stripe_price_id_pro_monthly');
|
$priceId = config('subscription.stripe_price_id_pro_monthly');
|
||||||
break;
|
break;
|
||||||
case 'pro-yearly':
|
case 'pro-yearly':
|
||||||
$priceId = config('subscription.stripe_price_id_pro_yearly');
|
$priceId = config('subscription.stripe_price_id_pro_yearly');
|
||||||
break;
|
break;
|
||||||
|
case 'ultimate-monthly':
|
||||||
|
$priceId = config('subscription.stripe_price_id_ultimate_monthly');
|
||||||
|
break;
|
||||||
case 'ultimate-yearly':
|
case 'ultimate-yearly':
|
||||||
$priceId = config('subscription.stripe_price_id_ultimate_yearly');
|
$priceId = config('subscription.stripe_price_id_ultimate_yearly');
|
||||||
break;
|
break;
|
||||||
@@ -64,18 +65,25 @@ class PricingPlans extends Component
|
|||||||
'success_url' => route('dashboard', ['success' => true]),
|
'success_url' => route('dashboard', ['success' => true]),
|
||||||
'cancel_url' => route('subscription.index', ['cancelled' => true]),
|
'cancel_url' => route('subscription.index', ['cancelled' => true]),
|
||||||
];
|
];
|
||||||
|
if (str($type)->contains('ultimate')) {
|
||||||
if (!data_get($team,'subscription.stripe_trial_already_ended')) {
|
$payload['line_items'][0]['adjustable_quantity'] = [
|
||||||
if (config('constants.limits.trial_period') > 0) {
|
'enabled' => true,
|
||||||
$payload['subscription_data'] = [
|
'minimum' => 10,
|
||||||
'trial_period_days' => config('constants.limits.trial_period'),
|
|
||||||
'trial_settings' => [
|
|
||||||
'end_behavior' => [
|
|
||||||
'missing_payment_method' => 'cancel',
|
|
||||||
]
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
$payload['line_items'][0]['quantity'] = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!data_get($team, 'subscription.stripe_trial_already_ended')) {
|
||||||
|
if (config('constants.limits.trial_period') > 0) {
|
||||||
|
$payload['subscription_data'] = [
|
||||||
|
'trial_period_days' => config('constants.limits.trial_period'),
|
||||||
|
'trial_settings' => [
|
||||||
|
'end_behavior' => [
|
||||||
|
'missing_payment_method' => 'cancel',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
$payload['payment_method_collection'] = 'if_required';
|
$payload['payment_method_collection'] = 'if_required';
|
||||||
}
|
}
|
||||||
$customer = currentTeam()->subscription?->stripe_customer_id ?? null;
|
$customer = currentTeam()->subscription?->stripe_customer_id ?? null;
|
||||||
|
|||||||
22
app/Livewire/Subscription/Show.php
Normal file
22
app/Livewire/Subscription/Show.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Subscription;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Show extends Component
|
||||||
|
{
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
if (!isCloud()) {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
if (!data_get(currentTeam(), 'subscription')) {
|
||||||
|
return redirect()->route('subscription.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.subscription.show');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ class Index extends Component
|
|||||||
try {
|
try {
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->dispatch('success', 'Team updated successfully.');
|
$this->dispatch('success', 'Team updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class InviteLink extends Component
|
|||||||
]);
|
]);
|
||||||
$mail->subject('You have been invited to ' . currentTeam()->name . ' on ' . config('app.name') . '.');
|
$mail->subject('You have been invited to ' . currentTeam()->name . ' on ' . config('app.name') . '.');
|
||||||
send_user_an_email($mail, $this->email);
|
send_user_an_email($mail, $this->email);
|
||||||
$this->dispatch('success', 'Invitation sent via email successfully.');
|
$this->dispatch('success', 'Invitation sent via email.');
|
||||||
$this->dispatch('refreshInvitations');
|
$this->dispatch('refreshInvitations');
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ class Member extends Component
|
|||||||
$this->dispatch('reloadWindow');
|
$this->dispatch('reloadWindow');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function makeOwner()
|
||||||
|
{
|
||||||
|
$this->member->teams()->updateExistingPivot(currentTeam()->id, ['role' => 'owner']);
|
||||||
|
$this->dispatch('reloadWindow');
|
||||||
|
}
|
||||||
public function makeReadonly()
|
public function makeReadonly()
|
||||||
{
|
{
|
||||||
$this->member->teams()->updateExistingPivot(currentTeam()->id, ['role' => 'member']);
|
$this->member->teams()->updateExistingPivot(currentTeam()->id, ['role' => 'member']);
|
||||||
@@ -26,7 +31,7 @@ class Member extends Component
|
|||||||
{
|
{
|
||||||
$this->member->teams()->detach(currentTeam());
|
$this->member->teams()->detach(currentTeam());
|
||||||
Cache::forget("team:{$this->member->id}");
|
Cache::forget("team:{$this->member->id}");
|
||||||
Cache::remember('team:' . $this->member->id, 3600, function() {
|
Cache::remember('team:' . $this->member->id, 3600, function () {
|
||||||
return $this->member->teams()->first();
|
return $this->member->teams()->first();
|
||||||
});
|
});
|
||||||
$this->dispatch('reloadWindow');
|
$this->dispatch('reloadWindow');
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ use App\Enums\ApplicationDeploymentStatus;
|
|||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
class Application extends BaseModel
|
class Application extends BaseModel
|
||||||
@@ -66,6 +66,13 @@ class Application extends BaseModel
|
|||||||
return $this->belongsToMany(StandaloneDocker::class, 'additional_destinations')
|
return $this->belongsToMany(StandaloneDocker::class, 'additional_destinations')
|
||||||
->withPivot('server_id', 'status');
|
->withPivot('server_id', 'status');
|
||||||
}
|
}
|
||||||
|
public function is_public_repository(): bool
|
||||||
|
{
|
||||||
|
if (data_get($this, 'source.is_public')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
public function is_github_based(): bool
|
public function is_github_based(): bool
|
||||||
{
|
{
|
||||||
if (data_get($this, 'source')) {
|
if (data_get($this, 'source')) {
|
||||||
@@ -73,6 +80,18 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public function isForceHttpsEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'settings.is_force_https_enabled', false);
|
||||||
|
}
|
||||||
|
public function isStripprefixEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'settings.is_stripprefix_enabled', true);
|
||||||
|
}
|
||||||
|
public function isGzipEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'settings.is_gzip_enabled', true);
|
||||||
|
}
|
||||||
public function link()
|
public function link()
|
||||||
{
|
{
|
||||||
if (data_get($this, 'environment.project.uuid')) {
|
if (data_get($this, 'environment.project.uuid')) {
|
||||||
@@ -274,15 +293,11 @@ class Application extends BaseModel
|
|||||||
foreach ($additional_servers_status as $status) {
|
foreach ($additional_servers_status as $status) {
|
||||||
$server_status = str($status)->before(':')->value();
|
$server_status = str($status)->before(':')->value();
|
||||||
$server_health = str($status)->after(':')->value() ?? 'unhealthy';
|
$server_health = str($status)->after(':')->value() ?? 'unhealthy';
|
||||||
if ($server_status !== 'running') {
|
if ($main_server_status !== $server_status) {
|
||||||
if ($main_server_status !== $server_status) {
|
$complex_status = 'degraded';
|
||||||
$complex_status = 'degraded';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ($server_health !== 'healthy') {
|
if ($main_server_health !== $server_health) {
|
||||||
if ($main_server_health !== $server_health) {
|
$complex_health = 'unhealthy';
|
||||||
$complex_health = 'unhealthy';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "$complex_status:$complex_health";
|
return "$complex_status:$complex_health";
|
||||||
@@ -400,7 +415,10 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public function get_last_days_deployments()
|
||||||
|
{
|
||||||
|
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('created_at', '>=', now()->subDays(7))->orderBy('created_at', 'desc')->get();
|
||||||
|
}
|
||||||
public function deployments(int $skip = 0, int $take = 10)
|
public function deployments(int $skip = 0, int $take = 10)
|
||||||
{
|
{
|
||||||
$deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->orderBy('created_at', 'desc');
|
$deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->orderBy('created_at', 'desc');
|
||||||
@@ -471,6 +489,10 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public function workdir()
|
||||||
|
{
|
||||||
|
return application_configuration_dir() . "/{$this->uuid}";
|
||||||
|
}
|
||||||
public function isLogDrainEnabled()
|
public function isLogDrainEnabled()
|
||||||
{
|
{
|
||||||
return data_get($this, 'settings.is_log_drain_enabled', false);
|
return data_get($this, 'settings.is_log_drain_enabled', false);
|
||||||
@@ -705,6 +727,64 @@ class Application extends BaseModel
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function parseRawCompose()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$yaml = Yaml::parse($this->docker_compose_raw);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new \Exception($e->getMessage());
|
||||||
|
}
|
||||||
|
$services = data_get($yaml, 'services');
|
||||||
|
$commands = collect([]);
|
||||||
|
$services = collect($services)->map(function ($service) use ($commands) {
|
||||||
|
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
||||||
|
if ($serviceVolumes->count() > 0) {
|
||||||
|
foreach ($serviceVolumes as $volume) {
|
||||||
|
$workdir = $this->workdir();
|
||||||
|
$type = null;
|
||||||
|
$source = null;
|
||||||
|
if (is_string($volume)) {
|
||||||
|
$source = Str::of($volume)->before(':');
|
||||||
|
if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) {
|
||||||
|
$type = Str::of('bind');
|
||||||
|
}
|
||||||
|
} else if (is_array($volume)) {
|
||||||
|
$type = data_get_str($volume, 'type');
|
||||||
|
$source = data_get_str($volume, 'source');
|
||||||
|
}
|
||||||
|
if ($type->value() === 'bind') {
|
||||||
|
if ($source->value() === "/var/run/docker.sock") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($source->startsWith('.')) {
|
||||||
|
$source = $source->after('.');
|
||||||
|
$source = $workdir . $source;
|
||||||
|
}
|
||||||
|
$commands->push("mkdir -p $source > /dev/null 2>&1 || true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$labels = collect(data_get($service, 'labels', []));
|
||||||
|
if (!$labels->contains('coolify.managed')) {
|
||||||
|
$labels->push('coolify.managed=true');
|
||||||
|
}
|
||||||
|
if (!$labels->contains('coolify.applicationId')) {
|
||||||
|
$labels->push('coolify.applicationId=' . $this->id);
|
||||||
|
}
|
||||||
|
if (!$labels->contains('coolify.type')) {
|
||||||
|
$labels->push('coolify.type=application');
|
||||||
|
}
|
||||||
|
data_set($service, 'labels', $labels->toArray());
|
||||||
|
return $service;
|
||||||
|
});
|
||||||
|
data_set($yaml, 'services', $services->toArray());
|
||||||
|
$this->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||||
|
|
||||||
|
instant_remote_process($commands, $this->destination->server, false);
|
||||||
|
}
|
||||||
function parseCompose(int $pull_request_id = 0)
|
function parseCompose(int $pull_request_id = 0)
|
||||||
{
|
{
|
||||||
if ($this->docker_compose_raw) {
|
if ($this->docker_compose_raw) {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class LocalFileVolume extends BaseModel
|
class LocalFileVolume extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -57,6 +56,7 @@ class LocalFileVolume extends BaseModel
|
|||||||
if ($content) {
|
if ($content) {
|
||||||
$content = base64_encode($content);
|
$content = base64_encode($content);
|
||||||
$commands->push("echo '$content' | base64 -d > $path");
|
$commands->push("echo '$content' | base64 -d > $path");
|
||||||
|
$commands->push("chmod +x $path");
|
||||||
}
|
}
|
||||||
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
|
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
|
||||||
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
|
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
|
||||||
|
|||||||
@@ -30,4 +30,8 @@ class ScheduledDatabaseBackup extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(S3Storage::class, 's3_storage_id');
|
return $this->belongsTo(S3Storage::class, 's3_storage_id');
|
||||||
}
|
}
|
||||||
|
public function get_last_days_backup_status($days = 7)
|
||||||
|
{
|
||||||
|
return $this->hasMany(ScheduledDatabaseBackupExecution::class)->where('created_at', '>=', now()->subDays($days))->get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use App\Notifications\Server\Unreachable;
|
|||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
||||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -69,7 +70,7 @@ class Server extends BaseModel
|
|||||||
|
|
||||||
static public function isUsable()
|
static public function isUsable()
|
||||||
{
|
{
|
||||||
return Server::ownedByCurrentTeam()->whereRelation('settings', 'is_reachable', true)->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_swarm_worker', false)->whereRelation('settings', 'is_build_server', false);
|
return Server::ownedByCurrentTeam()->whereRelation('settings', 'is_reachable', true)->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_swarm_worker', false)->whereRelation('settings', 'is_build_server', false)->whereRelation('settings', 'force_disabled', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function destinationsByServer(string $server_id)
|
static public function destinationsByServer(string $server_id)
|
||||||
@@ -146,11 +147,34 @@ class Server extends BaseModel
|
|||||||
public function skipServer()
|
public function skipServer()
|
||||||
{
|
{
|
||||||
if ($this->ip === '1.2.3.4') {
|
if ($this->ip === '1.2.3.4') {
|
||||||
ray('skipping 1.2.3.4');
|
// ray('skipping 1.2.3.4');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($this->settings->force_disabled === true) {
|
||||||
|
// ray('force_disabled');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public function isForceDisabled()
|
||||||
|
{
|
||||||
|
return $this->settings->force_disabled;
|
||||||
|
}
|
||||||
|
public function forceEnableServer()
|
||||||
|
{
|
||||||
|
$this->settings->update([
|
||||||
|
'force_disabled' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function forceDisableServer()
|
||||||
|
{
|
||||||
|
$this->settings->update([
|
||||||
|
'force_disabled' => true,
|
||||||
|
]);
|
||||||
|
$sshKeyFileLocation = "id.root@{$this->uuid}";
|
||||||
|
Storage::disk('ssh-keys')->delete($sshKeyFileLocation);
|
||||||
|
Storage::disk('ssh-mux')->delete($this->muxFilename());
|
||||||
|
}
|
||||||
public function isServerReady(int $tries = 3)
|
public function isServerReady(int $tries = 3)
|
||||||
{
|
{
|
||||||
if ($this->skipServer()) {
|
if ($this->skipServer()) {
|
||||||
@@ -239,17 +263,21 @@ class Server extends BaseModel
|
|||||||
}
|
}
|
||||||
public function loadUnmanagedContainers()
|
public function loadUnmanagedContainers()
|
||||||
{
|
{
|
||||||
$containers = instant_remote_process(["docker ps -a --format '{{json .}}' "], $this);
|
if ($this->isFunctional()) {
|
||||||
$containers = format_docker_command_output_to_json($containers);
|
$containers = instant_remote_process(["docker ps -a --format '{{json .}}' "], $this);
|
||||||
$containers = $containers->map(function ($container) {
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
$labels = data_get($container, 'Labels');
|
$containers = $containers->map(function ($container) {
|
||||||
if (!str($labels)->contains("coolify.managed")) {
|
$labels = data_get($container, 'Labels');
|
||||||
return $container;
|
if (!str($labels)->contains("coolify.managed")) {
|
||||||
}
|
return $container;
|
||||||
return null;
|
}
|
||||||
});
|
return null;
|
||||||
$containers = $containers->filter();
|
});
|
||||||
return collect($containers);
|
$containers = $containers->filter();
|
||||||
|
return collect($containers);
|
||||||
|
} else {
|
||||||
|
return collect([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public function hasDefinedResources()
|
public function hasDefinedResources()
|
||||||
{
|
{
|
||||||
@@ -337,8 +365,9 @@ class Server extends BaseModel
|
|||||||
{
|
{
|
||||||
$standalone_docker = $this->hasMany(StandaloneDocker::class)->get();
|
$standalone_docker = $this->hasMany(StandaloneDocker::class)->get();
|
||||||
$swarm_docker = $this->hasMany(SwarmDocker::class)->get();
|
$swarm_docker = $this->hasMany(SwarmDocker::class)->get();
|
||||||
$asd = $this->belongsToMany(StandaloneDocker::class, 'additional_destinations')->withPivot('server_id')->get();
|
// $additional_dockers = $this->belongsToMany(StandaloneDocker::class, 'additional_destinations')->withPivot('server_id')->get();
|
||||||
return $standalone_docker->concat($swarm_docker)->concat($asd);
|
// return $standalone_docker->concat($swarm_docker)->concat($additional_dockers);
|
||||||
|
return $standalone_docker->concat($swarm_docker);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function standaloneDockers()
|
public function standaloneDockers()
|
||||||
@@ -374,7 +403,7 @@ class Server extends BaseModel
|
|||||||
}
|
}
|
||||||
public function isFunctional()
|
public function isFunctional()
|
||||||
{
|
{
|
||||||
return $this->settings->is_reachable && $this->settings->is_usable;
|
return $this->settings->is_reachable && $this->settings->is_usable && !$this->settings->force_disabled;
|
||||||
}
|
}
|
||||||
public function isLogDrainEnabled()
|
public function isLogDrainEnabled()
|
||||||
{
|
{
|
||||||
@@ -398,10 +427,10 @@ class Server extends BaseModel
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if ($supported->count() === 1) {
|
if ($supported->count() === 1) {
|
||||||
ray('supported');
|
// ray('supported');
|
||||||
return str($supported->first());
|
return str($supported->first());
|
||||||
} else {
|
} else {
|
||||||
ray('not supported');
|
// ray('not supported');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -468,6 +497,16 @@ class Server extends BaseModel
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
instant_remote_process(["docker version"], $this);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->settings->is_usable = false;
|
||||||
|
$this->settings->save();
|
||||||
|
if ($throwError) {
|
||||||
|
throw new \Exception('Server is not usable. Docker Engine is not running.');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$this->settings->is_usable = true;
|
$this->settings->is_usable = true;
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->validateCoolifyNetwork(isSwarm: false, isBuildServer: $this->settings->is_build_server);
|
$this->validateCoolifyNetwork(isSwarm: false, isBuildServer: $this->settings->is_build_server);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class Service extends BaseModel
|
class Service extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -28,47 +27,73 @@ class Service extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphToMany(Tag::class, 'taggable');
|
return $this->morphToMany(Tag::class, 'taggable');
|
||||||
}
|
}
|
||||||
public function status() {
|
public function status()
|
||||||
$foundRunning = false;
|
{
|
||||||
$isDegraded = false;
|
|
||||||
$foundRestaring = false;
|
|
||||||
$applications = $this->applications;
|
$applications = $this->applications;
|
||||||
$databases = $this->databases;
|
$databases = $this->databases;
|
||||||
|
|
||||||
|
$complexStatus = null;
|
||||||
|
$complexHealth = null;
|
||||||
|
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
if ($application->exclude_from_status) {
|
if ($application->exclude_from_status) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (Str::of($application->status)->startsWith('running')) {
|
$status = str($application->status)->before('(')->trim();
|
||||||
$foundRunning = true;
|
$health = str($application->status)->between('(', ')')->trim();
|
||||||
} else if (Str::of($application->status)->startsWith('restarting')) {
|
if ($complexStatus === 'degraded') {
|
||||||
$foundRestaring = true;
|
continue;
|
||||||
|
}
|
||||||
|
if ($status->startsWith('running')) {
|
||||||
|
if ($complexStatus === 'exited') {
|
||||||
|
$complexStatus = 'degraded';
|
||||||
|
} else {
|
||||||
|
$complexStatus = 'running';
|
||||||
|
}
|
||||||
|
} else if ($status->startsWith('restarting')) {
|
||||||
|
$complexStatus = 'degraded';
|
||||||
|
} else if ($status->startsWith('exited')) {
|
||||||
|
$complexStatus = 'exited';
|
||||||
|
}
|
||||||
|
if ($health->value() === 'healthy') {
|
||||||
|
if ($complexHealth === 'unhealthy') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$complexHealth = 'healthy';
|
||||||
} else {
|
} else {
|
||||||
$isDegraded = true;
|
$complexHealth = 'unhealthy';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($databases as $database) {
|
foreach ($databases as $database) {
|
||||||
if ($database->exclude_from_status) {
|
if ($database->exclude_from_status) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (Str::of($database->status)->startsWith('running')) {
|
$status = str($database->status)->before('(')->trim();
|
||||||
$foundRunning = true;
|
$health = str($database->status)->between('(', ')')->trim();
|
||||||
} else if (Str::of($database->status)->startsWith('restarting')) {
|
if ($complexStatus === 'degraded') {
|
||||||
$foundRestaring = true;
|
continue;
|
||||||
|
}
|
||||||
|
if ($status->startsWith('running')) {
|
||||||
|
if ($complexStatus === 'exited') {
|
||||||
|
$complexStatus = 'degraded';
|
||||||
|
} else {
|
||||||
|
$complexStatus = 'running';
|
||||||
|
}
|
||||||
|
} else if ($status->startsWith('restarting')) {
|
||||||
|
$complexStatus = 'degraded';
|
||||||
|
} else if ($status->startsWith('exited')) {
|
||||||
|
$complexStatus = 'exited';
|
||||||
|
}
|
||||||
|
if ($health->value() === 'healthy') {
|
||||||
|
if ($complexHealth === 'unhealthy') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$complexHealth = 'healthy';
|
||||||
} else {
|
} else {
|
||||||
$isDegraded = true;
|
$complexHealth = 'unhealthy';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($foundRestaring) {
|
return "{$complexStatus}:{$complexHealth}";
|
||||||
return 'degraded';
|
|
||||||
}
|
|
||||||
if ($foundRunning && !$isDegraded) {
|
|
||||||
return 'running';
|
|
||||||
} else if ($foundRunning && $isDegraded) {
|
|
||||||
return 'degraded';
|
|
||||||
} else if (!$foundRunning && !$isDegraded) {
|
|
||||||
return 'exited';
|
|
||||||
}
|
|
||||||
return 'exited';
|
|
||||||
}
|
}
|
||||||
public function extraFields()
|
public function extraFields()
|
||||||
{
|
{
|
||||||
@@ -77,6 +102,56 @@ class Service extends BaseModel
|
|||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
$image = str($application->image)->before(':')->value();
|
$image = str($application->image)->before(':')->value();
|
||||||
switch ($image) {
|
switch ($image) {
|
||||||
|
case str($image)?->contains('directus'):
|
||||||
|
$data = collect([]);
|
||||||
|
$admin_email = $this->environment_variables()->where('key', 'ADMIN_EMAIL')->first();
|
||||||
|
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ADMIN')->first();
|
||||||
|
|
||||||
|
if ($admin_email) {
|
||||||
|
$data = $data->merge([
|
||||||
|
'Admin Email' => [
|
||||||
|
'key' => data_get($admin_email, 'key'),
|
||||||
|
'value' => data_get($admin_email, 'value'),
|
||||||
|
'rules' => 'required|email',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($admin_password) {
|
||||||
|
$data = $data->merge([
|
||||||
|
'Admin Password' => [
|
||||||
|
'key' => data_get($admin_password, 'key'),
|
||||||
|
'value' => data_get($admin_password, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$fields->put('Directus', $data);
|
||||||
|
break;
|
||||||
|
case str($image)?->contains('kong'):
|
||||||
|
$data = collect([]);
|
||||||
|
$dashboard_user = $this->environment_variables()->where('key', 'SERVICE_USER_ADMIN')->first();
|
||||||
|
$dashboard_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ADMIN')->first();
|
||||||
|
if ($dashboard_user) {
|
||||||
|
$data = $data->merge([
|
||||||
|
'Dashboard User' => [
|
||||||
|
'key' => data_get($dashboard_user, 'key'),
|
||||||
|
'value' => data_get($dashboard_user, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($dashboard_password) {
|
||||||
|
$data = $data->merge([
|
||||||
|
'Dashboard Password' => [
|
||||||
|
'key' => data_get($dashboard_password, 'key'),
|
||||||
|
'value' => data_get($dashboard_password, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$fields->put('Supabase', $data->toArray());
|
||||||
case str($image)?->contains('minio'):
|
case str($image)?->contains('minio'):
|
||||||
$data = collect([]);
|
$data = collect([]);
|
||||||
$console_url = $this->environment_variables()->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first();
|
$console_url = $this->environment_variables()->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first();
|
||||||
@@ -414,7 +489,7 @@ class Service extends BaseModel
|
|||||||
public function documentation()
|
public function documentation()
|
||||||
{
|
{
|
||||||
$services = getServiceTemplates();
|
$services = getServiceTemplates();
|
||||||
$service = data_get($services, Str::of($this->name)->beforeLast('-')->value, []);
|
$service = data_get($services, str($this->name)->beforeLast('-')->value, []);
|
||||||
return data_get($service, 'documentation', config('constants.docs.base_url'));
|
return data_get($service, 'documentation', config('constants.docs.base_url'));
|
||||||
}
|
}
|
||||||
public function applications()
|
public function applications()
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ class ServiceApplication extends BaseModel
|
|||||||
{
|
{
|
||||||
return data_get($this, 'is_log_drain_enabled', false);
|
return data_get($this, 'is_log_drain_enabled', false);
|
||||||
}
|
}
|
||||||
|
public function isStripprefixEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'is_stripprefix_enabled', true);
|
||||||
|
}
|
||||||
public function isGzipEnabled()
|
public function isGzipEnabled()
|
||||||
{
|
{
|
||||||
return data_get($this, 'is_gzip_enabled', true);
|
return data_get($this, 'is_gzip_enabled', true);
|
||||||
|
|||||||
@@ -21,9 +21,13 @@ class ServiceDatabase extends BaseModel
|
|||||||
{
|
{
|
||||||
return data_get($this, 'is_log_drain_enabled', false);
|
return data_get($this, 'is_log_drain_enabled', false);
|
||||||
}
|
}
|
||||||
|
public function isStripprefixEnabled()
|
||||||
|
{
|
||||||
|
return data_get($this, 'is_stripprefix_enabled', true);
|
||||||
|
}
|
||||||
public function isGzipEnabled()
|
public function isGzipEnabled()
|
||||||
{
|
{
|
||||||
return true;
|
return data_get($this, 'is_gzip_enabled', true);
|
||||||
}
|
}
|
||||||
public function type()
|
public function type()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class StandaloneMariadb extends BaseModel
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDbUrl(bool $useInternal = false): string
|
public function get_db_url(bool $useInternal = false): string
|
||||||
{
|
{
|
||||||
if ($this->is_public && !$useInternal) {
|
if ($this->is_public && !$useInternal) {
|
||||||
return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}";
|
return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}";
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ class StandaloneMongodb extends BaseModel
|
|||||||
{
|
{
|
||||||
return 'standalone-mongodb';
|
return 'standalone-mongodb';
|
||||||
}
|
}
|
||||||
public function getDbUrl(bool $useInternal = false)
|
public function get_db_url(bool $useInternal = false)
|
||||||
{
|
{
|
||||||
if ($this->is_public && !$useInternal) {
|
if ($this->is_public && !$useInternal) {
|
||||||
return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true";
|
return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true";
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class StandaloneMysql extends BaseModel
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDbUrl(bool $useInternal = false): string
|
public function get_db_url(bool $useInternal = false): string
|
||||||
{
|
{
|
||||||
if ($this->is_public && !$useInternal) {
|
if ($this->is_public && !$useInternal) {
|
||||||
return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}";
|
return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}";
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ class StandalonePostgresql extends BaseModel
|
|||||||
{
|
{
|
||||||
return 'standalone-postgresql';
|
return 'standalone-postgresql';
|
||||||
}
|
}
|
||||||
public function getDbUrl(bool $useInternal = false): string
|
public function get_db_url(bool $useInternal = false): string
|
||||||
{
|
{
|
||||||
if ($this->is_public && !$useInternal) {
|
if ($this->is_public && !$useInternal) {
|
||||||
return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}";
|
return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}";
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class StandaloneRedis extends BaseModel
|
|||||||
{
|
{
|
||||||
return 'standalone-redis';
|
return 'standalone-redis';
|
||||||
}
|
}
|
||||||
public function getDbUrl(bool $useInternal = false): string
|
public function get_db_url(bool $useInternal = false): string
|
||||||
{
|
{
|
||||||
if ($this->is_public && !$useInternal) {
|
if ($this->is_public && !$useInternal) {
|
||||||
return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
|
return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ class Subscription extends Model
|
|||||||
if (in_array($subscription, $ultimate)) {
|
if (in_array($subscription, $ultimate)) {
|
||||||
return 'ultimate';
|
return 'ultimate';
|
||||||
}
|
}
|
||||||
}
|
} else if (isStripe()) {
|
||||||
if (isStripe()) {
|
|
||||||
if (!$this->stripe_plan_id) {
|
if (!$this->stripe_plan_id) {
|
||||||
return 'zero';
|
return 'zero';
|
||||||
}
|
}
|
||||||
@@ -55,7 +54,7 @@ class Subscription extends Model
|
|||||||
};
|
};
|
||||||
})->first();
|
})->first();
|
||||||
if ($stripePlanId) {
|
if ($stripePlanId) {
|
||||||
return Str::of($stripePlanId)->after('stripe_price_id_')->before('_')->lower();
|
return str($stripePlanId)->after('stripe_price_id_')->before('_')->lower();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 'zero';
|
return 'zero';
|
||||||
|
|||||||
@@ -48,7 +48,25 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||||||
}
|
}
|
||||||
return explode(',', $recipients);
|
return explode(',', $recipients);
|
||||||
}
|
}
|
||||||
|
static public function serverLimitReached() {
|
||||||
|
$serverLimit = Team::serverLimit();
|
||||||
|
$team = currentTeam();
|
||||||
|
$servers = $team->servers->count();
|
||||||
|
return $servers >= $serverLimit;
|
||||||
|
}
|
||||||
|
public function serverOverflow() {
|
||||||
|
if ($this->serverLimit() < $this->servers->count()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static public function serverLimit()
|
||||||
|
{
|
||||||
|
if (currentTeam()->id === 0 && isDev()) {
|
||||||
|
return 9999999;
|
||||||
|
}
|
||||||
|
return Team::find(currentTeam()->id)->limits['serverLimit'];
|
||||||
|
}
|
||||||
public function limits(): Attribute
|
public function limits(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
@@ -63,14 +81,19 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||||||
$subscription = $subscription->type();
|
$subscription = $subscription->type();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$serverLimit = config('constants.limits.server')[strtolower($subscription)];
|
if ($this->custom_server_limit) {
|
||||||
|
$serverLimit = $this->custom_server_limit;
|
||||||
|
} else {
|
||||||
|
$serverLimit = config('constants.limits.server')[strtolower($subscription)];
|
||||||
|
}
|
||||||
$sharedEmailEnabled = config('constants.limits.email')[strtolower($subscription)];
|
$sharedEmailEnabled = config('constants.limits.email')[strtolower($subscription)];
|
||||||
return ['serverLimit' => $serverLimit, 'sharedEmailEnabled' => $sharedEmailEnabled];
|
return ['serverLimit' => $serverLimit, 'sharedEmailEnabled' => $sharedEmailEnabled];
|
||||||
}
|
}
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public function environment_variables() {
|
public function environment_variables()
|
||||||
|
{
|
||||||
return $this->hasMany(SharedEnvironmentVariable::class)->whereNull('project_id')->whereNull('environment_id');
|
return $this->hasMany(SharedEnvironmentVariable::class)->whereNull('project_id')->whereNull('environment_id');
|
||||||
}
|
}
|
||||||
public function members()
|
public function members()
|
||||||
@@ -121,7 +144,6 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||||||
$sources = collect([]);
|
$sources = collect([]);
|
||||||
$github_apps = $this->hasMany(GithubApp::class)->whereisPublic(false)->get();
|
$github_apps = $this->hasMany(GithubApp::class)->whereisPublic(false)->get();
|
||||||
$gitlab_apps = $this->hasMany(GitlabApp::class)->whereisPublic(false)->get();
|
$gitlab_apps = $this->hasMany(GitlabApp::class)->whereisPublic(false)->get();
|
||||||
// $bitbucket_apps = $this->hasMany(BitbucketApp::class)->get();
|
|
||||||
$sources = $sources->merge($github_apps)->merge($gitlab_apps);
|
$sources = $sources->merge($github_apps)->merge($gitlab_apps);
|
||||||
return $sources;
|
return $sources;
|
||||||
}
|
}
|
||||||
@@ -130,7 +152,8 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||||||
{
|
{
|
||||||
return $this->hasMany(S3Storage::class)->where('is_usable', true);
|
return $this->hasMany(S3Storage::class)->where('is_usable', true);
|
||||||
}
|
}
|
||||||
public function trialEnded() {
|
public function trialEnded()
|
||||||
|
{
|
||||||
foreach ($this->servers as $server) {
|
foreach ($this->servers as $server) {
|
||||||
$server->settings()->update([
|
$server->settings()->update([
|
||||||
'is_usable' => false,
|
'is_usable' => false,
|
||||||
@@ -138,7 +161,8 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function trialEndedButSubscribed() {
|
public function trialEndedButSubscribed()
|
||||||
|
{
|
||||||
foreach ($this->servers as $server) {
|
foreach ($this->servers as $server) {
|
||||||
$server->settings()->update([
|
$server->settings()->update([
|
||||||
'is_usable' => true,
|
'is_usable' => true,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class TeamInvitation extends Model
|
|||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
$this->delete();
|
$this->delete();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user