mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-30 20:59:22 +00:00
Compare commits
282 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bfac7ef3b | ||
|
|
1581e0e439 | ||
|
|
6773848874 | ||
|
|
3e9219173c | ||
|
|
d46a565e6c | ||
|
|
6267804d38 | ||
|
|
25c4d282ce | ||
|
|
45281360d5 | ||
|
|
7a32b8d1d2 | ||
|
|
7b0018b661 | ||
|
|
9032879e20 | ||
|
|
8b668cf8b7 | ||
|
|
3323533fd0 | ||
|
|
50e96baea1 | ||
|
|
2ac92df9d3 | ||
|
|
63e64cea60 | ||
|
|
0267c76de2 | ||
|
|
2382a10bba | ||
|
|
a8db40e99a | ||
|
|
8553fffffe | ||
|
|
d9d38a27ff | ||
|
|
1e7a5562ab | ||
|
|
e71032a8fc | ||
|
|
484a3dbe5c | ||
|
|
9c7f40e4fe | ||
|
|
f35b7ab6f4 | ||
|
|
a38dd3d5c8 | ||
|
|
00ad35fc3e | ||
|
|
0d78b44c80 | ||
|
|
fb614637a9 | ||
|
|
d0482d66c2 | ||
|
|
ef178dfd24 | ||
|
|
6cdc6e0780 | ||
|
|
c961e6d9c7 | ||
|
|
c6844ff47a | ||
|
|
605a630411 | ||
|
|
2b75cf1508 | ||
|
|
007054301b | ||
|
|
cf01d32237 | ||
|
|
5ec553f68d | ||
|
|
d95e5a169d | ||
|
|
8debefddad | ||
|
|
bd25860ccf | ||
|
|
b21cb5c0e9 | ||
|
|
ff79a2d3f4 | ||
|
|
9ae3743a58 | ||
|
|
ffcdbcc802 | ||
|
|
3ff78a3a47 | ||
|
|
4d78ac4789 | ||
|
|
be24f2d520 | ||
|
|
ff7fccb6a2 | ||
|
|
ee5a2b3c38 | ||
|
|
72b9001447 | ||
|
|
353245bb7d | ||
|
|
f9411bf0ed | ||
|
|
0eedbd2aa1 | ||
|
|
5e36c37838 | ||
|
|
35c76c8e2a | ||
|
|
90ad46b7c5 | ||
|
|
1c06e1e2a4 | ||
|
|
15d2c0e436 | ||
|
|
9984aea42f | ||
|
|
a9cc5cc351 | ||
|
|
a6cbabfba5 | ||
|
|
1ef85242ec | ||
|
|
ffe75b0856 | ||
|
|
25e3c4fcdc | ||
|
|
a922497f5d | ||
|
|
acb100908b | ||
|
|
7fa2cbc746 | ||
|
|
b36491af8e | ||
|
|
ea4fe81cb2 | ||
|
|
2191f1b826 | ||
|
|
08f6367752 | ||
|
|
e1b09f4844 | ||
|
|
884c46b054 | ||
|
|
d1cde123dc | ||
|
|
5eaded7e3a | ||
|
|
4ae4e88800 | ||
|
|
85fb27a631 | ||
|
|
8bf0561009 | ||
|
|
ddfded048c | ||
|
|
a2e889587e | ||
|
|
b63aaad645 | ||
|
|
2c41b5d4fb | ||
|
|
34aa4c6412 | ||
|
|
82372ea252 | ||
|
|
1f4440bcf9 | ||
|
|
31fcff4afc | ||
|
|
082f17f940 | ||
|
|
aa2ac3865c | ||
|
|
db8ffe50ac | ||
|
|
8fe658bacc | ||
|
|
c71d5e2dfb | ||
|
|
36c31dcd67 | ||
|
|
c223408c3c | ||
|
|
3cdccb49ef | ||
|
|
0844645a8b | ||
|
|
30bfad455c | ||
|
|
d84fdc3cd5 | ||
|
|
2b64b9de63 | ||
|
|
cdb3d863db | ||
|
|
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 | ||
|
|
9616d858cf | ||
|
|
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 | ||
|
|
d9389c91ee | ||
|
|
480cb00098 | ||
|
|
07ed550dc2 | ||
|
|
6045870398 | ||
|
|
2bf102cdf1 | ||
|
|
889a5b2bce | ||
|
|
df964a094b | ||
|
|
e395d4ecee | ||
|
|
d077e0c83c | ||
|
|
d931241edc | ||
|
|
647376ab3f | ||
|
|
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
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: Coolify's version (see bottom left corner).
|
description: Coolify's version (see top of your screen).
|
||||||
validations:
|
validations:
|
||||||
required: true
|
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
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -40,15 +40,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -64,13 +64,13 @@ jobs:
|
|||||||
needs: [ amd64, aarch64 ]
|
needs: [ amd64, aarch64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
|||||||
20
.github/workflows/coolify-helper.yml
vendored
20
.github/workflows/coolify-helper.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -40,15 +40,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -64,13 +64,13 @@ jobs:
|
|||||||
needs: [ amd64, aarch64 ]
|
needs: [ amd64, aarch64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
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
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -40,15 +40,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -64,13 +64,13 @@ jobs:
|
|||||||
needs: [ amd64, aarch64 ]
|
needs: [ amd64, aarch64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
|||||||
2
.github/workflows/docker-image.yml
vendored
2
.github/workflows/docker-image.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
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`
|
Mails are caught by Mailpit: `localhost:8025`
|
||||||
|
|
||||||
## New Service Contribution
|
## 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).
|
||||||
|
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -28,16 +28,18 @@ https://coolify.io/sponsorships
|
|||||||
|
|
||||||
Thank you so much!
|
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://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+)
|
## Github Sponsors ($40+)
|
||||||
<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://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://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></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://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://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
|
||||||
|
<a href="https://github.com/Flowko"><img src="https://barrad.me/_ipx/f_webp&s_300x300/younes.jpg" width="60px" alt="Younes Barrad" /></a>
|
||||||
|
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Automaze" /></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/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>
|
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
|
||||||
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
|
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
|
||||||
|
|||||||
16
SECURITY.md
Normal file
16
SECURITY.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Use this section to tell people about which versions of your project are
|
||||||
|
currently being supported with security updates.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| > 4 | :white_check_mark: |
|
||||||
|
| 3 | :x: |
|
||||||
|
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you have any vulnerability please report at hi@coollabs.io
|
||||||
159
app/Actions/Database/StartClickhouse.php
Normal file
159
app/Actions/Database/StartClickhouse.php
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StartClickhouse
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public StandaloneClickhouse $database;
|
||||||
|
public array $commands = [];
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
public function handle(StandaloneClickhouse $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
|
||||||
|
$container_name = $this->database->uuid;
|
||||||
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
|
$this->commands = [
|
||||||
|
"echo 'Starting {$database->name}.'",
|
||||||
|
"mkdir -p $this->configuration_dir",
|
||||||
|
];
|
||||||
|
|
||||||
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
|
$environment_variables = $this->generate_environment_variables();
|
||||||
|
|
||||||
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
|
'services' => [
|
||||||
|
$container_name => [
|
||||||
|
'image' => $this->database->image,
|
||||||
|
'container_name' => $container_name,
|
||||||
|
'environment' => $environment_variables,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network,
|
||||||
|
],
|
||||||
|
'ulimits' => [
|
||||||
|
'nofile' => [
|
||||||
|
'soft' => 262144,
|
||||||
|
'hard' => 262144,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'labels' => [
|
||||||
|
'coolify.managed' => 'true',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => "clickhouse-client --password {$this->database->clickhouse_admin_password} --query 'SELECT 1'",
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 10,
|
||||||
|
'start_period' => '5s'
|
||||||
|
],
|
||||||
|
'mem_limit' => $this->database->limits_memory,
|
||||||
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $this->database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
|
}
|
||||||
|
if (count($persistent_storages) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
|
}
|
||||||
|
if (count($volume_names) > 0) {
|
||||||
|
$docker_compose['volumes'] = $volume_names;
|
||||||
|
}
|
||||||
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
|
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes_only_volume_names()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes_names = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes_names[$name] = [
|
||||||
|
'name' => $name,
|
||||||
|
'external' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_environment_variables()
|
||||||
|
{
|
||||||
|
$environment_variables = collect();
|
||||||
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_USER'))->isEmpty()) {
|
||||||
|
$environment_variables->push("CLICKHOUSE_ADMIN_USER={$this->database->clickhouse_admin_user}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_PASSWORD'))->isEmpty()) {
|
||||||
|
$environment_variables->push("CLICKHOUSE_ADMIN_PASSWORD={$this->database->clickhouse_admin_password}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $environment_variables->all();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,9 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -15,7 +18,7 @@ class StartDatabaseProxy
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase $database)
|
||||||
{
|
{
|
||||||
$internalPort = null;
|
$internalPort = null;
|
||||||
$type = $database->getMorphClass();
|
$type = $database->getMorphClass();
|
||||||
@@ -25,7 +28,8 @@ class StartDatabaseProxy
|
|||||||
$proxyContainerName = "{$database->uuid}-proxy";
|
$proxyContainerName = "{$database->uuid}-proxy";
|
||||||
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
$databaseType = $database->databaseType();
|
$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');
|
$server = data_get($database, 'service.destination.server');
|
||||||
$proxyContainerName = "{$database->service->uuid}-proxy";
|
$proxyContainerName = "{$database->service->uuid}-proxy";
|
||||||
switch ($databaseType) {
|
switch ($databaseType) {
|
||||||
@@ -49,6 +53,18 @@ class StartDatabaseProxy
|
|||||||
$type = 'App\Models\StandaloneRedis';
|
$type = 'App\Models\StandaloneRedis';
|
||||||
$containerName = "redis-{$database->service->uuid}";
|
$containerName = "redis-{$database->service->uuid}";
|
||||||
break;
|
break;
|
||||||
|
case 'standalone-keydb':
|
||||||
|
$type = 'App\Models\StandaloneKeydb';
|
||||||
|
$containerName = "keydb-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
|
case 'standalone-dragonfly':
|
||||||
|
$type = 'App\Models\StandaloneDragonfly';
|
||||||
|
$containerName = "dragonfly-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
|
case 'standalone-clickhouse':
|
||||||
|
$type = 'App\Models\StandaloneClickhouse';
|
||||||
|
$containerName = "clickhouse-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($type === 'App\Models\StandaloneRedis') {
|
if ($type === 'App\Models\StandaloneRedis') {
|
||||||
@@ -61,6 +77,12 @@ class StartDatabaseProxy
|
|||||||
$internalPort = 3306;
|
$internalPort = 3306;
|
||||||
} else if ($type === 'App\Models\StandaloneMariadb') {
|
} else if ($type === 'App\Models\StandaloneMariadb') {
|
||||||
$internalPort = 3306;
|
$internalPort = 3306;
|
||||||
|
} else if ($type === 'App\Models\StandaloneKeydb') {
|
||||||
|
$internalPort = 6379;
|
||||||
|
} else if ($type === 'App\Models\StandaloneDragonfly') {
|
||||||
|
$internalPort = 6379;
|
||||||
|
} else if ($type === 'App\Models\StandaloneClickhouse') {
|
||||||
|
$internalPort = 9000;
|
||||||
}
|
}
|
||||||
$configuration_dir = database_proxy_dir($database->uuid);
|
$configuration_dir = database_proxy_dir($database->uuid);
|
||||||
$nginxconf = <<<EOF
|
$nginxconf = <<<EOF
|
||||||
@@ -124,6 +146,7 @@ class StartDatabaseProxy
|
|||||||
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
||||||
$nginxconf_base64 = base64_encode($nginxconf);
|
$nginxconf_base64 = base64_encode($nginxconf);
|
||||||
$dockerfile_base64 = base64_encode($dockerfile);
|
$dockerfile_base64 = base64_encode($dockerfile);
|
||||||
|
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
|
||||||
instant_remote_process([
|
instant_remote_process([
|
||||||
"mkdir -p $configuration_dir",
|
"mkdir -p $configuration_dir",
|
||||||
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||||
|
|||||||
156
app/Actions/Database/StartDragonfly.php
Normal file
156
app/Actions/Database/StartDragonfly.php
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StartDragonfly
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public StandaloneDragonfly $database;
|
||||||
|
public array $commands = [];
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
|
||||||
|
public function handle(StandaloneDragonfly $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
$startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}";
|
||||||
|
|
||||||
|
$container_name = $this->database->uuid;
|
||||||
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
|
$this->commands = [
|
||||||
|
"echo 'Starting {$database->name}.'",
|
||||||
|
"mkdir -p $this->configuration_dir",
|
||||||
|
];
|
||||||
|
|
||||||
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
|
$environment_variables = $this->generate_environment_variables();
|
||||||
|
|
||||||
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
|
'services' => [
|
||||||
|
$container_name => [
|
||||||
|
'image' => $this->database->image,
|
||||||
|
'command' => $startCommand,
|
||||||
|
'container_name' => $container_name,
|
||||||
|
'environment' => $environment_variables,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network,
|
||||||
|
],
|
||||||
|
'ulimits' => [
|
||||||
|
'memlock'=> '-1'
|
||||||
|
],
|
||||||
|
'labels' => [
|
||||||
|
'coolify.managed' => 'true',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => "redis-cli -a {$this->database->dragonfly_password} ping",
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 10,
|
||||||
|
'start_period' => '5s'
|
||||||
|
],
|
||||||
|
'mem_limit' => $this->database->limits_memory,
|
||||||
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $this->database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
|
}
|
||||||
|
if (count($persistent_storages) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
|
}
|
||||||
|
if (count($volume_names) > 0) {
|
||||||
|
$docker_compose['volumes'] = $volume_names;
|
||||||
|
}
|
||||||
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
|
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes_only_volume_names()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes_names = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes_names[$name] = [
|
||||||
|
'name' => $name,
|
||||||
|
'external' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_environment_variables()
|
||||||
|
{
|
||||||
|
$environment_variables = collect();
|
||||||
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||||
|
$environment_variables->push("REDIS_PASSWORD={$this->database->dragonfly_password}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $environment_variables->all();
|
||||||
|
}
|
||||||
|
}
|
||||||
174
app/Actions/Database/StartKeydb.php
Normal file
174
app/Actions/Database/StartKeydb.php
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StartKeydb
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public StandaloneKeydb $database;
|
||||||
|
public array $commands = [];
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
|
||||||
|
public function handle(StandaloneKeydb $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
$startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes";
|
||||||
|
|
||||||
|
$container_name = $this->database->uuid;
|
||||||
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
|
$this->commands = [
|
||||||
|
"echo 'Starting {$database->name}.'",
|
||||||
|
"mkdir -p $this->configuration_dir",
|
||||||
|
];
|
||||||
|
|
||||||
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
|
$environment_variables = $this->generate_environment_variables();
|
||||||
|
$this->add_custom_keydb();
|
||||||
|
|
||||||
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
|
'services' => [
|
||||||
|
$container_name => [
|
||||||
|
'image' => $this->database->image,
|
||||||
|
'command' => $startCommand,
|
||||||
|
'container_name' => $container_name,
|
||||||
|
'environment' => $environment_variables,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network,
|
||||||
|
],
|
||||||
|
'labels' => [
|
||||||
|
'coolify.managed' => 'true',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => "keydb-cli --pass {$this->database->keydb_password} ping",
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 10,
|
||||||
|
'start_period' => '5s'
|
||||||
|
],
|
||||||
|
'mem_limit' => $this->database->limits_memory,
|
||||||
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $this->database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
|
}
|
||||||
|
if (count($persistent_storages) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
|
}
|
||||||
|
if (count($volume_names) > 0) {
|
||||||
|
$docker_compose['volumes'] = $volume_names;
|
||||||
|
}
|
||||||
|
if (!is_null($this->database->keydb_conf)) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
|
'type' => 'bind',
|
||||||
|
'source' => $this->configuration_dir . '/keydb.conf',
|
||||||
|
'target' => '/etc/keydb/keydb.conf',
|
||||||
|
'read_only' => true,
|
||||||
|
];
|
||||||
|
$docker_compose['services'][$container_name]['command'] = "keydb-server /etc/keydb/keydb.conf --requirepass {$this->database->keydb_password} --appendonly yes";
|
||||||
|
}
|
||||||
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
|
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes_only_volume_names()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes_names = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes_names[$name] = [
|
||||||
|
'name' => $name,
|
||||||
|
'external' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_environment_variables()
|
||||||
|
{
|
||||||
|
$environment_variables = collect();
|
||||||
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||||
|
$environment_variables->push("REDIS_PASSWORD={$this->database->keydb_password}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $environment_variables->all();
|
||||||
|
}
|
||||||
|
private function add_custom_keydb()
|
||||||
|
{
|
||||||
|
if (is_null($this->database->keydb_conf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$filename = 'keydb.conf';
|
||||||
|
Storage::disk('local')->put("tmp/keydb.conf_{$this->database->uuid}", $this->database->keydb_conf);
|
||||||
|
$path = Storage::path("tmp/keydb.conf_{$this->database->uuid}");
|
||||||
|
instant_scp($path, "{$this->configuration_dir}/{$filename}", $this->database->destination->server);
|
||||||
|
Storage::disk('local')->delete("tmp/keydb.conf_{$this->database->uuid}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -114,8 +114,12 @@ class StartMariadb
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,8 +130,12 @@ class StartMongodb
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,8 +114,12 @@ class StartMysql
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,8 +136,12 @@ class StartPostgresql
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,8 +125,12 @@ class StartRedis
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Events\DatabaseStatusChanged;
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -14,7 +16,7 @@ class StopDatabase
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
|
||||||
{
|
{
|
||||||
$server = $database->destination->server;
|
$server = $database->destination->server;
|
||||||
if (!$server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -14,13 +17,15 @@ class StopDatabaseProxy
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|ServiceDatabase|StandaloneDragonfly|StandaloneClickhouse $database)
|
||||||
{
|
{
|
||||||
$server = data_get($database, 'destination.server');
|
$server = data_get($database, 'destination.server');
|
||||||
|
$uuid = $database->uuid;
|
||||||
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
|
$uuid = $database->service->uuid;
|
||||||
$server = data_get($database, 'service.server');
|
$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->is_public = false;
|
||||||
$database->save();
|
$database->save();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ class CheckProxy
|
|||||||
if ($server->proxyType() === 'NONE') {
|
if ($server->proxyType() === 'NONE') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!$server->validateConnection()) {
|
||||||
|
throw new \Exception("Server Connection Error");
|
||||||
|
}
|
||||||
if (!$server->isProxyShouldRun()) {
|
if (!$server->isProxyShouldRun()) {
|
||||||
if ($fromUI) {
|
if ($fromUI) {
|
||||||
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
||||||
@@ -35,6 +38,9 @@ class CheckProxy
|
|||||||
$server->save();
|
$server->save();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if ($server->settings->is_cloudflare_tunnel) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$ip = $server->ip;
|
$ip = $server->ip;
|
||||||
if ($server->id === 0) {
|
if ($server->id === 0) {
|
||||||
$ip = 'host.docker.internal';
|
$ip = 'host.docker.internal';
|
||||||
@@ -46,14 +52,14 @@ class CheckProxy
|
|||||||
$port443 = is_resource($connection443) && fclose($connection443);
|
$port443 = is_resource($connection443) && fclose($connection443);
|
||||||
if ($port80) {
|
if ($port80) {
|
||||||
if ($fromUI) {
|
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 {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($port443) {
|
if ($port443) {
|
||||||
if ($fromUI) {
|
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 {
|
} else {
|
||||||
return false;
|
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();
|
$supported_os_type = $server->validateOS();
|
||||||
if (!$supported_os_type) {
|
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);
|
ray('Installing Docker on server: ' . $server->name . ' (' . $server->ip . ')' . ' with OS type: ' . $supported_os_type);
|
||||||
$dockerVersion = '24.0';
|
$dockerVersion = '24.0';
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ class UpdateCoolify
|
|||||||
public ?Server $server = null;
|
public ?Server $server = null;
|
||||||
public ?string $latestVersion = null;
|
public ?string $latestVersion = null;
|
||||||
public ?string $currentVersion = null;
|
public ?string $currentVersion = null;
|
||||||
|
public bool $async = false;
|
||||||
|
|
||||||
public function handle(bool $force)
|
public function handle(bool $force = false, bool $async = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$this->async = $async;
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
ray('Running InstanceAutoUpdateJob');
|
ray('Running InstanceAutoUpdateJob');
|
||||||
$this->server = Server::find(0);
|
$this->server = Server::find(0);
|
||||||
@@ -43,7 +45,6 @@ class UpdateCoolify
|
|||||||
}
|
}
|
||||||
$this->update();
|
$this->update();
|
||||||
}
|
}
|
||||||
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray('InstanceAutoUpdateJob failed');
|
ray('InstanceAutoUpdateJob failed');
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
@@ -56,17 +57,32 @@ class UpdateCoolify
|
|||||||
{
|
{
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
ray("Running update on local docker container. Updating to $this->latestVersion");
|
ray("Running update on local docker container. Updating to $this->latestVersion");
|
||||||
remote_process([
|
if ($this->async) {
|
||||||
"sleep 10"
|
ray('Running async update');
|
||||||
], $this->server);
|
remote_process([
|
||||||
|
"sleep 10"
|
||||||
|
], $this->server);
|
||||||
|
} else {
|
||||||
|
instant_remote_process([
|
||||||
|
"sleep 10"
|
||||||
|
], $this->server);
|
||||||
|
}
|
||||||
ray('Update done');
|
ray('Update done');
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
ray('Running update on production server');
|
ray('Running update on production server');
|
||||||
remote_process([
|
if ($this->async) {
|
||||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
remote_process([
|
||||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||||
], $this->server);
|
"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);
|
||||||
|
}
|
||||||
|
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class CleanupDatabase extends Command
|
|||||||
$keep_days = 60;
|
$keep_days = 60;
|
||||||
echo "Keep days: $keep_days\n";
|
echo "Keep days: $keep_days\n";
|
||||||
// Cleanup failed jobs table
|
// 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();
|
$count = $failed_jobs->count();
|
||||||
echo "Delete $count entries from failed_jobs.\n";
|
echo "Delete $count entries from failed_jobs.\n";
|
||||||
if ($this->option('yes')) {
|
if ($this->option('yes')) {
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ use App\Models\ScheduledTask;
|
|||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -55,6 +58,33 @@ class CleanupStuckedResources extends Command
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
$keydbs = StandaloneKeydb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($keydbs as $keydb) {
|
||||||
|
echo "Deleting stuck keydb: {$keydb->name}\n";
|
||||||
|
$redis->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck keydb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$dragonflies = StandaloneDragonfly::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($dragonflies as $dragonfly) {
|
||||||
|
echo "Deleting stuck dragonfly: {$dragonfly->name}\n";
|
||||||
|
$redis->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck dragonfly: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$clickhouses = StandaloneClickhouse::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($clickhouses as $clickhouse) {
|
||||||
|
echo "Deleting stuck clickhouse: {$clickhouse->name}\n";
|
||||||
|
$redis->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck clickhouse: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($mongodbs as $mongodb) {
|
foreach ($mongodbs as $mongodb) {
|
||||||
|
|||||||
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->cleanup_stucked_helper_containers();
|
||||||
$this->call('cleanup:queue');
|
$this->call('cleanup:queue');
|
||||||
$this->call('cleanup:stucked-resources');
|
$this->call('cleanup:stucked-resources');
|
||||||
try {
|
if (!isCloud()) {
|
||||||
$server = Server::find(0)->first();
|
try {
|
||||||
$server->setupDynamicProxyConfiguration();
|
$server = Server::find(0)->first();
|
||||||
} catch (\Throwable $e) {
|
$server->setupDynamicProxyConfiguration();
|
||||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
} catch (\Throwable $e) {
|
||||||
|
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$settings = InstanceSettings::get();
|
$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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ class Kernel extends ConsoleKernel
|
|||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->pull_helper_image($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
$this->check_scheduled_tasks($schedule);
|
$this->check_scheduled_tasks($schedule);
|
||||||
|
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||||
} else {
|
} else {
|
||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
@@ -48,9 +49,8 @@ class Kernel extends ConsoleKernel
|
|||||||
$this->pull_helper_image($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
$this->check_scheduled_tasks($schedule);
|
$this->check_scheduled_tasks($schedule);
|
||||||
|
|
||||||
if (!isCloud()) {
|
$schedule->command('cleanup:database --yes')->daily();
|
||||||
$schedule->command('cleanup:database --yes')->daily();
|
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function pull_helper_image($schedule)
|
private function pull_helper_image($schedule)
|
||||||
@@ -72,43 +72,14 @@ class Kernel extends ConsoleKernel
|
|||||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||||
}
|
}
|
||||||
foreach ($containerServers as $server) {
|
foreach ($containerServers as $server) {
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ContainerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
||||||
if ($server->isLogDrainEnabled()) {
|
if ($server->isLogDrainEnabled()) {
|
||||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new CheckLogDrainContainerJob($server))->everyTwoMinutes()->onOneServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($servers as $server) {
|
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)
|
private function instance_auto_update($schedule)
|
||||||
{
|
{
|
||||||
@@ -152,6 +123,9 @@ class Kernel extends ConsoleKernel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach ($scheduled_tasks as $scheduled_task) {
|
foreach ($scheduled_tasks as $scheduled_task) {
|
||||||
|
if ($scheduled_task->enabled === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$service = $scheduled_task->service;
|
$service = $scheduled_task->service;
|
||||||
$application = $scheduled_task->application;
|
$application = $scheduled_task->application;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartClickhouse;
|
||||||
|
use App\Actions\Database\StartDragonfly;
|
||||||
|
use App\Actions\Database\StartKeydb;
|
||||||
use App\Actions\Database\StartMariadb;
|
use App\Actions\Database\StartMariadb;
|
||||||
use App\Actions\Database\StartMongodb;
|
use App\Actions\Database\StartMongodb;
|
||||||
use App\Actions\Database\StartMysql;
|
use App\Actions\Database\StartMysql;
|
||||||
@@ -44,7 +47,7 @@ class Deploy extends Controller
|
|||||||
$force = $request->query->get('force') ?? false;
|
$force = $request->query->get('force') ?? false;
|
||||||
|
|
||||||
if ($uuids && $tags) {
|
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)) {
|
if (is_null($teamId)) {
|
||||||
return invalid_token();
|
return invalid_token();
|
||||||
@@ -54,7 +57,7 @@ class Deploy extends Controller
|
|||||||
} else if ($uuids) {
|
} else if ($uuids) {
|
||||||
return $this->by_uuids($uuids, $teamId, $force);
|
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)
|
private function by_uuids(string $uuid, int $teamId, bool $force = false)
|
||||||
{
|
{
|
||||||
@@ -62,7 +65,7 @@ class Deploy extends Controller
|
|||||||
$uuids = collect(array_filter($uuids));
|
$uuids = collect(array_filter($uuids));
|
||||||
|
|
||||||
if (count($uuids) === 0) {
|
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();
|
$deployments = collect();
|
||||||
$payload = collect();
|
$payload = collect();
|
||||||
@@ -81,7 +84,7 @@ class Deploy extends Controller
|
|||||||
$payload->put('deployments', $deployments->toArray());
|
$payload->put('deployments', $deployments->toArray());
|
||||||
return response()->json($payload->toArray(), 200);
|
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)
|
public function by_tags(string $tags, int $team_id, bool $force = false)
|
||||||
{
|
{
|
||||||
@@ -89,7 +92,7 @@ class Deploy extends Controller
|
|||||||
$tags = collect(array_filter($tags));
|
$tags = collect(array_filter($tags));
|
||||||
|
|
||||||
if (count($tags) === 0) {
|
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([]);
|
$message = collect([]);
|
||||||
$deployments = collect();
|
$deployments = collect();
|
||||||
@@ -127,7 +130,7 @@ class Deploy extends Controller
|
|||||||
return response()->json($payload->toArray(), 200);
|
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
|
public function deploy_resource($resource, bool $force = false): array
|
||||||
{
|
{
|
||||||
@@ -157,6 +160,24 @@ class Deploy extends Controller
|
|||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message = "Database {$resource->name} started.";
|
||||||
|
} else if ($type === 'App\Models\StandaloneKeydb') {
|
||||||
|
StartKeydb::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} else if ($type === 'App\Models\StandaloneDragonfly') {
|
||||||
|
StartDragonfly::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} else if ($type === 'App\Models\StandaloneClickhouse') {
|
||||||
|
StartClickhouse::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
} else if ($type === 'App\Models\StandaloneMongodb') {
|
} else if ($type === 'App\Models\StandaloneMongodb') {
|
||||||
StartMongodb::run($resource);
|
StartMongodb::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class Team extends Controller
|
|||||||
$teams = auth()->user()->teams;
|
$teams = auth()->user()->teams;
|
||||||
$team = $teams->where('id', $id)->first();
|
$team = $teams->where('id', $id)->first();
|
||||||
if (is_null($team)) {
|
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);
|
return response()->json($team);
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ class Team extends Controller
|
|||||||
$teams = auth()->user()->teams;
|
$teams = auth()->user()->teams;
|
||||||
$team = $teams->where('id', $id)->first();
|
$team = $teams->where('id', $id)->first();
|
||||||
if (is_null($team)) {
|
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);
|
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')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
app/Http/Controllers/UploadController.php
Normal file
83
app/Http/Controllers/UploadController.php
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Pion\Laravel\ChunkUpload\Exceptions\UploadFailedException;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
|
||||||
|
use Pion\Laravel\ChunkUpload\Handler\AbstractHandler;
|
||||||
|
use Pion\Laravel\ChunkUpload\Handler\HandlerFactory;
|
||||||
|
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
|
||||||
|
|
||||||
|
class UploadController extends BaseController
|
||||||
|
{
|
||||||
|
public function upload(Request $request)
|
||||||
|
{
|
||||||
|
$resource = getResourceByUuid(request()->route('databaseUuid'), data_get(auth()->user()->currentTeam(), 'id'));
|
||||||
|
if (is_null($resource)) {
|
||||||
|
return response()->json(['error' => 'You do not have permission for this database'], 500);
|
||||||
|
}
|
||||||
|
$receiver = new FileReceiver("file", $request, HandlerFactory::classFromRequest($request));
|
||||||
|
|
||||||
|
if ($receiver->isUploaded() === false) {
|
||||||
|
throw new UploadMissingFileException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$save = $receiver->receive();
|
||||||
|
|
||||||
|
if ($save->isFinished()) {
|
||||||
|
return $this->saveFile($save->getFile(), $resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
$handler = $save->handler();
|
||||||
|
return response()->json([
|
||||||
|
"done" => $handler->getPercentageDone(),
|
||||||
|
'status' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// protected function saveFileToS3($file)
|
||||||
|
// {
|
||||||
|
// $fileName = $this->createFilename($file);
|
||||||
|
|
||||||
|
// $disk = Storage::disk('s3');
|
||||||
|
// // It's better to use streaming Streaming (laravel 5.4+)
|
||||||
|
// $disk->putFileAs('photos', $file, $fileName);
|
||||||
|
|
||||||
|
// // for older laravel
|
||||||
|
// // $disk->put($fileName, file_get_contents($file), 'public');
|
||||||
|
// $mime = str_replace('/', '-', $file->getMimeType());
|
||||||
|
|
||||||
|
// // We need to delete the file when uploaded to s3
|
||||||
|
// unlink($file->getPathname());
|
||||||
|
|
||||||
|
// return response()->json([
|
||||||
|
// 'path' => $disk->url($fileName),
|
||||||
|
// 'name' => $fileName,
|
||||||
|
// 'mime_type' => $mime
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
protected function saveFile(UploadedFile $file, $resource)
|
||||||
|
{
|
||||||
|
$mime = str_replace('/', '-', $file->getMimeType());
|
||||||
|
$filePath = "upload/{$resource->uuid}";
|
||||||
|
$finalPath = storage_path("app/" . $filePath);
|
||||||
|
$file->move($finalPath, 'restore');
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'mime_type' => $mime
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
protected function createFilename(UploadedFile $file)
|
||||||
|
{
|
||||||
|
$extension = $file->getClientOriginalExtension();
|
||||||
|
$filename = str_replace("." . $extension, "", $file->getClientOriginalName()); // Filename without extension
|
||||||
|
|
||||||
|
$filename .= "_" . md5(time()) . "." . $extension;
|
||||||
|
|
||||||
|
return $filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,6 @@ class Github extends Controller
|
|||||||
public function manual(Request $request)
|
public function manual(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
ray($request);
|
|
||||||
$return_payloads = collect([]);
|
$return_payloads = collect([]);
|
||||||
$x_github_delivery = request()->header('X-GitHub-Delivery');
|
$x_github_delivery = request()->header('X-GitHub-Delivery');
|
||||||
if (app()->isDownForMaintenance()) {
|
if (app()->isDownForMaintenance()) {
|
||||||
@@ -68,6 +67,10 @@ class Github extends Controller
|
|||||||
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||||
$branch = Str::after($branch, 'refs/heads/');
|
$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);
|
ray('Manual Webhook GitHub Push Event with branch: ' . $branch);
|
||||||
}
|
}
|
||||||
if ($x_github_event === 'pull_request') {
|
if ($x_github_event === 'pull_request') {
|
||||||
@@ -118,24 +121,41 @@ class Github extends Controller
|
|||||||
}
|
}
|
||||||
if ($x_github_event === 'push') {
|
if ($x_github_event === 'push') {
|
||||||
if ($application->isDeployable()) {
|
if ($application->isDeployable()) {
|
||||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
$deployment_uuid = new Cuid2(7);
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
queue_application_deployment(
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
application: $application,
|
$deployment_uuid = new Cuid2(7);
|
||||||
deployment_uuid: $deployment_uuid,
|
queue_application_deployment(
|
||||||
force_rebuild: false,
|
application: $application,
|
||||||
is_webhook: true,
|
deployment_uuid: $deployment_uuid,
|
||||||
);
|
force_rebuild: false,
|
||||||
$return_payloads->push([
|
is_webhook: true,
|
||||||
'application' => $application->name,
|
);
|
||||||
'status' => 'success',
|
$return_payloads->push([
|
||||||
'message' => 'Deployment queued.',
|
'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 {
|
} else {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Deployments disabled.',
|
'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)) {
|
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||||
$branch = Str::after($branch, 'refs/heads/');
|
$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);
|
ray('Webhook GitHub Push Event: ' . $id . ' with branch: ' . $branch);
|
||||||
}
|
}
|
||||||
if ($x_github_event === 'pull_request') {
|
if ($x_github_event === 'pull_request') {
|
||||||
@@ -298,32 +322,50 @@ class Github extends Controller
|
|||||||
$isFunctional = $application->destination->server->isFunctional();
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
if (!$isFunctional) {
|
if (!$isFunctional) {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Server is not functional.',
|
'message' => 'Server is not functional.',
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'application_name' => $application->name,
|
||||||
]);
|
]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($x_github_event === 'push') {
|
if ($x_github_event === 'push') {
|
||||||
if ($application->isDeployable()) {
|
if ($application->isDeployable()) {
|
||||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
$deployment_uuid = new Cuid2(7);
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
queue_application_deployment(
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
application: $application,
|
$deployment_uuid = new Cuid2(7);
|
||||||
deployment_uuid: $deployment_uuid,
|
queue_application_deployment(
|
||||||
force_rebuild: false,
|
application: $application,
|
||||||
is_webhook: true
|
deployment_uuid: $deployment_uuid,
|
||||||
);
|
force_rebuild: false,
|
||||||
$return_payloads->push([
|
is_webhook: true,
|
||||||
'application' => $application->name,
|
);
|
||||||
'status' => 'success',
|
$return_payloads->push([
|
||||||
'message' => 'Deployment queued.',
|
'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 {
|
} else {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Deployments disabled.',
|
'message' => 'Deployments disabled.',
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'application_name' => $application->name,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,10 @@ class Gitlab extends Controller
|
|||||||
]);
|
]);
|
||||||
return response($return_payloads);
|
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);
|
ray('Manual Webhook GitLab Push Event with branch: ' . $branch);
|
||||||
}
|
}
|
||||||
if ($x_gitlab_event === 'merge_request') {
|
if ($x_gitlab_event === 'merge_request') {
|
||||||
@@ -113,19 +117,41 @@ class Gitlab extends Controller
|
|||||||
}
|
}
|
||||||
if ($x_gitlab_event === 'push') {
|
if ($x_gitlab_event === 'push') {
|
||||||
if ($application->isDeployable()) {
|
if ($application->isDeployable()) {
|
||||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
$deployment_uuid = new Cuid2(7);
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
queue_application_deployment(
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
application: $application,
|
$deployment_uuid = new Cuid2(7);
|
||||||
deployment_uuid: $deployment_uuid,
|
queue_application_deployment(
|
||||||
force_rebuild: false,
|
application: $application,
|
||||||
is_webhook: true
|
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 {
|
} else {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Deployments disabled',
|
'message' => 'Deployments disabled',
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'application_name' => $application->name,
|
||||||
]);
|
]);
|
||||||
ray('Deployments disabled for ' . $application->name);
|
ray('Deployments disabled for ' . $application->name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
private $env_nixpacks_args;
|
private $env_nixpacks_args;
|
||||||
private $docker_compose;
|
private $docker_compose;
|
||||||
private $docker_compose_base64;
|
private $docker_compose_base64;
|
||||||
|
private ?string $env_filename = null;
|
||||||
private ?string $nixpacks_plan = null;
|
private ?string $nixpacks_plan = null;
|
||||||
private ?string $nixpacks_type = null;
|
private ?string $nixpacks_type = null;
|
||||||
private string $dockerfile_location = '/Dockerfile';
|
private string $dockerfile_location = '/Dockerfile';
|
||||||
@@ -179,6 +180,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
|
if (!$this->server->isFunctional()) {
|
||||||
|
$this->application_deployment_queue->addLogEntry("Server is not functional.");
|
||||||
|
$this->fail("Server is not functional.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// Generate custom host<->ip mapping
|
// Generate custom host<->ip mapping
|
||||||
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
||||||
@@ -234,50 +240,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->build_server = $this->server;
|
$this->build_server = $this->server;
|
||||||
$this->original_server = $this->server;
|
$this->original_server = $this->server;
|
||||||
}
|
}
|
||||||
if ($this->restart_only && $this->application->build_pack !== 'dockerimage') {
|
$this->decide_what_to_do();
|
||||||
$this->just_restart();
|
|
||||||
if ($this->server->isProxyShouldRun()) {
|
|
||||||
dispatch(new ContainerStatusJob($this->server));
|
|
||||||
}
|
|
||||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
|
||||||
$this->application->isConfigurationChanged(false);
|
|
||||||
$this->run_post_deployment_command();
|
|
||||||
return;
|
|
||||||
} else if ($this->pull_request_id !== 0) {
|
|
||||||
$this->deploy_pull_request();
|
|
||||||
} else if ($this->application->dockerfile) {
|
|
||||||
$this->deploy_simple_dockerfile();
|
|
||||||
} else if ($this->application->build_pack === 'dockercompose') {
|
|
||||||
$this->deploy_docker_compose_buildpack();
|
|
||||||
} else if ($this->application->build_pack === 'dockerimage') {
|
|
||||||
$this->deploy_dockerimage_buildpack();
|
|
||||||
} else if ($this->application->build_pack === 'dockerfile') {
|
|
||||||
$this->deploy_dockerfile_buildpack();
|
|
||||||
} else if ($this->application->build_pack === 'static') {
|
|
||||||
$this->deploy_static_buildpack();
|
|
||||||
} else {
|
|
||||||
$this->deploy_nixpacks_buildpack();
|
|
||||||
}
|
|
||||||
if ($this->server->isProxyShouldRun()) {
|
|
||||||
dispatch(new ContainerStatusJob($this->server));
|
|
||||||
}
|
|
||||||
// Otherwise built image needs to be pushed before from the build server.
|
|
||||||
// ray($this->use_build_server);
|
|
||||||
// if (!$this->use_build_server) {
|
|
||||||
// if ($this->application->additional_servers->count() > 0) {
|
|
||||||
// $this->push_to_docker_registry(forceFail: true);
|
|
||||||
// } else {
|
|
||||||
// $this->push_to_docker_registry();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
|
||||||
if ($this->pull_request_id !== 0) {
|
|
||||||
if ($this->application->is_github_based()) {
|
|
||||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->run_post_deployment_command();
|
|
||||||
$this->application->isConfigurationChanged(true);
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
|
if ($this->pull_request_id !== 0 && $this->application->is_github_based()) {
|
||||||
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
|
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::ERROR);
|
||||||
@@ -298,6 +261,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
"ignore_errors" => true,
|
"ignore_errors" => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// $this->execute_remote_command(
|
// $this->execute_remote_command(
|
||||||
// [
|
// [
|
||||||
// "docker image prune -f >/dev/null 2>&1",
|
// "docker image prune -f >/dev/null 2>&1",
|
||||||
@@ -305,9 +270,48 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// "ignore_errors" => true,
|
// "ignore_errors" => true,
|
||||||
// ]
|
// ]
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
|
||||||
ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
|
ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private function decide_what_to_do()
|
||||||
|
{
|
||||||
|
if ($this->restart_only && $this->application->build_pack !== 'dockerimage' && $this->application->build_pack !== 'dockerfile') {
|
||||||
|
$this->just_restart();
|
||||||
|
return;
|
||||||
|
} else if ($this->pull_request_id !== 0) {
|
||||||
|
$this->deploy_pull_request();
|
||||||
|
} else if ($this->application->dockerfile) {
|
||||||
|
$this->deploy_simple_dockerfile();
|
||||||
|
} else if ($this->application->build_pack === 'dockercompose') {
|
||||||
|
$this->deploy_docker_compose_buildpack();
|
||||||
|
} else if ($this->application->build_pack === 'dockerimage') {
|
||||||
|
$this->deploy_dockerimage_buildpack();
|
||||||
|
} else if ($this->application->build_pack === 'dockerfile') {
|
||||||
|
$this->deploy_dockerfile_buildpack();
|
||||||
|
} else if ($this->application->build_pack === 'static') {
|
||||||
|
$this->deploy_static_buildpack();
|
||||||
|
} else {
|
||||||
|
$this->deploy_nixpacks_buildpack();
|
||||||
|
}
|
||||||
|
$this->post_deployment();
|
||||||
|
}
|
||||||
|
private function post_deployment()
|
||||||
|
{
|
||||||
|
|
||||||
|
if ($this->server->isProxyShouldRun()) {
|
||||||
|
dispatch(new ContainerStatusJob($this->server));
|
||||||
|
}
|
||||||
|
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||||
|
if ($this->pull_request_id !== 0) {
|
||||||
|
if ($this->application->is_github_based()) {
|
||||||
|
ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::FINISHED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->run_post_deployment_command();
|
||||||
|
$this->application->isConfigurationChanged(true);
|
||||||
|
}
|
||||||
private function deploy_simple_dockerfile()
|
private function deploy_simple_dockerfile()
|
||||||
{
|
{
|
||||||
if ($this->use_build_server) {
|
if ($this->use_build_server) {
|
||||||
@@ -322,17 +326,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
$this->generate_image_names();
|
$this->generate_image_names();
|
||||||
if (!$this->force_rebuild) {
|
|
||||||
$this->check_image_locally_or_remotely();
|
// Always rebuild dockerfile based container.
|
||||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
// if (!$this->force_rebuild) {
|
||||||
$this->create_workdir();
|
// $this->check_image_locally_or_remotely();
|
||||||
$this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
// if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||||
$this->generate_compose_file();
|
// $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||||
$this->push_to_docker_registry();
|
// $this->generate_compose_file();
|
||||||
$this->rolling_update();
|
// $this->push_to_docker_registry();
|
||||||
return;
|
// $this->rolling_update();
|
||||||
}
|
// return;
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->generate_build_env_variables();
|
$this->generate_build_env_variables();
|
||||||
$this->add_build_env_variables_to_dockerfile();
|
$this->add_build_env_variables_to_dockerfile();
|
||||||
@@ -417,15 +422,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
"docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
|
"docker network connect {$networkId} coolify-proxy || true", "hidden" => true, "ignore_errors" => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$this->write_deployment_configurations();
|
|
||||||
|
|
||||||
// Start compose file
|
// Start compose file
|
||||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||||
if ($this->docker_compose_custom_start_command) {
|
if ($this->docker_compose_custom_start_command) {
|
||||||
$this->execute_remote_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 {
|
} else {
|
||||||
|
$this->write_deployment_configurations();
|
||||||
$server_workdir = $this->application->workdir();
|
$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");
|
ray("SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d");
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
@@ -437,14 +443,15 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
|
||||||
);
|
);
|
||||||
|
$this->write_deployment_configurations();
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command(
|
$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],
|
[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.");
|
$this->application_deployment_queue->addLogEntry("New container started.");
|
||||||
}
|
}
|
||||||
private function deploy_dockerfile_buildpack()
|
private function deploy_dockerfile_buildpack()
|
||||||
@@ -464,7 +471,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if (!$this->force_rebuild) {
|
if (!$this->force_rebuild) {
|
||||||
$this->check_image_locally_or_remotely();
|
$this->check_image_locally_or_remotely();
|
||||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
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->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->generate_compose_file();
|
||||||
$this->push_to_docker_registry();
|
$this->push_to_docker_registry();
|
||||||
@@ -493,7 +499,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if (!$this->force_rebuild) {
|
if (!$this->force_rebuild) {
|
||||||
$this->check_image_locally_or_remotely();
|
$this->check_image_locally_or_remotely();
|
||||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
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->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->generate_compose_file();
|
||||||
ray('pushing to docker registry');
|
ray('pushing to docker registry');
|
||||||
@@ -527,7 +532,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if (!$this->force_rebuild) {
|
if (!$this->force_rebuild) {
|
||||||
$this->check_image_locally_or_remotely();
|
$this->check_image_locally_or_remotely();
|
||||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
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->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->generate_compose_file();
|
||||||
$this->push_to_docker_registry();
|
$this->push_to_docker_registry();
|
||||||
@@ -638,21 +642,21 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
if ($this->application->dockerfile) {
|
if ($this->application->dockerfile) {
|
||||||
if ($this->application->docker_registry_image_name) {
|
if ($this->application->docker_registry_image_name) {
|
||||||
$this->build_image_name = Str::lower("{$this->application->docker_registry_image_name}:build");
|
$this->build_image_name = "{$this->application->docker_registry_image_name}:build";
|
||||||
$this->production_image_name = Str::lower("{$this->application->docker_registry_image_name}:latest");
|
$this->production_image_name = "{$this->application->docker_registry_image_name}:latest";
|
||||||
} else {
|
} else {
|
||||||
$this->build_image_name = Str::lower("{$this->application->uuid}:build");
|
$this->build_image_name = "{$this->application->uuid}:build";
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:latest");
|
$this->production_image_name = "{$this->application->uuid}:latest";
|
||||||
}
|
}
|
||||||
} else if ($this->application->build_pack === 'dockerimage') {
|
} else if ($this->application->build_pack === 'dockerimage') {
|
||||||
$this->production_image_name = Str::lower("{$this->dockerImage}:{$this->dockerImageTag}");
|
$this->production_image_name = "{$this->dockerImage}:{$this->dockerImageTag}";
|
||||||
} else if ($this->pull_request_id !== 0) {
|
} else if ($this->pull_request_id !== 0) {
|
||||||
if ($this->application->docker_registry_image_name) {
|
if ($this->application->docker_registry_image_name) {
|
||||||
$this->build_image_name = Str::lower("{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}-build");
|
$this->build_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}-build";
|
||||||
$this->production_image_name = Str::lower("{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}");
|
$this->production_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}";
|
||||||
} else {
|
} else {
|
||||||
$this->build_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}-build");
|
$this->build_image_name = "{$this->application->uuid}:pr-{$this->pull_request_id}-build";
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}");
|
$this->production_image_name = "{$this->application->uuid}:pr-{$this->pull_request_id}";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->dockerImageTag = str($this->commit)->substr(0, 128);
|
$this->dockerImageTag = str($this->commit)->substr(0, 128);
|
||||||
@@ -660,17 +664,17 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
$this->dockerImageTag = $this->application->docker_registry_image_tag;
|
||||||
}
|
}
|
||||||
if ($this->application->docker_registry_image_name) {
|
if ($this->application->docker_registry_image_name) {
|
||||||
$this->build_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}-build");
|
$this->build_image_name = "{$this->application->docker_registry_image_name}:{$this->dockerImageTag}-build";
|
||||||
$this->production_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}");
|
$this->production_image_name = "{$this->application->docker_registry_image_name}:{$this->dockerImageTag}";
|
||||||
} else {
|
} else {
|
||||||
$this->build_image_name = Str::lower("{$this->application->uuid}:{$this->dockerImageTag}-build");
|
$this->build_image_name = "{$this->application->uuid}:{$this->dockerImageTag}-build";
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:{$this->dockerImageTag}");
|
$this->production_image_name = "{$this->application->uuid}:{$this->dockerImageTag}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function just_restart()
|
private function just_restart()
|
||||||
{
|
{
|
||||||
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
|
$this->application_deployment_queue->addLogEntry("Restarting {$this->customRepository}:{$this->application->git_branch} on {$this->server->name}.");
|
||||||
$this->prepare_builder_image();
|
$this->prepare_builder_image();
|
||||||
$this->check_git_if_build_needed();
|
$this->check_git_if_build_needed();
|
||||||
$this->set_base_dir();
|
$this->set_base_dir();
|
||||||
@@ -678,12 +682,14 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->check_image_locally_or_remotely();
|
$this->check_image_locally_or_remotely();
|
||||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
||||||
$this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.");
|
$this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.");
|
||||||
$this->create_workdir();
|
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->rolling_update();
|
$this->rolling_update();
|
||||||
return;
|
$this->post_deployment();
|
||||||
|
} else {
|
||||||
|
$this->application_deployment_queue->addLogEntry("Image not found ({$this->production_image_name}). Redeploying the application.");
|
||||||
|
$this->restart_only = false;
|
||||||
|
$this->decide_what_to_do();
|
||||||
}
|
}
|
||||||
throw new RuntimeException('Cannot find image anywhere. Please redeploy the application.');
|
|
||||||
}
|
}
|
||||||
private function check_image_locally_or_remotely()
|
private function check_image_locally_or_remotely()
|
||||||
{
|
{
|
||||||
@@ -703,19 +709,35 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
$envs = collect([]);
|
$envs = collect([]);
|
||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
|
$this->env_filename = ".env-pr-$this->pull_request_id";
|
||||||
foreach ($this->application->environment_variables_preview as $env) {
|
foreach ($this->application->environment_variables_preview as $env) {
|
||||||
$envs->push($env->key . '=' . $env->real_value);
|
$envs->push($env->key . '=' . $env->real_value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
$this->env_filename = ".env";
|
||||||
foreach ($this->application->environment_variables as $env) {
|
foreach ($this->application->environment_variables as $env) {
|
||||||
$envs->push($env->key . '=' . $env->real_value);
|
$envs->push($env->key . '=' . $env->real_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($envs->isEmpty()) {
|
||||||
|
$this->env_filename = null;
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"command" => "rm -f $this->configuration_dir/{$this->env_filename}",
|
||||||
|
"hidden" => true,
|
||||||
|
"ignore_errors" => true
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
|
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d > $this->workdir/.env")
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"echo '$envs_base64' | base64 -d > $this->configuration_dir/{$this->env_filename}"
|
||||||
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,7 +753,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
|
$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) {
|
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()
|
private function rolling_update()
|
||||||
@@ -749,7 +771,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->write_deployment_configurations();
|
$this->write_deployment_configurations();
|
||||||
$this->server = $this->original_server;
|
$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("----------------------------------------");
|
$this->application_deployment_queue->addLogEntry("----------------------------------------");
|
||||||
if (count($this->application->ports_mappings_array) > 0) {
|
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.");
|
$this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
|
||||||
@@ -761,6 +783,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->application->settings->is_consistent_container_name_enabled = true;
|
$this->application->settings->is_consistent_container_name_enabled = true;
|
||||||
$this->application_deployment_queue->addLogEntry("Pull request deployment, rolling update is not supported.");
|
$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->stop_running_container(force: true);
|
||||||
$this->start_by_compose_file();
|
$this->start_by_compose_file();
|
||||||
} else {
|
} else {
|
||||||
@@ -819,6 +844,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
private function deploy_pull_request()
|
private function deploy_pull_request()
|
||||||
{
|
{
|
||||||
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
|
$this->deploy_docker_compose_buildpack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ($this->use_build_server) {
|
if ($this->use_build_server) {
|
||||||
$this->server = $this->build_server;
|
$this->server = $this->build_server;
|
||||||
}
|
}
|
||||||
@@ -848,6 +877,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
[
|
[
|
||||||
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
|
"command" => executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}")
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"command" => "mkdir -p {$this->configuration_dir}"
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private function prepare_builder_image()
|
private function prepare_builder_image()
|
||||||
@@ -870,6 +902,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
$this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
|
$this->application_deployment_queue->addLogEntry("Preparing container with helper image: $helperImage.");
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"command" => "docker rm -f {$this->deployment_uuid}",
|
||||||
|
"ignore_errors" => true,
|
||||||
|
"hidden" => true
|
||||||
|
],
|
||||||
[
|
[
|
||||||
$runCommand,
|
$runCommand,
|
||||||
"hidden" => true,
|
"hidden" => true,
|
||||||
@@ -885,6 +922,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->application->additional_networks->count() === 0) {
|
if ($this->application->additional_networks->count() === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ($this->pull_request_id !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
$destination_ids = $this->application->additional_networks->pluck('id');
|
$destination_ids = $this->application->additional_networks->pluck('id');
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
$this->application_deployment_queue->addLogEntry("Additional destinations are not supported in swarm mode.");
|
$this->application_deployment_queue->addLogEntry("Additional destinations are not supported in swarm mode.");
|
||||||
@@ -970,6 +1010,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$importCommands, "hidden" => true
|
$importCommands, "hidden" => true
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
$this->create_workdir();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_git_import_commands()
|
private function generate_git_import_commands()
|
||||||
@@ -983,11 +1024,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
return $commands;
|
return $commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function set_git_import_settings($git_clone_command)
|
|
||||||
{
|
|
||||||
return $this->application->setGitImportSettings($this->deployment_uuid, $git_clone_command);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function cleanup_git()
|
private function cleanup_git()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
@@ -1080,6 +1116,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private function generate_compose_file()
|
private function generate_compose_file()
|
||||||
{
|
{
|
||||||
|
$this->create_workdir();
|
||||||
$ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array;
|
$ports = $this->application->settings->is_static ? [80] : $this->application->ports_exposes_array;
|
||||||
$onlyPort = null;
|
$onlyPort = null;
|
||||||
if (count($ports) > 0) {
|
if (count($ports) > 0) {
|
||||||
@@ -1128,9 +1165,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->custom_healthcheck_found = false;
|
$this->custom_healthcheck_found = false;
|
||||||
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
|
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
|
||||||
$this->execute_remote_command([
|
$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')) {
|
if (str($dockerfile)->contains('HEALTHCHECK')) {
|
||||||
$this->custom_healthcheck_found = true;
|
$this->custom_healthcheck_found = true;
|
||||||
}
|
}
|
||||||
@@ -1163,6 +1200,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->env_filename) {
|
||||||
|
$docker_compose['services'][$this->container_name]['env_file'] = [
|
||||||
|
$this->env_filename
|
||||||
|
];
|
||||||
|
}
|
||||||
if (!$this->custom_healthcheck_found) {
|
if (!$this->custom_healthcheck_found) {
|
||||||
$docker_compose['services'][$this->container_name]['healthcheck'] = [
|
$docker_compose['services'][$this->container_name]['healthcheck'] = [
|
||||||
'test' => [
|
'test' => [
|
||||||
@@ -1274,10 +1316,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
if ($this->pull_request_id === 0) {
|
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) {
|
if ((bool)$this->application->settings->is_consistent_container_name_enabled) {
|
||||||
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
$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) {
|
if (count($custom_compose) > 0) {
|
||||||
$ipv4 = data_get($custom_compose, 'ip.0');
|
$ipv4 = data_get($custom_compose, 'ip.0');
|
||||||
$ipv6 = data_get($custom_compose, 'ip6.0');
|
$ipv6 = data_get($custom_compose, 'ip6.0');
|
||||||
@@ -1295,7 +1336,6 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
$docker_compose['services'][$this->application->uuid] = array_merge_recursive($docker_compose['services'][$this->application->uuid], $custom_compose);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
|
||||||
if (count($custom_compose) > 0) {
|
if (count($custom_compose) > 0) {
|
||||||
$ipv4 = data_get($custom_compose, 'ip.0');
|
$ipv4 = data_get($custom_compose, 'ip.0');
|
||||||
$ipv6 = data_get($custom_compose, 'ip6.0');
|
$ipv6 = data_get($custom_compose, 'ip6.0');
|
||||||
@@ -1318,13 +1358,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
$this->docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
$this->docker_compose_base64 = base64_encode($this->docker_compose);
|
||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}/docker-compose.yml"), "hidden" => true]);
|
||||||
|
$this->save_environment_variables();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->application->persistentStorages as $persistentStorage) {
|
foreach ($this->application->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
|
$volume_name = $persistentStorage->host_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
}
|
||||||
if ($this->pull_request_id !== 0) {
|
if ($this->pull_request_id !== 0) {
|
||||||
$volume_name = $volume_name . '-pr-' . $this->pull_request_id;
|
$volume_name = $volume_name . '-pr-' . $this->pull_request_id;
|
||||||
}
|
}
|
||||||
@@ -1359,23 +1404,53 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
foreach ($this->application->runtime_environment_variables as $env) {
|
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) {
|
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 {
|
} else {
|
||||||
foreach ($this->application->runtime_environment_variables_preview as $env) {
|
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) {
|
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
|
// Add PORT if not exists, use the first port as default
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('PORT'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('PORT'))->isEmpty()) {
|
||||||
$environment_variables->push("PORT={$ports[0]}");
|
$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 ($environment_variables->filter(fn ($env) => Str::of($env)->startsWith('SOURCE_COMMIT'))->isEmpty()) {
|
||||||
if (!is_null($this->commit)) {
|
if (!is_null($this->commit)) {
|
||||||
$environment_variables->push("SOURCE_COMMIT={$this->commit}");
|
$environment_variables->push("SOURCE_COMMIT={$this->commit}");
|
||||||
@@ -1768,7 +1843,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
|
|
||||||
public function failed(Throwable $exception): void
|
public function failed(Throwable $exception): void
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->next(ApplicationDeploymentStatus::FAILED->value);
|
$this->next(ApplicationDeploymentStatus::FAILED->value);
|
||||||
$this->application_deployment_queue->addLogEntry("Oops something is not okay, are you okay? 😢", 'stderr');
|
$this->application_deployment_queue->addLogEntry("Oops something is not okay, are you okay? 😢", 'stderr');
|
||||||
if (str($exception->getMessage())->isNotEmpty()) {
|
if (str($exception->getMessage())->isNotEmpty()) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use App\Actions\Proxy\StartProxy;
|
|||||||
use App\Actions\Shared\ComplexStatusCheck;
|
use App\Actions\Shared\ComplexStatusCheck;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
use App\Notifications\Container\ContainerRestarted;
|
use App\Notifications\Container\ContainerRestarted;
|
||||||
use App\Notifications\Container\ContainerStopped;
|
use App\Notifications\Container\ContainerStopped;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
@@ -149,31 +150,58 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||||
|
$type = data_get($labels, 'coolify.type');
|
||||||
|
|
||||||
if ($uuid) {
|
if ($uuid) {
|
||||||
$database = $databases->where('uuid', $uuid)->first();
|
if ($type === 'service') {
|
||||||
if ($database) {
|
$database_id = data_get($labels, 'coolify.service.subId');
|
||||||
$isPublic = data_get($database, 'is_public');
|
if ($database_id) {
|
||||||
$foundDatabases[] = $database->id;
|
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
||||||
$statusFromDb = $database->status;
|
if ($service_db) {
|
||||||
if ($statusFromDb !== $containerStatus) {
|
$uuid = $service_db->service->uuid;
|
||||||
$database->update(['status' => $containerStatus]);
|
$isPublic = data_get($service_db, 'is_public');
|
||||||
}
|
if ($isPublic) {
|
||||||
if ($isPublic) {
|
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
if ($this->server->isSwarm()) {
|
||||||
if ($this->server->isSwarm()) {
|
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
} else {
|
||||||
} else {
|
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||||
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 {
|
} 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') {
|
if (data_get($container, 'Name') === '/coolify-db') {
|
||||||
$foundDatabases[] = 0;
|
$foundDatabases[] = 0;
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$databaseType = $this->database->databaseType();
|
$databaseType = $this->database->databaseType();
|
||||||
$serviceUuid = $this->database->service->uuid;
|
$serviceUuid = $this->database->service->uuid;
|
||||||
$serviceName = str($this->database->service->name)->slug();
|
$serviceName = str($this->database->service->name)->slug();
|
||||||
if ($databaseType === 'standalone-postgresql') {
|
if (str($databaseType)->contains('postgres')) {
|
||||||
$this->container_name = "{$this->database->name}-$serviceUuid";
|
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||||
$this->directory_name = $serviceName . '-' . $this->container_name;
|
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||||
$commands[] = "docker exec $this->container_name env | grep POSTGRES_";
|
$commands[] = "docker exec $this->container_name env | grep POSTGRES_";
|
||||||
@@ -120,7 +120,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} else {
|
} else {
|
||||||
$databasesToBackup = $this->database->postgres_user;
|
$databasesToBackup = $this->database->postgres_user;
|
||||||
}
|
}
|
||||||
} else if ($databaseType === 'standalone-mysql') {
|
} else if (str($databaseType)->contains('mysql')) {
|
||||||
$this->container_name = "{$this->database->name}-$serviceUuid";
|
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||||
$this->directory_name = $serviceName . '-' . $this->container_name;
|
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||||
$commands[] = "docker exec $this->container_name env | grep MYSQL_";
|
$commands[] = "docker exec $this->container_name env | grep MYSQL_";
|
||||||
@@ -143,7 +143,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} else {
|
} else {
|
||||||
throw new \Exception('MYSQL_DATABASE not found');
|
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->container_name = "{$this->database->name}-$serviceUuid";
|
||||||
$this->directory_name = $serviceName . '-' . $this->container_name;
|
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||||
$commands[] = "docker exec $this->container_name env";
|
$commands[] = "docker exec $this->container_name env";
|
||||||
@@ -190,32 +190,32 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_null($databasesToBackup)) {
|
if (is_null($databasesToBackup)) {
|
||||||
if ($databaseType === 'standalone-postgresql') {
|
if (str($databaseType)->contains('postgres')) {
|
||||||
$databasesToBackup = [$this->database->postgres_db];
|
$databasesToBackup = [$this->database->postgres_db];
|
||||||
} else if ($databaseType === 'standalone-mongodb') {
|
} else if (str($databaseType)->contains('mongodb')) {
|
||||||
$databasesToBackup = ['*'];
|
$databasesToBackup = ['*'];
|
||||||
} else if ($databaseType === 'standalone-mysql') {
|
} else if (str($databaseType)->contains('mysql')) {
|
||||||
$databasesToBackup = [$this->database->mysql_database];
|
$databasesToBackup = [$this->database->mysql_database];
|
||||||
} else if ($databaseType === 'standalone-mariadb') {
|
} else if (str($databaseType)->contains('mariadb')) {
|
||||||
$databasesToBackup = [$this->database->mariadb_database];
|
$databasesToBackup = [$this->database->mariadb_database];
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($databaseType === 'standalone-postgresql') {
|
if (str($databaseType)->contains('postgres')) {
|
||||||
// Format: db1,db2,db3
|
// Format: db1,db2,db3
|
||||||
$databasesToBackup = explode(',', $databasesToBackup);
|
$databasesToBackup = explode(',', $databasesToBackup);
|
||||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||||
} else if ($databaseType === 'standalone-mongodb') {
|
} else if (str($databaseType)->contains('mongodb')) {
|
||||||
// Format: db1:collection1,collection2|db2:collection3,collection4
|
// Format: db1:collection1,collection2|db2:collection3,collection4
|
||||||
$databasesToBackup = explode('|', $databasesToBackup);
|
$databasesToBackup = explode('|', $databasesToBackup);
|
||||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||||
ray($databasesToBackup);
|
ray($databasesToBackup);
|
||||||
} else if ($databaseType === 'standalone-mysql') {
|
} else if (str($databaseType)->contains('mysql')) {
|
||||||
// Format: db1,db2,db3
|
// Format: db1,db2,db3
|
||||||
$databasesToBackup = explode(',', $databasesToBackup);
|
$databasesToBackup = explode(',', $databasesToBackup);
|
||||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||||
} else if ($databaseType === 'standalone-mariadb') {
|
} else if (str($databaseType)->contains('mariadb')) {
|
||||||
// Format: db1,db2,db3
|
// Format: db1,db2,db3
|
||||||
$databasesToBackup = explode(',', $databasesToBackup);
|
$databasesToBackup = explode(',', $databasesToBackup);
|
||||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||||
@@ -235,7 +235,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$size = 0;
|
$size = 0;
|
||||||
ray('Backing up ' . $database);
|
ray('Backing up ' . $database);
|
||||||
try {
|
try {
|
||||||
if ($databaseType === 'standalone-postgresql') {
|
if (str($databaseType)->contains('postgres')) {
|
||||||
$this->backup_file = "/pg-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
$this->backup_file = "/pg-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||||
@@ -244,7 +244,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'scheduled_database_backup_id' => $this->backup->id,
|
'scheduled_database_backup_id' => $this->backup->id,
|
||||||
]);
|
]);
|
||||||
$this->backup_standalone_postgresql($database);
|
$this->backup_standalone_postgresql($database);
|
||||||
} else if ($databaseType === 'standalone-mongodb') {
|
} else if (str($databaseType)->contains('mongodb')) {
|
||||||
if ($database === '*') {
|
if ($database === '*') {
|
||||||
$database = 'all';
|
$database = 'all';
|
||||||
$databaseName = 'all';
|
$databaseName = 'all';
|
||||||
@@ -263,7 +263,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'scheduled_database_backup_id' => $this->backup->id,
|
'scheduled_database_backup_id' => $this->backup->id,
|
||||||
]);
|
]);
|
||||||
$this->backup_standalone_mongodb($database);
|
$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_file = "/mysql-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||||
@@ -272,7 +272,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
'scheduled_database_backup_id' => $this->backup->id,
|
'scheduled_database_backup_id' => $this->backup->id,
|
||||||
]);
|
]);
|
||||||
$this->backup_standalone_mysql($database);
|
$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_file = "/mariadb-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ use App\Actions\Service\DeleteService;
|
|||||||
use App\Actions\Service\StopService;
|
use App\Actions\Service\StopService;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -25,7 +28,7 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource)
|
public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, public bool $deleteConfigurations = false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +45,9 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
case 'standalone-mongodb':
|
case 'standalone-mongodb':
|
||||||
case 'standalone-mysql':
|
case 'standalone-mysql':
|
||||||
case 'standalone-mariadb':
|
case 'standalone-mariadb':
|
||||||
|
case 'standalone-keydb':
|
||||||
|
case 'standalone-dragonfly':
|
||||||
|
case 'standalone-clickhouse':
|
||||||
StopDatabase::run($this->resource);
|
StopDatabase::run($this->resource);
|
||||||
break;
|
break;
|
||||||
case 'service':
|
case 'service':
|
||||||
@@ -49,6 +55,9 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
DeleteService::run($this->resource);
|
DeleteService::run($this->resource);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if ($this->deleteConfigurations) {
|
||||||
|
$this->resource?->delete_configurations();
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncr
|
|||||||
|
|
||||||
public function handle(): void
|
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 $activityId;
|
||||||
public $eventToDispatch = 'activityFinished';
|
public $eventToDispatch = 'activityFinished';
|
||||||
public $isPollingActive = false;
|
public $isPollingActive = false;
|
||||||
|
public bool $showWaiting = false;
|
||||||
|
|
||||||
protected $activity;
|
protected $activity;
|
||||||
protected $listeners = ['activityMonitor' => 'newMonitorActivity'];
|
protected $listeners = ['activityMonitor' => 'newMonitorActivity'];
|
||||||
|
|||||||
@@ -8,7 +8,31 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class Index extends 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()
|
public function mount()
|
||||||
{
|
{
|
||||||
if (!isCloud()) {
|
if (!isCloud()) {
|
||||||
@@ -17,7 +41,15 @@ class Index extends Component
|
|||||||
if (auth()->user()->id !== 0) {
|
if (auth()->user()->id !== 0) {
|
||||||
return redirect()->route('dashboard');
|
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);
|
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||||
})->get()->filter(function ($user) {
|
})->get()->filter(function ($user) {
|
||||||
return $user->id !== 0;
|
return $user->id !== 0;
|
||||||
|
|||||||
@@ -2,22 +2,24 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Boarding;
|
namespace App\Livewire\Boarding;
|
||||||
|
|
||||||
use App\Actions\Server\InstallDocker;
|
|
||||||
use App\Enums\ProxyTypes;
|
use App\Enums\ProxyTypes;
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Livewire\Attributes\Url;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
protected $listeners = ['serverInstalled' => 'validateServer'];
|
protected $listeners = ['serverInstalled' => 'validateServer'];
|
||||||
|
|
||||||
public string $currentState = 'welcome';
|
public string $currentState = 'welcome';
|
||||||
|
|
||||||
public ?string $selectedServerType = null;
|
public ?string $selectedServerType = null;
|
||||||
public ?Collection $privateKeys = null;
|
public ?Collection $privateKeys = null;
|
||||||
|
|
||||||
public ?int $selectedExistingPrivateKey = null;
|
public ?int $selectedExistingPrivateKey = null;
|
||||||
public ?string $privateKeyType = null;
|
public ?string $privateKeyType = null;
|
||||||
public ?string $privateKey = null;
|
public ?string $privateKey = null;
|
||||||
@@ -27,6 +29,7 @@ class Index extends Component
|
|||||||
public ?PrivateKey $createdPrivateKey = null;
|
public ?PrivateKey $createdPrivateKey = null;
|
||||||
|
|
||||||
public ?Collection $servers = null;
|
public ?Collection $servers = null;
|
||||||
|
|
||||||
public ?int $selectedExistingServer = null;
|
public ?int $selectedExistingServer = null;
|
||||||
public ?string $remoteServerName = null;
|
public ?string $remoteServerName = null;
|
||||||
public ?string $remoteServerDescription = null;
|
public ?string $remoteServerDescription = null;
|
||||||
@@ -38,7 +41,8 @@ class Index extends Component
|
|||||||
public ?Server $createdServer = null;
|
public ?Server $createdServer = null;
|
||||||
|
|
||||||
public Collection $projects;
|
public Collection $projects;
|
||||||
public ?int $selectedExistingProject = null;
|
|
||||||
|
public ?int $selectedProject = null;
|
||||||
public ?Project $createdProject = null;
|
public ?Project $createdProject = null;
|
||||||
|
|
||||||
public bool $dockerInstallationStarted = false;
|
public bool $dockerInstallationStarted = false;
|
||||||
@@ -62,6 +66,26 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->remoteServerDescription = 'Created by Coolify';
|
$this->remoteServerDescription = 'Created by Coolify';
|
||||||
$this->remoteServerHost = 'coolify-testing-host';
|
$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()
|
public function explanation()
|
||||||
{
|
{
|
||||||
@@ -89,6 +113,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->selectedServerType = $type;
|
$this->selectedServerType = $type;
|
||||||
if ($this->selectedServerType === 'localhost') {
|
if ($this->selectedServerType === 'localhost') {
|
||||||
$this->createdServer = Server::find(0);
|
$this->createdServer = Server::find(0);
|
||||||
|
$this->selectedExistingServer = 0;
|
||||||
if (!$this->createdServer) {
|
if (!$this->createdServer) {
|
||||||
return $this->dispatch('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
|
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()
|
public function selectExistingPrivateKey()
|
||||||
{
|
{
|
||||||
|
if (is_null($this->selectedExistingPrivateKey)) {
|
||||||
|
$this->restartBoarding();
|
||||||
|
return;
|
||||||
|
}
|
||||||
$this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey);
|
$this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey);
|
||||||
$this->privateKey = $this->createdPrivateKey->private_key;
|
$this->privateKey = $this->createdPrivateKey->private_key;
|
||||||
$this->currentState = 'create-server';
|
$this->currentState = 'create-server';
|
||||||
@@ -196,6 +225,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
|
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
|
||||||
$this->createdServer->settings->save();
|
$this->createdServer->settings->save();
|
||||||
$this->createdServer->addInitialNetwork();
|
$this->createdServer->addInitialNetwork();
|
||||||
|
$this->selectedExistingServer = $this->createdServer->id;
|
||||||
$this->currentState = 'validate-server';
|
$this->currentState = 'validate-server';
|
||||||
}
|
}
|
||||||
public function installServer()
|
public function installServer()
|
||||||
@@ -249,13 +279,13 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
{
|
{
|
||||||
$this->projects = Project::ownedByCurrentTeam(['name'])->get();
|
$this->projects = Project::ownedByCurrentTeam(['name'])->get();
|
||||||
if ($this->projects->count() > 0) {
|
if ($this->projects->count() > 0) {
|
||||||
$this->selectedExistingProject = $this->projects->first()->id;
|
$this->selectedProject = $this->projects->first()->id;
|
||||||
}
|
}
|
||||||
$this->currentState = 'create-project';
|
$this->currentState = 'create-project';
|
||||||
}
|
}
|
||||||
public function selectExistingProject()
|
public function selectExistingProject()
|
||||||
{
|
{
|
||||||
$this->createdProject = Project::find($this->selectedExistingProject);
|
$this->createdProject = Project::find($this->selectedProject);
|
||||||
$this->currentState = 'create-resource';
|
$this->currentState = 'create-resource';
|
||||||
}
|
}
|
||||||
public function createNewProject()
|
public function createNewProject()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
use App\Models\PrivateKey;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -13,9 +14,11 @@ class Dashboard extends Component
|
|||||||
{
|
{
|
||||||
public $projects = [];
|
public $projects = [];
|
||||||
public Collection $servers;
|
public Collection $servers;
|
||||||
|
public Collection $private_keys;
|
||||||
public $deployments_per_server;
|
public $deployments_per_server;
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
|
||||||
$this->servers = Server::ownedByCurrentTeam()->get();
|
$this->servers = Server::ownedByCurrentTeam()->get();
|
||||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||||
$this->get_deployments();
|
$this->get_deployments();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace App\Livewire\Destination\New;
|
|||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ class Docker extends Component
|
|||||||
public string $name;
|
public string $name;
|
||||||
public string $network;
|
public string $network;
|
||||||
|
|
||||||
public Collection $servers;
|
public ?Collection $servers = null;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public ?int $server_id = null;
|
public ?int $server_id = null;
|
||||||
public bool $is_swarm = false;
|
public bool $is_swarm = false;
|
||||||
@@ -34,6 +34,9 @@ class Docker extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
if (is_null($this->servers)) {
|
||||||
|
$this->servers = Server::isReachable()->get();
|
||||||
|
}
|
||||||
if (request()->query('server_id')) {
|
if (request()->query('server_id')) {
|
||||||
$this->server_id = request()->query('server_id');
|
$this->server_id = request()->query('server_id');
|
||||||
} else {
|
} else {
|
||||||
@@ -46,7 +49,9 @@ class Docker extends Component
|
|||||||
} else {
|
} else {
|
||||||
$this->network = new Cuid2(7);
|
$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()
|
public function generate_name()
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace App\Livewire\Destination;
|
namespace App\Livewire\Destination;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\StandaloneDocker;
|
||||||
|
use App\Models\SwarmDocker;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -11,6 +13,40 @@ class Show extends Component
|
|||||||
public Server $server;
|
public Server $server;
|
||||||
public Collection|array $networks = [];
|
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()
|
public function scan()
|
||||||
{
|
{
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
@@ -26,6 +62,8 @@ class Show extends Component
|
|||||||
});
|
});
|
||||||
if ($this->networks->count() === 0) {
|
if ($this->networks->count() === 0) {
|
||||||
$this->dispatch('success', 'No new networks found.');
|
$this->dispatch('success', 'No new networks found.');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
$this->dispatch('success', 'Scan done.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class ForcePasswordReset extends Component
|
|||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.force-password-reset')->layout('layouts.simple');
|
return view('livewire.force-password-reset');
|
||||||
}
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,14 +17,6 @@ class LayoutPopups extends Component
|
|||||||
{
|
{
|
||||||
$this->dispatch('success', 'Realtime events configured!');
|
$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()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.layout-popups');
|
return view('livewire.layout-popups');
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use App\Models\Team;
|
|||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class DiscordSettings extends Component
|
class Discord extends Component
|
||||||
{
|
{
|
||||||
public Team $team;
|
public Team $team;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
@@ -55,4 +55,8 @@ class DiscordSettings extends Component
|
|||||||
$this->team?->notify(new Test());
|
$this->team?->notify(new Test());
|
||||||
$this->dispatch('success', 'Test notification sent.');
|
$this->dispatch('success', 'Test notification sent.');
|
||||||
}
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.notifications.discord');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Notifications;
|
namespace App\Livewire\Notifications;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
use Livewire\Component;
|
|
||||||
use Log;
|
|
||||||
|
|
||||||
class EmailSettings extends Component
|
class Email extends Component
|
||||||
{
|
{
|
||||||
public Team $team;
|
public Team $team;
|
||||||
public string $emails;
|
public string $emails;
|
||||||
@@ -119,16 +118,18 @@ class EmailSettings extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->resetErrorBag();
|
$this->resetErrorBag();
|
||||||
$this->validate([
|
if (!$this->team->use_instance_email_settings) {
|
||||||
'team.smtp_from_address' => 'required|email',
|
$this->validate([
|
||||||
'team.smtp_from_name' => 'required',
|
'team.smtp_from_address' => 'required|email',
|
||||||
'team.smtp_host' => 'required',
|
'team.smtp_from_name' => 'required',
|
||||||
'team.smtp_port' => 'required|numeric',
|
'team.smtp_host' => 'required',
|
||||||
'team.smtp_encryption' => 'nullable',
|
'team.smtp_port' => 'required|numeric',
|
||||||
'team.smtp_username' => 'nullable',
|
'team.smtp_encryption' => 'nullable',
|
||||||
'team.smtp_password' => 'nullable',
|
'team.smtp_username' => 'nullable',
|
||||||
'team.smtp_timeout' => 'nullable',
|
'team.smtp_password' => 'nullable',
|
||||||
]);
|
'team.smtp_timeout' => 'nullable',
|
||||||
|
]);
|
||||||
|
}
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->dispatch('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
@@ -189,4 +190,8 @@ class EmailSettings extends Component
|
|||||||
}
|
}
|
||||||
$this->dispatch('error', 'Instance SMTP/Resend settings are not enabled.');
|
$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 App\Notifications\Test;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class TelegramSettings extends Component
|
class Telegram extends Component
|
||||||
{
|
{
|
||||||
|
|
||||||
public Team $team;
|
public Team $team;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'team.telegram_enabled' => 'nullable|boolean',
|
'team.telegram_enabled' => 'nullable|boolean',
|
||||||
@@ -61,4 +62,8 @@ class TelegramSettings extends Component
|
|||||||
$this->team?->notify(new Test());
|
$this->team?->notify(new Test());
|
||||||
$this->dispatch('success', 'Test notification sent.');
|
$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 int $userId;
|
||||||
public string $email;
|
public string $email;
|
||||||
|
|
||||||
#[Validate('required')]
|
|
||||||
public string $current_password;
|
public string $current_password;
|
||||||
#[Validate('required|min:8')]
|
|
||||||
public string $new_password;
|
public string $new_password;
|
||||||
#[Validate('required|min:8|same:new_password')]
|
|
||||||
public string $new_password_confirmation;
|
public string $new_password_confirmation;
|
||||||
|
|
||||||
#[Validate('required')]
|
#[Validate('required')]
|
||||||
@@ -29,7 +26,9 @@ class Index extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate([
|
||||||
|
'name' => 'required',
|
||||||
|
]);
|
||||||
auth()->user()->update([
|
auth()->user()->update([
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
]);
|
]);
|
||||||
@@ -42,7 +41,11 @@ class Index extends Component
|
|||||||
public function resetPassword()
|
public function resetPassword()
|
||||||
{
|
{
|
||||||
try {
|
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)) {
|
if (!Hash::check($this->current_password, auth()->user()->password)) {
|
||||||
$this->dispatch('error', 'Current password is incorrect.');
|
$this->dispatch('error', 'Current password is incorrect.');
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use Livewire\Component;
|
|||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
public Application $application;
|
public Application $application;
|
||||||
public array|Collection $deployments = [];
|
public ?Collection $deployments;
|
||||||
public int $deployments_count = 0;
|
public int $deployments_count = 0;
|
||||||
public string $current_url;
|
public string $current_url;
|
||||||
public int $skip = 0;
|
public int $skip = 0;
|
||||||
@@ -48,9 +48,9 @@ class Index extends Component
|
|||||||
}
|
}
|
||||||
private function show_more()
|
private function show_more()
|
||||||
{
|
{
|
||||||
if (count($this->deployments) !== 0) {
|
if ($this->deployments->count() !== 0) {
|
||||||
$this->show_next = true;
|
$this->show_next = true;
|
||||||
if (count($this->deployments) < $this->default_take) {
|
if ($this->deployments->count() < $this->default_take) {
|
||||||
$this->show_next = false;
|
$this->show_next = false;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -63,7 +63,6 @@ class Index extends Component
|
|||||||
}
|
}
|
||||||
public function previous_page(?int $take = null)
|
public function previous_page(?int $take = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($take) {
|
if ($take) {
|
||||||
$this->skip = $this->skip - $take;
|
$this->skip = $this->skip - $take;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class General extends Component
|
|||||||
|
|
||||||
public $customLabels;
|
public $customLabels;
|
||||||
public bool $labelsChanged = false;
|
public bool $labelsChanged = false;
|
||||||
public bool $isConfigurationChanged = false;
|
public bool $initLoadingCompose = false;
|
||||||
|
|
||||||
public ?string $initialDockerComposeLocation = null;
|
public ?string $initialDockerComposeLocation = null;
|
||||||
public ?string $initialDockerComposePrLocation = null;
|
public ?string $initialDockerComposePrLocation = null;
|
||||||
@@ -73,6 +73,7 @@ class General extends Component
|
|||||||
'application.settings.is_static' => 'boolean|required',
|
'application.settings.is_static' => 'boolean|required',
|
||||||
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
|
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
|
||||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||||
|
'application.watch_paths' => 'nullable',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@@ -108,6 +109,7 @@ class General extends Component
|
|||||||
'application.settings.is_static' => 'Is static',
|
'application.settings.is_static' => 'Is static',
|
||||||
'application.settings.is_raw_compose_deployment_enabled' => 'Is raw compose deployment enabled',
|
'application.settings.is_raw_compose_deployment_enabled' => 'Is raw compose deployment enabled',
|
||||||
'application.settings.is_build_server_enabled' => 'Is build server enabled',
|
'application.settings.is_build_server_enabled' => 'Is build server enabled',
|
||||||
|
'application.watch_paths' => 'Watch paths',
|
||||||
];
|
];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -121,19 +123,22 @@ class General extends Component
|
|||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
}
|
}
|
||||||
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
||||||
|
|
||||||
$this->ports_exposes = $this->application->ports_exposes;
|
$this->ports_exposes = $this->application->ports_exposes;
|
||||||
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
$this->customLabels = $this->application->parseContainerLabels();
|
||||||
$this->application->isConfigurationChanged(true);
|
|
||||||
}
|
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
|
||||||
$this->customLabels = $this->application->parseContainerLabels();
|
|
||||||
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
||||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
}
|
}
|
||||||
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||||
|
if ($this->application->build_pack === 'dockercompose' && !$this->application->docker_compose_raw) {
|
||||||
|
$this->initLoadingCompose = true;
|
||||||
|
$this->dispatch('info', 'Loading docker compose file...');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
@@ -152,31 +157,36 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
|
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
|
||||||
$this->dispatch('success', 'Docker compose file loaded.');
|
$this->dispatch('success', 'Docker compose file loaded.');
|
||||||
|
$this->dispatch('compose_loaded');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->application->docker_compose_location = $this->initialDockerComposeLocation;
|
$this->application->docker_compose_location = $this->initialDockerComposeLocation;
|
||||||
$this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
|
$this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->initLoadingCompose = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function generateDomain(string $serviceName)
|
public function generateDomain(string $serviceName)
|
||||||
{
|
{
|
||||||
$domain = $this->parsedServiceDomains[$serviceName]['domain'] ?? null;
|
$uuid = new Cuid2(7);
|
||||||
if (!$domain) {
|
$domain = generateFqdn($this->application->destination->server, $uuid);
|
||||||
$uuid = new Cuid2(7);
|
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
|
||||||
$domain = generateFqdn($this->application->destination->server, $uuid);
|
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||||
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
|
$this->application->save();
|
||||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
$this->dispatch('success', 'Domain generated.');
|
||||||
$this->application->save();
|
|
||||||
}
|
|
||||||
return $domain;
|
return $domain;
|
||||||
}
|
}
|
||||||
public function updatedApplicationBaseDirectory() {
|
public function updatedApplicationBaseDirectory()
|
||||||
raY('asdf');
|
{
|
||||||
if ($this->application->build_pack === 'dockercompose') {
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
$this->loadComposeFile();
|
$this->loadComposeFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function updatedApplicationFqdn() {
|
||||||
|
$this->resetDefaultLabels();
|
||||||
|
}
|
||||||
public function updatedApplicationBuildPack()
|
public function updatedApplicationBuildPack()
|
||||||
{
|
{
|
||||||
if ($this->application->build_pack !== 'nixpacks') {
|
if ($this->application->build_pack !== 'nixpacks') {
|
||||||
@@ -204,30 +214,49 @@ class General extends Component
|
|||||||
$fqdn = generateFqdn($server, $this->application->uuid);
|
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||||
$this->application->fqdn = $fqdn;
|
$this->application->fqdn = $fqdn;
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->updatedApplicationFqdn();
|
$this->resetDefaultLabels();
|
||||||
|
$this->dispatch('success', 'Wildcard domain generated.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function resetDefaultLabels($showToaster = true)
|
public function resetDefaultLabels()
|
||||||
{
|
{
|
||||||
|
ray('resetDefaultLabels');
|
||||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||||
$this->ports_exposes = $this->application->ports_exposes;
|
$this->ports_exposes = $this->application->ports_exposes;
|
||||||
$this->submit($showToaster);
|
|
||||||
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
|
$this->application->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updatedApplicationFqdn()
|
public function checkFqdns($showToaster = true)
|
||||||
{
|
{
|
||||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
if (data_get($this->application, 'fqdn')) {
|
||||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
$domains = str($this->application->fqdn)->trim()->explode(',');
|
||||||
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
if ($this->application->additional_servers->count() === 0) {
|
||||||
return str($domain)->trim()->lower();
|
foreach ($domains as $domain) {
|
||||||
});
|
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
||||||
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
|
$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.");
|
||||||
$this->application->save();
|
}
|
||||||
$this->resetDefaultLabels(false);
|
}
|
||||||
|
}
|
||||||
|
check_domain_usage(resource: $this->application);
|
||||||
|
$this->application->fqdn = $domains->implode(',');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public function submit($showToaster = true)
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$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->checkFqdns();
|
||||||
|
|
||||||
|
$this->application->save();
|
||||||
|
|
||||||
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
||||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
@@ -239,25 +268,14 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
||||||
$this->resetDefaultLabels(false);
|
$this->resetDefaultLabels();
|
||||||
}
|
}
|
||||||
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'application.docker_registry_image_name' => 'required',
|
'application.docker_registry_image_name' => 'required',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if (data_get($this->application, 'fqdn')) {
|
|
||||||
$domains = str($this->application->fqdn)->trim()->explode(',');
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check_fqdn_usage($this->application);
|
|
||||||
$this->application->fqdn = $domains->implode(',');
|
|
||||||
}
|
|
||||||
if (data_get($this->application, 'custom_docker_run_options')) {
|
if (data_get($this->application, 'custom_docker_run_options')) {
|
||||||
$this->application->custom_docker_run_options = str($this->application->custom_docker_run_options)->trim();
|
$this->application->custom_docker_run_options = str($this->application->custom_docker_run_options)->trim();
|
||||||
}
|
}
|
||||||
@@ -275,6 +293,15 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
if ($this->application->build_pack === 'dockercompose') {
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||||
|
foreach ($this->parsedServiceDomains as $serviceName => $service) {
|
||||||
|
$domain = data_get($service, 'domain');
|
||||||
|
if ($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='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||||
|
}
|
||||||
|
check_domain_usage(resource: $this->application);
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||||
$this->application->parseRawCompose();
|
$this->application->parseRawCompose();
|
||||||
} else {
|
} else {
|
||||||
@@ -287,7 +314,7 @@ class General extends Component
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
$this->dispatch('configurationChanged');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class Heading extends Component
|
|||||||
$teamId = auth()->user()->currentTeam()->id;
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
return [
|
return [
|
||||||
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
|
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
|
||||||
|
"compose_loaded" => '$refresh',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -38,6 +39,7 @@ class Heading extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
|
if ($showNotification) $this->dispatch('success', "Success", "Application status updated.");
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function force_deploy_without_cache()
|
public function force_deploy_without_cache()
|
||||||
@@ -56,11 +58,11 @@ class Heading extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data_get($this->application, 'settings.is_build_server_enabled') && str($this->application->docker_registry_image_name)->isEmpty()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
@@ -99,7 +101,7 @@ class Heading extends Component
|
|||||||
public function restart()
|
public function restart()
|
||||||
{
|
{
|
||||||
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ class Index extends Component
|
|||||||
{
|
{
|
||||||
public $database;
|
public $database;
|
||||||
public $s3s;
|
public $s3s;
|
||||||
public function mount() {
|
public function mount()
|
||||||
|
{
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
@@ -21,8 +22,13 @@ class Index extends Component
|
|||||||
if (!$database) {
|
if (!$database) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
// No backups for redis
|
// No backups
|
||||||
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
|
if (
|
||||||
|
$database->getMorphClass() === 'App\Models\StandaloneRedis' ||
|
||||||
|
$database->getMorphClass() === 'App\Models\StandaloneKeydb' ||
|
||||||
|
$database->getMorphClass() === 'App\Models\StandaloneDragonfly'||
|
||||||
|
$database->getMorphClass() === 'App\Models\StandaloneClickhouse'
|
||||||
|
) {
|
||||||
return redirect()->route('project.database.configuration', [
|
return redirect()->route('project.database.configuration', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
|
|||||||
@@ -42,19 +42,21 @@ class BackupEdit extends Component
|
|||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
// TODO: Delete backup from server and add a confirmation modal
|
try {
|
||||||
$this->backup->delete();
|
$this->backup->delete();
|
||||||
if ($this->backup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
if ($this->backup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
$previousUrl = url()->previous();
|
$previousUrl = url()->previous();
|
||||||
$url = Url::fromString($previousUrl);
|
$url = Url::fromString($previousUrl);
|
||||||
$url = $url->withoutQueryParameter('selectedBackupId');
|
$url = $url->withoutQueryParameter('selectedBackupId');
|
||||||
$url = $url->withFragment('backups');
|
$url = $url->withFragment('backups');
|
||||||
$url = $url->getPath() . "#{$url->getFragment()}";
|
$url = $url->getPath() . "#{$url->getFragment()}";
|
||||||
return redirect($url);
|
return redirect($url);
|
||||||
} else {
|
} else {
|
||||||
return redirect()->route('project.database.backup.index', $this->parameters);
|
return redirect()->route('project.database.backup.index', $this->parameters);
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
@@ -63,7 +65,7 @@ class BackupEdit extends Component
|
|||||||
$this->custom_validate();
|
$this->custom_validate();
|
||||||
$this->backup->save();
|
$this->backup->save();
|
||||||
$this->backup->refresh();
|
$this->backup->refresh();
|
||||||
$this->dispatch('success', 'Backup updated successfully');
|
$this->dispatch('success', 'Backup updated successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->dispatch('error', $e->getMessage());
|
$this->dispatch('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database;
|
|||||||
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
|
||||||
class BackupExecutions extends Component
|
class BackupExecutions extends Component
|
||||||
{
|
{
|
||||||
@@ -36,32 +37,9 @@ class BackupExecutions extends Component
|
|||||||
$this->dispatch('success', 'Backup deleted.');
|
$this->dispatch('success', 'Backup deleted.');
|
||||||
$this->refreshBackupExecutions();
|
$this->refreshBackupExecutions();
|
||||||
}
|
}
|
||||||
public function download($exeuctionId)
|
public function download_file($exeuctionId)
|
||||||
{
|
{
|
||||||
try {
|
return redirect()->route('download.backup', $exeuctionId);
|
||||||
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
|
||||||
if (is_null($execution)) {
|
|
||||||
$this->dispatch('error', 'Backup execution not found.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$filename = data_get($execution, 'filename');
|
|
||||||
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
|
||||||
$server = $execution->scheduledDatabaseBackup->database->service->destination->server;
|
|
||||||
} else {
|
|
||||||
$server = $execution->scheduledDatabaseBackup->database->destination->server;
|
|
||||||
}
|
|
||||||
$privateKeyLocation = savePrivateKeyToFs($server);
|
|
||||||
$disk = Storage::build([
|
|
||||||
'driver' => 'sftp',
|
|
||||||
'host' => $server->ip,
|
|
||||||
'port' => $server->port,
|
|
||||||
'username' => $server->user,
|
|
||||||
'privateKey' => $privateKeyLocation,
|
|
||||||
]);
|
|
||||||
return $disk->download($filename);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public function refreshBackupExecutions(): void
|
public function refreshBackupExecutions(): void
|
||||||
{
|
{
|
||||||
|
|||||||
115
app/Livewire/Project/Database/Clickhouse/General.php
Normal file
115
app/Livewire/Project/Database/Clickhouse/General.php
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Database\Clickhouse;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use Exception;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class General extends Component
|
||||||
|
{
|
||||||
|
public StandaloneClickhouse $database;
|
||||||
|
public ?string $db_url = null;
|
||||||
|
public ?string $db_url_public = null;
|
||||||
|
|
||||||
|
protected $listeners = ['refresh'];
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'database.name' => 'required',
|
||||||
|
'database.description' => 'nullable',
|
||||||
|
'database.clickhouse_admin_user' => 'required',
|
||||||
|
'database.clickhouse_admin_password' => 'required',
|
||||||
|
'database.image' => 'required',
|
||||||
|
'database.ports_mappings' => 'nullable',
|
||||||
|
'database.is_public' => 'nullable|boolean',
|
||||||
|
'database.public_port' => 'nullable|integer',
|
||||||
|
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'database.name' => 'Name',
|
||||||
|
'database.description' => 'Description',
|
||||||
|
'database.clickhouse_admin_user' => 'Postgres User',
|
||||||
|
'database.clickhouse_admin_password' => 'Postgres Password',
|
||||||
|
'database.image' => 'Image',
|
||||||
|
'database.ports_mappings' => 'Port Mapping',
|
||||||
|
'database.is_public' => 'Is Public',
|
||||||
|
'database.public_port' => 'Public Port',
|
||||||
|
];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->db_url = $this->database->get_db_url(true);
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSaveAdvanced() {
|
||||||
|
try {
|
||||||
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->dispatch('success', 'Database updated.');
|
||||||
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->database->is_public && !$this->database->public_port) {
|
||||||
|
$this->dispatch('error', 'Public port is required.');
|
||||||
|
$this->database->is_public = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
if (!str($this->database->status)->startsWith('running')) {
|
||||||
|
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||||
|
$this->database->is_public = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StartDatabaseProxy::run($this->database);
|
||||||
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
|
} else {
|
||||||
|
StopDatabaseProxy::run($this->database);
|
||||||
|
$this->db_url_public = null;
|
||||||
|
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->database->is_public = !$this->database->is_public;
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refresh(): void
|
||||||
|
{
|
||||||
|
$this->database->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (str($this->database->public_port)->isEmpty()) {
|
||||||
|
$this->database->public_port = null;
|
||||||
|
}
|
||||||
|
$this->validate();
|
||||||
|
$this->database->save();
|
||||||
|
$this->dispatch('success', 'Database updated.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
if (is_null($this->database->config_hash)) {
|
||||||
|
$this->database->isConfigurationChanged(true);
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,8 @@ use Livewire\Component;
|
|||||||
class Configuration extends Component
|
class Configuration extends Component
|
||||||
{
|
{
|
||||||
public $database;
|
public $database;
|
||||||
public function mount() {
|
public function mount()
|
||||||
|
{
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
@@ -21,6 +22,10 @@ class Configuration extends Component
|
|||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
|
if (str($this->database->status)->startsWith('running') && is_null($this->database->config_hash)) {
|
||||||
|
$this->database->isConfigurationChanged(true);
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|||||||
113
app/Livewire/Project/Database/Dragonfly/General.php
Normal file
113
app/Livewire/Project/Database/Dragonfly/General.php
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Database\Dragonfly;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use Exception;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class General extends Component
|
||||||
|
{
|
||||||
|
protected $listeners = ['refresh'];
|
||||||
|
|
||||||
|
public StandaloneDragonfly $database;
|
||||||
|
public ?string $db_url = null;
|
||||||
|
public ?string $db_url_public = null;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'database.name' => 'required',
|
||||||
|
'database.description' => 'nullable',
|
||||||
|
'database.dragonfly_password' => 'required',
|
||||||
|
'database.image' => 'required',
|
||||||
|
'database.ports_mappings' => 'nullable',
|
||||||
|
'database.is_public' => 'nullable|boolean',
|
||||||
|
'database.public_port' => 'nullable|integer',
|
||||||
|
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'database.name' => 'Name',
|
||||||
|
'database.description' => 'Description',
|
||||||
|
'database.dragonfly_password' => 'Redis Password',
|
||||||
|
'database.image' => 'Image',
|
||||||
|
'database.ports_mappings' => 'Port Mapping',
|
||||||
|
'database.is_public' => 'Is Public',
|
||||||
|
'database.public_port' => 'Public Port',
|
||||||
|
];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->db_url = $this->database->get_db_url(true);
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSaveAdvanced() {
|
||||||
|
try {
|
||||||
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->dispatch('success', 'Database updated.');
|
||||||
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->database->save();
|
||||||
|
$this->dispatch('success', 'Database updated.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
if (is_null($this->database->config_hash)) {
|
||||||
|
$this->database->isConfigurationChanged(true);
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->database->is_public && !$this->database->public_port) {
|
||||||
|
$this->dispatch('error', 'Public port is required.');
|
||||||
|
$this->database->is_public = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
if (!str($this->database->status)->startsWith('running')) {
|
||||||
|
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||||
|
$this->database->is_public = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StartDatabaseProxy::run($this->database);
|
||||||
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
|
} else {
|
||||||
|
StopDatabaseProxy::run($this->database);
|
||||||
|
$this->db_url_public = null;
|
||||||
|
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->database->is_public = !$this->database->is_public;
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function refresh(): void
|
||||||
|
{
|
||||||
|
$this->database->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.database.dragonfly.general');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartClickhouse;
|
||||||
|
use App\Actions\Database\StartDragonfly;
|
||||||
|
use App\Actions\Database\StartKeydb;
|
||||||
use App\Actions\Database\StartMariadb;
|
use App\Actions\Database\StartMariadb;
|
||||||
use App\Actions\Database\StartMongodb;
|
use App\Actions\Database\StartMongodb;
|
||||||
use App\Actions\Database\StartMysql;
|
use App\Actions\Database\StartMysql;
|
||||||
use App\Actions\Database\StartPostgresql;
|
use App\Actions\Database\StartPostgresql;
|
||||||
use App\Actions\Database\StartRedis;
|
use App\Actions\Database\StartRedis;
|
||||||
use App\Actions\Database\StopDatabase;
|
use App\Actions\Database\StopDatabase;
|
||||||
use App\Events\DatabaseStatusChanged;
|
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -32,6 +34,12 @@ class Heading extends Component
|
|||||||
]);
|
]);
|
||||||
$this->dispatch('refresh');
|
$this->dispatch('refresh');
|
||||||
$this->check_status();
|
$this->check_status();
|
||||||
|
if (is_null($this->database->config_hash) || $this->database->isConfigurationChanged()) {
|
||||||
|
$this->database->isConfigurationChanged(true);
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function check_status($showNotification = false)
|
public function check_status($showNotification = false)
|
||||||
@@ -71,6 +79,15 @@ class Heading extends Component
|
|||||||
} else if ($this->database->type() === 'standalone-mariadb') {
|
} else if ($this->database->type() === 'standalone-mariadb') {
|
||||||
$activity = StartMariadb::run($this->database);
|
$activity = StartMariadb::run($this->database);
|
||||||
$this->dispatch('activityMonitor', $activity->id);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
|
} else if ($this->database->type() === 'standalone-keydb') {
|
||||||
|
$activity = StartKeydb::run($this->database);
|
||||||
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
|
} else if ($this->database->type() === 'standalone-dragonfly') {
|
||||||
|
$activity = StartDragonfly::run($this->database);
|
||||||
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
|
} else if ($this->database->type() === 'standalone-clickhouse') {
|
||||||
|
$activity = StartClickhouse::run($this->database);
|
||||||
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,29 +2,25 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Database;
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Livewire\WithFileUploads;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneMariadb;
|
|
||||||
use App\Models\StandaloneMongodb;
|
|
||||||
use App\Models\StandaloneMysql;
|
|
||||||
use App\Models\StandalonePostgresql;
|
|
||||||
use App\Models\StandaloneRedis;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class Import extends Component
|
class Import extends Component
|
||||||
{
|
{
|
||||||
use WithFileUploads;
|
public bool $unsupported = false;
|
||||||
|
|
||||||
public $file;
|
|
||||||
public $resource;
|
public $resource;
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public $containers;
|
public $containers;
|
||||||
public bool $validated = true;
|
|
||||||
public bool $scpInProgress = false;
|
public bool $scpInProgress = false;
|
||||||
public bool $importRunning = false;
|
public bool $importRunning = false;
|
||||||
public string $validationMsg = '';
|
|
||||||
|
public ?string $filename = null;
|
||||||
|
public ?string $filesize = null;
|
||||||
|
public bool $isUploading = false;
|
||||||
|
public int $progress = 0;
|
||||||
|
public bool $error = false;
|
||||||
|
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public string $container;
|
public string $container;
|
||||||
public array $importCommands = [];
|
public array $importCommands = [];
|
||||||
@@ -51,22 +47,9 @@ class Import extends Component
|
|||||||
if (!data_get($this->parameters, 'database_uuid')) {
|
if (!data_get($this->parameters, 'database_uuid')) {
|
||||||
abort(404);
|
abort(404);
|
||||||
}
|
}
|
||||||
|
$resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
|
||||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
if (is_null($resource)) {
|
||||||
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
abort(404);
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$this->resource = $resource;
|
$this->resource = $resource;
|
||||||
$this->server = $this->resource->destination->server;
|
$this->server = $this->resource->destination->server;
|
||||||
@@ -75,38 +58,34 @@ class Import extends Component
|
|||||||
$this->containers->push($this->container);
|
$this->containers->push($this->container);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->containers->count() > 1) {
|
|
||||||
$this->validated = false;
|
|
||||||
$this->validationMsg = 'The database service has more than one container running. Cannot import.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$this->resource->getMorphClass() == 'App\Models\StandaloneRedis'
|
$this->resource->getMorphClass() == 'App\Models\StandaloneRedis' ||
|
||||||
|| $this->resource->getMorphClass() == 'App\Models\StandaloneMongodb'
|
$this->resource->getMorphClass() == 'App\Models\StandaloneKeydb' ||
|
||||||
|
$this->resource->getMorphClass() == 'App\Models\StandaloneDragonfly' ||
|
||||||
|
$this->resource->getMorphClass() == 'App\Models\StandaloneClickhouse' ||
|
||||||
|
$this->resource->getMorphClass() == 'App\Models\StandaloneMongodb'
|
||||||
) {
|
) {
|
||||||
$this->validated = false;
|
$this->unsupported = true;
|
||||||
$this->validationMsg = 'This database type is not currently supported.';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function runImport()
|
public function runImport()
|
||||||
{
|
{
|
||||||
$this->validate([
|
|
||||||
'file' => 'required|file|max:102400'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->importRunning = true;
|
|
||||||
$this->scpInProgress = true;
|
|
||||||
|
|
||||||
|
if ($this->filename == '') {
|
||||||
|
$this->dispatch('error', 'Please select a file to import.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$uploadedFilename = $this->file->store('backup-import');
|
$uploadedFilename = "upload/{$this->resource->uuid}/restore";
|
||||||
$path = Storage::path($uploadedFilename);
|
$path = Storage::path($uploadedFilename);
|
||||||
|
if (!Storage::exists($uploadedFilename)) {
|
||||||
|
$this->dispatch('error', 'The file does not exist or has been deleted.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
$tmpPath = '/tmp/' . basename($uploadedFilename);
|
$tmpPath = '/tmp/' . basename($uploadedFilename);
|
||||||
|
|
||||||
// SCP the backup file to the server.
|
|
||||||
instant_scp($path, $tmpPath, $this->server);
|
instant_scp($path, $tmpPath, $this->server);
|
||||||
$this->scpInProgress = false;
|
Storage::delete($uploadedFilename);
|
||||||
|
|
||||||
$this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}";
|
$this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}";
|
||||||
|
|
||||||
switch ($this->resource->getMorphClass()) {
|
switch ($this->resource->getMorphClass()) {
|
||||||
@@ -132,8 +111,7 @@ class Import extends Component
|
|||||||
$this->dispatch('activityMonitor', $activity->id);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->validated = false;
|
return handleError($e, $this);
|
||||||
$this->validationMsg = $e->getMessage();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
117
app/Livewire/Project/Database/Keydb/General.php
Normal file
117
app/Livewire/Project/Database/Keydb/General.php
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Database\Keydb;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
|
use Exception;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class General extends Component
|
||||||
|
{
|
||||||
|
protected $listeners = ['refresh'];
|
||||||
|
|
||||||
|
public StandaloneKeydb $database;
|
||||||
|
public ?string $db_url = null;
|
||||||
|
public ?string $db_url_public = null;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'database.name' => 'required',
|
||||||
|
'database.description' => 'nullable',
|
||||||
|
'database.keydb_conf' => 'nullable',
|
||||||
|
'database.keydb_password' => 'required',
|
||||||
|
'database.image' => 'required',
|
||||||
|
'database.ports_mappings' => 'nullable',
|
||||||
|
'database.is_public' => 'nullable|boolean',
|
||||||
|
'database.public_port' => 'nullable|integer',
|
||||||
|
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'database.name' => 'Name',
|
||||||
|
'database.description' => 'Description',
|
||||||
|
'database.keydb_conf' => 'Redis Configuration',
|
||||||
|
'database.keydb_password' => 'Redis Password',
|
||||||
|
'database.image' => 'Image',
|
||||||
|
'database.ports_mappings' => 'Port Mapping',
|
||||||
|
'database.is_public' => 'Is Public',
|
||||||
|
'database.public_port' => 'Public Port',
|
||||||
|
];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->db_url = $this->database->get_db_url(true);
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSaveAdvanced() {
|
||||||
|
try {
|
||||||
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->dispatch('success', 'Database updated.');
|
||||||
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
if ($this->database->keydb_conf === "") {
|
||||||
|
$this->database->keydb_conf = null;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->dispatch('success', 'Database updated.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
if (is_null($this->database->config_hash)) {
|
||||||
|
$this->database->isConfigurationChanged(true);
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->database->is_public && !$this->database->public_port) {
|
||||||
|
$this->dispatch('error', 'Public port is required.');
|
||||||
|
$this->database->is_public = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
if (!str($this->database->status)->startsWith('running')) {
|
||||||
|
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||||
|
$this->database->is_public = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StartDatabaseProxy::run($this->database);
|
||||||
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
|
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||||
|
} else {
|
||||||
|
StopDatabaseProxy::run($this->database);
|
||||||
|
$this->db_url_public = null;
|
||||||
|
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->database->is_public = !$this->database->is_public;
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function refresh(): void
|
||||||
|
{
|
||||||
|
$this->database->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.database.keydb.general');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,12 @@ class General extends Component
|
|||||||
$this->dispatch('success', 'Database updated.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
if (is_null($this->database->config_hash)) {
|
||||||
|
$this->database->isConfigurationChanged(true);
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
|
|||||||
@@ -78,6 +78,12 @@ class General extends Component
|
|||||||
$this->dispatch('success', 'Database updated.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
if (is_null($this->database->config_hash)) {
|
||||||
|
$this->database->isConfigurationChanged(true);
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ class General extends Component
|
|||||||
$this->dispatch('success', 'Database updated.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
if (is_null($this->database->config_hash)) {
|
||||||
|
$this->database->isConfigurationChanged(true);
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ class General extends Component
|
|||||||
$this->db_url_public = $this->database->get_db_url();
|
$this->db_url_public = $this->database->get_db_url();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSaveAdvanced() {
|
public function instantSaveAdvanced()
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||||
$this->database->is_log_drain_enabled = false;
|
$this->database->is_log_drain_enabled = false;
|
||||||
@@ -164,6 +165,12 @@ class General extends Component
|
|||||||
$this->dispatch('success', 'Database updated.');
|
$this->dispatch('success', 'Database updated.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
if (is_null($this->database->config_hash)) {
|
||||||
|
$this->database->isConfigurationChanged(true);
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,14 @@ class Edit extends Component
|
|||||||
public function saveKey($data)
|
public function saveKey($data)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$found = $this->project->environment_variables()->where('key', $data['key'])->first();
|
||||||
|
if ($found) {
|
||||||
|
throw new \Exception('Variable already exists.');
|
||||||
|
}
|
||||||
$this->project->environment_variables()->create([
|
$this->project->environment_variables()->create([
|
||||||
'key' => $data['key'],
|
'key' => $data['key'],
|
||||||
'value' => $data['value'],
|
'value' => $data['value'],
|
||||||
|
'is_multiline' => $data['is_multiline'],
|
||||||
'type' => 'project',
|
'type' => 'project',
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
@@ -41,10 +46,11 @@ class Edit extends Component
|
|||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
|
||||||
try {
|
try {
|
||||||
|
$this->validate();
|
||||||
$this->project->save();
|
$this->project->save();
|
||||||
$this->dispatch('saved');
|
$this->dispatch('saved');
|
||||||
|
$this->dispatch('success', 'Project updated.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,14 @@ class EnvironmentEdit extends Component
|
|||||||
public function saveKey($data)
|
public function saveKey($data)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$found = $this->environment->environment_variables()->where('key', $data['key'])->first();
|
||||||
|
if ($found) {
|
||||||
|
throw new \Exception('Variable already exists.');
|
||||||
|
}
|
||||||
$this->environment->environment_variables()->create([
|
$this->environment->environment_variables()->create([
|
||||||
'key' => $data['key'],
|
'key' => $data['key'],
|
||||||
'value' => $data['value'],
|
'value' => $data['value'],
|
||||||
|
'is_multiline' => $data['is_multiline'],
|
||||||
'type' => 'environment',
|
'type' => 'environment',
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project;
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
|
use App\Models\PrivateKey;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -10,7 +11,9 @@ class Index extends Component
|
|||||||
{
|
{
|
||||||
public $projects;
|
public $projects;
|
||||||
public $servers;
|
public $servers;
|
||||||
|
public $private_keys;
|
||||||
public function mount() {
|
public function mount() {
|
||||||
|
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
|
||||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||||
$this->servers = Server::ownedByCurrentTeam()->count();
|
$this->servers = Server::ownedByCurrentTeam()->count();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,12 @@ use App\Models\GithubApp;
|
|||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use App\Traits\SaveFromRedirect;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class GithubPrivateRepository extends Component
|
class GithubPrivateRepository extends Component
|
||||||
{
|
{
|
||||||
use SaveFromRedirect;
|
|
||||||
public $current_step = 'github_apps';
|
public $current_step = 'github_apps';
|
||||||
public $github_apps;
|
public $github_apps;
|
||||||
public GithubApp $github_app;
|
public GithubApp $github_app;
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ class Select extends Component
|
|||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$this->loadServices();
|
|
||||||
return view('livewire.project.new.select');
|
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)
|
public function loadServices(bool $force = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$this->loadingServices = true;
|
||||||
if (count($this->allServices) > 0 && !$force) {
|
if (count($this->allServices) > 0 && !$force) {
|
||||||
if (!$this->search) {
|
if (!$this->search) {
|
||||||
$this->services = $this->allServices;
|
$this->services = $this->allServices;
|
||||||
@@ -90,8 +94,7 @@ class Select extends Component
|
|||||||
$this->allServices = getServiceTemplates();
|
$this->allServices = getServiceTemplates();
|
||||||
$this->services = $this->allServices->filter(function ($service, $key) {
|
$this->services = $this->allServices->filter(function ($service, $key) {
|
||||||
return str_contains(strtolower($key), strtolower($this->search));
|
return str_contains(strtolower($key), strtolower($this->search));
|
||||||
});;
|
});
|
||||||
$this->dispatch('success', 'Successfully loaded services.');
|
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -117,6 +120,9 @@ class Select extends Component
|
|||||||
case 'mysql':
|
case 'mysql':
|
||||||
case 'mariadb':
|
case 'mariadb':
|
||||||
case 'redis':
|
case 'redis':
|
||||||
|
case 'keydb':
|
||||||
|
case 'dragonfly':
|
||||||
|
case 'clickhouse':
|
||||||
case 'mongodb':
|
case 'mongodb':
|
||||||
$this->isDatabase = true;
|
$this->isDatabase = true;
|
||||||
$this->includeSwarm = false;
|
$this->includeSwarm = false;
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ class Create extends Component
|
|||||||
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
||||||
} else if ($type->value() === 'mariadb') {
|
} else if ($type->value() === 'mariadb') {
|
||||||
$database = create_standalone_mariadb($environment->id, $destination_uuid);
|
$database = create_standalone_mariadb($environment->id, $destination_uuid);
|
||||||
|
} else if ($type->value() === 'keydb') {
|
||||||
|
$database = create_standalone_keydb($environment->id, $destination_uuid);
|
||||||
|
} else if ($type->value() === 'dragonfly') {
|
||||||
|
$database = create_standalone_dragonfly($environment->id, $destination_uuid);
|
||||||
|
}else if ($type->value() === 'clickhouse') {
|
||||||
|
$database = create_standalone_clickhouse($environment->id, $destination_uuid);
|
||||||
}
|
}
|
||||||
return redirect()->route('project.database.configuration', [
|
return redirect()->route('project.database.configuration', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ class Index extends Component
|
|||||||
public $mongodbs = [];
|
public $mongodbs = [];
|
||||||
public $mysqls = [];
|
public $mysqls = [];
|
||||||
public $mariadbs = [];
|
public $mariadbs = [];
|
||||||
|
public $keydbs = [];
|
||||||
|
public $dragonflies = [];
|
||||||
|
public $clickhouses = [];
|
||||||
public $services = [];
|
public $services = [];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -96,6 +99,39 @@ class Index extends Component
|
|||||||
}
|
}
|
||||||
return $mariadb;
|
return $mariadb;
|
||||||
});
|
});
|
||||||
|
$this->keydbs = $this->environment->keydbs->load(['tags'])->sortBy('name');
|
||||||
|
$this->keydbs = $this->keydbs->map(function ($keydb) {
|
||||||
|
if (data_get($keydb, 'environment.project.uuid')) {
|
||||||
|
$keydb->hrefLink = route('project.database.configuration', [
|
||||||
|
'project_uuid' => data_get($keydb, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($keydb, 'environment.name'),
|
||||||
|
'database_uuid' => data_get($keydb, 'uuid')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $keydb;
|
||||||
|
});
|
||||||
|
$this->dragonflies = $this->environment->dragonflies->load(['tags'])->sortBy('name');
|
||||||
|
$this->dragonflies = $this->dragonflies->map(function ($dragonfly) {
|
||||||
|
if (data_get($dragonfly, 'environment.project.uuid')) {
|
||||||
|
$dragonfly->hrefLink = route('project.database.configuration', [
|
||||||
|
'project_uuid' => data_get($dragonfly, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($dragonfly, 'environment.name'),
|
||||||
|
'database_uuid' => data_get($dragonfly, 'uuid')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $dragonfly;
|
||||||
|
});
|
||||||
|
$this->clickhouses = $this->environment->clickhouses->load(['tags'])->sortBy('name');
|
||||||
|
$this->clickhouses = $this->clickhouses->map(function ($clickhouse) {
|
||||||
|
if (data_get($clickhouse, 'environment.project.uuid')) {
|
||||||
|
$clickhouse->hrefLink = route('project.database.configuration', [
|
||||||
|
'project_uuid' => data_get($clickhouse, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($clickhouse, 'environment.name'),
|
||||||
|
'database_uuid' => data_get($clickhouse, 'uuid')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $clickhouse;
|
||||||
|
});
|
||||||
$this->services = $this->environment->services->load(['tags'])->sortBy('name');
|
$this->services = $this->environment->services->load(['tags'])->sortBy('name');
|
||||||
$this->services = $this->services->map(function ($service) {
|
$this->services = $this->services->map(function ($service) {
|
||||||
if (data_get($service, 'environment.project.uuid')) {
|
if (data_get($service, 'environment.project.uuid')) {
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ class Configuration extends Component
|
|||||||
$userId = auth()->user()->id;
|
$userId = auth()->user()->id;
|
||||||
return [
|
return [
|
||||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
|
"echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
|
||||||
"check_status"
|
"check_status",
|
||||||
|
"refresh" => '$refresh',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
@@ -36,12 +37,36 @@ class Configuration extends Component
|
|||||||
$this->applications = $this->service->applications->sort();
|
$this->applications = $this->service->applications->sort();
|
||||||
$this->databases = $this->service->databases->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()
|
public function check_status()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
dispatch_sync(new ContainerStatusJob($this->service->server));
|
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||||
$this->dispatch('refresh')->self();
|
$this->dispatch('refresh')->self();
|
||||||
$this->dispatch('serviceStatusChanged');
|
$this->dispatch('updateStatus');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ class Database extends Component
|
|||||||
}
|
}
|
||||||
$this->refreshFileStorages();
|
$this->refreshFileStorages();
|
||||||
}
|
}
|
||||||
public function instantSaveAdvanced()
|
public function instantSaveExclude()
|
||||||
|
{
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
|
public function instantSaveLogDrain()
|
||||||
{
|
{
|
||||||
if (!$this->database->service->destination->server->isLogDrainEnabled()) {
|
if (!$this->database->service->destination->server->isLogDrainEnabled()) {
|
||||||
$this->database->is_log_drain_enabled = false;
|
$this->database->is_log_drain_enabled = false;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Modal;
|
namespace App\Livewire\Project\Service;
|
||||||
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use LivewireUI\Modal\ModalComponent;
|
use Livewire\Component;
|
||||||
|
|
||||||
class EditCompose extends ModalComponent
|
class EditCompose extends Component
|
||||||
{
|
{
|
||||||
public Service $service;
|
public Service $service;
|
||||||
public $serviceId;
|
public $serviceId;
|
||||||
@@ -16,13 +16,13 @@ class EditCompose extends ModalComponent
|
|||||||
public function mount() {
|
public function mount() {
|
||||||
$this->service = Service::find($this->serviceId);
|
$this->service = Service::find($this->serviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function saveEditedCompose() {
|
||||||
|
$this->dispatch('info', "Saving new docker compose...");
|
||||||
|
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
|
||||||
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.modal.edit-compose');
|
return view('livewire.project.service.edit-compose');
|
||||||
}
|
|
||||||
public function submit() {
|
|
||||||
$this->dispatch('warning', "Saving new docker compose...");
|
|
||||||
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
|
|
||||||
$this->closeModal();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
53
app/Livewire/Project/Service/EditDomain.php
Normal file
53
app/Livewire/Project/Service/EditDomain.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class EditDomain extends Component
|
||||||
|
{
|
||||||
|
public $applicationId;
|
||||||
|
public ServiceApplication $application;
|
||||||
|
protected $rules = [
|
||||||
|
'application.fqdn' => 'nullable',
|
||||||
|
'application.required_fqdn' => 'required|boolean',
|
||||||
|
];
|
||||||
|
public function mount() {
|
||||||
|
$this->application = ServiceApplication::find($this->applicationId);
|
||||||
|
}
|
||||||
|
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 submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
check_domain_usage(resource: $this->application);
|
||||||
|
$this->validate();
|
||||||
|
$this->application->save();
|
||||||
|
updateCompose($this->application);
|
||||||
|
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.<br><br>Only use multiple domains if you know what you are doing.');
|
||||||
|
} else {
|
||||||
|
$this->dispatch('success', 'Service saved.');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->dispatch('generateDockerCompose');
|
||||||
|
$this->dispatch('refresh');
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.edit-domain');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,14 @@ namespace App\Livewire\Project\Service;
|
|||||||
use App\Models\LocalFileVolume;
|
use App\Models\LocalFileVolume;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class FileStorage extends Component
|
class FileStorage extends Component
|
||||||
{
|
{
|
||||||
public LocalFileVolume $fileStorage;
|
public LocalFileVolume $fileStorage;
|
||||||
public ServiceApplication|ServiceDatabase $service;
|
public ServiceApplication|ServiceDatabase|StandaloneClickhouse $resource;
|
||||||
public string $fs_path;
|
public string $fs_path;
|
||||||
public ?string $workdir = null;
|
public ?string $workdir = null;
|
||||||
|
|
||||||
@@ -23,14 +24,14 @@ class FileStorage extends Component
|
|||||||
];
|
];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->service = $this->fileStorage->service;
|
$this->resource = $this->fileStorage->service;
|
||||||
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
|
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
|
||||||
$this->workdir = $this->service->service->workdir();
|
$this->workdir = $this->resource->service->workdir();
|
||||||
$this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
|
$this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
|
||||||
} else {
|
} else {
|
||||||
$this->workdir = null;
|
$this->workdir = null;
|
||||||
$this->fs_path = $this->fileStorage->fs_path;
|
$this->fs_path = $this->fileStorage->fs_path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
public Service $service;
|
public ?Service $service = null;
|
||||||
public ?ServiceApplication $serviceApplication = null;
|
public ?ServiceApplication $serviceApplication = null;
|
||||||
public ?ServiceDatabase $serviceDatabase = null;
|
public ?ServiceDatabase $serviceDatabase = null;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
@@ -26,7 +26,10 @@ class Index extends Component
|
|||||||
$this->services = collect([]);
|
$this->services = collect([]);
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
$this->query = request()->query();
|
$this->query = request()->query();
|
||||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
$this->service = Service::whereUuid($this->parameters['service_uuid'])->first();
|
||||||
|
if (!$this->service) {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
$service = $this->service->applications()->whereUuid($this->parameters['stack_service_uuid'])->first();
|
$service = $this->service->applications()->whereUuid($this->parameters['stack_service_uuid'])->first();
|
||||||
if ($service) {
|
if ($service) {
|
||||||
$this->serviceApplication = $service;
|
$this->serviceApplication = $service;
|
||||||
|
|||||||
@@ -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,36 @@ class Navbar extends Component
|
|||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
public $isDeploymentProgress = false;
|
public $isDeploymentProgress = false;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
if (str($this->service->status())->contains('running') && is_null($this->service->config_hash)) {
|
||||||
|
ray('isConfigurationChanged init');
|
||||||
|
$this->service->isConfigurationChanged(true);
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
|
}
|
||||||
public function getListeners()
|
public function getListeners()
|
||||||
{
|
{
|
||||||
|
$userId = auth()->user()->id;
|
||||||
return [
|
return [
|
||||||
"serviceStatusChanged"
|
"echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
|
||||||
|
"updateStatus"=> '$refresh',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
public function serviceStatusChanged()
|
public function serviceStarted()
|
||||||
{
|
{
|
||||||
$this->dispatch('refresh')->self();
|
$this->dispatch('success', 'Service status changed.');
|
||||||
|
if (is_null($this->service->config_hash) || $this->service->isConfigurationChanged()) {
|
||||||
|
$this->service->isConfigurationChanged(true);
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public function check_status() {
|
|
||||||
|
public function check_status()
|
||||||
|
{
|
||||||
$this->dispatch('check_status');
|
$this->dispatch('check_status');
|
||||||
$this->dispatch('success', 'Service status updated.');
|
$this->dispatch('success', 'Service status updated.');
|
||||||
}
|
}
|
||||||
@@ -44,7 +63,7 @@ class Navbar extends Component
|
|||||||
$this->isDeploymentProgress = false;
|
$this->isDeploymentProgress = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function deploy()
|
public function start()
|
||||||
{
|
{
|
||||||
$this->checkDeployments();
|
$this->checkDeployments();
|
||||||
if ($this->isDeploymentProgress) {
|
if ($this->isDeploymentProgress) {
|
||||||
@@ -73,9 +92,9 @@ class Navbar extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PullImage::run($this->service);
|
PullImage::run($this->service);
|
||||||
$this->dispatch('image-pulled');
|
|
||||||
StopService::run($this->service);
|
StopService::run($this->service);
|
||||||
$this->service->parse();
|
$this->service->parse();
|
||||||
|
$this->dispatch('imagePulled');
|
||||||
$activity = StartService::run($this->service);
|
$activity = StartService::run($this->service);
|
||||||
$this->dispatch('activityMonitor', $activity->id);
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,16 @@ class ServiceApplicationView extends Component
|
|||||||
{
|
{
|
||||||
return view('livewire.project.service.service-application-view');
|
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()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
$this->submit();
|
$this->submit();
|
||||||
@@ -55,11 +65,15 @@ class ServiceApplicationView extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
check_fqdn_usage($this->application);
|
check_domain_usage(resource: $this->application);
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
updateCompose($this->application);
|
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.<br><br>Only use multiple domains if you know what you are doing.');
|
||||||
|
} else {
|
||||||
|
$this->dispatch('success', 'Service saved.');
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Service;
|
namespace App\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class StackForm extends Component
|
class StackForm extends Component
|
||||||
{
|
{
|
||||||
public $service;
|
public Service $service;
|
||||||
public $fields = [];
|
public $fields = [];
|
||||||
protected $listeners = ["saveCompose"];
|
protected $listeners = ["saveCompose"];
|
||||||
public $rules = [
|
public $rules = [
|
||||||
@@ -68,6 +69,13 @@ class StackForm extends Component
|
|||||||
$this->dispatch('success', 'Service saved.');
|
$this->dispatch('success', 'Service saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
if (is_null($this->service->config_hash)) {
|
||||||
|
ray('asdf');
|
||||||
|
$this->service->isConfigurationChanged(true);
|
||||||
|
} else {
|
||||||
|
$this->dispatch('configurationChanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
|
|||||||
34
app/Livewire/Project/Shared/ConfigurationChecker.php
Normal file
34
app/Livewire/Project/Shared/ConfigurationChecker.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
|
use App\Models\StandaloneMariadb;
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use App\Models\StandaloneMysql;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class ConfigurationChecker extends Component
|
||||||
|
{
|
||||||
|
public bool $isConfigurationChanged = false;
|
||||||
|
public Application|Service|StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource;
|
||||||
|
protected $listeners = ['configurationChanged'];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->configurationChanged();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.configuration-checker');
|
||||||
|
}
|
||||||
|
public function configurationChanged()
|
||||||
|
{
|
||||||
|
$this->isConfigurationChanged = $this->resource->isConfigurationChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ class Danger extends Component
|
|||||||
public $resource;
|
public $resource;
|
||||||
public $projectUuid;
|
public $projectUuid;
|
||||||
public $environmentName;
|
public $environmentName;
|
||||||
|
public bool $delete_configurations = true;
|
||||||
public ?string $modalId = null;
|
public ?string $modalId = null;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -20,12 +21,11 @@ class Danger extends Component
|
|||||||
$this->projectUuid = data_get($parameters, 'project_uuid');
|
$this->projectUuid = data_get($parameters, 'project_uuid');
|
||||||
$this->environmentName = data_get($parameters, 'environment_name');
|
$this->environmentName = data_get($parameters, 'environment_name');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->resource->delete();
|
$this->resource->delete();
|
||||||
DeleteResourceJob::dispatch($this->resource);
|
DeleteResourceJob::dispatch($this->resource, $this->delete_configurations);
|
||||||
return redirect()->route('project.resource.index', [
|
return redirect()->route('project.resource.index', [
|
||||||
'project_uuid' => $this->projectUuid,
|
'project_uuid' => $this->projectUuid,
|
||||||
'environment_name' => $this->environmentName
|
'environment_name' => $this->environmentName
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class Destination extends Component
|
|||||||
public function redeploy(int $network_id, int $server_id)
|
public function redeploy(int $network_id, int $server_id)
|
||||||
{
|
{
|
||||||
if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
|||||||
@@ -11,17 +11,20 @@ class Add extends Component
|
|||||||
public string $key;
|
public string $key;
|
||||||
public ?string $value = null;
|
public ?string $value = null;
|
||||||
public bool $is_build_time = false;
|
public bool $is_build_time = false;
|
||||||
|
public bool $is_multiline = false;
|
||||||
|
|
||||||
protected $listeners = ['clearAddEnv' => 'clear'];
|
protected $listeners = ['clearAddEnv' => 'clear'];
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'key' => 'required|string',
|
'key' => 'required|string',
|
||||||
'value' => 'nullable',
|
'value' => 'nullable',
|
||||||
'is_build_time' => 'required|boolean',
|
'is_build_time' => 'required|boolean',
|
||||||
|
'is_multiline' => 'required|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'key' => 'key',
|
'key' => 'key',
|
||||||
'value' => 'value',
|
'value' => 'value',
|
||||||
'is_build_time' => 'build',
|
'is_build_time' => 'build',
|
||||||
|
'is_multiline' => 'multiline',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -43,6 +46,7 @@ class Add extends Component
|
|||||||
'key' => $this->key,
|
'key' => $this->key,
|
||||||
'value' => $this->value,
|
'value' => $this->value,
|
||||||
'is_build_time' => $this->is_build_time,
|
'is_build_time' => $this->is_build_time,
|
||||||
|
'is_multiline' => $this->is_multiline,
|
||||||
'is_preview' => $this->is_preview,
|
'is_preview' => $this->is_preview,
|
||||||
]);
|
]);
|
||||||
$this->clear();
|
$this->clear();
|
||||||
@@ -53,5 +57,6 @@ class Add extends Component
|
|||||||
$this->key = '';
|
$this->key = '';
|
||||||
$this->value = '';
|
$this->value = '';
|
||||||
$this->is_build_time = false;
|
$this->is_build_time = false;
|
||||||
|
$this->is_multiline = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class All extends Component
|
|||||||
{
|
{
|
||||||
public $resource;
|
public $resource;
|
||||||
public bool $showPreview = false;
|
public bool $showPreview = false;
|
||||||
public string|null $modalId = null;
|
public ?string $modalId = null;
|
||||||
public ?string $variables = null;
|
public ?string $variables = null;
|
||||||
public ?string $variablesPreview = null;
|
public ?string $variablesPreview = null;
|
||||||
public string $view = 'normal';
|
public string $view = 'normal';
|
||||||
@@ -34,6 +34,9 @@ class All extends Component
|
|||||||
if ($item->is_shown_once) {
|
if ($item->is_shown_once) {
|
||||||
return "$item->key=(locked secret)";
|
return "$item->key=(locked secret)";
|
||||||
}
|
}
|
||||||
|
if ($item->is_multiline) {
|
||||||
|
return "$item->key=(multiline, edit in normal view)";
|
||||||
|
}
|
||||||
return "$item->key=$item->value";
|
return "$item->key=$item->value";
|
||||||
})->sort()->join('
|
})->sort()->join('
|
||||||
');
|
');
|
||||||
@@ -42,6 +45,9 @@ class All extends Component
|
|||||||
if ($item->is_shown_once) {
|
if ($item->is_shown_once) {
|
||||||
return "$item->key=(locked secret)";
|
return "$item->key=(locked secret)";
|
||||||
}
|
}
|
||||||
|
if ($item->is_multiline) {
|
||||||
|
return "$item->key=(multiline, edit in normal view)";
|
||||||
|
}
|
||||||
return "$item->key=$item->value";
|
return "$item->key=$item->value";
|
||||||
})->sort()->join('
|
})->sort()->join('
|
||||||
');
|
');
|
||||||
@@ -67,7 +73,7 @@ class All extends Component
|
|||||||
$found = $this->resource->environment_variables()->where('key', $key)->first();
|
$found = $this->resource->environment_variables()->where('key', $key)->first();
|
||||||
}
|
}
|
||||||
if ($found) {
|
if ($found) {
|
||||||
if ($found->is_shown_once) {
|
if ($found->is_shown_once || $found->is_multiline) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$found->value = $variable;
|
$found->value = $variable;
|
||||||
@@ -92,6 +98,7 @@ class All extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$environment->is_build_time = false;
|
$environment->is_build_time = false;
|
||||||
|
$environment->is_multiline = false;
|
||||||
$environment->is_preview = $isPreview ? true : false;
|
$environment->is_preview = $isPreview ? true : false;
|
||||||
switch ($this->resource->type()) {
|
switch ($this->resource->type()) {
|
||||||
case 'application':
|
case 'application':
|
||||||
@@ -112,6 +119,15 @@ class All extends Component
|
|||||||
case 'standalone-mariadb':
|
case 'standalone-mariadb':
|
||||||
$environment->standalone_mariadb_id = $this->resource->id;
|
$environment->standalone_mariadb_id = $this->resource->id;
|
||||||
break;
|
break;
|
||||||
|
case 'standalone-keydb':
|
||||||
|
$environment->standalone_keydb_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'standalone-dragonfly':
|
||||||
|
$environment->standalone_dragonfly_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'standalone-clickhouse':
|
||||||
|
$environment->standalone_clickhouse_id = $this->resource->id;
|
||||||
|
break;
|
||||||
case 'service':
|
case 'service':
|
||||||
$environment->service_id = $this->resource->id;
|
$environment->service_id = $this->resource->id;
|
||||||
break;
|
break;
|
||||||
@@ -144,6 +160,7 @@ class All extends Component
|
|||||||
$environment->key = $data['key'];
|
$environment->key = $data['key'];
|
||||||
$environment->value = $data['value'];
|
$environment->value = $data['value'];
|
||||||
$environment->is_build_time = $data['is_build_time'];
|
$environment->is_build_time = $data['is_build_time'];
|
||||||
|
$environment->is_multiline = $data['is_multiline'];
|
||||||
$environment->is_preview = $data['is_preview'];
|
$environment->is_preview = $data['is_preview'];
|
||||||
|
|
||||||
switch ($this->resource->type()) {
|
switch ($this->resource->type()) {
|
||||||
@@ -165,6 +182,15 @@ class All extends Component
|
|||||||
case 'standalone-mariadb':
|
case 'standalone-mariadb':
|
||||||
$environment->standalone_mariadb_id = $this->resource->id;
|
$environment->standalone_mariadb_id = $this->resource->id;
|
||||||
break;
|
break;
|
||||||
|
case 'standalone-keydb':
|
||||||
|
$environment->standalone_keydb_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'standalone-dragonfly':
|
||||||
|
$environment->standalone_dragonfly_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'standalone-clickhouse':
|
||||||
|
$environment->standalone_clickhouse_id = $this->resource->id;
|
||||||
|
break;
|
||||||
case 'service':
|
case 'service':
|
||||||
$environment->service_id = $this->resource->id;
|
$environment->service_id = $this->resource->id;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class Show extends Component
|
|||||||
'env.key' => 'required|string',
|
'env.key' => 'required|string',
|
||||||
'env.value' => 'nullable',
|
'env.value' => 'nullable',
|
||||||
'env.is_build_time' => 'required|boolean',
|
'env.is_build_time' => 'required|boolean',
|
||||||
|
'env.is_multiline' => 'required|boolean',
|
||||||
'env.is_shown_once' => 'required|boolean',
|
'env.is_shown_once' => 'required|boolean',
|
||||||
'env.real_value' => 'nullable',
|
'env.real_value' => 'nullable',
|
||||||
];
|
];
|
||||||
@@ -28,6 +29,7 @@ class Show extends Component
|
|||||||
'env.key' => 'Key',
|
'env.key' => 'Key',
|
||||||
'env.value' => 'Value',
|
'env.value' => 'Value',
|
||||||
'env.is_build_time' => 'Build Time',
|
'env.is_build_time' => 'Build Time',
|
||||||
|
'env.is_multiline' => 'Multiline',
|
||||||
'env.is_shown_once' => 'Shown Once',
|
'env.is_shown_once' => 'Shown Once',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,6 @@ namespace App\Livewire\Project\Shared;
|
|||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\StandaloneMariadb;
|
|
||||||
use App\Models\StandaloneMongodb;
|
|
||||||
use App\Models\StandaloneMysql;
|
|
||||||
use App\Models\StandalonePostgresql;
|
|
||||||
use App\Models\StandaloneRedis;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -50,21 +45,9 @@ class ExecuteContainerCommand extends Component
|
|||||||
}
|
}
|
||||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||||
$this->type = 'database';
|
$this->type = 'database';
|
||||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
$resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(),'id'));
|
||||||
if (is_null($resource)) {
|
if (is_null($resource)) {
|
||||||
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
abort(404);
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$this->resource = $resource;
|
$this->resource = $resource;
|
||||||
if ($this->resource->destination->server->isFunctional()) {
|
if ($this->resource->destination->server->isFunctional()) {
|
||||||
@@ -109,7 +92,7 @@ class ExecuteContainerCommand extends Component
|
|||||||
];
|
];
|
||||||
$this->containers = $this->containers->push($payload);
|
$this->containers = $this->containers->push($payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->containers->count() > 0) {
|
if ($this->containers->count() > 0) {
|
||||||
if (data_get($this->parameters, 'application_uuid')) {
|
if (data_get($this->parameters, 'application_uuid')) {
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ use App\Models\Server;
|
|||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -19,7 +22,7 @@ class GetLogs extends Component
|
|||||||
{
|
{
|
||||||
public string $outputs = '';
|
public string $outputs = '';
|
||||||
public string $errors = '';
|
public string $errors = '';
|
||||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|null $resource = null;
|
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|null $resource = null;
|
||||||
public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
|
public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public ?string $container = null;
|
public ?string $container = null;
|
||||||
@@ -71,6 +74,9 @@ class GetLogs extends Component
|
|||||||
}
|
}
|
||||||
public function getLogs($refresh = false)
|
public function getLogs($refresh = false)
|
||||||
{
|
{
|
||||||
|
if (!$this->server->isFunctional()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ($this->resource?->getMorphClass() === 'App\Models\Application') {
|
if ($this->resource?->getMorphClass() === 'App\Models\Application') {
|
||||||
if (str($this->container)->contains('-pr-')) {
|
if (str($this->container)->contains('-pr-')) {
|
||||||
$this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value();
|
$this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value();
|
||||||
@@ -79,6 +85,9 @@ class GetLogs extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$refresh && ($this->resource?->getMorphClass() === 'App\Models\Service' || str($this->container)->contains('-pr-'))) return;
|
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->container) {
|
||||||
if ($this->showTimeStamps) {
|
if ($this->showTimeStamps) {
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ namespace App\Livewire\Project\Shared;
|
|||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -16,7 +19,7 @@ use Livewire\Component;
|
|||||||
class Logs extends Component
|
class Logs extends Component
|
||||||
{
|
{
|
||||||
public ?string $type = null;
|
public ?string $type = null;
|
||||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
|
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource;
|
||||||
public Collection $servers;
|
public Collection $servers;
|
||||||
public Collection $containers;
|
public Collection $containers;
|
||||||
public $container = [];
|
public $container = [];
|
||||||
@@ -29,6 +32,9 @@ class Logs extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$server = $this->servers->firstWhere('id', $server_id);
|
$server = $this->servers->firstWhere('id', $server_id);
|
||||||
|
if (!$server->isFunctional()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ($server->isSwarm()) {
|
if ($server->isSwarm()) {
|
||||||
$containers = collect([
|
$containers = collect([
|
||||||
[
|
[
|
||||||
@@ -64,21 +70,9 @@ class Logs extends Component
|
|||||||
}
|
}
|
||||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||||
$this->type = 'database';
|
$this->type = 'database';
|
||||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
$resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
|
||||||
if (is_null($resource)) {
|
if (is_null($resource)) {
|
||||||
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
abort(404);
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$this->resource = $resource;
|
$this->resource = $resource;
|
||||||
$this->status = $this->resource->status;
|
$this->status = $this->resource->status;
|
||||||
@@ -96,7 +90,6 @@ class Logs extends Component
|
|||||||
$this->resource->databases()->get()->each(function ($database) {
|
$this->resource->databases()->get()->each(function ($database) {
|
||||||
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
|
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($this->resource->server->isFunctional()) {
|
if ($this->resource->server->isFunctional()) {
|
||||||
$this->servers = $this->servers->push($this->resource->server);
|
$this->servers = $this->servers->push($this->resource->server);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,10 @@ class ResourceOperations extends Component
|
|||||||
$this->resource->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
$this->resource->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
||||||
$this->resource->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
$this->resource->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
||||||
$this->resource->getMorphClass() === 'App\Models\StandaloneMariadb' ||
|
$this->resource->getMorphClass() === 'App\Models\StandaloneMariadb' ||
|
||||||
$this->resource->getMorphClass() === 'App\Models\StandaloneRedis'
|
$this->resource->getMorphClass() === 'App\Models\StandaloneRedis' ||
|
||||||
|
$this->resource->getMorphClass() === 'App\Models\StandaloneKeydb' ||
|
||||||
|
$this->resource->getMorphClass() === 'App\Models\StandaloneDragonfly' ||
|
||||||
|
$this->resource->getMorphClass() === 'App\Models\StandaloneClickhouse'
|
||||||
) {
|
) {
|
||||||
$uuid = (string)new Cuid2(7);
|
$uuid = (string)new Cuid2(7);
|
||||||
$new_resource = $this->resource->replicate()->fill([
|
$new_resource = $this->resource->replicate()->fill([
|
||||||
|
|||||||
@@ -33,19 +33,23 @@ class Add extends Component
|
|||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
try {
|
||||||
$isValid = validate_cron_expression($this->frequency);
|
$this->validate();
|
||||||
if (!$isValid) {
|
$isValid = validate_cron_expression($this->frequency);
|
||||||
$this->dispatch('error', 'Invalid Cron / Human expression.');
|
if (!$isValid) {
|
||||||
return;
|
$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()
|
public function clear()
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Shared\Storages;
|
namespace App\Livewire\Project\Shared\Storages;
|
||||||
|
|
||||||
use App\Models\LocalPersistentVolume;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class All extends Component
|
class All extends Component
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class Tags extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->resource->tags()->where('id', $id)->exists()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
$this->resource->tags()->syncWithoutDetaching($id);
|
$this->resource->tags()->syncWithoutDetaching($id);
|
||||||
@@ -66,7 +66,7 @@ class Tags extends Component
|
|||||||
$tags = str($this->new_tag)->trim()->explode(' ');
|
$tags = str($this->new_tag)->trim()->explode(' ');
|
||||||
foreach ($tags as $tag) {
|
foreach ($tags as $tag) {
|
||||||
if ($this->resource->tags()->where('name', $tag)->exists()) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
$found = Tag::where(['name' => $tag, 'team_id' => currentTeam()->id])->first();
|
$found = Tag::where(['name' => $tag, 'team_id' => currentTeam()->id])->first();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user