mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-24 04:59:32 +00:00
Compare commits
181 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
725c3fd547 | ||
|
|
dcfcee1db6 | ||
|
|
9f8c44d96b | ||
|
|
5b74fd34f5 | ||
|
|
5a4c9422b2 | ||
|
|
81b916724e | ||
|
|
9540f60fa2 | ||
|
|
8eb1686125 | ||
|
|
daf3710a5e | ||
|
|
1067f37e4d | ||
|
|
3b3c0b94e5 | ||
|
|
8b0a0d67da | ||
|
|
5541c135df | ||
|
|
2552cb2208 | ||
|
|
f001e9bc34 | ||
|
|
f943fdc5be | ||
|
|
a4f1fcba58 | ||
|
|
68091b44fc | ||
|
|
9f8caac91c | ||
|
|
8082dc1a01 | ||
|
|
a71cf5bc66 | ||
|
|
0775074509 | ||
|
|
242d2fb283 | ||
|
|
5646818965 | ||
|
|
ffc5320940 | ||
|
|
7c10c55b1c | ||
|
|
7c96b6207a | ||
|
|
0be8ffbdc9 | ||
|
|
24fa56762e | ||
|
|
84d8e35411 | ||
|
|
3d3ccc435c | ||
|
|
be3b01472e | ||
|
|
de6f5b1105 | ||
|
|
14d9c06dcd | ||
|
|
8abfaa1967 | ||
|
|
46f7ae9588 | ||
|
|
f2c32b9aeb | ||
|
|
3dab1eb92e | ||
|
|
9c22e01716 | ||
|
|
d1c47a4062 | ||
|
|
6c3f97d9ae | ||
|
|
ebd8e2ce40 | ||
|
|
b650f3f754 | ||
|
|
f33ba40478 | ||
|
|
5cea9c4603 | ||
|
|
d32832fabc | ||
|
|
165f0a3d4a | ||
|
|
f14995200b | ||
|
|
12bb2ecc4a | ||
|
|
933ec5741d | ||
|
|
8004a40139 | ||
|
|
a6209fbe5c | ||
|
|
b47c327b55 | ||
|
|
25434a7acd | ||
|
|
0de042dbac | ||
|
|
eb9e2203b0 | ||
|
|
dcaa7a6ad7 | ||
|
|
5b584a6c6d | ||
|
|
c40ea6f1da | ||
|
|
61a7b9ac94 | ||
|
|
9e81416fef | ||
|
|
c58706e3e4 | ||
|
|
45bca8649b | ||
|
|
b095b88281 | ||
|
|
cb41584137 | ||
|
|
6659153804 | ||
|
|
44c429a224 | ||
|
|
fe9c501c1d | ||
|
|
4e94b4a0c1 | ||
|
|
2f4d7c0e43 | ||
|
|
e443fc394a | ||
|
|
5b56c50f03 | ||
|
|
3adeb2f73f | ||
|
|
9eaa13a08a | ||
|
|
df5a4a9667 | ||
|
|
26048339d6 | ||
|
|
d85af3fefc | ||
|
|
575338609b | ||
|
|
d32e43ef37 | ||
|
|
a96ef1bfab | ||
|
|
1bfedf69f2 | ||
|
|
208fe7d87b | ||
|
|
a6d58b5d72 | ||
|
|
277b4276e6 | ||
|
|
f6adc9285a | ||
|
|
f03bbe0e95 | ||
|
|
535375193c | ||
|
|
d79c063fd6 | ||
|
|
35f45492e3 | ||
|
|
ab8a7893d9 | ||
|
|
76b8d048d4 | ||
|
|
0e583334e7 | ||
|
|
0ad8ca224f | ||
|
|
050e56f69a | ||
|
|
6099ac11d9 | ||
|
|
adac728a60 | ||
|
|
e2e64e36a0 | ||
|
|
762af66cbf | ||
|
|
b08f525bd4 | ||
|
|
5ae16b195c | ||
|
|
91db1953ff | ||
|
|
1c8f92d3b7 | ||
|
|
4075572dbc | ||
|
|
2971e360d7 | ||
|
|
32bb2780f2 | ||
|
|
af69575b29 | ||
|
|
d4a7d0d25f | ||
|
|
45f9def0f6 | ||
|
|
5a90eed7ef | ||
|
|
38e1f17edf | ||
|
|
1651845e20 | ||
|
|
38e96548b5 | ||
|
|
47e4126dca | ||
|
|
e0b175ab07 | ||
|
|
93ec785f4f | ||
|
|
e849addab8 | ||
|
|
a5e6975dac | ||
|
|
4ac8e1cc67 | ||
|
|
1a5e3a7836 | ||
|
|
4498d1ed4b | ||
|
|
c8b974820b | ||
|
|
527373e297 | ||
|
|
a84be8dc33 | ||
|
|
bd856f7f67 | ||
|
|
8ff216e5fb | ||
|
|
5255311a2e | ||
|
|
774a245e84 | ||
|
|
194675c838 | ||
|
|
75862ca8de | ||
|
|
5580a4e704 | ||
|
|
51e601a303 | ||
|
|
734e9fd68d | ||
|
|
09fc950ae8 | ||
|
|
cf6caa279d | ||
|
|
68c976ab70 | ||
|
|
1560ab2a50 | ||
|
|
e3a6458506 | ||
|
|
1768b9374f | ||
|
|
51d0a30a6c | ||
|
|
9701c65297 | ||
|
|
58e3bb2571 | ||
|
|
31cbd1602d | ||
|
|
dd5723d596 | ||
|
|
620f26a6f1 | ||
|
|
540717e809 | ||
|
|
d446cd4103 | ||
|
|
7d1a76570c | ||
|
|
e18766ec21 | ||
|
|
3d0354cf7e | ||
|
|
af5b9fced1 | ||
|
|
ab5202515e | ||
|
|
f863db7ea5 | ||
|
|
3adc0bdd6e | ||
|
|
97027875bf | ||
|
|
fd9c13009f | ||
|
|
46a72fac47 | ||
|
|
5d6ee04991 | ||
|
|
d523becb29 | ||
|
|
41672f75d0 | ||
|
|
acd8541e68 | ||
|
|
ed6af777a4 | ||
|
|
05f162f4e8 | ||
|
|
5e0adc3777 | ||
|
|
7d06fc4403 | ||
|
|
0e1bcceb8e | ||
|
|
a922f2fedf | ||
|
|
1e0226c8ed | ||
|
|
5c45908087 | ||
|
|
aefdc76805 | ||
|
|
b3c8c881b7 | ||
|
|
9ab5a1f7bd | ||
|
|
23968e7886 | ||
|
|
390d24b6d7 | ||
|
|
e4296345b3 | ||
|
|
bcffbe418b | ||
|
|
bfbee4e78f | ||
|
|
2bdb44dac3 | ||
|
|
4daa1b8c16 | ||
|
|
febc399568 | ||
|
|
9b9d4f9941 | ||
|
|
f49b87870c |
@@ -1,11 +1,3 @@
|
|||||||
############################################################################################################
|
|
||||||
# Development Environment
|
|
||||||
|
|
||||||
# User and group id for the user that will run the application inside the container
|
|
||||||
# Run in your terminal: `id -u` and `id -g` and that's the results
|
|
||||||
USERID=
|
|
||||||
GROUPID=
|
|
||||||
############################################################################################################
|
|
||||||
APP_NAME=Coolify-localhost
|
APP_NAME=Coolify-localhost
|
||||||
APP_ID=development
|
APP_ID=development
|
||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
@@ -13,6 +5,7 @@ APP_KEY=
|
|||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
APP_PORT=8000
|
APP_PORT=8000
|
||||||
|
MUX_ENABLED=false
|
||||||
|
|
||||||
DUSK_DRIVER_URL=http://selenium:4444
|
DUSK_DRIVER_URL=http://selenium:4444
|
||||||
|
|
||||||
|
|||||||
29
CONTRIBUTION.md
Normal file
29
CONTRIBUTION.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
> "First, thanks for considering to contribute to my project.
|
||||||
|
It really means a lot!" - [@andrasbacsai](https://github.com/andrasbacsai)
|
||||||
|
|
||||||
|
You can ask for guidance anytime on our
|
||||||
|
[Discord server](https://coollabs.io/discord) in the `#contribution` channel.
|
||||||
|
|
||||||
|
|
||||||
|
## 1) Setup your development environment
|
||||||
|
|
||||||
|
- You need to have Docker Engine (or equivalent) [installed](https://docs.docker.com/engine/install/) on your system.
|
||||||
|
- For better DX, install [Spin](https://serversideup.net/open-source/spin/).
|
||||||
|
|
||||||
|
## 2) Set your environment variables
|
||||||
|
|
||||||
|
- Copy [.env.development.example](./.env.development.example) to .env.
|
||||||
|
|
||||||
|
## 3) Start & setup Coolify
|
||||||
|
|
||||||
|
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
||||||
|
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
||||||
|
|
||||||
|
- Run `./scripts/run setup:dev` - This will generate a secret key for you, delete any existing database layouts, migrate database to the new layout, and seed your database.
|
||||||
|
|
||||||
|
## 4) Start development
|
||||||
|
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||||
|
|
||||||
|
Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if you logged in with root user.
|
||||||
@@ -36,7 +36,7 @@ You can find the installation script [here](./scripts/install.sh).
|
|||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Contact us [here](https://docs.coollabs.io/contact).
|
Contact us [here](https://coolify.io/docs/contact).
|
||||||
|
|
||||||
## Recognitions
|
## Recognitions
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
'password' => Hash::make($input['password']),
|
'password' => Hash::make($input['password']),
|
||||||
]);
|
]);
|
||||||
$team = $user->teams()->first();
|
$team = $user->teams()->first();
|
||||||
|
if (isCloud()) {
|
||||||
|
$user->sendVerificationEmail();
|
||||||
|
} else {
|
||||||
|
$user->markEmailAsVerified();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Set session variable
|
// Set session variable
|
||||||
session(['currentTeam' => $user->currentTeam = $team]);
|
session(['currentTeam' => $user->currentTeam = $team]);
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
|
|
||||||
class InstallDocker
|
class InstallDocker
|
||||||
{
|
{
|
||||||
public function __invoke(Server $server)
|
use AsAction;
|
||||||
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
$dockerVersion = '24.0';
|
$dockerVersion = '24.0';
|
||||||
$config = base64_encode('{
|
$config = base64_encode('{
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ namespace App\Actions\Service;
|
|||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartService
|
class StartService
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
|
$network = $service->destination->network;
|
||||||
$service->saveComposeConfigs();
|
$service->saveComposeConfigs();
|
||||||
$commands[] = "cd " . $service->workdir();
|
$commands[] = "cd " . $service->workdir();
|
||||||
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
||||||
@@ -21,6 +23,11 @@ class StartService
|
|||||||
$commands[] = "echo '####### Starting containers.'";
|
$commands[] = "echo '####### Starting containers.'";
|
||||||
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
||||||
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
||||||
|
$compose = data_get($service,'docker_compose',[]);
|
||||||
|
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
|
||||||
|
foreach($serviceNames as $serviceName => $serviceConfig){
|
||||||
|
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} 2>/dev/null || true";
|
||||||
|
}
|
||||||
$activity = remote_process($commands, $service->server);
|
$activity = remote_process($commands, $service->server);
|
||||||
return $activity;
|
return $activity;
|
||||||
}
|
}
|
||||||
|
|||||||
33
app/Console/Commands/Cloud.php
Normal file
33
app/Console/Commands/Cloud.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class Cloud extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'cloud:unused-servers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Get Unused Servers from Cloud';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended',true)->each(function($server){
|
||||||
|
$this->info($server->name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
108
app/Console/Commands/ResourcesDelete.php
Normal file
108
app/Console/Commands/ResourcesDelete.php
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\confirm;
|
||||||
|
use function Laravel\Prompts\multiselect;
|
||||||
|
use function Laravel\Prompts\select;
|
||||||
|
|
||||||
|
class ResourcesDelete extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'resources:delete';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Delete a resource from the database';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$resource = select(
|
||||||
|
'What resource do you want to delete?',
|
||||||
|
['Application', 'Database', 'Service'],
|
||||||
|
);
|
||||||
|
if ($resource === 'Application') {
|
||||||
|
$this->deleteApplication();
|
||||||
|
} elseif ($resource === 'Database') {
|
||||||
|
$this->deleteDatabase();
|
||||||
|
} elseif ($resource === 'Service') {
|
||||||
|
$this->deleteService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function deleteApplication()
|
||||||
|
{
|
||||||
|
$applications = Application::all();
|
||||||
|
if ($applications->count() === 0) {
|
||||||
|
$this->error('There are no applications to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$applicationsToDelete = multiselect(
|
||||||
|
'What application do you want to delete?',
|
||||||
|
$applications->pluck('name')->toArray(),
|
||||||
|
);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($applicationsToDelete as $application) {
|
||||||
|
$toDelete = $applications->where('name', $application)->first();
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function deleteDatabase()
|
||||||
|
{
|
||||||
|
$databases = StandalonePostgresql::all();
|
||||||
|
if ($databases->count() === 0) {
|
||||||
|
$this->error('There are no databases to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$databasesToDelete = multiselect(
|
||||||
|
'What database do you want to delete?',
|
||||||
|
$databases->pluck('name')->toArray(),
|
||||||
|
);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($databasesToDelete as $database) {
|
||||||
|
$toDelete = $databases->where('name', $database)->first();
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private function deleteService()
|
||||||
|
{
|
||||||
|
$services = Service::all();
|
||||||
|
if ($services->count() === 0) {
|
||||||
|
$this->error('There are no services to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$servicesToDelete = multiselect(
|
||||||
|
'What service do you want to delete?',
|
||||||
|
$services->pluck('name')->toArray(),
|
||||||
|
);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($servicesToDelete as $service) {
|
||||||
|
$toDelete = $services->where('name', $service)->first();
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
app/Console/Commands/UsersResetRoot.php
Normal file
49
app/Console/Commands/UsersResetRoot.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\password;
|
||||||
|
|
||||||
|
class UsersResetRoot extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'users:reset-root';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Reset Root Password';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
$this->info('You are about to reset the root password.');
|
||||||
|
$password = password('Give me a new password for root user: ');
|
||||||
|
$passwordAgain = password('Again');
|
||||||
|
if ($password != $passwordAgain) {
|
||||||
|
$this->error('Passwords do not match.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->info('Updating root password...');
|
||||||
|
try {
|
||||||
|
User::find(0)->update(['password' => Hash::make($password)]);
|
||||||
|
$this->info('Root password updated successfully.');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('Failed to update root password.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,7 +32,6 @@ class Kernel extends ConsoleKernel
|
|||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||||
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
// $schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
|
|
||||||
$this->instance_auto_update($schedule);
|
$this->instance_auto_update($schedule);
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
@@ -48,7 +47,11 @@ class Kernel extends ConsoleKernel
|
|||||||
}
|
}
|
||||||
private function check_resources($schedule)
|
private function check_resources($schedule)
|
||||||
{
|
{
|
||||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
if (isCloud()) {
|
||||||
|
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false);
|
||||||
|
} else {
|
||||||
|
$servers = Server::all();
|
||||||
|
}
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,15 +46,6 @@ class Controller extends BaseController
|
|||||||
}
|
}
|
||||||
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
||||||
}
|
}
|
||||||
public function subscription()
|
|
||||||
{
|
|
||||||
if (!isCloud()) {
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
return view('subscription.index', [
|
|
||||||
'settings' => InstanceSettings::get(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function license()
|
public function license()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use App\Models\EnvironmentVariable;
|
|||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use App\Models\StandaloneDocker;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class ProjectController extends Controller
|
class ProjectController extends Controller
|
||||||
@@ -69,17 +69,19 @@ class ProjectController extends Controller
|
|||||||
if ($type->startsWith('one-click-service-') && !is_null( (int)$server_id)) {
|
if ($type->startsWith('one-click-service-') && !is_null( (int)$server_id)) {
|
||||||
$oneClickServiceName = $type->after('one-click-service-')->value();
|
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||||
ray($oneClickServiceName);
|
|
||||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||||
if ($oneClickDotEnvs) {
|
if ($oneClickDotEnvs) {
|
||||||
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
||||||
}
|
}
|
||||||
if ($oneClickService) {
|
if ($oneClickService) {
|
||||||
|
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||||
$service = Service::create([
|
$service = Service::create([
|
||||||
'name' => "$oneClickServiceName-" . Str::random(10),
|
'name' => "$oneClickServiceName-" . Str::random(10),
|
||||||
'docker_compose_raw' => base64_decode($oneClickService),
|
'docker_compose_raw' => base64_decode($oneClickService),
|
||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'server_id' => (int) $server_id,
|
'server_id' => (int) $server_id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination->getMorphClass(),
|
||||||
]);
|
]);
|
||||||
$service->name = "$oneClickServiceName-" . $service->uuid;
|
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||||
$service->save();
|
$service->save();
|
||||||
@@ -90,6 +92,7 @@ class ProjectController extends Controller
|
|||||||
$generatedValue = $value;
|
$generatedValue = $value;
|
||||||
if ($value->contains('SERVICE_')) {
|
if ($value->contains('SERVICE_')) {
|
||||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
|
// TODO: make it shared with Service.php
|
||||||
switch ($command->value()) {
|
switch ($command->value()) {
|
||||||
case 'PASSWORD':
|
case 'PASSWORD':
|
||||||
$generatedValue = Str::password(symbols: false);
|
$generatedValue = Str::password(symbols: false);
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
|
||||||
|
|
||||||
class ServerController extends Controller
|
|
||||||
{
|
|
||||||
use AuthorizesRequests, ValidatesRequests;
|
|
||||||
|
|
||||||
public function new_server()
|
|
||||||
{
|
|
||||||
$privateKeys = PrivateKey::ownedByCurrentTeam()->get();
|
|
||||||
if (!isCloud()) {
|
|
||||||
return view('server.create', [
|
|
||||||
'limit_reached' => false,
|
|
||||||
'private_keys' => $privateKeys,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$team = currentTeam();
|
|
||||||
$servers = $team->servers->count();
|
|
||||||
['serverLimit' => $serverLimit] = $team->limits;
|
|
||||||
$limit_reached = $servers >= $serverLimit;
|
|
||||||
|
|
||||||
return view('server.create', [
|
|
||||||
'limit_reached' => $limit_reached,
|
|
||||||
'private_keys' => $privateKeys,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,8 +38,7 @@ class Kernel extends HttpKernel
|
|||||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
\App\Http\Middleware\CheckForcePasswordReset::class,
|
\App\Http\Middleware\CheckForcePasswordReset::class,
|
||||||
\App\Http\Middleware\IsSubscriptionValid::class,
|
\App\Http\Middleware\DecideWhatToDoWithUser::class,
|
||||||
\App\Http\Middleware\IsBoardingFlow::class,
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
Team::find(currentTeam()->id)->update([
|
Team::find(currentTeam()->id)->update([
|
||||||
'show_boarding' => false
|
'show_boarding' => false
|
||||||
]);
|
]);
|
||||||
ray(currentTeam());
|
|
||||||
refreshSession();
|
refreshSession();
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
@@ -165,7 +164,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
{
|
{
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'remoteServerName' => 'required',
|
'remoteServerName' => 'required',
|
||||||
'remoteServerHost' => 'required',
|
'remoteServerHost' => 'required|ip',
|
||||||
'remoteServerPort' => 'required|integer',
|
'remoteServerPort' => 'required|integer',
|
||||||
'remoteServerUser' => 'required',
|
'remoteServerUser' => 'required',
|
||||||
]);
|
]);
|
||||||
@@ -221,7 +220,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
public function installDocker()
|
public function installDocker()
|
||||||
{
|
{
|
||||||
$this->dockerInstallationStarted = true;
|
$this->dockerInstallationStarted = true;
|
||||||
$activity = resolve(InstallDocker::class)($this->createdServer);
|
$activity = InstallDocker::run($this->createdServer);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
}
|
}
|
||||||
public function dockerInstalledOrSkipped()
|
public function dockerInstalledOrSkipped()
|
||||||
|
|||||||
@@ -9,21 +9,13 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class Dashboard extends Component
|
class Dashboard extends Component
|
||||||
{
|
{
|
||||||
public int $projects = 0;
|
public $projects = [];
|
||||||
public int $servers = 0;
|
public $servers = [];
|
||||||
public int $s3s = 0;
|
|
||||||
public int $resources = 0;
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->servers = Server::ownedByCurrentTeam()->get()->count();
|
$this->servers = Server::ownedByCurrentTeam()->get();
|
||||||
$this->s3s = S3Storage::ownedByCurrentTeam()->get()->count();
|
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||||
$projects = Project::ownedByCurrentTeam()->get();
|
|
||||||
foreach ($projects as $project) {
|
|
||||||
$this->resources += $project->applications->count();
|
|
||||||
$this->resources += $project->postgresqls->count();
|
|
||||||
}
|
|
||||||
$this->projects = $projects->count();
|
|
||||||
}
|
}
|
||||||
// public function getIptables()
|
// public function getIptables()
|
||||||
// {
|
// {
|
||||||
|
|||||||
28
app/Http/Livewire/Dev/Compose.php
Normal file
28
app/Http/Livewire/Dev/Compose.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Dev;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Compose extends Component
|
||||||
|
{
|
||||||
|
public string $compose = '';
|
||||||
|
public string $base64 = '';
|
||||||
|
public $services;
|
||||||
|
public function mount() {
|
||||||
|
$this->services = getServiceTemplates();
|
||||||
|
}
|
||||||
|
public function setService(string $selected) {
|
||||||
|
$this->base64 = data_get($this->services, $selected . '.compose');
|
||||||
|
if ($this->base64) {
|
||||||
|
$this->compose = base64_decode($this->base64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function updatedCompose($value) {
|
||||||
|
$this->base64 = base64_encode($value);
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.dev.compose');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,10 +17,10 @@ class General extends Component
|
|||||||
public Application $application;
|
public Application $application;
|
||||||
public Collection $services;
|
public Collection $services;
|
||||||
public string $name;
|
public string $name;
|
||||||
public string|null $fqdn;
|
public ?string $fqdn = null;
|
||||||
public string $git_repository;
|
public string $git_repository;
|
||||||
public string $git_branch;
|
public string $git_branch;
|
||||||
public string|null $git_commit_sha;
|
public ?string $git_commit_sha = null;
|
||||||
public string $build_pack;
|
public string $build_pack;
|
||||||
|
|
||||||
public bool $is_static;
|
public bool $is_static;
|
||||||
@@ -49,6 +49,9 @@ class General extends Component
|
|||||||
'application.ports_exposes' => 'required',
|
'application.ports_exposes' => 'required',
|
||||||
'application.ports_mappings' => 'nullable',
|
'application.ports_mappings' => 'nullable',
|
||||||
'application.dockerfile' => 'nullable',
|
'application.dockerfile' => 'nullable',
|
||||||
|
'application.docker_registry_image_name' => 'nullable',
|
||||||
|
'application.docker_registry_image_tag' => 'nullable',
|
||||||
|
'application.dockerfile_location' => 'nullable',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@@ -67,9 +70,15 @@ class General extends Component
|
|||||||
'application.ports_exposes' => 'Ports exposes',
|
'application.ports_exposes' => 'Ports exposes',
|
||||||
'application.ports_mappings' => 'Ports mappings',
|
'application.ports_mappings' => 'Ports mappings',
|
||||||
'application.dockerfile' => 'Dockerfile',
|
'application.dockerfile' => 'Dockerfile',
|
||||||
|
'application.docker_registry_image_name' => 'Docker registry image name',
|
||||||
|
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
||||||
|
'application.dockerfile_location' => 'Dockerfile location',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function updatedApplicationBuildPack(){
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
// @TODO: find another way - if possible
|
// @TODO: find another way - if possible
|
||||||
@@ -104,19 +113,27 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->is_static = $this->application->settings->is_static;
|
if (data_get($this->application,'settings')) {
|
||||||
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
$this->is_static = $this->application->settings->is_static;
|
||||||
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
||||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
||||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
||||||
|
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
if (data_get($this->application,'build_pack') === 'dockerimage') {
|
||||||
|
$this->validate([
|
||||||
|
'application.docker_registry_image_name' => 'required',
|
||||||
|
'application.docker_registry_image_tag' => 'required',
|
||||||
|
]);
|
||||||
|
}
|
||||||
if (data_get($this->application, 'fqdn')) {
|
if (data_get($this->application, 'fqdn')) {
|
||||||
$domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
$domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||||
return Str::of($domain)->trim()->lower();
|
return Str::of($domain)->trim()->lower();
|
||||||
@@ -125,7 +142,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
if (data_get($this->application, 'dockerfile')) {
|
if (data_get($this->application, 'dockerfile')) {
|
||||||
$port = get_port_from_dockerfile($this->application->dockerfile);
|
$port = get_port_from_dockerfile($this->application->dockerfile);
|
||||||
if ($port) {
|
if ($port && !$this->application->ports_exposes) {
|
||||||
$this->application->ports_exposes = $port;
|
$this->application->ports_exposes = $port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,13 @@ class Heading extends Component
|
|||||||
|
|
||||||
public function check_status()
|
public function check_status()
|
||||||
{
|
{
|
||||||
dispatch(new ContainerStatusJob($this->application->destination->server));
|
if ($this->application->destination->server->isFunctional()) {
|
||||||
$this->application->refresh();
|
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||||
$this->application->previews->each(function ($preview) {
|
$this->application->refresh();
|
||||||
$preview->refresh();
|
$this->application->previews->each(function ($preview) {
|
||||||
});
|
$preview->refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function force_deploy_without_cache()
|
public function force_deploy_without_cache()
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ class Form extends Component
|
|||||||
public function generate_real_url()
|
public function generate_real_url()
|
||||||
{
|
{
|
||||||
if (data_get($this->application, 'fqdn')) {
|
if (data_get($this->application, 'fqdn')) {
|
||||||
$url = Url::fromString($this->application->fqdn);
|
$firstFqdn = Str::of($this->application->fqdn)->before(',');
|
||||||
|
$url = Url::fromString($firstFqdn);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$this->preview_url_template = Str::of($this->application->preview_url_template)->replace('{{domain}}', $host);
|
$this->preview_url_template = Str::of($this->application->preview_url_template)->replace('{{domain}}', $host);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class Previews extends Component
|
|||||||
public function load_prs()
|
public function load_prs()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
['rate_limit_remaining' => $rate_limit_remaining, 'data' => $data] = git_api(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/pulls");
|
['rate_limit_remaining' => $rate_limit_remaining, 'data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/pulls");
|
||||||
$this->rate_limit_remaining = $rate_limit_remaining;
|
$this->rate_limit_remaining = $rate_limit_remaining;
|
||||||
$this->pull_requests = $data->sortBy('number')->values();
|
$this->pull_requests = $data->sortBy('number')->values();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -72,8 +72,7 @@ class Previews extends Component
|
|||||||
public function stop(int $pull_request_id)
|
public function stop(int $pull_request_id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$container_name = generateApplicationContainerName($this->application);
|
$container_name = generateApplicationContainerName($this->application, $pull_request_id);
|
||||||
ray('Stopping container: ' . $container_name);
|
|
||||||
|
|
||||||
instant_remote_process(["docker rm -f $container_name"], $this->application->destination->server, throwError: false);
|
instant_remote_process(["docker rm -f $container_name"], $this->application->destination->server, throwError: false);
|
||||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->delete();
|
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->delete();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class BackupEdit extends Component
|
|||||||
{
|
{
|
||||||
public $backup;
|
public $backup;
|
||||||
public $s3s;
|
public $s3s;
|
||||||
|
public ?string $status = null;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class CreateScheduledBackup extends Component
|
|||||||
public $database;
|
public $database;
|
||||||
public $frequency;
|
public $frequency;
|
||||||
public bool $enabled = true;
|
public bool $enabled = true;
|
||||||
public bool $save_s3 = true;
|
public bool $save_s3 = false;
|
||||||
public $s3_storage_id;
|
public $s3_storage_id;
|
||||||
public $s3s;
|
public $s3s;
|
||||||
|
|
||||||
|
|||||||
@@ -50,8 +50,9 @@ class General extends Component
|
|||||||
$this->getDbUrl();
|
$this->getDbUrl();
|
||||||
}
|
}
|
||||||
public function getDbUrl() {
|
public function getDbUrl() {
|
||||||
|
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->destination->server->ip}:{$this->database->public_port}/{$this->database->postgres_db}";
|
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->destination->server->getIp}:{$this->database->public_port}/{$this->database->postgres_db}";
|
||||||
} else {
|
} else {
|
||||||
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->uuid}:5432/{$this->database->postgres_db}";
|
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->uuid}:5432/{$this->database->postgres_db}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ class DockerCompose extends Component
|
|||||||
- type: volume
|
- type: volume
|
||||||
source: mydata
|
source: mydata
|
||||||
target: /data
|
target: /data
|
||||||
volume:
|
|
||||||
nocopy: true
|
|
||||||
- type: bind
|
- type: bind
|
||||||
source: ./var/lib/ghost/data
|
source: ./var/lib/ghost/data
|
||||||
target: /data
|
target: /data
|
||||||
|
|||||||
78
app/Http/Livewire/Project/New/DockerImage.php
Normal file
78
app/Http/Livewire/Project/New/DockerImage.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\New;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\StandaloneDocker;
|
||||||
|
use App\Models\SwarmDocker;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class DockerImage extends Component
|
||||||
|
{
|
||||||
|
public string $dockerImage = '';
|
||||||
|
public array $parameters;
|
||||||
|
public array $query;
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$this->validate([
|
||||||
|
'dockerImage' => 'required'
|
||||||
|
]);
|
||||||
|
$image = Str::of($this->dockerImage)->before(':');
|
||||||
|
if (Str::of($this->dockerImage)->contains(':')) {
|
||||||
|
$tag = Str::of($this->dockerImage)->after(':');
|
||||||
|
} else {
|
||||||
|
$tag = 'latest';
|
||||||
|
}
|
||||||
|
$destination_uuid = $this->query['destination'];
|
||||||
|
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||||
|
if (!$destination) {
|
||||||
|
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
||||||
|
}
|
||||||
|
if (!$destination) {
|
||||||
|
throw new \Exception('Destination not found. What?!');
|
||||||
|
}
|
||||||
|
$destination_class = $destination->getMorphClass();
|
||||||
|
|
||||||
|
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||||
|
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||||
|
ray($image,$tag);
|
||||||
|
$application = Application::create([
|
||||||
|
'name' => 'docker-image-' . new Cuid2(7),
|
||||||
|
'repository_project_id' => 0,
|
||||||
|
'git_repository' => "coollabsio/coolify",
|
||||||
|
'git_branch' => 'main',
|
||||||
|
'build_pack' => 'dockerimage',
|
||||||
|
'ports_exposes' => 80,
|
||||||
|
'docker_registry_image_name' => $image,
|
||||||
|
'docker_registry_image_tag' => $tag,
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination_class,
|
||||||
|
'health_check_enabled' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
|
$application->update([
|
||||||
|
'name' => 'docker-image-' . $application->uuid,
|
||||||
|
'fqdn' => $fqdn
|
||||||
|
]);
|
||||||
|
|
||||||
|
redirect()->route('project.application.configuration', [
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'environment_name' => $environment->name,
|
||||||
|
'project_uuid' => $project->uuid,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.new.docker-image');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ use App\Models\StandaloneDocker;
|
|||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class GithubPrivateRepositoryDeployKey extends Component
|
class GithubPrivateRepositoryDeployKey extends Component
|
||||||
{
|
{
|
||||||
@@ -29,7 +30,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
public string $repository_url;
|
public string $repository_url;
|
||||||
public string $branch;
|
public string $branch;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'repository_url' => 'required|url',
|
'repository_url' => 'required',
|
||||||
'branch' => 'required|string',
|
'branch' => 'required|string',
|
||||||
'port' => 'required|numeric',
|
'port' => 'required|numeric',
|
||||||
'is_static' => 'required|boolean',
|
'is_static' => 'required|boolean',
|
||||||
@@ -43,8 +44,8 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
'publish_directory' => 'Publish directory',
|
'publish_directory' => 'Publish directory',
|
||||||
];
|
];
|
||||||
private object $repository_url_parsed;
|
private object $repository_url_parsed;
|
||||||
private GithubApp|GitlabApp|null $git_source = null;
|
private GithubApp|GitlabApp|string $git_source = 'other';
|
||||||
private string $git_host;
|
private ?string $git_host = null;
|
||||||
private string $git_repository;
|
private string $git_repository;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -92,21 +93,38 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
|
|
||||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||||
$application_init = [
|
if ($this->git_source === 'other') {
|
||||||
'name' => generate_random_name(),
|
$application_init = [
|
||||||
'git_repository' => $this->git_repository,
|
'name' => generate_random_name(),
|
||||||
'git_branch' => $this->branch,
|
'git_repository' => $this->git_repository,
|
||||||
'git_full_url' => "git@$this->git_host:$this->git_repository.git",
|
'git_branch' => $this->branch,
|
||||||
'build_pack' => 'nixpacks',
|
'git_full_url' => $this->git_repository,
|
||||||
'ports_exposes' => $this->port,
|
'build_pack' => 'nixpacks',
|
||||||
'publish_directory' => $this->publish_directory,
|
'ports_exposes' => $this->port,
|
||||||
'environment_id' => $environment->id,
|
'publish_directory' => $this->publish_directory,
|
||||||
'destination_id' => $destination->id,
|
'environment_id' => $environment->id,
|
||||||
'destination_type' => $destination_class,
|
'destination_id' => $destination->id,
|
||||||
'private_key_id' => $this->private_key_id,
|
'destination_type' => $destination_class,
|
||||||
'source_id' => $this->git_source->id,
|
'private_key_id' => $this->private_key_id,
|
||||||
'source_type' => $this->git_source->getMorphClass()
|
];
|
||||||
];
|
} else {
|
||||||
|
$application_init = [
|
||||||
|
'name' => generate_random_name(),
|
||||||
|
'git_repository' => $this->git_repository,
|
||||||
|
'git_branch' => $this->branch,
|
||||||
|
'git_full_url' => "git@$this->git_host:$this->git_repository.git",
|
||||||
|
'build_pack' => 'nixpacks',
|
||||||
|
'ports_exposes' => $this->port,
|
||||||
|
'publish_directory' => $this->publish_directory,
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination_class,
|
||||||
|
'private_key_id' => $this->private_key_id,
|
||||||
|
'source_id' => $this->git_source->id,
|
||||||
|
'source_type' => $this->git_source->getMorphClass()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$application = Application::create($application_init);
|
$application = Application::create($application_init);
|
||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
@@ -134,10 +152,13 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
|
|
||||||
if ($this->git_host == 'github.com') {
|
if ($this->git_host == 'github.com') {
|
||||||
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
||||||
} elseif ($this->git_host == 'gitlab.com') {
|
return;
|
||||||
$this->git_source = GitlabApp::where('name', 'Public GitLab')->first();
|
|
||||||
} elseif ($this->git_host == 'bitbucket.org') {
|
|
||||||
// Not supported yet
|
|
||||||
}
|
}
|
||||||
|
if (Str::of($this->repository_url)->startsWith('http')) {
|
||||||
|
$this->git_host = $this->repository_url_parsed->getHost();
|
||||||
|
$this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2);
|
||||||
|
$this->git_repository = Str::finish("git@$this->git_host:$this->git_repository", '.git');
|
||||||
|
}
|
||||||
|
$this->git_source = 'other';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ class PublicGitRepository extends Component
|
|||||||
public string $git_branch = 'main';
|
public string $git_branch = 'main';
|
||||||
public int $rate_limit_remaining = 0;
|
public int $rate_limit_remaining = 0;
|
||||||
public $rate_limit_reset = 0;
|
public $rate_limit_reset = 0;
|
||||||
|
private object $repository_url_parsed;
|
||||||
|
public GithubApp|GitlabApp|string $git_source = 'other';
|
||||||
|
public string $git_host;
|
||||||
|
public string $git_repository;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'repository_url' => 'required|url',
|
'repository_url' => 'required|url',
|
||||||
'port' => 'required|numeric',
|
'port' => 'required|numeric',
|
||||||
@@ -38,10 +43,6 @@ class PublicGitRepository extends Component
|
|||||||
'is_static' => 'static',
|
'is_static' => 'static',
|
||||||
'publish_directory' => 'publish directory',
|
'publish_directory' => 'publish directory',
|
||||||
];
|
];
|
||||||
private object $repository_url_parsed;
|
|
||||||
private GithubApp|GitlabApp|null $git_source = null;
|
|
||||||
private string $git_host;
|
|
||||||
private string $git_repository;
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -64,18 +65,26 @@ class PublicGitRepository extends Component
|
|||||||
}
|
}
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
}
|
}
|
||||||
|
public function load_any_git()
|
||||||
|
{
|
||||||
|
$this->branch_found = true;
|
||||||
|
}
|
||||||
public function load_branch()
|
public function load_branch()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->branch_found = false;
|
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'repository_url' => 'required|url'
|
'repository_url' => 'required|url'
|
||||||
]);
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$this->branch_found = false;
|
||||||
$this->get_git_source();
|
$this->get_git_source();
|
||||||
$this->get_branch();
|
$this->get_branch();
|
||||||
$this->selected_branch = $this->git_branch;
|
$this->selected_branch = $this->git_branch;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
if (!$this->branch_found && $this->git_branch == 'main') {
|
if (!$this->branch_found && $this->git_branch == 'main') {
|
||||||
try {
|
try {
|
||||||
$this->git_branch = 'master';
|
$this->git_branch = 'master';
|
||||||
@@ -98,21 +107,23 @@ class PublicGitRepository extends Component
|
|||||||
|
|
||||||
if ($this->git_host == 'github.com') {
|
if ($this->git_host == 'github.com') {
|
||||||
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
||||||
} elseif ($this->git_host == 'gitlab.com') {
|
return;
|
||||||
$this->git_source = GitlabApp::where('name', 'Public GitLab')->first();
|
|
||||||
} elseif ($this->git_host == 'bitbucket.org') {
|
|
||||||
// Not supported yet
|
|
||||||
}
|
|
||||||
if (is_null($this->git_source)) {
|
|
||||||
throw new \Exception('Git source not found. What?!');
|
|
||||||
}
|
}
|
||||||
|
$this->git_repository = $this->repository_url;
|
||||||
|
$this->git_source = 'other';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_branch()
|
private function get_branch()
|
||||||
{
|
{
|
||||||
['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = git_api(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}");
|
if ($this->git_source === 'other') {
|
||||||
$this->rate_limit_reset = Carbon::parse((int)$this->rate_limit_reset)->format('Y-M-d H:i:s');
|
$this->branch_found = true;
|
||||||
$this->branch_found = true;
|
return;
|
||||||
|
}
|
||||||
|
if ($this->git_source->getMorphClass() === 'App\Models\GithubApp') {
|
||||||
|
['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = githubApi(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}");
|
||||||
|
$this->rate_limit_reset = Carbon::parse((int)$this->rate_limit_reset)->format('Y-M-d H:i:s');
|
||||||
|
$this->branch_found = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
@@ -123,9 +134,6 @@ class PublicGitRepository extends Component
|
|||||||
$project_uuid = $this->parameters['project_uuid'];
|
$project_uuid = $this->parameters['project_uuid'];
|
||||||
$environment_name = $this->parameters['environment_name'];
|
$environment_name = $this->parameters['environment_name'];
|
||||||
|
|
||||||
$this->get_git_source();
|
|
||||||
$this->git_branch = $this->selected_branch ?? $this->git_branch;
|
|
||||||
|
|
||||||
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||||
if (!$destination) {
|
if (!$destination) {
|
||||||
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
||||||
@@ -138,19 +146,34 @@ class PublicGitRepository extends Component
|
|||||||
$project = Project::where('uuid', $project_uuid)->first();
|
$project = Project::where('uuid', $project_uuid)->first();
|
||||||
$environment = $project->load(['environments'])->environments->where('name', $environment_name)->first();
|
$environment = $project->load(['environments'])->environments->where('name', $environment_name)->first();
|
||||||
|
|
||||||
$application_init = [
|
if ($this->git_source === 'other') {
|
||||||
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
$application_init = [
|
||||||
'git_repository' => $this->git_repository,
|
'name' => generate_random_name(),
|
||||||
'git_branch' => $this->git_branch,
|
'git_repository' => $this->git_repository,
|
||||||
'build_pack' => 'nixpacks',
|
'git_branch' => $this->git_branch,
|
||||||
'ports_exposes' => $this->port,
|
'build_pack' => 'nixpacks',
|
||||||
'publish_directory' => $this->publish_directory,
|
'ports_exposes' => $this->port,
|
||||||
'environment_id' => $environment->id,
|
'publish_directory' => $this->publish_directory,
|
||||||
'destination_id' => $destination->id,
|
'environment_id' => $environment->id,
|
||||||
'destination_type' => $destination_class,
|
'destination_id' => $destination->id,
|
||||||
'source_id' => $this->git_source->id,
|
'destination_type' => $destination_class,
|
||||||
'source_type' => $this->git_source->getMorphClass()
|
];
|
||||||
];
|
} else {
|
||||||
|
$application_init = [
|
||||||
|
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
||||||
|
'git_repository' => $this->git_repository,
|
||||||
|
'git_branch' => $this->git_branch,
|
||||||
|
'build_pack' => 'nixpacks',
|
||||||
|
'ports_exposes' => $this->port,
|
||||||
|
'publish_directory' => $this->publish_directory,
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination_class,
|
||||||
|
'source_id' => $this->git_source->id,
|
||||||
|
'source_type' => $this->git_source->getMorphClass()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$application = Application::create($application_init);
|
$application = Application::create($application_init);
|
||||||
|
|
||||||
@@ -159,7 +182,6 @@ class PublicGitRepository extends Component
|
|||||||
|
|
||||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
$application->fqdn = $fqdn;
|
$application->fqdn = $fqdn;
|
||||||
$application->name = generate_application_name($this->git_repository, $this->git_branch, $application->uuid);
|
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
return redirect()->route('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
|
|||||||
@@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\New;
|
namespace App\Http\Livewire\Project\New;
|
||||||
|
|
||||||
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Countable;
|
use Countable;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\File;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Select extends Component
|
class Select extends Component
|
||||||
@@ -24,7 +23,8 @@ class Select extends Component
|
|||||||
public Collection|array $services = [];
|
public Collection|array $services = [];
|
||||||
public bool $loadingServices = true;
|
public bool $loadingServices = true;
|
||||||
public bool $loading = false;
|
public bool $loading = false;
|
||||||
|
public $environments = [];
|
||||||
|
public ?string $selectedEnvironment = null;
|
||||||
public ?string $existingPostgresqlUrl = null;
|
public ?string $existingPostgresqlUrl = null;
|
||||||
|
|
||||||
protected $queryString = [
|
protected $queryString = [
|
||||||
@@ -37,8 +37,18 @@ class Select extends Component
|
|||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$this->existingPostgresqlUrl = 'postgres://coolify:password@coolify-db:5432';
|
$this->existingPostgresqlUrl = 'postgres://coolify:password@coolify-db:5432';
|
||||||
}
|
}
|
||||||
|
$projectUuid = data_get($this->parameters, 'project_uuid');
|
||||||
|
$this->environments = Project::whereUuid($projectUuid)->first()->environments;
|
||||||
|
$this->selectedEnvironment = data_get($this->parameters, 'environment_name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updatedSelectedEnvironment()
|
||||||
|
{
|
||||||
|
return redirect()->route('project.resources.new', [
|
||||||
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
|
'environment_name' => $this->selectedEnvironment,
|
||||||
|
]);
|
||||||
|
}
|
||||||
// public function addExistingPostgresql()
|
// public function addExistingPostgresql()
|
||||||
// {
|
// {
|
||||||
// try {
|
// try {
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||||
|
|
||||||
$port = get_port_from_dockerfile($this->dockerfile);
|
$port = get_port_from_dockerfile($this->dockerfile);
|
||||||
|
if (!$port) {
|
||||||
|
$port = 80;
|
||||||
|
}
|
||||||
$application = Application::create([
|
$application = Application::create([
|
||||||
'name' => 'dockerfile-' . new Cuid2(7),
|
'name' => 'dockerfile-' . new Cuid2(7),
|
||||||
'repository_project_id' => 0,
|
'repository_project_id' => 0,
|
||||||
@@ -56,6 +59,7 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'destination_id' => $destination->id,
|
'destination_id' => $destination->id,
|
||||||
'destination_type' => $destination_class,
|
'destination_type' => $destination_class,
|
||||||
|
'health_check_enabled' => false,
|
||||||
'source_id' => 0,
|
'source_id' => 0,
|
||||||
'source_type' => GithubApp::class
|
'source_type' => GithubApp::class
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ class Index extends Component
|
|||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
$this->query = request()->query();
|
$this->query = request()->query();
|
||||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||||
$this->refreshStack();
|
$this->applications = $this->service->applications->sort();
|
||||||
|
$this->databases = $this->service->databases->sort();
|
||||||
}
|
}
|
||||||
public function saveCompose($raw)
|
public function saveCompose($raw)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class Navbar extends Component
|
|||||||
}
|
}
|
||||||
public function serviceStatusUpdated()
|
public function serviceStatusUpdated()
|
||||||
{
|
{
|
||||||
ray('serviceStatusUpdated');
|
|
||||||
$this->check_status();
|
$this->check_status();
|
||||||
}
|
}
|
||||||
public function check_status()
|
public function check_status()
|
||||||
|
|||||||
@@ -33,9 +33,6 @@ class Show extends Component
|
|||||||
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
||||||
$this->serviceDatabase->getFilesFromServer();
|
$this->serviceDatabase->getFilesFromServer();
|
||||||
}
|
}
|
||||||
if (is_null($service)) {
|
|
||||||
throw new \Exception("Service not found.");
|
|
||||||
}
|
|
||||||
} catch(\Throwable $e) {
|
} catch(\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
35
app/Http/Livewire/Project/Service/Storage.php
Normal file
35
app/Http/Livewire/Project/Service/Storage.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\LocalPersistentVolume;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Storage extends Component
|
||||||
|
{
|
||||||
|
protected $listeners = ['addNewVolume'];
|
||||||
|
public $resource;
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.storage');
|
||||||
|
}
|
||||||
|
public function addNewVolume($data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
LocalPersistentVolume::create([
|
||||||
|
'name' => $data['name'],
|
||||||
|
'mount_path' => $data['mount_path'],
|
||||||
|
'host_path' => $data['host_path'],
|
||||||
|
'resource_id' => $this->resource->id,
|
||||||
|
'resource_type' => $this->resource->getMorphClass(),
|
||||||
|
]);
|
||||||
|
$this->resource->refresh();
|
||||||
|
$this->emit('success', 'Storage added successfully');
|
||||||
|
$this->emit('clearAddStorage');
|
||||||
|
$this->emit('refreshStorages');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,9 @@ class Danger extends Component
|
|||||||
$destination = $this->resource->destination->getMorphClass()::where('id', $this->resource->destination->id)->first();
|
$destination = $this->resource->destination->getMorphClass()::where('id', $this->resource->destination->id)->first();
|
||||||
$server = $destination->server;
|
$server = $destination->server;
|
||||||
}
|
}
|
||||||
instant_remote_process(["docker rm -f {$this->resource->uuid}"], $server);
|
if ($this->resource->destination->server->isFunctional()) {
|
||||||
|
instant_remote_process(["docker rm -f {$this->resource->uuid}"], $server);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->resource->delete();
|
$this->resource->delete();
|
||||||
return redirect()->route('project.resources', [
|
return redirect()->route('project.resources', [
|
||||||
|
|||||||
40
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
40
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class GetLogs extends Component
|
||||||
|
{
|
||||||
|
public string $outputs = '';
|
||||||
|
public string $errors = '';
|
||||||
|
public Server $server;
|
||||||
|
public ?string $container = null;
|
||||||
|
public ?bool $streamLogs = false;
|
||||||
|
public int $numberOfLines = 100;
|
||||||
|
public function doSomethingWithThisChunkOfOutput($output)
|
||||||
|
{
|
||||||
|
$this->outputs .= $output;
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function getLogs($refresh = false)
|
||||||
|
{
|
||||||
|
if ($this->container) {
|
||||||
|
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
|
||||||
|
if ($refresh) {
|
||||||
|
$this->outputs = '';
|
||||||
|
}
|
||||||
|
Process::run($sshCommand, function (string $type, string $output) {
|
||||||
|
$this->doSomethingWithThisChunkOfOutput($output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.get-logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ class HealthChecks extends Component
|
|||||||
|
|
||||||
public $resource;
|
public $resource;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
|
'resource.health_check_enabled' => 'boolean',
|
||||||
'resource.health_check_path' => 'string',
|
'resource.health_check_path' => 'string',
|
||||||
'resource.health_check_port' => 'nullable|string',
|
'resource.health_check_port' => 'nullable|string',
|
||||||
'resource.health_check_host' => 'string',
|
'resource.health_check_host' => 'string',
|
||||||
@@ -22,12 +23,19 @@ class HealthChecks extends Component
|
|||||||
'resource.health_check_start_period' => 'integer',
|
'resource.health_check_start_period' => 'integer',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->resource->save();
|
||||||
|
$this->emit('success', 'Health check updated.');
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->resource->save();
|
$this->resource->save();
|
||||||
$this->emit('saved');
|
$this->emit('success', 'Health check updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
53
app/Http/Livewire/Project/Shared/Logs.php
Normal file
53
app/Http/Livewire/Project/Shared/Logs.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Logs extends Component
|
||||||
|
{
|
||||||
|
public ?string $type = null;
|
||||||
|
public Application|StandalonePostgresql|Service $resource;
|
||||||
|
public Server $server;
|
||||||
|
public ?string $container = null;
|
||||||
|
public $parameters;
|
||||||
|
public $query;
|
||||||
|
public $status;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$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;
|
||||||
|
$this->server = $this->resource->destination->server;
|
||||||
|
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id);
|
||||||
|
if ($containers->count() > 0) {
|
||||||
|
$this->container = data_get($containers[0], 'Names');
|
||||||
|
}
|
||||||
|
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||||
|
$this->type = 'database';
|
||||||
|
$this->resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->firstOrFail();
|
||||||
|
$this->status = $this->resource->status;
|
||||||
|
$this->server = $this->resource->destination->server;
|
||||||
|
$this->container = $this->resource->uuid;
|
||||||
|
} else if (data_get($this->parameters, 'service_uuid')) {
|
||||||
|
$this->type = 'service';
|
||||||
|
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
||||||
|
$this->status = $this->resource->status;
|
||||||
|
$this->server = $this->resource->server;
|
||||||
|
$this->container = data_get($this->parameters, 'service_name') . '-' . $this->resource->uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class Add extends Component
|
class Add extends Component
|
||||||
{
|
{
|
||||||
|
public $uuid;
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public string $name;
|
public string $name;
|
||||||
public string $mount_path;
|
public string $mount_path;
|
||||||
@@ -31,8 +32,9 @@ class Add extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->emitUp('submit', [
|
$name = $this->uuid . '-' . $this->name;
|
||||||
'name' => $this->name,
|
$this->emit('addNewVolume', [
|
||||||
|
'name' => $name,
|
||||||
'mount_path' => $this->mount_path,
|
'mount_path' => $this->mount_path,
|
||||||
'host_path' => $this->host_path,
|
'host_path' => $this->host_path,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -8,28 +8,10 @@ use Livewire\Component;
|
|||||||
class All extends Component
|
class All extends Component
|
||||||
{
|
{
|
||||||
public $resource;
|
public $resource;
|
||||||
protected $listeners = ['refreshStorages', 'submit'];
|
protected $listeners = ['refreshStorages'];
|
||||||
|
|
||||||
public function refreshStorages()
|
public function refreshStorages()
|
||||||
{
|
{
|
||||||
$this->resource->refresh();
|
$this->resource->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit($data)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
LocalPersistentVolume::create([
|
|
||||||
'name' => $data['name'],
|
|
||||||
'mount_path' => $data['mount_path'],
|
|
||||||
'host_path' => $data['host_path'],
|
|
||||||
'resource_id' => $this->resource->id,
|
|
||||||
'resource_type' => $this->resource->getMorphClass(),
|
|
||||||
]);
|
|
||||||
$this->resource->refresh();
|
|
||||||
$this->emit('success', 'Storage added successfully');
|
|
||||||
$this->emit('clearAddStorage');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class Show extends Component
|
|||||||
public LocalPersistentVolume $storage;
|
public LocalPersistentVolume $storage;
|
||||||
public bool $isReadOnly = false;
|
public bool $isReadOnly = false;
|
||||||
public ?string $modalId = null;
|
public ?string $modalId = null;
|
||||||
public ?string $realName = null;
|
public bool $isFirst = true;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'storage.name' => 'required|string',
|
'storage.name' => 'required|string',
|
||||||
@@ -26,11 +26,6 @@ class Show extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if ($this->storage->resource_type === 'App\Models\ServiceApplication' || $this->storage->resource_type === 'App\Models\ServiceDatabase') {
|
|
||||||
$this->realName = "{$this->storage->service->service->uuid}_{$this->storage->name}";
|
|
||||||
} else {
|
|
||||||
$this->realName = $this->storage->name;
|
|
||||||
}
|
|
||||||
$this->modalId = new Cuid2(7);
|
$this->modalId = new Cuid2(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
29
app/Http/Livewire/Server/Create.php
Normal file
29
app/Http/Livewire/Server/Create.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Server;
|
||||||
|
|
||||||
|
use App\Models\PrivateKey;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Create extends Component
|
||||||
|
{
|
||||||
|
public $private_keys = [];
|
||||||
|
public bool $limit_reached = false;
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
|
||||||
|
if (!isCloud()) {
|
||||||
|
$this->limit_reached = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$team = currentTeam();
|
||||||
|
$servers = $team->servers->count();
|
||||||
|
['serverLimit' => $serverLimit] = $team->limits;
|
||||||
|
|
||||||
|
$this->limit_reached = $servers >= $serverLimit;
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.create');
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/Http/Livewire/Server/Destination/Show.php
Normal file
28
app/Http/Livewire/Server/Destination/Show.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Server\Destination;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Show extends Component
|
||||||
|
{
|
||||||
|
public ?Server $server = null;
|
||||||
|
public $parameters = [];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
try {
|
||||||
|
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||||
|
if (is_null($this->server)) {
|
||||||
|
return redirect()->route('server.all');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.destination.show');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,11 +11,12 @@ class Form extends Component
|
|||||||
{
|
{
|
||||||
use AuthorizesRequests;
|
use AuthorizesRequests;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public $uptime;
|
public bool $isValidConnection = false;
|
||||||
public $dockerVersion;
|
public bool $isValidDocker = false;
|
||||||
public string|null $wildcard_domain = null;
|
public ?string $wildcard_domain = null;
|
||||||
public int $cleanup_after_percentage;
|
public int $cleanup_after_percentage;
|
||||||
public bool $dockerInstallationStarted = false;
|
public bool $dockerInstallationStarted = false;
|
||||||
|
protected $listeners = ['serverRefresh'];
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'server.name' => 'required|min:6',
|
'server.name' => 'required|min:6',
|
||||||
@@ -44,37 +45,49 @@ class Form extends Component
|
|||||||
$this->wildcard_domain = $this->server->settings->wildcard_domain;
|
$this->wildcard_domain = $this->server->settings->wildcard_domain;
|
||||||
$this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
|
$this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
|
||||||
}
|
}
|
||||||
public function instantSave() {
|
public function serverRefresh() {
|
||||||
|
$this->validateServer();
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
refresh_server_connection($this->server->privateKey);
|
refresh_server_connection($this->server->privateKey);
|
||||||
$this->validateServer();
|
$this->validateServer();
|
||||||
$this->server->settings->save();
|
$this->server->settings->save();
|
||||||
}
|
}
|
||||||
public function installDocker()
|
public function installDocker()
|
||||||
{
|
{
|
||||||
|
$this->emit('installDocker');
|
||||||
$this->dockerInstallationStarted = true;
|
$this->dockerInstallationStarted = true;
|
||||||
$activity = resolve(InstallDocker::class)($this->server);
|
$activity = InstallDocker::run($this->server);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validateServer()
|
public function validateServer($install = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true);
|
$uptime = $this->server->validateConnection();
|
||||||
if ($uptime) {
|
if ($uptime) {
|
||||||
$this->uptime = $uptime;
|
$install && $this->emit('success', 'Server is reachable.');
|
||||||
$this->emit('success', 'Server is reachable.');
|
|
||||||
} else {
|
} else {
|
||||||
$this->emit('error', 'Server is not reachable.');
|
$install &&$this->emit('error', 'Server is not reachable. Please check your connection and private key configuration.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($dockerVersion) {
|
$dockerInstalled = $this->server->validateDockerEngine();
|
||||||
$this->dockerVersion = $dockerVersion;
|
if ($dockerInstalled) {
|
||||||
$this->emit('success', 'Docker Engine 23+ is installed!');
|
$install && $this->emit('success', 'Docker Engine is installed.<br> Checking version.');
|
||||||
} else {
|
} else {
|
||||||
$this->emit('error', 'No Docker Engine or older than 23 version installed.');
|
$install && $this->installDocker();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$dockerVersion = $this->server->validateDockerEngineVersion();
|
||||||
|
if ($dockerVersion) {
|
||||||
|
$install && $this->emit('success', 'Docker Engine version is 23+.');
|
||||||
|
} else {
|
||||||
|
$install && $this->installDocker();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this, customErrorMessage: "Server is not reachable: ");
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->emit('proxyStatusUpdated');
|
$this->emit('proxyStatusUpdated');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ class ByIp extends Component
|
|||||||
{
|
{
|
||||||
public $private_keys;
|
public $private_keys;
|
||||||
public $limit_reached;
|
public $limit_reached;
|
||||||
public int|null $private_key_id = null;
|
public ?int $private_key_id = null;
|
||||||
public $new_private_key_name;
|
public $new_private_key_name;
|
||||||
public $new_private_key_description;
|
public $new_private_key_description;
|
||||||
public $new_private_key_value;
|
public $new_private_key_value;
|
||||||
|
|
||||||
public string $name;
|
public string $name;
|
||||||
public string|null $description = null;
|
public ?string $description = null;
|
||||||
public string $ip;
|
public string $ip;
|
||||||
public string $user = 'root';
|
public string $user = 'root';
|
||||||
public int $port = 22;
|
public int $port = 22;
|
||||||
@@ -26,16 +26,16 @@ class ByIp extends Component
|
|||||||
protected $rules = [
|
protected $rules = [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'description' => 'nullable|string',
|
'description' => 'nullable|string',
|
||||||
'ip' => 'required',
|
'ip' => 'required|ip',
|
||||||
'user' => 'required|string',
|
'user' => 'required|string',
|
||||||
'port' => 'required|integer',
|
'port' => 'required|integer',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'name' => 'name',
|
'name' => 'Name',
|
||||||
'description' => 'description',
|
'description' => 'Description',
|
||||||
'ip' => 'ip',
|
'ip' => 'IP Address',
|
||||||
'user' => 'user',
|
'user' => 'User',
|
||||||
'port' => 'port',
|
'port' => 'Port',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
|
|||||||
31
app/Http/Livewire/Server/PrivateKey/Show.php
Normal file
31
app/Http/Livewire/Server/PrivateKey/Show.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Server\PrivateKey;
|
||||||
|
|
||||||
|
use App\Models\PrivateKey;
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Show extends Component
|
||||||
|
{
|
||||||
|
public ?Server $server = null;
|
||||||
|
public $privateKeys = [];
|
||||||
|
public $parameters = [];
|
||||||
|
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.all');
|
||||||
|
}
|
||||||
|
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.private-key.show');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ class Deploy extends Component
|
|||||||
public Server $server;
|
public Server $server;
|
||||||
public bool $traefikDashboardAvailable = false;
|
public bool $traefikDashboardAvailable = false;
|
||||||
public ?string $currentRoute = null;
|
public ?string $currentRoute = null;
|
||||||
protected $listeners = ['proxyStatusUpdated', 'traefikDashboardAvailable'];
|
protected $listeners = ['proxyStatusUpdated', 'traefikDashboardAvailable', 'serverRefresh' => 'proxyStatusUpdated'];
|
||||||
|
|
||||||
public function mount() {
|
public function mount() {
|
||||||
$this->currentRoute = request()->route()->getName();
|
$this->currentRoute = request()->route()->getName();
|
||||||
|
|||||||
28
app/Http/Livewire/Server/Proxy/Logs.php
Normal file
28
app/Http/Livewire/Server/Proxy/Logs.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Server\Proxy;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Logs extends Component
|
||||||
|
{
|
||||||
|
public ?Server $server = null;
|
||||||
|
public $parameters = [];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
try {
|
||||||
|
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||||
|
if (is_null($this->server)) {
|
||||||
|
return redirect()->route('server.all');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.proxy.logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Http/Livewire/Server/Proxy/Show.php
Normal file
33
app/Http/Livewire/Server/Proxy/Show.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Server\Proxy;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Show extends Component
|
||||||
|
{
|
||||||
|
public ?Server $server = null;
|
||||||
|
public $parameters = [];
|
||||||
|
protected $listeners = ['proxyStatusUpdated'];
|
||||||
|
public function proxyStatusUpdated()
|
||||||
|
{
|
||||||
|
$this->server->refresh();
|
||||||
|
}
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
try {
|
||||||
|
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||||
|
if (is_null($this->server)) {
|
||||||
|
return redirect()->route('server.all');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.server.proxy.show');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,9 @@ class Status extends Component
|
|||||||
}
|
}
|
||||||
public function getProxyStatusWithNoti()
|
public function getProxyStatusWithNoti()
|
||||||
{
|
{
|
||||||
$this->emit('success', 'Refreshed proxy status.');
|
if ($this->server->isFunctional()) {
|
||||||
$this->getProxyStatus();
|
$this->emit('success', 'Refreshed proxy status.');
|
||||||
|
$this->getProxyStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ class Show extends Component
|
|||||||
{
|
{
|
||||||
use AuthorizesRequests;
|
use AuthorizesRequests;
|
||||||
public ?Server $server = null;
|
public ?Server $server = null;
|
||||||
|
public $parameters = [];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
$this->server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||||
if (is_null($this->server)) {
|
if (is_null($this->server)) {
|
||||||
@@ -21,6 +23,10 @@ class Show extends Component
|
|||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$this->emit('serverRefresh');
|
||||||
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.server.show');
|
return view('livewire.server.show');
|
||||||
|
|||||||
@@ -35,31 +35,13 @@ class ShowPrivateKey extends Component
|
|||||||
public function checkConnection()
|
public function checkConnection()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true);
|
$uptime = $this->server->validateConnection();
|
||||||
if ($uptime) {
|
if ($uptime) {
|
||||||
$this->server->settings->update([
|
$this->emit('success', 'Server is reachable.');
|
||||||
'is_reachable' => true
|
|
||||||
]);
|
|
||||||
$this->emit('success', 'Server is reachable with this private key.');
|
|
||||||
} else {
|
} else {
|
||||||
$this->server->settings->update([
|
$this->emit('error', 'Server is not reachable. Please check your connection and private key configuration.');
|
||||||
'is_reachable' => false,
|
|
||||||
'is_usable' => false
|
|
||||||
]);
|
|
||||||
$this->emit('error', 'Server is not reachable with this private key.');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($dockerVersion) {
|
|
||||||
$this->server->settings->update([
|
|
||||||
'is_usable' => true
|
|
||||||
]);
|
|
||||||
$this->emit('success', 'Server is usable for Coolify.');
|
|
||||||
} else {
|
|
||||||
$this->server->settings->update([
|
|
||||||
'is_usable' => false
|
|
||||||
]);
|
|
||||||
$this->emit('error', 'Old (lower than 23) or no Docker version detected. Install Docker Engine on the General tab.');
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
30
app/Http/Livewire/Subscription/Show.php
Normal file
30
app/Http/Livewire/Subscription/Show.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Subscription;
|
||||||
|
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Show extends Component
|
||||||
|
{
|
||||||
|
public InstanceSettings $settings;
|
||||||
|
public bool $alreadySubscribed = false;
|
||||||
|
public function mount() {
|
||||||
|
if (!isCloud()) {
|
||||||
|
return redirect('/');
|
||||||
|
}
|
||||||
|
$this->settings = InstanceSettings::get();
|
||||||
|
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
||||||
|
}
|
||||||
|
public function stripeCustomerPortal() {
|
||||||
|
$session = getStripeCustomerPortalSession(currentTeam());
|
||||||
|
if (is_null($session)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return redirect($session->url);
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.subscription.show')->layout('layouts.subscription');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,7 +64,7 @@ class Create extends Component
|
|||||||
}
|
}
|
||||||
$this->storage->team_id = currentTeam()->id;
|
$this->storage->team_id = currentTeam()->id;
|
||||||
$this->storage->testConnection();
|
$this->storage->testConnection();
|
||||||
$this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
$this->storage->is_usable = true;
|
||||||
$this->storage->save();
|
$this->storage->save();
|
||||||
return redirect()->route('team.storages.show', $this->storage->uuid);
|
return redirect()->route('team.storages.show', $this->storage->uuid);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class Form extends Component
|
|||||||
{
|
{
|
||||||
public S3Storage $storage;
|
public S3Storage $storage;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
|
'storage.is_usable' => 'nullable|boolean',
|
||||||
'storage.name' => 'nullable|min:3|max:255',
|
'storage.name' => 'nullable|min:3|max:255',
|
||||||
'storage.description' => 'nullable|min:3|max:255',
|
'storage.description' => 'nullable|min:3|max:255',
|
||||||
'storage.region' => 'required|max:255',
|
'storage.region' => 'required|max:255',
|
||||||
@@ -18,6 +19,7 @@ class Form extends Component
|
|||||||
'storage.endpoint' => 'required|url|max:255',
|
'storage.endpoint' => 'required|url|max:255',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
|
'storage.is_usable' => 'Is Usable',
|
||||||
'storage.name' => 'Name',
|
'storage.name' => 'Name',
|
||||||
'storage.description' => 'Description',
|
'storage.description' => 'Description',
|
||||||
'storage.region' => 'Region',
|
'storage.region' => 'Region',
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ namespace App\Http\Livewire;
|
|||||||
use App\Actions\Server\UpdateCoolify;
|
use App\Actions\Server\UpdateCoolify;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Masmerise\Toaster\Toaster;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
|
|
||||||
class Upgrade extends Component
|
class Upgrade extends Component
|
||||||
{
|
{
|
||||||
|
use WithRateLimiting;
|
||||||
public bool $showProgress = false;
|
public bool $showProgress = false;
|
||||||
public bool $isUpgradeAvailable = false;
|
public bool $isUpgradeAvailable = false;
|
||||||
public string $latestVersion = '';
|
public string $latestVersion = '';
|
||||||
@@ -31,6 +32,7 @@ class Upgrade extends Component
|
|||||||
public function upgrade()
|
public function upgrade()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$this->rateLimit(1, 30);
|
||||||
if ($this->showProgress) {
|
if ($this->showProgress) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
26
app/Http/Livewire/VerifyEmail.php
Normal file
26
app/Http/Livewire/VerifyEmail.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
|
|
||||||
|
class VerifyEmail extends Component
|
||||||
|
{
|
||||||
|
use WithRateLimiting;
|
||||||
|
public function again() {
|
||||||
|
try {
|
||||||
|
$this->rateLimit(1, 300);
|
||||||
|
auth()->user()->sendVerificationEmail();
|
||||||
|
$this->emit('success', 'Email verification link sent!');
|
||||||
|
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
ray($e);
|
||||||
|
return handleError($e,$this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.verify-email');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,9 @@ class Index extends Component
|
|||||||
}
|
}
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
if (config('coolify.waitlist') == false) {
|
||||||
|
return redirect()->route('register');
|
||||||
|
}
|
||||||
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
||||||
$this->users = User::count();
|
$this->users = User::count();
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
|
|||||||
45
app/Http/Middleware/DecideWhatToDoWithUser.php
Normal file
45
app/Http/Middleware/DecideWhatToDoWithUser.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class DecideWhatToDoWithUser
|
||||||
|
{
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
if (!auth()->user() || !isCloud() || isInstanceAdmin()) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
if (!auth()->user()->hasVerifiedEmail()) {
|
||||||
|
if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
return redirect('/verify');
|
||||||
|
}
|
||||||
|
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
||||||
|
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
||||||
|
if (Str::startsWith($request->path(), 'invitations')) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
return redirect('subscription');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
||||||
|
if (Str::startsWith($request->path(), 'invitations')) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
return redirect('boarding');
|
||||||
|
}
|
||||||
|
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
|
||||||
|
return redirect('/');
|
||||||
|
}
|
||||||
|
if (isSubscriptionActive() && $request->path() === 'subscription') {
|
||||||
|
return redirect('/');
|
||||||
|
}
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,16 +45,20 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private string $commit;
|
private string $commit;
|
||||||
private bool $force_rebuild;
|
private bool $force_rebuild;
|
||||||
|
|
||||||
private GithubApp|GitlabApp $source;
|
private ?string $dockerImage = null;
|
||||||
|
private ?string $dockerImageTag = null;
|
||||||
|
|
||||||
|
private GithubApp|GitlabApp|string $source = 'other';
|
||||||
private StandaloneDocker|SwarmDocker $destination;
|
private StandaloneDocker|SwarmDocker $destination;
|
||||||
private Server $server;
|
private Server $server;
|
||||||
private ApplicationPreview|null $preview = null;
|
private ApplicationPreview|null $preview = null;
|
||||||
|
|
||||||
private string $container_name;
|
private string $container_name;
|
||||||
private string|null $currently_running_container_name = null;
|
private string|null $currently_running_container_name = null;
|
||||||
|
private string $basedir;
|
||||||
private string $workdir;
|
private string $workdir;
|
||||||
|
private ?string $build_pack = null;
|
||||||
private string $configuration_dir;
|
private string $configuration_dir;
|
||||||
private string $build_workdir;
|
|
||||||
private string $build_image_name;
|
private string $build_image_name;
|
||||||
private string $production_image_name;
|
private string $production_image_name;
|
||||||
private bool $is_debug_enabled;
|
private bool $is_debug_enabled;
|
||||||
@@ -62,6 +66,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private $env_args;
|
private $env_args;
|
||||||
private $docker_compose;
|
private $docker_compose;
|
||||||
private $docker_compose_base64;
|
private $docker_compose_base64;
|
||||||
|
private string $dockerfile_location = '/Dockerfile';
|
||||||
|
|
||||||
private $log_model;
|
private $log_model;
|
||||||
private Collection $saved_outputs;
|
private Collection $saved_outputs;
|
||||||
@@ -73,6 +78,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
|
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
|
||||||
$this->log_model = $this->application_deployment_queue;
|
$this->log_model = $this->application_deployment_queue;
|
||||||
$this->application = Application::find($this->application_deployment_queue->application_id);
|
$this->application = Application::find($this->application_deployment_queue->application_id);
|
||||||
|
$this->build_pack = data_get($this->application, 'build_pack');
|
||||||
|
|
||||||
$this->application_deployment_queue_id = $application_deployment_queue_id;
|
$this->application_deployment_queue_id = $application_deployment_queue_id;
|
||||||
$this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
|
$this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
|
||||||
@@ -80,16 +86,20 @@ 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->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first();
|
$source = data_get($this->application, 'source');
|
||||||
|
if ($source) {
|
||||||
|
$this->source = $source->getMorphClass()::where('id', $this->application->source->id)->first();
|
||||||
|
}
|
||||||
$this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first();
|
$this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first();
|
||||||
$this->server = $this->destination->server;
|
$this->server = $this->destination->server;
|
||||||
|
|
||||||
$this->workdir = "/artifacts/{$this->deployment_uuid}";
|
$this->basedir = "/artifacts/{$this->deployment_uuid}";
|
||||||
|
$this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/');
|
||||||
$this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}";
|
$this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}";
|
||||||
$this->build_workdir = "{$this->workdir}" . rtrim($this->application->base_directory, '/');
|
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
|
|
||||||
$this->container_name = generateApplicationContainerName($this->application);
|
ray($this->basedir, $this->workdir);
|
||||||
|
$this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
|
||||||
savePrivateKeyToFs($this->server);
|
savePrivateKeyToFs($this->server);
|
||||||
$this->saved_outputs = collect();
|
$this->saved_outputs = collect();
|
||||||
|
|
||||||
@@ -97,7 +107,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
$this->preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pull_request_id);
|
$this->preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pull_request_id);
|
||||||
if ($this->application->fqdn) {
|
if ($this->application->fqdn) {
|
||||||
$preview_fqdn = getFqdnWithoutPort(data_get($this->preview, 'fqdn'));
|
if (data_get($this->preview, 'fqdn')) {
|
||||||
|
$preview_fqdn = getFqdnWithoutPort(data_get($this->preview, 'fqdn'));
|
||||||
|
}
|
||||||
$template = $this->application->preview_url_template;
|
$template = $this->application->preview_url_template;
|
||||||
$url = Url::fromString($this->application->fqdn);
|
$url = Url::fromString($this->application->fqdn);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
@@ -129,6 +141,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
try {
|
try {
|
||||||
if ($this->application->dockerfile) {
|
if ($this->application->dockerfile) {
|
||||||
$this->deploy_simple_dockerfile();
|
$this->deploy_simple_dockerfile();
|
||||||
|
} else if ($this->application->build_pack === 'dockerimage') {
|
||||||
|
$this->deploy_dockerimage();
|
||||||
|
} else if ($this->application->build_pack === 'dockerfile') {
|
||||||
|
$this->deploy_dockerfile();
|
||||||
} else {
|
} else {
|
||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
$this->deploy_pull_request();
|
$this->deploy_pull_request();
|
||||||
@@ -167,6 +183,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deploy_docker_compose()
|
private function deploy_docker_compose()
|
||||||
{
|
{
|
||||||
$dockercompose_base64 = base64_encode($this->application->dockercompose);
|
$dockercompose_base64 = base64_encode($this->application->dockercompose);
|
||||||
@@ -231,7 +248,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
);
|
);
|
||||||
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
$this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
||||||
ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->generate_build_env_variables();
|
$this->generate_build_env_variables();
|
||||||
$this->add_build_env_variables_to_dockerfile();
|
$this->add_build_env_variables_to_dockerfile();
|
||||||
@@ -239,6 +256,50 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->rolling_update();
|
$this->rolling_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function deploy_dockerimage()
|
||||||
|
{
|
||||||
|
$this->dockerImage = $this->application->docker_registry_image_name;
|
||||||
|
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
||||||
|
ray("echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag}.'");
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"echo 'Starting deployment of {$this->dockerImage}:{$this->dockerImageTag}.'"
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$this->production_image_name = Str::lower("{$this->dockerImage}:{$this->dockerImageTag}");
|
||||||
|
$this->prepare_builder_image();
|
||||||
|
$this->generate_compose_file();
|
||||||
|
$this->rolling_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function deploy_dockerfile()
|
||||||
|
{
|
||||||
|
if (data_get($this->application, 'dockerfile_location')) {
|
||||||
|
$this->dockerfile_location = $this->application->dockerfile_location;
|
||||||
|
}
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}.'"
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$this->prepare_builder_image();
|
||||||
|
$this->clone_repository();
|
||||||
|
$this->set_base_dir();
|
||||||
|
$tag = Str::of("{$this->commit}-{$this->application->id}-{$this->pull_request_id}");
|
||||||
|
if (strlen($tag) > 128) {
|
||||||
|
$tag = $tag->substr(0, 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->build_image_name = Str::lower("{$this->application->git_repository}:{$tag}-build");
|
||||||
|
$this->production_image_name = Str::lower("{$this->application->uuid}:{$tag}");
|
||||||
|
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
||||||
|
$this->cleanup_git();
|
||||||
|
$this->generate_compose_file();
|
||||||
|
$this->generate_build_env_variables();
|
||||||
|
$this->add_build_env_variables_to_dockerfile();
|
||||||
|
// $this->build_image();
|
||||||
|
$this->rolling_update();
|
||||||
|
}
|
||||||
private function deploy()
|
private function deploy()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
@@ -248,7 +309,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
);
|
);
|
||||||
$this->prepare_builder_image();
|
$this->prepare_builder_image();
|
||||||
$this->clone_repository();
|
$this->clone_repository();
|
||||||
|
$this->set_base_dir();
|
||||||
$tag = Str::of("{$this->commit}-{$this->application->id}-{$this->pull_request_id}");
|
$tag = Str::of("{$this->commit}-{$this->application->id}-{$this->pull_request_id}");
|
||||||
if (strlen($tag) > 128) {
|
if (strlen($tag) > 128) {
|
||||||
$tag = $tag->substr(0, 128);
|
$tag = $tag->substr(0, 128);
|
||||||
@@ -256,7 +317,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
$this->build_image_name = Str::lower("{$this->application->git_repository}:{$tag}-build");
|
$this->build_image_name = Str::lower("{$this->application->git_repository}:{$tag}-build");
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:{$tag}");
|
$this->production_image_name = Str::lower("{$this->application->uuid}:{$tag}");
|
||||||
ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
||||||
|
|
||||||
if (!$this->force_rebuild) {
|
if (!$this->force_rebuild) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
@@ -284,7 +345,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private function rolling_update()
|
private function rolling_update()
|
||||||
{
|
{
|
||||||
if (count($this->application->ports_mappings_array) > 0){
|
if (count($this->application->ports_mappings_array) > 0) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Application has ports mapped to the host system, rolling update is not supported. Stopping current container.'"],
|
["echo -n 'Application has ports mapped to the host system, rolling update is not supported. Stopping current container.'"],
|
||||||
);
|
);
|
||||||
@@ -301,7 +362,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
private function health_check()
|
private function health_check()
|
||||||
{
|
{
|
||||||
ray('New container name: ', $this->container_name);
|
if ($this->application->isHealthcheckDisabled()) {
|
||||||
|
$this->newVersionIsHealthy = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ray('New container name: ', $this->container_name);
|
||||||
if ($this->container_name) {
|
if ($this->container_name) {
|
||||||
$counter = 0;
|
$counter = 0;
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
@@ -329,9 +394,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if (Str::of($this->saved_outputs->get('health_check'))->contains('healthy')) {
|
if (Str::of($this->saved_outputs->get('health_check'))->contains('healthy')) {
|
||||||
$this->newVersionIsHealthy = true;
|
$this->newVersionIsHealthy = true;
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
|
||||||
"echo 'New version of your application is healthy.'"
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
"echo 'Rolling update completed.'"
|
"echo 'Rolling update completed.'"
|
||||||
],
|
],
|
||||||
@@ -348,12 +410,13 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
$this->build_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}-build");
|
$this->build_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}-build");
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}");
|
$this->production_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}");
|
||||||
ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"echo 'Starting pull request (#{$this->pull_request_id}) deployment of {$this->application->git_repository}:{$this->application->git_branch}.'",
|
"echo 'Starting pull request (#{$this->pull_request_id}) deployment of {$this->application->git_repository}:{$this->application->git_branch}.'",
|
||||||
]);
|
]);
|
||||||
$this->prepare_builder_image();
|
$this->prepare_builder_image();
|
||||||
$this->clone_repository();
|
$this->clone_repository();
|
||||||
|
$this->set_base_dir();
|
||||||
$this->cleanup_git();
|
$this->cleanup_git();
|
||||||
if ($this->application->build_pack === 'nixpacks') {
|
if ($this->application->build_pack === 'nixpacks') {
|
||||||
$this->generate_nixpacks_confs();
|
$this->generate_nixpacks_confs();
|
||||||
@@ -373,9 +436,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function prepare_builder_image()
|
private function prepare_builder_image()
|
||||||
{
|
{
|
||||||
$pull = "--pull=always";
|
$pull = "--pull=always";
|
||||||
if (isDev()) {
|
|
||||||
$pull = "--pull=never";
|
|
||||||
}
|
|
||||||
$helperImage = config('coolify.helper_image');
|
$helperImage = config('coolify.helper_image');
|
||||||
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||||
|
|
||||||
@@ -388,24 +448,31 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
"hidden" => true,
|
"hidden" => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
|
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->basedir}")
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function set_base_dir()
|
||||||
|
{
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"echo -n 'Setting base directory to {$this->workdir}.'"
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private function clone_repository()
|
private function clone_repository()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
"echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} to {$this->workdir}. '"
|
"echo -n 'Importing {$this->application->git_repository}:{$this->application->git_branch} (commit sha {$this->application->git_commit_sha}) to {$this->basedir}. '"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
$this->importing_git_repository()
|
$this->importing_git_repository(), "hidden" => true
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "cd {$this->workdir} && git rev-parse HEAD"),
|
executeInDocker($this->deployment_uuid, "cd {$this->basedir} && git rev-parse HEAD"),
|
||||||
"hidden" => true,
|
"hidden" => true,
|
||||||
"save" => "git_commit_sha"
|
"save" => "git_commit_sha"
|
||||||
],
|
],
|
||||||
@@ -429,23 +496,23 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
if ($this->source->getMorphClass() == 'App\Models\GithubApp') {
|
if ($this->source->getMorphClass() == 'App\Models\GithubApp') {
|
||||||
if ($this->source->is_public) {
|
if ($this->source->is_public) {
|
||||||
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$this->application->git_repository} {$this->workdir}";
|
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$this->application->git_repository} {$this->basedir}";
|
||||||
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
||||||
|
|
||||||
$commands->push(executeInDocker($this->deployment_uuid, $git_clone_command));
|
$commands->push(executeInDocker($this->deployment_uuid, $git_clone_command));
|
||||||
} else {
|
} else {
|
||||||
$github_access_token = generate_github_installation_token($this->source);
|
$github_access_token = generate_github_installation_token($this->source);
|
||||||
$commands->push(executeInDocker($this->deployment_uuid, "git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->workdir}"));
|
$commands->push(executeInDocker($this->deployment_uuid, "git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->basedir}"));
|
||||||
}
|
}
|
||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
$commands->push(executeInDocker($this->deployment_uuid, "cd {$this->workdir} && git fetch origin pull/{$this->pull_request_id}/head:$pr_branch_name && git checkout $pr_branch_name"));
|
$commands->push(executeInDocker($this->deployment_uuid, "cd {$this->basedir} && git fetch origin pull/{$this->pull_request_id}/head:$pr_branch_name && git checkout $pr_branch_name"));
|
||||||
}
|
}
|
||||||
return $commands->implode(' && ');
|
return $commands->implode(' && ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->application->deploymentType() === 'deploy_key') {
|
if ($this->application->deploymentType() === 'deploy_key') {
|
||||||
$private_key = base64_encode($this->application->private_key->private_key);
|
$private_key = base64_encode($this->application->private_key->private_key);
|
||||||
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->application->git_full_url} {$this->workdir}";
|
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->application->git_full_url} {$this->basedir}";
|
||||||
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
||||||
$commands = collect([
|
$commands = collect([
|
||||||
executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh"),
|
executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh"),
|
||||||
@@ -455,18 +522,25 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]);
|
]);
|
||||||
return $commands->implode(' && ');
|
return $commands->implode(' && ');
|
||||||
}
|
}
|
||||||
|
if ($this->application->deploymentType() === 'other') {
|
||||||
|
$git_clone_command = "{$git_clone_command} {$this->application->git_repository} {$this->basedir}";
|
||||||
|
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
||||||
|
$commands->push(executeInDocker($this->deployment_uuid, $git_clone_command));
|
||||||
|
ray($commands);
|
||||||
|
return $commands->implode(' && ');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function set_git_import_settings($git_clone_command)
|
private function set_git_import_settings($git_clone_command)
|
||||||
{
|
{
|
||||||
if ($this->application->git_commit_sha !== 'HEAD') {
|
if ($this->application->git_commit_sha !== 'HEAD') {
|
||||||
$git_clone_command = "{$git_clone_command} && cd {$this->workdir} && git -c advice.detachedHead=false checkout {$this->application->git_commit_sha} >/dev/null 2>&1";
|
$git_clone_command = "{$git_clone_command} && cd {$this->basedir} && git -c advice.detachedHead=false checkout {$this->application->git_commit_sha} >/dev/null 2>&1";
|
||||||
}
|
}
|
||||||
if ($this->application->settings->is_git_submodules_enabled) {
|
if ($this->application->settings->is_git_submodules_enabled) {
|
||||||
$git_clone_command = "{$git_clone_command} && cd {$this->workdir} && git submodule update --init --recursive";
|
$git_clone_command = "{$git_clone_command} && cd {$this->basedir} && git submodule update --init --recursive";
|
||||||
}
|
}
|
||||||
if ($this->application->settings->is_git_lfs_enabled) {
|
if ($this->application->settings->is_git_lfs_enabled) {
|
||||||
$git_clone_command = "{$git_clone_command} && cd {$this->workdir} && git lfs pull";
|
$git_clone_command = "{$git_clone_command} && cd {$this->basedir} && git lfs pull";
|
||||||
}
|
}
|
||||||
return $git_clone_command;
|
return $git_clone_command;
|
||||||
}
|
}
|
||||||
@@ -474,17 +548,24 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function cleanup_git()
|
private function cleanup_git()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[executeInDocker($this->deployment_uuid, "rm -fr {$this->workdir}/.git")],
|
[executeInDocker($this->deployment_uuid, "rm -fr {$this->basedir}/.git")],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_nixpacks_confs()
|
private function generate_nixpacks_confs()
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
"echo -n 'Generating nixpacks configuration.'",
|
"echo -n 'Generating nixpacks configuration.'",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$nixpacks_command = $this->nixpacks_build_cmd();
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"echo -n Running: $nixpacks_command",
|
||||||
],
|
],
|
||||||
[$this->nixpacks_build_cmd()],
|
[executeInDocker($this->deployment_uuid, $nixpacks_command)],
|
||||||
[executeInDocker($this->deployment_uuid, "cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile")],
|
[executeInDocker($this->deployment_uuid, "cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile")],
|
||||||
[executeInDocker($this->deployment_uuid, "rm -f {$this->workdir}/.nixpacks/Dockerfile")]
|
[executeInDocker($this->deployment_uuid, "rm -f {$this->workdir}/.nixpacks/Dockerfile")]
|
||||||
);
|
);
|
||||||
@@ -493,7 +574,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function nixpacks_build_cmd()
|
private function nixpacks_build_cmd()
|
||||||
{
|
{
|
||||||
$this->generate_env_variables();
|
$this->generate_env_variables();
|
||||||
$nixpacks_command = "nixpacks build -o {$this->workdir} {$this->env_args} --no-error-without-start";
|
$nixpacks_command = "nixpacks build --no-cache -o {$this->workdir} {$this->env_args} --no-error-without-start";
|
||||||
if ($this->application->build_command) {
|
if ($this->application->build_command) {
|
||||||
$nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
|
$nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
|
||||||
}
|
}
|
||||||
@@ -504,7 +585,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
|
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
|
||||||
}
|
}
|
||||||
$nixpacks_command .= " {$this->workdir}";
|
$nixpacks_command .= " {$this->workdir}";
|
||||||
return executeInDocker($this->deployment_uuid, $nixpacks_command);
|
return $nixpacks_command;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_env_variables()
|
private function generate_env_variables()
|
||||||
@@ -539,7 +620,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'container_name' => $this->container_name,
|
'container_name' => $this->container_name,
|
||||||
'restart' => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
'environment' => $environment_variables,
|
'environment' => $environment_variables,
|
||||||
'labels' => generateLabelsApplication($this->application, $this->preview),
|
'labels' => generateLabelsApplication($this->application, $this->preview, $ports),
|
||||||
'expose' => $ports,
|
'expose' => $ports,
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->destination->network,
|
$this->destination->network,
|
||||||
@@ -571,6 +652,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->application->isHealthcheckDisabled()) {
|
||||||
|
data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck');
|
||||||
|
}
|
||||||
if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) {
|
if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) {
|
||||||
$docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array;
|
$docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array;
|
||||||
}
|
}
|
||||||
@@ -580,6 +664,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
|
if ($this->build_pack === 'dockerfile') {
|
||||||
|
$docker_compose['services'][$this->container_name]['build'] = [
|
||||||
|
'context' => $this->workdir,
|
||||||
|
'dockerfile' => $this->workdir . $this->dockerfile_location,
|
||||||
|
];
|
||||||
|
}
|
||||||
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
||||||
@@ -622,14 +712,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function generate_environment_variables($ports)
|
private function generate_environment_variables($ports)
|
||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
ray('Generate Environment Variables')->green();
|
// ray('Generate Environment Variables')->green();
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
ray($this->application->runtime_environment_variables)->green();
|
// ray($this->application->runtime_environment_variables)->green();
|
||||||
foreach ($this->application->runtime_environment_variables as $env) {
|
foreach ($this->application->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->value");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ray($this->application->runtime_environment_variables_preview)->green();
|
// ray($this->application->runtime_environment_variables_preview)->green();
|
||||||
foreach ($this->application->runtime_environment_variables_preview as $env) {
|
foreach ($this->application->runtime_environment_variables_preview as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->value");
|
||||||
}
|
}
|
||||||
@@ -643,7 +733,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private function generate_healthcheck_commands()
|
private function generate_healthcheck_commands()
|
||||||
{
|
{
|
||||||
if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile') {
|
if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') {
|
||||||
// TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl.
|
// TODO: disabled HC because there are several ways to hc a simple docker image, hard to figure out a good way. Like some docker images (pocketbase) does not have curl.
|
||||||
return 'exit 0';
|
return 'exit 0';
|
||||||
}
|
}
|
||||||
@@ -667,7 +757,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private function build_image()
|
private function build_image()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"echo -n 'Building docker image for your application.'",
|
"echo -n 'Building docker image for your application. To check the current progress, click on Show Debug Logs.'",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($this->application->settings->is_static) {
|
if ($this->application->settings->is_static) {
|
||||||
@@ -736,7 +826,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Starting application (could take a while).'"],
|
["echo -n 'Starting application (could take a while).'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d >/dev/null"), "hidden" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private function update_comment()
|
private function update_comment()
|
||||||
{
|
{
|
||||||
['data' => $data] = git_api(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->preview->pull_request_issue_comment_id}", method: 'patch', data: [
|
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->preview->pull_request_issue_comment_id}", method: 'patch', data: [
|
||||||
'body' => $this->body,
|
'body' => $this->body,
|
||||||
], throwError: false);
|
], throwError: false);
|
||||||
if (data_get($data, 'message') === 'Not Found') {
|
if (data_get($data, 'message') === 'Not Found') {
|
||||||
@@ -77,7 +77,7 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private function create_comment()
|
private function create_comment()
|
||||||
{
|
{
|
||||||
['data' => $data] = git_api(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/{$this->pull_request_id}/comments", method: 'post', data: [
|
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/{$this->pull_request_id}/comments", method: 'post', data: [
|
||||||
'body' => $this->body,
|
'body' => $this->body,
|
||||||
]);
|
]);
|
||||||
$this->preview->pull_request_issue_comment_id = $data['id'];
|
$this->preview->pull_request_issue_comment_id = $data['id'];
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use App\Models\ApplicationPreview;
|
|||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Notifications\Container\ContainerRestarted;
|
use App\Notifications\Container\ContainerRestarted;
|
||||||
use App\Notifications\Container\ContainerStopped;
|
use App\Notifications\Container\ContainerStopped;
|
||||||
|
use App\Notifications\Server\Revived;
|
||||||
use App\Notifications\Server\Unreachable;
|
use App\Notifications\Server\Unreachable;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
@@ -40,33 +41,60 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
return $this->server->uuid;
|
return $this->server->uuid;
|
||||||
}
|
}
|
||||||
|
public function handle()
|
||||||
private function checkServerConnection()
|
|
||||||
{
|
|
||||||
$uptime = instant_remote_process(['uptime'], $this->server, false);
|
|
||||||
if (!is_null($uptime)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function handle(): void
|
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
// ray("checking server status for {$this->server->name}");
|
||||||
// ray()->clearAll();
|
// ray()->clearAll();
|
||||||
$serverUptimeCheckNumber = 0;
|
$serverUptimeCheckNumber = $this->server->unreachable_count;
|
||||||
$serverUptimeCheckNumberMax = 3;
|
$serverUptimeCheckNumberMax = 3;
|
||||||
while (true) {
|
|
||||||
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
// ray('checking # ' . $serverUptimeCheckNumber);
|
||||||
$this->server->settings()->update(['is_reachable' => false]);
|
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
||||||
$this->server->team->notify(new Unreachable($this->server));
|
if ($this->server->unreachable_email_sent === false) {
|
||||||
return;
|
ray('Server unreachable, sending notification...');
|
||||||
|
// $this->server->team->notify(new Unreachable($this->server));
|
||||||
|
$this->server->update(['unreachable_email_sent' => true]);
|
||||||
}
|
}
|
||||||
$result = $this->checkServerConnection();
|
$this->server->settings()->update([
|
||||||
if ($result) {
|
'is_reachable' => false,
|
||||||
break;
|
]);
|
||||||
}
|
return;
|
||||||
$serverUptimeCheckNumber++;
|
|
||||||
sleep(5);
|
|
||||||
}
|
}
|
||||||
|
$result = $this->server->validateConnection();
|
||||||
|
if ($result) {
|
||||||
|
$this->server->settings()->update([
|
||||||
|
'is_reachable' => true,
|
||||||
|
]);
|
||||||
|
$this->server->update([
|
||||||
|
'unreachable_count' => 0,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$serverUptimeCheckNumber++;
|
||||||
|
$this->server->settings()->update([
|
||||||
|
'is_reachable' => false,
|
||||||
|
]);
|
||||||
|
$this->server->update([
|
||||||
|
'unreachable_count' => $serverUptimeCheckNumber,
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_get($this->server, 'unreachable_email_sent') === true) {
|
||||||
|
ray('Server is reachable again, sending notification...');
|
||||||
|
// $this->server->team->notify(new Revived($this->server));
|
||||||
|
$this->server->update(['unreachable_email_sent' => false]);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
data_get($this->server, 'settings.is_reachable') === false ||
|
||||||
|
data_get($this->server, 'settings.is_usable') === false
|
||||||
|
) {
|
||||||
|
$this->server->settings()->update([
|
||||||
|
'is_reachable' => true,
|
||||||
|
'is_usable' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// $this->server->validateDockerEngine(true);
|
||||||
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
||||||
if (!$containers) {
|
if (!$containers) {
|
||||||
return;
|
return;
|
||||||
@@ -108,9 +136,9 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$labelId = data_get($labels, 'coolify.applicationId');
|
$labelId = data_get($labels, 'coolify.applicationId');
|
||||||
if ($labelId) {
|
if ($labelId) {
|
||||||
if (str_contains($labelId, '-pr-')) {
|
if (str_contains($labelId, '-pr-')) {
|
||||||
$previewId = (int) Str::after($labelId, '-pr-');
|
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||||
$applicationId = (int) Str::before($labelId, '-pr-');
|
$applicationId = (int) Str::before($labelId, '-pr-');
|
||||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $previewId)->first();
|
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||||
if ($preview) {
|
if ($preview) {
|
||||||
$foundApplicationPreviews[] = $preview->id;
|
$foundApplicationPreviews[] = $preview->id;
|
||||||
$statusFromDb = $preview->status;
|
$statusFromDb = $preview->status;
|
||||||
@@ -266,7 +294,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage());
|
send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
throw $e;
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public ?string $container_name = null;
|
public ?string $container_name = null;
|
||||||
public ?ScheduledDatabaseBackupExecution $backup_log = null;
|
public ?ScheduledDatabaseBackupExecution $backup_log = null;
|
||||||
public string $backup_status;
|
public string $backup_status = 'failed';
|
||||||
public ?string $backup_location = null;
|
public ?string $backup_location = null;
|
||||||
public string $backup_dir;
|
public string $backup_dir;
|
||||||
public string $backup_file;
|
public string $backup_file;
|
||||||
@@ -61,7 +61,8 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (data_get($this->database, 'status') !== 'running') {
|
$status = Str::of(data_get($this->database, 'status'));
|
||||||
|
if (!$status->startsWith('running') && $this->database->id !== 0) {
|
||||||
ray('database not running');
|
ray('database not running');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -73,7 +74,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$ip = Str::slug($this->server->ip);
|
$ip = Str::slug($this->server->ip);
|
||||||
$this->backup_dir = backup_dir() . "/coolify" . "/coolify-db-$ip";
|
$this->backup_dir = backup_dir() . "/coolify" . "/coolify-db-$ip";
|
||||||
}
|
}
|
||||||
$this->backup_file = "/pg_dump-" . Carbon::now()->timestamp . ".dump";
|
$this->backup_file = "/pg-backup-customformat-" . Carbon::now()->timestamp . ".backup";
|
||||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||||
|
|
||||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||||
@@ -89,11 +90,17 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->upload_to_s3();
|
$this->upload_to_s3();
|
||||||
}
|
}
|
||||||
$this->save_backup_logs();
|
$this->save_backup_logs();
|
||||||
// TODO: Notify user
|
$this->team->notify(new BackupSuccess($this->backup, $this->database));
|
||||||
|
$this->backup_status = 'success';
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e->getMessage());
|
$this->backup_status = 'failed';
|
||||||
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||||
|
$this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
||||||
throw $e;
|
throw $e;
|
||||||
|
} finally {
|
||||||
|
$this->backup_log->update([
|
||||||
|
'status' => $this->backup_status,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,28 +110,15 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
ray($this->backup_dir);
|
ray($this->backup_dir);
|
||||||
$commands[] = "mkdir -p " . $this->backup_dir;
|
$commands[] = "mkdir -p " . $this->backup_dir;
|
||||||
$commands[] = "docker exec $this->container_name pg_dump -Fc -U {$this->database->postgres_user} > $this->backup_location";
|
$commands[] = "docker exec $this->container_name pg_dump -Fc -U {$this->database->postgres_user} > $this->backup_location";
|
||||||
|
|
||||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||||
|
|
||||||
$this->backup_output = trim($this->backup_output);
|
$this->backup_output = trim($this->backup_output);
|
||||||
|
|
||||||
if ($this->backup_output === '') {
|
if ($this->backup_output === '') {
|
||||||
$this->backup_output = null;
|
$this->backup_output = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location);
|
ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location);
|
||||||
|
|
||||||
$this->backup_status = 'success';
|
|
||||||
$this->team->notify(new BackupSuccess($this->backup, $this->database));
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->backup_status = 'failed';
|
|
||||||
$this->add_to_backup_output($e->getMessage());
|
$this->add_to_backup_output($e->getMessage());
|
||||||
ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage());
|
ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage());
|
||||||
$this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
|
||||||
} finally {
|
|
||||||
$this->backup_log->update([
|
|
||||||
'status' => $this->backup_status,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,11 +157,16 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
$key = $this->s3->key;
|
$key = $this->s3->key;
|
||||||
$secret = $this->s3->secret;
|
$secret = $this->s3->secret;
|
||||||
// $region = $this->s3->region;
|
// $region = $this->s3->region;
|
||||||
$bucket = $this->s3->bucket;
|
$bucket = $this->s3->bucket;
|
||||||
$endpoint = $this->s3->endpoint;
|
$endpoint = $this->s3->endpoint;
|
||||||
|
$this->s3->testConnection();
|
||||||
|
if (isDev()) {
|
||||||
|
$commands[] = "docker run --pull=always -d --network {$this->database->destination->network} --name backup-of-{$this->backup->uuid} --rm -v coolify_coolify-data-dev:/data/coolify:ro ghcr.io/coollabsio/coolify-helper >/dev/null 2>&1";
|
||||||
|
} else {
|
||||||
|
$commands[] = "docker run --pull=always -d --network {$this->database->destination->network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper >/dev/null 2>&1";
|
||||||
|
}
|
||||||
|
|
||||||
$commands[] = "docker run --pull=always -d --network {$this->database->destination->network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
|
|
||||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
||||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
||||||
instant_remote_process($commands, $this->server);
|
instant_remote_process($commands, $this->server);
|
||||||
@@ -175,7 +174,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
ray('Uploaded to S3. ' . $this->backup_location . ' to s3://' . $bucket . $this->backup_dir);
|
ray('Uploaded to S3. ' . $this->backup_location . ' to s3://' . $bucket . $this->backup_dir);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->add_to_backup_output($e->getMessage());
|
$this->add_to_backup_output($e->getMessage());
|
||||||
ray($e->getMessage());
|
throw $e;
|
||||||
} finally {
|
} finally {
|
||||||
$command = "docker rm -f backup-of-{$this->backup->uuid}";
|
$command = "docker rm -f backup-of-{$this->backup->uuid}";
|
||||||
instant_remote_process([$command], $this->server);
|
instant_remote_process([$command], $this->server);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
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 Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Application extends BaseModel
|
class Application extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -12,16 +13,37 @@ class Application extends BaseModel
|
|||||||
|
|
||||||
protected static function booted()
|
protected static function booted()
|
||||||
{
|
{
|
||||||
|
static::saving(function ($application) {
|
||||||
|
if ($application->fqdn == '') {
|
||||||
|
$application->fqdn = null;
|
||||||
|
}
|
||||||
|
$application->forceFill([
|
||||||
|
'fqdn' => $application->fqdn,
|
||||||
|
'install_command' => Str::of($application->install_command)->trim(),
|
||||||
|
'build_command' => Str::of($application->build_command)->trim(),
|
||||||
|
'start_command' => Str::of($application->start_command)->trim(),
|
||||||
|
'base_directory' => Str::of($application->base_directory)->trim(),
|
||||||
|
'publish_directory' => Str::of($application->publish_directory)->trim(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
static::created(function ($application) {
|
static::created(function ($application) {
|
||||||
ApplicationSetting::create([
|
ApplicationSetting::create([
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
static::deleting(function ($application) {
|
static::deleting(function ($application) {
|
||||||
|
// Stop Container
|
||||||
|
if ($application->destination->server->isFunctional()) {
|
||||||
|
instant_remote_process(
|
||||||
|
["docker rm -f {$application->uuid}"],
|
||||||
|
$application->destination->server,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
$application->settings()->delete();
|
$application->settings()->delete();
|
||||||
$storages = $application->persistentStorages()->get();
|
$storages = $application->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
instant_remote_process(["docker volume rm -f $storage->name"], $application->destination->server);
|
instant_remote_process(["docker volume rm -f $storage->name"], $application->destination->server, false);
|
||||||
}
|
}
|
||||||
$application->persistentStorages()->delete();
|
$application->persistentStorages()->delete();
|
||||||
$application->environment_variables()->delete();
|
$application->environment_variables()->delete();
|
||||||
@@ -38,6 +60,10 @@ class Application extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||||
}
|
}
|
||||||
|
public function fileStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
public function type()
|
public function type()
|
||||||
{
|
{
|
||||||
@@ -58,6 +84,7 @@ class Application extends BaseModel
|
|||||||
if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
|
if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
|
||||||
return "{$this->source->html_url}/{$this->git_repository}/tree/{$this->git_branch}";
|
return "{$this->source->html_url}/{$this->git_repository}/tree/{$this->git_branch}";
|
||||||
}
|
}
|
||||||
|
return $this->git_repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
);
|
);
|
||||||
@@ -70,10 +97,25 @@ class Application extends BaseModel
|
|||||||
if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
|
if (!is_null($this->source?->html_url) && !is_null($this->git_repository) && !is_null($this->git_branch)) {
|
||||||
return "{$this->source->html_url}/{$this->git_repository}/commits/{$this->git_branch}";
|
return "{$this->source->html_url}/{$this->git_repository}/commits/{$this->git_branch}";
|
||||||
}
|
}
|
||||||
|
return $this->git_repository;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public function dockerfileLocation(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
set: function ($value) {
|
||||||
|
if (is_null($value) || $value === '') {
|
||||||
|
return '/Dockerfile';
|
||||||
|
} else {
|
||||||
|
if ($value !== '/') {
|
||||||
|
return Str::start(Str::replaceEnd('/', '', $value), '/');
|
||||||
|
}
|
||||||
|
return Str::start($value, '/');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function baseDirectory(): Attribute
|
public function baseDirectory(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
@@ -214,6 +256,8 @@ class Application extends BaseModel
|
|||||||
return 'deploy_key';
|
return 'deploy_key';
|
||||||
} else if (data_get($this, 'source')) {
|
} else if (data_get($this, 'source')) {
|
||||||
return 'source';
|
return 'source';
|
||||||
|
} else {
|
||||||
|
return 'other';
|
||||||
}
|
}
|
||||||
throw new \Exception('No deployment type found');
|
throw new \Exception('No deployment type found');
|
||||||
}
|
}
|
||||||
@@ -229,6 +273,16 @@ class Application extends BaseModel
|
|||||||
if ($this->dockerfile) {
|
if ($this->dockerfile) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if ($this->build_pack === 'dockerimage') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
public function isHealthcheckDisabled(): bool
|
||||||
|
{
|
||||||
|
if (data_get($this, 'health_check_enabled') === false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,23 +31,18 @@ class LocalFileVolume extends BaseModel
|
|||||||
}
|
}
|
||||||
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
|
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
|
||||||
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
|
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
|
||||||
ray($isFile);
|
|
||||||
if ($isFile == 'OK' && $fileVolume->is_directory) {
|
if ($isFile == 'OK' && $fileVolume->is_directory) {
|
||||||
throw new \Exception("File $path is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.");
|
throw new \Exception("File $path is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.");
|
||||||
} else if ($isDir == 'OK' && !$fileVolume->is_directory) {
|
} else if ($isDir == 'OK' && !$fileVolume->is_directory) {
|
||||||
throw new \Exception("File $path is a directory on the server, but you are trying to mark it as a file. Please delete the directory on the server or mark it as directory.");
|
throw new \Exception("File $path is a directory on the server, but you are trying to mark it as a file. Please delete the directory on the server or mark it as directory.");
|
||||||
}
|
}
|
||||||
if (($isFile == 'NOK' && !$fileVolume->is_directory) || $isFile == 'OK') {
|
if (!$fileVolume->is_directory && $isDir == 'NOK') {
|
||||||
$rootDir = Str::of($path)->dirname();
|
$content = base64_encode($content);
|
||||||
$commands->push("mkdir -p $rootDir > /dev/null 2>&1 || true");
|
$commands->push("echo '$content' | base64 -d > $path");
|
||||||
$commands->push("touch $path > /dev/null 2>&1 || true");
|
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
|
||||||
if ($content) {
|
|
||||||
$content = base64_encode($content);
|
|
||||||
$commands->push("echo '$content' | base64 -d > $path");
|
|
||||||
}
|
|
||||||
} 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");
|
||||||
}
|
}
|
||||||
|
ray($commands->toArray());
|
||||||
return instant_remote_process($commands, $server);
|
return instant_remote_process($commands, $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class Project extends BaseModel
|
|||||||
'project_id' => $project->id,
|
'project_id' => $project->id,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
static::deleted(function ($project) {
|
static::deleting(function ($project) {
|
||||||
$project->environments()->delete();
|
$project->environments()->delete();
|
||||||
$project->settings()->delete();
|
$project->settings()->delete();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class S3Storage extends BaseModel
|
class S3Storage extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -10,6 +12,7 @@ class S3Storage extends BaseModel
|
|||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
|
'is_usable' => 'boolean',
|
||||||
'key' => 'encrypted',
|
'key' => 'encrypted',
|
||||||
'secret' => 'encrypted',
|
'secret' => 'encrypted',
|
||||||
];
|
];
|
||||||
@@ -19,7 +22,15 @@ class S3Storage extends BaseModel
|
|||||||
$selectArray = collect($select)->concat(['id']);
|
$selectArray = collect($select)->concat(['id']);
|
||||||
return S3Storage::whereTeamId(currentTeam()->id)->select($selectArray->all())->orderBy('name');
|
return S3Storage::whereTeamId(currentTeam()->id)->select($selectArray->all())->orderBy('name');
|
||||||
}
|
}
|
||||||
|
public function isUsable()
|
||||||
|
{
|
||||||
|
return $this->is_usable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function team()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Team::class);
|
||||||
|
}
|
||||||
public function awsUrl()
|
public function awsUrl()
|
||||||
{
|
{
|
||||||
return "{$this->endpoint}/{$this->bucket}";
|
return "{$this->endpoint}/{$this->bucket}";
|
||||||
@@ -27,7 +38,34 @@ class S3Storage extends BaseModel
|
|||||||
|
|
||||||
public function testConnection()
|
public function testConnection()
|
||||||
{
|
{
|
||||||
set_s3_target($this);
|
try {
|
||||||
return \Storage::disk('custom-s3')->files();
|
set_s3_target($this);
|
||||||
|
Storage::disk('custom-s3')->files();
|
||||||
|
$this->unusable_email_sent = false;
|
||||||
|
$this->is_usable = true;
|
||||||
|
return;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->is_usable = false;
|
||||||
|
if ($this->unusable_email_sent === false && is_transactional_emails_active()) {
|
||||||
|
$mail = new MailMessage();
|
||||||
|
$mail->subject('Coolify: S3 Storage Connection Error');
|
||||||
|
$mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('team.storages.show', ['storage_uuid' => $this->uuid])]);
|
||||||
|
$users = collect([]);
|
||||||
|
$members = $this->team->members()->get();
|
||||||
|
foreach ($members as $user) {
|
||||||
|
if ($user->isAdmin()) {
|
||||||
|
$users->push($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($users as $user) {
|
||||||
|
send_user_an_email($mail, $user->email);
|
||||||
|
}
|
||||||
|
$this->unusable_email_sent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
} finally {
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ namespace App\Models;
|
|||||||
use App\Enums\ProxyStatus;
|
use App\Enums\ProxyStatus;
|
||||||
use App\Enums\ProxyTypes;
|
use App\Enums\ProxyTypes;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
||||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Server extends BaseModel
|
class Server extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -14,6 +16,17 @@ class Server extends BaseModel
|
|||||||
|
|
||||||
protected static function booted()
|
protected static function booted()
|
||||||
{
|
{
|
||||||
|
static::saving(function ($server) {
|
||||||
|
$payload = [];
|
||||||
|
if ($server->user) {
|
||||||
|
$payload['user'] = Str::of($server->user)->trim();
|
||||||
|
}
|
||||||
|
if ($server->ip) {
|
||||||
|
$payload['ip'] = Str::of($server->ip)->trim();
|
||||||
|
}
|
||||||
|
$server->forceFill($payload);
|
||||||
|
});
|
||||||
|
|
||||||
static::created(function ($server) {
|
static::created(function ($server) {
|
||||||
ServerSetting::create([
|
ServerSetting::create([
|
||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
@@ -120,7 +133,20 @@ class Server extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Service::class);
|
return $this->hasMany(Service::class);
|
||||||
}
|
}
|
||||||
|
public function getIp(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: function () {
|
||||||
|
if (isDev()) {
|
||||||
|
return '127.0.0.1';
|
||||||
|
}
|
||||||
|
if ($this->ip === 'host.docker.internal') {
|
||||||
|
return base_ip();
|
||||||
|
}
|
||||||
|
return $this->ip;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
public function previews()
|
public function previews()
|
||||||
{
|
{
|
||||||
return $this->destinations()->map(function ($standaloneDocker) {
|
return $this->destinations()->map(function ($standaloneDocker) {
|
||||||
@@ -185,4 +211,48 @@ class Server extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->settings->is_reachable && $this->settings->is_usable;
|
return $this->settings->is_reachable && $this->settings->is_usable;
|
||||||
}
|
}
|
||||||
|
public function validateConnection()
|
||||||
|
{
|
||||||
|
$uptime = instant_remote_process(['uptime'], $this, false);
|
||||||
|
if (!$uptime) {
|
||||||
|
$this->settings->is_reachable = false;
|
||||||
|
$this->settings->save();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->settings->is_reachable = true;
|
||||||
|
$this->settings->save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public function validateDockerEngine($throwError = false)
|
||||||
|
{
|
||||||
|
$dockerBinary = instant_remote_process(["command -v docker"], $this, false);
|
||||||
|
if (is_null($dockerBinary)) {
|
||||||
|
$this->settings->is_usable = false;
|
||||||
|
$this->settings->save();
|
||||||
|
if ($throwError) {
|
||||||
|
throw new \Exception('Server is not usable.');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->settings->is_usable = true;
|
||||||
|
$this->settings->save();
|
||||||
|
$this->validateCoolifyNetwork();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public function validateDockerEngineVersion()
|
||||||
|
{
|
||||||
|
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this, false);
|
||||||
|
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||||
|
if (is_null($dockerVersion)) {
|
||||||
|
$this->settings->is_usable = false;
|
||||||
|
$this->settings->save();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->settings->is_usable = true;
|
||||||
|
$this->settings->save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public function validateCoolifyNetwork() {
|
||||||
|
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use Illuminate\Support\Collection;
|
|||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Spatie\Url\Url;
|
|
||||||
|
|
||||||
class Service extends BaseModel
|
class Service extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -17,9 +16,10 @@ class Service extends BaseModel
|
|||||||
|
|
||||||
protected static function booted()
|
protected static function booted()
|
||||||
{
|
{
|
||||||
static::deleted(function ($service) {
|
static::deleting(function ($service) {
|
||||||
$storagesToDelete = collect([]);
|
$storagesToDelete = collect([]);
|
||||||
foreach ($service->applications()->get() as $application) {
|
foreach ($service->applications()->get() as $application) {
|
||||||
|
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server, false);
|
||||||
$storages = $application->persistentStorages()->get();
|
$storages = $application->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
@@ -27,6 +27,7 @@ class Service extends BaseModel
|
|||||||
$application->persistentStorages()->delete();
|
$application->persistentStorages()->delete();
|
||||||
}
|
}
|
||||||
foreach ($service->databases()->get() as $database) {
|
foreach ($service->databases()->get() as $database) {
|
||||||
|
instant_remote_process(["docker rm -f {$database->name}-{$service->uuid}"], $service->server, false);
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
@@ -63,6 +64,10 @@ class Service extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->hasMany(ServiceDatabase::class);
|
return $this->hasMany(ServiceDatabase::class);
|
||||||
}
|
}
|
||||||
|
public function destination()
|
||||||
|
{
|
||||||
|
return $this->morphTo();
|
||||||
|
}
|
||||||
public function environment()
|
public function environment()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Environment::class);
|
return $this->belongsTo(Environment::class);
|
||||||
@@ -112,7 +117,7 @@ class Service extends BaseModel
|
|||||||
|
|
||||||
public function parse(bool $isNew = false): Collection
|
public function parse(bool $isNew = false): Collection
|
||||||
{
|
{
|
||||||
// ray()->clearAll();
|
ray()->clearAll();
|
||||||
if ($this->docker_compose_raw) {
|
if ($this->docker_compose_raw) {
|
||||||
try {
|
try {
|
||||||
$yaml = Yaml::parse($this->docker_compose_raw);
|
$yaml = Yaml::parse($this->docker_compose_raw);
|
||||||
@@ -124,9 +129,16 @@ class Service extends BaseModel
|
|||||||
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
|
$topLevelNetworks = collect(data_get($yaml, 'networks', []));
|
||||||
$dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
|
$dockerComposeVersion = data_get($yaml, 'version') ?? '3.8';
|
||||||
$services = data_get($yaml, 'services');
|
$services = data_get($yaml, 'services');
|
||||||
$definedNetwork = $this->uuid;
|
|
||||||
|
|
||||||
$generatedServiceFQDNS = collect([]);
|
$generatedServiceFQDNS = collect([]);
|
||||||
|
if (is_null($this->destination)) {
|
||||||
|
$destination = $this->server->destinations()->first();
|
||||||
|
if ($destination) {
|
||||||
|
$this->destination()->associate($destination);
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$definedNetwork = collect([$this->uuid]);
|
||||||
|
|
||||||
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS) {
|
$services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS) {
|
||||||
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
||||||
@@ -237,18 +249,24 @@ class Service extends BaseModel
|
|||||||
return $value == $definedNetwork;
|
return $value == $definedNetwork;
|
||||||
});
|
});
|
||||||
if (!$definedNetworkExists) {
|
if (!$definedNetworkExists) {
|
||||||
$topLevelNetworks->put($definedNetwork, [
|
foreach ($definedNetwork as $network) {
|
||||||
'name' => $definedNetwork,
|
$topLevelNetworks->put($network, [
|
||||||
'external' => true
|
'name' => $network,
|
||||||
]);
|
'external' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$networks = $serviceNetworks->toArray();
|
$networks = $serviceNetworks->toArray();
|
||||||
$networks = array_merge($networks, [$definedNetwork]);
|
foreach ($definedNetwork as $key => $network) {
|
||||||
|
$networks = array_merge($networks, [
|
||||||
|
$network
|
||||||
|
]);
|
||||||
|
}
|
||||||
data_set($service, 'networks', $networks);
|
data_set($service, 'networks', $networks);
|
||||||
|
|
||||||
// Collect/create/update volumes
|
// Collect/create/update volumes
|
||||||
if ($serviceVolumes->count() > 0) {
|
if ($serviceVolumes->count() > 0) {
|
||||||
foreach ($serviceVolumes as $volume) {
|
$serviceVolumes = $serviceVolumes->map(function ($volume) use ($savedService, $topLevelVolumes) {
|
||||||
$type = null;
|
$type = null;
|
||||||
$source = null;
|
$source = null;
|
||||||
$target = null;
|
$target = null;
|
||||||
@@ -270,16 +288,19 @@ class Service extends BaseModel
|
|||||||
$isDirectory = (bool) data_get($volume, 'isDirectory', false);
|
$isDirectory = (bool) data_get($volume, 'isDirectory', false);
|
||||||
$foundConfig = $savedService->fileStorages()->whereMountPath($target)->first();
|
$foundConfig = $savedService->fileStorages()->whereMountPath($target)->first();
|
||||||
if ($foundConfig) {
|
if ($foundConfig) {
|
||||||
$content = data_get($foundConfig, 'content');
|
$contentNotNull = data_get($foundConfig, 'content');
|
||||||
|
if ($contentNotNull) {
|
||||||
|
$content = $contentNotNull;
|
||||||
|
}
|
||||||
$isDirectory = (bool) data_get($foundConfig, 'is_directory');
|
$isDirectory = (bool) data_get($foundConfig, 'is_directory');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($type->value() === 'bind') {
|
if ($type->value() === 'bind') {
|
||||||
if ($source->value() === "/var/run/docker.sock") {
|
if ($source->value() === "/var/run/docker.sock") {
|
||||||
continue;
|
return $volume;
|
||||||
}
|
}
|
||||||
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
continue;
|
return $volume;
|
||||||
}
|
}
|
||||||
LocalFileVolume::updateOrCreate(
|
LocalFileVolume::updateOrCreate(
|
||||||
[
|
[
|
||||||
@@ -297,7 +318,19 @@ class Service extends BaseModel
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else if ($type->value() === 'volume') {
|
} else if ($type->value() === 'volume') {
|
||||||
$topLevelVolumes->put($source->value(), null);
|
$slugWithoutUuid = Str::slug($source, '-');
|
||||||
|
$name = "{$savedService->service->uuid}_{$slugWithoutUuid}";
|
||||||
|
if (is_string($volume)) {
|
||||||
|
$source = Str::of($volume)->before(':');
|
||||||
|
$target = Str::of($volume)->after(':')->beforeLast(':');
|
||||||
|
$source = $name;
|
||||||
|
$volume = "$source:$target";
|
||||||
|
} else if (is_array($volume)) {
|
||||||
|
data_set($volume, 'source', $name);
|
||||||
|
}
|
||||||
|
$topLevelVolumes->put($name, [
|
||||||
|
'name' => $name,
|
||||||
|
]);
|
||||||
LocalPersistentVolume::updateOrCreate(
|
LocalPersistentVolume::updateOrCreate(
|
||||||
[
|
[
|
||||||
'mount_path' => $target,
|
'mount_path' => $target,
|
||||||
@@ -305,15 +338,17 @@ class Service extends BaseModel
|
|||||||
'resource_type' => get_class($savedService)
|
'resource_type' => get_class($savedService)
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => Str::slug($source, '-'),
|
'name' => $name,
|
||||||
'mount_path' => $target,
|
'mount_path' => $target,
|
||||||
'resource_id' => $savedService->id,
|
'resource_id' => $savedService->id,
|
||||||
'resource_type' => get_class($savedService)
|
'resource_type' => get_class($savedService)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$savedService->getFilesFromServer();
|
$savedService->getFilesFromServer(isInit: true);
|
||||||
}
|
return $volume;
|
||||||
|
});
|
||||||
|
data_set($service, 'volumes', $serviceVolumes->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add env_file with at least .env to the service
|
// Add env_file with at least .env to the service
|
||||||
@@ -349,9 +384,23 @@ class Service extends BaseModel
|
|||||||
$value = Str::of($variable);
|
$value = Str::of($variable);
|
||||||
}
|
}
|
||||||
if ($key->startsWith('SERVICE_FQDN')) {
|
if ($key->startsWith('SERVICE_FQDN')) {
|
||||||
if (is_null(data_get($savedService, 'fqdn'))) {
|
if ($isNew) {
|
||||||
$fqdn = generateFqdn($this->server, $containerName);
|
$name = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower();
|
||||||
if (substr_count($key->value(), '_') === 2 && $key->contains("=")) {
|
$fqdn = generateFqdn($this->server, "{$name->value()}-{$this->uuid}");
|
||||||
|
if (substr_count($key->value(), '_') === 3) {
|
||||||
|
// SERVICE_FQDN_UMAMI_1000
|
||||||
|
$port = $key->afterLast('_');
|
||||||
|
} else {
|
||||||
|
// SERVICE_FQDN_UMAMI
|
||||||
|
$port = null;
|
||||||
|
}
|
||||||
|
if ($port) {
|
||||||
|
$fqdn = "$fqdn:$port";
|
||||||
|
}
|
||||||
|
if (substr_count($key->value(), '_') >= 2) {
|
||||||
|
if (is_null($value)) {
|
||||||
|
$value = Str::of('/');
|
||||||
|
}
|
||||||
$path = $value->value();
|
$path = $value->value();
|
||||||
if ($generatedServiceFQDNS->count() > 0) {
|
if ($generatedServiceFQDNS->count() > 0) {
|
||||||
$alreadyGenerated = $generatedServiceFQDNS->has($key->value());
|
$alreadyGenerated = $generatedServiceFQDNS->has($key->value());
|
||||||
@@ -365,11 +414,22 @@ class Service extends BaseModel
|
|||||||
}
|
}
|
||||||
$fqdn = "$fqdn$path";
|
$fqdn = "$fqdn$path";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$isDatabase) {
|
if (!$isDatabase) {
|
||||||
|
if ($savedService->fqdn) {
|
||||||
|
$fqdn = $savedService->fqdn . ',' . $fqdn;
|
||||||
|
} else {
|
||||||
|
$fqdn = $fqdn;
|
||||||
|
}
|
||||||
$savedService->fqdn = $fqdn;
|
$savedService->fqdn = $fqdn;
|
||||||
$savedService->save();
|
$savedService->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// data_forget($service, "environment.$variableName");
|
||||||
|
// $yaml = data_forget($yaml, "services.$serviceName.environment.$variableName");
|
||||||
|
// if (count(data_get($yaml, 'services.' . $serviceName . '.environment')) === 0) {
|
||||||
|
// $yaml = data_forget($yaml, "services.$serviceName.environment");
|
||||||
|
// }
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($value?->startsWith('$')) {
|
if ($value?->startsWith('$')) {
|
||||||
@@ -384,10 +444,17 @@ class Service extends BaseModel
|
|||||||
$forService = $value->afterLast('_');
|
$forService = $value->afterLast('_');
|
||||||
$generatedValue = null;
|
$generatedValue = null;
|
||||||
if ($command->value() === 'FQDN' || $command->value() === 'URL') {
|
if ($command->value() === 'FQDN' || $command->value() === 'URL') {
|
||||||
$fqdn = generateFqdn($this->server, $containerName);
|
if (Str::lower($forService) === $serviceName) {
|
||||||
|
$fqdn = generateFqdn($this->server, $containerName);
|
||||||
|
} else {
|
||||||
|
$fqdn = generateFqdn($this->server, Str::lower($forService) . '-' . $this->uuid);
|
||||||
|
}
|
||||||
if ($foundEnv) {
|
if ($foundEnv) {
|
||||||
$fqdn = data_get($foundEnv, 'value');
|
$fqdn = data_get($foundEnv, 'value');
|
||||||
} else {
|
} else {
|
||||||
|
if ($command->value() === 'URL') {
|
||||||
|
$fqdn = Str::of($fqdn)->after('://')->value();
|
||||||
|
}
|
||||||
EnvironmentVariable::create([
|
EnvironmentVariable::create([
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
'value' => $fqdn,
|
'value' => $fqdn,
|
||||||
@@ -396,10 +463,11 @@ class Service extends BaseModel
|
|||||||
'is_preview' => false,
|
'is_preview' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$isDatabase) {
|
if (!$isDatabase) {
|
||||||
$savedService->fqdn = $fqdn;
|
if ($command->value() === 'FQDN') {
|
||||||
$savedService->save();
|
$savedService->fqdn = $fqdn;
|
||||||
|
$savedService->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch ($command) {
|
switch ($command) {
|
||||||
@@ -472,25 +540,27 @@ class Service extends BaseModel
|
|||||||
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||||
if (!$isDatabase && $fqdns->count() > 0) {
|
if (!$isDatabase && $fqdns->count() > 0) {
|
||||||
if ($fqdns) {
|
if ($fqdns) {
|
||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($fqdns, $containerName, true));
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($fqdns, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data_set($service, 'labels', $serviceLabels->toArray());
|
data_set($service, 'labels', $serviceLabels->toArray());
|
||||||
data_forget($service, 'is_database');
|
data_forget($service, 'is_database');
|
||||||
data_set($service, 'restart', RESTART_MODE);
|
data_set($service, 'restart', RESTART_MODE);
|
||||||
data_set($service, 'container_name', $containerName);
|
data_set($service, 'container_name', $containerName);
|
||||||
data_forget($service, 'volumes.*.content');
|
data_forget($service, 'volumes.*.content');
|
||||||
data_forget($service, 'volumes.*.isDirectory');
|
data_forget($service, 'volumes.*.isDirectory');
|
||||||
|
|
||||||
// Remove unnecessary variables from service.environment
|
// Remove unnecessary variables from service.environment
|
||||||
$withoutServiceEnvs = collect([]);
|
// $withoutServiceEnvs = collect([]);
|
||||||
collect(data_get($service, 'environment'))->each(function ($value, $key) use ($withoutServiceEnvs) {
|
// collect(data_get($service, 'environment'))->each(function ($value, $key) use ($withoutServiceEnvs) {
|
||||||
if (!Str::of($key)->startsWith('$SERVICE_')) {
|
// ray($key, $value);
|
||||||
$withoutServiceEnvs->put($key, $value);
|
// if (!Str::of($key)->startsWith('$SERVICE_') && !Str::of($value)->startsWith('SERVICE_')) {
|
||||||
}
|
// $k = Str::of($value)->before("=");
|
||||||
});
|
// $v = Str::of($value)->after("=");
|
||||||
data_set($service, 'environment', $withoutServiceEnvs->toArray());
|
// $withoutServiceEnvs->put($k->value(), $v->value());
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// ray($withoutServiceEnvs);
|
||||||
|
// data_set($service, 'environment', $withoutServiceEnvs->toArray());
|
||||||
return $service;
|
return $service;
|
||||||
});
|
});
|
||||||
$finalServices = [
|
$finalServices = [
|
||||||
@@ -500,7 +570,7 @@ class Service extends BaseModel
|
|||||||
'networks' => $topLevelNetworks->toArray(),
|
'networks' => $topLevelNetworks->toArray(),
|
||||||
];
|
];
|
||||||
$this->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
$this->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||||
$this->docker_compose = Yaml::dump($finalServices, 10, 2);
|
$this->docker_compose = Yaml::dump($finalServices, 10, 2);
|
||||||
$this->save();
|
$this->save();
|
||||||
$this->saveComposeConfigs();
|
$this->saveComposeConfigs();
|
||||||
return collect([]);
|
return collect([]);
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ class ServiceApplication extends BaseModel
|
|||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public function getFilesFromServer()
|
public function getFilesFromServer(bool $isInit = false)
|
||||||
{
|
{
|
||||||
getFilesystemVolumesFromServer($this);
|
getFilesystemVolumesFromServer($this, $isInit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class ServiceDatabase extends BaseModel
|
class ServiceDatabase extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -26,8 +25,8 @@ class ServiceDatabase extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(LocalFileVolume::class, 'resource');
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
}
|
}
|
||||||
public function getFilesFromServer()
|
public function getFilesFromServer(bool $isInit = false)
|
||||||
{
|
{
|
||||||
getFilesystemVolumesFromServer($this);
|
getFilesystemVolumesFromServer($this, $isInit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,21 @@ class StandalonePostgresql extends BaseModel
|
|||||||
'is_readonly' => true
|
'is_readonly' => true
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
static::deleted(function ($database) {
|
static::deleting(function ($database) {
|
||||||
|
// Stop Container
|
||||||
|
instant_remote_process(
|
||||||
|
["docker rm -f {$database->uuid}"],
|
||||||
|
$database->destination->server,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// Stop TCP Proxy
|
||||||
|
if ($database->is_public) {
|
||||||
|
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server, false);
|
||||||
|
}
|
||||||
$database->scheduledBackups()->delete();
|
$database->scheduledBackups()->delete();
|
||||||
$database->persistentStorages()->delete();
|
$database->persistentStorages()->delete();
|
||||||
$database->environment_variables()->delete();
|
$database->environment_variables()->delete();
|
||||||
|
// Remove Volume
|
||||||
instant_remote_process(['docker volume rm postgres-data-' . $database->uuid], $database->destination->server, false);
|
instant_remote_process(['docker volume rm postgres-data-' . $database->uuid], $database->destination->server, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -65,6 +76,11 @@ class StandalonePostgresql extends BaseModel
|
|||||||
return $this->belongsTo(Environment::class);
|
return $this->belongsTo(Environment::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function fileStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
public function destination()
|
public function destination()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo();
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class Subscription extends Model
|
|||||||
}
|
}
|
||||||
if (isStripe()) {
|
if (isStripe()) {
|
||||||
if (!$this->stripe_plan_id) {
|
if (!$this->stripe_plan_id) {
|
||||||
return 'zero';
|
return 'zero';
|
||||||
}
|
}
|
||||||
$subscription = Subscription::where('id', $this->id)->first();
|
$subscription = Subscription::where('id', $this->id)->first();
|
||||||
if (!$subscription) {
|
if (!$subscription) {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||||||
}
|
}
|
||||||
return explode(',', $recipients);
|
return explode(',', $recipients);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function limits(): Attribute
|
public function limits(): Attribute
|
||||||
{
|
{
|
||||||
return Attribute::make(
|
return Attribute::make(
|
||||||
@@ -125,7 +126,7 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||||||
|
|
||||||
public function s3s()
|
public function s3s()
|
||||||
{
|
{
|
||||||
return $this->hasMany(S3Storage::class);
|
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) {
|
||||||
|
|||||||
@@ -6,8 +6,12 @@ use App\Notifications\Channels\SendsEmail;
|
|||||||
use App\Notifications\TransactionalEmails\ResetPassword as TransactionalEmailsResetPassword;
|
use App\Notifications\TransactionalEmails\ResetPassword as TransactionalEmailsResetPassword;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||||
use Laravel\Sanctum\HasApiTokens;
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
|
||||||
@@ -54,6 +58,23 @@ class User extends Authenticatable implements SendsEmail
|
|||||||
return $this->email;
|
return $this->email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function sendVerificationEmail()
|
||||||
|
{
|
||||||
|
$mail = new MailMessage();
|
||||||
|
$url = Url::temporarySignedRoute(
|
||||||
|
'verify.verify',
|
||||||
|
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
|
||||||
|
[
|
||||||
|
'id' => $this->getKey(),
|
||||||
|
'hash' => sha1($this->getEmailForVerification()),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$mail->view('emails.email-verification', [
|
||||||
|
'url' => $url,
|
||||||
|
]);
|
||||||
|
$mail->subject('Coolify Cloud: Verify your email.');
|
||||||
|
send_user_an_email($mail, $this->email);
|
||||||
|
}
|
||||||
public function sendPasswordResetNotification($token): void
|
public function sendPasswordResetNotification($token): void
|
||||||
{
|
{
|
||||||
$this->notify(new TransactionalEmailsResetPassword($token));
|
$this->notify(new TransactionalEmailsResetPassword($token));
|
||||||
@@ -61,7 +82,7 @@ class User extends Authenticatable implements SendsEmail
|
|||||||
|
|
||||||
public function isAdmin()
|
public function isAdmin()
|
||||||
{
|
{
|
||||||
return data_get($this->pivot,'role') === 'admin' || data_get($this->pivot,'role') === 'owner';
|
return data_get($this->pivot, 'role') === 'admin' || data_get($this->pivot, 'role') === 'owner';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isAdminFromSession()
|
public function isAdminFromSession()
|
||||||
@@ -79,7 +100,7 @@ class User extends Authenticatable implements SendsEmail
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$team = $teams->where('id', session('currentTeam')->id)->first();
|
$team = $teams->where('id', session('currentTeam')->id)->first();
|
||||||
$role = data_get($team,'pivot.role');
|
$role = data_get($team, 'pivot.role');
|
||||||
return $role === 'admin' || $role === 'owner';
|
return $role === 'admin' || $role === 'owner';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +117,10 @@ class User extends Authenticatable implements SendsEmail
|
|||||||
|
|
||||||
public function currentTeam()
|
public function currentTeam()
|
||||||
{
|
{
|
||||||
return Cache::remember('team:' . auth()->user()->id, 3600, function() {
|
return Cache::remember('team:' . auth()->user()->id, 3600, function () {
|
||||||
|
if (is_null(data_get(session('currentTeam'), 'id'))) {
|
||||||
|
return auth()->user()->teams[0];
|
||||||
|
}
|
||||||
return Team::find(session('currentTeam')->id);
|
return Team::find(session('currentTeam')->id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ class DeploymentFailed extends Notification implements ShouldQueue
|
|||||||
$pull_request_id = data_get($this->preview, 'pull_request_id', 0);
|
$pull_request_id = data_get($this->preview, 'pull_request_id', 0);
|
||||||
$fqdn = $this->fqdn;
|
$fqdn = $this->fqdn;
|
||||||
if ($pull_request_id === 0) {
|
if ($pull_request_id === 0) {
|
||||||
$mail->subject('❌ Deployment failed of ' . $this->application_name . '.');
|
$mail->subject('Coolify: Deployment failed of ' . $this->application_name . '.');
|
||||||
} else {
|
} else {
|
||||||
$fqdn = $this->preview->fqdn;
|
$fqdn = $this->preview->fqdn;
|
||||||
$mail->subject('❌ Deployment failed of pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . '.');
|
$mail->subject('Coolify: Deployment failed of pull request #' . $this->preview->pull_request_id . ' of ' . $this->application_name . '.');
|
||||||
}
|
}
|
||||||
$mail->view('emails.application-deployment-failed', [
|
$mail->view('emails.application-deployment-failed', [
|
||||||
'name' => $this->application_name,
|
'name' => $this->application_name,
|
||||||
@@ -69,10 +69,10 @@ class DeploymentFailed extends Notification implements ShouldQueue
|
|||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
if ($this->preview) {
|
if ($this->preview) {
|
||||||
$message = '❌ Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: ';
|
$message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: ';
|
||||||
$message .= '[View Deployment Logs](' . $this->deployment_url . ')';
|
$message .= '[View Deployment Logs](' . $this->deployment_url . ')';
|
||||||
} else {
|
} else {
|
||||||
$message = '❌ Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): ';
|
$message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): ';
|
||||||
$message .= '[View Deployment Logs](' . $this->deployment_url . ')';
|
$message .= '[View Deployment Logs](' . $this->deployment_url . ')';
|
||||||
}
|
}
|
||||||
return $message;
|
return $message;
|
||||||
@@ -80,9 +80,9 @@ class DeploymentFailed extends Notification implements ShouldQueue
|
|||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
if ($this->preview) {
|
if ($this->preview) {
|
||||||
$message = '❌ Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: ';
|
$message = 'Coolify: Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: ';
|
||||||
} else {
|
} else {
|
||||||
$message = '❌ Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): ';
|
$message = 'Coolify: Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): ';
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
"message" => $message,
|
"message" => $message,
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
|||||||
$pull_request_id = data_get($this->preview, 'pull_request_id', 0);
|
$pull_request_id = data_get($this->preview, 'pull_request_id', 0);
|
||||||
$fqdn = $this->fqdn;
|
$fqdn = $this->fqdn;
|
||||||
if ($pull_request_id === 0) {
|
if ($pull_request_id === 0) {
|
||||||
$mail->subject("✅ New version is deployed of {$this->application_name}");
|
$mail->subject("Coolify: New version is deployed of {$this->application_name}");
|
||||||
} else {
|
} else {
|
||||||
$fqdn = $this->preview->fqdn;
|
$fqdn = $this->preview->fqdn;
|
||||||
$mail->subject("✅ Pull request #{$pull_request_id} of {$this->application_name} deployed successfully");
|
$mail->subject("Coolify: Pull request #{$pull_request_id} of {$this->application_name} deployed successfully");
|
||||||
}
|
}
|
||||||
$mail->view('emails.application-deployment-success', [
|
$mail->view('emails.application-deployment-success', [
|
||||||
'name' => $this->application_name,
|
'name' => $this->application_name,
|
||||||
@@ -69,7 +69,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
|||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
if ($this->preview) {
|
if ($this->preview) {
|
||||||
$message = '✅ New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . '
|
$message = 'Coolify: New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . '
|
||||||
|
|
||||||
';
|
';
|
||||||
if ($this->preview->fqdn) {
|
if ($this->preview->fqdn) {
|
||||||
@@ -77,7 +77,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
|||||||
}
|
}
|
||||||
$message .= '[Deployment logs](' . $this->deployment_url . ')';
|
$message .= '[Deployment logs](' . $this->deployment_url . ')';
|
||||||
} else {
|
} else {
|
||||||
$message = '✅ New version successfully deployed of ' . $this->application_name . '
|
$message = 'Coolify: New version successfully deployed of ' . $this->application_name . '
|
||||||
|
|
||||||
';
|
';
|
||||||
if ($this->fqdn) {
|
if ($this->fqdn) {
|
||||||
@@ -90,7 +90,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
|||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
if ($this->preview) {
|
if ($this->preview) {
|
||||||
$message = '✅ New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . '';
|
$message = 'Coolify: New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . '';
|
||||||
if ($this->preview->fqdn) {
|
if ($this->preview->fqdn) {
|
||||||
$buttons[] = [
|
$buttons[] = [
|
||||||
"text" => "Open Application",
|
"text" => "Open Application",
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class StatusChanged extends Notification implements ShouldQueue
|
|||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$fqdn = $this->fqdn;
|
$fqdn = $this->fqdn;
|
||||||
$mail->subject("⛔ {$this->application_name} has been stopped");
|
$mail->subject("Coolify: {$this->application_name} has been stopped");
|
||||||
$mail->view('emails.application-status-changes', [
|
$mail->view('emails.application-status-changes', [
|
||||||
'name' => $this->application_name,
|
'name' => $this->application_name,
|
||||||
'fqdn' => $fqdn,
|
'fqdn' => $fqdn,
|
||||||
@@ -56,7 +56,7 @@ class StatusChanged extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = '⛔ ' . $this->application_name . ' has been stopped.
|
$message = 'Coolify: ' . $this->application_name . ' has been stopped.
|
||||||
|
|
||||||
';
|
';
|
||||||
$message .= '[Open Application in Coolify](' . $this->application_url . ')';
|
$message .= '[Open Application in Coolify](' . $this->application_url . ')';
|
||||||
@@ -64,7 +64,7 @@ class StatusChanged extends Notification implements ShouldQueue
|
|||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
$message = '⛔ ' . $this->application_name . ' has been stopped.';
|
$message = 'Coolify: ' . $this->application_name . ' has been stopped.';
|
||||||
return [
|
return [
|
||||||
"message" => $message,
|
"message" => $message,
|
||||||
"buttons" => [
|
"buttons" => [
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ class EmailChannel
|
|||||||
try {
|
try {
|
||||||
$this->bootConfigs($notifiable);
|
$this->bootConfigs($notifiable);
|
||||||
$recepients = $notifiable->getRecepients($notification);
|
$recepients = $notifiable->getRecepients($notification);
|
||||||
ray($recepients);
|
|
||||||
if (count($recepients) === 0) {
|
if (count($recepients) === 0) {
|
||||||
throw new Exception('No email recipients found');
|
throw new Exception('No email recipients found');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
|||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject("✅ Container ({$this->name}) has been restarted automatically on {$this->server->name}");
|
$mail->subject("Coolify: Container ({$this->name}) has been restarted automatically on {$this->server->name}");
|
||||||
$mail->view('emails.container-restarted', [
|
$mail->view('emails.container-restarted', [
|
||||||
'containerName' => $this->name,
|
'containerName' => $this->name,
|
||||||
'serverName' => $this->server->name,
|
'serverName' => $this->server->name,
|
||||||
@@ -38,12 +38,12 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = "✅ Container ({$this->name}) has been restarted automatically on {$this->server->name}";
|
$message = "Coolify: Container ({$this->name}) has been restarted automatically on {$this->server->name}";
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
$message = "✅ Container ({$this->name}) has been restarted automatically on {$this->server->name}";
|
$message = "Coolify: Container ({$this->name}) has been restarted automatically on {$this->server->name}";
|
||||||
$payload = [
|
$payload = [
|
||||||
"message" => $message,
|
"message" => $message,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class ContainerStopped extends Notification implements ShouldQueue
|
|||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject("⛔ Container {$this->name} has been stopped on {$this->server->name}");
|
$mail->subject("Coolify: Container ({$this->name}) has been stopped on {$this->server->name}");
|
||||||
$mail->view('emails.container-stopped', [
|
$mail->view('emails.container-stopped', [
|
||||||
'containerName' => $this->name,
|
'containerName' => $this->name,
|
||||||
'serverName' => $this->server->name,
|
'serverName' => $this->server->name,
|
||||||
@@ -37,12 +37,12 @@ class ContainerStopped extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = "⛔ Container {$this->name} has been stopped on {$this->server->name}";
|
$message = "Coolify: Container ({$this->name}) has been stopped on {$this->server->name}";
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
$message = "⛔ Container ($this->name} has been stopped on {$this->server->name}";
|
$message = "Coolify: Container ($this->name} has been stopped on {$this->server->name}";
|
||||||
$payload = [
|
$payload = [
|
||||||
"message" => $message,
|
"message" => $message,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
namespace App\Notifications\Database;
|
namespace App\Notifications\Database;
|
||||||
|
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
|
use App\Notifications\Channels\DiscordChannel;
|
||||||
|
use App\Notifications\Channels\TelegramChannel;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Channels\MailChannel;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
@@ -24,13 +27,13 @@ class BackupFailed extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
return setNotificationChannels($notifiable, 'database_backups');
|
return [DiscordChannel::class, TelegramChannel::class, MailChannel::class];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject("❌ [ACTION REQUIRED] Backup FAILED for {$this->database->name}");
|
$mail->subject("Coolify: [ACTION REQUIRED] Backup FAILED for {$this->database->name}");
|
||||||
$mail->view('emails.backup-failed', [
|
$mail->view('emails.backup-failed', [
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'frequency' => $this->frequency,
|
'frequency' => $this->frequency,
|
||||||
@@ -41,11 +44,11 @@ class BackupFailed extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
return "❌ Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}";
|
return "Coolify: Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}";
|
||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
$message = "❌ Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}";
|
$message = "Coolify: Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}";
|
||||||
return [
|
return [
|
||||||
"message" => $message,
|
"message" => $message,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class BackupSuccess extends Notification implements ShouldQueue
|
|||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject("✅ Backup successfully done for {$this->database->name}");
|
$mail->subject("Coolify: Backup successfully done for {$this->database->name}");
|
||||||
$mail->view('emails.backup-success', [
|
$mail->view('emails.backup-success', [
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'frequency' => $this->frequency,
|
'frequency' => $this->frequency,
|
||||||
@@ -40,11 +40,11 @@ class BackupSuccess extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
return "✅ Database backup for {$this->name} with frequency of {$this->frequency} was successful.";
|
return "Coolify: Database backup for {$this->name} with frequency of {$this->frequency} was successful.";
|
||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
$message = "✅ Database backup for {$this->name} with frequency of {$this->frequency} was successful.";
|
$message = "Coolify: Database backup for {$this->name} with frequency of {$this->frequency} was successful.";
|
||||||
return [
|
return [
|
||||||
"message" => $message,
|
"message" => $message,
|
||||||
];
|
];
|
||||||
|
|||||||
66
app/Notifications/Server/Revived.php
Normal file
66
app/Notifications/Server/Revived.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications\Server;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use App\Notifications\Channels\DiscordChannel;
|
||||||
|
use App\Notifications\Channels\EmailChannel;
|
||||||
|
use App\Notifications\Channels\TelegramChannel;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class Revived extends Notification implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public $tries = 1;
|
||||||
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
if ($this->server->unreachable_email_sent === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function via(object $notifiable): array
|
||||||
|
{
|
||||||
|
$channels = [];
|
||||||
|
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||||
|
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||||
|
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||||
|
|
||||||
|
if ($isDiscordEnabled) {
|
||||||
|
$channels[] = DiscordChannel::class;
|
||||||
|
}
|
||||||
|
if ($isEmailEnabled ) {
|
||||||
|
$channels[] = EmailChannel::class;
|
||||||
|
}
|
||||||
|
if ($isTelegramEnabled) {
|
||||||
|
$channels[] = TelegramChannel::class;
|
||||||
|
}
|
||||||
|
return $channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toMail(): MailMessage
|
||||||
|
{
|
||||||
|
$mail = new MailMessage();
|
||||||
|
$mail->subject("Coolify: Server ({$this->server->name}) revived.");
|
||||||
|
$mail->view('emails.server-revived', [
|
||||||
|
'name' => $this->server->name,
|
||||||
|
]);
|
||||||
|
return $mail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toDiscord(): string
|
||||||
|
{
|
||||||
|
$message = "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!";
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
public function toTelegram(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"message" => "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,9 @@
|
|||||||
namespace App\Notifications\Server;
|
namespace App\Notifications\Server;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Notifications\Channels\DiscordChannel;
|
||||||
|
use App\Notifications\Channels\EmailChannel;
|
||||||
|
use App\Notifications\Channels\TelegramChannel;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
@@ -20,13 +23,27 @@ class Unreachable extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
return setNotificationChannels($notifiable, 'status_changes');
|
$channels = [];
|
||||||
|
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||||
|
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||||
|
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||||
|
|
||||||
|
if ($isDiscordEnabled) {
|
||||||
|
$channels[] = DiscordChannel::class;
|
||||||
|
}
|
||||||
|
if ($isEmailEnabled ) {
|
||||||
|
$channels[] = EmailChannel::class;
|
||||||
|
}
|
||||||
|
if ($isTelegramEnabled) {
|
||||||
|
$channels[] = TelegramChannel::class;
|
||||||
|
}
|
||||||
|
return $channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject("⛔ Server ({$this->server->name}) is unreachable after trying to connect to it 5 times");
|
$mail->subject("Coolify: Server ({$this->server->name}) is unreachable after trying to connect to it 5 times");
|
||||||
$mail->view('emails.server-lost-connection', [
|
$mail->view('emails.server-lost-connection', [
|
||||||
'name' => $this->server->name,
|
'name' => $this->server->name,
|
||||||
]);
|
]);
|
||||||
@@ -35,13 +52,13 @@ class Unreachable extends Notification implements ShouldQueue
|
|||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = "⛔ Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: You have to validate your server again after you fix the issue.";
|
$message = "Coolify: Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations.";
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"message" => "⛔ Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: You have to validate your server again after you fix the issue."
|
"message" => "Coolify: Server '{$this->server->name}' is unreachable after trying to connect to it 5 times. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations."
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ class Test extends Notification implements ShouldQueue
|
|||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject("Test Email");
|
$mail->subject("Coolify: Test Email");
|
||||||
$mail->view('emails.test');
|
$mail->view('emails.test');
|
||||||
return $mail;
|
return $mail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
$message = 'This is a test Discord notification from Coolify.';
|
$message = 'Coolify: This is a test Discord notification from Coolify.';
|
||||||
$message .= "\n\n";
|
$message .= "\n\n";
|
||||||
$message .= '[Go to your dashboard](' . base_url() . ')';
|
$message .= '[Go to your dashboard](' . base_url() . ')';
|
||||||
return $message;
|
return $message;
|
||||||
@@ -39,7 +39,7 @@ class Test extends Notification implements ShouldQueue
|
|||||||
public function toTelegram(): array
|
public function toTelegram(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"message" => 'This is a test Telegram notification from Coolify.',
|
"message" => 'Coolify: This is a test Telegram notification from Coolify.',
|
||||||
"buttons" => [
|
"buttons" => [
|
||||||
[
|
[
|
||||||
"text" => "Go to your dashboard",
|
"text" => "Go to your dashboard",
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class InvitationLink extends Notification implements ShouldQueue
|
|||||||
$invitation_team = Team::find($invitation->team->id);
|
$invitation_team = Team::find($invitation->team->id);
|
||||||
|
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject('Invitation for ' . $invitation_team->name);
|
$mail->subject('Coolify: Invitation for ' . $invitation_team->name);
|
||||||
$mail->view('emails.invitation-link', [
|
$mail->view('emails.invitation-link', [
|
||||||
'team' => $invitation_team->name,
|
'team' => $invitation_team->name,
|
||||||
'email' => $this->user->email,
|
'email' => $this->user->email,
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class ResetPassword extends Notification
|
|||||||
protected function buildMailMessage($url)
|
protected function buildMailMessage($url)
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject('Reset Password');
|
$mail->subject('Coolify: Reset Password');
|
||||||
$mail->view('emails.reset-password', ['url' => $url, 'count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]);
|
$mail->view('emails.reset-password', ['url' => $url, 'count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]);
|
||||||
return $mail;
|
return $mail;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class Test extends Notification implements ShouldQueue
|
|||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
{
|
{
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->subject('Test Email');
|
$mail->subject('Coolify: Test Email');
|
||||||
$mail->view('emails.test');
|
$mail->view('emails.test');
|
||||||
return $mail;
|
return $mail;
|
||||||
}
|
}
|
||||||
|
|||||||
27
app/View/Components/Server/Sidebar.php
Normal file
27
app/View/Components/Server/Sidebar.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Components\Server;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
|
class Sidebar extends Component
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new component instance.
|
||||||
|
*/
|
||||||
|
public function __construct(public Server $server, public $parameters)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the view / contents that represent the component.
|
||||||
|
*/
|
||||||
|
public function render(): View|Closure|string
|
||||||
|
{
|
||||||
|
return view('components.server.sidebar');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Enums\ProxyTypes;
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
function getCurrentApplicationContainerStatus(Server $server, int $id): Collection
|
function getCurrentApplicationContainerStatus(Server $server, int $id): Collection
|
||||||
{
|
{
|
||||||
@@ -104,16 +104,16 @@ function getContainerStatus(Server $server, string $container_id, bool $all_data
|
|||||||
return data_get($container[0], 'State.Status', 'exited');
|
return data_get($container[0], 'State.Status', 'exited');
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateApplicationContainerName(Application $application)
|
function generateApplicationContainerName(Application $application, $pull_request_id = 0)
|
||||||
{
|
{
|
||||||
$now = now()->format('Hisu');
|
$now = now()->format('Hisu');
|
||||||
if ($application->pull_request_id !== 0 && $application->pull_request_id !== null) {
|
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
||||||
return $application->uuid . '-pr-' . $application->pull_request_id;
|
return $application->uuid . '-pr-' . $pull_request_id;
|
||||||
} else {
|
} else {
|
||||||
return $application->uuid . '-' . $now;
|
return $application->uuid . '-' . $now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function get_port_from_dockerfile($dockerfile): int
|
function get_port_from_dockerfile($dockerfile): int|null
|
||||||
{
|
{
|
||||||
$dockerfile_array = explode("\n", $dockerfile);
|
$dockerfile_array = explode("\n", $dockerfile);
|
||||||
$found_exposed_port = null;
|
$found_exposed_port = null;
|
||||||
@@ -127,7 +127,7 @@ function get_port_from_dockerfile($dockerfile): int
|
|||||||
if ($found_exposed_port) {
|
if ($found_exposed_port) {
|
||||||
return (int)$found_exposed_port->value();
|
return (int)$found_exposed_port->value();
|
||||||
}
|
}
|
||||||
return 80;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'application', $subType = null, $subId = null)
|
function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'application', $subType = null, $subId = null)
|
||||||
@@ -147,20 +147,22 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
|
|||||||
}
|
}
|
||||||
return $labels;
|
return $labels;
|
||||||
}
|
}
|
||||||
function fqdnLabelsForTraefik(Collection $domains, $container_name, $is_force_https_enabled)
|
function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled, $onlyPort = null)
|
||||||
{
|
{
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
$labels->push('traefik.enable=true');
|
$labels->push('traefik.enable=true');
|
||||||
foreach ($domains as $domain) {
|
foreach ($domains as $domain) {
|
||||||
|
$uuid = (string)new Cuid2(7);
|
||||||
$url = Url::fromString($domain);
|
$url = Url::fromString($domain);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$path = $url->getPath();
|
$path = $url->getPath();
|
||||||
$schema = $url->getScheme();
|
$schema = $url->getScheme();
|
||||||
$port = $url->getPort();
|
$port = $url->getPort();
|
||||||
$slug = Str::slug($host . $path);
|
if (is_null($port) && !is_null($onlyPort)) {
|
||||||
|
$port = $onlyPort;
|
||||||
$http_label = "{$container_name}-{$slug}-http";
|
}
|
||||||
$https_label = "{$container_name}-{$slug}-https";
|
$http_label = "{$uuid}-http";
|
||||||
|
$https_label = "{$uuid}-https";
|
||||||
|
|
||||||
if ($schema === 'https') {
|
if ($schema === 'https') {
|
||||||
// Set labels for https
|
// Set labels for https
|
||||||
@@ -203,14 +205,17 @@ function fqdnLabelsForTraefik(Collection $domains, $container_name, $is_force_ht
|
|||||||
|
|
||||||
return $labels;
|
return $labels;
|
||||||
}
|
}
|
||||||
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
|
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null, $ports): array
|
||||||
{
|
{
|
||||||
|
$onlyPort = null;
|
||||||
|
if (count($ports) === 1) {
|
||||||
|
$onlyPort = $ports[0];
|
||||||
|
}
|
||||||
$pull_request_id = data_get($preview, 'pull_request_id', 0);
|
$pull_request_id = data_get($preview, 'pull_request_id', 0);
|
||||||
$container_name = generateApplicationContainerName($application);
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
$appId = $application->id;
|
$appId = $application->id;
|
||||||
if ($pull_request_id !== 0) {
|
if ($pull_request_id !== 0 && $pull_request_id !== null) {
|
||||||
$appId = $appId . '-pr-' . $application->pull_request_id;
|
$appId = $appId . '-pr-' . $pull_request_id;
|
||||||
}
|
}
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
$labels = $labels->merge(defaultLabels($appId, $container_name, $pull_request_id));
|
$labels = $labels->merge(defaultLabels($appId, $container_name, $pull_request_id));
|
||||||
@@ -221,7 +226,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
|||||||
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
||||||
}
|
}
|
||||||
// Add Traefik labels no matter which proxy is selected
|
// Add Traefik labels no matter which proxy is selected
|
||||||
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $container_name, $application->settings->is_force_https_enabled));
|
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $application->settings->is_force_https_enabled,$onlyPort));
|
||||||
}
|
}
|
||||||
return $labels->all();
|
return $labels->all();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ function generate_github_jwt_token(GithubApp $source)
|
|||||||
return $issuedToken;
|
return $issuedToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
function git_api(GithubApp|GitlabApp $source, string $endpoint, string $method = 'get', array|null $data = null, bool $throwError = true)
|
function githubApi(GithubApp|GitlabApp $source, string $endpoint, string $method = 'get', array|null $data = null, bool $throwError = true)
|
||||||
{
|
{
|
||||||
if ($source->getMorphClass() == 'App\Models\GithubApp') {
|
if ($source->getMorphClass() == 'App\Models\GithubApp') {
|
||||||
if ($source->is_public) {
|
if ($source->is_public) {
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ function generate_default_proxy_configuration(Server $server)
|
|||||||
];
|
];
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$config['services']['traefik']['command'][] = "--log.level=debug";
|
$config['services']['traefik']['command'][] = "--log.level=debug";
|
||||||
|
$config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log";
|
||||||
|
$config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100";
|
||||||
}
|
}
|
||||||
$config = Yaml::dump($config, 4, 2);
|
$config = Yaml::dump($config, 4, 2);
|
||||||
SaveConfiguration::run($server, $config);
|
SaveConfiguration::run($server, $config);
|
||||||
@@ -204,17 +206,23 @@ stream {
|
|||||||
proxy_pass $database->uuid:5432;
|
proxy_pass $database->uuid:5432;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EOF;
|
||||||
|
$dockerfile = <<< EOF
|
||||||
|
FROM nginx:stable-alpine
|
||||||
|
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
EOF;
|
EOF;
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
$containerName => [
|
$containerName => [
|
||||||
|
'build' => [
|
||||||
|
'context' => $configuration_dir,
|
||||||
|
'dockerfile' => 'Dockerfile',
|
||||||
|
],
|
||||||
'image' => "nginx:stable-alpine",
|
'image' => "nginx:stable-alpine",
|
||||||
'container_name' => $containerName,
|
'container_name' => $containerName,
|
||||||
'restart' => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
'volumes' => [
|
|
||||||
"$configuration_dir/nginx.conf:/etc/nginx/nginx.conf:ro",
|
|
||||||
],
|
|
||||||
'ports' => [
|
'ports' => [
|
||||||
"$database->public_port:$database->public_port",
|
"$database->public_port:$database->public_port",
|
||||||
],
|
],
|
||||||
@@ -243,13 +251,13 @@ EOF;
|
|||||||
];
|
];
|
||||||
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
||||||
$nginxconf_base64 = base64_encode($nginxconf);
|
$nginxconf_base64 = base64_encode($nginxconf);
|
||||||
|
$dockerfile_base64 = base64_encode($dockerfile);
|
||||||
instant_remote_process([
|
instant_remote_process([
|
||||||
"mkdir -p $configuration_dir",
|
"mkdir -p $configuration_dir",
|
||||||
|
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||||
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
||||||
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
||||||
"docker compose --project-directory {$configuration_dir} up -d >/dev/null",
|
"docker compose --project-directory {$configuration_dir} up --build -d >/dev/null",
|
||||||
|
|
||||||
|
|
||||||
], $database->destination->server);
|
], $database->destination->server);
|
||||||
}
|
}
|
||||||
function stopPostgresProxy(StandalonePostgresql $database)
|
function stopPostgresProxy(StandalonePostgresql $database)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use App\Models\Application;
|
|||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Notifications\Server\Revived;
|
||||||
|
use App\Notifications\Server\Unreachable;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -85,7 +87,7 @@ function generateSshCommand(Server $server, string $command, bool $isMux = true)
|
|||||||
if ($isMux && config('coolify.mux_enabled')) {
|
if ($isMux && config('coolify.mux_enabled')) {
|
||||||
$ssh_command .= '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/ssh/mux/%h_%p_%r ';
|
$ssh_command .= '-o ControlMaster=auto -o ControlPersist=1m -o ControlPath=/var/www/html/storage/app/ssh/mux/%h_%p_%r ';
|
||||||
}
|
}
|
||||||
if (data_get($server,'settings.is_cloudflare_tunnel')) {
|
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
|
||||||
$ssh_command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
|
$ssh_command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
|
||||||
}
|
}
|
||||||
$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";
|
$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";
|
||||||
@@ -122,13 +124,14 @@ function instant_remote_process(Collection|array $command, Server $server, $thro
|
|||||||
}
|
}
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
function excludeCertainErrors(string $errorOutput, ?int $exitCode = null) {
|
function excludeCertainErrors(string $errorOutput, ?int $exitCode = null)
|
||||||
|
{
|
||||||
$ignoredErrors = collect([
|
$ignoredErrors = collect([
|
||||||
'Permission denied (publickey',
|
'Permission denied (publickey',
|
||||||
'Could not resolve hostname',
|
'Could not resolve hostname',
|
||||||
]);
|
]);
|
||||||
$ignored = false;
|
$ignored = false;
|
||||||
foreach ($ignoredErrors as $ignoredError) {
|
foreach ($ignoredErrors as $ignoredError) {
|
||||||
if (Str::contains($errorOutput, $ignoredError)) {
|
if (Str::contains($errorOutput, $ignoredError)) {
|
||||||
$ignored = true;
|
$ignored = true;
|
||||||
break;
|
break;
|
||||||
@@ -177,45 +180,55 @@ function refresh_server_connection(PrivateKey $private_key)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateServer(Server $server, bool $throwError = false)
|
// function validateServer(Server $server, bool $throwError = false)
|
||||||
{
|
// {
|
||||||
try {
|
// try {
|
||||||
$uptime = instant_remote_process(['uptime'], $server, $throwError);
|
// $uptime = instant_remote_process(['uptime'], $server, $throwError);
|
||||||
if (!$uptime) {
|
// if (!$uptime) {
|
||||||
$server->settings->is_reachable = false;
|
// $server->settings->is_reachable = false;
|
||||||
return [
|
// $server->team->notify(new Unreachable($server));
|
||||||
"uptime" => null,
|
// $server->unreachable_email_sent = true;
|
||||||
"dockerVersion" => null,
|
// $server->save();
|
||||||
];
|
// return [
|
||||||
}
|
// "uptime" => null,
|
||||||
$server->settings->is_reachable = true;
|
// "dockerVersion" => null,
|
||||||
instant_remote_process(["docker ps"], $server, $throwError);
|
// ];
|
||||||
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, $throwError);
|
// }
|
||||||
if (!$dockerVersion) {
|
// $server->settings->is_reachable = true;
|
||||||
$dockerVersion = null;
|
// instant_remote_process(["docker ps"], $server, $throwError);
|
||||||
return [
|
// $dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $server, $throwError);
|
||||||
"uptime" => $uptime,
|
// if (!$dockerVersion) {
|
||||||
"dockerVersion" => null,
|
// $dockerVersion = null;
|
||||||
];
|
// return [
|
||||||
}
|
// "uptime" => $uptime,
|
||||||
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
// "dockerVersion" => null,
|
||||||
if (is_null($dockerVersion)) {
|
// ];
|
||||||
$server->settings->is_usable = false;
|
// }
|
||||||
} else {
|
// $dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||||
$server->settings->is_usable = true;
|
// if (is_null($dockerVersion)) {
|
||||||
}
|
// $server->settings->is_usable = false;
|
||||||
return [
|
// } else {
|
||||||
"uptime" => $uptime,
|
// $server->settings->is_usable = true;
|
||||||
"dockerVersion" => $dockerVersion,
|
// if (data_get($server, 'unreachable_email_sent') === true) {
|
||||||
];
|
// $server->team->notify(new Revived($server));
|
||||||
} catch (\Throwable $e) {
|
// $server->unreachable_email_sent = false;
|
||||||
$server->settings->is_reachable = false;
|
// $server->save();
|
||||||
$server->settings->is_usable = false;
|
// }
|
||||||
throw $e;
|
// }
|
||||||
} finally {
|
// return [
|
||||||
if (data_get($server, 'settings')) $server->settings->save();
|
// "uptime" => $uptime,
|
||||||
}
|
// "dockerVersion" => $dockerVersion,
|
||||||
}
|
// ];
|
||||||
|
// } catch (\Throwable $e) {
|
||||||
|
// $server->settings->is_reachable = false;
|
||||||
|
// $server->settings->is_usable = false;
|
||||||
|
// throw $e;
|
||||||
|
// } finally {
|
||||||
|
// if (data_get($server, 'settings')) {
|
||||||
|
// $server->settings->save();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
function checkRequiredCommands(Server $server)
|
function checkRequiredCommands(Server $server)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ function serviceStatus(Service $service)
|
|||||||
}
|
}
|
||||||
return 'exited';
|
return 'exited';
|
||||||
}
|
}
|
||||||
function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneService)
|
function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneService, bool $isInit = false)
|
||||||
{
|
{
|
||||||
// TODO: make this async
|
// TODO: make this async
|
||||||
try {
|
try {
|
||||||
@@ -85,24 +85,39 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase $oneS
|
|||||||
} else {
|
} else {
|
||||||
$fileLocation = $path;
|
$fileLocation = $path;
|
||||||
}
|
}
|
||||||
|
ray($path,$fileLocation);
|
||||||
|
// Exists and is a file
|
||||||
$isFile = instant_remote_process(["test -f $fileLocation && echo OK || echo NOK"], $server);
|
$isFile = instant_remote_process(["test -f $fileLocation && echo OK || echo NOK"], $server);
|
||||||
|
// Exists and is a directory
|
||||||
$isDir = instant_remote_process(["test -d $fileLocation && echo OK || echo NOK"], $server);
|
$isDir = instant_remote_process(["test -d $fileLocation && echo OK || echo NOK"], $server);
|
||||||
if ($isFile == 'OK' && !$fileVolume->is_directory) {
|
|
||||||
|
if ($isFile == 'OK') {
|
||||||
|
// If its a file & exists
|
||||||
$filesystemContent = instant_remote_process(["cat $fileLocation"], $server);
|
$filesystemContent = instant_remote_process(["cat $fileLocation"], $server);
|
||||||
if (base64_encode($filesystemContent) != base64_encode($content)) {
|
$fileVolume->content = $filesystemContent;
|
||||||
$fileVolume->content = $filesystemContent;
|
$fileVolume->is_directory = false;
|
||||||
$fileVolume->save();
|
$fileVolume->save();
|
||||||
}
|
} else if ($isDir == 'OK') {
|
||||||
} else {
|
// If its a directory & exists
|
||||||
if ($isDir == 'OK') {
|
$fileVolume->content = null;
|
||||||
$fileVolume->content = null;
|
$fileVolume->is_directory = true;
|
||||||
$fileVolume->is_directory = true;
|
$fileVolume->save();
|
||||||
$fileVolume->save();
|
} else if ($isFile == 'NOK' && $isDir == 'NOK' && !$fileVolume->is_directory && $isInit && $content) {
|
||||||
} else {
|
// Does not exists (no dir or file), not flagged as directory, is init, has content
|
||||||
$fileVolume->content = null;
|
$fileVolume->content = $content;
|
||||||
$fileVolume->is_directory = false;
|
$fileVolume->is_directory = false;
|
||||||
$fileVolume->save();
|
$fileVolume->save();
|
||||||
}
|
$content = base64_encode($content);
|
||||||
|
$dir = Str::of($fileLocation)->dirname();
|
||||||
|
instant_remote_process([
|
||||||
|
"mkdir -p $dir",
|
||||||
|
"echo '$content' | base64 -d > $fileLocation"
|
||||||
|
], $server);
|
||||||
|
} else if ($isFile == 'NOK' && $isDir == 'NOK' && $fileVolume->is_directory && $isInit) {
|
||||||
|
$fileVolume->content = null;
|
||||||
|
$fileVolume->is_directory = true;
|
||||||
|
$fileVolume->save();
|
||||||
|
instant_remote_process(["mkdir -p $fileLocation"], $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -122,13 +137,18 @@ function updateCompose($resource)
|
|||||||
|
|
||||||
// Update FQDN
|
// Update FQDN
|
||||||
$variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper();
|
$variableName = "SERVICE_FQDN_" . Str::of($resource->name)->upper();
|
||||||
ray($variableName);
|
|
||||||
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
||||||
if ($generatedEnv) {
|
if ($generatedEnv) {
|
||||||
$generatedEnv->value = $resource->fqdn;
|
$generatedEnv->value = $resource->fqdn;
|
||||||
$generatedEnv->save();
|
$generatedEnv->save();
|
||||||
}
|
}
|
||||||
|
$variableName = "SERVICE_URL_" . Str::of($resource->name)->upper();
|
||||||
|
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
|
||||||
|
if ($generatedEnv) {
|
||||||
|
$url = Str::of($resource->fqdn)->after('://');
|
||||||
|
$generatedEnv->value = $url;
|
||||||
|
$generatedEnv->save();
|
||||||
|
}
|
||||||
|
|
||||||
$dockerComposeRaw = Yaml::dump($dockerCompose, 10, 2);
|
$dockerComposeRaw = Yaml::dump($dockerCompose, 10, 2);
|
||||||
$resource->service->docker_compose_raw = $dockerComposeRaw;
|
$resource->service->docker_compose_raw = $dockerComposeRaw;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use Illuminate\Mail\Message;
|
|||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
@@ -309,6 +310,7 @@ function send_internal_notification(string $message): void
|
|||||||
$baseUrl = config('app.name');
|
$baseUrl = config('app.name');
|
||||||
$team = Team::find(0);
|
$team = Team::find(0);
|
||||||
$team->notify(new GeneralNotification("👀 {$baseUrl}: " . $message));
|
$team->notify(new GeneralNotification("👀 {$baseUrl}: " . $message));
|
||||||
|
ray("👀 {$baseUrl}: " . $message);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
}
|
}
|
||||||
@@ -342,6 +344,15 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function isTestEmailEnabled($notifiable)
|
||||||
|
{
|
||||||
|
if (data_get($notifiable, 'use_instance_email_settings') && isInstanceAdmin()) {
|
||||||
|
return true;
|
||||||
|
} else if (data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') && auth()->user()->isAdminFromSession()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
function isEmailEnabled($notifiable)
|
function isEmailEnabled($notifiable)
|
||||||
{
|
{
|
||||||
return data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') || data_get($notifiable, 'use_instance_email_settings');
|
return data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') || data_get($notifiable, 'use_instance_email_settings');
|
||||||
@@ -352,13 +363,14 @@ function setNotificationChannels($notifiable, $event)
|
|||||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||||
|
$isSubscribedToEmailEvent = data_get($notifiable, "smtp_notifications_$event");
|
||||||
$isSubscribedToDiscordEvent = data_get($notifiable, "discord_notifications_$event");
|
$isSubscribedToDiscordEvent = data_get($notifiable, "discord_notifications_$event");
|
||||||
$isSubscribedToTelegramEvent = data_get($notifiable, "telegram_notifications_$event");
|
$isSubscribedToTelegramEvent = data_get($notifiable, "telegram_notifications_$event");
|
||||||
|
|
||||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
||||||
$channels[] = DiscordChannel::class;
|
$channels[] = DiscordChannel::class;
|
||||||
}
|
}
|
||||||
if ($isEmailEnabled) {
|
if ($isEmailEnabled && $isSubscribedToEmailEvent) {
|
||||||
$channels[] = EmailChannel::class;
|
$channels[] = EmailChannel::class;
|
||||||
}
|
}
|
||||||
if ($isTelegramEnabled && $isSubscribedToTelegramEvent) {
|
if ($isTelegramEnabled && $isSubscribedToTelegramEvent) {
|
||||||
@@ -405,7 +417,7 @@ function generateFqdn(Server $server, string $random)
|
|||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$path = $url->getPath() === '/' ? '' : $url->getPath();
|
$path = $url->getPath() === '/' ? '' : $url->getPath();
|
||||||
$scheme = $url->getScheme();
|
$scheme = $url->getScheme();
|
||||||
$finalFqdn = "$scheme://{$random}.$host$path" ;
|
$finalFqdn = "$scheme://{$random}.$host$path";
|
||||||
return $finalFqdn;
|
return $finalFqdn;
|
||||||
}
|
}
|
||||||
function sslip(Server $server)
|
function sslip(Server $server)
|
||||||
@@ -425,6 +437,16 @@ function getServiceTemplates()
|
|||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$services = File::get(base_path('templates/service-templates.json'));
|
$services = File::get(base_path('templates/service-templates.json'));
|
||||||
$services = collect(json_decode($services))->sortKeys();
|
$services = collect(json_decode($services))->sortKeys();
|
||||||
|
$deprecated = File::get(base_path('templates/deprecated.json'));
|
||||||
|
$deprecated = collect(json_decode($deprecated))->sortKeys();
|
||||||
|
$services = $services->merge($deprecated);
|
||||||
|
$version = config('version');
|
||||||
|
$services = $services->map(function ($service) use ($version) {
|
||||||
|
if (version_compare($version, data_get($service, 'minVersion', '0.0.0'), '<')) {
|
||||||
|
$service->disabled = true;
|
||||||
|
}
|
||||||
|
return $service;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
$services = Http::get(config('constants.services.official'));
|
$services = Http::get(config('constants.services.official'));
|
||||||
if ($services->failed()) {
|
if ($services->failed()) {
|
||||||
|
|||||||
@@ -110,7 +110,10 @@ function getStripeCustomerPortalSession(Team $team)
|
|||||||
{
|
{
|
||||||
Stripe::setApiKey(config('subscription.stripe_api_key'));
|
Stripe::setApiKey(config('subscription.stripe_api_key'));
|
||||||
$return_url = route('team.index');
|
$return_url = route('team.index');
|
||||||
$stripe_customer_id = $team->subscription->stripe_customer_id;
|
$stripe_customer_id = data_get($team,'subscription.stripe_customer_id');
|
||||||
|
if (!$stripe_customer_id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
$session = \Stripe\BillingPortal\Session::create([
|
$session = \Stripe\BillingPortal\Session::create([
|
||||||
'customer' => $stripe_customer_id,
|
'customer' => $stripe_customer_id,
|
||||||
'return_url' => $return_url,
|
'return_url' => $return_url,
|
||||||
@@ -122,14 +125,14 @@ function allowedPathsForUnsubscribedAccounts()
|
|||||||
return [
|
return [
|
||||||
'subscription',
|
'subscription',
|
||||||
'login',
|
'login',
|
||||||
'register',
|
'logout',
|
||||||
'waitlist',
|
'waitlist',
|
||||||
'force-password-reset',
|
'force-password-reset',
|
||||||
'logout',
|
|
||||||
'livewire/message/force-password-reset',
|
'livewire/message/force-password-reset',
|
||||||
'livewire/message/check-license',
|
'livewire/message/check-license',
|
||||||
'livewire/message/switch-team',
|
'livewire/message/switch-team',
|
||||||
'livewire/message/subscription.pricing-plans'
|
'livewire/message/subscription.pricing-plans',
|
||||||
|
'livewire/message/help'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
function allowedPathsForBoardingAccounts()
|
function allowedPathsForBoardingAccounts()
|
||||||
@@ -141,3 +144,11 @@ function allowedPathsForBoardingAccounts()
|
|||||||
'livewire/message/activity-monitor'
|
'livewire/message/activity-monitor'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
function allowedPathsForInvalidAccounts() {
|
||||||
|
return [
|
||||||
|
'logout',
|
||||||
|
'verify',
|
||||||
|
'livewire/message/verify-email',
|
||||||
|
'livewire/message/help'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|||||||
391
composer.lock
generated
391
composer.lock
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user