mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-29 20:59:24 +00:00
Compare commits
66 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 |
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ You can ask for guidance anytime on our
|
|||||||
## 2) Set your environment variables
|
## 2) Set your environment variables
|
||||||
|
|
||||||
- Copy [.env.development.example](./.env.development.example) to .env.
|
- Copy [.env.development.example](./.env.development.example) to .env.
|
||||||
- If necessary, set `USERID` & `GROUPID` accordingly (read in .env file).
|
|
||||||
|
|
||||||
## 3) Start & setup Coolify
|
## 3) Start & setup Coolify
|
||||||
|
|
||||||
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
- 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.
|
- 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
|
## 4) Start development
|
||||||
|
|||||||
@@ -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('{
|
||||||
|
|||||||
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -164,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',
|
||||||
]);
|
]);
|
||||||
@@ -220,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()
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -119,6 +128,12 @@ class General extends Component
|
|||||||
{
|
{
|
||||||
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();
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -72,10 +72,14 @@ class PublicGitRepository extends Component
|
|||||||
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;
|
||||||
@@ -144,7 +148,7 @@ class PublicGitRepository extends Component
|
|||||||
|
|
||||||
if ($this->git_source === 'other') {
|
if ($this->git_source === 'other') {
|
||||||
$application_init = [
|
$application_init = [
|
||||||
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
'name' => generate_random_name(),
|
||||||
'git_repository' => $this->git_repository,
|
'git_repository' => $this->git_repository,
|
||||||
'git_branch' => $this->git_branch,
|
'git_branch' => $this->git_branch,
|
||||||
'build_pack' => 'nixpacks',
|
'build_pack' => 'nixpacks',
|
||||||
@@ -178,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 {
|
||||||
|
|||||||
@@ -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', [
|
||||||
|
|||||||
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
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,6 +45,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private string $commit;
|
private string $commit;
|
||||||
private bool $force_rebuild;
|
private bool $force_rebuild;
|
||||||
|
|
||||||
|
private ?string $dockerImage = null;
|
||||||
|
private ?string $dockerImageTag = null;
|
||||||
|
|
||||||
private GithubApp|GitlabApp|string $source = 'other';
|
private GithubApp|GitlabApp|string $source = 'other';
|
||||||
private StandaloneDocker|SwarmDocker $destination;
|
private StandaloneDocker|SwarmDocker $destination;
|
||||||
private Server $server;
|
private Server $server;
|
||||||
@@ -54,6 +57,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private string|null $currently_running_container_name = null;
|
private string|null $currently_running_container_name = null;
|
||||||
private string $basedir;
|
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_image_name;
|
private string $build_image_name;
|
||||||
private string $production_image_name;
|
private string $production_image_name;
|
||||||
@@ -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;
|
||||||
@@ -135,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();
|
||||||
@@ -173,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);
|
||||||
@@ -245,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(
|
||||||
@@ -254,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);
|
||||||
@@ -339,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.'"
|
||||||
],
|
],
|
||||||
@@ -364,6 +416,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]);
|
]);
|
||||||
$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();
|
||||||
@@ -400,7 +453,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
@@ -452,7 +512,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
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"),
|
||||||
@@ -560,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,
|
||||||
@@ -604,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]);
|
||||||
@@ -667,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';
|
||||||
}
|
}
|
||||||
@@ -760,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],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -74,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([
|
||||||
@@ -90,10 +90,17 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->upload_to_s3();
|
$this->upload_to_s3();
|
||||||
}
|
}
|
||||||
$this->save_backup_logs();
|
$this->save_backup_logs();
|
||||||
|
$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,6 +13,19 @@ 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,
|
||||||
@@ -19,11 +33,13 @@ class Application extends BaseModel
|
|||||||
});
|
});
|
||||||
static::deleting(function ($application) {
|
static::deleting(function ($application) {
|
||||||
// Stop Container
|
// Stop Container
|
||||||
instant_remote_process(
|
if ($application->destination->server->isFunctional()) {
|
||||||
["docker rm -f {$application->uuid}"],
|
instant_remote_process(
|
||||||
$application->destination->server,
|
["docker rm -f {$application->uuid}"],
|
||||||
false
|
$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) {
|
||||||
@@ -85,7 +101,21 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
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(
|
||||||
@@ -243,13 +273,14 @@ 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
|
public function isHealthcheckDisabled(): bool
|
||||||
{
|
{
|
||||||
if (data_get($this, 'dockerfile') || data_get($this, 'build_pack') === 'dockerfile' || data_get($this, 'health_check_enabled') === false) {
|
if (data_get($this, 'health_check_enabled') === false) {
|
||||||
ray('dockerfile');
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Builder;
|
|||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
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
|
||||||
{
|
{
|
||||||
@@ -15,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,
|
||||||
@@ -199,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" => [
|
||||||
|
|||||||
@@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -147,7 +147,7 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
|
|||||||
}
|
}
|
||||||
return $labels;
|
return $labels;
|
||||||
}
|
}
|
||||||
function fqdnLabelsForTraefik(Collection $domains, bool $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');
|
||||||
@@ -158,7 +158,9 @@ function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled)
|
|||||||
$path = $url->getPath();
|
$path = $url->getPath();
|
||||||
$schema = $url->getScheme();
|
$schema = $url->getScheme();
|
||||||
$port = $url->getPort();
|
$port = $url->getPort();
|
||||||
|
if (is_null($port) && !is_null($onlyPort)) {
|
||||||
|
$port = $onlyPort;
|
||||||
|
}
|
||||||
$http_label = "{$uuid}-http";
|
$http_label = "{$uuid}-http";
|
||||||
$https_label = "{$uuid}-https";
|
$https_label = "{$uuid}-https";
|
||||||
|
|
||||||
@@ -203,9 +205,12 @@ function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled)
|
|||||||
|
|
||||||
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, $pull_request_id);
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
$appId = $application->id;
|
$appId = $application->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, $application->settings->is_force_https_enabled));
|
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $application->settings->is_force_https_enabled,$onlyPort));
|
||||||
}
|
}
|
||||||
return $labels->all();
|
return $labels->all();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -310,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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|||||||
136
composer.lock
generated
136
composer.lock
generated
@@ -62,16 +62,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "aws/aws-sdk-php",
|
"name": "aws/aws-sdk-php",
|
||||||
"version": "3.283.0",
|
"version": "3.283.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||||
"reference": "5084c03431ecda0003e35d7fc7a12eeca4242685"
|
"reference": "6616677d76e39af28138512740199d38a461859f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5084c03431ecda0003e35d7fc7a12eeca4242685",
|
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6616677d76e39af28138512740199d38a461859f",
|
||||||
"reference": "5084c03431ecda0003e35d7fc7a12eeca4242685",
|
"reference": "6616677d76e39af28138512740199d38a461859f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -151,9 +151,9 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
||||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.283.0"
|
"source": "https://github.com/aws/aws-sdk-php/tree/3.283.2"
|
||||||
},
|
},
|
||||||
"time": "2023-10-04T18:08:32+00:00"
|
"time": "2023-10-06T18:09:54+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
@@ -1083,16 +1083,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "egulias/email-validator",
|
"name": "egulias/email-validator",
|
||||||
"version": "4.0.1",
|
"version": "4.0.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/egulias/EmailValidator.git",
|
"url": "https://github.com/egulias/EmailValidator.git",
|
||||||
"reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff"
|
"reference": "ebaaf5be6c0286928352e054f2d5125608e5405e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff",
|
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e",
|
||||||
"reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff",
|
"reference": "ebaaf5be6c0286928352e054f2d5125608e5405e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1101,8 +1101,8 @@
|
|||||||
"symfony/polyfill-intl-idn": "^1.26"
|
"symfony/polyfill-intl-idn": "^1.26"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9.5.27",
|
"phpunit/phpunit": "^10.2",
|
||||||
"vimeo/psalm": "^4.30"
|
"vimeo/psalm": "^5.12"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
|
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
|
||||||
@@ -1138,7 +1138,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/egulias/EmailValidator/issues",
|
"issues": "https://github.com/egulias/EmailValidator/issues",
|
||||||
"source": "https://github.com/egulias/EmailValidator/tree/4.0.1"
|
"source": "https://github.com/egulias/EmailValidator/tree/4.0.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1146,7 +1146,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-01-14T14:17:03+00:00"
|
"time": "2023-10-06T06:47:41+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "fruitcake/php-cors",
|
"name": "fruitcake/php-cors",
|
||||||
@@ -2724,16 +2724,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/flysystem",
|
"name": "league/flysystem",
|
||||||
"version": "3.16.0",
|
"version": "3.17.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/thephpleague/flysystem.git",
|
"url": "https://github.com/thephpleague/flysystem.git",
|
||||||
"reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729"
|
"reference": "bd4c9b26849d82364119c68429541f1631fba94b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fdf372ca6b63c6e281b1c01a624349ccb757729",
|
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/bd4c9b26849d82364119c68429541f1631fba94b",
|
||||||
"reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729",
|
"reference": "bd4c9b26849d82364119c68429541f1631fba94b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -2751,8 +2751,8 @@
|
|||||||
"symfony/http-client": "<5.2"
|
"symfony/http-client": "<5.2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"async-aws/s3": "^1.5",
|
"async-aws/s3": "^1.5 || ^2.0",
|
||||||
"async-aws/simple-s3": "^1.1",
|
"async-aws/simple-s3": "^1.1 || ^2.0",
|
||||||
"aws/aws-sdk-php": "^3.220.0",
|
"aws/aws-sdk-php": "^3.220.0",
|
||||||
"composer/semver": "^3.0",
|
"composer/semver": "^3.0",
|
||||||
"ext-fileinfo": "*",
|
"ext-fileinfo": "*",
|
||||||
@@ -2798,7 +2798,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/thephpleague/flysystem/issues",
|
"issues": "https://github.com/thephpleague/flysystem/issues",
|
||||||
"source": "https://github.com/thephpleague/flysystem/tree/3.16.0"
|
"source": "https://github.com/thephpleague/flysystem/tree/3.17.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2810,7 +2810,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-09-07T19:22:17+00:00"
|
"time": "2023-10-05T20:15:05+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/flysystem-aws-s3-v3",
|
"name": "league/flysystem-aws-s3-v3",
|
||||||
@@ -3557,16 +3557,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nette/schema",
|
"name": "nette/schema",
|
||||||
"version": "v1.2.4",
|
"version": "v1.2.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nette/schema.git",
|
"url": "https://github.com/nette/schema.git",
|
||||||
"reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab"
|
"reference": "0462f0166e823aad657c9224d0f849ecac1ba10a"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nette/schema/zipball/c9ff517a53903b3d4e29ec547fb20feecb05b8ab",
|
"url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a",
|
||||||
"reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab",
|
"reference": "0462f0166e823aad657c9224d0f849ecac1ba10a",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -3613,9 +3613,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nette/schema/issues",
|
"issues": "https://github.com/nette/schema/issues",
|
||||||
"source": "https://github.com/nette/schema/tree/v1.2.4"
|
"source": "https://github.com/nette/schema/tree/v1.2.5"
|
||||||
},
|
},
|
||||||
"time": "2023-08-05T18:56:25+00:00"
|
"time": "2023-10-05T20:37:59+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nette/utils",
|
"name": "nette/utils",
|
||||||
@@ -6757,16 +6757,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "stripe/stripe-php",
|
"name": "stripe/stripe-php",
|
||||||
"version": "v12.5.0",
|
"version": "v12.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/stripe/stripe-php.git",
|
"url": "https://github.com/stripe/stripe-php.git",
|
||||||
"reference": "a4249b4a90437844f6c35e8701f8c68acd206f56"
|
"reference": "e0c15e4cbf252e708b937482bb1a50fa7e01bc9d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/a4249b4a90437844f6c35e8701f8c68acd206f56",
|
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/e0c15e4cbf252e708b937482bb1a50fa7e01bc9d",
|
||||||
"reference": "a4249b4a90437844f6c35e8701f8c68acd206f56",
|
"reference": "e0c15e4cbf252e708b937482bb1a50fa7e01bc9d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -6811,9 +6811,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/stripe/stripe-php/issues",
|
"issues": "https://github.com/stripe/stripe-php/issues",
|
||||||
"source": "https://github.com/stripe/stripe-php/tree/v12.5.0"
|
"source": "https://github.com/stripe/stripe-php/tree/v12.6.0"
|
||||||
},
|
},
|
||||||
"time": "2023-09-28T23:06:27+00:00"
|
"time": "2023-10-05T18:01:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
@@ -10258,16 +10258,16 @@
|
|||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
"name": "brianium/paratest",
|
"name": "brianium/paratest",
|
||||||
"version": "v7.2.8",
|
"version": "v7.2.9",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/paratestphp/paratest.git",
|
"url": "https://github.com/paratestphp/paratest.git",
|
||||||
"reference": "882b02d197328138686bb06ce7d8cbb98fc0a16c"
|
"reference": "1f9e41c0779be4540654d92a9314016713f5e62c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/882b02d197328138686bb06ce7d8cbb98fc0a16c",
|
"url": "https://api.github.com/repos/paratestphp/paratest/zipball/1f9e41c0779be4540654d92a9314016713f5e62c",
|
||||||
"reference": "882b02d197328138686bb06ce7d8cbb98fc0a16c",
|
"reference": "1f9e41c0779be4540654d92a9314016713f5e62c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10275,13 +10275,13 @@
|
|||||||
"ext-pcre": "*",
|
"ext-pcre": "*",
|
||||||
"ext-reflection": "*",
|
"ext-reflection": "*",
|
||||||
"ext-simplexml": "*",
|
"ext-simplexml": "*",
|
||||||
"fidry/cpu-core-counter": "^0.4.1 || ^0.5.1",
|
"fidry/cpu-core-counter": "^0.5.1",
|
||||||
"jean85/pretty-package-versions": "^2.0.5",
|
"jean85/pretty-package-versions": "^2.0.5",
|
||||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
|
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
|
||||||
"phpunit/php-code-coverage": "^10.1.3",
|
"phpunit/php-code-coverage": "^10.1.7",
|
||||||
"phpunit/php-file-iterator": "^4.0.2",
|
"phpunit/php-file-iterator": "^4.1.0",
|
||||||
"phpunit/php-timer": "^6.0",
|
"phpunit/php-timer": "^6.0",
|
||||||
"phpunit/phpunit": "^10.3.2",
|
"phpunit/phpunit": "^10.4.0",
|
||||||
"sebastian/environment": "^6.0.1",
|
"sebastian/environment": "^6.0.1",
|
||||||
"symfony/console": "^6.3.4",
|
"symfony/console": "^6.3.4",
|
||||||
"symfony/process": "^6.3.4"
|
"symfony/process": "^6.3.4"
|
||||||
@@ -10290,8 +10290,8 @@
|
|||||||
"doctrine/coding-standard": "^12.0.0",
|
"doctrine/coding-standard": "^12.0.0",
|
||||||
"ext-pcov": "*",
|
"ext-pcov": "*",
|
||||||
"ext-posix": "*",
|
"ext-posix": "*",
|
||||||
"infection/infection": "^0.27.0",
|
"infection/infection": "^0.27.3",
|
||||||
"phpstan/phpstan": "^1.10.32",
|
"phpstan/phpstan": "^1.10.37",
|
||||||
"phpstan/phpstan-deprecation-rules": "^1.1.4",
|
"phpstan/phpstan-deprecation-rules": "^1.1.4",
|
||||||
"phpstan/phpstan-phpunit": "^1.3.14",
|
"phpstan/phpstan-phpunit": "^1.3.14",
|
||||||
"phpstan/phpstan-strict-rules": "^1.5.1",
|
"phpstan/phpstan-strict-rules": "^1.5.1",
|
||||||
@@ -10337,7 +10337,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/paratestphp/paratest/issues",
|
"issues": "https://github.com/paratestphp/paratest/issues",
|
||||||
"source": "https://github.com/paratestphp/paratest/tree/v7.2.8"
|
"source": "https://github.com/paratestphp/paratest/tree/v7.2.9"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -10349,7 +10349,7 @@
|
|||||||
"type": "paypal"
|
"type": "paypal"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-10-04T13:38:04+00:00"
|
"time": "2023-10-06T07:53:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "fakerphp/faker",
|
"name": "fakerphp/faker",
|
||||||
@@ -10983,35 +10983,35 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pestphp/pest",
|
"name": "pestphp/pest",
|
||||||
"version": "v2.20.0",
|
"version": "v2.21.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pestphp/pest.git",
|
"url": "https://github.com/pestphp/pest.git",
|
||||||
"reference": "a8b785f69e44ae3f902cbf08fe6b79359ba46945"
|
"reference": "2ffafd445d42c8b7b7e1874bde1c29945767a49d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pestphp/pest/zipball/a8b785f69e44ae3f902cbf08fe6b79359ba46945",
|
"url": "https://api.github.com/repos/pestphp/pest/zipball/2ffafd445d42c8b7b7e1874bde1c29945767a49d",
|
||||||
"reference": "a8b785f69e44ae3f902cbf08fe6b79359ba46945",
|
"reference": "2ffafd445d42c8b7b7e1874bde1c29945767a49d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"brianium/paratest": "^7.2.7",
|
"brianium/paratest": "^7.2.9",
|
||||||
"nunomaduro/collision": "^7.9.0",
|
"nunomaduro/collision": "^7.9.0",
|
||||||
"nunomaduro/termwind": "^1.15.1",
|
"nunomaduro/termwind": "^1.15.1",
|
||||||
"pestphp/pest-plugin": "^2.1.1",
|
"pestphp/pest-plugin": "^2.1.1",
|
||||||
"pestphp/pest-plugin-arch": "^2.3.3",
|
"pestphp/pest-plugin-arch": "^2.3.3",
|
||||||
"php": "^8.1.0",
|
"php": "^8.1.0",
|
||||||
"phpunit/phpunit": "^10.3.5"
|
"phpunit/phpunit": "^10.4.0"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"phpunit/phpunit": ">10.3.5",
|
"phpunit/phpunit": ">10.4.0",
|
||||||
"sebastian/exporter": "<5.1.0",
|
"sebastian/exporter": "<5.1.0",
|
||||||
"webmozart/assert": "<1.11.0"
|
"webmozart/assert": "<1.11.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"pestphp/pest-dev-tools": "^2.16.0",
|
"pestphp/pest-dev-tools": "^2.16.0",
|
||||||
"pestphp/pest-plugin-type-coverage": "^2.2.0",
|
"pestphp/pest-plugin-type-coverage": "^2.4.0",
|
||||||
"symfony/process": "^6.3.4"
|
"symfony/process": "^6.3.4"
|
||||||
},
|
},
|
||||||
"bin": [
|
"bin": [
|
||||||
@@ -11070,7 +11070,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pestphp/pest/issues",
|
"issues": "https://github.com/pestphp/pest/issues",
|
||||||
"source": "https://github.com/pestphp/pest/tree/v2.20.0"
|
"source": "https://github.com/pestphp/pest/tree/v2.21.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -11082,7 +11082,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-09-29T18:05:52+00:00"
|
"time": "2023-10-06T12:33:39+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pestphp/pest-plugin",
|
"name": "pestphp/pest-plugin",
|
||||||
@@ -11454,16 +11454,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan",
|
"name": "phpstan/phpstan",
|
||||||
"version": "1.10.37",
|
"version": "1.10.38",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan.git",
|
"url": "https://github.com/phpstan/phpstan.git",
|
||||||
"reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e"
|
"reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/058ba07e92f744d4dcf6061ae75283d0c6456f2e",
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/5302bb402c57f00fb3c2c015bac86e0827e4b691",
|
||||||
"reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e",
|
"reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -11512,7 +11512,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-10-02T16:18:37+00:00"
|
"time": "2023-10-06T14:19:14+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
@@ -11837,16 +11837,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "10.3.5",
|
"version": "10.4.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503"
|
"reference": "9784e877e3700de37475545bdbdce8383ff53d25"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9784e877e3700de37475545bdbdce8383ff53d25",
|
||||||
"reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503",
|
"reference": "9784e877e3700de37475545bdbdce8383ff53d25",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -11886,7 +11886,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "10.3-dev"
|
"dev-main": "10.4-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -11918,7 +11918,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5"
|
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -11934,7 +11934,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-09-19T05:42:37+00:00"
|
"time": "2023-10-06T03:41:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/cli-parser",
|
"name": "sebastian/cli-parser",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
'docs' => 'https://coolify.io/docs/contact',
|
||||||
'self_hosted' => env('SELF_HOSTED', true),
|
'self_hosted' => env('SELF_HOSTED', true),
|
||||||
'waitlist' => env('WAITLIST', false),
|
'waitlist' => env('WAITLIST', false),
|
||||||
'license_url' => 'https://licenses.coollabs.io',
|
'license_url' => 'https://licenses.coollabs.io',
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ return [
|
|||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.69',
|
'release' => '4.0.0-beta.76',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.69';
|
return '4.0.0-beta.76';
|
||||||
|
|||||||
31
database/migrations/2023_09_23_111819_add_server_emails.php
Normal file
31
database/migrations/2023_09_23_111819_add_server_emails.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->boolean('unreachable_email_sent')->default(false);
|
||||||
|
$table->dropColumn('unreachable_count');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('unreachable_email_sent');
|
||||||
|
$table->integer('unreachable_count')->default(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->integer('unreachable_count')->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('unreachable_count');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('s3_storages', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_usable')->default(false);
|
||||||
|
$table->boolean('unusable_email_sent')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('s3_storages', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_usable');
|
||||||
|
$table->dropColumn('unusable_email_sent');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->string('dockerfile_location')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('dockerfile_location');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -34,14 +34,16 @@ services:
|
|||||||
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
||||||
POSTGRES_HOST_AUTH_METHOD: "trust"
|
POSTGRES_HOST_AUTH_METHOD: "trust"
|
||||||
volumes:
|
volumes:
|
||||||
- coolify-pg-data-dev:/var/lib/postgresql/data
|
- ./_data/coolify/_volumes/database/:/var/lib/postgresql/data
|
||||||
|
# - coolify-pg-data-dev:/var/lib/postgresql/data
|
||||||
redis:
|
redis:
|
||||||
ports:
|
ports:
|
||||||
- "${FORWARD_REDIS_PORT:-6379}:6379"
|
- "${FORWARD_REDIS_PORT:-6379}:6379"
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
volumes:
|
volumes:
|
||||||
- coolify-redis-data-dev:/data
|
- ./_data/coolify/_volumes/redis/:/data
|
||||||
|
# - coolify-redis-data-dev:/data
|
||||||
vite:
|
vite:
|
||||||
image: node:19
|
image: node:19
|
||||||
working_dir: /var/www/html
|
working_dir: /var/www/html
|
||||||
@@ -56,7 +58,8 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /:/host
|
- /:/host
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- coolify-data-dev:/data/coolify
|
- ./_data/coolify/:/data/coolify
|
||||||
|
# - coolify-data-dev:/data/coolify
|
||||||
mailpit:
|
mailpit:
|
||||||
image: "axllent/mailpit:latest"
|
image: "axllent/mailpit:latest"
|
||||||
container_name: coolify-mail
|
container_name: coolify-mail
|
||||||
@@ -76,7 +79,8 @@ services:
|
|||||||
MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}"
|
MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY:-minioadmin}"
|
||||||
MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}"
|
MINIO_SECRET_KEY: "${MINIO_SECRET_KEY:-minioadmin}"
|
||||||
volumes:
|
volumes:
|
||||||
- coolify-minio-data-dev:/data
|
- ./_data/coolify/_volumes/minio/:/data
|
||||||
|
# - coolify-minio-data-dev:/data
|
||||||
networks:
|
networks:
|
||||||
- coolify
|
- coolify
|
||||||
|
|
||||||
|
|||||||
58
package-lock.json
generated
58
package-lock.json
generated
@@ -6,18 +6,18 @@
|
|||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/typography": "0.5.10",
|
"@tailwindcss/typography": "0.5.10",
|
||||||
"alpinejs": "3.13.0",
|
"alpinejs": "3.13.1",
|
||||||
"daisyui": "3.7.7",
|
"daisyui": "3.9.2",
|
||||||
"tailwindcss-scrollbar": "0.1.0"
|
"tailwindcss-scrollbar": "0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.3.4",
|
"@vitejs/plugin-vue": "4.4.0",
|
||||||
"autoprefixer": "10.4.16",
|
"autoprefixer": "10.4.16",
|
||||||
"axios": "1.5.0",
|
"axios": "1.5.1",
|
||||||
"laravel-vite-plugin": "0.8.0",
|
"laravel-vite-plugin": "0.8.1",
|
||||||
"postcss": "8.4.30",
|
"postcss": "8.4.31",
|
||||||
"tailwindcss": "3.3.3",
|
"tailwindcss": "3.3.3",
|
||||||
"vite": "4.4.9",
|
"vite": "4.4.11",
|
||||||
"vue": "3.3.4"
|
"vue": "3.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -503,9 +503,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "4.3.4",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz",
|
||||||
"integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==",
|
"integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.0.0"
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
@@ -683,9 +683,9 @@
|
|||||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
|
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
|
||||||
},
|
},
|
||||||
"node_modules/alpinejs": {
|
"node_modules/alpinejs": {
|
||||||
"version": "3.13.0",
|
"version": "3.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.1.tgz",
|
||||||
"integrity": "sha512-7FYR1Yz3evIjlJD1mZ3SYWSw+jlOmQGeQ1QiSufSQ6J84XMQFkzxm6OobiZ928SfqhGdoIp2SsABNsS4rXMMJw==",
|
"integrity": "sha512-/LZ7mumW02V7AV5xTTftJFHS0I3KOXLl7tHm4xpxXAV+HJ/zjTT0n8MU7RZ6UoGPhmO/i+KEhQojaH/0RsH5tg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "~3.1.1"
|
"@vue/reactivity": "~3.1.1"
|
||||||
}
|
}
|
||||||
@@ -756,9 +756,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz",
|
||||||
"integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
|
"integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.0",
|
"follow-redirects": "^1.15.0",
|
||||||
@@ -953,15 +953,15 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/daisyui": {
|
"node_modules/daisyui": {
|
||||||
"version": "3.7.7",
|
"version": "3.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.7.7.tgz",
|
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.2.tgz",
|
||||||
"integrity": "sha512-2/nFdW/6R9MMnR8tTm07jPVyPaZwpUSkVsFAADb7Oq8N2Ynbls57laDdNqxTCUmn0QvcZi01TKl8zQbAwRfw1w==",
|
"integrity": "sha512-yJZ1QjHUaL+r9BkquTdzNHb7KIgAJVFh0zbOXql2Wu0r7zx5qZNLxclhjN0WLoIpY+o2h/8lqXg7ijj8oTigOw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colord": "^2.9",
|
"colord": "^2.9",
|
||||||
"css-selector-tokenizer": "^0.8",
|
"css-selector-tokenizer": "^0.8",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"postcss-js": "^4",
|
"postcss-js": "^4",
|
||||||
"tailwindcss": "^3"
|
"tailwindcss": "^3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.9.0"
|
"node": ">=16.9.0"
|
||||||
@@ -1289,9 +1289,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/laravel-vite-plugin": {
|
"node_modules/laravel-vite-plugin": {
|
||||||
"version": "0.8.0",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.1.tgz",
|
||||||
"integrity": "sha512-6VjLI+azBpeK6rWBiKcb/En5GnTdYpL0U4zS8gXYvb2/VSq4mlau5H3NWpSktUDBMM1b97LLgICx5zevi8IY0w==",
|
"integrity": "sha512-fxzUDjOA37kOsYq8dP+3oPIlw8/kJVXwu0hOXLun82R1LpV02shGeWGYKx2lbpKffL5I0sfPPjfqbYxuqBluAA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picocolors": "^1.0.0",
|
"picocolors": "^1.0.0",
|
||||||
@@ -1516,9 +1516,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.30",
|
"version": "8.4.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
|
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -1920,9 +1920,9 @@
|
|||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "4.4.9",
|
"version": "4.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz",
|
||||||
"integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==",
|
"integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.18.10",
|
"esbuild": "^0.18.10",
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -6,19 +6,19 @@
|
|||||||
"build": "vite build"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.3.4",
|
"@vitejs/plugin-vue": "4.4.0",
|
||||||
"autoprefixer": "10.4.16",
|
"autoprefixer": "10.4.16",
|
||||||
"axios": "1.5.0",
|
"axios": "1.5.1",
|
||||||
"laravel-vite-plugin": "0.8.0",
|
"laravel-vite-plugin": "0.8.1",
|
||||||
"postcss": "8.4.30",
|
"postcss": "8.4.31",
|
||||||
"tailwindcss": "3.3.3",
|
"tailwindcss": "3.3.3",
|
||||||
"vite": "4.4.9",
|
"vite": "4.4.11",
|
||||||
"vue": "3.3.4"
|
"vue": "3.3.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/typography": "0.5.10",
|
"@tailwindcss/typography": "0.5.10",
|
||||||
"alpinejs": "3.13.0",
|
"alpinejs": "3.13.1",
|
||||||
"daisyui": "3.7.7",
|
"daisyui": "3.9.2",
|
||||||
"tailwindcss-scrollbar": "0.1.0"
|
"tailwindcss-scrollbar": "0.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ a {
|
|||||||
@apply flex items-center p-2 transition-colors cursor-pointer min-h-16 bg-coolgray-200 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem];
|
@apply flex items-center p-2 transition-colors cursor-pointer min-h-16 bg-coolgray-200 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem];
|
||||||
}
|
}
|
||||||
.box-without-bg {
|
.box-without-bg {
|
||||||
@apply flex items-center p-2 transition-colors min-h-16 hover:text-white hover:no-underline min-w-[24rem];
|
@apply flex items-center p-2 transition-colors min-h-16 hover:text-white hover:no-underline min-w-[24rem];
|
||||||
}
|
}
|
||||||
|
|
||||||
.lds-heart {
|
.lds-heart {
|
||||||
|
|||||||
@@ -9,12 +9,12 @@
|
|||||||
@if ($is_registration_enabled)
|
@if ($is_registration_enabled)
|
||||||
@if (config('coolify.waitlist'))
|
@if (config('coolify.waitlist'))
|
||||||
<a href="/waitlist"
|
<a href="/waitlist"
|
||||||
class="text-xs normal-case hover:no-underline btn btn-sm bg-coollabs-gradient">
|
class="text-xs text-center text-white normal-case bg-transparent border-none rounded no-animation hover:no-underline btn btn-sm bg-coollabs-gradient">
|
||||||
Join the waitlist
|
Join the waitlist
|
||||||
</a>
|
</a>
|
||||||
@else
|
@else
|
||||||
<a href="/register"
|
<a href="/register"
|
||||||
class="text-xs normal-case hover:no-underline btn btn-sm bg-coollabs-gradient">
|
class="text-xs text-center text-white normal-case bg-transparent border-none rounded no-animation hover:no-underline btn btn-sm bg-coollabs-gradient">
|
||||||
{{ __('auth.register_now') }}
|
{{ __('auth.register_now') }}
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
12
resources/views/auth/verify-email.blade.php
Normal file
12
resources/views/auth/verify-email.blade.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<x-layout-subscription>
|
||||||
|
<div class="min-h-screen hero">
|
||||||
|
<div class="min-w-fit">
|
||||||
|
<h1> Verification Email Sent </h1>
|
||||||
|
<div class="flex justify-center gap-2 text-center">
|
||||||
|
<br>To activate your account, please open the email and follow the
|
||||||
|
instructions.
|
||||||
|
</div>
|
||||||
|
<livewire:verify-email />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-layout-subscription>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<x-applications.advanced :application="$application" />
|
<x-applications.advanced :application="$application" />
|
||||||
|
|
||||||
@if ($application->status !== 'exited')
|
@if ($application->status !== 'exited')
|
||||||
<button wire:click='deploy' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
<button title="With rolling update if possible" wire:click='deploy' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24" stroke-width="2"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24" stroke-width="2"
|
||||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
</path>
|
</path>
|
||||||
<path d="M7.05 11.038v-3.988"></path>
|
<path d="M7.05 11.038v-3.988"></path>
|
||||||
</svg>
|
</svg>
|
||||||
Restart
|
Redeploy
|
||||||
</button>
|
</button>
|
||||||
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
<button wire:click='stop' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24" stroke-width="2"
|
||||||
|
|||||||
@@ -256,7 +256,7 @@
|
|||||||
your self-hosted instance?
|
your self-hosted instance?
|
||||||
<x-forms.button>
|
<x-forms.button>
|
||||||
<a class="font-bold text-white hover:no-underline"
|
<a class="font-bold text-white hover:no-underline"
|
||||||
href="https://docs.coollabs.io/contact">Contact Us</a>
|
href="{{ config('coolify.docs') }}">Contact Us</a>
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,25 +8,25 @@
|
|||||||
<nav class="navbar-main">
|
<nav class="navbar-main">
|
||||||
<a class="{{ request()->routeIs('server.show') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.show') ? 'text-white' : '' }}"
|
||||||
href="{{ route('server.show', [
|
href="{{ route('server.show', [
|
||||||
'server_uuid' => Route::current()->parameters()['server_uuid'],
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>General</button>
|
<button>General</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('server.private-key') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.private-key') ? 'text-white' : '' }}"
|
||||||
href="{{ route('server.private-key', [
|
href="{{ route('server.private-key', [
|
||||||
'server_uuid' => Route::current()->parameters()['server_uuid'],
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Private Key</button>
|
<button>Private Key</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('server.proxy') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.proxy') ? 'text-white' : '' }}"
|
||||||
href="{{ route('server.proxy', [
|
href="{{ route('server.proxy', [
|
||||||
'server_uuid' => Route::current()->parameters()['server_uuid'],
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Proxy</button>
|
<button>Proxy</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('server.destinations') ? 'text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.destinations') ? 'text-white' : '' }}"
|
||||||
href="{{ route('server.destinations', [
|
href="{{ route('server.destinations', [
|
||||||
'server_uuid' => Route::current()->parameters()['server_uuid'],
|
'server_uuid' => data_get($parameters, 'server_uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Destinations</button>
|
<button>Destinations</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
20
resources/views/components/server/sidebar.blade.php
Normal file
20
resources/views/components/server/sidebar.blade.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<div>
|
||||||
|
@if ($server->isFunctional())
|
||||||
|
<div class="flex h-full pr-4">
|
||||||
|
<div class="flex flex-col gap-4 min-w-fit">
|
||||||
|
<a class="{{ request()->routeIs('server.proxy') ? 'text-white' : '' }}"
|
||||||
|
href="{{ route('server.proxy', $parameters) }}">
|
||||||
|
<button>Configuration</button>
|
||||||
|
</a>
|
||||||
|
@if (data_get($server, 'proxy.type') !== 'NONE')
|
||||||
|
<a class="{{ request()->routeIs('server.proxy.logs') ? 'text-white' : '' }}"
|
||||||
|
href="{{ route('server.proxy.logs', $parameters) }}">
|
||||||
|
<button>Logs</button>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div>Server is not validated. Validate first.</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
<div class="pb-6">
|
<div class="pb-6">
|
||||||
<h1>Team</h1>
|
<div class="flex items-end gap-2">
|
||||||
|
<h1>Team</h1>
|
||||||
|
<a href="/team/new"><x-forms.button>+ New Team</x-forms.button></a>
|
||||||
|
</div>
|
||||||
<nav class="flex pt-2 pb-10">
|
<nav class="flex pt-2 pb-10">
|
||||||
<ol class="inline-flex items-center">
|
<ol class="inline-flex items-center">
|
||||||
<li>
|
<li>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span>Currently active team: <span
|
<span>Currently active team: <span
|
||||||
class="text-warning">{{ session('currentTeam.name') }}</span></span>
|
class="text-warning">{{ session('currentTeam.name') }}</span></span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ Container ({{ $containerName }}) has been restarted automatically on {{$serverNa
|
|||||||
@if ($containerName === 'coolify-proxy')
|
@if ($containerName === 'coolify-proxy')
|
||||||
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
|
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
|
||||||
|
|
||||||
Note: The proxy should not stop unexpectedly, so please check what is going on your server.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If you don't want to use Coolify Proxy, please remove FQDN from your resources or set Proxy type to Custom(None).
|
If you don't want to use Coolify Proxy, please remove FQDN from your resources or set Proxy type to Custom(None).
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|||||||
3
resources/views/emails/email-verification.blade.php
Normal file
3
resources/views/emails/email-verification.blade.php
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<x-emails.layout>
|
||||||
|
Verify your email [here]({{ $url }}).
|
||||||
|
</x-emails.layout>
|
||||||
6
resources/views/emails/s3-connection-error.blade.php
Normal file
6
resources/views/emails/s3-connection-error.blade.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<x-emails.layout>
|
||||||
|
Connection could not be establised with one of your S3 Storage ({{ $name }}). Please fix it
|
||||||
|
[here]({{ $url }}).
|
||||||
|
|
||||||
|
{{ $reason }}
|
||||||
|
</x-emails.layout>
|
||||||
@@ -4,7 +4,7 @@ Coolify cannot connect to your server ({{$name}}). Please check your server and
|
|||||||
|
|
||||||
All automations & integrations are turned off!
|
All automations & integrations are turned off!
|
||||||
|
|
||||||
IMPORTANT: You have to validate your server again after you fix the issue.
|
IMPORTANT: We automatically try to revive your server. If your server is back online, we will automatically turn on all automations & integrations.
|
||||||
|
|
||||||
If you have any questions, please contact us.
|
If you have any questions, please contact us.
|
||||||
|
|
||||||
|
|||||||
6
resources/views/emails/server-revived.blade.php
Normal file
6
resources/views/emails/server-revived.blade.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<x-emails.layout>
|
||||||
|
|
||||||
|
Your server ({{$name}}) was offline for a while, but it is back online now. All automations & integrations are turned on again.
|
||||||
|
|
||||||
|
</x-emails.layout>
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<a href="/">
|
<a href="/">
|
||||||
<x-forms.button>Go back home</x-forms.button>
|
<x-forms.button>Go back home</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
<a target="_blank" class="text-xs" href="https://docs.coollabs.io/contact.html">Contact
|
<a target="_blank" class="text-xs" href="{{ config('coolify.docs') }}">Contact
|
||||||
support
|
support
|
||||||
<x-external-link />
|
<x-external-link />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<a href="/">
|
<a href="/">
|
||||||
<x-forms.button>Go back home</x-forms.button>
|
<x-forms.button>Go back home</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
<a target="_blank" class="text-xs" href="https://docs.coollabs.io/contact.html">Contact
|
<a target="_blank" class="text-xs" href="{{ config('coolify.docs') }}">Contact
|
||||||
support
|
support
|
||||||
<x-external-link />
|
<x-external-link />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<a href="/">
|
<a href="/">
|
||||||
<x-forms.button>Go back home</x-forms.button>
|
<x-forms.button>Go back home</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
<a target="_blank" class="text-xs" href="https://docs.coollabs.io/contact.html">Contact
|
<a target="_blank" class="text-xs" href="{{ config('coolify.docs') }}">Contact
|
||||||
support
|
support
|
||||||
<x-external-link />
|
<x-external-link />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<a href="/">
|
<a href="/">
|
||||||
<x-forms.button>Go back home</x-forms.button>
|
<x-forms.button>Go back home</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://docs.coollabs.io/contact.html" class="font-semibold text-white ">Contact
|
<a href="{{ config('coolify.docs') }}" class="font-semibold text-white ">Contact
|
||||||
support
|
support
|
||||||
<span aria-hidden="true">→</span></a>
|
<span aria-hidden="true">→</span></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<a href="/">
|
<a href="/">
|
||||||
<x-forms.button>Go back home</x-forms.button>
|
<x-forms.button>Go back home</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://docs.coollabs.io/contact.html" class="font-semibold text-white ">Contact
|
<a href="{{ config('coolify.docs') }}" class="font-semibold text-white ">Contact
|
||||||
support
|
support
|
||||||
<span aria-hidden="true">→</span></a>
|
<span aria-hidden="true">→</span></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<a href="/">
|
<a href="/">
|
||||||
<x-forms.button>Go back home</x-forms.button>
|
<x-forms.button>Go back home</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://docs.coollabs.io/contact.html" class="font-semibold text-white">Contact
|
<a href="{{ config('coolify.docs') }}" class="font-semibold text-white">Contact
|
||||||
support
|
support
|
||||||
<span aria-hidden="true">→</span></a>
|
<span aria-hidden="true">→</span></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
patience.
|
patience.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex items-center justify-center mt-10 gap-x-6">
|
<div class="flex items-center justify-center mt-10 gap-x-6">
|
||||||
<a href="https://docs.coollabs.io/contact.html" class="font-semibold text-white ">Contact
|
<a href="{{ config('coolify.docs') }}" class="font-semibold text-white ">Contact
|
||||||
support
|
support
|
||||||
<span aria-hidden="true">→</span></a>
|
<span aria-hidden="true">→</span></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -200,7 +200,7 @@
|
|||||||
label="Description" id="remoteServerDescription" />
|
label="Description" id="remoteServerDescription" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.input required placeholder="Hostname or IP address" label="Hostname or IP Address"
|
<x-forms.input required placeholder="127.0.0.1" label="IP Address"
|
||||||
id="remoteServerHost" />
|
id="remoteServerHost" />
|
||||||
<x-forms.input required placeholder="Port number of your server. Default is 22."
|
<x-forms.input required placeholder="Port number of your server. Default is 22."
|
||||||
label="Port" id="remoteServerPort" />
|
label="Port" id="remoteServerPort" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<span x-data x-init="$wire.emit('error', '{{ session('error') }}')" />
|
<span x-data x-init="$wire.emit('error', '{{ session('error') }}')" />
|
||||||
@endif
|
@endif
|
||||||
<h1>Dashboard</h1>
|
<h1>Dashboard</h1>
|
||||||
<div class="subtitle">Something <x-highlighted text="(more)" /> useful will be here.</div>
|
<div class="subtitle">Your self-hosted environment</div>
|
||||||
@if (request()->query->get('success'))
|
@if (request()->query->get('success'))
|
||||||
<div class="rounded alert alert-success">
|
<div class="rounded alert alert-success">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
||||||
@@ -14,27 +14,89 @@
|
|||||||
<span>Your subscription has been activated! Welcome onboard!</span>
|
<span>Your subscription has been activated! Welcome onboard!</span>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div class="w-full rounded stats stats-vertical lg:stats-horizontal">
|
|
||||||
<div class="stat">
|
|
||||||
<div class="stat-title">Servers</div>
|
|
||||||
<div class="stat-value">{{ $servers }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat">
|
<h3 class="pb-4">Projects</h3>
|
||||||
<div class="stat-title">Projects</div>
|
|
||||||
<div class="stat-value">{{ $projects }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat">
|
@if ($projects->count() === 1)
|
||||||
<div class="stat-title">Resources</div>
|
<div class="grid grid-cols-1 gap-2">
|
||||||
<div class="stat-value">{{ $resources }}</div>
|
@else
|
||||||
<div class="stat-desc">Applications, databases, etc...</div>
|
<div class="grid grid-cols-3 gap-2">
|
||||||
|
@endif
|
||||||
|
@foreach ($projects as $project)
|
||||||
|
<div class="gap-2 border border-transparent cursor-pointer box group" x-data
|
||||||
|
x-on:click="gotoProject('{{ $project->uuid }}','{{ data_get($project, 'environments.0.name', 'production') }}')">
|
||||||
|
@if (data_get($project, 'environments.0.name'))
|
||||||
|
<a class="flex flex-col flex-1 mx-6 hover:no-underline"
|
||||||
|
href="{{ route('project.resources', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
|
||||||
|
<div class="font-bold text-white">{{ $project->name }}</div>
|
||||||
|
<div class="text-xs group-hover:text-white hover:no-underline">
|
||||||
|
{{ $project->description }}</div>
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<a class="flex flex-col flex-1 mx-6 hover:no-underline"
|
||||||
|
href="{{ route('project.show', ['project_uuid' => data_get($project, 'uuid')]) }}">
|
||||||
|
<div class="font-bold text-white">{{ $project->name }}</div>
|
||||||
|
<div class="text-xs group-hover:text-white hover:no-underline">
|
||||||
|
{{ $project->description }}</div>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
<a class="mx-4 rounded group-hover:text-white hover:no-underline "
|
||||||
|
href="{{ route('project.resources.new', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
|
||||||
|
<span class="font-bold hover:text-warning">+ New Resource</span>
|
||||||
|
</a>
|
||||||
|
<a class="mx-4 rounded group-hover:text-white"
|
||||||
|
href="{{ route('project.edit', ['project_uuid' => data_get($project, 'uuid')]) }}">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning" viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path
|
||||||
|
d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" />
|
||||||
|
<path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat">
|
@endforeach
|
||||||
<div class="stat-title">S3 Storages</div>
|
</div>
|
||||||
<div class="stat-value">{{ $s3s }}</div>
|
<h3 class="py-4">Servers</h3>
|
||||||
</div>
|
@if ($servers->count() === 1)
|
||||||
</div>
|
<div class="grid grid-cols-1 gap-2">
|
||||||
|
@else
|
||||||
{{-- <x-forms.button wire:click='getIptables'>Get IPTABLES</x-forms.button> --}}
|
<div class="grid grid-cols-3 gap-2">
|
||||||
|
@endif
|
||||||
|
@foreach ($servers as $server)
|
||||||
|
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}" @class([
|
||||||
|
'gap-2 border cursor-pointer box group',
|
||||||
|
'border-transparent' => $server->settings->is_reachable,
|
||||||
|
'border-red-500' => !$server->settings->is_reachable,
|
||||||
|
])>
|
||||||
|
<div class="flex flex-col mx-6">
|
||||||
|
<div class="font-bold text-white">
|
||||||
|
{{ $server->name }}
|
||||||
|
</div>
|
||||||
|
<div class="text-xs group-hover:text-white">
|
||||||
|
{{ $server->description }}</div>
|
||||||
|
<div class="flex gap-1 text-xs text-error">
|
||||||
|
@if (!$server->settings->is_reachable)
|
||||||
|
<span>Not reachable</span>
|
||||||
|
@endif
|
||||||
|
@if (!$server->settings->is_reachable && !$server->settings->is_usable)
|
||||||
|
&
|
||||||
|
@endif
|
||||||
|
@if (!$server->settings->is_usable)
|
||||||
|
<span>Not usable by Coolify</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
</a>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function gotoProject(uuid, environment = 'production') {
|
||||||
|
window.location.href = '/project/' + uuid + '/' + environment;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{-- <x-forms.button wire:click='getIptables'>Get IPTABLES</x-forms.button> --}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,9 +21,10 @@
|
|||||||
@if (!$application->dockerfile)
|
@if (!$application->dockerfile)
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.select id="application.build_pack" label="Build Pack" required>
|
<x-forms.select wire:model="application.build_pack" label="Build Pack" required>
|
||||||
<option value="nixpacks">Nixpacks</option>
|
<option value="nixpacks">Nixpacks</option>
|
||||||
<option value="dockerfile">Dockerfile</option>
|
<option value="dockerfile">Dockerfile</option>
|
||||||
|
<option value="dockerimage">Docker Image</option>
|
||||||
</x-forms.select>
|
</x-forms.select>
|
||||||
@if ($application->settings->is_static)
|
@if ($application->settings->is_static)
|
||||||
<x-forms.select id="application.static_image" label="Static Image" required>
|
<x-forms.select id="application.static_image" label="Static Image" required>
|
||||||
@@ -41,25 +42,42 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($application->could_set_build_commands())
|
@if ($application->build_pack !== 'dockerimage')
|
||||||
<h3>Build</h3>
|
<h3>Build</h3>
|
||||||
<div class="flex flex-col gap-2 xl:flex-row">
|
@if ($application->could_set_build_commands())
|
||||||
<x-forms.input placeholder="pnpm install" id="application.install_command"
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
label="Install Command" />
|
<x-forms.input placeholder="pnpm install" id="application.install_command"
|
||||||
<x-forms.input placeholder="pnpm build" id="application.build_command" label="Build Command" />
|
label="Install Command" />
|
||||||
<x-forms.input placeholder="pnpm start" id="application.start_command" label="Start Command" />
|
<x-forms.input placeholder="pnpm build" id="application.build_command" label="Build Command" />
|
||||||
</div>
|
<x-forms.input placeholder="pnpm start" id="application.start_command" label="Start Command" />
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 xl:flex-row">
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
||||||
helper="Directory to use as root. Useful for monorepos." />
|
helper="Directory to use as root. Useful for monorepos." />
|
||||||
@if ($application->settings->is_static)
|
@if ($application->build_pack === 'dockerfile')
|
||||||
<x-forms.input placeholder="/dist" id="application.publish_directory" label="Publish Directory"
|
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
|
||||||
required />
|
label="Dockerfile Location"
|
||||||
@else
|
helper="It is calculated together with the Base Directory: {{ Str::start($application->base_directory . $application->dockerfile_location, '/') }}" />
|
||||||
<x-forms.input placeholder="/" id="application.publish_directory" label="Publish Directory" />
|
@endif
|
||||||
|
@if ($application->could_set_build_commands())
|
||||||
|
@if ($application->settings->is_static)
|
||||||
|
<x-forms.input placeholder="/dist" id="application.publish_directory"
|
||||||
|
label="Publish Directory" required />
|
||||||
|
@else
|
||||||
|
<x-forms.input placeholder="/" id="application.publish_directory"
|
||||||
|
label="Publish Directory" />
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
|
<x-forms.input id="application.docker_registry_image_name" label="Docker Image" />
|
||||||
|
<x-forms.input id="application.docker_registry_image_tag" label="Docker Image Tag" />
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if ($application->dockerfile)
|
@if ($application->dockerfile)
|
||||||
<x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
|
<x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
|
||||||
@endif
|
@endif
|
||||||
@@ -78,7 +96,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<h3>Advanced</h3>
|
<h3>Advanced</h3>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
|
|
||||||
<x-forms.checkbox
|
<x-forms.checkbox
|
||||||
helper="Your application will be available only on https if your domain starts with https://..."
|
helper="Your application will be available only on https if your domain starts with https://..."
|
||||||
instantSave id="is_force_https_enabled" label="Force Https" />
|
instantSave id="is_force_https_enabled" label="Force Https" />
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user