mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
185 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c71d5e2dfb | ||
|
|
36c31dcd67 | ||
|
|
c223408c3c | ||
|
|
3cdccb49ef | ||
|
|
0844645a8b | ||
|
|
30bfad455c | ||
|
|
d84fdc3cd5 | ||
|
|
2b64b9de63 | ||
|
|
74e3524e92 | ||
|
|
d31f75d1ec | ||
|
|
0b34207148 | ||
|
|
3c2beded68 | ||
|
|
f33fdb3bfd | ||
|
|
96a0f29f19 | ||
|
|
49b3a75a8b | ||
|
|
f13fc737f1 | ||
|
|
b7121c5000 | ||
|
|
22a1d3882e | ||
|
|
82f74e2264 | ||
|
|
3dd5699cde | ||
|
|
a198bfc5c0 | ||
|
|
132807b55d | ||
|
|
735081af50 | ||
|
|
31651aeaab | ||
|
|
7f4230d026 | ||
|
|
6333d3fd13 | ||
|
|
fd9dae6e4b | ||
|
|
db5d7857c8 | ||
|
|
0f4eab3cf2 | ||
|
|
75b9f4fcbf | ||
|
|
b9b58b8985 | ||
|
|
64b8aa1c01 | ||
|
|
6b21dc132d | ||
|
|
7aca4930db | ||
|
|
7a92ecfa30 | ||
|
|
171f6f4608 | ||
|
|
d569c8d31f | ||
|
|
51d716253f | ||
|
|
971b17b364 | ||
|
|
db5ff7f16d | ||
|
|
5db045f392 | ||
|
|
42c143d19e | ||
|
|
7c1948ebd9 | ||
|
|
9cd15645a2 | ||
|
|
c0a4a5c2f5 | ||
|
|
518004afbc | ||
|
|
833a4b9367 | ||
|
|
6aa82724b4 | ||
|
|
cfbee40ecd | ||
|
|
79d589c7a9 | ||
|
|
6b82fc3011 | ||
|
|
d921456036 | ||
|
|
6cc93250b8 | ||
|
|
39082541ff | ||
|
|
8cff40fdd4 | ||
|
|
a777db1234 | ||
|
|
3fca169096 | ||
|
|
58b451f616 | ||
|
|
f9b82f711f | ||
|
|
d92b5db320 | ||
|
|
a164e4bf3a | ||
|
|
be3cbd9e21 | ||
|
|
43fed96af1 | ||
|
|
3d3d31ef29 | ||
|
|
d51e70bcaa | ||
|
|
32f4c6c982 | ||
|
|
8f47761200 | ||
|
|
342ebecef2 | ||
|
|
220a8fe2cc | ||
|
|
480cb00098 | ||
|
|
07ed550dc2 | ||
|
|
6045870398 | ||
|
|
2bf102cdf1 | ||
|
|
889a5b2bce | ||
|
|
df964a094b | ||
|
|
e395d4ecee | ||
|
|
d077e0c83c | ||
|
|
d931241edc | ||
|
|
64a9a72457 | ||
|
|
a8417aca16 | ||
|
|
b7b2ecad59 | ||
|
|
dcaa2f4168 | ||
|
|
7c7f54d224 | ||
|
|
b942f8c726 | ||
|
|
f661f23ee5 | ||
|
|
5a631df2a2 | ||
|
|
fc9bb7dac6 | ||
|
|
0a82dc2e8e | ||
|
|
c5eff85c28 | ||
|
|
d1627276a6 | ||
|
|
2be2a2621e | ||
|
|
995c303f27 | ||
|
|
9c03525369 | ||
|
|
afe0673fd1 | ||
|
|
37333f7fbe | ||
|
|
c9160cabc5 | ||
|
|
9e289d5e97 | ||
|
|
901a580e11 | ||
|
|
d196292551 | ||
|
|
8d856b0ec6 | ||
|
|
90fad52760 | ||
|
|
2817875461 | ||
|
|
bcbdee1dcc | ||
|
|
3de4f2805a | ||
|
|
995197cad9 | ||
|
|
89cc4d1df4 | ||
|
|
61f3b3592f | ||
|
|
8cb6f67a60 | ||
|
|
7c580f898c | ||
|
|
9ad6ce5851 | ||
|
|
a66090b594 | ||
|
|
d992a3f7d7 | ||
|
|
b418a78e2e | ||
|
|
cae9ae51ad | ||
|
|
04c92ec4bd | ||
|
|
54834891fb | ||
|
|
c9e2f4244d | ||
|
|
503b86ac13 | ||
|
|
ec051eba38 | ||
|
|
2f50f64ecf | ||
|
|
66fe124dd1 | ||
|
|
87e56c2f66 | ||
|
|
f044b0292c | ||
|
|
ee1d4cd45d | ||
|
|
b25f83e096 | ||
|
|
8b7e1e4169 | ||
|
|
ca9a2cb13a | ||
|
|
93af92743c | ||
|
|
0ebef3792d | ||
|
|
7a3bb14653 | ||
|
|
fbc0a39a1c | ||
|
|
473bad24b7 | ||
|
|
313d968985 | ||
|
|
b5775ff9d2 | ||
|
|
8f66a41c09 | ||
|
|
6721471c63 | ||
|
|
638421de40 | ||
|
|
7bc5338cb3 | ||
|
|
07c9db9b54 | ||
|
|
fafc4fb71e | ||
|
|
6b49d32102 | ||
|
|
3a391aa3cb | ||
|
|
42019321e3 | ||
|
|
b61860b3ab | ||
|
|
91950e1891 | ||
|
|
42715bba50 | ||
|
|
0aacad655d | ||
|
|
289480c954 | ||
|
|
3da7746629 | ||
|
|
8d48051a8d | ||
|
|
ec16c0f0f4 | ||
|
|
19d19112d9 | ||
|
|
d9f1c2a406 | ||
|
|
ef2be40478 | ||
|
|
8e2ee5e5e4 | ||
|
|
c3da2bfade | ||
|
|
bbd6780971 | ||
|
|
0a6dab1f24 | ||
|
|
faa9a982a9 | ||
|
|
de8bb8a951 | ||
|
|
b766eef5ef | ||
|
|
a185787044 | ||
|
|
76f7cd08ee | ||
|
|
460451bcce | ||
|
|
71edb68995 | ||
|
|
e188482247 | ||
|
|
047d320665 | ||
|
|
5c4d9a85be | ||
|
|
56c08056d2 | ||
|
|
b39ac73cd8 | ||
|
|
c9054e7d8c | ||
|
|
adf5c9bd46 | ||
|
|
3ea3674407 | ||
|
|
657c7d8cff | ||
|
|
350e32326f | ||
|
|
1894573c2f | ||
|
|
73f889ac9f | ||
|
|
a336dae84c | ||
|
|
467c826c04 | ||
|
|
a4c164a57e | ||
|
|
8c0c22a925 | ||
|
|
c8d528ffc4 | ||
|
|
bb7b1f9e0c | ||
|
|
f994f83ce1 | ||
|
|
1f37318f79 |
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
@@ -21,6 +21,6 @@ body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Coolify's version (see bottom left corner).
|
||||
description: Coolify's version (see top of your screen).
|
||||
validations:
|
||||
required: true
|
||||
|
||||
20
.github/workflows/coolify-helper-next.yml
vendored
20
.github/workflows/coolify-helper-next.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -40,15 +40,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -64,13 +64,13 @@ jobs:
|
||||
needs: [ amd64, aarch64 ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
|
||||
20
.github/workflows/coolify-helper.yml
vendored
20
.github/workflows/coolify-helper.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -40,15 +40,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -64,13 +64,13 @@ jobs:
|
||||
needs: [ amd64, aarch64 ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
|
||||
20
.github/workflows/coolify-testing-host.yml
vendored
20
.github/workflows/coolify-testing-host.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -40,15 +40,15 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
@@ -64,13 +64,13 @@ jobs:
|
||||
needs: [ amd64, aarch64 ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
|
||||
2
.github/workflows/docker-image.yml
vendored
2
.github/workflows/docker-image.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
$email = 'test@example.com';
|
||||
$user = User::whereEmail($email)->first();
|
||||
$teams = $user->teams;
|
||||
foreach ($teams as $team) {
|
||||
$servers = $team->servers;
|
||||
if ($servers->count() > 0) {
|
||||
foreach ($servers as $server) {
|
||||
dump($server);
|
||||
$server->delete();
|
||||
}
|
||||
}
|
||||
dump($team);
|
||||
$team->delete();
|
||||
}
|
||||
if ($user) {
|
||||
dump($user);
|
||||
$user->delete();
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @label Send Email
|
||||
* @description Send email to all users
|
||||
*/
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
set_transanctional_email_settings();
|
||||
|
||||
$users = User::whereEmail('test@example.com');
|
||||
foreach ($users as $user) {
|
||||
Mail::send([], [], function ($message) use ($user) {
|
||||
$message
|
||||
->to($user->email)
|
||||
->subject("Testing")
|
||||
->text(
|
||||
<<<EOF
|
||||
Hello,
|
||||
|
||||
Welcome to Coolify Cloud.
|
||||
Here is your user id: $user->id
|
||||
|
||||
EOF
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -30,5 +30,5 @@ Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if y
|
||||
Mails are caught by Mailpit: `localhost:8025`
|
||||
|
||||
## New Service Contribution
|
||||
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
|
||||
Check out the docs [here](https://coolify.io/docs/resources/services/add-service).
|
||||
|
||||
|
||||
@@ -28,15 +28,16 @@ https://coolify.io/sponsorships
|
||||
|
||||
Thank you so much!
|
||||
|
||||
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
||||
Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
|
||||
|
||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
||||
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
||||
|
||||
## Github Sponsors ($40+)
|
||||
<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
|
||||
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
||||
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
||||
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
|
||||
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
|
||||
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
|
||||
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
||||
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
|
||||
|
||||
@@ -25,7 +25,8 @@ class StartDatabaseProxy
|
||||
$proxyContainerName = "{$database->uuid}-proxy";
|
||||
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||
$databaseType = $database->databaseType();
|
||||
$network = data_get($database, 'service.destination.network');
|
||||
// $connectPredefined = data_get($database, 'service.connect_to_docker_network');
|
||||
$network = $database->service->uuid;
|
||||
$server = data_get($database, 'service.destination.server');
|
||||
$proxyContainerName = "{$database->service->uuid}-proxy";
|
||||
switch ($databaseType) {
|
||||
@@ -124,6 +125,7 @@ class StartDatabaseProxy
|
||||
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
||||
$nginxconf_base64 = base64_encode($nginxconf);
|
||||
$dockerfile_base64 = base64_encode($dockerfile);
|
||||
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
|
||||
instant_remote_process([
|
||||
"mkdir -p $configuration_dir",
|
||||
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||
|
||||
@@ -17,10 +17,12 @@ class StopDatabaseProxy
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
||||
{
|
||||
$server = data_get($database, 'destination.server');
|
||||
$uuid = $database->uuid;
|
||||
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||
$uuid = $database->service->uuid;
|
||||
$server = data_get($database, 'service.server');
|
||||
}
|
||||
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $server);
|
||||
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
|
||||
$database->is_public = false;
|
||||
$database->save();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ class CheckProxy
|
||||
$server->save();
|
||||
return false;
|
||||
}
|
||||
if ($server->settings->is_cloudflare_tunnel) {
|
||||
return false;
|
||||
}
|
||||
$ip = $server->ip;
|
||||
if ($server->id === 0) {
|
||||
$ip = 'host.docker.internal';
|
||||
@@ -46,14 +49,14 @@ class CheckProxy
|
||||
$port443 = is_resource($connection443) && fclose($connection443);
|
||||
if ($port80) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($port443) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
44
app/Actions/Server/ConfigureCloudflared.php
Normal file
44
app/Actions/Server/ConfigureCloudflared.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ConfigureCloudflared
|
||||
{
|
||||
use AsAction;
|
||||
public function handle(Server $server, string $cloudflare_token)
|
||||
{
|
||||
try {
|
||||
$config = [
|
||||
"services" => [
|
||||
"coolify-cloudflared" => [
|
||||
"container_name" => "coolify-cloudflared",
|
||||
"image" => "cloudflare/cloudflared:latest",
|
||||
"restart" => RESTART_MODE,
|
||||
"network_mode" => "host",
|
||||
"command" => "tunnel run",
|
||||
"environment" => [
|
||||
"TUNNEL_TOKEN={$cloudflare_token}",
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$config = Yaml::dump($config, 12, 2);
|
||||
$docker_compose_yml_base64 = base64_encode($config);
|
||||
$commands = collect([
|
||||
"mkdir -p /tmp/cloudflared && cd /tmp/cloudflared",
|
||||
"echo '$docker_compose_yml_base64' | base64 -d > docker-compose.yml",
|
||||
"docker compose pull",
|
||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||
"docker compose up -d --remove-orphans",
|
||||
]);
|
||||
instant_remote_process($commands, $server);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ class InstallDocker
|
||||
{
|
||||
$supported_os_type = $server->validateOS();
|
||||
if (!$supported_os_type) {
|
||||
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/servers#install-docker-engine-manually">documentation</a>.');
|
||||
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
|
||||
}
|
||||
ray('Installing Docker on server: ' . $server->name . ' (' . $server->ip . ')' . ' with OS type: ' . $supported_os_type);
|
||||
$dockerVersion = '24.0';
|
||||
|
||||
@@ -12,10 +12,12 @@ class UpdateCoolify
|
||||
public ?Server $server = null;
|
||||
public ?string $latestVersion = null;
|
||||
public ?string $currentVersion = null;
|
||||
public bool $async = false;
|
||||
|
||||
public function handle(bool $force)
|
||||
public function handle(bool $force = false, bool $async = false)
|
||||
{
|
||||
try {
|
||||
$this->async = $async;
|
||||
$settings = InstanceSettings::get();
|
||||
ray('Running InstanceAutoUpdateJob');
|
||||
$this->server = Server::find(0);
|
||||
@@ -56,17 +58,31 @@ class UpdateCoolify
|
||||
{
|
||||
if (isDev()) {
|
||||
ray("Running update on local docker container. Updating to $this->latestVersion");
|
||||
remote_process([
|
||||
"sleep 10"
|
||||
], $this->server);
|
||||
if ($this->async) {
|
||||
ray('Running async update');
|
||||
remote_process([
|
||||
"sleep 10"
|
||||
], $this->server);
|
||||
} else {
|
||||
instant_remote_process([
|
||||
"sleep 10"
|
||||
], $this->server);
|
||||
}
|
||||
ray('Update done');
|
||||
return;
|
||||
} else {
|
||||
ray('Running update on production server');
|
||||
remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||
], $this->server);
|
||||
if ($this->async) {
|
||||
remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||
], $this->server);
|
||||
} else {
|
||||
instant_remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||
], $this->server);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class CleanupDatabase extends Command
|
||||
$keep_days = 60;
|
||||
echo "Keep days: $keep_days\n";
|
||||
// Cleanup failed jobs table
|
||||
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(7));
|
||||
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(1));
|
||||
$count = $failed_jobs->count();
|
||||
echo "Delete $count entries from failed_jobs.\n";
|
||||
if ($this->option('yes')) {
|
||||
|
||||
21
app/Console/Commands/Horizon.php
Normal file
21
app/Console/Commands/Horizon.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Horizon extends Command
|
||||
{
|
||||
protected $signature = 'start:horizon';
|
||||
protected $description = 'Start Horizon';
|
||||
public function handle()
|
||||
{
|
||||
if (config('coolify.is_horizon_enabled')) {
|
||||
$this->info('Horizon is enabled. Starting.');
|
||||
$this->call('horizon');
|
||||
exit(0);
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,11 +34,13 @@ class Init extends Command
|
||||
$this->cleanup_stucked_helper_containers();
|
||||
$this->call('cleanup:queue');
|
||||
$this->call('cleanup:stucked-resources');
|
||||
try {
|
||||
$server = Server::find(0)->first();
|
||||
$server->setupDynamicProxyConfiguration();
|
||||
} catch (\Throwable $e) {
|
||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||
if (!isCloud()) {
|
||||
try {
|
||||
$server = Server::find(0)->first();
|
||||
$server->setupDynamicProxyConfiguration();
|
||||
} catch (\Throwable $e) {
|
||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
|
||||
$settings = InstanceSettings::get();
|
||||
|
||||
21
app/Console/Commands/Scheduler.php
Normal file
21
app/Console/Commands/Scheduler.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Scheduler extends Command
|
||||
{
|
||||
protected $signature = 'start:scheduler';
|
||||
protected $description = 'Start Scheduler';
|
||||
public function handle()
|
||||
{
|
||||
if (config('coolify.is_scheduler_enabled')) {
|
||||
$this->info('Scheduler is enabled. Starting.');
|
||||
$this->call('schedule:work');
|
||||
exit(0);
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,9 +48,7 @@ class Kernel extends ConsoleKernel
|
||||
$this->pull_helper_image($schedule);
|
||||
$this->check_scheduled_tasks($schedule);
|
||||
|
||||
if (!isCloud()) {
|
||||
$schedule->command('cleanup:database --yes')->daily();
|
||||
}
|
||||
$schedule->command('cleanup:database --yes')->daily();
|
||||
}
|
||||
}
|
||||
private function pull_helper_image($schedule)
|
||||
@@ -72,43 +70,14 @@ class Kernel extends ConsoleKernel
|
||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||
}
|
||||
foreach ($containerServers as $server) {
|
||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||
$schedule->job(new ContainerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
||||
if ($server->isLogDrainEnabled()) {
|
||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyTwoMinutes()->onOneServer();
|
||||
}
|
||||
}
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
||||
$schedule->job(new ServerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
||||
}
|
||||
// Delayed Jobs
|
||||
// foreach ($containerServers as $server) {
|
||||
// $schedule
|
||||
// ->call(function () use ($server) {
|
||||
// $randomSeconds = rand(1, 40);
|
||||
// $job = new ContainerStatusJob($server);
|
||||
// $job->delay($randomSeconds);
|
||||
// ray('dispatching container status job in ' . $randomSeconds . ' seconds');
|
||||
// dispatch($job);
|
||||
// })->name('container-status-' . $server->id)->everyMinute()->onOneServer();
|
||||
// if ($server->isLogDrainEnabled()) {
|
||||
// $schedule
|
||||
// ->call(function () use ($server) {
|
||||
// $randomSeconds = rand(1, 40);
|
||||
// $job = new CheckLogDrainContainerJob($server);
|
||||
// $job->delay($randomSeconds);
|
||||
// dispatch($job);
|
||||
// })->name('log-drain-container-check-' . $server->id)->everyMinute()->onOneServer();
|
||||
// }
|
||||
// }
|
||||
// foreach ($servers as $server) {
|
||||
// $schedule
|
||||
// ->call(function () use ($server) {
|
||||
// $randomSeconds = rand(1, 40);
|
||||
// $job = new ServerStatusJob($server);
|
||||
// $job->delay($randomSeconds);
|
||||
// dispatch($job);
|
||||
// })->name('server-status-job-' . $server->id)->everyMinute()->onOneServer();
|
||||
// }
|
||||
}
|
||||
private function instance_auto_update($schedule)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ class Deploy extends Controller
|
||||
$force = $request->query->get('force') ?? false;
|
||||
|
||||
if ($uuids && $tags) {
|
||||
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||
}
|
||||
if (is_null($teamId)) {
|
||||
return invalid_token();
|
||||
@@ -54,7 +54,7 @@ class Deploy extends Controller
|
||||
} else if ($uuids) {
|
||||
return $this->by_uuids($uuids, $teamId, $force);
|
||||
}
|
||||
return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||
return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||
}
|
||||
private function by_uuids(string $uuid, int $teamId, bool $force = false)
|
||||
{
|
||||
@@ -62,7 +62,7 @@ class Deploy extends Controller
|
||||
$uuids = collect(array_filter($uuids));
|
||||
|
||||
if (count($uuids) === 0) {
|
||||
return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||
return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||
}
|
||||
$deployments = collect();
|
||||
$payload = collect();
|
||||
@@ -81,7 +81,7 @@ class Deploy extends Controller
|
||||
$payload->put('deployments', $deployments->toArray());
|
||||
return response()->json($payload->toArray(), 200);
|
||||
}
|
||||
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
|
||||
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
|
||||
}
|
||||
public function by_tags(string $tags, int $team_id, bool $force = false)
|
||||
{
|
||||
@@ -89,7 +89,7 @@ class Deploy extends Controller
|
||||
$tags = collect(array_filter($tags));
|
||||
|
||||
if (count($tags) === 0) {
|
||||
return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||
return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||
}
|
||||
$message = collect([]);
|
||||
$deployments = collect();
|
||||
@@ -127,7 +127,7 @@ class Deploy extends Controller
|
||||
return response()->json($payload->toArray(), 200);
|
||||
}
|
||||
|
||||
return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
|
||||
return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
|
||||
}
|
||||
public function deploy_resource($resource, bool $force = false): array
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ class Team extends Controller
|
||||
$teams = auth()->user()->teams;
|
||||
$team = $teams->where('id', $id)->first();
|
||||
if (is_null($team)) {
|
||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id"], 404);
|
||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid"], 404);
|
||||
}
|
||||
return response()->json($team);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class Team extends Controller
|
||||
$teams = auth()->user()->teams;
|
||||
$team = $teams->where('id', $id)->first();
|
||||
if (is_null($team)) {
|
||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api/team-by-id-members"], 404);
|
||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid-members"], 404);
|
||||
}
|
||||
return response()->json($team->members);
|
||||
}
|
||||
|
||||
35
app/Http/Controllers/OauthController.php
Normal file
35
app/Http/Controllers/OauthController.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class OauthController extends Controller {
|
||||
public function redirect(string $provider)
|
||||
{
|
||||
$socialite_provider = get_socialite_provider($provider);
|
||||
return $socialite_provider->redirect();
|
||||
}
|
||||
|
||||
public function callback(string $provider)
|
||||
{
|
||||
try {
|
||||
$oauthUser = get_socialite_provider($provider)->user();
|
||||
$user = User::whereEmail($oauthUser->email)->first();
|
||||
if (!$user) {
|
||||
$user = User::create([
|
||||
'name' => $oauthUser->name,
|
||||
'email' => $oauthUser->email,
|
||||
]);
|
||||
}
|
||||
Auth::login($user);
|
||||
return redirect('/');
|
||||
} catch (\Exception $e) {
|
||||
ray($e->getMessage());
|
||||
return redirect()->route('login')->withErrors([__('auth.failed.callback')]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ class Github extends Controller
|
||||
public function manual(Request $request)
|
||||
{
|
||||
try {
|
||||
ray($request);
|
||||
$return_payloads = collect([]);
|
||||
$x_github_delivery = request()->header('X-GitHub-Delivery');
|
||||
if (app()->isDownForMaintenance()) {
|
||||
@@ -68,6 +67,10 @@ class Github extends Controller
|
||||
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||
$branch = Str::after($branch, 'refs/heads/');
|
||||
}
|
||||
$added_files = data_get($payload, 'commits.*.added');
|
||||
$removed_files = data_get($payload, 'commits.*.removed');
|
||||
$modified_files = data_get($payload, 'commits.*.modified');
|
||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||
ray('Manual Webhook GitHub Push Event with branch: ' . $branch);
|
||||
}
|
||||
if ($x_github_event === 'pull_request') {
|
||||
@@ -118,24 +121,41 @@ class Github extends Controller
|
||||
}
|
||||
if ($x_github_event === 'push') {
|
||||
if ($application->isDeployable()) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
]);
|
||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
} else {
|
||||
$paths = str($application->watch_paths)->explode("\n");
|
||||
$return_payloads->push([
|
||||
'status' => 'failed',
|
||||
'message' => 'Changed files do not match watch paths. Ignoring deployment.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'details' => [
|
||||
'changed_files' => $changed_files,
|
||||
'watch_paths' => $paths,
|
||||
],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Deployments disabled.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -266,6 +286,10 @@ class Github extends Controller
|
||||
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||
$branch = Str::after($branch, 'refs/heads/');
|
||||
}
|
||||
$added_files = data_get($payload, 'commits.*.added');
|
||||
$removed_files = data_get($payload, 'commits.*.removed');
|
||||
$modified_files = data_get($payload, 'commits.*.modified');
|
||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||
ray('Webhook GitHub Push Event: ' . $id . ' with branch: ' . $branch);
|
||||
}
|
||||
if ($x_github_event === 'pull_request') {
|
||||
@@ -298,32 +322,50 @@ class Github extends Controller
|
||||
$isFunctional = $application->destination->server->isFunctional();
|
||||
if (!$isFunctional) {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Server is not functional.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
if ($x_github_event === 'push') {
|
||||
if ($application->isDeployable()) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true
|
||||
);
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
]);
|
||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
} else {
|
||||
$paths = str($application->watch_paths)->explode("\n");
|
||||
$return_payloads->push([
|
||||
'status' => 'failed',
|
||||
'message' => 'Changed files do not match watch paths. Ignoring deployment.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'details' => [
|
||||
'changed_files' => $changed_files,
|
||||
'watch_paths' => $paths,
|
||||
],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Deployments disabled.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@ class Gitlab extends Controller
|
||||
]);
|
||||
return response($return_payloads);
|
||||
}
|
||||
$added_files = data_get($payload, 'commits.*.added');
|
||||
$removed_files = data_get($payload, 'commits.*.removed');
|
||||
$modified_files = data_get($payload, 'commits.*.modified');
|
||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||
ray('Manual Webhook GitLab Push Event with branch: ' . $branch);
|
||||
}
|
||||
if ($x_gitlab_event === 'merge_request') {
|
||||
@@ -113,19 +117,41 @@ class Gitlab extends Controller
|
||||
}
|
||||
if ($x_gitlab_event === 'push') {
|
||||
if ($application->isDeployable()) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true
|
||||
);
|
||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
} else {
|
||||
$paths = str($application->watch_paths)->explode("\n");
|
||||
$return_payloads->push([
|
||||
'status' => 'failed',
|
||||
'message' => 'Changed files do not match watch paths. Ignoring deployment.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'details' => [
|
||||
'changed_files' => $changed_files,
|
||||
'watch_paths' => $paths,
|
||||
],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Deployments disabled',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
ray('Deployments disabled for ' . $application->name);
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->build_server = $this->server;
|
||||
$this->original_server = $this->server;
|
||||
}
|
||||
if ($this->restart_only && $this->application->build_pack !== 'dockerimage') {
|
||||
if ($this->restart_only && $this->application->build_pack !== 'dockerimage' && $this->application->build_pack !== 'dockerfile') {
|
||||
$this->just_restart();
|
||||
if ($this->server->isProxyShouldRun()) {
|
||||
dispatch(new ContainerStatusJob($this->server));
|
||||
@@ -298,6 +298,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
"ignore_errors" => true,
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
// $this->execute_remote_command(
|
||||
// [
|
||||
// "docker image prune -f >/dev/null 2>&1",
|
||||
@@ -305,6 +307,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
// "ignore_errors" => true,
|
||||
// ]
|
||||
// );
|
||||
|
||||
|
||||
ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
|
||||
}
|
||||
}
|
||||
@@ -322,17 +326,19 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
],
|
||||
);
|
||||
$this->generate_image_names();
|
||||
if (!$this->force_rebuild) {
|
||||
$this->check_image_locally_or_remotely();
|
||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||
$this->create_workdir();
|
||||
$this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||
$this->generate_compose_file();
|
||||
$this->push_to_docker_registry();
|
||||
$this->rolling_update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Always rebuild dockerfile based container.
|
||||
// if (!$this->force_rebuild) {
|
||||
// $this->check_image_locally_or_remotely();
|
||||
// if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||
// $this->create_workdir();
|
||||
// $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||
// $this->generate_compose_file();
|
||||
// $this->push_to_docker_registry();
|
||||
// $this->rolling_update();
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
$this->generate_compose_file();
|
||||
$this->generate_build_env_variables();
|
||||
$this->add_build_env_variables_to_dockerfile();
|
||||
@@ -417,15 +423,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
"docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
|
||||
]);
|
||||
}
|
||||
$this->write_deployment_configurations();
|
||||
|
||||
// Start compose file
|
||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||
if ($this->docker_compose_custom_start_command) {
|
||||
$this->execute_remote_command(
|
||||
["cd {$this->basedir} && {$this->docker_compose_custom_start_command}", "hidden" => true],
|
||||
[executeInDocker($this->deployment_uuid, "cd {$this->workdir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
|
||||
);
|
||||
$this->write_deployment_configurations();
|
||||
} else {
|
||||
$this->write_deployment_configurations();
|
||||
$server_workdir = $this->application->workdir();
|
||||
ray("SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d");
|
||||
$this->execute_remote_command(
|
||||
@@ -437,14 +444,15 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
|
||||
);
|
||||
$this->write_deployment_configurations();
|
||||
} else {
|
||||
$this->execute_remote_command(
|
||||
[executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
|
||||
);
|
||||
$this->write_deployment_configurations();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->application_deployment_queue->addLogEntry("New container started.");
|
||||
}
|
||||
private function deploy_dockerfile_buildpack()
|
||||
@@ -731,7 +739,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
|
||||
}
|
||||
if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) {
|
||||
$this->application_deployment_queue->addLogEntry("There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/frameworks/laravel#requirements", 'stderr');
|
||||
$this->application_deployment_queue->addLogEntry("There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/resources/laravel", 'stderr');
|
||||
}
|
||||
}
|
||||
private function rolling_update()
|
||||
@@ -749,7 +757,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->write_deployment_configurations();
|
||||
$this->server = $this->original_server;
|
||||
}
|
||||
if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || $this->pull_request_id !== 0) {
|
||||
if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || $this->pull_request_id !== 0 || str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) {
|
||||
$this->application_deployment_queue->addLogEntry("----------------------------------------");
|
||||
if (count($this->application->ports_mappings_array) > 0) {
|
||||
$this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
|
||||
@@ -761,6 +769,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->application->settings->is_consistent_container_name_enabled = true;
|
||||
$this->application_deployment_queue->addLogEntry("Pull request deployment, rolling update is not supported.");
|
||||
}
|
||||
if (str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) {
|
||||
$this->application_deployment_queue->addLogEntry("Custom IP address is set, rolling update is not supported.");
|
||||
}
|
||||
$this->stop_running_container(force: true);
|
||||
$this->start_by_compose_file();
|
||||
} else {
|
||||
@@ -819,6 +830,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
private function deploy_pull_request()
|
||||
{
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->deploy_docker_compose_buildpack();
|
||||
return;
|
||||
}
|
||||
if ($this->use_build_server) {
|
||||
$this->server = $this->build_server;
|
||||
}
|
||||
@@ -885,6 +900,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->application->additional_networks->count() === 0) {
|
||||
return;
|
||||
}
|
||||
if ($this->pull_request_id !== 0) {
|
||||
return;
|
||||
}
|
||||
$destination_ids = $this->application->additional_networks->pluck('id');
|
||||
if ($this->server->isSwarm()) {
|
||||
$this->application_deployment_queue->addLogEntry("Additional destinations are not supported in swarm mode.");
|
||||
@@ -1128,9 +1146,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->custom_healthcheck_found = false;
|
||||
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile', "ignore_errors" => true
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), "hidden" => true, "save" => 'dockerfile_from_repo', "ignore_errors" => true
|
||||
]);
|
||||
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
||||
$dockerfile = collect(Str::of($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
|
||||
if (str($dockerfile)->contains('HEALTHCHECK')) {
|
||||
$this->custom_healthcheck_found = true;
|
||||
}
|
||||
@@ -1274,10 +1292,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
// }
|
||||
|
||||
if ($this->pull_request_id === 0) {
|
||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||
if ((bool)$this->application->settings->is_consistent_container_name_enabled) {
|
||||
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
||||
data_forget($docker_compose, 'services.' . $this->container_name);
|
||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||
if (count($custom_compose) > 0) {
|
||||
$ipv4 = data_get($custom_compose, 'ip.0');
|
||||
$ipv6 = data_get($custom_compose, 'ip6.0');
|
||||
@@ -1295,7 +1312,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
||||
}
|
||||
} else {
|
||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||
if (count($custom_compose) > 0) {
|
||||
$ipv4 = data_get($custom_compose, 'ip.0');
|
||||
$ipv6 = data_get($custom_compose, 'ip6.0');
|
||||
@@ -1359,23 +1375,53 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$environment_variables = collect();
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
// This is necessary because we have to escape the value of the environment variable
|
||||
// but only if the environment variable is created after 4.0.0-beta.240
|
||||
// when I implemented the escaping feature.
|
||||
|
||||
// Old environment variables are not escaped, because it could break the application
|
||||
// as the application could expect the unescaped value.
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
foreach ($this->application->nixpacks_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->runtime_environment_variables_preview as $env) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
$environment_variables->push("$env->key=$real_value");
|
||||
}
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('PORT'))->isEmpty()) {
|
||||
$environment_variables->push("PORT={$ports[0]}");
|
||||
}
|
||||
// Add HOST if not exists
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('HOST'))->isEmpty()) {
|
||||
$environment_variables->push("HOST=0.0.0.0");
|
||||
}
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('SOURCE_COMMIT'))->isEmpty()) {
|
||||
if (!is_null($this->commit)) {
|
||||
$environment_variables->push("SOURCE_COMMIT={$this->commit}");
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Actions\Proxy\StartProxy;
|
||||
use App\Actions\Shared\ComplexStatusCheck;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Notifications\Container\ContainerRestarted;
|
||||
use App\Notifications\Container\ContainerStopped;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@@ -149,31 +150,58 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
} else {
|
||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||
$type = data_get($labels, 'coolify.type');
|
||||
|
||||
if ($uuid) {
|
||||
$database = $databases->where('uuid', $uuid)->first();
|
||||
if ($database) {
|
||||
$isPublic = data_get($database, 'is_public');
|
||||
$foundDatabases[] = $database->id;
|
||||
$statusFromDb = $database->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$database->update(['status' => $containerStatus]);
|
||||
}
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
if ($type === 'service') {
|
||||
$database_id = data_get($labels, 'coolify.service.subId');
|
||||
if ($database_id) {
|
||||
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
||||
if ($service_db) {
|
||||
$uuid = $service_db->service->uuid;
|
||||
$isPublic = data_get($service_db, 'is_public');
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($service_db);
|
||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||
}
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($database);
|
||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Notify user that this container should not be there.
|
||||
$database = $databases->where('uuid', $uuid)->first();
|
||||
if ($database) {
|
||||
$isPublic = data_get($database, 'is_public');
|
||||
$foundDatabases[] = $database->id;
|
||||
$statusFromDb = $database->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$database->update(['status' => $containerStatus]);
|
||||
}
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($database);
|
||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Notify user that this container should not be there.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (data_get($container, 'Name') === '/coolify-db') {
|
||||
$foundDatabases[] = 0;
|
||||
|
||||
@@ -95,7 +95,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$databaseType = $this->database->databaseType();
|
||||
$serviceUuid = $this->database->service->uuid;
|
||||
$serviceName = str($this->database->service->name)->slug();
|
||||
if ($databaseType === 'standalone-postgresql') {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||
$commands[] = "docker exec $this->container_name env | grep POSTGRES_";
|
||||
@@ -120,7 +120,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
$databasesToBackup = $this->database->postgres_user;
|
||||
}
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
} else if (str($databaseType)->contains('mysql')) {
|
||||
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||
$commands[] = "docker exec $this->container_name env | grep MYSQL_";
|
||||
@@ -143,7 +143,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
throw new \Exception('MYSQL_DATABASE not found');
|
||||
}
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
} else if (str($databaseType)->contains('mariadb')) {
|
||||
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||
$commands[] = "docker exec $this->container_name env";
|
||||
@@ -190,32 +190,32 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
|
||||
if (is_null($databasesToBackup)) {
|
||||
if ($databaseType === 'standalone-postgresql') {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
$databasesToBackup = [$this->database->postgres_db];
|
||||
} else if ($databaseType === 'standalone-mongodb') {
|
||||
} else if (str($databaseType)->contains('mongodb')) {
|
||||
$databasesToBackup = ['*'];
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
} else if (str($databaseType)->contains('mysql')) {
|
||||
$databasesToBackup = [$this->database->mysql_database];
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
} else if (str($databaseType)->contains('mariadb')) {
|
||||
$databasesToBackup = [$this->database->mariadb_database];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ($databaseType === 'standalone-postgresql') {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} else if ($databaseType === 'standalone-mongodb') {
|
||||
} else if (str($databaseType)->contains('mongodb')) {
|
||||
// Format: db1:collection1,collection2|db2:collection3,collection4
|
||||
$databasesToBackup = explode('|', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
ray($databasesToBackup);
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
} else if (str($databaseType)->contains('mysql')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
} else if (str($databaseType)->contains('mariadb')) {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
@@ -235,7 +235,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$size = 0;
|
||||
ray('Backing up ' . $database);
|
||||
try {
|
||||
if ($databaseType === 'standalone-postgresql') {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
$this->backup_file = "/pg-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
@@ -244,7 +244,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
$this->backup_standalone_postgresql($database);
|
||||
} else if ($databaseType === 'standalone-mongodb') {
|
||||
} else if (str($databaseType)->contains('mongodb')) {
|
||||
if ($database === '*') {
|
||||
$database = 'all';
|
||||
$databaseName = 'all';
|
||||
@@ -263,7 +263,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
$this->backup_standalone_mongodb($database);
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
} else if (str($databaseType)->contains('mysql')) {
|
||||
$this->backup_file = "/mysql-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
@@ -272,7 +272,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
$this->backup_standalone_mysql($database);
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
} else if (str($databaseType)->contains('mariadb')) {
|
||||
$this->backup_file = "/mariadb-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
|
||||
@@ -23,6 +23,6 @@ class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncr
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
UpdateCoolify::run($this->force);
|
||||
UpdateCoolify::run(force: $this->force, async: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ class ActivityMonitor extends Component
|
||||
public $activityId;
|
||||
public $eventToDispatch = 'activityFinished';
|
||||
public $isPollingActive = false;
|
||||
public bool $showWaiting = false;
|
||||
|
||||
protected $activity;
|
||||
protected $listeners = ['activityMonitor' => 'newMonitorActivity'];
|
||||
|
||||
@@ -8,7 +8,31 @@ use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public $users = [];
|
||||
public $active_subscribers = [];
|
||||
public $inactive_subscribers = [];
|
||||
public $search = '';
|
||||
public function submitSearch() {
|
||||
if ($this->search !== "") {
|
||||
$this->inactive_subscribers = User::whereDoesntHave('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->where(function ($query) {
|
||||
$query->where('name', 'like', "%{$this->search}%")
|
||||
->orWhere('email', 'like', "%{$this->search}%");
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
$this->active_subscribers = User::whereHas('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->where(function ($query) {
|
||||
$query->where('name', 'like', "%{$this->search}%")
|
||||
->orWhere('email', 'like', "%{$this->search}%");
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
} else {
|
||||
$this->getSubscribers();
|
||||
}
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
if (!isCloud()) {
|
||||
@@ -17,7 +41,15 @@ class Index extends Component
|
||||
if (auth()->user()->id !== 0) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->users = User::whereHas('teams', function ($query) {
|
||||
$this->getSubscribers();
|
||||
}
|
||||
public function getSubscribers() {
|
||||
$this->inactive_subscribers = User::whereDoesntHave('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
$this->active_subscribers = User::whereHas('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
|
||||
@@ -2,22 +2,24 @@
|
||||
|
||||
namespace App\Livewire\Boarding;
|
||||
|
||||
use App\Actions\Server\InstallDocker;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
protected $listeners = ['serverInstalled' => 'validateServer'];
|
||||
|
||||
public string $currentState = 'welcome';
|
||||
|
||||
public ?string $selectedServerType = null;
|
||||
public ?Collection $privateKeys = null;
|
||||
|
||||
public ?int $selectedExistingPrivateKey = null;
|
||||
public ?string $privateKeyType = null;
|
||||
public ?string $privateKey = null;
|
||||
@@ -27,6 +29,7 @@ class Index extends Component
|
||||
public ?PrivateKey $createdPrivateKey = null;
|
||||
|
||||
public ?Collection $servers = null;
|
||||
|
||||
public ?int $selectedExistingServer = null;
|
||||
public ?string $remoteServerName = null;
|
||||
public ?string $remoteServerDescription = null;
|
||||
@@ -38,7 +41,8 @@ class Index extends Component
|
||||
public ?Server $createdServer = null;
|
||||
|
||||
public Collection $projects;
|
||||
public ?int $selectedExistingProject = null;
|
||||
|
||||
public ?int $selectedProject = null;
|
||||
public ?Project $createdProject = null;
|
||||
|
||||
public bool $dockerInstallationStarted = false;
|
||||
@@ -62,6 +66,26 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
$this->remoteServerDescription = 'Created by Coolify';
|
||||
$this->remoteServerHost = 'coolify-testing-host';
|
||||
}
|
||||
// if ($this->currentState === 'create-project') {
|
||||
// $this->getProjects();
|
||||
// }
|
||||
// if ($this->currentState === 'create-resource') {
|
||||
// $this->selectExistingServer();
|
||||
// $this->selectExistingProject();
|
||||
// }
|
||||
// if ($this->currentState === 'private-key') {
|
||||
// $this->setServerType('remote');
|
||||
// }
|
||||
// if ($this->currentState === 'create-server') {
|
||||
// $this->selectExistingPrivateKey();
|
||||
// }
|
||||
// if ($this->currentState === 'validate-server') {
|
||||
// $this->selectExistingServer();
|
||||
// }
|
||||
// if ($this->currentState === 'select-existing-server') {
|
||||
// $this->selectExistingServer();
|
||||
// }
|
||||
|
||||
}
|
||||
public function explanation()
|
||||
{
|
||||
@@ -89,6 +113,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
$this->selectedServerType = $type;
|
||||
if ($this->selectedServerType === 'localhost') {
|
||||
$this->createdServer = Server::find(0);
|
||||
$this->selectedExistingServer = 0;
|
||||
if (!$this->createdServer) {
|
||||
return $this->dispatch('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
|
||||
}
|
||||
@@ -137,6 +162,10 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
}
|
||||
public function selectExistingPrivateKey()
|
||||
{
|
||||
if (is_null($this->selectedExistingPrivateKey)) {
|
||||
$this->restartBoarding();
|
||||
return;
|
||||
}
|
||||
$this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey);
|
||||
$this->privateKey = $this->createdPrivateKey->private_key;
|
||||
$this->currentState = 'create-server';
|
||||
@@ -196,6 +225,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
|
||||
$this->createdServer->settings->save();
|
||||
$this->createdServer->addInitialNetwork();
|
||||
$this->selectedExistingServer = $this->createdServer->id;
|
||||
$this->currentState = 'validate-server';
|
||||
}
|
||||
public function installServer()
|
||||
@@ -249,13 +279,13 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
{
|
||||
$this->projects = Project::ownedByCurrentTeam(['name'])->get();
|
||||
if ($this->projects->count() > 0) {
|
||||
$this->selectedExistingProject = $this->projects->first()->id;
|
||||
$this->selectedProject = $this->projects->first()->id;
|
||||
}
|
||||
$this->currentState = 'create-project';
|
||||
}
|
||||
public function selectExistingProject()
|
||||
{
|
||||
$this->createdProject = Project::find($this->selectedExistingProject);
|
||||
$this->createdProject = Project::find($this->selectedProject);
|
||||
$this->currentState = 'create-resource';
|
||||
}
|
||||
public function createNewProject()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -13,9 +14,11 @@ class Dashboard extends Component
|
||||
{
|
||||
public $projects = [];
|
||||
public Collection $servers;
|
||||
public Collection $private_keys;
|
||||
public $deployments_per_server;
|
||||
public function mount()
|
||||
{
|
||||
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
|
||||
$this->servers = Server::ownedByCurrentTeam()->get();
|
||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||
$this->get_deployments();
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Livewire\Destination\New;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
@@ -14,7 +14,7 @@ class Docker extends Component
|
||||
public string $name;
|
||||
public string $network;
|
||||
|
||||
public Collection $servers;
|
||||
public ?Collection $servers = null;
|
||||
public Server $server;
|
||||
public ?int $server_id = null;
|
||||
public bool $is_swarm = false;
|
||||
@@ -34,6 +34,9 @@ class Docker extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (is_null($this->servers)) {
|
||||
$this->servers = Server::isReachable()->get();
|
||||
}
|
||||
if (request()->query('server_id')) {
|
||||
$this->server_id = request()->query('server_id');
|
||||
} else {
|
||||
@@ -46,7 +49,9 @@ class Docker extends Component
|
||||
} else {
|
||||
$this->network = new Cuid2(7);
|
||||
}
|
||||
$this->name = str("{$this->servers->first()->name}-{$this->network}")->kebab();
|
||||
if ($this->servers->count() > 0) {
|
||||
$this->name = str("{$this->servers->first()->name}-{$this->network}")->kebab();
|
||||
}
|
||||
}
|
||||
|
||||
public function generate_name()
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace App\Livewire\Destination;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -11,6 +13,40 @@ class Show extends Component
|
||||
public Server $server;
|
||||
public Collection|array $networks = [];
|
||||
|
||||
private function createNetworkAndAttachToProxy()
|
||||
{
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||
}
|
||||
public function add($name)
|
||||
{
|
||||
if ($this->server->isSwarm()) {
|
||||
$found = $this->server->swarmDockers()->where('network', $name)->first();
|
||||
if ($found) {
|
||||
$this->dispatch('error', 'Network already added to this server.');
|
||||
return;
|
||||
} else {
|
||||
SwarmDocker::create([
|
||||
'name' => $this->server->name . "-" . $name,
|
||||
'network' => $this->name,
|
||||
'server_id' => $this->server->id,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$found = $this->server->standaloneDockers()->where('network', $name)->first();
|
||||
if ($found) {
|
||||
$this->dispatch('error', 'Network already added to this server.');
|
||||
return;
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'name' => $this->server->name . "-" . $name,
|
||||
'network' => $name,
|
||||
'server_id' => $this->server->id,
|
||||
]);
|
||||
}
|
||||
$this->createNetworkAndAttachToProxy();
|
||||
}
|
||||
}
|
||||
public function scan()
|
||||
{
|
||||
if ($this->server->isSwarm()) {
|
||||
@@ -26,6 +62,8 @@ class Show extends Component
|
||||
});
|
||||
if ($this->networks->count() === 0) {
|
||||
$this->dispatch('success', 'No new networks found.');
|
||||
return;
|
||||
}
|
||||
$this->dispatch('success', 'Scan done.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,6 @@ class LayoutPopups extends Component
|
||||
{
|
||||
$this->dispatch('success', 'Realtime events configured!');
|
||||
}
|
||||
public function disableSponsorship()
|
||||
{
|
||||
auth()->user()->update(['is_notification_sponsorship_enabled' => false]);
|
||||
}
|
||||
public function disableNotifications()
|
||||
{
|
||||
auth()->user()->update(['is_notification_notifications_enabled' => false]);
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.layout-popups');
|
||||
|
||||
@@ -6,7 +6,7 @@ use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Component;
|
||||
|
||||
class DiscordSettings extends Component
|
||||
class Discord extends Component
|
||||
{
|
||||
public Team $team;
|
||||
protected $rules = [
|
||||
@@ -55,4 +55,8 @@ class DiscordSettings extends Component
|
||||
$this->team?->notify(new Test());
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.notifications.discord');
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
namespace App\Livewire\Notifications;
|
||||
|
||||
use Livewire\Component;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Component;
|
||||
use Log;
|
||||
|
||||
class EmailSettings extends Component
|
||||
class Email extends Component
|
||||
{
|
||||
public Team $team;
|
||||
public string $emails;
|
||||
@@ -119,16 +118,18 @@ class EmailSettings extends Component
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'team.smtp_from_address' => 'required|email',
|
||||
'team.smtp_from_name' => 'required',
|
||||
'team.smtp_host' => 'required',
|
||||
'team.smtp_port' => 'required|numeric',
|
||||
'team.smtp_encryption' => 'nullable',
|
||||
'team.smtp_username' => 'nullable',
|
||||
'team.smtp_password' => 'nullable',
|
||||
'team.smtp_timeout' => 'nullable',
|
||||
]);
|
||||
if (!$this->team->use_instance_email_settings) {
|
||||
$this->validate([
|
||||
'team.smtp_from_address' => 'required|email',
|
||||
'team.smtp_from_name' => 'required',
|
||||
'team.smtp_host' => 'required',
|
||||
'team.smtp_port' => 'required|numeric',
|
||||
'team.smtp_encryption' => 'nullable',
|
||||
'team.smtp_username' => 'nullable',
|
||||
'team.smtp_password' => 'nullable',
|
||||
'team.smtp_timeout' => 'nullable',
|
||||
]);
|
||||
}
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
@@ -189,4 +190,8 @@ class EmailSettings extends Component
|
||||
}
|
||||
$this->dispatch('error', 'Instance SMTP/Resend settings are not enabled.');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.notifications.email');
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,9 @@ use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Component;
|
||||
|
||||
class TelegramSettings extends Component
|
||||
class Telegram extends Component
|
||||
{
|
||||
|
||||
public Team $team;
|
||||
protected $rules = [
|
||||
'team.telegram_enabled' => 'nullable|boolean',
|
||||
@@ -61,4 +62,8 @@ class TelegramSettings extends Component
|
||||
$this->team?->notify(new Test());
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.notifications.telegram');
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,8 @@ class Index extends Component
|
||||
public int $userId;
|
||||
public string $email;
|
||||
|
||||
#[Validate('required')]
|
||||
public string $current_password;
|
||||
#[Validate('required|min:8')]
|
||||
public string $new_password;
|
||||
#[Validate('required|min:8|same:new_password')]
|
||||
public string $new_password_confirmation;
|
||||
|
||||
#[Validate('required')]
|
||||
@@ -29,7 +26,9 @@ class Index extends Component
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->validate([
|
||||
'name' => 'required',
|
||||
]);
|
||||
auth()->user()->update([
|
||||
'name' => $this->name,
|
||||
]);
|
||||
@@ -42,7 +41,11 @@ class Index extends Component
|
||||
public function resetPassword()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->validate([
|
||||
'current_password' => 'required',
|
||||
'new_password' => 'required|min:8',
|
||||
'new_password_confirmation' => 'required|min:8|same:new_password',
|
||||
]);
|
||||
if (!Hash::check($this->current_password, auth()->user()->password)) {
|
||||
$this->dispatch('error', 'Current password is incorrect.');
|
||||
return;
|
||||
|
||||
@@ -9,7 +9,7 @@ use Livewire\Component;
|
||||
class Index extends Component
|
||||
{
|
||||
public Application $application;
|
||||
public array|Collection $deployments = [];
|
||||
public ?Collection $deployments;
|
||||
public int $deployments_count = 0;
|
||||
public string $current_url;
|
||||
public int $skip = 0;
|
||||
@@ -48,9 +48,9 @@ class Index extends Component
|
||||
}
|
||||
private function show_more()
|
||||
{
|
||||
if (count($this->deployments) !== 0) {
|
||||
if ($this->deployments->count() !== 0) {
|
||||
$this->show_next = true;
|
||||
if (count($this->deployments) < $this->default_take) {
|
||||
if ($this->deployments->count() < $this->default_take) {
|
||||
$this->show_next = false;
|
||||
}
|
||||
return;
|
||||
@@ -63,7 +63,6 @@ class Index extends Component
|
||||
}
|
||||
public function previous_page(?int $take = null)
|
||||
{
|
||||
|
||||
if ($take) {
|
||||
$this->skip = $this->skip - $take;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ class General extends Component
|
||||
'application.settings.is_static' => 'boolean|required',
|
||||
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
|
||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||
'application.watch_paths' => 'nullable',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'application.name' => 'name',
|
||||
@@ -108,6 +109,7 @@ class General extends Component
|
||||
'application.settings.is_static' => 'Is static',
|
||||
'application.settings.is_raw_compose_deployment_enabled' => 'Is raw compose deployment enabled',
|
||||
'application.settings.is_build_server_enabled' => 'Is build server enabled',
|
||||
'application.watch_paths' => 'Watch paths',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
@@ -251,7 +253,7 @@ class General extends Component
|
||||
if ($this->application->additional_servers->count() === 0) {
|
||||
foreach ($domains as $domain) {
|
||||
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
||||
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='text-white underline' href='https://coolify.io/docs/dns-settings'>documentation</a> for further help.");
|
||||
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,11 +56,11 @@ class Heading extends Component
|
||||
return;
|
||||
}
|
||||
if (data_get($this->application, 'settings.is_build_server_enabled') && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/build-server">documentation</a>');
|
||||
return;
|
||||
}
|
||||
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
|
||||
return;
|
||||
}
|
||||
$this->setDeploymentUuid();
|
||||
@@ -99,7 +99,7 @@ class Heading extends Component
|
||||
public function restart()
|
||||
{
|
||||
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
|
||||
return;
|
||||
}
|
||||
$this->setDeploymentUuid();
|
||||
|
||||
@@ -42,19 +42,21 @@ class BackupEdit extends Component
|
||||
|
||||
public function delete()
|
||||
{
|
||||
// TODO: Delete backup from server and add a confirmation modal
|
||||
$this->backup->delete();
|
||||
if ($this->backup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||
$previousUrl = url()->previous();
|
||||
$url = Url::fromString($previousUrl);
|
||||
$url = $url->withoutQueryParameter('selectedBackupId');
|
||||
$url = $url->withFragment('backups');
|
||||
$url = $url->getPath() . "#{$url->getFragment()}";
|
||||
return redirect($url);
|
||||
} else {
|
||||
return redirect()->route('project.database.backup.index', $this->parameters);
|
||||
try {
|
||||
$this->backup->delete();
|
||||
if ($this->backup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||
$previousUrl = url()->previous();
|
||||
$url = Url::fromString($previousUrl);
|
||||
$url = $url->withoutQueryParameter('selectedBackupId');
|
||||
$url = $url->withFragment('backups');
|
||||
$url = $url->getPath() . "#{$url->getFragment()}";
|
||||
return redirect($url);
|
||||
} else {
|
||||
return redirect()->route('project.database.backup.index', $this->parameters);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
@@ -63,7 +65,7 @@ class BackupEdit extends Component
|
||||
$this->custom_validate();
|
||||
$this->backup->save();
|
||||
$this->backup->refresh();
|
||||
$this->dispatch('success', 'Backup updated successfully');
|
||||
$this->dispatch('success', 'Backup updated successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->dispatch('error', $e->getMessage());
|
||||
}
|
||||
|
||||
@@ -17,9 +17,14 @@ class Edit extends Component
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$found = $this->project->environment_variables()->where('key', $data['key'])->first();
|
||||
if ($found) {
|
||||
throw new \Exception('Variable already exists.');
|
||||
}
|
||||
$this->project->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'type' => 'project',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
@@ -41,10 +46,11 @@ class Edit extends Component
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
try {
|
||||
$this->validate();
|
||||
$this->project->save();
|
||||
$this->dispatch('saved');
|
||||
$this->dispatch('success', 'Project updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,14 @@ class EnvironmentEdit extends Component
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$found = $this->environment->environment_variables()->where('key', $data['key'])->first();
|
||||
if ($found) {
|
||||
throw new \Exception('Variable already exists.');
|
||||
}
|
||||
$this->environment->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'type' => 'environment',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Project;
|
||||
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
@@ -10,7 +11,9 @@ class Index extends Component
|
||||
{
|
||||
public $projects;
|
||||
public $servers;
|
||||
public $private_keys;
|
||||
public function mount() {
|
||||
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
|
||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||
$this->servers = Server::ownedByCurrentTeam()->count();
|
||||
}
|
||||
|
||||
@@ -7,14 +7,12 @@ use App\Models\GithubApp;
|
||||
use App\Models\Project;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use App\Traits\SaveFromRedirect;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Livewire\Component;
|
||||
|
||||
class GithubPrivateRepository extends Component
|
||||
{
|
||||
use SaveFromRedirect;
|
||||
public $current_step = 'github_apps';
|
||||
public $github_apps;
|
||||
public GithubApp $github_app;
|
||||
|
||||
@@ -49,7 +49,6 @@ class Select extends Component
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
$this->loadServices();
|
||||
return view('livewire.project.new.select');
|
||||
}
|
||||
|
||||
@@ -71,9 +70,14 @@ class Select extends Component
|
||||
// }
|
||||
// }
|
||||
|
||||
public function updatedSearch()
|
||||
{
|
||||
$this->loadServices();
|
||||
}
|
||||
public function loadServices(bool $force = false)
|
||||
{
|
||||
try {
|
||||
$this->loadingServices = true;
|
||||
if (count($this->allServices) > 0 && !$force) {
|
||||
if (!$this->search) {
|
||||
$this->services = $this->allServices;
|
||||
@@ -90,8 +94,7 @@ class Select extends Component
|
||||
$this->allServices = getServiceTemplates();
|
||||
$this->services = $this->allServices->filter(function ($service, $key) {
|
||||
return str_contains(strtolower($key), strtolower($this->search));
|
||||
});;
|
||||
$this->dispatch('success', 'Successfully loaded services.');
|
||||
});
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -36,6 +36,30 @@ class Configuration extends Component
|
||||
$this->applications = $this->service->applications->sort();
|
||||
$this->databases = $this->service->databases->sort();
|
||||
}
|
||||
public function restartApplication($id)
|
||||
{
|
||||
try {
|
||||
$application = $this->service->applications->find($id);
|
||||
if ($application) {
|
||||
$application->restart();
|
||||
$this->dispatch('success', 'Application restarted successfully.');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function restartDatabase($id)
|
||||
{
|
||||
try {
|
||||
$database = $this->service->databases->find($id);
|
||||
if ($database) {
|
||||
$database->restart();
|
||||
$this->dispatch('success', 'Database restarted successfully.');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function check_status()
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -34,7 +34,11 @@ class Database extends Component
|
||||
}
|
||||
$this->refreshFileStorages();
|
||||
}
|
||||
public function instantSaveAdvanced()
|
||||
public function instantSaveExclude()
|
||||
{
|
||||
$this->submit();
|
||||
}
|
||||
public function instantSaveLogDrain()
|
||||
{
|
||||
if (!$this->database->service->destination->server->isLogDrainEnabled()) {
|
||||
$this->database->is_log_drain_enabled = false;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Modal;
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Service;
|
||||
use LivewireUI\Modal\ModalComponent;
|
||||
use Livewire\Component;
|
||||
|
||||
class EditCompose extends ModalComponent
|
||||
class EditCompose extends Component
|
||||
{
|
||||
public Service $service;
|
||||
public $serviceId;
|
||||
@@ -16,13 +16,13 @@ class EditCompose extends ModalComponent
|
||||
public function mount() {
|
||||
$this->service = Service::find($this->serviceId);
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.modal.edit-compose');
|
||||
}
|
||||
public function submit() {
|
||||
|
||||
public function saveEditedCompose() {
|
||||
$this->dispatch('warning', "Saving new docker compose...");
|
||||
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
|
||||
$this->closeModal();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.edit-compose');
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Modal extends Component
|
||||
{
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.modal');
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,24 @@ class Navbar extends Component
|
||||
public array $parameters;
|
||||
public array $query;
|
||||
public $isDeploymentProgress = false;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$userId = auth()->user()->id;
|
||||
return [
|
||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
|
||||
"serviceStatusChanged"
|
||||
];
|
||||
}
|
||||
public function serviceStarted() {
|
||||
$this->dispatch('success', 'Service status changed.');
|
||||
}
|
||||
public function serviceStatusChanged()
|
||||
{
|
||||
$this->dispatch('refresh')->self();
|
||||
}
|
||||
public function check_status() {
|
||||
public function check_status()
|
||||
{
|
||||
$this->dispatch('check_status');
|
||||
$this->dispatch('success', 'Service status updated.');
|
||||
}
|
||||
@@ -44,7 +51,7 @@ class Navbar extends Component
|
||||
$this->isDeploymentProgress = false;
|
||||
}
|
||||
}
|
||||
public function deploy()
|
||||
public function start()
|
||||
{
|
||||
$this->checkDeployments();
|
||||
if ($this->isDeploymentProgress) {
|
||||
@@ -73,9 +80,9 @@ class Navbar extends Component
|
||||
return;
|
||||
}
|
||||
PullImage::run($this->service);
|
||||
$this->dispatch('image-pulled');
|
||||
StopService::run($this->service);
|
||||
$this->service->parse();
|
||||
$this->dispatch('imagePulled');
|
||||
$activity = StartService::run($this->service);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,16 @@ class ServiceApplicationView extends Component
|
||||
{
|
||||
return view('livewire.project.service.service-application-view');
|
||||
}
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return str($domain)->trim()->lower();
|
||||
});
|
||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
||||
$this->application->save();
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
$this->submit();
|
||||
@@ -59,7 +69,11 @@ class ServiceApplicationView extends Component
|
||||
$this->validate();
|
||||
$this->application->save();
|
||||
updateCompose($this->application);
|
||||
$this->dispatch('success', 'Application saved.');
|
||||
if (str($this->application->fqdn)->contains(',')) {
|
||||
$this->dispatch('warning', 'Some services do not support multiple domains, which can lead to problems and is NOT RECOMMENDED.');
|
||||
} else {
|
||||
$this->dispatch('success', 'Service saved.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\Service;
|
||||
use Livewire\Component;
|
||||
|
||||
class StackForm extends Component
|
||||
{
|
||||
public $service;
|
||||
public Service $service;
|
||||
public $fields = [];
|
||||
protected $listeners = ["saveCompose"];
|
||||
public $rules = [
|
||||
|
||||
@@ -56,7 +56,7 @@ class Destination extends Component
|
||||
public function redeploy(int $network_id, int $server_id)
|
||||
{
|
||||
if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) {
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
|
||||
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/multiple-servers">documentation</a>');
|
||||
return;
|
||||
}
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
|
||||
@@ -11,17 +11,20 @@ class Add extends Component
|
||||
public string $key;
|
||||
public ?string $value = null;
|
||||
public bool $is_build_time = false;
|
||||
public bool $is_multiline = false;
|
||||
|
||||
protected $listeners = ['clearAddEnv' => 'clear'];
|
||||
protected $rules = [
|
||||
'key' => 'required|string',
|
||||
'value' => 'nullable',
|
||||
'is_build_time' => 'required|boolean',
|
||||
'is_multiline' => 'required|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'key' => 'key',
|
||||
'value' => 'value',
|
||||
'is_build_time' => 'build',
|
||||
'is_multiline' => 'multiline',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
@@ -43,6 +46,7 @@ class Add extends Component
|
||||
'key' => $this->key,
|
||||
'value' => $this->value,
|
||||
'is_build_time' => $this->is_build_time,
|
||||
'is_multiline' => $this->is_multiline,
|
||||
'is_preview' => $this->is_preview,
|
||||
]);
|
||||
$this->clear();
|
||||
@@ -53,5 +57,6 @@ class Add extends Component
|
||||
$this->key = '';
|
||||
$this->value = '';
|
||||
$this->is_build_time = false;
|
||||
$this->is_multiline = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class All extends Component
|
||||
{
|
||||
public $resource;
|
||||
public bool $showPreview = false;
|
||||
public string|null $modalId = null;
|
||||
public ?string $modalId = null;
|
||||
public ?string $variables = null;
|
||||
public ?string $variablesPreview = null;
|
||||
public string $view = 'normal';
|
||||
@@ -34,6 +34,9 @@ class All extends Component
|
||||
if ($item->is_shown_once) {
|
||||
return "$item->key=(locked secret)";
|
||||
}
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(multiline, edit in normal view)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
');
|
||||
@@ -42,6 +45,9 @@ class All extends Component
|
||||
if ($item->is_shown_once) {
|
||||
return "$item->key=(locked secret)";
|
||||
}
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(multiline, edit in normal view)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
');
|
||||
@@ -67,7 +73,7 @@ class All extends Component
|
||||
$found = $this->resource->environment_variables()->where('key', $key)->first();
|
||||
}
|
||||
if ($found) {
|
||||
if ($found->is_shown_once) {
|
||||
if ($found->is_shown_once || $found->is_multiline) {
|
||||
continue;
|
||||
}
|
||||
$found->value = $variable;
|
||||
@@ -92,6 +98,7 @@ class All extends Component
|
||||
}
|
||||
}
|
||||
$environment->is_build_time = false;
|
||||
$environment->is_multiline = false;
|
||||
$environment->is_preview = $isPreview ? true : false;
|
||||
switch ($this->resource->type()) {
|
||||
case 'application':
|
||||
@@ -144,6 +151,7 @@ class All extends Component
|
||||
$environment->key = $data['key'];
|
||||
$environment->value = $data['value'];
|
||||
$environment->is_build_time = $data['is_build_time'];
|
||||
$environment->is_multiline = $data['is_multiline'];
|
||||
$environment->is_preview = $data['is_preview'];
|
||||
|
||||
switch ($this->resource->type()) {
|
||||
|
||||
@@ -21,6 +21,7 @@ class Show extends Component
|
||||
'env.key' => 'required|string',
|
||||
'env.value' => 'nullable',
|
||||
'env.is_build_time' => 'required|boolean',
|
||||
'env.is_multiline' => 'required|boolean',
|
||||
'env.is_shown_once' => 'required|boolean',
|
||||
'env.real_value' => 'nullable',
|
||||
];
|
||||
@@ -28,6 +29,7 @@ class Show extends Component
|
||||
'env.key' => 'Key',
|
||||
'env.value' => 'Value',
|
||||
'env.is_build_time' => 'Build Time',
|
||||
'env.is_multiline' => 'Multiline',
|
||||
'env.is_shown_once' => 'Shown Once',
|
||||
];
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@ class GetLogs extends Component
|
||||
}
|
||||
public function getLogs($refresh = false)
|
||||
{
|
||||
if (!$this->server->isFunctional()) {
|
||||
return;
|
||||
}
|
||||
if ($this->resource?->getMorphClass() === 'App\Models\Application') {
|
||||
if (str($this->container)->contains('-pr-')) {
|
||||
$this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value();
|
||||
@@ -79,6 +82,9 @@ class GetLogs extends Component
|
||||
}
|
||||
}
|
||||
if (!$refresh && ($this->resource?->getMorphClass() === 'App\Models\Service' || str($this->container)->contains('-pr-'))) return;
|
||||
if (!$this->numberOfLines) {
|
||||
$this->numberOfLines = 1000;
|
||||
}
|
||||
if ($this->container) {
|
||||
if ($this->showTimeStamps) {
|
||||
if ($this->server->isSwarm()) {
|
||||
|
||||
@@ -29,6 +29,9 @@ class Logs extends Component
|
||||
{
|
||||
try {
|
||||
$server = $this->servers->firstWhere('id', $server_id);
|
||||
if (!$server->isFunctional()) {
|
||||
return;
|
||||
}
|
||||
if ($server->isSwarm()) {
|
||||
$containers = collect([
|
||||
[
|
||||
@@ -96,7 +99,6 @@ class Logs extends Component
|
||||
$this->resource->databases()->get()->each(function ($database) {
|
||||
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
|
||||
});
|
||||
|
||||
if ($this->resource->server->isFunctional()) {
|
||||
$this->servers = $this->servers->push($this->resource->server);
|
||||
}
|
||||
|
||||
@@ -33,19 +33,23 @@ class Add extends Component
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$isValid = validate_cron_expression($this->frequency);
|
||||
if (!$isValid) {
|
||||
$this->dispatch('error', 'Invalid Cron / Human expression.');
|
||||
return;
|
||||
try {
|
||||
$this->validate();
|
||||
$isValid = validate_cron_expression($this->frequency);
|
||||
if (!$isValid) {
|
||||
$this->dispatch('error', 'Invalid Cron / Human expression.');
|
||||
return;
|
||||
}
|
||||
$this->dispatch('saveScheduledTask', [
|
||||
'name' => $this->name,
|
||||
'command' => $this->command,
|
||||
'frequency' => $this->frequency,
|
||||
'container' => $this->container,
|
||||
]);
|
||||
$this->clear();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->dispatch('saveScheduledTask', [
|
||||
'name' => $this->name,
|
||||
'command' => $this->command,
|
||||
'frequency' => $this->frequency,
|
||||
'container' => $this->container,
|
||||
]);
|
||||
$this->clear();
|
||||
}
|
||||
|
||||
public function clear()
|
||||
|
||||
@@ -28,7 +28,7 @@ class Tags extends Component
|
||||
{
|
||||
try {
|
||||
if ($this->resource->tags()->where('id', $id)->exists()) {
|
||||
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='text-warning'>$name</span> already added.");
|
||||
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='dark:text-warning'>$name</span> already added.");
|
||||
return;
|
||||
}
|
||||
$this->resource->tags()->syncWithoutDetaching($id);
|
||||
@@ -66,7 +66,7 @@ class Tags extends Component
|
||||
$tags = str($this->new_tag)->trim()->explode(' ');
|
||||
foreach ($tags as $tag) {
|
||||
if ($this->resource->tags()->where('name', $tag)->exists()) {
|
||||
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='text-warning'>$tag</span> already added.");
|
||||
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='dark:text-warning'>$tag</span> already added.");
|
||||
continue;
|
||||
}
|
||||
$found = Tag::where(['name' => $tag, 'team_id' => currentTeam()->id])->first();
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class RealtimeConnection extends Component
|
||||
{
|
||||
public $checkConnection = false;
|
||||
public $showNotification = false;
|
||||
public $isNotificationEnabled = true;
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.realtime-connection');
|
||||
}
|
||||
public function disable()
|
||||
{
|
||||
auth()->user()->update(['is_notification_realtime_enabled' => false]);
|
||||
$this->showNotification = false;
|
||||
}
|
||||
public function mount() {
|
||||
$this->isNotificationEnabled = auth()->user()->is_notification_realtime_enabled;
|
||||
$this->checkConnection = auth()->user()->id === 0;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class Create extends Component
|
||||
'value' => 'private Key',
|
||||
];
|
||||
|
||||
public function generateNewKey()
|
||||
public function generateNewRSAKey()
|
||||
{
|
||||
try {
|
||||
$this->rateLimit(10);
|
||||
@@ -37,6 +37,17 @@ class Create extends Component
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function generateNewEDKey()
|
||||
{
|
||||
try {
|
||||
$this->rateLimit(10);
|
||||
$this->name = generate_random_name();
|
||||
$this->description = 'Created by Coolify';
|
||||
['private' => $this->value, 'public' => $this->publicKey] = generateSSHKey('ed25519');
|
||||
} catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function updated($updateProperty)
|
||||
{
|
||||
if ($updateProperty === 'value') {
|
||||
@@ -67,7 +78,7 @@ class Create extends Component
|
||||
'team_id' => currentTeam()->id
|
||||
]);
|
||||
if ($this->from === 'server') {
|
||||
return redirect()->route('server.create');
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -8,7 +8,7 @@ use Livewire\Component;
|
||||
class Show extends Component
|
||||
{
|
||||
public PrivateKey $private_key;
|
||||
public $public_key;
|
||||
public $public_key = "Loading...";
|
||||
protected $rules = [
|
||||
'private_key.name' => 'required|string',
|
||||
'private_key.description' => 'nullable|string',
|
||||
@@ -25,11 +25,13 @@ class Show extends Component
|
||||
{
|
||||
try {
|
||||
$this->private_key = PrivateKey::ownedByCurrentTeam(['name', 'description', 'private_key', 'is_git_related'])->whereUuid(request()->private_key_uuid)->firstOrFail();
|
||||
$this->public_key = $this->private_key->publicKey();
|
||||
}catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function loadPublicKey() {
|
||||
$this->public_key = $this->private_key->publicKey();
|
||||
}
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
@@ -50,6 +52,7 @@ class Show extends Component
|
||||
$this->private_key->private_key = formatPrivateKey($this->private_key->private_key);
|
||||
$this->private_key->save();
|
||||
refresh_server_connection($this->private_key);
|
||||
$this->dispatch('success', 'Private key updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
45
app/Livewire/Server/ConfigureCloudflareTunnels.php
Normal file
45
app/Livewire/Server/ConfigureCloudflareTunnels.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Actions\Server\ConfigureCloudflared;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
class ConfigureCloudflareTunnels extends Component
|
||||
{
|
||||
public $server_id;
|
||||
public string $cloudflare_token;
|
||||
public string $ssh_domain;
|
||||
public function alreadyConfigured()
|
||||
{
|
||||
try {
|
||||
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
|
||||
$server->settings->is_cloudflare_tunnel = true;
|
||||
$server->settings->save();
|
||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||
$this->dispatch('serverInstalled');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
|
||||
ConfigureCloudflared::run($server, $this->cloudflare_token);
|
||||
$server->settings->is_cloudflare_tunnel = true;
|
||||
$server->ip = $this->ssh_domain;
|
||||
$server->save();
|
||||
$server->settings->save();
|
||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||
$this->dispatch('serverInstalled');
|
||||
} catch(\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.configure-cloudflare-tunnels');
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class Form extends Component
|
||||
$this->server->settings->is_usable = true;
|
||||
$this->server->settings->save();
|
||||
} else {
|
||||
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh">documentation</a> for further help.');
|
||||
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class ByIp extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->name = generate_random_name();
|
||||
$this->private_key_id = $this->private_keys->first()->id;
|
||||
$this->private_key_id = $this->private_keys->first()?->id;
|
||||
$this->swarm_managers = Server::isUsable()->get()->where('settings.is_swarm_manager', true);
|
||||
if ($this->swarm_managers->count() > 0) {
|
||||
$this->selected_swarm_cluster = $this->swarm_managers->first()->id;
|
||||
|
||||
@@ -49,7 +49,8 @@ class Deploy extends Component
|
||||
{
|
||||
$this->server->refresh();
|
||||
}
|
||||
public function restart() {
|
||||
public function restart()
|
||||
{
|
||||
try {
|
||||
$this->stop();
|
||||
$this->dispatch('checkProxy');
|
||||
|
||||
@@ -42,7 +42,11 @@ class Resources extends Component
|
||||
$this->dispatch('success', 'Resource statuses refreshed.');
|
||||
}
|
||||
public function loadUnmanagedContainers() {
|
||||
$this->unmanagedContainers = $this->server->loadUnmanagedContainers();
|
||||
try {
|
||||
$this->unmanagedContainers = $this->server->loadUnmanagedContainers();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function mount() {
|
||||
$this->unmanagedContainers = collect();
|
||||
|
||||
@@ -39,7 +39,7 @@ class ShowPrivateKey extends Component
|
||||
if ($uptime) {
|
||||
$this->dispatch('success', 'Server is reachable.');
|
||||
} else {
|
||||
$this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh#openssh">documentation</a> for further help.');
|
||||
$this->dispatch('error', 'Server is not reachable.<br>Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.');
|
||||
return;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -75,7 +75,7 @@ class ValidateAndInstall extends Component
|
||||
{
|
||||
$this->uptime = $this->server->validateConnection();
|
||||
if (!$this->uptime) {
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/server/openssh">documentation</a> for further help.';
|
||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.';
|
||||
return;
|
||||
}
|
||||
$this->dispatch('validateOS');
|
||||
|
||||
43
app/Livewire/Settings/Auth.php
Normal file
43
app/Livewire/Settings/Auth.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Settings;
|
||||
|
||||
use Livewire\Component;
|
||||
use App\Models\OauthSetting;
|
||||
|
||||
class Auth extends Component {
|
||||
public $oauth_settings_map;
|
||||
|
||||
protected function rules() {
|
||||
return OauthSetting::all()->reduce(function($carry, $setting) {
|
||||
$carry["oauth_settings_map.$setting->provider.enabled"] = 'required';
|
||||
$carry["oauth_settings_map.$setting->provider.client_id"] = 'nullable';
|
||||
$carry["oauth_settings_map.$setting->provider.client_secret"] = 'nullable';
|
||||
$carry["oauth_settings_map.$setting->provider.redirect_uri"] = 'nullable';
|
||||
$carry["oauth_settings_map.$setting->provider.tenant"] = 'nullable';
|
||||
return $carry;
|
||||
}, []);
|
||||
}
|
||||
|
||||
public function mount() {
|
||||
$this->oauth_settings_map = OauthSetting::all()->sortBy('provider')->reduce(function($carry, $setting) {
|
||||
$carry[$setting->provider] = $setting;
|
||||
return $carry;
|
||||
}, []);
|
||||
}
|
||||
|
||||
private function updateOauthSettings() {
|
||||
foreach (array_values($this->oauth_settings_map) as &$setting) {
|
||||
$setting->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave() {
|
||||
$this->updateOauthSettings();
|
||||
}
|
||||
|
||||
public function submit() {
|
||||
$this->updateOauthSettings();
|
||||
$this->dispatch('success', 'Instance settings updated successfully!');
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,8 @@ class Email extends Component
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'settings.smtp_from_address' => 'required|email',
|
||||
'settings.smtp_from_name' => 'required',
|
||||
'settings.resend_api_key' => 'required'
|
||||
]);
|
||||
$this->settings->save();
|
||||
@@ -90,6 +92,8 @@ class Email extends Component
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'settings.smtp_from_address' => 'required|email',
|
||||
'settings.smtp_from_name' => 'required',
|
||||
'settings.smtp_host' => 'required',
|
||||
'settings.smtp_port' => 'required|numeric',
|
||||
'settings.smtp_encryption' => 'nullable',
|
||||
|
||||
@@ -30,7 +30,7 @@ class License extends Component
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.settings.license')->layout('layouts.subscription');
|
||||
return view('livewire.settings.license');
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ class Index extends Component
|
||||
if (!isCloud()) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
if (data_get(currentTeam(), 'subscription')) {
|
||||
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
|
||||
return redirect()->route('subscription.show');
|
||||
}
|
||||
$this->settings = InstanceSettings::get();
|
||||
@@ -31,6 +31,6 @@ class Index extends Component
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.subscription.index')->layout('layouts.subscription');
|
||||
return view('livewire.subscription.index');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ use Livewire\Component;
|
||||
class SwitchTeam extends Component
|
||||
{
|
||||
public string $selectedTeamId = 'default';
|
||||
|
||||
public function mount() {
|
||||
$this->selectedTeamId = auth()->user()->currentTeam()->id;
|
||||
}
|
||||
public function updatedSelectedTeamId()
|
||||
{
|
||||
$this->switch_to($this->selectedTeamId);
|
||||
|
||||
@@ -2,14 +2,73 @@
|
||||
|
||||
namespace App\Livewire\Tags;
|
||||
|
||||
use App\Http\Controllers\Api\Deploy;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public $tags = [];
|
||||
public function mount() {
|
||||
$this->tags = Tag::where('team_id', currentTeam()->id)->get()->unique('name')->sortBy('name');
|
||||
#[Url()]
|
||||
public ?string $tag = null;
|
||||
|
||||
public Collection $tags;
|
||||
public Collection $applications;
|
||||
public Collection $services;
|
||||
public $webhook = null;
|
||||
public $deployments_per_tag_per_server = [];
|
||||
|
||||
public function updatedTag()
|
||||
{
|
||||
$tag = $this->tags->where('name', $this->tag)->first();
|
||||
$this->webhook = generatTagDeployWebhook($tag->name);
|
||||
$this->applications = $tag->applications()->get();
|
||||
$this->services = $tag->services()->get();
|
||||
$this->get_deployments();
|
||||
}
|
||||
public function get_deployments()
|
||||
{
|
||||
try {
|
||||
$resource_ids = $this->applications->pluck('id');
|
||||
$this->deployments_per_tag_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn('application_id', $resource_ids)->get([
|
||||
"id",
|
||||
"application_id",
|
||||
"application_name",
|
||||
"deployment_url",
|
||||
"pull_request_id",
|
||||
"server_name",
|
||||
"server_id",
|
||||
"status"
|
||||
])->sortBy('id')->groupBy('server_name')->toArray();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function redeploy_all()
|
||||
{
|
||||
try {
|
||||
$message = collect([]);
|
||||
$this->applications->each(function ($resource) use ($message) {
|
||||
$deploy = new Deploy();
|
||||
$message->push($deploy->deploy_resource($resource));
|
||||
});
|
||||
$this->services->each(function ($resource) use ($message) {
|
||||
$deploy = new Deploy();
|
||||
$message->push($deploy->deploy_resource($resource));
|
||||
});
|
||||
$this->dispatch('success', 'Mass deployment started.');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$this->tags = Tag::ownedByCurrentTeam()->get()->unique('name')->sortBy('name');
|
||||
if ($this->tag) {
|
||||
$this->updatedTag();
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Team\Notification;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.team.notification.index');
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,14 @@ class TeamSharedVariablesIndex extends Component
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$found = $this->team->environment_variables()->where('key', $data['key'])->first();
|
||||
if ($found) {
|
||||
throw new \Exception('Variable already exists.');
|
||||
}
|
||||
$this->team->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'type' => 'team',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
|
||||
@@ -37,8 +37,8 @@ class Upgrade extends Component
|
||||
return;
|
||||
}
|
||||
$this->showProgress = true;
|
||||
UpdateCoolify::run(true);
|
||||
$this->dispatch('success', "Upgrading to {$this->latestVersion} version...");
|
||||
UpdateCoolify::run(force: true, async: true);
|
||||
$this->dispatch('success', "Updating Coolify to {$this->latestVersion} version...");
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Enums\ApplicationDeploymentStatus;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
use Illuminate\Support\Str;
|
||||
use RuntimeException;
|
||||
@@ -501,9 +502,9 @@ class Application extends BaseModel
|
||||
{
|
||||
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
||||
if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
|
||||
$newConfigHash .= json_encode($this->environment_variables());
|
||||
$newConfigHash .= json_encode($this->environment_variables()->get('updated_at'));
|
||||
} else {
|
||||
$newConfigHash .= json_encode($this->environment_variables_preview->all());
|
||||
$newConfigHash .= json_encode($this->environment_variables_preview->get('updated_at'));
|
||||
}
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
@@ -752,7 +753,7 @@ class Application extends BaseModel
|
||||
$type = data_get_str($volume, 'type');
|
||||
$source = data_get_str($volume, 'source');
|
||||
}
|
||||
if ($type->value() === 'bind') {
|
||||
if ($type?->value() === 'bind') {
|
||||
if ($source->value() === "/var/run/docker.sock") {
|
||||
continue;
|
||||
}
|
||||
@@ -903,4 +904,27 @@ class Application extends BaseModel
|
||||
: explode(',', $this->fqdn),
|
||||
);
|
||||
}
|
||||
public function watchPaths(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
set: function ($value) {
|
||||
if ($value) {
|
||||
return trim($value);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
public function isWatchPathsTriggered(Collection $modified_files): bool
|
||||
{
|
||||
if (is_null($this->watch_paths)) {
|
||||
return false;
|
||||
}
|
||||
$watch_paths = collect(explode("\n", $this->watch_paths));
|
||||
$matches = $modified_files->filter(function ($file) use ($watch_paths) {
|
||||
return $watch_paths->contains(function ($glob) use ($file) {
|
||||
return fnmatch($glob, $file);
|
||||
});
|
||||
});
|
||||
return $matches->count() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,28 +14,34 @@ class EnvironmentVariable extends Model
|
||||
'key' => 'string',
|
||||
'value' => 'encrypted',
|
||||
'is_build_time' => 'boolean',
|
||||
'is_multiline' => 'boolean',
|
||||
'is_preview' => 'boolean',
|
||||
'version' => 'string'
|
||||
];
|
||||
protected $appends = ['real_value', 'is_shared'];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($environment_variable) {
|
||||
static::created(function (EnvironmentVariable $environment_variable) {
|
||||
if ($environment_variable->application_id && !$environment_variable->is_preview) {
|
||||
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first();
|
||||
$application = Application::find($environment_variable->application_id);
|
||||
if ($application->build_pack === 'dockerfile') {
|
||||
return;
|
||||
}
|
||||
if (!$found) {
|
||||
ModelsEnvironmentVariable::create([
|
||||
'key' => $environment_variable->key,
|
||||
'value' => $environment_variable->value,
|
||||
'is_build_time' => $environment_variable->is_build_time,
|
||||
'application_id' => $environment_variable->application_id,
|
||||
'is_preview' => true,
|
||||
]);
|
||||
$application = Application::find($environment_variable->application_id);
|
||||
if ($application->build_pack !== 'dockerfile') {
|
||||
ModelsEnvironmentVariable::create([
|
||||
'key' => $environment_variable->key,
|
||||
'value' => $environment_variable->value,
|
||||
'is_build_time' => $environment_variable->is_build_time,
|
||||
'is_multiline' => $environment_variable->is_multiline,
|
||||
'application_id' => $environment_variable->application_id,
|
||||
'is_preview' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$environment_variable->update([
|
||||
'version' => config('version')
|
||||
]);
|
||||
});
|
||||
}
|
||||
public function service()
|
||||
@@ -49,7 +55,7 @@ class EnvironmentVariable extends Model
|
||||
set: fn (?string $value = null) => $this->set_environment_variables($value),
|
||||
);
|
||||
}
|
||||
public function realValue(): Attribute
|
||||
public function resource()
|
||||
{
|
||||
$resource = null;
|
||||
if ($this->application_id) {
|
||||
@@ -71,9 +77,19 @@ class EnvironmentVariable extends Model
|
||||
}
|
||||
}
|
||||
}
|
||||
return $resource;
|
||||
}
|
||||
public function realValue(): Attribute
|
||||
{
|
||||
$resource = $this->resource();
|
||||
return Attribute::make(
|
||||
get: function () use ($resource) {
|
||||
return $this->get_real_environment_variables($this->value, $resource);
|
||||
$env = $this->get_real_environment_variables($this->value, $resource);
|
||||
return data_get($env, 'value', $env);
|
||||
if (is_string($env)) {
|
||||
return $env;
|
||||
}
|
||||
return $env->value;
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -89,9 +105,9 @@ class EnvironmentVariable extends Model
|
||||
}
|
||||
);
|
||||
}
|
||||
private function get_real_environment_variables(?string $environment_variable = null, $resource = null): string|null
|
||||
private function get_real_environment_variables(?string $environment_variable = null, $resource = null)
|
||||
{
|
||||
if (!$environment_variable || !$resource) {
|
||||
if ((is_null($environment_variable) && $environment_variable == '') || is_null($resource)) {
|
||||
return null;
|
||||
}
|
||||
$environment_variable = trim($environment_variable);
|
||||
@@ -112,7 +128,7 @@ class EnvironmentVariable extends Model
|
||||
}
|
||||
$environment_variable_found = SharedEnvironmentVariable::where("type", $type)->where('key', $variable)->where('team_id', $resource->team()->id)->where("{$type}_id", $id)->first();
|
||||
if ($environment_variable_found) {
|
||||
return $environment_variable_found->value;
|
||||
return $environment_variable_found;
|
||||
}
|
||||
}
|
||||
return $environment_variable;
|
||||
|
||||
21
app/Models/OauthSetting.php
Normal file
21
app/Models/OauthSetting.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class OauthSetting extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected function clientSecret(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (string | null $value) => empty($value) ? null : Crypt::decryptString($value),
|
||||
set: fn (string | null $value) => empty($value) ? null : Crypt::encryptString($value),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -550,21 +550,21 @@ $schema://$host {
|
||||
}
|
||||
public function loadUnmanagedContainers()
|
||||
{
|
||||
if ($this->isFunctional()) {
|
||||
$containers = instant_remote_process(["docker ps -a --format '{{json .}}' "], $this);
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
$containers = $containers->map(function ($container) {
|
||||
$labels = data_get($container, 'Labels');
|
||||
if (!str($labels)->contains("coolify.managed")) {
|
||||
return $container;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
$containers = $containers->filter();
|
||||
return collect($containers);
|
||||
} else {
|
||||
return collect([]);
|
||||
}
|
||||
if ($this->isFunctional()) {
|
||||
$containers = instant_remote_process(["docker ps -a --format '{{json .}}' "], $this);
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
$containers = $containers->map(function ($container) {
|
||||
$labels = data_get($container, 'Labels');
|
||||
if (!str($labels)->contains("coolify.managed")) {
|
||||
return $container;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
$containers = $containers->filter();
|
||||
return collect($containers);
|
||||
} else {
|
||||
return collect([]);
|
||||
}
|
||||
}
|
||||
public function hasDefinedResources()
|
||||
{
|
||||
|
||||
@@ -19,6 +19,11 @@ class ServiceApplication extends BaseModel
|
||||
$service->fileStorages()->delete();
|
||||
});
|
||||
}
|
||||
public function restart()
|
||||
{
|
||||
$container_id = $this->name . '-' . $this->service->uuid;
|
||||
instant_remote_process(["docker restart {$container_id}"], $this->service->server);
|
||||
}
|
||||
public function isLogDrainEnabled()
|
||||
{
|
||||
return data_get($this, 'is_log_drain_enabled', false);
|
||||
|
||||
@@ -17,6 +17,11 @@ class ServiceDatabase extends BaseModel
|
||||
$service->fileStorages()->delete();
|
||||
});
|
||||
}
|
||||
public function restart()
|
||||
{
|
||||
$container_id = $this->name . '-' . $this->service->uuid;
|
||||
remote_process(["docker restart {$container_id}"], $this->service->server);
|
||||
}
|
||||
public function isLogDrainEnabled()
|
||||
{
|
||||
return data_get($this, 'is_log_drain_enabled', false);
|
||||
@@ -52,8 +57,7 @@ class ServiceDatabase extends BaseModel
|
||||
if ($this->service->server->isLocalhost() || isDev()) {
|
||||
$realIp = base_ip();
|
||||
}
|
||||
$url = "{$realIp}:{$port}";
|
||||
return $url;
|
||||
return "{$realIp}:{$port}";
|
||||
}
|
||||
public function service()
|
||||
{
|
||||
|
||||
@@ -21,9 +21,11 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
// static::saved(function () {
|
||||
// refreshSession();
|
||||
// });
|
||||
static::saving(function ($team) {
|
||||
if (auth()->user()->isMember()) {
|
||||
throw new \Exception('You are not allowed to update this team.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function routeNotificationForDiscord()
|
||||
@@ -177,9 +179,6 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
if (isCloud()) {
|
||||
return true;
|
||||
}
|
||||
if (!data_get(auth()->user(), 'is_notification_notifications_enabled')) {
|
||||
return true;
|
||||
}
|
||||
if ($this->smtp_enabled || $this->resend_enabled || $this->discord_enabled || $this->telegram_enabled || $this->use_instance_email_settings) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -127,6 +127,10 @@ class User extends Authenticatable implements SendsEmail
|
||||
{
|
||||
return $this->role() === 'owner';
|
||||
}
|
||||
public function isMember()
|
||||
{
|
||||
return $this->role() === 'member';
|
||||
}
|
||||
public function isAdminFromSession()
|
||||
{
|
||||
if (auth()->user()->id === 0) {
|
||||
|
||||
@@ -53,13 +53,13 @@ class HighDiskUsage extends Notification implements ShouldQueue
|
||||
|
||||
public function toDiscord(): string
|
||||
{
|
||||
$message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/automated-cleanup.";
|
||||
$message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.";
|
||||
return $message;
|
||||
}
|
||||
public function toTelegram(): array
|
||||
{
|
||||
return [
|
||||
"message" => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/automated-cleanup."
|
||||
"message" => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup."
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,10 @@ class EventServiceProvider extends ServiceProvider
|
||||
MaintenanceModeDisabled::class => [
|
||||
MaintenanceModeDisabledNotification::class,
|
||||
],
|
||||
ProxyStarted::class => [
|
||||
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
|
||||
\SocialiteProviders\Azure\AzureExtendSocialite::class.'@handle',
|
||||
],
|
||||
ProxyStarted::class => [
|
||||
ProxyStartedNotification::class,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Actions\Fortify\ResetUserPassword;
|
||||
use App\Actions\Fortify\UpdateUserPassword;
|
||||
use App\Actions\Fortify\UpdateUserProfileInformation;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\OauthSetting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -56,13 +57,15 @@ class FortifyServiceProvider extends ServiceProvider
|
||||
|
||||
Fortify::loginView(function () {
|
||||
$settings = InstanceSettings::get();
|
||||
$enabled_oauth_providers = OauthSetting::where('enabled', true)->get();
|
||||
$users = User::count();
|
||||
if ($users == 0) {
|
||||
// If there are no users, redirect to registration
|
||||
return redirect()->route('register');
|
||||
}
|
||||
return view('auth.login', [
|
||||
'is_registration_enabled' => $settings->is_registration_enabled
|
||||
'is_registration_enabled' => $settings->is_registration_enabled,
|
||||
'enabled_oauth_providers' => $enabled_oauth_providers,
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ class Button extends Component
|
||||
*/
|
||||
public function __construct(
|
||||
public bool $disabled = false,
|
||||
public bool $isModal = false,
|
||||
public bool $noStyle = false,
|
||||
public ?string $modalId = null,
|
||||
public string $defaultClass = "btn btn-primary btn-sm font-normal text-white normal-case no-animation rounded border-none"
|
||||
public string $defaultClass = "button"
|
||||
) {
|
||||
if ($this->noStyle) {
|
||||
$this->defaultClass = "";
|
||||
|
||||
@@ -12,14 +12,14 @@ class Checkbox extends Component
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string|null $id = null,
|
||||
public string|null $name = null,
|
||||
public string|null $value = null,
|
||||
public string|null $label = null,
|
||||
public string|null $helper = null,
|
||||
public string|bool $instantSave = false,
|
||||
public ?string $id = null,
|
||||
public ?string $name = null,
|
||||
public ?string $value = null,
|
||||
public ?string $label = null,
|
||||
public ?string $helper = null,
|
||||
public string|bool $instantSave = false,
|
||||
public bool $disabled = false,
|
||||
public string $defaultClass = "toggle toggle-xs toggle-warning rounded disabled:bg-coolgray-200 disabled:opacity-50 placeholder:text-neutral-700",
|
||||
public string $defaultClass = "dark:border-neutral-700 text-coolgray-400 focus:ring-warning dark:bg-coolgray-100 rounded cursor-pointer dark:disabled:bg-base dark:disabled:cursor-not-allowed",
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
@@ -10,17 +10,18 @@ use Visus\Cuid2\Cuid2;
|
||||
class Input extends Component
|
||||
{
|
||||
public function __construct(
|
||||
public string|null $id = null,
|
||||
public string|null $name = null,
|
||||
public string|null $type = 'text',
|
||||
public string|null $value = null,
|
||||
public string|null $label = null,
|
||||
public bool $required = false,
|
||||
public bool $disabled = false,
|
||||
public bool $readonly = false,
|
||||
public string|null $helper = null,
|
||||
public bool $allowToPeak = true,
|
||||
public string $defaultClass = "input input-sm bg-coolgray-100 rounded text-white w-full disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
|
||||
public ?string $id = null,
|
||||
public ?string $name = null,
|
||||
public ?string $type = 'text',
|
||||
public ?string $value = null,
|
||||
public ?string $label = null,
|
||||
public bool $required = false,
|
||||
public bool $disabled = false,
|
||||
public bool $readonly = false,
|
||||
public ?string $helper = null,
|
||||
public bool $allowToPeak = true,
|
||||
public bool $isMultiline = false,
|
||||
public string $defaultClass = "input",
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -28,7 +29,9 @@ class Input extends Component
|
||||
{
|
||||
if (is_null($this->id)) $this->id = new Cuid2(7);
|
||||
if (is_null($this->name)) $this->name = $this->id;
|
||||
|
||||
if ($this->type === 'password') {
|
||||
$this->defaultClass = $this->defaultClass . " pr-[2.8rem]";
|
||||
}
|
||||
// $this->label = Str::title($this->label);
|
||||
return view('components.forms.input');
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ class Select extends Component
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string|null $id = null,
|
||||
public string|null $name = null,
|
||||
public string|null $label = null,
|
||||
public string|null $helper = null,
|
||||
public bool $required = false,
|
||||
public string $defaultClass = "select select-sm w-full rounded text-sm bg-coolgray-100 font-normal disabled:bg-coolgray-200/50 disabled:border-none"
|
||||
public ?string $id = null,
|
||||
public ?string $name = null,
|
||||
public ?string $label = null,
|
||||
public ?string $helper = null,
|
||||
public bool $required = false,
|
||||
public string $defaultClass = "select"
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\View\Components\Forms;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
@@ -14,18 +13,20 @@ class Textarea extends Component
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public string|null $id = null,
|
||||
public string|null $name = null,
|
||||
public string|null $type = 'text',
|
||||
public string|null $value = null,
|
||||
public string|null $label = null,
|
||||
public string|null $placeholder = null,
|
||||
public bool $required = false,
|
||||
public bool $disabled = false,
|
||||
public bool $readonly = false,
|
||||
public string|null $helper = null,
|
||||
public bool $realtimeValidation = false,
|
||||
public string $defaultClass = "textarea leading-normal bg-coolgray-100 rounded text-white scrollbar disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
|
||||
public ?string $id = null,
|
||||
public ?string $name = null,
|
||||
public ?string $type = 'text',
|
||||
public ?string $value = null,
|
||||
public ?string $label = null,
|
||||
public ?string $placeholder = null,
|
||||
public bool $required = false,
|
||||
public bool $disabled = false,
|
||||
public bool $readonly = false,
|
||||
public ?string $helper = null,
|
||||
public bool $realtimeValidation = false,
|
||||
public bool $allowToPeak = true,
|
||||
public string $defaultClass = "input scrollbar",
|
||||
public string $defaultClassInput = "input"
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ function get_team_id_from_token()
|
||||
}
|
||||
function invalid_token()
|
||||
{
|
||||
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api/authentication'], 400);
|
||||
return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api-reference/authorization'], 400);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user