mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-24 12:33:17 +00:00
Compare commits
86 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12fc5a8f91 | ||
|
|
9eba058cf7 | ||
|
|
fa4f5fea8c | ||
|
|
898563fe7c | ||
|
|
c418a17161 | ||
|
|
cd0da04ea2 | ||
|
|
01e942c6a0 | ||
|
|
bb9abafa82 | ||
|
|
d0cd926517 | ||
|
|
9baf0161c7 | ||
|
|
8ba18b2ce1 | ||
|
|
ab021ee535 | ||
|
|
c76a1b1ba5 | ||
|
|
6266a5e500 | ||
|
|
5d27e89bfa | ||
|
|
6da4e78374 | ||
|
|
be30651172 | ||
|
|
95764c2b76 | ||
|
|
92a75685b5 | ||
|
|
a1592373aa | ||
|
|
5747a87f66 | ||
|
|
9cda671aef | ||
|
|
2c9983046c | ||
|
|
11d33f328e | ||
|
|
f79c741d95 | ||
|
|
8a39a4469a | ||
|
|
42daae10c6 | ||
|
|
64a65e2018 | ||
|
|
16c71f3647 | ||
|
|
363f525ad1 | ||
|
|
f4fb519d55 | ||
|
|
b7786504b8 | ||
|
|
f0adf10e6a | ||
|
|
cc8c6c5d16 | ||
|
|
4782446f42 | ||
|
|
230155312f | ||
|
|
3ab4365fca | ||
|
|
7349068b95 | ||
|
|
da6cc151d1 | ||
|
|
50527cf0a3 | ||
|
|
26f490bb00 | ||
|
|
dc4f412227 | ||
|
|
ec4234e243 | ||
|
|
f47fcb01ce | ||
|
|
02f6673345 | ||
|
|
e6cd8702b5 | ||
|
|
fda4ea8cca | ||
|
|
655d004ce7 | ||
|
|
56981d134c | ||
|
|
b9d49d2951 | ||
|
|
0c1e7c499e | ||
|
|
32fead5753 | ||
|
|
e5e9faba35 | ||
|
|
2852630d6c | ||
|
|
a4cc406114 | ||
|
|
53b15a5762 | ||
|
|
929a4e6474 | ||
|
|
45b597bbab | ||
|
|
0d1a2aa5d1 | ||
|
|
b82353d5e2 | ||
|
|
b17c09f7a7 | ||
|
|
f6c3fe7888 | ||
|
|
2e855e030f | ||
|
|
49f86621f4 | ||
|
|
03d9f93397 | ||
|
|
c472042a94 | ||
|
|
9f4356f67d | ||
|
|
a50317cc76 | ||
|
|
8afa98a1ca | ||
|
|
f6737f21dd | ||
|
|
e4a51cc116 | ||
|
|
acd78ae196 | ||
|
|
953bcfb5bb | ||
|
|
dacfab8b29 | ||
|
|
48b3e99939 | ||
|
|
41ad67c7c9 | ||
|
|
b49725cb1c | ||
|
|
75e674a966 | ||
|
|
9d826d9fb4 | ||
|
|
0af221f9fc | ||
|
|
9066c9bf90 | ||
|
|
77e3208f00 | ||
|
|
db5ecf07bd | ||
|
|
7f28aa6985 | ||
|
|
9d53e04ce9 | ||
|
|
3db9a1dd6e |
26
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
Normal file
26
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Bug report
|
||||
description: Create a new bug report
|
||||
title: '[Bug]: '
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: A clear and concise description of the problem
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Minimal Reproduction (if possible, example repository)
|
||||
description: Please provide a step by step guide to reproduce the issue
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Exception or Error
|
||||
description: Please provide error logs if possible.
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Coolify's version (see bottom left corner).
|
||||
validations:
|
||||
required: true
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 🤔 Community Support (Chat)
|
||||
url: https://coollabs.io/discord
|
||||
about: Reach out to us on Discord.
|
||||
- name: 🙋♂️ Feature Requests
|
||||
url: https://github.com/coollabsio/coolify/discussions/categories/feature-requests-ideas
|
||||
about: All feature requests will be discussed here.
|
||||
4
.github/workflows/development-build.yml
vendored
4
.github/workflows/development-build.yml
vendored
@@ -13,7 +13,7 @@ env:
|
||||
|
||||
jobs:
|
||||
amd64:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [self-hosted, x64]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Login to ghcr.io
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64
|
||||
merge-manifest:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [self-hosted, x64]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -31,3 +31,4 @@ _ide_helper.php
|
||||
_ide_helper_models.php
|
||||
.rnd
|
||||
/.ssh
|
||||
scripts/load-test/*
|
||||
|
||||
52
README.md
52
README.md
@@ -1,20 +1,30 @@
|
||||
# Coolify v4 Beta
|
||||
# About the Project
|
||||
|
||||
An open-source & self-hostable Heroku / Netlify alternative.
|
||||
Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc.
|
||||
|
||||
It helps you to manage your servers, applications, databases on your own hardware, all you need is SSH connection. You can manage VPS, Bare Metal, Raspberry PI's anything.
|
||||
|
||||
Image if you could have the ease of a cloud but with your own servers. That is **Coolify**.
|
||||
|
||||
No vendor lock-in, which means that all the configuration for your applications/databases/etc are saved to your server. So if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You just lose the automations and all the magic. 🪄️
|
||||
|
||||
For more information, take a look at our landing page [here](https://coolify.io).
|
||||
|
||||
> If you are looking for previous (v3) version, it is [here](https://github.com/coollabsio/coolify/tree/v3).
|
||||
|
||||
# Cloud
|
||||
|
||||
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
||||
|
||||
You can easily attach your own servers, get all the automations, free email notifications, etc.
|
||||
|
||||
For more information & pricing, take a look at our landing page [here](https://coolify.io).
|
||||
|
||||
# Beta
|
||||
|
||||
You are checking the next-gen of Coolify, aka v4. Hi 👋
|
||||
The latest version (v4) is still in beta. That does not mean it is unstable. All the features that are available are stable enough be usable in real-life.
|
||||
|
||||
It is still in beta, lots of improvements will come every day. Things could break, but we are working hard to make it stable as soon as possible. If you find any bugs, please report them.
|
||||
|
||||
Automatic updates are available, so you will receive the latest version as soon as it is released.
|
||||
|
||||
If you are looking for v3, check out the [v3 branch](https://github.com/coollabsio/coolify/tree/v3).
|
||||
|
||||
## What's new?
|
||||
|
||||
Well, the whole tech stack changed, core is different, so yeah, a lot (documentation incoming).
|
||||
There are hundreds of people using it for managing their client's applications, freelancers, hobbyists, businesses.
|
||||
|
||||
# Installation
|
||||
|
||||
@@ -26,13 +36,19 @@ You can find the installation script [here](./scripts/install.sh).
|
||||
|
||||
## Support
|
||||
|
||||
- Twitter: [@heyandras](https://twitter.com/heyandras)
|
||||
- Mastodon: [@andrasbacsai@fosstodon.org](https://fosstodon.org/@andrasbacsai)
|
||||
- Email: [andras@coollabs.io](mailto:andras@coollabs.io)
|
||||
- Discord: [Invitation](https://coollabs.io/discord)
|
||||
- Telegram: [@andrasbacsai](https://t.me/andrasbacsai)
|
||||
Contact us [here](https://docs.coollabs.io/contact).
|
||||
|
||||
---
|
||||
## Recognitions
|
||||
|
||||
<a href="https://news.ycombinator.com/item?id=26624341">
|
||||
<img
|
||||
style="width: 250px; height: 54px;" width="250" height="54"
|
||||
alt="Featured on Hacker News"
|
||||
src="https://hackernews-badge.vercel.app/api?id=26624341"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a href="https://www.producthunt.com/posts/coolify?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An open-source & self-hostable Heroku, Netlify alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
|
||||
## 💰 Financial Contributors
|
||||
|
||||
|
||||
@@ -97,7 +97,6 @@ class RunRemoteProcess
|
||||
'status' => $status->value,
|
||||
]);
|
||||
$this->activity->save();
|
||||
|
||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||
throw new \RuntimeException($processResult->errorOutput());
|
||||
}
|
||||
|
||||
@@ -57,13 +57,13 @@ class CheckResaleLicense
|
||||
throw new \Exception('Invalid license key.');
|
||||
}
|
||||
throw new \Exception('Cannot activate license key.');
|
||||
} catch (\Throwable $th) {
|
||||
ray($th);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
$settings->update([
|
||||
'resale_license' => null,
|
||||
'is_resale_license_active' => false,
|
||||
]);
|
||||
throw $th;
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ class SaveConfigurationSync
|
||||
"mkdir -p $proxy_path",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
||||
], $server);
|
||||
} catch (\Throwable $th) {
|
||||
ray($th);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace App\Actions\Proxy;
|
||||
|
||||
use App\Enums\ProxyStatus;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
@@ -30,19 +28,22 @@ class StartProxy
|
||||
$server->save();
|
||||
|
||||
$activity = remote_process([
|
||||
"echo 'Creating required Docker networks...'",
|
||||
"echo '####### Creating required Docker networks...'",
|
||||
...$create_networks_command,
|
||||
"cd $proxy_path",
|
||||
"echo 'Creating Docker Compose file...'",
|
||||
"echo 'Pulling docker image...'",
|
||||
'docker compose pull -q',
|
||||
"echo 'Stopping existing proxy...'",
|
||||
"echo '####### Creating Docker Compose file...'",
|
||||
"echo '####### Pulling docker image...'",
|
||||
'docker compose pull',
|
||||
"echo '####### Stopping existing coolify-proxy...'",
|
||||
'docker compose down -v --remove-orphans',
|
||||
"lsof -nt -i:80 | xargs -r kill -9",
|
||||
"lsof -nt -i:443 | xargs -r kill -9",
|
||||
"echo 'Starting proxy...'",
|
||||
"systemctl disable nginx > /dev/null 2>&1 || true",
|
||||
"systemctl disable apache2 > /dev/null 2>&1 || true",
|
||||
"systemctl disable apache > /dev/null 2>&1 || true",
|
||||
"echo '####### Starting coolify-proxy...'",
|
||||
'docker compose up -d --remove-orphans',
|
||||
"echo 'Proxy installed successfully...'"
|
||||
"echo '####### Proxy installed successfully...'"
|
||||
], $server);
|
||||
|
||||
return $activity;
|
||||
|
||||
@@ -18,42 +18,42 @@ class InstallDocker
|
||||
"max-file": "3"
|
||||
}
|
||||
}');
|
||||
$found = StandaloneDocker::where('server_id', $server->id);
|
||||
if ($found->count() == 0) {
|
||||
StandaloneDocker::create([
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
if (isDev()) {
|
||||
$activity = remote_process([
|
||||
"echo ####### Installing Prerequisites...",
|
||||
"echo ####### Installing/updating Docker Engine...",
|
||||
"echo ####### Configuring Docker Engine (merging existing configuration with the required)...",
|
||||
"echo ####### Restarting Docker Engine...",
|
||||
return remote_process([
|
||||
"echo '####### Installing Prerequisites...'",
|
||||
"sleep 1",
|
||||
"echo '####### Installing/updating Docker Engine...'",
|
||||
"echo '####### Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||
"sleep 4",
|
||||
"echo '####### Restarting Docker Engine...'",
|
||||
"ls -l /tmp"
|
||||
], $server);
|
||||
} else {
|
||||
$activity = remote_process([
|
||||
"echo ####### Installing Prerequisites...",
|
||||
return remote_process([
|
||||
"echo '####### Installing Prerequisites...'",
|
||||
"command -v jq >/dev/null || apt-get update",
|
||||
"command -v jq >/dev/null || apt install -y jq",
|
||||
"echo ####### Installing/updating Docker Engine...",
|
||||
"echo '####### Installing/updating Docker Engine...'",
|
||||
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh",
|
||||
"echo ####### Configuring Docker Engine (merging existing configuration with the required)...",
|
||||
"echo '####### Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
|
||||
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
||||
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
|
||||
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
|
||||
"echo ####### Restarting Docker Engine...",
|
||||
"echo '####### Restarting Docker Engine...'",
|
||||
"systemctl restart docker",
|
||||
"echo ####### Creating default network...",
|
||||
"docker network create --attachable coolify",
|
||||
"echo ####### Done!"
|
||||
"echo '####### Creating default Docker network (coolify)...'",
|
||||
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
||||
"echo '####### Done!'"
|
||||
], $server);
|
||||
$found = StandaloneDocker::where('server_id', $server->id);
|
||||
if ($found->count() == 0) {
|
||||
StandaloneDocker::create([
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $activity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@ class UpdateCoolify
|
||||
$this->update();
|
||||
}
|
||||
send_internal_notification('InstanceAutoUpdateJob done to version: ' . $this->latestVersion . ' from version: ' . $this->currentVersion);
|
||||
} catch (\Exception $th) {
|
||||
} catch (\Throwable $e) {
|
||||
ray('InstanceAutoUpdateJob failed');
|
||||
ray($th->getMessage());
|
||||
send_internal_notification('InstanceAutoUpdateJob failed: ' . $th->getMessage());
|
||||
throw $th;
|
||||
ray($e->getMessage());
|
||||
send_internal_notification('InstanceAutoUpdateJob failed: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\SendConfirmationForWaitlistJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\Team;
|
||||
use App\Models\TeamInvitation;
|
||||
use App\Models\User;
|
||||
use App\Models\Waitlist;
|
||||
use App\Notifications\Application\DeploymentFailed;
|
||||
use App\Notifications\Application\DeploymentSuccess;
|
||||
use App\Notifications\Application\StatusChanged;
|
||||
@@ -22,31 +26,34 @@ use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Mail;
|
||||
use Str;
|
||||
|
||||
use function Laravel\Prompts\confirm;
|
||||
use function Laravel\Prompts\select;
|
||||
use function Laravel\Prompts\text;
|
||||
|
||||
class TestEmail extends Command
|
||||
class Emails extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'email:test';
|
||||
protected $signature = 'emails';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Send a test email to the admin';
|
||||
protected $description = 'Send out test / prod emails';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
private ?MailMessage $mail = null;
|
||||
private ?string $email = null;
|
||||
public function handle()
|
||||
{
|
||||
$email = select(
|
||||
$type = select(
|
||||
'Which Email should be sent?',
|
||||
options: [
|
||||
'emails-test' => 'Test',
|
||||
@@ -58,17 +65,22 @@ class TestEmail extends Command
|
||||
'invitation-link' => 'Invitation Link',
|
||||
'waitlist-invitation-link' => 'Waitlist Invitation Link',
|
||||
'waitlist-confirmation' => 'Waitlist Confirmation',
|
||||
'realusers-before-trial' => 'REAL - Registered Users Before Trial without Subscription',
|
||||
'realusers-server-lost-connection' => 'REAL - Server Lost Connection',
|
||||
],
|
||||
);
|
||||
$type = set_transanctional_email_settings();
|
||||
if (!$type) {
|
||||
throw new Exception('No email settings found.');
|
||||
$emailsGathered = ['realusers-before-trial','realusers-server-lost-connection'];
|
||||
if (!in_array($type, $emailsGathered)) {
|
||||
$this->email = text('Email Address to send to');
|
||||
}
|
||||
set_transanctional_email_settings();
|
||||
|
||||
$this->mail = new MailMessage();
|
||||
$this->mail->subject("Test Email");
|
||||
switch ($email) {
|
||||
switch ($type) {
|
||||
case 'emails-test':
|
||||
$this->mail = (new Test())->toMail();
|
||||
$this->sendEmail();
|
||||
break;
|
||||
case 'application-deployment-success':
|
||||
$application = Application::all()->first();
|
||||
@@ -146,39 +158,90 @@ class TestEmail extends Command
|
||||
case 'waitlist-invitation-link':
|
||||
$this->mail = new MailMessage();
|
||||
$this->mail->view('emails.waitlist-invitation', [
|
||||
'email' => 'test2@example.com',
|
||||
'password' => "supersecretpassword",
|
||||
'loginLink' => 'https://coolify.io',
|
||||
]);
|
||||
$this->mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
||||
$this->sendEmail();
|
||||
break;
|
||||
case 'waitlist-confirmation':
|
||||
$found = Waitlist::where('email', $this->email)->first();
|
||||
if ($found) {
|
||||
SendConfirmationForWaitlistJob::dispatch($this->email, $found->uuid);
|
||||
} else {
|
||||
throw new Exception('Waitlist not found');
|
||||
}
|
||||
|
||||
break;
|
||||
case 'realusers-before-trial':
|
||||
$this->mail = new MailMessage();
|
||||
$this->mail->view(
|
||||
'emails.waitlist-confirmation',
|
||||
[
|
||||
'confirmation_url' => 'http://example.com',
|
||||
'cancel_url' => 'http://example.com',
|
||||
]
|
||||
);
|
||||
$this->mail->subject('You are on the waitlist!');
|
||||
$this->sendEmail();
|
||||
$this->mail->view('emails.before-trial-conversion');
|
||||
$this->mail->subject('Trial period has been added for all subscription plans.');
|
||||
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
|
||||
if (!$teams || $teams->isEmpty()) {
|
||||
echo 'No teams found.' . PHP_EOL;
|
||||
return;
|
||||
}
|
||||
$emails = [];
|
||||
foreach ($teams as $team) {
|
||||
foreach ($team->members as $member) {
|
||||
if ($member->email) {
|
||||
$emails[] = $member->email;
|
||||
}
|
||||
}
|
||||
}
|
||||
$emails = array_unique($emails);
|
||||
$this->info("Sending to " . count($emails) . " emails.");
|
||||
foreach ($emails as $email) {
|
||||
$this->info($email);
|
||||
}
|
||||
$confirmed = confirm('Are you sure?');
|
||||
if ($confirmed) {
|
||||
foreach ($emails as $email) {
|
||||
$this->sendEmail($email);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'realusers-server-lost-connection':
|
||||
$serverId = text('Server Id');
|
||||
$server = Server::find($serverId);
|
||||
if (!$server) {
|
||||
throw new Exception('Server not found');
|
||||
}
|
||||
$admins = [];
|
||||
$members = $server->team->members;
|
||||
foreach ($members as $member) {
|
||||
if ($member->isAdmin()) {
|
||||
$admins[] = $member->email;
|
||||
}
|
||||
}
|
||||
$this->info('Sending to ' . count($admins) . ' admins.');
|
||||
foreach ($admins as $admin) {
|
||||
$this->info($admin);
|
||||
}
|
||||
$this->mail = new MailMessage();
|
||||
$this->mail->view('emails.server-lost-connection', [
|
||||
'name' => $server->name,
|
||||
]);
|
||||
$this->mail->subject('Action required: Server ' . $server->name . ' lost connection.');
|
||||
foreach ($admins as $email) {
|
||||
$this->sendEmail($email);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
private function sendEmail()
|
||||
private function sendEmail(string $email = null)
|
||||
{
|
||||
if ($email) {
|
||||
$this->email = $email;
|
||||
}
|
||||
Mail::send(
|
||||
[],
|
||||
[],
|
||||
fn (Message $message) => $message
|
||||
->from(
|
||||
'internal@example.com',
|
||||
'Test Email',
|
||||
)
|
||||
->to('test@example.com')
|
||||
->to($this->email)
|
||||
->subject($this->mail->subject)
|
||||
->html((string)$this->mail->render())
|
||||
);
|
||||
$this->info("Email sent to $this->email successfully. 📧");
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class Init extends Command
|
||||
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
||||
$deployment->save();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class SyncBunny extends Command
|
||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
||||
]);
|
||||
echo "All files uploaded & purged...\n";
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class WaitlistInvite extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'waitlist:invite {email?} {--only-email}';
|
||||
protected $signature = 'waitlist:invite {--people=1} {--only-email} {email?}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -33,6 +33,12 @@ class WaitlistInvite extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$people = $this->option('people');
|
||||
for ($i = 0; $i < $people; $i++) {
|
||||
$this->main();
|
||||
}
|
||||
}
|
||||
private function main() {
|
||||
if ($this->argument('email')) {
|
||||
if ($this->option('only-email')) {
|
||||
$this->next_patient = User::whereEmail($this->argument('email'))->first();
|
||||
@@ -86,13 +92,10 @@ class WaitlistInvite extends Command
|
||||
}
|
||||
private function send_email()
|
||||
{
|
||||
ray($this->next_patient->email, $this->password);
|
||||
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
||||
$loginLink = route('auth.link', ['token' => $token]);
|
||||
$mail = new MailMessage();
|
||||
$mail->view('emails.waitlist-invitation', [
|
||||
'email' => $this->next_patient->email,
|
||||
'password' => $this->password,
|
||||
'loginLink' => $loginLink,
|
||||
]);
|
||||
$mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Jobs\ApplicationContainerStatusJob;
|
||||
use App\Jobs\CheckResaleLicenseJob;
|
||||
use App\Jobs\CleanupInstanceStuffsJob;
|
||||
@@ -9,11 +10,12 @@ use App\Jobs\DatabaseBackupJob;
|
||||
use App\Jobs\DatabaseContainerStatusJob;
|
||||
use App\Jobs\DockerCleanupJob;
|
||||
use App\Jobs\InstanceAutoUpdateJob;
|
||||
use App\Jobs\ProxyCheckJob;
|
||||
use App\Jobs\ResourceStatusJob;
|
||||
use App\Jobs\ProxyContainerStatusJob;
|
||||
use App\Jobs\ServerDetailsCheckJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
@@ -23,23 +25,32 @@ class Kernel extends ConsoleKernel
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
if (isDev()) {
|
||||
$schedule->command('horizon:snapshot')->everyMinute();
|
||||
// $schedule->job(new ResourceStatusJob)->everyMinute();
|
||||
$schedule->job(new ProxyCheckJob)->everyFiveMinutes();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute();
|
||||
$schedule->job(new ServerDetailsCheckJob(Server::find(0)))->everyTenMinutes()->onOneServer();
|
||||
// $schedule->command('horizon:snapshot')->everyMinute();
|
||||
// $schedule->job(new CleanupInstanceStuffsJob)->everyMinute();
|
||||
// $schedule->job(new CheckResaleLicenseJob)->hourly();
|
||||
$schedule->job(new DockerCleanupJob)->everyOddHour();
|
||||
// $schedule->job(new DockerCleanupJob)->everyOddHour();
|
||||
// $this->instance_auto_update($schedule);
|
||||
// $this->check_scheduled_backups($schedule);
|
||||
// $this->check_resources($schedule);
|
||||
// $this->check_proxies($schedule);
|
||||
} else {
|
||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTenMinutes()->onOneServer();
|
||||
// $schedule->job(new ResourceStatusJob)->everyMinute()->onOneServer();
|
||||
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||
$schedule->job(new ProxyCheckJob)->everyFiveMinutes()->onOneServer();
|
||||
$schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
|
||||
$this->instance_auto_update($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
$this->check_proxies($schedule);
|
||||
}
|
||||
}
|
||||
private function check_proxies($schedule)
|
||||
{
|
||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true)->whereNotNull('proxy.type')->where('proxy.type', '!=', ProxyTypes::NONE->value);
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new ProxyContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||
}
|
||||
$this->instance_auto_update($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
}
|
||||
private function check_resources($schedule)
|
||||
{
|
||||
@@ -53,7 +64,8 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->job(new DatabaseContainerStatusJob($postgresql))->everyMinute()->onOneServer();
|
||||
}
|
||||
}
|
||||
private function instance_auto_update($schedule){
|
||||
private function instance_auto_update($schedule)
|
||||
{
|
||||
if (isDev()) {
|
||||
return;
|
||||
}
|
||||
@@ -74,6 +86,11 @@ class Kernel extends ConsoleKernel
|
||||
if (!$scheduled_backup->enabled) {
|
||||
continue;
|
||||
}
|
||||
if (is_null(data_get($scheduled_backup, 'database'))) {
|
||||
ray('database not found');
|
||||
$scheduled_backup->delete();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) {
|
||||
$scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency];
|
||||
|
||||
@@ -25,7 +25,7 @@ class Handler extends ExceptionHandler
|
||||
* @var array<int, class-string<\Throwable>>
|
||||
*/
|
||||
protected $dontReport = [
|
||||
//
|
||||
ProcessException::class
|
||||
];
|
||||
/**
|
||||
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||
@@ -50,8 +50,13 @@ class Handler extends ExceptionHandler
|
||||
return;
|
||||
}
|
||||
app('sentry')->configureScope(
|
||||
function (Scope $scope){
|
||||
$scope->setUser(['id'=> config('sentry.server_name')]);
|
||||
function (Scope $scope) {
|
||||
$scope->setUser(
|
||||
[
|
||||
'id' => config('sentry.server_name'),
|
||||
'email' => auth()->user()->email
|
||||
]
|
||||
);
|
||||
}
|
||||
);
|
||||
Integration::captureUnhandledException($e);
|
||||
|
||||
10
app/Exceptions/ProcessException.php
Normal file
10
app/Exceptions/ProcessException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ProcessException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -154,8 +154,8 @@ class Controller extends BaseController
|
||||
$invitation->delete();
|
||||
abort(401);
|
||||
}
|
||||
} catch (Throwable $th) {
|
||||
throw $th;
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,8 +172,8 @@ class Controller extends BaseController
|
||||
}
|
||||
$invitation->delete();
|
||||
return redirect()->route('team.index');
|
||||
} catch (Throwable $th) {
|
||||
throw $th;
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Actions\Server\InstallDocker;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -70,9 +71,10 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
}
|
||||
public function skipBoarding()
|
||||
{
|
||||
currentTeam()->update([
|
||||
Team::find(currentTeam()->id)->update([
|
||||
'show_boarding' => false
|
||||
]);
|
||||
ray(currentTeam());
|
||||
refreshSession();
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
@@ -133,7 +135,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
{
|
||||
$this->selectedExistingPrivateKey = null;
|
||||
$this->privateKeyType = $type;
|
||||
if ($type === 'create' && !isDev()) {
|
||||
if ($type === 'create') {
|
||||
$this->createNewPrivateKey();
|
||||
}
|
||||
$this->currentState = 'create-private-key';
|
||||
@@ -191,7 +193,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
}
|
||||
$this->getProxyType();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@ class CheckLicense extends Component
|
||||
try {
|
||||
resolve(CheckResaleLicense::class)();
|
||||
$this->emit('reloadWindow');
|
||||
} catch (\Throwable $th) {
|
||||
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $th->getMessage());
|
||||
ray($th->getMessage());
|
||||
} catch (\Throwable $e) {
|
||||
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
return redirect()->to('/settings/license');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class Form extends Component
|
||||
}
|
||||
$this->destination->delete();
|
||||
return redirect()->route('dashboard');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class StandaloneDocker extends Component
|
||||
}
|
||||
$this->createNetworkAndAttachToProxy();
|
||||
return redirect()->route('destination.show', $docker->uuid);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class ForcePasswordReset extends Component
|
||||
send_internal_notification('First login for ' . auth()->user()->email);
|
||||
}
|
||||
return redirect()->route('dashboard');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Help extends Component
|
||||
try {
|
||||
$this->rateLimit(1, 60);
|
||||
$this->validate();
|
||||
$subscriptionType = auth()->user()?->subscription?->type() ?? 'unknown';
|
||||
$subscriptionType = auth()->user()?->subscription?->type() ?? 'Free';
|
||||
$debug = "Route: {$this->path}";
|
||||
$mail = new MailMessage();
|
||||
$mail->view(
|
||||
@@ -41,9 +41,9 @@ class Help extends Component
|
||||
]
|
||||
);
|
||||
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
||||
send_user_an_email($mail, 'hi@coollabs.io');
|
||||
send_user_an_email($mail, 'hi@coollabs.io', auth()->user()?->email);
|
||||
$this->emit('success', 'Your message has been sent successfully. We will get in touch with you as soon as possible.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class DiscordSettings extends Component
|
||||
{
|
||||
try {
|
||||
$this->submit();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
$this->team->discord_enabled = false;
|
||||
$this->validate();
|
||||
|
||||
@@ -63,7 +63,7 @@ class EmailSettings extends Component
|
||||
]);
|
||||
$this->team->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class EmailSettings extends Component
|
||||
$this->team->resend_enabled = false;
|
||||
$this->team->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ class EmailSettings extends Component
|
||||
try {
|
||||
$this->team->smtp_enabled = false;
|
||||
$this->submitResend();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
@@ -102,12 +102,19 @@ class EmailSettings extends Component
|
||||
try {
|
||||
$this->team->resend_enabled = false;
|
||||
$this->submit();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->team->save();
|
||||
if (is_a($this->team, Team::class)) {
|
||||
refreshSession();
|
||||
}
|
||||
$this->emit('success', 'Settings saved.');
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
@@ -124,7 +131,7 @@ class EmailSettings extends Component
|
||||
]);
|
||||
$this->team->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
@@ -139,7 +146,7 @@ class EmailSettings extends Component
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->resend_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ class TelegramSettings extends Component
|
||||
'team.telegram_notifications_deployments' => 'nullable|boolean',
|
||||
'team.telegram_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.telegram_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.telegram_notifications_test_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_deployments_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_status_changes_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_database_backups_message_thread_id' => 'nullable|string',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'team.telegram_token' => 'Token',
|
||||
@@ -31,7 +35,7 @@ class TelegramSettings extends Component
|
||||
{
|
||||
try {
|
||||
$this->submit();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
$this->team->telegram_enabled = false;
|
||||
$this->validate();
|
||||
|
||||
@@ -8,7 +8,7 @@ use Livewire\Component;
|
||||
class Change extends Component
|
||||
{
|
||||
public PrivateKey $private_key;
|
||||
|
||||
public $public_key;
|
||||
protected $rules = [
|
||||
'private_key.name' => 'required|string',
|
||||
'private_key.description' => 'nullable|string',
|
||||
@@ -21,6 +21,14 @@ class Change extends Component
|
||||
'private_key.private_key' => 'private key'
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
$this->public_key = $this->private_key->publicKey();
|
||||
}catch(\Exception $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
@@ -30,7 +38,7 @@ class Change extends Component
|
||||
return redirect()->route('security.private-key.index');
|
||||
}
|
||||
$this->emit('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
@@ -41,7 +49,7 @@ class Change extends Component
|
||||
$this->private_key->private_key = formatPrivateKey($this->private_key->private_key);
|
||||
$this->private_key->save();
|
||||
refresh_server_connection($this->private_key);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ class Create extends Component
|
||||
if ($this->from === 'server') {
|
||||
return redirect()->route('server.create');
|
||||
}
|
||||
return redirect()->route('private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class AddEmpty extends Component
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
return redirect()->route('project.show', $project->uuid);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
general_error_handler($e, $this);
|
||||
} finally {
|
||||
$this->name = '';
|
||||
|
||||
@@ -31,7 +31,7 @@ class AddEnvironment extends Component
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
general_error_handler($e, $this);
|
||||
} finally {
|
||||
$this->name = '';
|
||||
|
||||
@@ -159,7 +159,7 @@ class General extends Component
|
||||
}
|
||||
$this->application->save();
|
||||
$this->emit('success', 'Application settings updated!');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class BackupEdit extends Component
|
||||
$this->backup->save();
|
||||
$this->backup->refresh();
|
||||
$this->emit('success', 'Backup updated successfully');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->emit('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ class BackupEdit extends Component
|
||||
$this->backup->save();
|
||||
$this->backup->refresh();
|
||||
$this->emit('success', 'Backup updated successfully');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->emit('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class CreateScheduledBackup extends Component
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->emit('refreshScheduledBackups');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
general_error_handler($e, $this);
|
||||
} finally {
|
||||
$this->frequency = '';
|
||||
|
||||
@@ -42,8 +42,13 @@ class Heading extends Component
|
||||
["docker rm -f {$this->database->uuid}"],
|
||||
$this->database->destination->server
|
||||
);
|
||||
if ($this->database->is_public) {
|
||||
stopPostgresProxy($this->database);
|
||||
$this->database->is_public = false;
|
||||
}
|
||||
$this->database->status = 'stopped';
|
||||
$this->database->save();
|
||||
$this->emit('refresh');
|
||||
// $this->database->environment->project->team->notify(new StatusChanged($this->database));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ class General extends Component
|
||||
public StandalonePostgresql $database;
|
||||
public string $new_filename;
|
||||
public string $new_content;
|
||||
public string $db_url;
|
||||
|
||||
protected $listeners = ['refresh', 'save_init_script', 'delete_init_script'];
|
||||
|
||||
@@ -26,6 +27,8 @@ class General extends Component
|
||||
'database.init_scripts' => 'nullable',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
'database.is_public' => 'nullable|boolean',
|
||||
'database.public_port' => 'nullable|integer',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'database.name' => 'Name',
|
||||
@@ -38,8 +41,44 @@ class General extends Component
|
||||
'database.init_scripts' => 'Init Scripts',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$this->getDbUrl();
|
||||
}
|
||||
public function getDbUrl() {
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->destination->server->ip}:{$this->database->public_port}/{$this->database->postgres_db}";
|
||||
} else {
|
||||
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->uuid}:5432/{$this->database->postgres_db}";
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->database->is_public && !$this->database->public_port) {
|
||||
$this->emit('error', 'Public port is required.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
$this->emit('success', 'Starting TCP proxy...');
|
||||
startPostgresProxy($this->database);
|
||||
$this->emit('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
stopPostgresProxy($this->database);
|
||||
$this->emit('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->getDbUrl();
|
||||
$this->database->save();
|
||||
} catch(Exception $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
|
||||
}
|
||||
public function save_init_script($script)
|
||||
{
|
||||
$this->database->init_scripts = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']);
|
||||
|
||||
@@ -19,7 +19,7 @@ class Edit extends Component
|
||||
try {
|
||||
$this->project->save();
|
||||
$this->emit('saved');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class GithubPrivateRepository extends Component
|
||||
'environment_name' => $environment->name,
|
||||
'project_uuid' => $project->uuid,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
'environment_name' => $environment->name,
|
||||
'application_uuid' => $application->uuid,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,14 +75,14 @@ class PublicGitRepository extends Component
|
||||
$this->get_git_source();
|
||||
$this->get_branch();
|
||||
$this->selected_branch = $this->git_branch;
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
if (!$this->branch_found && $this->git_branch == 'main') {
|
||||
try {
|
||||
$this->git_branch = 'master';
|
||||
$this->get_branch();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ class PublicGitRepository extends Component
|
||||
'environment_name' => $environment->name,
|
||||
'application_uuid' => $application->uuid,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class Select extends Component
|
||||
// try {
|
||||
// instantCommand("psql {$this->existingPostgresqlUrl} -c 'SELECT 1'");
|
||||
// $this->emit('success', 'Successfully connected to the database.');
|
||||
// } catch (\Exception $e) {
|
||||
// } catch (\Throwable $e) {
|
||||
// return general_error_handler($e, $this);
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -5,21 +5,89 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
use Str;
|
||||
|
||||
class All extends Component
|
||||
{
|
||||
public $resource;
|
||||
public bool $showPreview = false;
|
||||
public string|null $modalId = null;
|
||||
public ?string $variables = null;
|
||||
public ?string $variablesPreview = null;
|
||||
public string $view = 'normal';
|
||||
protected $listeners = ['refreshEnvs', 'submit'];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$resourceClass = get_class($this->resource);
|
||||
$resourceWithPreviews = ['App\Models\Application'];
|
||||
$simpleDockerfile = !is_null(data_get($this->resource, 'dockerfile'));
|
||||
if (Str::of($resourceClass)->contains($resourceWithPreviews) && !$simpleDockerfile) {
|
||||
$this->showPreview = true;
|
||||
}
|
||||
$this->modalId = new Cuid2(7);
|
||||
$this->getDevView();
|
||||
}
|
||||
public function getDevView()
|
||||
{
|
||||
$this->variables = $this->resource->environment_variables->map(function ($item) {
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
');
|
||||
if ($this->showPreview) {
|
||||
$this->variablesPreview = $this->resource->environment_variables_preview->map(function ($item) {
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
');
|
||||
}
|
||||
}
|
||||
public function switch()
|
||||
{
|
||||
$this->view = $this->view === 'normal' ? 'dev' : 'normal';
|
||||
}
|
||||
public function saveVariables($isPreview)
|
||||
{
|
||||
if ($isPreview) {
|
||||
$variables = parseEnvFormatToArray($this->variablesPreview);
|
||||
$existingVariables = $this->resource->environment_variables_preview();
|
||||
$this->resource->environment_variables_preview()->delete();
|
||||
} else {
|
||||
$variables = parseEnvFormatToArray($this->variables);
|
||||
$existingVariables = $this->resource->environment_variables();
|
||||
$this->resource->environment_variables()->delete();
|
||||
}
|
||||
foreach ($variables as $key => $variable) {
|
||||
$found = $existingVariables->where('key', $key)->first();
|
||||
if ($found) {
|
||||
$found->value = $variable;
|
||||
$found->save();
|
||||
continue;
|
||||
} else {
|
||||
$environment = new EnvironmentVariable();
|
||||
$environment->key = $key;
|
||||
$environment->value = $variable;
|
||||
$environment->is_build_time = false;
|
||||
$environment->is_preview = $isPreview ? true : false;
|
||||
if ($this->resource->type() === 'application') {
|
||||
$environment->application_id = $this->resource->id;
|
||||
}
|
||||
if ($this->resource->type() === 'standalone-postgresql') {
|
||||
$environment->standalone_postgresql_id = $this->resource->id;
|
||||
}
|
||||
$environment->save();
|
||||
}
|
||||
}
|
||||
if ($isPreview) {
|
||||
$this->emit('success', 'Preview environment variables updated successfully.');
|
||||
} else {
|
||||
$this->emit('success', 'Environment variables updated successfully.');
|
||||
}
|
||||
$this->refreshEnvs();
|
||||
}
|
||||
|
||||
public function refreshEnvs()
|
||||
{
|
||||
$this->resource->refresh();
|
||||
$this->getDevView();
|
||||
}
|
||||
|
||||
public function submit($data)
|
||||
@@ -43,9 +111,9 @@ class All extends Component
|
||||
$environment->standalone_postgresql_id = $this->resource->id;
|
||||
}
|
||||
$environment->save();
|
||||
$this->resource->refresh();
|
||||
$this->refreshEnvs();
|
||||
$this->emit('success', 'Environment variable added successfully.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class ResourceLimits extends Component
|
||||
$this->validate();
|
||||
$this->resource->save();
|
||||
$this->emit('success', 'Resource limits updated successfully.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class All extends Component
|
||||
$this->resource->refresh();
|
||||
$this->emit('success', 'Storage added successfully');
|
||||
$this->emit('clearAddStorage');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class RunCommand extends Component
|
||||
try {
|
||||
$activity = remote_process([$this->command], Server::where('uuid', $this->server)->first(), ignore_errors: true);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,13 +54,20 @@ class Form extends Component
|
||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server);
|
||||
if ($uptime) {
|
||||
$this->uptime = $uptime;
|
||||
$this->emit('success', 'Server is reachable!');
|
||||
} else {
|
||||
$this->emit('error', 'Server is not rachable');
|
||||
return;
|
||||
}
|
||||
if ($dockerVersion) {
|
||||
$this->dockerVersion = $dockerVersion;
|
||||
$this->emit('proxyStatusUpdated');
|
||||
$this->emit('success', 'Docker Engine 23+ is installed!');
|
||||
} else {
|
||||
$this->emit('error', 'Old (lower than 23) or no Docker version detected. Install Docker Engine on the General tab.');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,11 +81,9 @@ class Form extends Component
|
||||
}
|
||||
$this->server->delete();
|
||||
return redirect()->route('server.all');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@ class ByIp extends Component
|
||||
{
|
||||
$this->validate();
|
||||
try {
|
||||
if (!$this->private_key_id) {
|
||||
if (is_null($this->private_key_id)) {
|
||||
return $this->emit('error', 'You must select a private key');
|
||||
}
|
||||
$server = Server::create([
|
||||
@@ -78,7 +78,7 @@ class ByIp extends Component
|
||||
$server->settings->is_part_of_swarm = $this->is_part_of_swarm;
|
||||
$server->settings->save();
|
||||
return redirect()->route('server.show', $server->uuid);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@ class Proxy extends Component
|
||||
|
||||
public ?string $selectedProxy = null;
|
||||
public $proxy_settings = null;
|
||||
public string|null $redirect_url = null;
|
||||
public ?string $redirect_url = null;
|
||||
|
||||
protected $listeners = ['proxyStatusUpdated', 'saveConfiguration' => 'submit'];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->selectedProxy = $this->server->proxy->type;
|
||||
$this->redirect_url = $this->server->proxy->redirect_url;
|
||||
$this->selectedProxy = data_get($this->server, 'proxy.type');
|
||||
$this->redirect_url = data_get($this->server, 'proxy.redirect_url');
|
||||
}
|
||||
|
||||
public function proxyStatusUpdated()
|
||||
@@ -55,7 +55,7 @@ class Proxy extends Component
|
||||
|
||||
setup_default_redirect_404(redirect_url: $this->server->proxy->redirect_url, server: $this->server);
|
||||
$this->emit('success', 'Proxy configuration saved.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
}
|
||||
}
|
||||
@@ -64,16 +64,17 @@ class Proxy extends Component
|
||||
{
|
||||
try {
|
||||
$this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server, true);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function load_proxy_configuration()
|
||||
public function loadProxyConfiguration()
|
||||
{
|
||||
try {
|
||||
ray('loadProxyConfiguration');
|
||||
$this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,20 @@ class Deploy extends Component
|
||||
{
|
||||
public Server $server;
|
||||
public $proxy_settings = null;
|
||||
protected $listeners = ['proxyStatusUpdated'];
|
||||
|
||||
public function start_proxy()
|
||||
public function proxyStatusUpdated() {
|
||||
$this->server->refresh();
|
||||
}
|
||||
public function startProxy()
|
||||
{
|
||||
if (
|
||||
$this->server->proxy->last_applied_settings &&
|
||||
$this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings
|
||||
) {
|
||||
$this->emit('saveConfiguration', $this->server);
|
||||
resolve(SaveConfigurationSync::class)($this->server, $this->proxy_settings);
|
||||
}
|
||||
|
||||
$activity = resolve(StartProxy::class)($this->server);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Livewire\Server\Proxy;
|
||||
|
||||
use App\Jobs\ProxyContainerStatusJob;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -10,14 +9,27 @@ class Status extends Component
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
public function get_status()
|
||||
protected $listeners = ['proxyStatusUpdated'];
|
||||
public function proxyStatusUpdated()
|
||||
{
|
||||
if (data_get($this->server,'settings.is_usable')) {
|
||||
dispatch_sync(new ProxyContainerStatusJob(
|
||||
server: $this->server
|
||||
));
|
||||
$this->server->refresh();
|
||||
$this->emit('proxyStatusUpdated');
|
||||
$this->server->refresh();
|
||||
}
|
||||
public function getProxyStatus()
|
||||
{
|
||||
try {
|
||||
if ($this->server->isFunctional()) {
|
||||
$container = getContainerStatus(server: $this->server, container_id: 'coolify-proxy');
|
||||
$this->server->proxy->status = $container;
|
||||
$this->server->save();
|
||||
$this->emit('proxyStatusUpdated');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e);
|
||||
}
|
||||
}
|
||||
public function getProxyStatusWithNoti()
|
||||
{
|
||||
$this->emit('success', 'Refreshed proxy status.');
|
||||
$this->getProxyStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Http\Livewire\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
use Masmerise\Toaster\Toaster;
|
||||
|
||||
class ShowPrivateKey extends Component
|
||||
{
|
||||
@@ -12,14 +11,24 @@ class ShowPrivateKey extends Component
|
||||
public $privateKeys;
|
||||
public $parameters;
|
||||
|
||||
public function setPrivateKey($private_key_id)
|
||||
public function setPrivateKey($newPrivateKeyId)
|
||||
{
|
||||
$this->server->update([
|
||||
'private_key_id' => $private_key_id
|
||||
]);
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->server->refresh();
|
||||
$this->checkConnection();
|
||||
try {
|
||||
$oldPrivateKeyId = $this->server->private_key_id;
|
||||
$this->server->update([
|
||||
'private_key_id' => $newPrivateKeyId
|
||||
]);
|
||||
$this->server->refresh();
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->checkConnection();
|
||||
} catch (\Throwable $e) {
|
||||
$this->server->update([
|
||||
'private_key_id' => $oldPrivateKeyId
|
||||
]);
|
||||
$this->server->refresh();
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
return general_error_handler($e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkConnection()
|
||||
@@ -27,13 +36,18 @@ class ShowPrivateKey extends Component
|
||||
try {
|
||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server);
|
||||
if ($uptime) {
|
||||
Toaster::success('Server is reachable with this private key.');
|
||||
$this->emit('success', 'Server is reachable with this private key.');
|
||||
} else {
|
||||
$this->emit('error', 'Server is not reachable with this private key.');
|
||||
return;
|
||||
}
|
||||
if ($dockerVersion) {
|
||||
Toaster::success('Server is usable for Coolify.');
|
||||
$this->emit('success', 'Server is usable for Coolify.');
|
||||
} else {
|
||||
$this->emit('error', 'Old (lower than 23) or no Docker version detected. Install Docker Engine on the General tab.');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this);
|
||||
} catch (\Throwable $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Http\Livewire\Settings;
|
||||
|
||||
use App\Jobs\ProxyStartJob;
|
||||
use App\Jobs\ProxyContainerStatusJob;
|
||||
use App\Models\InstanceSettings as ModelsInstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
@@ -124,7 +124,7 @@ class Configuration extends Component
|
||||
];
|
||||
}
|
||||
$this->save_configuration_to_disk($traefik_dynamic_conf, $file);
|
||||
dispatch(new ProxyStartJob($this->server));
|
||||
dispatch(new ProxyContainerStatusJob($this->server));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class Email extends Component
|
||||
]);
|
||||
$this->settings->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
@@ -60,19 +60,27 @@ class Email extends Component
|
||||
$this->validate([
|
||||
'settings.resend_api_key' => 'required'
|
||||
]);
|
||||
$this->settings->smtp_enabled = false;
|
||||
$this->settings->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->settings->resend_enabled = false;
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSaveResend() {
|
||||
try {
|
||||
$this->settings->smtp_enabled = false;
|
||||
$this->submitResend();
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->settings->resend_enabled = false;
|
||||
$this->submit();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
@@ -89,10 +97,9 @@ class Email extends Component
|
||||
'settings.smtp_password' => 'nullable',
|
||||
'settings.smtp_timeout' => 'nullable',
|
||||
]);
|
||||
$this->settings->resend_enabled = false;
|
||||
$this->settings->save();
|
||||
$this->emit('success', 'Settings saved successfully.');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class Change extends Component
|
||||
try {
|
||||
$this->validate();
|
||||
$this->github_app->save();
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ class Change extends Component
|
||||
try {
|
||||
$this->github_app->delete();
|
||||
redirect()->route('source.all');
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class Create extends Component
|
||||
session(['from' => session('from') + ['source_id' => $github_app->id]]);
|
||||
}
|
||||
redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Actions extends Component
|
||||
$this->emit('success', 'Subscription cancelled successfully. Reloading in 5s.');
|
||||
$this->emit('reloadWindow', 5000);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ class Actions extends Component
|
||||
$this->emit('success', 'Subscription resumed successfully. Reloading in 5s.');
|
||||
$this->emit('reloadWindow', 5000);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,13 @@ use Stripe\Checkout\Session;
|
||||
|
||||
class PricingPlans extends Component
|
||||
{
|
||||
public bool $isTrial = false;
|
||||
public function mount() {
|
||||
$this->isTrial = !data_get(currentTeam(),'subscription.stripe_trial_already_ended');
|
||||
}
|
||||
public function subscribeStripe($type)
|
||||
{
|
||||
$team = currentTeam();
|
||||
Stripe::setApiKey(config('subscription.stripe_api_key'));
|
||||
switch ($type) {
|
||||
case 'basic-monthly':
|
||||
@@ -50,10 +55,23 @@ class PricingPlans extends Component
|
||||
'automatic_tax' => [
|
||||
'enabled' => true,
|
||||
],
|
||||
|
||||
'mode' => 'subscription',
|
||||
'success_url' => route('dashboard', ['success' => true]),
|
||||
'cancel_url' => route('subscription.index', ['cancelled' => true]),
|
||||
];
|
||||
|
||||
if (!data_get($team,'subscription.stripe_trial_already_ended')) {
|
||||
$payload['subscription_data'] = [
|
||||
'trial_period_days' => config('constants.limits.trial_period'),
|
||||
'trial_settings' => [
|
||||
'end_behavior' => [
|
||||
'missing_payment_method' => 'cancel',
|
||||
]
|
||||
],
|
||||
];
|
||||
$payload['payment_method_collection'] = 'if_required';
|
||||
}
|
||||
$customer = currentTeam()->subscription?->stripe_customer_id ?? null;
|
||||
if ($customer) {
|
||||
$payload['customer'] = $customer;
|
||||
|
||||
@@ -23,7 +23,7 @@ class SwitchTeam extends Component
|
||||
if (!$team_to_switch_to) {
|
||||
return;
|
||||
}
|
||||
session(['currentTeam' => $team_to_switch_to]);
|
||||
refreshSession($team_to_switch_to);
|
||||
return redirect(request()->header('Referer'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ class Create extends Component
|
||||
auth()->user()->teams()->attach($team, ['role' => 'admin']);
|
||||
refreshSession();
|
||||
return redirect()->route('team.index');
|
||||
} catch (\Throwable $th) {
|
||||
return general_error_handler($th, $this);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ class Form extends Component
|
||||
try {
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
} catch (\Throwable $th) {
|
||||
return general_error_handler($th, $this);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,8 +67,8 @@ class Create extends Component
|
||||
$this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||
$this->storage->save();
|
||||
return redirect()->route('team.storages.show', $this->storage->uuid);
|
||||
} catch (\Throwable $th) {
|
||||
return general_error_handler($th, $this);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ class Create extends Component
|
||||
try {
|
||||
$this->storage->testConnection();
|
||||
return $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||
} catch (\Throwable $th) {
|
||||
return general_error_handler($th, $this);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ class Form extends Component
|
||||
try {
|
||||
$this->storage->testConnection();
|
||||
return $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||
} catch (\Throwable $th) {
|
||||
return general_error_handler($th, $this);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ class Form extends Component
|
||||
try {
|
||||
$this->storage->delete();
|
||||
return redirect()->route('team.storages.all');
|
||||
} catch (\Throwable $th) {
|
||||
return general_error_handler($th, $this);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ class Form extends Component
|
||||
$this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||
$this->storage->save();
|
||||
$this->emit('success', 'Storage settings saved.');
|
||||
} catch (\Throwable $th) {
|
||||
return general_error_handler($th, $this);
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class Upgrade extends Component
|
||||
$this->showProgress = true;
|
||||
resolve(UpdateCoolify::class)(true);
|
||||
Toaster::success("Upgrading to {$this->latestVersion} version...");
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Livewire\Component;
|
||||
class Index extends Component
|
||||
{
|
||||
public string $email;
|
||||
public int $users = 0;
|
||||
public int $waitingInLine = 0;
|
||||
|
||||
protected $rules = [
|
||||
@@ -22,6 +23,7 @@ class Index extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
||||
$this->users = User::count();
|
||||
if (isDev()) {
|
||||
$this->email = 'waitlist@example.com';
|
||||
}
|
||||
@@ -50,7 +52,7 @@ class Index extends Component
|
||||
|
||||
$this->emit('success', 'Check your email to verify your email address.');
|
||||
dispatch(new SendConfirmationForWaitlistJob($this->email, $waitlist->uuid));
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class IsBoardingFlow
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
ray()->showQueries()->color('orange');
|
||||
// ray()->showQueries()->color('orange');
|
||||
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
||||
return redirect('boarding');
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ class ApplicationContainerStatusJob implements ShouldQueue, ShouldBeUnique
|
||||
$this->application->status = $status;
|
||||
$this->application->save();
|
||||
}
|
||||
} catch (\Exception $th) {
|
||||
ray($th->getMessage());
|
||||
throw $th;
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ use Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Throwable;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
use Yosymfony\Toml\Toml;
|
||||
use Yosymfony\Toml\TomlArray;
|
||||
|
||||
class ApplicationDeploymentJob implements ShouldQueue
|
||||
{
|
||||
@@ -133,7 +135,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
||||
$this->deploy();
|
||||
}
|
||||
}
|
||||
if ($this->application->fqdn) dispatch(new ProxyStartJob($this->server));
|
||||
if ($this->application->fqdn) dispatch(new ProxyContainerStatusJob($this->server));
|
||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||
} catch (Exception $e) {
|
||||
ray($e);
|
||||
@@ -498,7 +500,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
||||
$this->destination->network => [
|
||||
'external' => true,
|
||||
'name' => $this->destination->network,
|
||||
'attachable' => true,
|
||||
'attachable' => true
|
||||
]
|
||||
]
|
||||
];
|
||||
@@ -638,6 +640,10 @@ class ApplicationDeploymentJob implements ShouldQueue
|
||||
|
||||
private function generate_healthcheck_commands()
|
||||
{
|
||||
if ($this->application->dockerfile) {
|
||||
// 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';
|
||||
}
|
||||
if (!$this->application->health_check_port) {
|
||||
$this->application->health_check_port = $this->application->ports_exposes_array[0];
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue
|
||||
} else {
|
||||
$this->create_comment();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ class CheckResaleLicenseJob implements ShouldQueue
|
||||
{
|
||||
try {
|
||||
resolve(CheckResaleLicense::class)();
|
||||
} catch (\Throwable $th) {
|
||||
send_internal_notification('CheckResaleLicenseJob failed with: ' . $th->getMessage());
|
||||
ray($th);
|
||||
throw $th;
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('CheckResaleLicenseJob failed with: ' . $e->getMessage());
|
||||
ray($e);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$this->cleanup_waitlist();
|
||||
} catch (\Exception $e) {
|
||||
// $this->cleanup_waitlist();
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
|
||||
@@ -24,31 +24,27 @@ class DatabaseBackupJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public Team|null $team = null;
|
||||
public ?Team $team = null;
|
||||
public Server $server;
|
||||
public ScheduledDatabaseBackup|null $backup;
|
||||
public string $database_type;
|
||||
public ScheduledDatabaseBackup $backup;
|
||||
public StandalonePostgresql $database;
|
||||
public string $database_status;
|
||||
|
||||
public string|null $container_name = null;
|
||||
public ScheduledDatabaseBackupExecution|null $backup_log = null;
|
||||
public ?string $container_name = null;
|
||||
public ?ScheduledDatabaseBackupExecution $backup_log = null;
|
||||
public string $backup_status;
|
||||
public string|null $backup_location = null;
|
||||
public ?string $backup_location = null;
|
||||
public string $backup_dir;
|
||||
public string $backup_file;
|
||||
public int $size = 0;
|
||||
public string|null $backup_output = null;
|
||||
public S3Storage|null $s3 = null;
|
||||
public ?string $backup_output = null;
|
||||
public ?S3Storage $s3 = null;
|
||||
|
||||
public function __construct($backup)
|
||||
{
|
||||
$this->backup = $backup;
|
||||
$this->team = Team::find($backup->team_id);
|
||||
$this->database = $this->backup->database;
|
||||
$this->database_type = $this->database->type();
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->server = $this->database->destination->server;
|
||||
$this->database_status = $this->database->status;
|
||||
$this->s3 = $this->backup->s3;
|
||||
}
|
||||
|
||||
@@ -65,7 +61,7 @@ class DatabaseBackupJob implements ShouldQueue
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
if ($this->database_status !== 'running') {
|
||||
if (data_get($this->database, 'status') !== 'running') {
|
||||
ray('database not running');
|
||||
return;
|
||||
}
|
||||
@@ -84,7 +80,7 @@ class DatabaseBackupJob implements ShouldQueue
|
||||
'filename' => $this->backup_location,
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
if ($this->database_type === 'standalone-postgresql') {
|
||||
if ($this->database->type() === 'standalone-postgresql') {
|
||||
$this->backup_standalone_postgresql();
|
||||
}
|
||||
$this->calculate_size();
|
||||
@@ -94,12 +90,11 @@ class DatabaseBackupJob implements ShouldQueue
|
||||
}
|
||||
$this->save_backup_logs();
|
||||
// TODO: Notify user
|
||||
} catch (\Throwable $th) {
|
||||
ray($th->getMessage());
|
||||
send_internal_notification('DatabaseBackupJob failed with: ' . $th->getMessage());
|
||||
throw $th;
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function backup_standalone_postgresql(): void
|
||||
@@ -121,10 +116,10 @@ class DatabaseBackupJob implements ShouldQueue
|
||||
|
||||
$this->backup_status = 'success';
|
||||
$this->team->notify(new BackupSuccess($this->backup, $this->database));
|
||||
} catch (Throwable $th) {
|
||||
} catch (Throwable $e) {
|
||||
$this->backup_status = 'failed';
|
||||
$this->add_to_backup_output($th->getMessage());
|
||||
ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $th->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());
|
||||
$this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
||||
} finally {
|
||||
$this->backup_log->update([
|
||||
@@ -178,9 +173,9 @@ class DatabaseBackupJob implements ShouldQueue
|
||||
instant_remote_process($commands, $this->server);
|
||||
$this->add_to_backup_output('Uploaded to S3.');
|
||||
ray('Uploaded to S3. ' . $this->backup_location . ' to s3://' . $bucket . $this->backup_dir);
|
||||
} catch (\Throwable $th) {
|
||||
$this->add_to_backup_output($th->getMessage());
|
||||
ray($th->getMessage());
|
||||
} catch (\Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
ray($e->getMessage());
|
||||
} finally {
|
||||
$command = "docker rm -f backup-of-{$this->backup->uuid}";
|
||||
instant_remote_process([$command], $this->server);
|
||||
|
||||
@@ -47,7 +47,7 @@ class DatabaseContainerStatusJob implements ShouldQueue, ShouldBeUnique
|
||||
$this->database->status = $status;
|
||||
$this->database->save();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('DatabaseContainerStatusJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
|
||||
@@ -36,11 +36,11 @@ class DockerCleanupJob implements ShouldQueue
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ray()->showQueries()->color('orange');
|
||||
// ray()->showQueries()->color('orange');
|
||||
$servers = Server::all();
|
||||
foreach ($servers as $server) {
|
||||
if (
|
||||
!$server->settings->is_reachable && !$server->settings->is_usable
|
||||
!$server->isFunctional()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class DockerCleanupJob implements ShouldQueue
|
||||
ray('No need to clean up ' . $server->name)->color('orange');
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage())->color('orange');
|
||||
throw $e;
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ProxyCheckJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$container_name = 'coolify-proxy';
|
||||
$servers = Server::all();
|
||||
foreach ($servers as $server) {
|
||||
if (
|
||||
$server->settings->is_reachable === false || $server->settings->is_usable === false
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$status = getContainerStatus(server: $server, container_id: $container_name);
|
||||
if ($status === 'running') {
|
||||
continue;
|
||||
}
|
||||
if (data_get($server, 'proxy.type')) {
|
||||
resolve(StartProxy::class)($server);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
ray($th->getMessage());
|
||||
send_internal_notification('ProxyCheckJob failed with: ' . $th->getMessage());
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Enums\ProxyStatus;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@@ -11,7 +13,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ProxyContainerStatusJob implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
@@ -28,31 +29,51 @@ class ProxyContainerStatusJob implements ShouldQueue, ShouldBeUnique
|
||||
|
||||
public function middleware(): array
|
||||
{
|
||||
return [new WithoutOverlapping($this->server->id)];
|
||||
return [new WithoutOverlapping($this->server->uuid)];
|
||||
}
|
||||
|
||||
public function uniqueId(): int
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return $this->server->id;
|
||||
ray($this->server->uuid);
|
||||
return $this->server->uuid;
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$container = getContainerStatus(server: $this->server, all_data: true, container_id: 'coolify-proxy', throwError: false);
|
||||
$status = data_get($container, 'State.Status');
|
||||
if ($status && data_get($this->server, 'proxy.status') !== $status) {
|
||||
$this->server->proxy->status = $status;
|
||||
if ($this->server->proxy->status === 'running') {
|
||||
$traefik = $container['Config']['Labels']['org.opencontainers.image.title'];
|
||||
$version = $container['Config']['Labels']['org.opencontainers.image.version'];
|
||||
if (isset($version) && isset($traefik) && $traefik === 'Traefik' && Str::of($version)->startsWith('v2')) {
|
||||
$this->server->proxy->type = ProxyTypes::TRAEFIK_V2->value;
|
||||
}
|
||||
}
|
||||
$this->server->save();
|
||||
$proxyType = data_get($this->server, 'proxy.type');
|
||||
if ($proxyType === ProxyTypes::NONE->value) {
|
||||
return;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if (is_null($proxyType)) {
|
||||
if ($this->server->isProxyShouldRun()) {
|
||||
$this->server->proxy->type = ProxyTypes::TRAEFIK_V2->value;
|
||||
$this->server->proxy->status = ProxyStatus::EXITED->value;
|
||||
$this->server->save();
|
||||
resolve(StartProxy::class)($this->server);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$container = getContainerStatus(server: $this->server, all_data: true, container_id: 'coolify-proxy', throwError: false);
|
||||
$containerStatus = data_get($container, 'State.Status');
|
||||
$databaseContainerStatus = data_get($this->server, 'proxy.status', 'exited');
|
||||
|
||||
|
||||
if ($proxyType !== ProxyTypes::NONE->value) {
|
||||
if ($containerStatus === 'running') {
|
||||
$this->server->proxy->status = $containerStatus;
|
||||
$this->server->save();
|
||||
return;
|
||||
}
|
||||
if ((is_null($containerStatus) ||$containerStatus !== 'running' || $databaseContainerStatus !== 'running' || ($containerStatus && $databaseContainerStatus !== $containerStatus)) && $this->server->isProxyShouldRun()) {
|
||||
$this->server->proxy->status = $containerStatus;
|
||||
$this->server->save();
|
||||
resolve(StartProxy::class)($this->server);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
if ($e->getCode() === 1) {
|
||||
$this->server->proxy->status = 'exited';
|
||||
$this->server->save();
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Enums\ProxyStatus;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ProxyStartJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(protected Server $server)
|
||||
{
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$container_name = 'coolify-proxy';
|
||||
ray('Starting proxy for server: ' . $this->server->name);
|
||||
$status = getContainerStatus(server: $this->server, container_id: $container_name);
|
||||
if ($status === 'running') {
|
||||
return;
|
||||
}
|
||||
if (is_null(data_get($this->server, 'proxy.type'))) {
|
||||
$this->server->proxy->type = ProxyTypes::TRAEFIK_V2->value;
|
||||
$this->server->proxy->status = ProxyStatus::EXITED->value;
|
||||
$this->server->save();
|
||||
}
|
||||
resolve(StartProxy::class)($this->server);
|
||||
} catch (\Throwable $th) {
|
||||
send_internal_notification('ProxyStartJob failed with: ' . $th->getMessage());
|
||||
ray($th->getMessage());
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ResourceStatusJob implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $applications;
|
||||
public $postgresqls;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->applications = Application::all();
|
||||
$this->postgresqls = StandalonePostgresql::all();
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
foreach ($this->applications as $application) {
|
||||
dispatch(new ApplicationContainerStatusJob(
|
||||
application: $application,
|
||||
));
|
||||
}
|
||||
foreach ($this->postgresqls as $postgresql) {
|
||||
dispatch(new DatabaseContainerStatusJob(
|
||||
database: $postgresql,
|
||||
));
|
||||
}
|
||||
} catch (\Exception $th) {
|
||||
send_internal_notification('ResourceStatusJob failed with: ' . $th->getMessage());
|
||||
ray($th);
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,8 @@ class SendConfirmationForWaitlistJob implements ShouldQueue
|
||||
{
|
||||
try {
|
||||
$mail = new MailMessage();
|
||||
|
||||
$confirmation_url = base_url() . '/webhooks/waitlist/confirm?email=' . $this->email . '&confirmation_code=' . $this->uuid;
|
||||
$cancel_url = base_url() . '/webhooks/waitlist/cancel?email=' . $this->email . '&confirmation_code=' . $this->uuid;
|
||||
|
||||
$mail->view('emails.waitlist-confirmation',
|
||||
[
|
||||
'confirmation_url' => $confirmation_url,
|
||||
@@ -36,10 +34,10 @@ class SendConfirmationForWaitlistJob implements ShouldQueue
|
||||
]);
|
||||
$mail->subject('You are on the waitlist!');
|
||||
send_user_an_email($mail, $this->email);
|
||||
} catch (\Throwable $th) {
|
||||
send_internal_notification("SendConfirmationForWaitlistJob failed for {$mail} with error: " . $th->getMessage());
|
||||
ray($th->getMessage());
|
||||
throw $th;
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification("SendConfirmationForWaitlistJob failed for {$this->email} with error: " . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class SendMessageToTelegramJob implements ShouldQueue
|
||||
public array $buttons,
|
||||
public string $token,
|
||||
public string $chatId,
|
||||
public ?string $topicId = null,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -63,7 +64,9 @@ class SendMessageToTelegramJob implements ShouldQueue
|
||||
'chat_id' => $this->chatId,
|
||||
'text' => $this->text,
|
||||
];
|
||||
ray($payload);
|
||||
if ($this->topicId) {
|
||||
$payload['message_thread_id'] = $this->topicId;
|
||||
}
|
||||
$response = Http::post($url, $payload);
|
||||
if ($response->failed()) {
|
||||
throw new \Exception('Telegram notification failed with ' . $response->status() . ' status code.' . $response->body());
|
||||
|
||||
79
app/Jobs/ServerDetailsCheckJob.php
Normal file
79
app/Jobs/ServerDetailsCheckJob.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Str;
|
||||
|
||||
class ServerDetailsCheckJob implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $tries = 1;
|
||||
public $timeout = 120;
|
||||
|
||||
public function __construct(public Server $server)
|
||||
{
|
||||
}
|
||||
|
||||
public function middleware(): array
|
||||
{
|
||||
return [new WithoutOverlapping($this->server->uuid)];
|
||||
}
|
||||
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return $this->server->uuid;
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
ray()->clearAll();
|
||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server);
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
$applications = $this->server->applications();
|
||||
// ray($applications);
|
||||
// ray(format_docker_command_output_to_json($containers));
|
||||
foreach ($applications as $application) {
|
||||
$uuid = data_get($application, 'uuid');
|
||||
$foundContainer = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
$image = data_get($value, 'Config.Image');
|
||||
return Str::startsWith($image, $uuid);
|
||||
})->first();
|
||||
|
||||
if ($foundContainer) {
|
||||
$containerStatus = data_get($foundContainer, 'State.Status');
|
||||
$databaseStatus = data_get($application, 'status');
|
||||
ray($containerStatus, $databaseStatus);
|
||||
if ($containerStatus !== $databaseStatus) {
|
||||
// $application->update(['status' => $containerStatus]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// foreach ($containers as $container) {
|
||||
// $labels = format_docker_labels_to_json(data_get($container,'Config.Labels'));
|
||||
// $foundLabel = $labels->filter(fn ($value, $key) => Str::startsWith($key, 'coolify.applicationId'));
|
||||
// if ($foundLabel->count() > 0) {
|
||||
// $appFound = $applications->where('id', $foundLabel['coolify.applicationId'])->first();
|
||||
// if ($appFound) {
|
||||
// $containerStatus = data_get($container, 'State.Status');
|
||||
// $databaseStatus = data_get($appFound, 'status');
|
||||
// ray($containerStatus, $databaseStatus);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
} catch (\Throwable $e) {
|
||||
// send_internal_notification('ServerDetailsCheckJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,10 +33,10 @@ class SubscriptionInvoiceFailedJob implements ShouldQueue
|
||||
send_user_an_email($mail, $member->email);
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $th) {
|
||||
send_internal_notification('SubscriptionInvoiceFailedJob failed with: ' . $th->getMessage());
|
||||
ray($th->getMessage());
|
||||
throw $th;
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('SubscriptionInvoiceFailedJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
app/Jobs/SubscriptionTrialEndedJob.php
Executable file
44
app/Jobs/SubscriptionTrialEndedJob.php
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Team;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class SubscriptionTrialEndedJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
public Team $team
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$session = getStripeCustomerPortalSession($this->team);
|
||||
$mail = new MailMessage();
|
||||
$mail->subject('Action required: You trial in Coolify Cloud ended.');
|
||||
$mail->view('emails.trial-ended', [
|
||||
'stripeCustomerPortal' => $session->url,
|
||||
]);
|
||||
$this->team->members()->each(function ($member) use ($mail) {
|
||||
if ($member->isAdmin()) {
|
||||
ray('Sending trial ended email to ' . $member->email);
|
||||
send_user_an_email($mail, $member->email);
|
||||
send_internal_notification('Trial reminder email sent to ' . $member->email);
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('SubscriptionTrialEndsSoonJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
app/Jobs/SubscriptionTrialEndsSoonJob.php
Executable file
44
app/Jobs/SubscriptionTrialEndsSoonJob.php
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Team;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class SubscriptionTrialEndsSoonJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
public Team $team
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$session = getStripeCustomerPortalSession($this->team);
|
||||
$mail = new MailMessage();
|
||||
$mail->subject('You trial in Coolify Cloud ends soon.');
|
||||
$mail->view('emails.trial-ends-soon', [
|
||||
'stripeCustomerPortal' => $session->url,
|
||||
]);
|
||||
$this->team->members()->each(function ($member) use ($mail) {
|
||||
if ($member->isAdmin()) {
|
||||
ray('Sending trial ending email to ' . $member->email);
|
||||
send_user_an_email($mail, $member->email);
|
||||
send_internal_notification('Trial reminder email sent to ' . $member->email);
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('SubscriptionTrialEndsSoonJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ class Application extends BaseModel
|
||||
|
||||
public function environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false);
|
||||
return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->orderBy('key', 'asc');
|
||||
}
|
||||
|
||||
public function runtime_environment_variables(): HasMany
|
||||
@@ -127,7 +127,7 @@ class Application extends BaseModel
|
||||
|
||||
public function environment_variables_preview(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true);
|
||||
return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderBy('key', 'asc');
|
||||
}
|
||||
|
||||
public function runtime_environment_variables_preview(): HasMany
|
||||
|
||||
@@ -20,13 +20,16 @@ class EnvironmentVariable extends Model
|
||||
{
|
||||
static::created(function ($environment_variable) {
|
||||
if ($environment_variable->application_id && !$environment_variable->is_preview) {
|
||||
ModelsEnvironmentVariable::create([
|
||||
'key' => $environment_variable->key,
|
||||
'value' => $environment_variable->value,
|
||||
'is_build_time' => $environment_variable->is_build_time,
|
||||
'application_id' => $environment_variable->application_id,
|
||||
'is_preview' => true,
|
||||
]);
|
||||
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first();
|
||||
if (!$found) {
|
||||
ModelsEnvironmentVariable::create([
|
||||
'key' => $environment_variable->key,
|
||||
'value' => $environment_variable->value,
|
||||
'is_build_time' => $environment_variable->is_build_time,
|
||||
'application_id' => $environment_variable->application_id,
|
||||
'is_preview' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -42,26 +45,22 @@ class EnvironmentVariable extends Model
|
||||
private function get_environment_variables(string $environment_variable): string|null
|
||||
{
|
||||
// $team_id = currentTeam()->id;
|
||||
if (str_contains(trim($environment_variable), '{{') && str_contains(trim($environment_variable), '}}')) {
|
||||
$environment_variable = preg_replace('/\s+/', '', $environment_variable);
|
||||
$environment_variable = str_replace('{{', '', $environment_variable);
|
||||
$environment_variable = str_replace('}}', '', $environment_variable);
|
||||
if (str_starts_with($environment_variable, 'global.')) {
|
||||
$environment_variable = str_replace('global.', '', $environment_variable);
|
||||
// $environment_variable = GlobalEnvironmentVariable::where('name', $environment_variable)->where('team_id', $team_id)->first()?->value;
|
||||
return $environment_variable;
|
||||
}
|
||||
$environment_variable = trim(decrypt($environment_variable));
|
||||
if (Str::startsWith($environment_variable, '{{') && Str::endsWith($environment_variable, '}}') && Str::contains($environment_variable, 'global.')) {
|
||||
$variable = Str::after($environment_variable, 'global.');
|
||||
$variable = Str::before($variable, '}}');
|
||||
$variable = Str::of($variable)->trim()->value;
|
||||
// $environment_variable = GlobalEnvironmentVariable::where('name', $environment_variable)->where('team_id', $team_id)->first()?->value;
|
||||
ray('global env variable');
|
||||
return $environment_variable;
|
||||
}
|
||||
return decrypt($environment_variable);
|
||||
return $environment_variable;
|
||||
}
|
||||
|
||||
private function set_environment_variables(string $environment_variable): string|null
|
||||
{
|
||||
$environment_variable = trim($environment_variable);
|
||||
if (!str_contains(trim($environment_variable), '{{') && !str_contains(trim($environment_variable), '}}')) {
|
||||
return encrypt($environment_variable);
|
||||
}
|
||||
return $environment_variable;
|
||||
return encrypt($environment_variable);
|
||||
}
|
||||
|
||||
protected function key(): Attribute
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use phpseclib3\Crypt\PublicKeyLoader;
|
||||
|
||||
class PrivateKey extends BaseModel
|
||||
{
|
||||
@@ -19,6 +20,15 @@ class PrivateKey extends BaseModel
|
||||
return PrivateKey::whereTeamId(currentTeam()->id)->select($selectArray->all());
|
||||
}
|
||||
|
||||
public function publicKey()
|
||||
{
|
||||
try {
|
||||
return PublicKeyLoader::load($this->private_key)->getPublicKey()->toString('OpenSSH',['comment' => '']);
|
||||
} catch (\Throwable $e) {
|
||||
return 'Error loading private key';
|
||||
}
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
if ($this->servers()->count() === 0 && $this->applications()->count() === 0 && $this->githubApps()->count() === 0 && $this->gitlabApps()->count() === 0) {
|
||||
|
||||
@@ -30,7 +30,6 @@ class Server extends BaseModel
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
|
||||
});
|
||||
static::deleting(function ($server) {
|
||||
$server->destinations()->each(function ($destination) {
|
||||
@@ -72,7 +71,6 @@ class Server extends BaseModel
|
||||
$swarmDocker = collect($server->swarmDockers->all());
|
||||
return $standaloneDocker->concat($swarmDocker);
|
||||
}
|
||||
|
||||
public function settings()
|
||||
{
|
||||
return $this->hasOne(ServerSetting::class);
|
||||
@@ -93,7 +91,8 @@ class Server extends BaseModel
|
||||
return false;
|
||||
}
|
||||
|
||||
public function databases() {
|
||||
public function databases()
|
||||
{
|
||||
return $this->destinations()->map(function ($standaloneDocker) {
|
||||
$postgresqls = $standaloneDocker->postgresqls;
|
||||
return $postgresqls?->concat([]) ?? collect([]);
|
||||
@@ -137,4 +136,24 @@ class Server extends BaseModel
|
||||
{
|
||||
return $this->belongsTo(Team::class);
|
||||
}
|
||||
public function isProxyShouldRun()
|
||||
{
|
||||
$shouldRun = false;
|
||||
foreach ($this->applications() as $application) {
|
||||
if (data_get($application, 'fqdn')) {
|
||||
$shouldRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($this->id === 0) {
|
||||
$settings = InstanceSettings::get();
|
||||
if (data_get($settings, 'fqdn')) {
|
||||
$shouldRun = true;
|
||||
}
|
||||
}
|
||||
return $shouldRun;
|
||||
}
|
||||
public function isFunctional() {
|
||||
return $this->settings->is_reachable && $this->settings->is_usable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,6 @@ class StandaloneDocker extends BaseModel
|
||||
|
||||
public function attachedTo()
|
||||
{
|
||||
return $this->applications->count() > 0 || $this->databases->count() > 0;
|
||||
return $this->applications?->count() > 0 || $this->databases?->count() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,11 @@ class StandalonePostgresql extends BaseModel
|
||||
'is_readonly' => true
|
||||
]);
|
||||
});
|
||||
static::deleted(function ($database) {
|
||||
$database->scheduledBackups()->delete();
|
||||
$database->persistentStorages()->delete();
|
||||
instant_remote_process(['docker volume rm postgres-data-' . $database->uuid], $database->destination->server, false);
|
||||
});
|
||||
}
|
||||
|
||||
public function portsMappings(): Attribute
|
||||
|
||||
@@ -33,7 +33,7 @@ class Subscription extends Model
|
||||
}
|
||||
if (isStripe()) {
|
||||
if (!$this->stripe_plan_id) {
|
||||
return 'unknown';
|
||||
return 'zero';
|
||||
}
|
||||
$subscription = Subscription::where('id', $this->id)->first();
|
||||
if (!$subscription) {
|
||||
@@ -54,6 +54,6 @@ class Subscription extends Model
|
||||
return Str::of($stripePlanId)->after('stripe_price_id_')->before('_')->lower();
|
||||
}
|
||||
}
|
||||
return 'unknown';
|
||||
return 'zero';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
{
|
||||
return [
|
||||
"token" => data_get($this, 'telegram_token', null),
|
||||
"chat_id" => data_get($this, 'telegram_chat_id', null)
|
||||
"chat_id" => data_get($this, 'telegram_chat_id', null),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -120,4 +120,20 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
{
|
||||
return $this->hasMany(S3Storage::class);
|
||||
}
|
||||
public function trialEnded() {
|
||||
foreach ($this->servers as $server) {
|
||||
$server->settings()->update([
|
||||
'is_usable' => false,
|
||||
'is_reachable' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
public function trialEndedButSubscribed() {
|
||||
foreach ($this->servers as $server) {
|
||||
$server->settings()->update([
|
||||
'is_usable' => true,
|
||||
'is_reachable' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Models;
|
||||
|
||||
use App\Notifications\Channels\SendsEmail;
|
||||
use App\Notifications\TransactionalEmails\ResetPassword as TransactionalEmailsResetPassword;
|
||||
use Cache;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
@@ -94,7 +95,9 @@ class User extends Authenticatable implements SendsEmail
|
||||
|
||||
public function currentTeam()
|
||||
{
|
||||
return Team::find(session('currentTeam')->id);
|
||||
return Cache::remember('team:' . auth()->user()->id, 3600, function() {
|
||||
return Team::find(session('currentTeam')->id);
|
||||
});
|
||||
}
|
||||
|
||||
public function otherTeams()
|
||||
|
||||
@@ -16,6 +16,7 @@ class DeploymentFailed extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public Application $application;
|
||||
public string $deployment_uuid;
|
||||
public ?ApplicationPreview $preview = null;
|
||||
|
||||
@@ -16,6 +16,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public Application $application;
|
||||
public string $deployment_uuid;
|
||||
public ApplicationPreview|null $preview = null;
|
||||
|
||||
@@ -14,6 +14,7 @@ class StatusChanged extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public $application;
|
||||
|
||||
public string $application_name;
|
||||
|
||||
@@ -10,16 +10,30 @@ class TelegramChannel
|
||||
{
|
||||
$data = $notification->toTelegram($notifiable);
|
||||
$telegramData = $notifiable->routeNotificationForTelegram();
|
||||
|
||||
$message = data_get($data, 'message');
|
||||
$buttons = data_get($data, 'buttons', []);
|
||||
ray($message, $buttons);
|
||||
$telegramToken = data_get($telegramData, 'token');
|
||||
$chatId = data_get($telegramData, 'chat_id');
|
||||
$topicId = null;
|
||||
$topicsInstance = get_class($notification);
|
||||
|
||||
if (!$telegramToken || !$chatId || !$message) {
|
||||
throw new \Exception('Telegram token, chat id and message are required');
|
||||
switch ($topicsInstance) {
|
||||
case 'App\Notifications\StatusChange':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_status_changes_message_thread_id');
|
||||
break;
|
||||
case 'App\Notifications\Test':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_test_message_thread_id');
|
||||
break;
|
||||
case 'App\Notifications\Deployment':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_deployments_message_thread_id');
|
||||
break;
|
||||
case 'App\Notifications\DatabaseBackup':
|
||||
$topicId = data_get($notifiable, 'telegram_notifications_database_backups_message_thread_id');
|
||||
break;
|
||||
}
|
||||
dispatch(new SendMessageToTelegramJob($message, $buttons, $telegramToken, $chatId));
|
||||
if (!$telegramToken || !$chatId || !$message) {
|
||||
return;
|
||||
}
|
||||
dispatch(new SendMessageToTelegramJob($message, $buttons, $telegramToken, $chatId, $topicId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ class BackupFailed extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public string $name;
|
||||
public string $frequency;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class BackupSuccess extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public string $name;
|
||||
public string $frequency;
|
||||
|
||||
|
||||
@@ -12,13 +12,24 @@ class GeneralNotification extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 5;
|
||||
public function __construct(public string $message)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return [TelegramChannel::class, DiscordChannel::class];
|
||||
$channels = [];
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
}
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
return $channels;
|
||||
}
|
||||
|
||||
public function toDiscord(): string
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user