Skip to content

Commit 436a1d9

Browse files
authored
Merge pull request coollabsio#3663 from coollabsio/next
v4.0.0-beta.350
2 parents 5354561 + a6a3abc commit 436a1d9

33 files changed

+320
-171
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use App\Enums\ApplicationDeploymentStatus;
6+
use App\Models\ApplicationDeploymentQueue;
7+
use Illuminate\Console\Command;
8+
9+
class CheckApplicationDeploymentQueue extends Command
10+
{
11+
protected $signature = 'check:deployment-queue {--force} {--seconds=3600}';
12+
13+
protected $description = 'Check application deployment queue.';
14+
15+
public function handle()
16+
{
17+
$seconds = $this->option('seconds');
18+
$deployments = ApplicationDeploymentQueue::whereIn('status', [
19+
ApplicationDeploymentStatus::IN_PROGRESS,
20+
ApplicationDeploymentStatus::QUEUED,
21+
])->where('created_at', '>=', now()->subSeconds($seconds))->get();
22+
if ($deployments->isEmpty()) {
23+
$this->info('No deployments found in the last '.$seconds.' seconds.');
24+
25+
return;
26+
}
27+
28+
$this->info('Found '.$deployments->count().' deployments created in the last '.$seconds.' seconds.');
29+
30+
foreach ($deployments as $deployment) {
31+
if ($this->option('force')) {
32+
$this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
33+
$this->cancelDeployment($deployment);
34+
} else {
35+
$this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
36+
if ($this->confirm('Do you want to cancel this deployment?', true)) {
37+
$this->cancelDeployment($deployment);
38+
}
39+
}
40+
}
41+
}
42+
43+
private function cancelDeployment(ApplicationDeploymentQueue $deployment)
44+
{
45+
$deployment->update(['status' => ApplicationDeploymentStatus::FAILED]);
46+
if ($deployment->server?->isFunctional()) {
47+
remote_process(['docker rm -f '.$deployment->deployment_uuid], $deployment->server, false);
48+
}
49+
}
50+
}

app/Console/Commands/CleanupApplicationDeploymentQueue.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
class CleanupApplicationDeploymentQueue extends Command
99
{
10-
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
10+
protected $signature = 'cleanup:deployment-queue {--team-id=}';
1111

12-
protected $description = 'CleanupApplicationDeploymentQueue';
12+
protected $description = 'Cleanup application deployment queue.';
1313

1414
public function handle()
1515
{

app/Console/Commands/CleanupStuckedResources.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use App\Jobs\CleanupHelperContainersJob;
66
use App\Models\Application;
7+
use App\Models\ApplicationDeploymentQueue;
78
use App\Models\ApplicationPreview;
89
use App\Models\ScheduledDatabaseBackup;
910
use App\Models\ScheduledTask;
@@ -47,6 +48,17 @@ private function cleanup_stucked_resources()
4748
} catch (\Throwable $e) {
4849
echo "Error in cleaning stucked resources: {$e->getMessage()}\n";
4950
}
51+
try {
52+
$applicationsDeploymentQueue = ApplicationDeploymentQueue::get();
53+
foreach ($applicationsDeploymentQueue as $applicationDeploymentQueue) {
54+
if (is_null($applicationDeploymentQueue->application)) {
55+
echo "Deleting stuck application deployment queue: {$applicationDeploymentQueue->id}\n";
56+
$applicationDeploymentQueue->delete();
57+
}
58+
}
59+
} catch (\Throwable $e) {
60+
echo "Error in cleaning stuck application deployment queue: {$e->getMessage()}\n";
61+
}
5062
try {
5163
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
5264
foreach ($applications as $application) {

app/Helpers/SshMultiplexingHelper.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ public static function generateScpCommand(Server $server, string $source, string
9494
$muxPersistTime = config('constants.ssh.mux_persist_time');
9595

9696
$scp_command = "timeout $timeout scp ";
97-
97+
if ($server->isIpv6()) {
98+
$scp_command .= '-6 ';
99+
}
98100
if (self::isMultiplexingEnabled()) {
99101
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
100102
self::ensureMultiplexedConnection($server);
@@ -136,8 +138,8 @@ public static function generateSshCommand(Server $server, string $command)
136138

137139
$ssh_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'));
138140

139-
$command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
140141
$delimiter = Hash::make($command);
142+
$delimiter = base64_encode($delimiter);
141143
$command = str_replace($delimiter, '', $command);
142144

143145
$ssh_command .= "{$server->user}@{$server->ip} 'bash -se' << \\$delimiter".PHP_EOL

app/Jobs/DatabaseBackupJob.php

Lines changed: 37 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace App\Jobs;
44

5-
use App\Actions\Database\StopDatabase;
65
use App\Events\BackupCreated;
76
use App\Models\S3Storage;
87
use App\Models\ScheduledDatabaseBackup;
@@ -24,7 +23,6 @@
2423
use Illuminate\Queue\InteractsWithQueue;
2524
use Illuminate\Queue\SerializesModels;
2625
use Illuminate\Support\Str;
27-
use Visus\Cuid2\Cuid2;
2826

2927
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
3028
{
@@ -63,30 +61,26 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
6361
public function __construct($backup)
6462
{
6563
$this->backup = $backup;
66-
$this->team = Team::find($backup->team_id);
67-
if (is_null($this->team)) {
68-
return;
69-
}
70-
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
71-
$this->database = data_get($this->backup, 'database');
72-
$this->server = $this->database->service->server;
73-
$this->s3 = $this->backup->s3;
74-
} else {
75-
$this->database = data_get($this->backup, 'database');
76-
$this->server = $this->database->destination->server;
77-
$this->s3 = $this->backup->s3;
78-
}
7964
}
8065

8166
public function handle(): void
8267
{
8368
try {
84-
// Check if team is exists
85-
if (is_null($this->team)) {
86-
StopDatabase::run($this->database);
87-
$this->database->delete();
88-
89-
return;
69+
$this->team = Team::findOrFail($this->backup->team_id);
70+
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
71+
$this->database = data_get($this->backup, 'database');
72+
$this->server = $this->database->service->server;
73+
$this->s3 = $this->backup->s3;
74+
} else {
75+
$this->database = data_get($this->backup, 'database');
76+
$this->server = $this->database->destination->server;
77+
$this->s3 = $this->backup->s3;
78+
}
79+
if (is_null($this->server)) {
80+
throw new \Exception('Server not found?!');
81+
}
82+
if (is_null($this->database)) {
83+
throw new \Exception('Database not found?!');
9084
}
9185

9286
BackupCreated::dispatch($this->team->id);
@@ -237,7 +231,6 @@ public function handle(): void
237231
}
238232
}
239233
$this->backup_dir = backup_dir().'/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name;
240-
241234
if ($this->database->name === 'coolify-db') {
242235
$databasesToBackup = ['coolify'];
243236
$this->directory_name = $this->container_name = 'coolify-db';
@@ -325,7 +318,9 @@ public function handle(): void
325318
send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage());
326319
throw $e;
327320
} finally {
328-
BackupCreated::dispatch($this->team->id);
321+
if ($this->team) {
322+
BackupCreated::dispatch($this->team->id);
323+
}
329324
}
330325
}
331326

@@ -466,34 +461,6 @@ private function remove_old_backups(): void
466461
}
467462
}
468463

469-
// private function upload_to_s3(): void
470-
// {
471-
// try {
472-
// if (is_null($this->s3)) {
473-
// return;
474-
// }
475-
// $key = $this->s3->key;
476-
// $secret = $this->s3->secret;
477-
// // $region = $this->s3->region;
478-
// $bucket = $this->s3->bucket;
479-
// $endpoint = $this->s3->endpoint;
480-
// $this->s3->testConnection(shouldSave: true);
481-
// $configName = new Cuid2;
482-
483-
// $s3_copy_dir = str($this->backup_location)->replace(backup_dir(), '/var/www/html/storage/app/backups/');
484-
// $commands[] = "docker exec coolify bash -c 'mc config host add {$configName} {$endpoint} $key $secret'";
485-
// $commands[] = "docker exec coolify bash -c 'mc cp $s3_copy_dir {$configName}/{$bucket}{$this->backup_dir}/'";
486-
// instant_remote_process($commands, $this->server);
487-
// $this->add_to_backup_output('Uploaded to S3.');
488-
// } catch (\Throwable $e) {
489-
// $this->add_to_backup_output($e->getMessage());
490-
// throw $e;
491-
// } finally {
492-
// $removeConfigCommands[] = "docker exec coolify bash -c 'mc config remove {$configName}'";
493-
// $removeConfigCommands[] = "docker exec coolify bash -c 'mc alias rm {$configName}'";
494-
// instant_remote_process($removeConfigCommands, $this->server, false);
495-
// }
496-
// }
497464
private function upload_to_s3(): void
498465
{
499466
try {
@@ -515,10 +482,27 @@ private function upload_to_s3(): void
515482
$this->ensureHelperImageAvailable();
516483

517484
$fullImageName = $this->getFullImageName();
518-
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
519-
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
485+
486+
if (isDev()) {
487+
if ($this->database->name === 'coolify-db') {
488+
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/coolify/coolify-db-'.$this->server->ip.$this->backup_file;
489+
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
490+
} else {
491+
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name.$this->backup_file;
492+
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
493+
}
494+
} else {
495+
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
496+
}
497+
if ($this->s3->isHetzner()) {
498+
$endpointWithoutBucket = 'https://'.str($endpoint)->after('https://')->after('.')->value();
499+
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc alias set --path=off --api=S3v4 temporary {$endpointWithoutBucket} $key $secret";
500+
} else {
501+
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
502+
}
520503
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
521504
instant_remote_process($commands, $this->server);
505+
522506
$this->add_to_backup_output('Uploaded to S3.');
523507
} catch (\Throwable $e) {
524508
$this->add_to_backup_output($e->getMessage());

app/Livewire/Dashboard.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function mount()
3030

3131
public function cleanup_queue()
3232
{
33-
Artisan::queue('cleanup:application-deployment-queue', [
33+
Artisan::queue('cleanup:deployment-queue', [
3434
'--team-id' => currentTeam()->id,
3535
]);
3636
}

app/Livewire/Project/Shared/Terminal.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public function sendTerminalCommand($isContainer, $identifier, $serverUuid)
3434
if ($status !== 'running') {
3535
return;
3636
}
37-
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
37+
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
3838
} else {
39-
$command = SshMultiplexingHelper::generateSshCommand($server, "sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
39+
$command = SshMultiplexingHelper::generateSshCommand($server, 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n "$SHELL" ]; then exec $SHELL; else sh; fi');
4040
}
4141

4242
// ssh command is sent back to frontend then to websocket

app/Livewire/Security/ApiTokens.php

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class ApiTokens extends Component
1515

1616
public bool $readOnly = true;
1717

18+
public bool $rootAccess = false;
19+
1820
public array $permissions = ['read-only'];
1921

2022
public $isApiEnabled;
@@ -35,24 +37,42 @@ public function updatedViewSensitiveData()
3537
if ($this->viewSensitiveData) {
3638
$this->permissions[] = 'view:sensitive';
3739
$this->permissions = array_diff($this->permissions, ['*']);
40+
$this->rootAccess = false;
3841
} else {
3942
$this->permissions = array_diff($this->permissions, ['view:sensitive']);
4043
}
41-
if (count($this->permissions) == 0) {
42-
$this->permissions = ['*'];
43-
}
44+
$this->makeSureOneIsSelected();
4445
}
4546

4647
public function updatedReadOnly()
4748
{
4849
if ($this->readOnly) {
4950
$this->permissions[] = 'read-only';
5051
$this->permissions = array_diff($this->permissions, ['*']);
52+
$this->rootAccess = false;
5153
} else {
5254
$this->permissions = array_diff($this->permissions, ['read-only']);
5355
}
54-
if (count($this->permissions) == 0) {
56+
$this->makeSureOneIsSelected();
57+
}
58+
59+
public function updatedRootAccess()
60+
{
61+
if ($this->rootAccess) {
5562
$this->permissions = ['*'];
63+
$this->readOnly = false;
64+
$this->viewSensitiveData = false;
65+
} else {
66+
$this->readOnly = true;
67+
$this->permissions = ['read-only'];
68+
}
69+
}
70+
71+
public function makeSureOneIsSelected()
72+
{
73+
if (count($this->permissions) == 0) {
74+
$this->permissions = ['read-only'];
75+
$this->readOnly = true;
5676
}
5777
}
5878

@@ -62,12 +82,6 @@ public function addNewToken()
6282
$this->validate([
6383
'description' => 'required|min:3|max:255',
6484
]);
65-
// if ($this->viewSensitiveData) {
66-
// $this->permissions[] = 'view:sensitive';
67-
// }
68-
// if ($this->readOnly) {
69-
// $this->permissions[] = 'read-only';
70-
// }
7185
$token = auth()->user()->createToken($this->description, $this->permissions);
7286
$this->tokens = auth()->user()->tokens;
7387
session()->flash('token', $token->plainTextToken);

app/Livewire/Server/ConfigureCloudflareTunnels.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public function alreadyConfigured()
3030
public function submit()
3131
{
3232
try {
33+
if (str($this->ssh_domain)->contains('https://')) {
34+
$this->ssh_domain = str($this->ssh_domain)->replace('https://', '')->replace('http://', '')->trim();
35+
// remove / from the end
36+
$this->ssh_domain = str($this->ssh_domain)->replace('/', '');
37+
}
3338
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
3439
ConfigureCloudflared::dispatch($server, $this->cloudflare_token);
3540
$server->settings->is_cloudflare_tunnel = true;

app/Livewire/Storage/Create.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,17 @@ class Create extends Component
4343
'endpoint' => 'Endpoint',
4444
];
4545

46-
public function mount()
46+
public function updatedEndpoint($value)
4747
{
48-
if (isDev()) {
49-
$this->name = 'Local MinIO';
50-
$this->description = 'Local MinIO';
51-
$this->key = 'minioadmin';
52-
$this->secret = 'minioadmin';
53-
$this->bucket = 'local';
54-
$this->endpoint = 'http://coolify-minio:9000';
48+
if (! str($value)->startsWith('https://') && ! str($value)->startsWith('http://')) {
49+
$this->endpoint = 'https://'.$value;
50+
$value = $this->endpoint;
51+
}
52+
53+
if (str($value)->contains('your-objectstorage.com') && ! isset($this->bucket)) {
54+
$this->bucket = str($value)->after('//')->before('.');
55+
} elseif (str($value)->contains('your-objectstorage.com')) {
56+
$this->bucket = $this->bucket ?: str($value)->after('//')->before('.');
5557
}
5658
}
5759

0 commit comments

Comments
 (0)