mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-31 05:09:24 +00:00
Compare commits
138 Commits
v4.0.0-bet
...
4.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abdb8074b1 | ||
|
|
157da798dd | ||
|
|
9c5501326e | ||
|
|
3ea462efc9 | ||
|
|
f77df5b732 | ||
|
|
b77074fe4e | ||
|
|
a7b7d3fa32 | ||
|
|
f4f3034389 | ||
|
|
4b2ffb456f | ||
|
|
e17ff99c5b | ||
|
|
1cf036bbc6 | ||
|
|
da4c2ee60f | ||
|
|
fcf7c5ddd5 | ||
|
|
27926322e1 | ||
|
|
c735ff545e | ||
|
|
b4f048b028 | ||
|
|
54a57d217f | ||
|
|
cf28490acc | ||
|
|
019670d5d1 | ||
|
|
b07cc500e7 | ||
|
|
82c235d5af | ||
|
|
307e4a6990 | ||
|
|
ddb7af63a6 | ||
|
|
7a429ee5bb | ||
|
|
bb591604ac | ||
|
|
81f7a65dd5 | ||
|
|
4b313bb1c6 | ||
|
|
aba0b2f13c | ||
|
|
9a284e47da | ||
|
|
9f2fbc661a | ||
|
|
949407368e | ||
|
|
93c65f6a79 | ||
|
|
d9fe16a3ee | ||
|
|
adaca4d4e3 | ||
|
|
a6d5f3038c | ||
|
|
4d49132821 | ||
|
|
c287276d0e | ||
|
|
e89868c692 | ||
|
|
f93317cd2e | ||
|
|
8412802f4d | ||
|
|
53c20e1e99 | ||
|
|
3c8c8e20b1 | ||
|
|
49f8abcd79 | ||
|
|
4a4d73b87b | ||
|
|
046eab3776 | ||
|
|
17c0e91a0d | ||
|
|
fe4a0ae166 | ||
|
|
52c84f8d22 | ||
|
|
b22fecb615 | ||
|
|
23b7fc3c54 | ||
|
|
1efb1235b4 | ||
|
|
0924070a13 | ||
|
|
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.
|
||||||
2
.github/workflows/development-build.yml
vendored
2
.github/workflows/development-build.yml
vendored
@@ -13,7 +13,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
amd64:
|
amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, x64]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
|
|||||||
2
.github/workflows/production-build.yml
vendored
2
.github/workflows/production-build.yml
vendored
@@ -10,7 +10,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
amd64:
|
amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, x64]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -31,3 +31,4 @@ _ide_helper.php
|
|||||||
_ide_helper_models.php
|
_ide_helper_models.php
|
||||||
.rnd
|
.rnd
|
||||||
/.ssh
|
/.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
|
# 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.
|
There are hundreds of people using it for managing their client's applications, freelancers, hobbyists, businesses.
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
@@ -26,13 +36,19 @@ You can find the installation script [here](./scripts/install.sh).
|
|||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
- Twitter: [@heyandras](https://twitter.com/heyandras)
|
Contact us [here](https://docs.coollabs.io/contact).
|
||||||
- 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)
|
|
||||||
|
|
||||||
---
|
## 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
|
## 💰 Financial Contributors
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,12 @@ namespace App\Actions\CoolifyTask;
|
|||||||
use App\Enums\ActivityTypes;
|
use App\Enums\ActivityTypes;
|
||||||
use App\Enums\ProcessStatus;
|
use App\Enums\ProcessStatus;
|
||||||
use App\Jobs\ApplicationDeploymentJob;
|
use App\Jobs\ApplicationDeploymentJob;
|
||||||
|
use App\Models\Server;
|
||||||
use Illuminate\Process\ProcessResult;
|
use Illuminate\Process\ProcessResult;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
|
||||||
const TIMEOUT = 3600;
|
|
||||||
const IDLE_TIMEOUT = 3600;
|
|
||||||
|
|
||||||
class RunRemoteProcess
|
class RunRemoteProcess
|
||||||
{
|
{
|
||||||
public Activity $activity;
|
public Activity $activity;
|
||||||
@@ -76,8 +74,7 @@ class RunRemoteProcess
|
|||||||
$this->time_start = hrtime(true);
|
$this->time_start = hrtime(true);
|
||||||
|
|
||||||
$status = ProcessStatus::IN_PROGRESS;
|
$status = ProcessStatus::IN_PROGRESS;
|
||||||
|
$processResult = Process::forever()->run($this->getCommand(), $this->handleOutput(...));
|
||||||
$processResult = Process::timeout(TIMEOUT)->idleTimeout(IDLE_TIMEOUT)->run($this->getCommand(), $this->handleOutput(...));
|
|
||||||
|
|
||||||
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
|
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
|
||||||
$status = ProcessStatus::ERROR;
|
$status = ProcessStatus::ERROR;
|
||||||
@@ -97,9 +94,8 @@ class RunRemoteProcess
|
|||||||
'status' => $status->value,
|
'status' => $status->value,
|
||||||
]);
|
]);
|
||||||
$this->activity->save();
|
$this->activity->save();
|
||||||
|
|
||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
throw new \RuntimeException($processResult->errorOutput());
|
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $processResult;
|
return $processResult;
|
||||||
@@ -107,13 +103,11 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
protected function getCommand(): string
|
protected function getCommand(): string
|
||||||
{
|
{
|
||||||
$user = $this->activity->getExtraProperty('user');
|
$server_uuid = $this->activity->getExtraProperty('server_uuid');
|
||||||
$server_ip = $this->activity->getExtraProperty('server_ip');
|
|
||||||
$private_key_location = $this->activity->getExtraProperty('private_key_location');
|
|
||||||
$port = $this->activity->getExtraProperty('port');
|
|
||||||
$command = $this->activity->getExtraProperty('command');
|
$command = $this->activity->getExtraProperty('command');
|
||||||
|
$server = Server::whereUuid($server_uuid)->firstOrFail();
|
||||||
|
|
||||||
return generate_ssh_command($private_key_location, $server_ip, $user, $port, $command);
|
return generateSshCommand($server, $command);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function handleOutput(string $type, string $output)
|
protected function handleOutput(string $type, string $output)
|
||||||
|
|||||||
@@ -57,13 +57,13 @@ class CheckResaleLicense
|
|||||||
throw new \Exception('Invalid license key.');
|
throw new \Exception('Invalid license key.');
|
||||||
}
|
}
|
||||||
throw new \Exception('Cannot activate license key.');
|
throw new \Exception('Cannot activate license key.');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
ray($th);
|
ray($e);
|
||||||
$settings->update([
|
$settings->update([
|
||||||
'resale_license' => null,
|
'resale_license' => null,
|
||||||
'is_resale_license_active' => false,
|
'is_resale_license_active' => false,
|
||||||
]);
|
]);
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,7 @@ class CheckConfigurationSync
|
|||||||
|
|
||||||
if ($reset || is_null($proxy_configuration)) {
|
if ($reset || is_null($proxy_configuration)) {
|
||||||
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
||||||
resolve(SaveConfigurationSync::class)($server, $proxy_configuration);
|
|
||||||
return $proxy_configuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $proxy_configuration;
|
return $proxy_configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,12 @@ use Illuminate\Support\Str;
|
|||||||
|
|
||||||
class SaveConfigurationSync
|
class SaveConfigurationSync
|
||||||
{
|
{
|
||||||
public function __invoke(Server $server, string $configuration)
|
public function __invoke(Server $server)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$proxy_settings = resolve(CheckConfigurationSync::class)($server, true);
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
||||||
|
|
||||||
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
$server->save();
|
$server->save();
|
||||||
@@ -20,8 +21,8 @@ class SaveConfigurationSync
|
|||||||
"mkdir -p $proxy_path",
|
"mkdir -p $proxy_path",
|
||||||
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
||||||
], $server);
|
], $server);
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
ray($th);
|
ray($e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,17 @@ use Spatie\Activitylog\Models\Activity;
|
|||||||
|
|
||||||
class StartProxy
|
class StartProxy
|
||||||
{
|
{
|
||||||
public function __invoke(Server $server): Activity
|
public function __invoke(Server $server, bool $async = true): Activity|string
|
||||||
{
|
{
|
||||||
|
$proxyType = data_get($server,'proxy.type');
|
||||||
|
if ($proxyType === 'none') {
|
||||||
|
return 'OK';
|
||||||
|
}
|
||||||
|
if (is_null($proxyType)) {
|
||||||
|
$server->proxy->type = ProxyTypes::TRAEFIK_V2->value;
|
||||||
|
$server->proxy->status = ProxyStatus::EXITED->value;
|
||||||
|
$server->save();
|
||||||
|
}
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
||||||
return $docker['network'];
|
return $docker['network'];
|
||||||
@@ -28,23 +37,37 @@ class StartProxy
|
|||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
$server->save();
|
$server->save();
|
||||||
|
$commands = [
|
||||||
$activity = remote_process([
|
"command -v lsof >/dev/null || echo '####### Installing lsof...'",
|
||||||
"echo 'Creating required Docker networks...'",
|
"command -v lsof >/dev/null || apt-get update",
|
||||||
|
"command -v lsof >/dev/null || apt install -y lsof",
|
||||||
|
"command -v lsof >/dev/null || command -v fuser >/dev/null || apt install -y psmisc",
|
||||||
|
"echo '####### Creating required Docker networks...'",
|
||||||
...$create_networks_command,
|
...$create_networks_command,
|
||||||
"cd $proxy_path",
|
"cd $proxy_path",
|
||||||
"echo 'Creating Docker Compose file...'",
|
"echo '####### Creating Docker Compose file...'",
|
||||||
"echo 'Pulling docker image...'",
|
"echo '####### Pulling docker image...'",
|
||||||
'docker compose pull -q',
|
'docker compose pull',
|
||||||
"echo 'Stopping existing proxy...'",
|
"echo '####### Stopping existing coolify-proxy...'",
|
||||||
'docker compose down -v --remove-orphans',
|
"docker compose down -v --remove-orphans > /dev/null 2>&1 || true",
|
||||||
"lsof -nt -i:80 | xargs -r kill -9",
|
"command -v fuser >/dev/null || command -v lsof >/dev/null || echo '####### Could not kill existing processes listening on port 80 & 443. Please stop the process holding these ports...'",
|
||||||
"lsof -nt -i:443 | xargs -r kill -9",
|
"command -v lsof >/dev/null && lsof -nt -i:80 | xargs -r kill -9 || true",
|
||||||
"echo 'Starting proxy...'",
|
"command -v lsof >/dev/null && lsof -nt -i:443 | xargs -r kill -9 || true",
|
||||||
|
"command -v fuser >/dev/null && fuser -k 80/tcp || true",
|
||||||
|
"command -v fuser >/dev/null && fuser -k 443/tcp || true",
|
||||||
|
"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',
|
'docker compose up -d --remove-orphans',
|
||||||
"echo 'Proxy installed successfully...'"
|
"echo '####### Proxy installed successfully...'"
|
||||||
], $server);
|
];
|
||||||
|
if (!$async) {
|
||||||
return $activity;
|
instant_remote_process($commands, $server);
|
||||||
|
return 'OK';
|
||||||
|
} else {
|
||||||
|
$activity = remote_process($commands, $server);
|
||||||
|
return $activity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,42 +18,42 @@ class InstallDocker
|
|||||||
"max-file": "3"
|
"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()) {
|
if (isDev()) {
|
||||||
$activity = remote_process([
|
return remote_process([
|
||||||
"echo ####### Installing Prerequisites...",
|
"echo '####### Installing Prerequisites...'",
|
||||||
"echo ####### Installing/updating Docker Engine...",
|
"sleep 1",
|
||||||
"echo ####### Configuring Docker Engine (merging existing configuration with the required)...",
|
"echo '####### Installing/updating Docker Engine...'",
|
||||||
"echo ####### Restarting Docker Engine...",
|
"echo '####### Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||||
|
"sleep 4",
|
||||||
|
"echo '####### Restarting Docker Engine...'",
|
||||||
|
"ls -l /tmp"
|
||||||
], $server);
|
], $server);
|
||||||
} else {
|
} else {
|
||||||
$activity = remote_process([
|
return remote_process([
|
||||||
"echo ####### Installing Prerequisites...",
|
"echo '####### Installing Prerequisites...'",
|
||||||
"command -v jq >/dev/null || apt-get update",
|
"command -v jq >/dev/null || apt-get update",
|
||||||
"command -v jq >/dev/null || apt install -y jq",
|
"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",
|
"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",
|
"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",
|
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
||||||
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /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",
|
"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",
|
"systemctl restart docker",
|
||||||
"echo ####### Creating default network...",
|
"echo '####### Creating default Docker network (coolify)...'",
|
||||||
"docker network create --attachable coolify",
|
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
||||||
"echo ####### Done!"
|
"echo '####### Done!'"
|
||||||
], $server);
|
], $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();
|
$this->update();
|
||||||
}
|
}
|
||||||
send_internal_notification('InstanceAutoUpdateJob done to version: ' . $this->latestVersion . ' from version: ' . $this->currentVersion);
|
send_internal_notification('InstanceAutoUpdateJob done to version: ' . $this->latestVersion . ' from version: ' . $this->currentVersion);
|
||||||
} catch (\Exception $th) {
|
} catch (\Throwable $e) {
|
||||||
ray('InstanceAutoUpdateJob failed');
|
ray('InstanceAutoUpdateJob failed');
|
||||||
ray($th->getMessage());
|
ray($e->getMessage());
|
||||||
send_internal_notification('InstanceAutoUpdateJob failed: ' . $th->getMessage());
|
send_internal_notification('InstanceAutoUpdateJob failed: ' . $e->getMessage());
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,16 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\SendConfirmationForWaitlistJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\Team;
|
||||||
use App\Models\TeamInvitation;
|
use App\Models\TeamInvitation;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Models\Waitlist;
|
||||||
use App\Notifications\Application\DeploymentFailed;
|
use App\Notifications\Application\DeploymentFailed;
|
||||||
use App\Notifications\Application\DeploymentSuccess;
|
use App\Notifications\Application\DeploymentSuccess;
|
||||||
use App\Notifications\Application\StatusChanged;
|
use App\Notifications\Application\StatusChanged;
|
||||||
@@ -20,33 +24,36 @@ use Illuminate\Console\Command;
|
|||||||
use Illuminate\Mail\Message;
|
use Illuminate\Mail\Message;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Mail;
|
use Mail;
|
||||||
use Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\confirm;
|
||||||
use function Laravel\Prompts\select;
|
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.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'email:test';
|
protected $signature = 'emails';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $description = 'Send a test email to the admin';
|
protected $description = 'Send out test / prod emails';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*/
|
*/
|
||||||
private ?MailMessage $mail = null;
|
private ?MailMessage $mail = null;
|
||||||
|
private ?string $email = null;
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$email = select(
|
$type = select(
|
||||||
'Which Email should be sent?',
|
'Which Email should be sent?',
|
||||||
options: [
|
options: [
|
||||||
'emails-test' => 'Test',
|
'emails-test' => 'Test',
|
||||||
@@ -55,20 +62,25 @@ class TestEmail extends Command
|
|||||||
'application-status-changed' => 'Application - Status Changed',
|
'application-status-changed' => 'Application - Status Changed',
|
||||||
'backup-success' => 'Database - Backup Success',
|
'backup-success' => 'Database - Backup Success',
|
||||||
'backup-failed' => 'Database - Backup Failed',
|
'backup-failed' => 'Database - Backup Failed',
|
||||||
'invitation-link' => 'Invitation Link',
|
// 'invitation-link' => 'Invitation Link',
|
||||||
'waitlist-invitation-link' => 'Waitlist Invitation Link',
|
'waitlist-invitation-link' => 'Waitlist Invitation Link',
|
||||||
'waitlist-confirmation' => 'Waitlist Confirmation',
|
'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();
|
$emailsGathered = ['realusers-before-trial','realusers-server-lost-connection'];
|
||||||
if (!$type) {
|
if (!in_array($type, $emailsGathered)) {
|
||||||
throw new Exception('No email settings found.');
|
$this->email = text('Email Address to send to');
|
||||||
}
|
}
|
||||||
|
set_transanctional_email_settings();
|
||||||
|
|
||||||
$this->mail = new MailMessage();
|
$this->mail = new MailMessage();
|
||||||
$this->mail->subject("Test Email");
|
$this->mail->subject("Test Email");
|
||||||
switch ($email) {
|
switch ($type) {
|
||||||
case 'emails-test':
|
case 'emails-test':
|
||||||
$this->mail = (new Test())->toMail();
|
$this->mail = (new Test())->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
break;
|
break;
|
||||||
case 'application-deployment-success':
|
case 'application-deployment-success':
|
||||||
$application = Application::all()->first();
|
$application = Application::all()->first();
|
||||||
@@ -129,56 +141,107 @@ class TestEmail extends Command
|
|||||||
$this->mail = (new BackupSuccess($backup, $db))->toMail();
|
$this->mail = (new BackupSuccess($backup, $db))->toMail();
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
break;
|
break;
|
||||||
case 'invitation-link':
|
// case 'invitation-link':
|
||||||
$user = User::all()->first();
|
// $user = User::all()->first();
|
||||||
$invitation = TeamInvitation::whereEmail($user->email)->first();
|
// $invitation = TeamInvitation::whereEmail($user->email)->first();
|
||||||
if (!$invitation) {
|
// if (!$invitation) {
|
||||||
$invitation = TeamInvitation::create([
|
// $invitation = TeamInvitation::create([
|
||||||
'uuid' => Str::uuid(),
|
// 'uuid' => Str::uuid(),
|
||||||
'email' => $user->email,
|
// 'email' => $user->email,
|
||||||
'team_id' => 1,
|
// 'team_id' => 1,
|
||||||
'link' => 'http://example.com',
|
// 'link' => 'http://example.com',
|
||||||
]);
|
// ]);
|
||||||
}
|
// }
|
||||||
$this->mail = (new InvitationLink($user))->toMail();
|
// $this->mail = (new InvitationLink($user))->toMail();
|
||||||
$this->sendEmail();
|
// $this->sendEmail();
|
||||||
break;
|
// break;
|
||||||
case 'waitlist-invitation-link':
|
case 'waitlist-invitation-link':
|
||||||
$this->mail = new MailMessage();
|
$this->mail = new MailMessage();
|
||||||
$this->mail->view('emails.waitlist-invitation', [
|
$this->mail->view('emails.waitlist-invitation', [
|
||||||
'email' => 'test2@example.com',
|
'loginLink' => 'https://coolify.io',
|
||||||
'password' => "supersecretpassword",
|
|
||||||
]);
|
]);
|
||||||
$this->mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
$this->mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
break;
|
break;
|
||||||
case 'waitlist-confirmation':
|
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 = new MailMessage();
|
||||||
$this->mail->view(
|
$this->mail->view('emails.before-trial-conversion');
|
||||||
'emails.waitlist-confirmation',
|
$this->mail->subject('Trial period has been added for all subscription plans.');
|
||||||
[
|
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
|
||||||
'confirmation_url' => 'http://example.com',
|
if (!$teams || $teams->isEmpty()) {
|
||||||
'cancel_url' => 'http://example.com',
|
echo 'No teams found.' . PHP_EOL;
|
||||||
]
|
return;
|
||||||
);
|
}
|
||||||
$this->mail->subject('You are on the waitlist!');
|
$emails = [];
|
||||||
$this->sendEmail();
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function sendEmail()
|
private function sendEmail(string $email = null)
|
||||||
{
|
{
|
||||||
|
if ($email) {
|
||||||
|
$this->email = $email;
|
||||||
|
}
|
||||||
Mail::send(
|
Mail::send(
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
fn (Message $message) => $message
|
fn (Message $message) => $message
|
||||||
->from(
|
->to($this->email)
|
||||||
'internal@example.com',
|
|
||||||
'Test Email',
|
|
||||||
)
|
|
||||||
->to('test@example.com')
|
|
||||||
->subject($this->mail->subject)
|
->subject($this->mail->subject)
|
||||||
->html((string)$this->mail->render())
|
->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->status = ApplicationDeploymentStatus::FAILED->value;
|
||||||
$deployment->save();
|
$deployment->save();
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error: {$e->getMessage()}\n";
|
echo "Error: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class SyncBunny extends Command
|
|||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
||||||
]);
|
]);
|
||||||
echo "All files uploaded & purged...\n";
|
echo "All files uploaded & purged...\n";
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
echo $e->getMessage();
|
echo $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class WaitlistInvite extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'waitlist:invite {email?} {--only-email}';
|
protected $signature = 'waitlist:invite {--people=1} {--only-email} {email?}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -33,6 +33,12 @@ class WaitlistInvite extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
$people = $this->option('people');
|
||||||
|
for ($i = 0; $i < $people; $i++) {
|
||||||
|
$this->main();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function main() {
|
||||||
if ($this->argument('email')) {
|
if ($this->argument('email')) {
|
||||||
if ($this->option('only-email')) {
|
if ($this->option('only-email')) {
|
||||||
$this->next_patient = User::whereEmail($this->argument('email'))->first();
|
$this->next_patient = User::whereEmail($this->argument('email'))->first();
|
||||||
@@ -86,13 +92,10 @@ class WaitlistInvite extends Command
|
|||||||
}
|
}
|
||||||
private function send_email()
|
private function send_email()
|
||||||
{
|
{
|
||||||
ray($this->next_patient->email, $this->password);
|
|
||||||
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
||||||
$loginLink = route('auth.link', ['token' => $token]);
|
$loginLink = route('auth.link', ['token' => $token]);
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->view('emails.waitlist-invitation', [
|
$mail->view('emails.waitlist-invitation', [
|
||||||
'email' => $this->next_patient->email,
|
|
||||||
'password' => $this->password,
|
|
||||||
'loginLink' => $loginLink,
|
'loginLink' => $loginLink,
|
||||||
]);
|
]);
|
||||||
$mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
$mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
||||||
|
|||||||
@@ -2,19 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Console;
|
namespace App\Console;
|
||||||
|
|
||||||
use App\Jobs\ApplicationContainerStatusJob;
|
|
||||||
use App\Jobs\CheckResaleLicenseJob;
|
use App\Jobs\CheckResaleLicenseJob;
|
||||||
use App\Jobs\CleanupInstanceStuffsJob;
|
use App\Jobs\CleanupInstanceStuffsJob;
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
use App\Jobs\DatabaseContainerStatusJob;
|
|
||||||
use App\Jobs\DockerCleanupJob;
|
use App\Jobs\DockerCleanupJob;
|
||||||
use App\Jobs\InstanceAutoUpdateJob;
|
use App\Jobs\InstanceAutoUpdateJob;
|
||||||
use App\Jobs\ProxyCheckJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Jobs\ResourceStatusJob;
|
|
||||||
use App\Models\Application;
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\Server;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
@@ -23,37 +19,35 @@ class Kernel extends ConsoleKernel
|
|||||||
protected function schedule(Schedule $schedule): void
|
protected function schedule(Schedule $schedule): void
|
||||||
{
|
{
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$schedule->command('horizon:snapshot')->everyMinute();
|
// $schedule->job(new ContainerStatusJob(Server::find(0)))->everyTenMinutes()->onOneServer();
|
||||||
// $schedule->job(new ResourceStatusJob)->everyMinute();
|
// $schedule->command('horizon:snapshot')->everyMinute();
|
||||||
$schedule->job(new ProxyCheckJob)->everyFiveMinutes();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute();
|
|
||||||
// $schedule->job(new CheckResaleLicenseJob)->hourly();
|
// $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);
|
||||||
} else {
|
} else {
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTenMinutes()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||||
// $schedule->job(new ResourceStatusJob)->everyMinute()->onOneServer();
|
|
||||||
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
$schedule->job(new ProxyCheckJob)->everyFiveMinutes()->onOneServer();
|
|
||||||
$schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
|
$schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
|
||||||
|
$this->instance_auto_update($schedule);
|
||||||
|
$this->check_scheduled_backups($schedule);
|
||||||
|
$this->check_resources($schedule);
|
||||||
}
|
}
|
||||||
$this->instance_auto_update($schedule);
|
|
||||||
$this->check_scheduled_backups($schedule);
|
|
||||||
$this->check_resources($schedule);
|
|
||||||
}
|
}
|
||||||
private function check_resources($schedule)
|
private function check_resources($schedule)
|
||||||
{
|
{
|
||||||
$applications = Application::all();
|
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
||||||
foreach ($applications as $application) {
|
ray($servers);
|
||||||
$schedule->job(new ApplicationContainerStatusJob($application))->everyMinute()->onOneServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
$postgresqls = StandalonePostgresql::all();
|
foreach ($servers as $server) {
|
||||||
foreach ($postgresqls as $postgresql) {
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
$schedule->job(new DatabaseContainerStatusJob($postgresql))->everyMinute()->onOneServer();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function instance_auto_update($schedule){
|
private function instance_auto_update($schedule)
|
||||||
|
{
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -74,6 +68,11 @@ class Kernel extends ConsoleKernel
|
|||||||
if (!$scheduled_backup->enabled) {
|
if (!$scheduled_backup->enabled) {
|
||||||
continue;
|
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])) {
|
if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) {
|
||||||
$scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency];
|
$scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency];
|
||||||
|
|||||||
@@ -12,11 +12,8 @@ use Spatie\LaravelData\Data;
|
|||||||
class CoolifyTaskArgs extends Data
|
class CoolifyTaskArgs extends Data
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $server_ip,
|
public string $server_uuid,
|
||||||
public string $private_key_location,
|
|
||||||
public string $command,
|
public string $command,
|
||||||
public int $port,
|
|
||||||
public string $user,
|
|
||||||
public string $type,
|
public string $type,
|
||||||
public ?string $type_uuid = null,
|
public ?string $type_uuid = null,
|
||||||
public ?Model $model = null,
|
public ?Model $model = null,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
use Sentry\Laravel\Integration;
|
use Sentry\Laravel\Integration;
|
||||||
use Sentry\State\Scope;
|
use Sentry\State\Scope;
|
||||||
@@ -25,7 +26,7 @@ class Handler extends ExceptionHandler
|
|||||||
* @var array<int, class-string<\Throwable>>
|
* @var array<int, class-string<\Throwable>>
|
||||||
*/
|
*/
|
||||||
protected $dontReport = [
|
protected $dontReport = [
|
||||||
//
|
ProcessException::class
|
||||||
];
|
];
|
||||||
/**
|
/**
|
||||||
* A list of the inputs that are never flashed to the session on validation exceptions.
|
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||||
@@ -45,13 +46,21 @@ class Handler extends ExceptionHandler
|
|||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$this->reportable(function (Throwable $e) {
|
$this->reportable(function (Throwable $e) {
|
||||||
|
if (isDev()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
$this->settings = InstanceSettings::get();
|
$this->settings = InstanceSettings::get();
|
||||||
if ($this->settings->do_not_track || isDev()) {
|
if ($this->settings->do_not_track) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app('sentry')->configureScope(
|
app('sentry')->configureScope(
|
||||||
function (Scope $scope){
|
function (Scope $scope) {
|
||||||
$scope->setUser(['id'=> config('sentry.server_name')]);
|
$scope->setUser(
|
||||||
|
[
|
||||||
|
'email' => auth()->user()->email,
|
||||||
|
'instanceAdmin' => User::find(0)->email
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
Integration::captureUnhandledException($e);
|
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
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,21 +3,17 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Project;
|
|
||||||
use App\Models\S3Storage;
|
use App\Models\S3Storage;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\TeamInvitation;
|
use App\Models\TeamInvitation;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Auth;
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Str;
|
||||||
use Throwable;
|
|
||||||
use Str;
|
|
||||||
|
|
||||||
|
|
||||||
class Controller extends BaseController
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
@@ -35,8 +31,15 @@ class Controller extends BaseController
|
|||||||
return redirect()->route('login');
|
return redirect()->route('login');
|
||||||
}
|
}
|
||||||
if (Hash::check($password, $user->password)) {
|
if (Hash::check($password, $user->password)) {
|
||||||
|
$invitation = TeamInvitation::whereEmail($email);
|
||||||
|
if ($invitation->exists()) {
|
||||||
|
$team = $invitation->first()->team;
|
||||||
|
$user->teams()->attach($team->id, ['role' => $invitation->first()->role]);
|
||||||
|
$invitation->delete();
|
||||||
|
} else {
|
||||||
|
$team = $user->teams()->first();
|
||||||
|
}
|
||||||
Auth::login($user);
|
Auth::login($user);
|
||||||
$team = $user->teams()->first();
|
|
||||||
session(['currentTeam' => $team]);
|
session(['currentTeam' => $team]);
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
@@ -137,25 +140,21 @@ class Controller extends BaseController
|
|||||||
try {
|
try {
|
||||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
||||||
$user = User::whereEmail($invitation->email)->firstOrFail();
|
$user = User::whereEmail($invitation->email)->firstOrFail();
|
||||||
if (is_null(auth()->user())) {
|
|
||||||
return redirect()->route('login');
|
|
||||||
}
|
|
||||||
if (auth()->user()->id !== $user->id) {
|
if (auth()->user()->id !== $user->id) {
|
||||||
abort(401);
|
abort(401);
|
||||||
}
|
}
|
||||||
|
$invitationValid = $invitation->isValid();
|
||||||
$createdAt = $invitation->created_at;
|
if ($invitationValid) {
|
||||||
$diff = $createdAt->diffInMinutes(now());
|
|
||||||
if ($diff <= config('constants.invitation.link.expiration')) {
|
|
||||||
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
||||||
|
refreshSession($invitation->team);
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
} else {
|
} else {
|
||||||
$invitation->delete();
|
|
||||||
abort(401);
|
abort(401);
|
||||||
}
|
}
|
||||||
} catch (Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
throw $th;
|
ray($e->getMessage());
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,8 +171,8 @@ class Controller extends BaseController
|
|||||||
}
|
}
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
} catch (Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ use App\Actions\Server\InstallDocker;
|
|||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\Team;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
@@ -52,12 +54,14 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->remoteServerHost = 'coolify-testing-host';
|
$this->remoteServerHost = 'coolify-testing-host';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function welcome() {
|
public function explanation()
|
||||||
|
{
|
||||||
if (isCloud()) {
|
if (isCloud()) {
|
||||||
return $this->setServerType('remote');
|
return $this->setServerType('remote');
|
||||||
}
|
}
|
||||||
$this->currentState = 'select-server-type';
|
$this->currentState = 'select-server-type';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function restartBoarding()
|
public function restartBoarding()
|
||||||
{
|
{
|
||||||
if ($this->createdServer) {
|
if ($this->createdServer) {
|
||||||
@@ -70,9 +74,10 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
}
|
}
|
||||||
public function skipBoarding()
|
public function skipBoarding()
|
||||||
{
|
{
|
||||||
currentTeam()->update([
|
Team::find(currentTeam()->id)->update([
|
||||||
'show_boarding' => false
|
'show_boarding' => false
|
||||||
]);
|
]);
|
||||||
|
ray(currentTeam());
|
||||||
refreshSession();
|
refreshSession();
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
@@ -84,7 +89,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
if (!$this->createdServer) {
|
if (!$this->createdServer) {
|
||||||
return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
|
return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
|
||||||
}
|
}
|
||||||
return $this->validateServer();
|
return $this->validateServer('localhost');
|
||||||
} elseif ($type === 'remote') {
|
} elseif ($type === 'remote') {
|
||||||
$this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get();
|
$this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get();
|
||||||
if ($this->privateKeys->count() > 0) {
|
if ($this->privateKeys->count() > 0) {
|
||||||
@@ -112,7 +117,8 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->getProxyType();
|
$this->getProxyType();
|
||||||
$this->getProjects();
|
$this->getProjects();
|
||||||
}
|
}
|
||||||
public function getProxyType() {
|
public function getProxyType()
|
||||||
|
{
|
||||||
$proxyTypeSet = $this->createdServer->proxy->type;
|
$proxyTypeSet = $this->createdServer->proxy->type;
|
||||||
if (!$proxyTypeSet) {
|
if (!$proxyTypeSet) {
|
||||||
$this->currentState = 'select-proxy';
|
$this->currentState = 'select-proxy';
|
||||||
@@ -133,7 +139,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
{
|
{
|
||||||
$this->selectedExistingPrivateKey = null;
|
$this->selectedExistingPrivateKey = null;
|
||||||
$this->privateKeyType = $type;
|
$this->privateKeyType = $type;
|
||||||
if ($type === 'create' && !isDev()) {
|
if ($type === 'create') {
|
||||||
$this->createNewPrivateKey();
|
$this->createNewPrivateKey();
|
||||||
}
|
}
|
||||||
$this->currentState = 'create-private-key';
|
$this->currentState = 'create-private-key';
|
||||||
@@ -150,49 +156,74 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
{
|
{
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'remoteServerName' => 'required',
|
'remoteServerName' => 'required',
|
||||||
'remoteServerHost' => 'required',
|
'remoteServerHost' => 'required|ip',
|
||||||
'remoteServerPort' => 'required',
|
'remoteServerPort' => 'required|integer',
|
||||||
'remoteServerUser' => 'required',
|
'remoteServerUser' => 'required',
|
||||||
]);
|
]);
|
||||||
$this->privateKey = formatPrivateKey($this->privateKey);
|
$this->privateKey = formatPrivateKey($this->privateKey);
|
||||||
$this->createdPrivateKey = PrivateKey::create([
|
$this->createdPrivateKey = new PrivateKey();
|
||||||
'name' => $this->privateKeyName,
|
$this->createdPrivateKey->private_key = $this->privateKey;
|
||||||
'description' => $this->privateKeyDescription,
|
$this->createdPrivateKey->name = $this->privateKeyName;
|
||||||
'private_key' => $this->privateKey,
|
$this->createdPrivateKey->description = $this->privateKeyDescription;
|
||||||
'team_id' => currentTeam()->id
|
$this->createdPrivateKey->team_id = currentTeam()->id;
|
||||||
]);
|
$foundServer = Server::whereIp($this->remoteServerHost)->first();
|
||||||
$this->createdServer = Server::create([
|
if ($foundServer) {
|
||||||
'name' => $this->remoteServerName,
|
return $this->emit('error', 'IP address is already in use by another team.');
|
||||||
'ip' => $this->remoteServerHost,
|
}
|
||||||
'port' => $this->remoteServerPort,
|
$this->createdServer = new Server();
|
||||||
'user' => $this->remoteServerUser,
|
$this->createdServer->uuid = (string)new Cuid2(7);
|
||||||
'description' => $this->remoteServerDescription,
|
$this->createdServer->name = $this->remoteServerName;
|
||||||
'private_key_id' => $this->createdPrivateKey->id,
|
$this->createdServer->ip = $this->remoteServerHost;
|
||||||
'team_id' => currentTeam()->id
|
$this->createdServer->port = $this->remoteServerPort;
|
||||||
]);
|
$this->createdServer->user = $this->remoteServerUser;
|
||||||
|
$this->createdServer->description = $this->remoteServerDescription;
|
||||||
|
$this->createdServer->privateKey = $this->createdPrivateKey;
|
||||||
|
$this->createdServer->team_id = currentTeam()->id;
|
||||||
|
|
||||||
|
ray($this->createdServer);
|
||||||
|
|
||||||
|
|
||||||
$this->validateServer();
|
$this->validateServer();
|
||||||
}
|
}
|
||||||
public function validateServer() {
|
public function validateServer(?string $type = null)
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->createdServer);
|
$customErrorMessage = "Server is not reachable:";
|
||||||
if (!$uptime) {
|
config()->set('coolify.mux_enabled', false);
|
||||||
throw new \Exception('Server is not reachable.');
|
instant_remote_process(['uptime'], $this->createdServer, true);
|
||||||
|
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true);
|
||||||
|
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||||
|
if (is_null($dockerVersion)) {
|
||||||
|
throw new \Exception('No Docker Engine or older than 23 version installed.');
|
||||||
|
}
|
||||||
|
$customErrorMessage = "Cannot create Server or Private Key. Please try again.";
|
||||||
|
if ($type !== 'localhost') {
|
||||||
|
$createdPrivateKey = PrivateKey::create([
|
||||||
|
'name' => $this->privateKeyName,
|
||||||
|
'description' => $this->privateKeyDescription,
|
||||||
|
'private_key' => $this->privateKey,
|
||||||
|
'team_id' => currentTeam()->id
|
||||||
|
]);
|
||||||
|
$server = Server::create([
|
||||||
|
'name' => $this->remoteServerName,
|
||||||
|
'ip' => $this->remoteServerHost,
|
||||||
|
'port' => $this->remoteServerPort,
|
||||||
|
'user' => $this->remoteServerUser,
|
||||||
|
'description' => $this->remoteServerDescription,
|
||||||
|
'private_key_id' => $createdPrivateKey->id,
|
||||||
|
'team_id' => currentTeam()->id,
|
||||||
|
]);
|
||||||
|
$server->settings->is_reachable = true;
|
||||||
|
$server->settings->is_usable = true;
|
||||||
|
$server->settings->save();
|
||||||
} else {
|
} else {
|
||||||
$this->createdServer->settings->update([
|
$this->createdServer->settings->update([
|
||||||
'is_reachable' => true,
|
'is_reachable' => true,
|
||||||
]);
|
]);
|
||||||
$this->emit('success', 'Server is reachable.');
|
|
||||||
}
|
|
||||||
ray($dockerVersion, $uptime);
|
|
||||||
if (!$dockerVersion) {
|
|
||||||
$this->emit('error', 'Docker is not installed on the server.');
|
|
||||||
$this->currentState = 'install-docker';
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
$this->getProxyType();
|
$this->getProxyType();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
} catch (\Exception $e) {
|
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
|
||||||
return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function installDocker()
|
public function installDocker()
|
||||||
@@ -212,14 +243,16 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->getProjects();
|
$this->getProjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getProjects() {
|
public function getProjects()
|
||||||
|
{
|
||||||
$this->projects = Project::ownedByCurrentTeam(['name'])->get();
|
$this->projects = Project::ownedByCurrentTeam(['name'])->get();
|
||||||
if ($this->projects->count() > 0) {
|
if ($this->projects->count() > 0) {
|
||||||
$this->selectedExistingProject = $this->projects->first()->id;
|
$this->selectedExistingProject = $this->projects->first()->id;
|
||||||
}
|
}
|
||||||
$this->currentState = 'create-project';
|
$this->currentState = 'create-project';
|
||||||
}
|
}
|
||||||
public function selectExistingProject() {
|
public function selectExistingProject()
|
||||||
|
{
|
||||||
$this->createdProject = Project::find($this->selectedExistingProject);
|
$this->createdProject = Project::find($this->selectedExistingProject);
|
||||||
$this->currentState = 'create-resource';
|
$this->currentState = 'create-resource';
|
||||||
}
|
}
|
||||||
@@ -239,7 +272,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
[
|
[
|
||||||
'project_uuid' => $this->createdProject->uuid,
|
'project_uuid' => $this->createdProject->uuid,
|
||||||
'environment_name' => 'production',
|
'environment_name' => 'production',
|
||||||
'server'=> $this->createdServer->id,
|
'server' => $this->createdServer->id,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ class CheckLicense extends Component
|
|||||||
try {
|
try {
|
||||||
resolve(CheckResaleLicense::class)();
|
resolve(CheckResaleLicense::class)();
|
||||||
$this->emit('reloadWindow');
|
$this->emit('reloadWindow');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $th->getMessage());
|
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
||||||
ray($th->getMessage());
|
ray($e->getMessage());
|
||||||
return redirect()->to('/settings/license');
|
return redirect()->to('/settings/license');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ class Form extends Component
|
|||||||
}
|
}
|
||||||
$this->destination->delete();
|
$this->destination->delete();
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ class StandaloneDocker extends Component
|
|||||||
}
|
}
|
||||||
$this->createNetworkAndAttachToProxy();
|
$this->createNetworkAndAttachToProxy();
|
||||||
return redirect()->route('destination.show', $docker->uuid);
|
return redirect()->route('destination.show', $docker->uuid);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ class ForcePasswordReset extends Component
|
|||||||
send_internal_notification('First login for ' . auth()->user()->email);
|
send_internal_notification('First login for ' . auth()->user()->email);
|
||||||
}
|
}
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Help extends Component
|
|||||||
];
|
];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->path = Route::current()->uri();
|
$this->path = Route::current()?->uri() ?? null;
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$this->description = "I'm having trouble with {$this->path}";
|
$this->description = "I'm having trouble with {$this->path}";
|
||||||
$this->subject = "Help with {$this->path}";
|
$this->subject = "Help with {$this->path}";
|
||||||
@@ -30,7 +30,7 @@ class Help extends Component
|
|||||||
try {
|
try {
|
||||||
$this->rateLimit(1, 60);
|
$this->rateLimit(1, 60);
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$subscriptionType = auth()->user()?->subscription?->type() ?? 'unknown';
|
$subscriptionType = auth()->user()?->subscription?->type() ?? 'Free';
|
||||||
$debug = "Route: {$this->path}";
|
$debug = "Route: {$this->path}";
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->view(
|
$mail->view(
|
||||||
@@ -41,10 +41,10 @@ class Help extends Component
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
||||||
send_user_an_email($mail, 'hi@coollabs.io');
|
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
||||||
$this->emit('success', 'Your message has been sent successfully. We will get in touch with you as soon as possible.');
|
$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);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class DiscordSettings extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->submit();
|
$this->submit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
$this->team->discord_enabled = false;
|
$this->team->discord_enabled = false;
|
||||||
$this->validate();
|
$this->validate();
|
||||||
@@ -46,9 +46,7 @@ class DiscordSettings extends Component
|
|||||||
public function saveModel()
|
public function saveModel()
|
||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
if (is_a($this->team, Team::class)) {
|
refreshSession();
|
||||||
refreshSession();
|
|
||||||
}
|
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->emit('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,9 +62,10 @@ class EmailSettings extends Component
|
|||||||
'team.smtp_from_name' => 'required',
|
'team.smtp_from_name' => 'required',
|
||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
@@ -81,9 +82,10 @@ class EmailSettings extends Component
|
|||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,9 +94,9 @@ class EmailSettings extends Component
|
|||||||
try {
|
try {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
$this->submitResend();
|
$this->submitResend();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
@@ -102,12 +104,17 @@ class EmailSettings extends Component
|
|||||||
try {
|
try {
|
||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
$this->submit();
|
$this->submit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function saveModel()
|
||||||
|
{
|
||||||
|
$this->team->save();
|
||||||
|
refreshSession();
|
||||||
|
$this->emit('success', 'Settings saved.');
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -123,10 +130,11 @@ class EmailSettings extends Component
|
|||||||
'team.smtp_timeout' => 'nullable',
|
'team.smtp_timeout' => 'nullable',
|
||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function submitResend()
|
public function submitResend()
|
||||||
@@ -134,14 +142,16 @@ class EmailSettings extends Component
|
|||||||
try {
|
try {
|
||||||
$this->resetErrorBag();
|
$this->resetErrorBag();
|
||||||
$this->validate([
|
$this->validate([
|
||||||
|
'team.smtp_from_address' => 'required|email',
|
||||||
|
'team.smtp_from_name' => 'required',
|
||||||
'team.resend_api_key' => 'required'
|
'team.resend_api_key' => 'required'
|
||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function copyFromInstanceSettings()
|
public function copyFromInstanceSettings()
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ class TelegramSettings extends Component
|
|||||||
'team.telegram_notifications_deployments' => 'nullable|boolean',
|
'team.telegram_notifications_deployments' => 'nullable|boolean',
|
||||||
'team.telegram_notifications_status_changes' => 'nullable|boolean',
|
'team.telegram_notifications_status_changes' => 'nullable|boolean',
|
||||||
'team.telegram_notifications_database_backups' => '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 = [
|
protected $validationAttributes = [
|
||||||
'team.telegram_token' => 'Token',
|
'team.telegram_token' => 'Token',
|
||||||
@@ -31,7 +35,7 @@ class TelegramSettings extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->submit();
|
$this->submit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
$this->team->telegram_enabled = false;
|
$this->team->telegram_enabled = false;
|
||||||
$this->validate();
|
$this->validate();
|
||||||
@@ -48,9 +52,7 @@ class TelegramSettings extends Component
|
|||||||
public function saveModel()
|
public function saveModel()
|
||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
if (is_a($this->team, Team::class)) {
|
refreshSession();
|
||||||
refreshSession();
|
|
||||||
}
|
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->emit('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use Livewire\Component;
|
|||||||
class Change extends Component
|
class Change extends Component
|
||||||
{
|
{
|
||||||
public PrivateKey $private_key;
|
public PrivateKey $private_key;
|
||||||
|
public $public_key;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'private_key.name' => 'required|string',
|
'private_key.name' => 'required|string',
|
||||||
'private_key.description' => 'nullable|string',
|
'private_key.description' => 'nullable|string',
|
||||||
@@ -21,6 +21,14 @@ class Change extends Component
|
|||||||
'private_key.private_key' => 'private key'
|
'private_key.private_key' => 'private key'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->public_key = $this->private_key->publicKey();
|
||||||
|
}catch(\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -30,8 +38,8 @@ class Change extends Component
|
|||||||
return redirect()->route('security.private-key.index');
|
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.');
|
$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);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,8 +49,8 @@ class Change extends Component
|
|||||||
$this->private_key->private_key = formatPrivateKey($this->private_key->private_key);
|
$this->private_key->private_key = formatPrivateKey($this->private_key->private_key);
|
||||||
$this->private_key->save();
|
$this->private_key->save();
|
||||||
refresh_server_connection($this->private_key);
|
refresh_server_connection($this->private_key);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,20 @@
|
|||||||
namespace App\Http\Livewire\PrivateKey;
|
namespace App\Http\Livewire\PrivateKey;
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use phpseclib3\Crypt\PublicKeyLoader;
|
||||||
|
|
||||||
class Create extends Component
|
class Create extends Component
|
||||||
{
|
{
|
||||||
public string|null $from = null;
|
use WithRateLimiting;
|
||||||
public string $name;
|
public string $name;
|
||||||
public string|null $description = null;
|
|
||||||
public string $value;
|
public string $value;
|
||||||
|
|
||||||
|
public ?string $from = null;
|
||||||
|
public ?string $description = null;
|
||||||
|
public ?string $publicKey = null;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'value' => 'required|string',
|
'value' => 'required|string',
|
||||||
@@ -20,6 +26,32 @@ class Create extends Component
|
|||||||
'value' => 'private Key',
|
'value' => 'private Key',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function generateNewKey()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->rateLimit(10);
|
||||||
|
$this->name = generate_random_name();
|
||||||
|
$this->description = 'Created by Coolify';
|
||||||
|
['private' => $this->value, 'public' => $this->publicKey] = generateSSHKey();
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function updated($updateProperty)
|
||||||
|
{
|
||||||
|
if ($updateProperty === 'value') {
|
||||||
|
try {
|
||||||
|
$this->publicKey = PublicKeyLoader::load($this->$updateProperty)->getPublicKey()->toString('OpenSSH',['comment' => '']);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
if ($this->$updateProperty === "") {
|
||||||
|
$this->publicKey = "";
|
||||||
|
} else {
|
||||||
|
$this->publicKey = "Invalid private key";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->validateOnly($updateProperty);
|
||||||
|
}
|
||||||
public function createPrivateKey()
|
public function createPrivateKey()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
@@ -37,9 +69,9 @@ class Create extends Component
|
|||||||
if ($this->from === 'server') {
|
if ($this->from === 'server') {
|
||||||
return redirect()->route('server.create');
|
return redirect()->route('server.create');
|
||||||
}
|
}
|
||||||
return redirect()->route('private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class Form extends Component
|
|||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
]);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ class AddEmpty extends Component
|
|||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
return redirect()->route('project.show', $project->uuid);
|
return redirect()->route('project.show', $project->uuid);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->name = '';
|
$this->name = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class AddEnvironment extends Component
|
|||||||
'project_uuid' => $this->project->uuid,
|
'project_uuid' => $this->project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
general_error_handler($e, $this);
|
handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->name = '';
|
$this->name = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class DeploymentNavbar extends Component
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,8 +159,8 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Http\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Jobs\ApplicationContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Notifications\Application\StatusChanged;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -22,10 +21,11 @@ class Heading extends Component
|
|||||||
|
|
||||||
public function check_status()
|
public function check_status()
|
||||||
{
|
{
|
||||||
dispatch_sync(new ApplicationContainerStatusJob(
|
dispatch_sync(new ContainerStatusJob($this->application->destination->server));
|
||||||
application: $this->application,
|
|
||||||
));
|
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
|
$this->application->previews->each(function ($preview) {
|
||||||
|
$preview->refresh();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function force_deploy_without_cache()
|
public function force_deploy_without_cache()
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Http\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Jobs\ApplicationContainerStatusJob;
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -23,14 +22,6 @@ class Previews extends Component
|
|||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadStatus($pull_request_id)
|
|
||||||
{
|
|
||||||
dispatch(new ApplicationContainerStatusJob(
|
|
||||||
application: $this->application,
|
|
||||||
pullRequestId: $pull_request_id
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function load_prs()
|
public function load_prs()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -39,7 +30,7 @@ class Previews extends Component
|
|||||||
$this->pull_requests = $data->sortBy('number')->values();
|
$this->pull_requests = $data->sortBy('number')->values();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->rate_limit_remaining = 0;
|
$this->rate_limit_remaining = 0;
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +59,7 @@ class Previews extends Component
|
|||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
]);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +79,7 @@ class Previews extends Component
|
|||||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->delete();
|
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->delete();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class Rollback extends Component
|
|||||||
];
|
];
|
||||||
})->toArray();
|
})->toArray();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class BackupEdit extends Component
|
|||||||
$this->backup->save();
|
$this->backup->save();
|
||||||
$this->backup->refresh();
|
$this->backup->refresh();
|
||||||
$this->emit('success', 'Backup updated successfully');
|
$this->emit('success', 'Backup updated successfully');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->emit('error', $e->getMessage());
|
$this->emit('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ class BackupEdit extends Component
|
|||||||
$this->backup->save();
|
$this->backup->save();
|
||||||
$this->backup->refresh();
|
$this->backup->refresh();
|
||||||
$this->emit('success', 'Backup updated successfully');
|
$this->emit('success', 'Backup updated successfully');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->emit('error', $e->getMessage());
|
$this->emit('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ class CreateScheduledBackup extends Component
|
|||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
$this->emit('refreshScheduledBackups');
|
$this->emit('refreshScheduledBackups');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
general_error_handler($e, $this);
|
handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->frequency = '';
|
$this->frequency = '';
|
||||||
$this->save_s3 = true;
|
$this->save_s3 = true;
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Http\Livewire\Project\Database;
|
||||||
|
|
||||||
use App\Actions\Database\StartPostgresql;
|
use App\Actions\Database\StartPostgresql;
|
||||||
use App\Jobs\DatabaseContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Notifications\Application\StatusChanged;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Heading extends Component
|
class Heading extends Component
|
||||||
@@ -25,9 +24,7 @@ class Heading extends Component
|
|||||||
|
|
||||||
public function check_status()
|
public function check_status()
|
||||||
{
|
{
|
||||||
dispatch_sync(new DatabaseContainerStatusJob(
|
dispatch_sync(new ContainerStatusJob($this->database->destination->server));
|
||||||
database: $this->database,
|
|
||||||
));
|
|
||||||
$this->database->refresh();
|
$this->database->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,12 +35,17 @@ class Heading extends Component
|
|||||||
|
|
||||||
public function stop()
|
public function stop()
|
||||||
{
|
{
|
||||||
remote_process(
|
instant_remote_process(
|
||||||
["docker rm -f {$this->database->uuid}"],
|
["docker rm -f {$this->database->uuid}"],
|
||||||
$this->database->destination->server
|
$this->database->destination->server
|
||||||
);
|
);
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
stopPostgresProxy($this->database);
|
||||||
|
$this->database->is_public = false;
|
||||||
|
}
|
||||||
$this->database->status = 'stopped';
|
$this->database->status = 'stopped';
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
|
$this->check_status();
|
||||||
// $this->database->environment->project->team->notify(new StatusChanged($this->database));
|
// $this->database->environment->project->team->notify(new StatusChanged($this->database));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class InitScript extends Component
|
|||||||
$this->script['filename'] = $this->filename;
|
$this->script['filename'] = $this->filename;
|
||||||
$this->emitUp('save_init_script', $this->script);
|
$this->emitUp('save_init_script', $this->script);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Http\Livewire\Project\Database\Postgresql;
|
|||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
use function Aws\filter;
|
use function Aws\filter;
|
||||||
|
|
||||||
class General extends Component
|
class General extends Component
|
||||||
@@ -12,6 +13,7 @@ class General extends Component
|
|||||||
public StandalonePostgresql $database;
|
public StandalonePostgresql $database;
|
||||||
public string $new_filename;
|
public string $new_filename;
|
||||||
public string $new_content;
|
public string $new_content;
|
||||||
|
public string $db_url;
|
||||||
|
|
||||||
protected $listeners = ['refresh', 'save_init_script', 'delete_init_script'];
|
protected $listeners = ['refresh', 'save_init_script', 'delete_init_script'];
|
||||||
|
|
||||||
@@ -26,6 +28,8 @@ class General extends Component
|
|||||||
'database.init_scripts' => 'nullable',
|
'database.init_scripts' => 'nullable',
|
||||||
'database.image' => 'required',
|
'database.image' => 'required',
|
||||||
'database.ports_mappings' => 'nullable',
|
'database.ports_mappings' => 'nullable',
|
||||||
|
'database.is_public' => 'nullable|boolean',
|
||||||
|
'database.public_port' => 'nullable|integer',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'database.name' => 'Name',
|
'database.name' => 'Name',
|
||||||
@@ -38,8 +42,44 @@ class General extends Component
|
|||||||
'database.init_scripts' => 'Init Scripts',
|
'database.init_scripts' => 'Init Scripts',
|
||||||
'database.image' => 'Image',
|
'database.image' => 'Image',
|
||||||
'database.ports_mappings' => 'Port Mapping',
|
'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(\Throwable $e) {
|
||||||
|
$this->database->is_public = !$this->database->is_public;
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
public function save_init_script($script)
|
public function save_init_script($script)
|
||||||
{
|
{
|
||||||
$this->database->init_scripts = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']);
|
$this->database->init_scripts = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']);
|
||||||
@@ -101,7 +141,7 @@ class General extends Component
|
|||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->emit('success', 'Database updated successfully.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ class Edit extends Component
|
|||||||
try {
|
try {
|
||||||
$this->project->save();
|
$this->project->save();
|
||||||
$this->emit('saved');
|
$this->emit('saved');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,8 +164,8 @@ class GithubPrivateRepository extends Component
|
|||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,8 +117,8 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,15 +75,15 @@ class PublicGitRepository extends Component
|
|||||||
$this->get_git_source();
|
$this->get_git_source();
|
||||||
$this->get_branch();
|
$this->get_branch();
|
||||||
$this->selected_branch = $this->git_branch;
|
$this->selected_branch = $this->git_branch;
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
if (!$this->branch_found && $this->git_branch == 'main') {
|
if (!$this->branch_found && $this->git_branch == 'main') {
|
||||||
try {
|
try {
|
||||||
$this->git_branch = 'master';
|
$this->git_branch = 'master';
|
||||||
$this->get_branch();
|
$this->get_branch();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,8 +161,8 @@ class PublicGitRepository extends Component
|
|||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ class Select extends Component
|
|||||||
// try {
|
// try {
|
||||||
// instantCommand("psql {$this->existingPostgresqlUrl} -c 'SELECT 1'");
|
// instantCommand("psql {$this->existingPostgresqlUrl} -c 'SELECT 1'");
|
||||||
// $this->emit('success', 'Successfully connected to the database.');
|
// $this->emit('success', 'Successfully connected to the database.');
|
||||||
// } catch (\Exception $e) {
|
// } catch (\Throwable $e) {
|
||||||
// return general_error_handler($e, $this);
|
// return handleError($e, $this);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
public function setType(string $type)
|
public function setType(string $type)
|
||||||
|
|||||||
@@ -5,21 +5,89 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
|
|||||||
use App\Models\EnvironmentVariable;
|
use App\Models\EnvironmentVariable;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class All extends Component
|
class All extends Component
|
||||||
{
|
{
|
||||||
public $resource;
|
public $resource;
|
||||||
|
public bool $showPreview = false;
|
||||||
public string|null $modalId = null;
|
public string|null $modalId = null;
|
||||||
|
public ?string $variables = null;
|
||||||
|
public ?string $variablesPreview = null;
|
||||||
|
public string $view = 'normal';
|
||||||
protected $listeners = ['refreshEnvs', 'submit'];
|
protected $listeners = ['refreshEnvs', 'submit'];
|
||||||
|
|
||||||
public function mount()
|
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->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()
|
public function refreshEnvs()
|
||||||
{
|
{
|
||||||
$this->resource->refresh();
|
$this->resource->refresh();
|
||||||
|
$this->getDevView();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit($data)
|
public function submit($data)
|
||||||
@@ -43,10 +111,10 @@ class All extends Component
|
|||||||
$environment->standalone_postgresql_id = $this->resource->id;
|
$environment->standalone_postgresql_id = $this->resource->id;
|
||||||
}
|
}
|
||||||
$environment->save();
|
$environment->save();
|
||||||
$this->resource->refresh();
|
$this->refreshEnvs();
|
||||||
$this->emit('success', 'Environment variable added successfully.');
|
$this->emit('success', 'Environment variable added successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ class ResourceLimits extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
$this->resource->save();
|
$this->resource->save();
|
||||||
$this->emit('success', 'Resource limits updated successfully.');
|
$this->emit('success', 'Resource limits updated successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ class All extends Component
|
|||||||
$this->resource->refresh();
|
$this->resource->refresh();
|
||||||
$this->emit('success', 'Storage added successfully');
|
$this->emit('success', 'Storage added successfully');
|
||||||
$this->emit('clearAddStorage');
|
$this->emit('clearAddStorage');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ class RunCommand extends Component
|
|||||||
try {
|
try {
|
||||||
$activity = remote_process([$this->command], Server::where('uuid', $this->server)->first(), ignore_errors: true);
|
$activity = remote_process([$this->command], Server::where('uuid', $this->server)->first(), ignore_errors: true);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,16 +51,23 @@ class Form extends Component
|
|||||||
public function validateServer()
|
public function validateServer()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server);
|
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true);
|
||||||
if ($uptime) {
|
if ($uptime) {
|
||||||
$this->uptime = $uptime;
|
$this->uptime = $uptime;
|
||||||
|
$this->emit('success', 'Server is reachable.');
|
||||||
|
} else {
|
||||||
|
$this->emit('error', 'Server is not reachable.');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if ($dockerVersion) {
|
if ($dockerVersion) {
|
||||||
$this->dockerVersion = $dockerVersion;
|
$this->dockerVersion = $dockerVersion;
|
||||||
$this->emit('proxyStatusUpdated');
|
$this->emit('proxyStatusUpdated');
|
||||||
|
$this->emit('success', 'Docker Engine 23+ is installed!');
|
||||||
|
} else {
|
||||||
|
$this->emit('error', 'No Docker Engine or older than 23 version installed.');
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this);
|
return handleError($e, $this, customErrorMessage: "Server is not reachable: ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,26 +81,20 @@ class Form extends Component
|
|||||||
}
|
}
|
||||||
$this->server->delete();
|
$this->server->delete();
|
||||||
return redirect()->route('server.all');
|
return redirect()->route('server.all');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
// $validation = Validator::make($this->server->toArray(), [
|
$uniqueIPs = Server::all()->reject(function (Server $server) {
|
||||||
// 'ip' => [
|
return $server->id === $this->server->id;
|
||||||
// 'ip'
|
})->pluck('ip')->toArray();
|
||||||
// ],
|
if (in_array($this->server->ip, $uniqueIPs)) {
|
||||||
// ]);
|
$this->emit('error', 'IP address is already in use by another team.');
|
||||||
// if ($validation->fails()) {
|
return;
|
||||||
// foreach ($validation->errors()->getMessages() as $key => $value) {
|
}
|
||||||
// $this->addError("server.{$key}", $value[0]);
|
|
||||||
// }
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
$this->server->settings->wildcard_domain = $this->wildcard_domain;
|
$this->server->settings->wildcard_domain = $this->wildcard_domain;
|
||||||
$this->server->settings->cleanup_after_percentage = $this->cleanup_after_percentage;
|
$this->server->settings->cleanup_after_percentage = $this->cleanup_after_percentage;
|
||||||
$this->server->settings->save();
|
$this->server->settings->save();
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class ByIp extends Component
|
|||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
try {
|
try {
|
||||||
if (!$this->private_key_id) {
|
if (is_null($this->private_key_id)) {
|
||||||
return $this->emit('error', 'You must select a private key');
|
return $this->emit('error', 'You must select a private key');
|
||||||
}
|
}
|
||||||
$server = Server::create([
|
$server = Server::create([
|
||||||
@@ -78,8 +78,8 @@ class ByIp extends Component
|
|||||||
$server->settings->is_part_of_swarm = $this->is_part_of_swarm;
|
$server->settings->is_part_of_swarm = $this->is_part_of_swarm;
|
||||||
$server->settings->save();
|
$server->settings->save();
|
||||||
return redirect()->route('server.show', $server->uuid);
|
return redirect()->route('server.show', $server->uuid);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ class Proxy extends Component
|
|||||||
|
|
||||||
public ?string $selectedProxy = null;
|
public ?string $selectedProxy = null;
|
||||||
public $proxy_settings = null;
|
public $proxy_settings = null;
|
||||||
public string|null $redirect_url = null;
|
public ?string $redirect_url = null;
|
||||||
|
|
||||||
protected $listeners = ['proxyStatusUpdated', 'saveConfiguration' => 'submit'];
|
protected $listeners = ['proxyStatusUpdated', 'saveConfiguration' => 'submit'];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->selectedProxy = $this->server->proxy->type;
|
$this->selectedProxy = data_get($this->server, 'proxy.type');
|
||||||
$this->redirect_url = $this->server->proxy->redirect_url;
|
$this->redirect_url = data_get($this->server, 'proxy.redirect_url');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function proxyStatusUpdated()
|
public function proxyStatusUpdated()
|
||||||
@@ -48,15 +48,15 @@ class Proxy extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
resolve(SaveConfigurationSync::class)($this->server, $this->proxy_settings);
|
resolve(SaveConfigurationSync::class)($this->server);
|
||||||
|
|
||||||
$this->server->proxy->redirect_url = $this->redirect_url;
|
$this->server->proxy->redirect_url = $this->redirect_url;
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
|
|
||||||
setup_default_redirect_404(redirect_url: $this->server->proxy->redirect_url, server: $this->server);
|
setup_default_redirect_404(redirect_url: $this->server->proxy->redirect_url, server: $this->server);
|
||||||
$this->emit('success', 'Proxy configuration saved.');
|
$this->emit('success', 'Proxy configuration saved.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,17 +64,18 @@ class Proxy extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server, true);
|
$this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server, true);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load_proxy_configuration()
|
public function loadProxyConfiguration()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
ray('loadProxyConfiguration');
|
||||||
$this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server);
|
$this->proxy_settings = resolve(CheckConfigurationSync::class)($this->server);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Server\Proxy;
|
namespace App\Http\Livewire\Server\Proxy;
|
||||||
|
|
||||||
|
use App\Actions\Proxy\SaveConfigurationSync;
|
||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -10,15 +11,20 @@ class Deploy extends Component
|
|||||||
{
|
{
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public $proxy_settings = null;
|
public $proxy_settings = null;
|
||||||
|
protected $listeners = ['proxyStatusUpdated'];
|
||||||
|
|
||||||
public function start_proxy()
|
public function proxyStatusUpdated() {
|
||||||
|
$this->server->refresh();
|
||||||
|
}
|
||||||
|
public function startProxy()
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
$this->server->proxy->last_applied_settings &&
|
$this->server->proxy->last_applied_settings &&
|
||||||
$this->server->proxy->last_saved_settings !== $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);
|
||||||
}
|
}
|
||||||
|
|
||||||
$activity = resolve(StartProxy::class)($this->server);
|
$activity = resolve(StartProxy::class)($this->server);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
}
|
}
|
||||||
|
|||||||
16
app/Http/Livewire/Server/Proxy/Modal.php
Normal file
16
app/Http/Livewire/Server/Proxy/Modal.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Server\Proxy;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Modal extends Component
|
||||||
|
{
|
||||||
|
public Server $server;
|
||||||
|
|
||||||
|
public function proxyStatusUpdated()
|
||||||
|
{
|
||||||
|
$this->emit('proxyStatusUpdated');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Server\Proxy;
|
namespace App\Http\Livewire\Server\Proxy;
|
||||||
|
|
||||||
use App\Jobs\ProxyContainerStatusJob;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -10,14 +9,27 @@ class Status extends Component
|
|||||||
{
|
{
|
||||||
public Server $server;
|
public Server $server;
|
||||||
|
|
||||||
public function get_status()
|
protected $listeners = ['proxyStatusUpdated'];
|
||||||
|
public function proxyStatusUpdated()
|
||||||
{
|
{
|
||||||
if (data_get($this->server,'settings.is_usable')) {
|
$this->server->refresh();
|
||||||
dispatch_sync(new ProxyContainerStatusJob(
|
}
|
||||||
server: $this->server
|
public function getProxyStatus()
|
||||||
));
|
{
|
||||||
$this->server->refresh();
|
try {
|
||||||
$this->emit('proxyStatusUpdated');
|
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 handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function getProxyStatusWithNoti()
|
||||||
|
{
|
||||||
|
$this->emit('success', 'Refreshed proxy status.');
|
||||||
|
$this->getProxyStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,12 @@ class Show extends Component
|
|||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->firstOrFail();
|
$this->server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||||
|
if (is_null($this->server)) {
|
||||||
|
return redirect()->route('server.all');
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ namespace App\Http\Livewire\Server;
|
|||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Masmerise\Toaster\Toaster;
|
|
||||||
|
|
||||||
class ShowPrivateKey extends Component
|
class ShowPrivateKey extends Component
|
||||||
{
|
{
|
||||||
@@ -12,28 +11,44 @@ class ShowPrivateKey extends Component
|
|||||||
public $privateKeys;
|
public $privateKeys;
|
||||||
public $parameters;
|
public $parameters;
|
||||||
|
|
||||||
public function setPrivateKey($private_key_id)
|
public function setPrivateKey($newPrivateKeyId)
|
||||||
{
|
{
|
||||||
$this->server->update([
|
try {
|
||||||
'private_key_id' => $private_key_id
|
refresh_server_connection($this->server->privateKey);
|
||||||
]);
|
$oldPrivateKeyId = $this->server->private_key_id;
|
||||||
refresh_server_connection($this->server->privateKey);
|
$this->server->update([
|
||||||
$this->server->refresh();
|
'private_key_id' => $newPrivateKeyId
|
||||||
$this->checkConnection();
|
]);
|
||||||
|
$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 handleError($e, $this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkConnection()
|
public function checkConnection()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server);
|
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true);
|
||||||
if ($uptime) {
|
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) {
|
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) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Settings;
|
namespace App\Http\Livewire\Settings;
|
||||||
|
|
||||||
use App\Jobs\ProxyStartJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Models\InstanceSettings as ModelsInstanceSettings;
|
use App\Models\InstanceSettings as ModelsInstanceSettings;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -124,7 +124,7 @@ class Configuration extends Component
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
$this->save_configuration_to_disk($traefik_dynamic_conf, $file);
|
$this->save_configuration_to_disk($traefik_dynamic_conf, $file);
|
||||||
dispatch(new ProxyStartJob($this->server));
|
dispatch(new ContainerStatusJob($this->server));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ class Email extends Component
|
|||||||
]);
|
]);
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function submitResend() {
|
public function submitResend() {
|
||||||
@@ -60,20 +60,28 @@ class Email extends Component
|
|||||||
$this->validate([
|
$this->validate([
|
||||||
'settings.resend_api_key' => 'required'
|
'settings.resend_api_key' => 'required'
|
||||||
]);
|
]);
|
||||||
$this->settings->smtp_enabled = false;
|
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->settings->resend_enabled = false;
|
$this->settings->resend_enabled = false;
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSaveResend() {
|
||||||
|
try {
|
||||||
|
$this->settings->smtp_enabled = false;
|
||||||
|
$this->submitResend();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$this->settings->resend_enabled = false;
|
||||||
$this->submit();
|
$this->submit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,11 +97,10 @@ class Email extends Component
|
|||||||
'settings.smtp_password' => 'nullable',
|
'settings.smtp_password' => 'nullable',
|
||||||
'settings.smtp_timeout' => 'nullable',
|
'settings.smtp_timeout' => 'nullable',
|
||||||
]);
|
]);
|
||||||
$this->settings->resend_enabled = false;
|
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ class Change extends Component
|
|||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->github_app->save();
|
$this->github_app->save();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +65,8 @@ class Change extends Component
|
|||||||
try {
|
try {
|
||||||
$this->github_app->delete();
|
$this->github_app->delete();
|
||||||
redirect()->route('source.all');
|
redirect()->route('source.all');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ class Create extends Component
|
|||||||
session(['from' => session('from') + ['source_id' => $github_app->id]]);
|
session(['from' => session('from') + ['source_id' => $github_app->id]]);
|
||||||
}
|
}
|
||||||
redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
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);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ class Actions extends Component
|
|||||||
$this->emit('success', 'Subscription cancelled successfully. Reloading in 5s.');
|
$this->emit('success', 'Subscription cancelled successfully. Reloading in 5s.');
|
||||||
$this->emit('reloadWindow', 5000);
|
$this->emit('reloadWindow', 5000);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function resume()
|
public function resume()
|
||||||
@@ -65,8 +65,8 @@ class Actions extends Component
|
|||||||
$this->emit('success', 'Subscription resumed successfully. Reloading in 5s.');
|
$this->emit('success', 'Subscription resumed successfully. Reloading in 5s.');
|
||||||
$this->emit('reloadWindow', 5000);
|
$this->emit('reloadWindow', 5000);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function stripeCustomerPortal() {
|
public function stripeCustomerPortal() {
|
||||||
|
|||||||
@@ -8,8 +8,13 @@ use Stripe\Checkout\Session;
|
|||||||
|
|
||||||
class PricingPlans extends Component
|
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)
|
public function subscribeStripe($type)
|
||||||
{
|
{
|
||||||
|
$team = currentTeam();
|
||||||
Stripe::setApiKey(config('subscription.stripe_api_key'));
|
Stripe::setApiKey(config('subscription.stripe_api_key'));
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'basic-monthly':
|
case 'basic-monthly':
|
||||||
@@ -39,6 +44,7 @@ class PricingPlans extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$payload = [
|
$payload = [
|
||||||
|
'billing_address_collection' => 'required',
|
||||||
'client_reference_id' => auth()->user()->id . ':' . currentTeam()->id,
|
'client_reference_id' => auth()->user()->id . ':' . currentTeam()->id,
|
||||||
'line_items' => [[
|
'line_items' => [[
|
||||||
'price' => $priceId,
|
'price' => $priceId,
|
||||||
@@ -50,10 +56,23 @@ class PricingPlans extends Component
|
|||||||
'automatic_tax' => [
|
'automatic_tax' => [
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
],
|
],
|
||||||
|
|
||||||
'mode' => 'subscription',
|
'mode' => 'subscription',
|
||||||
'success_url' => route('dashboard', ['success' => true]),
|
'success_url' => route('dashboard', ['success' => true]),
|
||||||
'cancel_url' => route('subscription.index', ['cancelled' => true]),
|
'cancel_url' => route('subscription.index', ['cancelled' => true]),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (!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;
|
$customer = currentTeam()->subscription?->stripe_customer_id ?? null;
|
||||||
if ($customer) {
|
if ($customer) {
|
||||||
$payload['customer'] = $customer;
|
$payload['customer'] = $customer;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class SwitchTeam extends Component
|
|||||||
if (!$team_to_switch_to) {
|
if (!$team_to_switch_to) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
session(['currentTeam' => $team_to_switch_to]);
|
refreshSession($team_to_switch_to);
|
||||||
return redirect(request()->header('Referer'));
|
return redirect(request()->header('Referer'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class Create extends Component
|
|||||||
auth()->user()->teams()->attach($team, ['role' => 'admin']);
|
auth()->user()->teams()->attach($team, ['role' => 'admin']);
|
||||||
refreshSession();
|
refreshSession();
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($th, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ class Delete extends Component
|
|||||||
$currentTeam = currentTeam();
|
$currentTeam = currentTeam();
|
||||||
$currentTeam->delete();
|
$currentTeam->delete();
|
||||||
|
|
||||||
$team = auth()->user()->teams()->first();
|
|
||||||
$currentTeam->members->each(function ($user) use ($currentTeam) {
|
$currentTeam->members->each(function ($user) use ($currentTeam) {
|
||||||
if ($user->id === auth()->user()->id) {
|
if ($user->id === auth()->user()->id) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ class Form extends Component
|
|||||||
try {
|
try {
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($th, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ namespace App\Http\Livewire\Team;
|
|||||||
|
|
||||||
use App\Models\TeamInvitation;
|
use App\Models\TeamInvitation;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Notifications\TransactionalEmails\InvitationLink;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class InviteLink extends Component
|
class InviteLink extends Component
|
||||||
{
|
{
|
||||||
@@ -20,64 +24,74 @@ class InviteLink extends Component
|
|||||||
|
|
||||||
public function viaEmail()
|
public function viaEmail()
|
||||||
{
|
{
|
||||||
$this->generate_invite_link(isEmail: true);
|
$this->generate_invite_link(sendEmail: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_invite_link(bool $isEmail = false)
|
public function viaLink()
|
||||||
|
{
|
||||||
|
$this->generate_invite_link(sendEmail: false);
|
||||||
|
}
|
||||||
|
private function generate_invite_link(bool $sendEmail = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$uuid = new Cuid2(32);
|
|
||||||
$link = url('/') . config('constants.invitation.link.base_url') . $uuid;
|
|
||||||
|
|
||||||
$user = User::whereEmail($this->email);
|
|
||||||
|
|
||||||
if (!$user->exists()) {
|
|
||||||
return general_error_handler(that: $this, customErrorMessage: "$this->email must be registered first (or activate transactional emails to invite via email).");
|
|
||||||
}
|
|
||||||
|
|
||||||
$member_emails = currentTeam()->members()->get()->pluck('email');
|
$member_emails = currentTeam()->members()->get()->pluck('email');
|
||||||
if ($member_emails->contains($this->email)) {
|
if ($member_emails->contains($this->email)) {
|
||||||
return general_error_handler(that: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . ".");
|
return handleError(livewire: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . ".");
|
||||||
}
|
}
|
||||||
|
$uuid = new Cuid2(32);
|
||||||
|
$link = url('/') . config('constants.invitation.link.base_url') . $uuid;
|
||||||
|
$user = User::whereEmail($this->email)->first();
|
||||||
|
|
||||||
$invitation = TeamInvitation::whereEmail($this->email);
|
if (is_null($user)) {
|
||||||
|
$password = Str::password();
|
||||||
if ($invitation->exists()) {
|
$user = User::create([
|
||||||
$created_at = $invitation->first()->created_at;
|
'name' => Str::of($this->email)->before('@'),
|
||||||
$diff = $created_at->diffInMinutes(now());
|
'email' => $this->email,
|
||||||
if ($diff <= config('constants.invitation.link.expiration')) {
|
'password' => Hash::make($password),
|
||||||
return general_error_handler(that: $this, customErrorMessage: "Invitation already sent to $this->email and waiting for action.");
|
'force_password_reset' => true,
|
||||||
|
]);
|
||||||
|
$token = Crypt::encryptString("{$user->email}@@@$password");
|
||||||
|
$link = route('auth.link', ['token' => $token]);
|
||||||
|
}
|
||||||
|
$invitation = TeamInvitation::whereEmail($this->email)->first();
|
||||||
|
if (!is_null($invitation)) {
|
||||||
|
$invitationValid = $invitation->isValid();
|
||||||
|
if ($invitationValid) {
|
||||||
|
return handleError(livewire: $this, customErrorMessage: "Pending invitation already exists for $this->email.");
|
||||||
} else {
|
} else {
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamInvitation::firstOrCreate([
|
$invitation = TeamInvitation::firstOrCreate([
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
'uuid' => $uuid,
|
'uuid' => $uuid,
|
||||||
'email' => $this->email,
|
'email' => $this->email,
|
||||||
'role' => $this->role,
|
'role' => $this->role,
|
||||||
'link' => $link,
|
'link' => $link,
|
||||||
'via' => $isEmail ? 'email' : 'link',
|
'via' => $sendEmail ? 'email' : 'link',
|
||||||
]);
|
]);
|
||||||
if ($isEmail) {
|
if ($sendEmail) {
|
||||||
$user->first()->notify(new InvitationLink);
|
$mail = new MailMessage();
|
||||||
|
$mail->view('emails.invitation-link', [
|
||||||
|
'team' => currentTeam()->name,
|
||||||
|
'invitation_link' => $link,
|
||||||
|
]);
|
||||||
|
$mail->subject('You have been invited to ' . currentTeam()->name . ' on ' . config('app.name') . '.');
|
||||||
|
send_user_an_email($mail, $this->email);
|
||||||
$this->emit('success', 'Invitation sent via email successfully.');
|
$this->emit('success', 'Invitation sent via email successfully.');
|
||||||
|
$this->emit('refreshInvitations');
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
$this->emit('success', 'Invitation link generated.');
|
$this->emit('success', 'Invitation link generated.');
|
||||||
|
$this->emit('refreshInvitations');
|
||||||
}
|
}
|
||||||
$this->emit('refreshInvitations');
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$error_message = $e->getMessage();
|
$error_message = $e->getMessage();
|
||||||
if ($e->getCode() === '23505') {
|
if ($e->getCode() === '23505') {
|
||||||
$error_message = 'Invitation already sent.';
|
$error_message = 'Invitation already sent.';
|
||||||
}
|
}
|
||||||
return general_error_handler(err: $e, that: $this, customErrorMessage: $error_message);
|
return handleError(error: $e, livewire: $this, customErrorMessage: $error_message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function viaLink()
|
|
||||||
{
|
|
||||||
$this->generate_invite_link();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Livewire\Team;
|
namespace App\Http\Livewire\Team;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Member extends Component
|
class Member extends Component
|
||||||
@@ -24,6 +25,10 @@ class Member extends Component
|
|||||||
public function remove()
|
public function remove()
|
||||||
{
|
{
|
||||||
$this->member->teams()->detach(currentTeam());
|
$this->member->teams()->detach(currentTeam());
|
||||||
|
Cache::forget("team:{$this->member->id}");
|
||||||
|
Cache::remember('team:' . $this->member->id, 3600, function() {
|
||||||
|
return $this->member->teams()->first();
|
||||||
|
});
|
||||||
$this->emit('reloadWindow');
|
$this->emit('reloadWindow');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ class Create extends Component
|
|||||||
$this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
$this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||||
$this->storage->save();
|
$this->storage->save();
|
||||||
return redirect()->route('team.storages.show', $this->storage->uuid);
|
return redirect()->route('team.storages.show', $this->storage->uuid);
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($th, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +77,8 @@ class Create extends Component
|
|||||||
try {
|
try {
|
||||||
$this->storage->testConnection();
|
$this->storage->testConnection();
|
||||||
return $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
return $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($th, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ class Form extends Component
|
|||||||
try {
|
try {
|
||||||
$this->storage->testConnection();
|
$this->storage->testConnection();
|
||||||
return $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
return $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($th, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ class Form extends Component
|
|||||||
try {
|
try {
|
||||||
$this->storage->delete();
|
$this->storage->delete();
|
||||||
return redirect()->route('team.storages.all');
|
return redirect()->route('team.storages.all');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($th, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +55,8 @@ class Form extends Component
|
|||||||
$this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
$this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.');
|
||||||
$this->storage->save();
|
$this->storage->save();
|
||||||
$this->emit('success', 'Storage settings saved.');
|
$this->emit('success', 'Storage settings saved.');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($th, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ class Upgrade extends Component
|
|||||||
}
|
}
|
||||||
$this->showProgress = true;
|
$this->showProgress = true;
|
||||||
resolve(UpdateCoolify::class)(true);
|
resolve(UpdateCoolify::class)(true);
|
||||||
Toaster::success("Upgrading to {$this->latestVersion} version...");
|
$this->emit('success', "Upgrading to {$this->latestVersion} version...");
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ use App\Jobs\SendConfirmationForWaitlistJob;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\Waitlist;
|
use App\Models\Waitlist;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
public string $email;
|
public string $email;
|
||||||
|
public int $users = 0;
|
||||||
public int $waitingInLine = 0;
|
public int $waitingInLine = 0;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
@@ -22,6 +24,7 @@ class Index extends Component
|
|||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
||||||
|
$this->users = User::count();
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$this->email = 'waitlist@example.com';
|
$this->email = 'waitlist@example.com';
|
||||||
}
|
}
|
||||||
@@ -44,14 +47,14 @@ class Index extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$waitlist = Waitlist::create([
|
$waitlist = Waitlist::create([
|
||||||
'email' => $this->email,
|
'email' => Str::lower($this->email),
|
||||||
'type' => 'registration',
|
'type' => 'registration',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->emit('success', 'Check your email to verify your email address.');
|
$this->emit('success', 'Check your email to verify your email address.');
|
||||||
dispatch(new SendConfirmationForWaitlistJob($this->email, $waitlist->uuid));
|
dispatch(new SendConfirmationForWaitlistJob($this->email, $waitlist->uuid));
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Http\Middleware;
|
|||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class IsBoardingFlow
|
class IsBoardingFlow
|
||||||
{
|
{
|
||||||
@@ -15,8 +16,11 @@ class IsBoardingFlow
|
|||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
ray()->showQueries()->color('orange');
|
// ray()->showQueries()->color('orange');
|
||||||
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
||||||
|
if (Str::startsWith($request->path(), 'invitations')) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
return redirect('boarding');
|
return redirect('boarding');
|
||||||
}
|
}
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Http\Middleware;
|
|||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class IsSubscriptionValid
|
class IsSubscriptionValid
|
||||||
{
|
{
|
||||||
@@ -31,6 +32,9 @@ class IsSubscriptionValid
|
|||||||
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
||||||
// ray('SubscriptionValid Middleware');
|
// ray('SubscriptionValid Middleware');
|
||||||
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
||||||
|
if (Str::startsWith($request->path(), 'invitations')) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
return redirect('subscription');
|
return redirect('subscription');
|
||||||
} else {
|
} else {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Jobs;
|
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use App\Models\ApplicationPreview;
|
|
||||||
use App\Notifications\Application\StatusChanged;
|
|
||||||
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 ApplicationContainerStatusJob implements ShouldQueue, ShouldBeUnique
|
|
||||||
{
|
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
||||||
|
|
||||||
public string $containerName;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public Application $application,
|
|
||||||
public int $pullRequestId = 0)
|
|
||||||
{
|
|
||||||
$this->containerName = generateApplicationContainerName($application->uuid, $pullRequestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): string
|
|
||||||
{
|
|
||||||
return $this->containerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$status = getApplicationContainerStatus(application: $this->application);
|
|
||||||
if ($this->application->status === 'running' && $status !== 'running') {
|
|
||||||
// $this->application->environment->project->team->notify(new StatusChanged($this->application));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->pullRequestId !== 0) {
|
|
||||||
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pullRequestId);
|
|
||||||
$preview->status = $status;
|
|
||||||
$preview->save();
|
|
||||||
} else {
|
|
||||||
$this->application->status = $status;
|
|
||||||
$this->application->save();
|
|
||||||
}
|
|
||||||
} catch (\Exception $th) {
|
|
||||||
ray($th->getMessage());
|
|
||||||
throw $th;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,10 +17,10 @@ use App\Notifications\Application\DeploymentSuccess;
|
|||||||
use App\Traits\ExecuteRemoteCommand;
|
use App\Traits\ExecuteRemoteCommand;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -29,7 +29,7 @@ use Symfony\Component\Yaml\Yaml;
|
|||||||
use Throwable;
|
use Throwable;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
class ApplicationDeploymentJob implements ShouldQueue
|
class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ExecuteRemoteCommand;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ExecuteRemoteCommand;
|
||||||
|
|
||||||
@@ -47,7 +47,6 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
private GithubApp|GitlabApp $source;
|
private GithubApp|GitlabApp $source;
|
||||||
private StandaloneDocker|SwarmDocker $destination;
|
private StandaloneDocker|SwarmDocker $destination;
|
||||||
private Server $server;
|
private Server $server;
|
||||||
private string $private_key_location;
|
|
||||||
private ApplicationPreview|null $preview = null;
|
private ApplicationPreview|null $preview = null;
|
||||||
|
|
||||||
private string $container_name;
|
private string $container_name;
|
||||||
@@ -90,7 +89,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
|
|
||||||
$this->container_name = generateApplicationContainerName($this->application->uuid, $this->pull_request_id);
|
$this->container_name = generateApplicationContainerName($this->application->uuid, $this->pull_request_id);
|
||||||
$this->private_key_location = save_private_key_for_server($this->server);
|
addPrivateKeyToSshAgent($this->server);
|
||||||
$this->saved_outputs = collect();
|
$this->saved_outputs = collect();
|
||||||
|
|
||||||
// Set preview fqdn
|
// Set preview fqdn
|
||||||
@@ -120,6 +119,9 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
if ($containers->count() > 0) {
|
if ($containers->count() > 0) {
|
||||||
$this->currently_running_container_name = data_get($containers[0], 'Names');
|
$this->currently_running_container_name = data_get($containers[0], 'Names');
|
||||||
}
|
}
|
||||||
|
if ($this->pull_request_id !== 0 && $this->pull_request_id !== null) {
|
||||||
|
$this->currently_running_container_name = $this->container_name;
|
||||||
|
}
|
||||||
$this->application_deployment_queue->update([
|
$this->application_deployment_queue->update([
|
||||||
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
|
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
|
||||||
]);
|
]);
|
||||||
@@ -133,7 +135,9 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
$this->deploy();
|
$this->deploy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->application->fqdn) dispatch(new ProxyStartJob($this->server));
|
if ($this->server->isProxyShouldRun()) {
|
||||||
|
dispatch(new ContainerStatusJob($this->server));
|
||||||
|
}
|
||||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
@@ -173,7 +177,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
$this->prepare_builder_image();
|
$this->prepare_builder_image();
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
$this->execute_in_builder("echo '$dockerfile_base64' | base64 -d > $this->workdir/Dockerfile")
|
executeInDocker($this->deployment_uuid, "echo '$dockerfile_base64' | base64 -d > $this->workdir/Dockerfile")
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
$this->build_image_name = Str::lower("{$this->application->git_repository}:build");
|
||||||
@@ -268,6 +272,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
"echo 'Rolling update completed.'"
|
"echo 'Rolling update completed.'"
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
$this->application->update(['status' => 'running']);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$counter++;
|
$counter++;
|
||||||
@@ -294,7 +299,11 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
// $this->generate_build_env_variables();
|
// $this->generate_build_env_variables();
|
||||||
// $this->add_build_env_variables_to_dockerfile();
|
// $this->add_build_env_variables_to_dockerfile();
|
||||||
$this->build_image();
|
$this->build_image();
|
||||||
$this->rolling_update();
|
$this->stop_running_container();
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Starting preview deployment.'"],
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function prepare_builder_image()
|
private function prepare_builder_image()
|
||||||
@@ -315,16 +324,12 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
"hidden" => true,
|
"hidden" => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"command" => $this->execute_in_builder("mkdir -p {$this->workdir}")
|
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function execute_in_builder(string $command)
|
|
||||||
{
|
|
||||||
return "docker exec {$this->deployment_uuid} bash -c '{$command}'";
|
|
||||||
// return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1; [ \$PIPESTATUS -eq 0 ] || exit \$PIPESTATUS'";
|
|
||||||
}
|
|
||||||
|
|
||||||
private function clone_repository()
|
private function clone_repository()
|
||||||
{
|
{
|
||||||
@@ -336,7 +341,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
$this->importing_git_repository()
|
$this->importing_git_repository()
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
$this->execute_in_builder("cd {$this->workdir} && git rev-parse HEAD"),
|
executeInDocker($this->deployment_uuid, "cd {$this->workdir} && git rev-parse HEAD"),
|
||||||
"hidden" => true,
|
"hidden" => true,
|
||||||
"save" => "git_commit_sha"
|
"save" => "git_commit_sha"
|
||||||
],
|
],
|
||||||
@@ -363,13 +368,13 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$this->application->git_repository} {$this->workdir}";
|
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$this->application->git_repository} {$this->workdir}";
|
||||||
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
||||||
|
|
||||||
$commands->push($this->execute_in_builder($git_clone_command));
|
$commands->push(executeInDocker($this->deployment_uuid, $git_clone_command));
|
||||||
} else {
|
} else {
|
||||||
$github_access_token = generate_github_installation_token($this->source);
|
$github_access_token = generate_github_installation_token($this->source);
|
||||||
$commands->push($this->execute_in_builder("git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->workdir}"));
|
$commands->push(executeInDocker($this->deployment_uuid, "git clone -q -b {$this->application->git_branch} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$this->application->git_repository}.git {$this->workdir}"));
|
||||||
}
|
}
|
||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
$commands->push($this->execute_in_builder("cd {$this->workdir} && git fetch origin pull/{$this->pull_request_id}/head:$pr_branch_name && git checkout $pr_branch_name"));
|
$commands->push(executeInDocker($this->deployment_uuid, "cd {$this->workdir} && git fetch origin pull/{$this->pull_request_id}/head:$pr_branch_name && git checkout $pr_branch_name"));
|
||||||
}
|
}
|
||||||
return $commands->implode(' && ');
|
return $commands->implode(' && ');
|
||||||
}
|
}
|
||||||
@@ -379,10 +384,10 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->application->git_full_url} {$this->workdir}";
|
$git_clone_command = "GIT_SSH_COMMAND=\"ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$this->application->git_full_url} {$this->workdir}";
|
||||||
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
$git_clone_command = $this->set_git_import_settings($git_clone_command);
|
||||||
$commands = collect([
|
$commands = collect([
|
||||||
$this->execute_in_builder("mkdir -p /root/.ssh"),
|
executeInDocker($this->deployment_uuid, "mkdir -p /root/.ssh"),
|
||||||
$this->execute_in_builder("echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"),
|
executeInDocker($this->deployment_uuid, "echo '{$private_key}' | base64 -d > /root/.ssh/id_rsa"),
|
||||||
$this->execute_in_builder("chmod 600 /root/.ssh/id_rsa"),
|
executeInDocker($this->deployment_uuid, "chmod 600 /root/.ssh/id_rsa"),
|
||||||
$this->execute_in_builder($git_clone_command)
|
executeInDocker($this->deployment_uuid, $git_clone_command)
|
||||||
]);
|
]);
|
||||||
return $commands->implode(' && ');
|
return $commands->implode(' && ');
|
||||||
}
|
}
|
||||||
@@ -405,7 +410,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
private function cleanup_git()
|
private function cleanup_git()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[$this->execute_in_builder("rm -fr {$this->workdir}/.git")],
|
[executeInDocker($this->deployment_uuid, "rm -fr {$this->workdir}/.git")],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,8 +421,8 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
"echo -n 'Generating nixpacks configuration.'",
|
"echo -n 'Generating nixpacks configuration.'",
|
||||||
],
|
],
|
||||||
[$this->nixpacks_build_cmd()],
|
[$this->nixpacks_build_cmd()],
|
||||||
[$this->execute_in_builder("cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile")],
|
[executeInDocker($this->deployment_uuid, "cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile")],
|
||||||
[$this->execute_in_builder("rm -f {$this->workdir}/.nixpacks/Dockerfile")]
|
[executeInDocker($this->deployment_uuid, "rm -f {$this->workdir}/.nixpacks/Dockerfile")]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,7 +440,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
|
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
|
||||||
}
|
}
|
||||||
$nixpacks_command .= " {$this->workdir}";
|
$nixpacks_command .= " {$this->workdir}";
|
||||||
return $this->execute_in_builder($nixpacks_command);
|
return executeInDocker($this->deployment_uuid, $nixpacks_command);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_env_variables()
|
private function generate_env_variables()
|
||||||
@@ -498,7 +503,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
$this->destination->network => [
|
$this->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->destination->network,
|
'name' => $this->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
@@ -513,7 +518,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
||||||
$this->execute_remote_command([$this->execute_in_builder("echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -574,10 +579,15 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
|
|
||||||
private function set_labels_for_applications()
|
private function set_labels_for_applications()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$appId = $this->application->id;
|
||||||
|
if ($this->pull_request_id !== 0) {
|
||||||
|
$appId = $appId . '-pr-' . $this->pull_request_id;
|
||||||
|
}
|
||||||
$labels = [];
|
$labels = [];
|
||||||
$labels[] = 'coolify.managed=true';
|
$labels[] = 'coolify.managed=true';
|
||||||
$labels[] = 'coolify.version=' . config('version');
|
$labels[] = 'coolify.version=' . config('version');
|
||||||
$labels[] = 'coolify.applicationId=' . $this->application->id;
|
$labels[] = 'coolify.applicationId=' . $appId;
|
||||||
$labels[] = 'coolify.type=application';
|
$labels[] = 'coolify.type=application';
|
||||||
$labels[] = 'coolify.name=' . $this->application->name;
|
$labels[] = 'coolify.name=' . $this->application->name;
|
||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
@@ -638,6 +648,10 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
|
|
||||||
private function generate_healthcheck_commands()
|
private function generate_healthcheck_commands()
|
||||||
{
|
{
|
||||||
|
if ($this->application->dockerfile || $this->application->build_pack === '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) {
|
if (!$this->application->health_check_port) {
|
||||||
$this->application->health_check_port = $this->application->ports_exposes_array[0];
|
$this->application->health_check_port = $this->application->ports_exposes_array[0];
|
||||||
}
|
}
|
||||||
@@ -661,7 +675,7 @@ class ApplicationDeploymentJob implements ShouldQueue
|
|||||||
|
|
||||||
if ($this->application->settings->is_static) {
|
if ($this->application->settings->is_static) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
$this->execute_in_builder("docker build --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||||
@@ -688,18 +702,18 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
}");
|
}");
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
$this->execute_in_builder("echo '{$dockerfile}' | base64 -d > {$this->workdir}/Dockerfile-prod")
|
executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d > {$this->workdir}/Dockerfile-prod")
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
$this->execute_in_builder("echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
|
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
$this->execute_in_builder("docker build --network host -f {$this->workdir}/Dockerfile-prod {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}/Dockerfile-prod {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
$this->execute_in_builder("docker build --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -709,7 +723,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
if ($this->currently_running_container_name) {
|
if ($this->currently_running_container_name) {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Removing old version of your application.'"],
|
["echo -n 'Removing old version of your application.'"],
|
||||||
[$this->execute_in_builder("docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -718,7 +732,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Rolling update started.'"],
|
["echo -n 'Rolling update started.'"],
|
||||||
[$this->execute_in_builder("docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,7 +755,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
private function add_build_env_variables_to_dockerfile()
|
private function add_build_env_variables_to_dockerfile()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
$this->execute_in_builder("cat {$this->workdir}/Dockerfile"), "hidden" => true, "save" => 'dockerfile'
|
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/Dockerfile"), "hidden" => true, "save" => 'dockerfile'
|
||||||
]);
|
]);
|
||||||
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
||||||
|
|
||||||
@@ -750,7 +764,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
}
|
}
|
||||||
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
$dockerfile_base64 = base64_encode($dockerfile->implode("\n"));
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
$this->execute_in_builder("echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}/Dockerfile"),
|
executeInDocker($this->deployment_uuid, "echo '{$dockerfile_base64}' | base64 -d > {$this->workdir}/Dockerfile"),
|
||||||
"hidden" => true
|
"hidden" => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ use App\Enums\ProcessStatus;
|
|||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class ApplicationPullRequestUpdateJob implements ShouldQueue
|
class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue
|
|||||||
} else {
|
} else {
|
||||||
$this->create_comment();
|
$this->create_comment();
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ namespace App\Jobs;
|
|||||||
|
|
||||||
use App\Actions\License\CheckResaleLicense;
|
use App\Actions\License\CheckResaleLicense;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class CheckResaleLicenseJob implements ShouldQueue
|
class CheckResaleLicenseJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -21,10 +22,10 @@ class CheckResaleLicenseJob implements ShouldQueue
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
resolve(CheckResaleLicense::class)();
|
resolve(CheckResaleLicense::class)();
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('CheckResaleLicenseJob failed with: ' . $th->getMessage());
|
send_internal_notification('CheckResaleLicenseJob failed with: ' . $e->getMessage());
|
||||||
ray($th);
|
ray($e);
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\TeamInvitation;
|
||||||
use App\Models\Waitlist;
|
use App\Models\Waitlist;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique
|
class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -27,11 +29,16 @@ class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique
|
|||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->cleanup_waitlist();
|
// $this->cleanup_waitlist();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
|
send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$this->cleanup_invitation_link();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage());
|
send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
throw $e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,4 +49,11 @@ class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique
|
|||||||
$item->delete();
|
$item->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private function cleanup_invitation_link()
|
||||||
|
{
|
||||||
|
$invitation = TeamInvitation::all();
|
||||||
|
foreach ($invitation as $item) {
|
||||||
|
$item->isValid();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
286
app/Jobs/ContainerStatusJob.php
Normal file
286
app/Jobs/ContainerStatusJob.php
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Actions\Proxy\StartProxy;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Notifications\Container\ContainerRestarted;
|
||||||
|
use App\Notifications\Container\ContainerStopped;
|
||||||
|
use App\Notifications\Server\Unreachable;
|
||||||
|
use Arr;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
|
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 Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkServerConnection() {
|
||||||
|
$uptime = instant_remote_process(['uptime'], $this->server, false);
|
||||||
|
if (!is_null($uptime)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// ray()->clearAll();
|
||||||
|
$serverUptimeCheckNumber = 0;
|
||||||
|
$serverUptimeCheckNumberMax = 5;
|
||||||
|
while (true) {
|
||||||
|
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
||||||
|
$this->server->settings()->update(['is_reachable' => false]);
|
||||||
|
$this->server->team->notify(new Unreachable($this->server));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$result = $this->checkServerConnection();
|
||||||
|
if ($result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$serverUptimeCheckNumber++;
|
||||||
|
sleep(5);
|
||||||
|
}
|
||||||
|
$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();
|
||||||
|
$databases = $this->server->databases();
|
||||||
|
$previews = $this->server->previews();
|
||||||
|
if ($this->server->isProxyShouldRun()) {
|
||||||
|
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||||
|
return data_get($value, 'Name') === '/coolify-proxy';
|
||||||
|
})->first();
|
||||||
|
if (!$foundProxyContainer) {
|
||||||
|
resolve(StartProxy::class)($this->server, false);
|
||||||
|
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$foundApplications = [];
|
||||||
|
$foundApplicationPreviews = [];
|
||||||
|
$foundDatabases = [];
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$containerStatus = data_get($container, 'State.Status');
|
||||||
|
$labels = data_get($container, 'Config.Labels');
|
||||||
|
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||||
|
$labelId = data_get($labels, 'coolify.applicationId');
|
||||||
|
if ($labelId) {
|
||||||
|
if (str_contains($labelId,'-pr-')) {
|
||||||
|
$previewId = (int) Str::after($labelId, '-pr-');
|
||||||
|
$applicationId = (int) Str::before($labelId, '-pr-');
|
||||||
|
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id',$previewId)->first();
|
||||||
|
if ($preview) {
|
||||||
|
$foundApplicationPreviews[] = $preview->id;
|
||||||
|
$statusFromDb = $preview->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$preview->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Notify user that this container should not be there.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$application = $applications->where('id', $labelId)->first();
|
||||||
|
if ($application) {
|
||||||
|
$foundApplications[] = $application->id;
|
||||||
|
$statusFromDb = $application->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$application->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Notify user that this container should not be there.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||||
|
if ($uuid) {
|
||||||
|
$database = $databases->where('uuid', $uuid)->first();
|
||||||
|
if ($database) {
|
||||||
|
$foundDatabases[] = $database->id;
|
||||||
|
$statusFromDb = $database->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$database->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Notify user that this container should not be there.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
$notRunningApplications = $applications->pluck('id')->diff($foundApplications);
|
||||||
|
foreach($notRunningApplications as $applicationId) {
|
||||||
|
$application = $applications->where('id', $applicationId)->first();
|
||||||
|
if ($application->status === 'exited') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$application->update(['status' => 'exited']);
|
||||||
|
|
||||||
|
$name = data_get($application, 'name');
|
||||||
|
$fqdn = data_get($application, 'fqdn');
|
||||||
|
|
||||||
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
|
||||||
|
$project = data_get($application, 'environment.project');
|
||||||
|
$environment = data_get($application, 'environment');
|
||||||
|
|
||||||
|
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/application/" . $application->uuid;
|
||||||
|
|
||||||
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
||||||
|
foreach ($notRunningApplicationPreviews as $previewId) {
|
||||||
|
$preview = $previews->where('id', $previewId)->first();
|
||||||
|
if ($preview->status === 'exited') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$preview->update(['status' => 'exited']);
|
||||||
|
|
||||||
|
$name = data_get($preview, 'name');
|
||||||
|
$fqdn = data_get($preview, 'fqdn');
|
||||||
|
|
||||||
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
|
||||||
|
$project = data_get($preview, 'application.environment.project');
|
||||||
|
$environment = data_get($preview, 'application.environment');
|
||||||
|
|
||||||
|
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/application/" . $preview->application->uuid;
|
||||||
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
||||||
|
foreach($notRunningDatabases as $database) {
|
||||||
|
$database = $databases->where('id', $database)->first();
|
||||||
|
if ($database->status === 'exited') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$database->update(['status' => 'exited']);
|
||||||
|
|
||||||
|
$name = data_get($database, 'name');
|
||||||
|
$fqdn = data_get($database, 'fqdn');
|
||||||
|
|
||||||
|
$containerName = $name;
|
||||||
|
|
||||||
|
$project = data_get($database, 'environment.project');
|
||||||
|
$environment = data_get($database, 'environment');
|
||||||
|
|
||||||
|
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/database/" . $database->uuid;
|
||||||
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$uuid = data_get($application, 'uuid');
|
||||||
|
$id = data_get($application, 'id');
|
||||||
|
$foundContainer = $containers->filter(function ($value, $key) use ($id, $uuid) {
|
||||||
|
$labels = data_get($value, 'Config.Labels');
|
||||||
|
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||||
|
$labelId = data_get($labels, 'coolify.applicationId');
|
||||||
|
if ($labelId == $id) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
$isPR = Str::startsWith(data_get($value, 'Name'), "/$uuid");
|
||||||
|
$isPR = Str::contains(data_get($value, 'Name'), "-pr-");
|
||||||
|
if ($isPR) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
})->first();
|
||||||
|
if ($foundContainer) {
|
||||||
|
$containerStatus = data_get($foundContainer, 'State.Status');
|
||||||
|
$databaseStatus = data_get($application, 'status');
|
||||||
|
if ($containerStatus !== $databaseStatus) {
|
||||||
|
$application->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$databaseStatus = data_get($application, 'status');
|
||||||
|
if ($databaseStatus !== 'exited') {
|
||||||
|
$application->update(['status' => 'exited']);
|
||||||
|
$name = data_get($application, 'name');
|
||||||
|
$fqdn = data_get($application, 'fqdn');
|
||||||
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
$project = data_get($application, 'environment.project');
|
||||||
|
$environment = data_get($application, 'environment');
|
||||||
|
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/application/" . $application->uuid;
|
||||||
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$previews = $application->previews;
|
||||||
|
foreach ($previews as $preview) {
|
||||||
|
$foundContainer = $containers->filter(function ($value, $key) use ($id, $uuid, $preview) {
|
||||||
|
$labels = data_get($value, 'Config.Labels');
|
||||||
|
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||||
|
$labelId = data_get($labels, 'coolify.applicationId');
|
||||||
|
if ($labelId == "$id-pr-{$preview->id}") {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
return Str::startsWith(data_get($value, 'Name'), "/$uuid-pr-{$preview->id}");
|
||||||
|
})->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
foreach ($databases as $database) {
|
||||||
|
$uuid = data_get($database, 'uuid');
|
||||||
|
$foundContainer = $containers->filter(function ($value, $key) use ($uuid) {
|
||||||
|
return Str::startsWith(data_get($value, 'Name'), "/$uuid");
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($foundContainer) {
|
||||||
|
$containerStatus = data_get($foundContainer, 'State.Status');
|
||||||
|
$databaseStatus = data_get($database, 'status');
|
||||||
|
if ($containerStatus !== $databaseStatus) {
|
||||||
|
$database->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$databaseStatus = data_get($database, 'status');
|
||||||
|
if ($databaseStatus !== 'exited') {
|
||||||
|
$database->update(['status' => 'exited']);
|
||||||
|
$name = data_get($database, 'name');
|
||||||
|
$containerName = $name;
|
||||||
|
$project = data_get($database, 'environment.project');
|
||||||
|
$environment = data_get($database, 'environment');
|
||||||
|
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/database/" . $database->uuid;
|
||||||
|
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO Monitor other containers not managed by Coolify
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,14 @@ namespace App\Jobs;
|
|||||||
|
|
||||||
use App\Actions\CoolifyTask\RunRemoteProcess;
|
use App\Actions\CoolifyTask\RunRemoteProcess;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
|
||||||
class CoolifyTask implements ShouldQueue
|
class CoolifyTask implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
|||||||
@@ -12,43 +12,39 @@ use App\Notifications\Database\BackupFailed;
|
|||||||
use App\Notifications\Database\BackupSuccess;
|
use App\Notifications\Database\BackupSuccess;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Throwable;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class DatabaseBackupJob implements ShouldQueue
|
class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public Team|null $team = null;
|
public ?Team $team = null;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public ScheduledDatabaseBackup|null $backup;
|
public ScheduledDatabaseBackup $backup;
|
||||||
public string $database_type;
|
|
||||||
public StandalonePostgresql $database;
|
public StandalonePostgresql $database;
|
||||||
public string $database_status;
|
|
||||||
|
|
||||||
public string|null $container_name = null;
|
public ?string $container_name = null;
|
||||||
public ScheduledDatabaseBackupExecution|null $backup_log = null;
|
public ?ScheduledDatabaseBackupExecution $backup_log = null;
|
||||||
public string $backup_status;
|
public string $backup_status;
|
||||||
public string|null $backup_location = null;
|
public ?string $backup_location = null;
|
||||||
public string $backup_dir;
|
public string $backup_dir;
|
||||||
public string $backup_file;
|
public string $backup_file;
|
||||||
public int $size = 0;
|
public int $size = 0;
|
||||||
public string|null $backup_output = null;
|
public ?string $backup_output = null;
|
||||||
public S3Storage|null $s3 = null;
|
public ?S3Storage $s3 = null;
|
||||||
|
|
||||||
public function __construct($backup)
|
public function __construct($backup)
|
||||||
{
|
{
|
||||||
$this->backup = $backup;
|
$this->backup = $backup;
|
||||||
$this->team = Team::find($backup->team_id);
|
$this->team = Team::find($backup->team_id);
|
||||||
$this->database = $this->backup->database;
|
$this->database = data_get($this->backup, 'database');
|
||||||
$this->database_type = $this->database->type();
|
|
||||||
$this->server = $this->database->destination->server;
|
$this->server = $this->database->destination->server;
|
||||||
$this->database_status = $this->database->status;
|
|
||||||
$this->s3 = $this->backup->s3;
|
$this->s3 = $this->backup->s3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +61,7 @@ class DatabaseBackupJob implements ShouldQueue
|
|||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->database_status !== 'running') {
|
if (data_get($this->database, 'status') !== 'running') {
|
||||||
ray('database not running');
|
ray('database not running');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -84,7 +80,7 @@ class DatabaseBackupJob implements ShouldQueue
|
|||||||
'filename' => $this->backup_location,
|
'filename' => $this->backup_location,
|
||||||
'scheduled_database_backup_id' => $this->backup->id,
|
'scheduled_database_backup_id' => $this->backup->id,
|
||||||
]);
|
]);
|
||||||
if ($this->database_type === 'standalone-postgresql') {
|
if ($this->database->type() === 'standalone-postgresql') {
|
||||||
$this->backup_standalone_postgresql();
|
$this->backup_standalone_postgresql();
|
||||||
}
|
}
|
||||||
$this->calculate_size();
|
$this->calculate_size();
|
||||||
@@ -94,12 +90,11 @@ class DatabaseBackupJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
$this->save_backup_logs();
|
$this->save_backup_logs();
|
||||||
// TODO: Notify user
|
// TODO: Notify user
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
ray($th->getMessage());
|
ray($e->getMessage());
|
||||||
send_internal_notification('DatabaseBackupJob failed with: ' . $th->getMessage());
|
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function backup_standalone_postgresql(): void
|
private function backup_standalone_postgresql(): void
|
||||||
@@ -121,10 +116,10 @@ class DatabaseBackupJob implements ShouldQueue
|
|||||||
|
|
||||||
$this->backup_status = 'success';
|
$this->backup_status = 'success';
|
||||||
$this->team->notify(new BackupSuccess($this->backup, $this->database));
|
$this->team->notify(new BackupSuccess($this->backup, $this->database));
|
||||||
} catch (Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
$this->backup_status = 'failed';
|
$this->backup_status = 'failed';
|
||||||
$this->add_to_backup_output($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:' . $th->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));
|
$this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
||||||
} finally {
|
} finally {
|
||||||
$this->backup_log->update([
|
$this->backup_log->update([
|
||||||
@@ -178,9 +173,9 @@ class DatabaseBackupJob implements ShouldQueue
|
|||||||
instant_remote_process($commands, $this->server);
|
instant_remote_process($commands, $this->server);
|
||||||
$this->add_to_backup_output('Uploaded to S3.');
|
$this->add_to_backup_output('Uploaded to S3.');
|
||||||
ray('Uploaded to S3. ' . $this->backup_location . ' to s3://' . $bucket . $this->backup_dir);
|
ray('Uploaded to S3. ' . $this->backup_location . ' to s3://' . $bucket . $this->backup_dir);
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
$this->add_to_backup_output($th->getMessage());
|
$this->add_to_backup_output($e->getMessage());
|
||||||
ray($th->getMessage());
|
ray($e->getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
$command = "docker rm -f backup-of-{$this->backup->uuid}";
|
$command = "docker rm -f backup-of-{$this->backup->uuid}";
|
||||||
instant_remote_process([$command], $this->server);
|
instant_remote_process([$command], $this->server);
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Jobs;
|
|
||||||
|
|
||||||
use App\Models\ApplicationPreview;
|
|
||||||
use App\Models\StandalonePostgresql;
|
|
||||||
use App\Notifications\Application\StatusChanged;
|
|
||||||
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 DatabaseContainerStatusJob implements ShouldQueue, ShouldBeUnique
|
|
||||||
{
|
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
||||||
|
|
||||||
public string $containerName;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public StandalonePostgresql $database,
|
|
||||||
) {
|
|
||||||
$this->containerName = $database->uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): string
|
|
||||||
{
|
|
||||||
return $this->containerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$status = getContainerStatus(
|
|
||||||
server: $this->database->destination->server,
|
|
||||||
container_id: $this->containerName,
|
|
||||||
throwError: false
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($this->database->status === 'running' && $status !== 'running') {
|
|
||||||
if (data_get($this->database, 'environment.project.team')) {
|
|
||||||
// $this->database->environment->project->team->notify(new StatusChanged($this->database));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->database->status !== $status) {
|
|
||||||
$this->database->status = $status;
|
|
||||||
$this->database->save();
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
send_internal_notification('DatabaseContainerStatusJob failed with: ' . $e->getMessage());
|
|
||||||
ray($e->getMessage());
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,13 +5,14 @@ namespace App\Jobs;
|
|||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class DockerCleanupJob implements ShouldQueue
|
class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -36,11 +37,11 @@ class DockerCleanupJob implements ShouldQueue
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ray()->showQueries()->color('orange');
|
// ray()->showQueries()->color('orange');
|
||||||
$servers = Server::all();
|
$servers = Server::all();
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
if (
|
if (
|
||||||
!$server->settings->is_reachable && !$server->settings->is_usable
|
!$server->isFunctional()
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -75,7 +76,7 @@ class DockerCleanupJob implements ShouldQueue
|
|||||||
ray('No need to clean up ' . $server->name)->color('orange');
|
ray('No need to clean up ' . $server->name)->color('orange');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage());
|
send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage());
|
||||||
ray($e->getMessage())->color('orange');
|
ray($e->getMessage())->color('orange');
|
||||||
throw $e;
|
throw $e;
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ namespace App\Jobs;
|
|||||||
|
|
||||||
use App\Actions\Server\UpdateCoolify;
|
use App\Actions\Server\UpdateCoolify;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique
|
class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Jobs;
|
|
||||||
|
|
||||||
use App\Enums\ProxyTypes;
|
|
||||||
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 Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class ProxyContainerStatusJob implements ShouldQueue, ShouldBeUnique
|
|
||||||
{
|
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
||||||
|
|
||||||
public Server $server;
|
|
||||||
public $tries = 1;
|
|
||||||
public $timeout = 120;
|
|
||||||
|
|
||||||
public function __construct(Server $server)
|
|
||||||
{
|
|
||||||
$this->server = $server;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [new WithoutOverlapping($this->server->id)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uniqueId(): int
|
|
||||||
{
|
|
||||||
return $this->server->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
if ($e->getCode() === 1) {
|
|
||||||
$this->server->proxy->status = 'exited';
|
|
||||||
$this->server->save();
|
|
||||||
}
|
|
||||||
send_internal_notification('ProxyContainerStatusJob failed with: ' . $e->getMessage());
|
|
||||||
ray($e->getMessage());
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ namespace App\Jobs;
|
|||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Waitlist;
|
use App\Models\Waitlist;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Mail\Message;
|
use Illuminate\Mail\Message;
|
||||||
@@ -13,7 +14,7 @@ use Illuminate\Queue\InteractsWithQueue;
|
|||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
class SendConfirmationForWaitlistJob implements ShouldQueue
|
class SendConfirmationForWaitlistJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -25,10 +26,8 @@ class SendConfirmationForWaitlistJob implements ShouldQueue
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
|
|
||||||
$confirmation_url = base_url() . '/webhooks/waitlist/confirm?email=' . $this->email . '&confirmation_code=' . $this->uuid;
|
$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;
|
$cancel_url = base_url() . '/webhooks/waitlist/cancel?email=' . $this->email . '&confirmation_code=' . $this->uuid;
|
||||||
|
|
||||||
$mail->view('emails.waitlist-confirmation',
|
$mail->view('emails.waitlist-confirmation',
|
||||||
[
|
[
|
||||||
'confirmation_url' => $confirmation_url,
|
'confirmation_url' => $confirmation_url,
|
||||||
@@ -36,10 +35,10 @@ class SendConfirmationForWaitlistJob implements ShouldQueue
|
|||||||
]);
|
]);
|
||||||
$mail->subject('You are on the waitlist!');
|
$mail->subject('You are on the waitlist!');
|
||||||
send_user_an_email($mail, $this->email);
|
send_user_an_email($mail, $this->email);
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification("SendConfirmationForWaitlistJob failed for {$mail} with error: " . $th->getMessage());
|
send_internal_notification("SendConfirmationForWaitlistJob failed for {$this->email} with error: " . $e->getMessage());
|
||||||
ray($th->getMessage());
|
ray($e->getMessage());
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@
|
|||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class SendMessageToDiscordJob implements ShouldQueue
|
class SendMessageToDiscordJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,15 @@
|
|||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class SendMessageToTelegramJob implements ShouldQueue
|
class SendMessageToTelegramJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ class SendMessageToTelegramJob implements ShouldQueue
|
|||||||
public array $buttons,
|
public array $buttons,
|
||||||
public string $token,
|
public string $token,
|
||||||
public string $chatId,
|
public string $chatId,
|
||||||
|
public ?string $topicId = null,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +65,9 @@ class SendMessageToTelegramJob implements ShouldQueue
|
|||||||
'chat_id' => $this->chatId,
|
'chat_id' => $this->chatId,
|
||||||
'text' => $this->text,
|
'text' => $this->text,
|
||||||
];
|
];
|
||||||
ray($payload);
|
if ($this->topicId) {
|
||||||
|
$payload['message_thread_id'] = $this->topicId;
|
||||||
|
}
|
||||||
$response = Http::post($url, $payload);
|
$response = Http::post($url, $payload);
|
||||||
if ($response->failed()) {
|
if ($response->failed()) {
|
||||||
throw new \Exception('Telegram notification failed with ' . $response->status() . ' status code.' . $response->body());
|
throw new \Exception('Telegram notification failed with ' . $response->status() . ' status code.' . $response->body());
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ namespace App\Jobs;
|
|||||||
|
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class SubscriptionInvoiceFailedJob implements ShouldQueue
|
class SubscriptionInvoiceFailedJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
@@ -33,10 +34,10 @@ class SubscriptionInvoiceFailedJob implements ShouldQueue
|
|||||||
send_user_an_email($mail, $member->email);
|
send_user_an_email($mail, $member->email);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('SubscriptionInvoiceFailedJob failed with: ' . $th->getMessage());
|
send_internal_notification('SubscriptionInvoiceFailedJob failed with: ' . $e->getMessage());
|
||||||
ray($th->getMessage());
|
ray($e->getMessage());
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user