mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-24 20:49:32 +00:00
Compare commits
410 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc8f09f05e | ||
|
|
2beda08717 | ||
|
|
b455d153ae | ||
|
|
5fddf01820 | ||
|
|
c80434141d | ||
|
|
d1128c7a1e | ||
|
|
aae81313a6 | ||
|
|
4667f96b40 | ||
|
|
28c320ae97 | ||
|
|
45017efe00 | ||
|
|
a20290cac8 | ||
|
|
31e02a154c | ||
|
|
023ee5db99 | ||
|
|
05d2e15ab5 | ||
|
|
7d6590c60a | ||
|
|
3152ce183b | ||
|
|
d9f1a7c4d0 | ||
|
|
952aed3c49 | ||
|
|
ab3c433450 | ||
|
|
2b5e4a34d4 | ||
|
|
35cea852ca | ||
|
|
88581c8983 | ||
|
|
a7a9aab189 | ||
|
|
370c9b63cf | ||
|
|
7cb08849de | ||
|
|
7f052163e3 | ||
|
|
277d939033 | ||
|
|
26fbdcfab0 | ||
|
|
6d63ba9d4d | ||
|
|
8963f4fd62 | ||
|
|
463021a9f3 | ||
|
|
f71a8e9fef | ||
|
|
2dd5be1b4e | ||
|
|
608838045f | ||
|
|
899d506faa | ||
|
|
21b3e3ea05 | ||
|
|
a68951541c | ||
|
|
7fd0deedb1 | ||
|
|
e9e12ad843 | ||
|
|
4fd3185d12 | ||
|
|
f5ccebfd41 | ||
|
|
294721eef9 | ||
|
|
8af509992d | ||
|
|
11fccb8e89 | ||
|
|
1e126dd2c3 | ||
|
|
cfe2f889a4 | ||
|
|
1bd76b0e07 | ||
|
|
6d8c935cc7 | ||
|
|
7144cee0f6 | ||
|
|
f75a8d56f2 | ||
|
|
2f321bcfd9 | ||
|
|
a157f4f17b | ||
|
|
7723c623d5 | ||
|
|
bc3bb78916 | ||
|
|
0ebf5e49fb | ||
|
|
c340921fbb | ||
|
|
30e26b101c | ||
|
|
1b17cab663 | ||
|
|
ab039adf97 | ||
|
|
62fe10df31 | ||
|
|
2004a751dd | ||
|
|
ace127acf4 | ||
|
|
82c5497a06 | ||
|
|
dbb7989027 | ||
|
|
103f677a93 | ||
|
|
cb6bf78595 | ||
|
|
62334ddef7 | ||
|
|
778f67f2e4 | ||
|
|
48737f8e60 | ||
|
|
94de62e503 | ||
|
|
0445052898 | ||
|
|
ccde90ea91 | ||
|
|
ed94355019 | ||
|
|
02fcd1b5fc | ||
|
|
ca934e7cdf | ||
|
|
0ddd5f0a79 | ||
|
|
099d6801b7 | ||
|
|
62293926ec | ||
|
|
c96daad12c | ||
|
|
9cc3be152f | ||
|
|
f841c0d4ba | ||
|
|
3cd1d8135e | ||
|
|
86474d9f90 | ||
|
|
9bf87a3033 | ||
|
|
71e32520cf | ||
|
|
e3e938c8eb | ||
|
|
03aa440424 | ||
|
|
85ca38be90 | ||
|
|
7c9790dff0 | ||
|
|
40a71a11cb | ||
|
|
46a500f5e5 | ||
|
|
2d1d03bf8e | ||
|
|
1092d00c7a | ||
|
|
cd58e0d01e | ||
|
|
30a9783348 | ||
|
|
c839cf50af | ||
|
|
f8d607b06f | ||
|
|
cfadeb07b1 | ||
|
|
072850be0b | ||
|
|
71d120bc4e | ||
|
|
68d3cea528 | ||
|
|
07e801f44d | ||
|
|
ee5c694aa2 | ||
|
|
efa5eb1770 | ||
|
|
42e37246f3 | ||
|
|
dabb08ff4a | ||
|
|
66b0e04cc6 | ||
|
|
74824b7737 | ||
|
|
df2bcdb854 | ||
|
|
a8e9ee2e95 | ||
|
|
5093697b27 | ||
|
|
5bacd63805 | ||
|
|
1d5932e63f | ||
|
|
022762c0c9 | ||
|
|
e16bd194a3 | ||
|
|
a3765c19e3 | ||
|
|
a845d92d88 | ||
|
|
668c9e5a64 | ||
|
|
aaa06f4120 | ||
|
|
e26f4ce707 | ||
|
|
a8c3a2d991 | ||
|
|
6d52cef73a | ||
|
|
edacfcdec7 | ||
|
|
683872ef4e | ||
|
|
7a299ba1f9 | ||
|
|
f5eaedfc72 | ||
|
|
11b6afbe09 | ||
|
|
cd7340915b | ||
|
|
b38bb3df5d | ||
|
|
1f7725ada3 | ||
|
|
622095ebc4 | ||
|
|
397b7fefe3 | ||
|
|
10d38b709b | ||
|
|
98985690f0 | ||
|
|
f50c483c64 | ||
|
|
a15eca137d | ||
|
|
ba4be02e75 | ||
|
|
d4f6a86a57 | ||
|
|
6a058372bb | ||
|
|
e6cce350bd | ||
|
|
7c0c1e6cf8 | ||
|
|
39f787b7db | ||
|
|
424437446d | ||
|
|
60a1859d89 | ||
|
|
ad5c1639e8 | ||
|
|
92828b22fa | ||
|
|
e470096e4e | ||
|
|
3c41608ee9 | ||
|
|
035e145cd1 | ||
|
|
2f621279c2 | ||
|
|
c30185c6ae | ||
|
|
908c74eb27 | ||
|
|
10c17fc9a9 | ||
|
|
a6a0cb928a | ||
|
|
8bca988520 | ||
|
|
0af0af8d8a | ||
|
|
97da13c3c4 | ||
|
|
7134b46cdc | ||
|
|
aecdf7a3d3 | ||
|
|
37e37d1998 | ||
|
|
6103a8590d | ||
|
|
ba62dadc00 | ||
|
|
26073b82fd | ||
|
|
9b73bca79d | ||
|
|
0a0bb0ca13 | ||
|
|
652df47c5c | ||
|
|
9248be2177 | ||
|
|
b3800fc42e | ||
|
|
15f304736f | ||
|
|
21cdf59065 | ||
|
|
e3693afb75 | ||
|
|
009a753585 | ||
|
|
5c72541044 | ||
|
|
a01e604443 | ||
|
|
52b339d0b8 | ||
|
|
579ed5b9c0 | ||
|
|
63e64b8bcc | ||
|
|
64f8583975 | ||
|
|
75e8064044 | ||
|
|
6f3e38e392 | ||
|
|
cb4d244f19 | ||
|
|
de3b8a10a0 | ||
|
|
8feece702c | ||
|
|
900308afec | ||
|
|
b47925a319 | ||
|
|
853325d9fd | ||
|
|
494be37715 | ||
|
|
d35cb5d072 | ||
|
|
df9ec711c5 | ||
|
|
d9d0837024 | ||
|
|
086138fbd9 | ||
|
|
bdbd4b57b7 | ||
|
|
5475448af5 | ||
|
|
c3da3f11d9 | ||
|
|
244c81587c | ||
|
|
a3877a2cb1 | ||
|
|
206df82d63 | ||
|
|
54e1e7684d | ||
|
|
27eef36677 | ||
|
|
d91953e70b | ||
|
|
bbc5a49054 | ||
|
|
f89fe9fbab | ||
|
|
0c042bfe50 | ||
|
|
1a7894b15e | ||
|
|
543f983e41 | ||
|
|
97e7e473b8 | ||
|
|
79c30f7a94 | ||
|
|
cccf86d388 | ||
|
|
c102c23831 | ||
|
|
80ada9c90a | ||
|
|
6c3b4070ba | ||
|
|
4b287b758d | ||
|
|
b6d129a5c1 | ||
|
|
4d08147647 | ||
|
|
ace7c17f2b | ||
|
|
10f3d8aa0f | ||
|
|
3237ca0d97 | ||
|
|
5682ab9570 | ||
|
|
a3d73634e7 | ||
|
|
98b6aec203 | ||
|
|
7feb788ed3 | ||
|
|
bea490081b | ||
|
|
7adc3ca003 | ||
|
|
f8cbc63ab0 | ||
|
|
418590fb35 | ||
|
|
56144482f1 | ||
|
|
59f681e6af | ||
|
|
d3296f5180 | ||
|
|
c6fff0aa13 | ||
|
|
41e0c42282 | ||
|
|
ede2274816 | ||
|
|
ead672afb2 | ||
|
|
73bc7b045e | ||
|
|
3281502c25 | ||
|
|
ca35e536db | ||
|
|
bb8e0eb7bf | ||
|
|
bb451ac3b5 | ||
|
|
b0b7842f9c | ||
|
|
aad661cb65 | ||
|
|
ed9b63520d | ||
|
|
0d89d4d0d3 | ||
|
|
5de1246827 | ||
|
|
edc3b014cd | ||
|
|
fec98f45ce | ||
|
|
94810d5066 | ||
|
|
431cc796d8 | ||
|
|
e9d2dbcc92 | ||
|
|
7a618ef89c | ||
|
|
5b56249d12 | ||
|
|
e2ba5abe76 | ||
|
|
70a4b7c863 | ||
|
|
10fde1b1ef | ||
|
|
6131746180 | ||
|
|
bf953bf1b5 | ||
|
|
d81da10dfa | ||
|
|
ed9cdc1ab7 | ||
|
|
9f506eb83a | ||
|
|
36e8c52b01 | ||
|
|
e6d1233bfe | ||
|
|
6847459022 | ||
|
|
285623a02b | ||
|
|
fbf64f8037 | ||
|
|
2b74ca2746 | ||
|
|
b106a82308 | ||
|
|
d71a0ddd66 | ||
|
|
5700f2f78a | ||
|
|
c5ca6abb90 | ||
|
|
6826b6e1f8 | ||
|
|
27c4fa2fcf | ||
|
|
6ef1aff991 | ||
|
|
57a026a7a1 | ||
|
|
cfc785358e | ||
|
|
c2d2b3f3b2 | ||
|
|
4aa153fcf1 | ||
|
|
824f63a3ad | ||
|
|
2446dc6950 | ||
|
|
f98405188d | ||
|
|
a5cf24773c | ||
|
|
01c1e4f8cb | ||
|
|
0759fb6436 | ||
|
|
ed5188069b | ||
|
|
aa0a9bde76 | ||
|
|
56a450a936 | ||
|
|
1e01106b94 | ||
|
|
b9a755d6d3 | ||
|
|
1d9d6c899d | ||
|
|
444dffb458 | ||
|
|
3f1b7192ff | ||
|
|
b992b19c66 | ||
|
|
e341121f61 | ||
|
|
cd3e2963b3 | ||
|
|
346faf1d07 | ||
|
|
1e09b2bbd8 | ||
|
|
0ffba45517 | ||
|
|
32ff346154 | ||
|
|
0008f44255 | ||
|
|
a3c519a061 | ||
|
|
5b8a923cb5 | ||
|
|
576cbc0b90 | ||
|
|
e95e2cf152 | ||
|
|
317dc10af4 | ||
|
|
f06065337c | ||
|
|
cc870ca302 | ||
|
|
80f5506b18 | ||
|
|
5321a75272 | ||
|
|
b70a78b7aa | ||
|
|
5ad08791ea | ||
|
|
9dc3ec0bf8 | ||
|
|
69dd9d0cac | ||
|
|
24f923e88e | ||
|
|
b5552a216d | ||
|
|
f3fe4433ae | ||
|
|
1988c617a0 | ||
|
|
5e531d6f96 | ||
|
|
1fb7e97700 | ||
|
|
64d27156f5 | ||
|
|
b528a0f4ec | ||
|
|
7f265a6692 | ||
|
|
55e00e35c1 | ||
|
|
d94e1ba55b | ||
|
|
00e7167174 | ||
|
|
9d7b69fc0e | ||
|
|
db9a68e9c9 | ||
|
|
b5d9d6e268 | ||
|
|
5ff0c563ec | ||
|
|
e2131523ec | ||
|
|
1f5f51e3e5 | ||
|
|
1bb0d54dce | ||
|
|
094bb37049 | ||
|
|
58601db5ef | ||
|
|
b5bef98a9b | ||
|
|
1026f1efa5 | ||
|
|
0c673fb524 | ||
|
|
7ee2c9478d | ||
|
|
4b51f8251b | ||
|
|
e91a64b1cc | ||
|
|
8920762fc5 | ||
|
|
ba40f93386 | ||
|
|
781bf52a8e | ||
|
|
cca1a9832e | ||
|
|
1a152a5597 | ||
|
|
76a5290351 | ||
|
|
829e17ef2b | ||
|
|
bc5d3bea14 | ||
|
|
edf2a2e68d | ||
|
|
5df443a016 | ||
|
|
f6396f2e74 | ||
|
|
c618e58a11 | ||
|
|
2ea27acdde | ||
|
|
331cad276e | ||
|
|
b74eab8377 | ||
|
|
fb80318553 | ||
|
|
3eb4aed867 | ||
|
|
f6f959a897 | ||
|
|
2b422a542a | ||
|
|
d0e9d58a43 | ||
|
|
8a1933b9b2 | ||
|
|
96a587f343 | ||
|
|
2bb6a71874 | ||
|
|
94acd12f1c | ||
|
|
5e44a61068 | ||
|
|
a54f0ed94d | ||
|
|
662c6f3cc2 | ||
|
|
d93c635a0a | ||
|
|
17b73aaf91 | ||
|
|
c194911458 | ||
|
|
92e99e3fb4 | ||
|
|
848e6102a1 | ||
|
|
ef37bf9b1a | ||
|
|
eb41e023c7 | ||
|
|
6b4987bf39 | ||
|
|
d46ff76887 | ||
|
|
d53a9e672c | ||
|
|
80ef99a24b | ||
|
|
6062a1f8c7 | ||
|
|
d974bd9a07 | ||
|
|
fb8c9566d5 | ||
|
|
7431bd69e9 | ||
|
|
5d7f393e94 | ||
|
|
ef25d100e1 | ||
|
|
17f82c8972 | ||
|
|
05c937743c | ||
|
|
bf2e7ff130 | ||
|
|
1b30ee606f | ||
|
|
3235907266 | ||
|
|
99c7e417d6 | ||
|
|
d81906d348 | ||
|
|
296872d2e4 | ||
|
|
7cd02b4916 | ||
|
|
0e217f48be | ||
|
|
61fdf4b6c7 | ||
|
|
91dbf1f01a | ||
|
|
d52aac76c0 | ||
|
|
6102e441d6 | ||
|
|
1a5fec39c0 | ||
|
|
efa5091b98 | ||
|
|
be4386658a | ||
|
|
0cddce7a37 | ||
|
|
a86d13632e | ||
|
|
d71682a3f7 | ||
|
|
c901ace21a | ||
|
|
418398a870 | ||
|
|
baca57062e | ||
|
|
52df8e6e8b | ||
|
|
b51747378a | ||
|
|
424a6b0428 | ||
|
|
4549223d6d | ||
|
|
91dd3468d5 | ||
|
|
692047e4c8 | ||
|
|
9cb981f068 |
19
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
19
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
@@ -1,17 +1,28 @@
|
||||
name: Bug report
|
||||
description: Create a new bug report
|
||||
description: 'Create a new bug report.'
|
||||
title: '[Bug]: '
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >-
|
||||
# 💎 Bounty program (with
|
||||
[algora.io](https://console.algora.io/org/coollabsio/bounties/new))
|
||||
|
||||
|
||||
If you would like to prioritize the issue resolution, you can add bounty
|
||||
to this issue.
|
||||
|
||||
|
||||
Click [here](https://console.algora.io/org/coollabsio/bounties/new) to
|
||||
get started.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: A clear and concise description of the problem
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Minimal Reproduction (if possible, example repository)
|
||||
description: Please provide a step by step guide to reproduce the issue
|
||||
description: Please provide a step by step guide to reproduce the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
4
.github/workflows/development-build.yml
vendored
4
.github/workflows/development-build.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/prod-ssu/Dockerfile
|
||||
file: docker/prod/Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/prod-ssu/Dockerfile
|
||||
file: docker/prod/Dockerfile
|
||||
platforms: linux/aarch64
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
|
||||
|
||||
4
.github/workflows/production-build.yml
vendored
4
.github/workflows/production-build.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/prod-ssu/Dockerfile
|
||||
file: docker/prod/Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/prod-ssu/Dockerfile
|
||||
file: docker/prod/Dockerfile
|
||||
platforms: linux/aarch64
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
|
||||
|
||||
@@ -3,7 +3,7 @@ tasks:
|
||||
# Fix because of https://github.com/gitpod-io/gitpod/issues/16614
|
||||
before: sudo curl -o /usr/local/bin/docker-compose -fsSL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-$(uname -m)
|
||||
init: |
|
||||
cp .env.example .env &&
|
||||
cp .env.development.example .env &&
|
||||
sed -i "s#APP_URL=http://localhost#APP_URL=$(gp url 8000)#g" .env
|
||||
sed -i "s#USERID=#USERID=33333#g" .env
|
||||
sed -i "s#GROUPID=#GROUPID=33333#g" .env
|
||||
@@ -20,7 +20,7 @@ tasks:
|
||||
echo "Waiting for Sail environment to boot up."
|
||||
gp sync-await spin-is-ready
|
||||
./vendor/bin/spin exec vite npm install
|
||||
./vendor/bin/spin exec vite npm run dev
|
||||
./vendor/bin/spin exec vite npm run dev -- --host
|
||||
|
||||
- name: Laravel Queue Worker, listening to code changes
|
||||
command: |
|
||||
|
||||
11
README.md
11
README.md
@@ -1,3 +1,6 @@
|
||||
[](https://console.algora.io/org/coollabsio/bounties/new)
|
||||
[](https://console.algora.io/org/coollabsio/bounties?status=open)
|
||||
[](https://console.algora.io/org/coollabsio/bounties?status=completed)
|
||||
# About the Project
|
||||
|
||||
Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc.
|
||||
@@ -34,9 +37,16 @@ Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
|
||||
|
||||
## Github Sponsors ($40+)
|
||||
<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://serpapi.com/?utm_source=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></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://www.quantcdn.io/?utm_source=coolify.io"><img src="https://github.com/quantcdn.png" width="60px" alt="QuantCDN"/></a>
|
||||
<a href="https://www.runpod.io/?utm_source=coolify.io">
|
||||
<svg style="width:60px;height:60px;background:#fff;" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 200 200"><g><path d="M74.5 51.1c-25.4 14.9-27 16-29.6 20.2-1.8 3-1.9 5.3-1.9 32.3 0 21.7.3 29.4 1.3 30.6 1.9 2.5 46.7 27.9 48.5 27.6 1.5-.3 1.7-3.1 2-27.7.2-21.9 0-27.8-1.1-29.5-.8-1.2-9.9-6.8-20.2-12.6-10.3-5.8-19.4-11.5-20.2-12.7-1.8-2.6-.9-5.9 1.8-7.4 1.6-.8 6.3 0 21.8 4C87.8 78.7 98 81 99.6 81c4.4 0 49.9-25.9 49.9-28.4 0-1.6-3.4-2.8-24-8.2-13.2-3.5-25.1-6.3-26.5-6.3-1.4.1-12.4 5.9-24.5 13z"></path><path d="m137.2 68.1-3.3 2.1 6.3 3.7c3.5 2 6.3 4.3 6.3 5.1 0 .9-8 6.1-19.4 12.6-10.6 6-20 11.9-20.7 12.9-1.2 1.6-1.4 7.2-1.2 29.4.3 24.8.5 27.6 2 27.9 1.8.3 46.6-25.1 48.6-27.6.9-1.2 1.2-8.8 1.2-30.2s-.3-29-1.2-30.2c-1.6-1.9-12.1-7.8-13.9-7.8-.8 0-2.9 1-4.7 2.1z"></path></g></svg></a>
|
||||
<a href="https://lightspeed.run/?utm_source=coolify.io"><img src="https://github.com/lightspeedrun.png" width="60px" alt="Lightspeed.run"/></a>
|
||||
<a href="https://www.flint.sh/en/home?utm_source=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></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://x.com/mrsmith9ja?utm_source=coolify.io"><img width="60px" alt="Thompson Edolo" src="https://github.com/verygreenboi.png"/></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>
|
||||
@@ -62,6 +72,7 @@ Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
|
||||
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
||||
|
||||
|
||||
## Individuals
|
||||
|
||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
||||
|
||||
@@ -60,7 +60,7 @@ class RunRemoteProcess
|
||||
$decoded = json_decode(
|
||||
data_get($activity, 'description'),
|
||||
associative: true,
|
||||
flags: JSON_THROW_ON_ERROR
|
||||
flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE
|
||||
);
|
||||
} catch (\JsonException $exception) {
|
||||
return '';
|
||||
@@ -164,8 +164,7 @@ class RunRemoteProcess
|
||||
|
||||
public function encodeOutput($type, $output)
|
||||
{
|
||||
$outputStack = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR);
|
||||
|
||||
$outputStack = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
|
||||
$outputStack[] = [
|
||||
'type' => $type,
|
||||
'output' => $output,
|
||||
@@ -174,12 +173,12 @@ class RunRemoteProcess
|
||||
'order' => $this->getLatestCounter(),
|
||||
];
|
||||
|
||||
return json_encode($outputStack, flags: JSON_THROW_ON_ERROR);
|
||||
return json_encode($outputStack, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
protected function getLatestCounter(): int
|
||||
{
|
||||
$description = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR);
|
||||
$description = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
|
||||
if ($description === null || count($description) === 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ class StartClickhouse
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||
$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,
|
||||
@@ -94,6 +94,11 @@ class StartClickhouse
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($persistent_file_volumes) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||
return "$item->fs_path:$item->mount_path";
|
||||
})->toArray();
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,6 @@ class StartDatabaseProxy
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
EOF;
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$proxyContainerName => [
|
||||
'build' => [
|
||||
|
||||
@@ -32,11 +32,11 @@ class StartDragonfly
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||
$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,
|
||||
@@ -95,6 +95,11 @@ class StartDragonfly
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($persistent_file_volumes) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||
return "$item->fs_path:$item->mount_path";
|
||||
})->toArray();
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ class StartKeydb
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||
$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,
|
||||
@@ -93,10 +93,15 @@ class StartKeydb
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($persistent_file_volumes) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||
return "$item->fs_path:$item->mount_path";
|
||||
})->toArray();
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->keydb_conf)) {
|
||||
if (!is_null($this->database->keydb_conf) || !empty($this->database->keydb_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/keydb.conf',
|
||||
@@ -162,7 +167,7 @@ class StartKeydb
|
||||
}
|
||||
private function add_custom_keydb()
|
||||
{
|
||||
if (is_null($this->database->keydb_conf)) {
|
||||
if (is_null($this->database->keydb_conf) || empty($this->database->keydb_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'keydb.conf';
|
||||
|
||||
@@ -28,11 +28,11 @@ class StartMariadb
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_mysql();
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
@@ -87,10 +87,15 @@ class StartMariadb
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($persistent_file_volumes) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||
return "$item->fs_path:$item->mount_path";
|
||||
})->toArray();
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->mariadb_conf)) {
|
||||
if (!is_null($this->database->mariadb_conf) || !empty($this->database->mariadb_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||
@@ -165,7 +170,7 @@ class StartMariadb
|
||||
}
|
||||
private function add_custom_mysql()
|
||||
{
|
||||
if (is_null($this->database->mariadb_conf)) {
|
||||
if (is_null($this->database->mariadb_conf) || empty($this->database->mariadb_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'custom-config.cnf';
|
||||
|
||||
@@ -30,12 +30,12 @@ class StartMongodb
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_mongo_conf();
|
||||
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
@@ -51,8 +51,9 @@ class StartMongodb
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => [
|
||||
'CMD-SHELL',
|
||||
'mongosh --eval "printjson(db.runCommand(\"ping\"))"'
|
||||
"CMD",
|
||||
"echo",
|
||||
"ok"
|
||||
],
|
||||
'interval' => '5s',
|
||||
'timeout' => '5s',
|
||||
@@ -94,10 +95,15 @@ class StartMongodb
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($persistent_file_volumes) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||
return "$item->fs_path:$item->mount_path";
|
||||
})->toArray();
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->mongo_conf)) {
|
||||
if (!is_null($this->database->mongo_conf) || !empty($this->database->mongo_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/mongod.conf',
|
||||
@@ -178,7 +184,7 @@ class StartMongodb
|
||||
}
|
||||
private function add_custom_mongo_conf()
|
||||
{
|
||||
if (is_null($this->database->mongo_conf)) {
|
||||
if (is_null($this->database->mongo_conf) || empty($this->database->mongo_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'mongod.conf';
|
||||
|
||||
@@ -28,11 +28,11 @@ class StartMysql
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_mysql();
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
@@ -87,10 +87,15 @@ class StartMysql
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($persistent_file_volumes) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||
return "$item->fs_path:$item->mount_path";
|
||||
})->toArray();
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->mysql_conf)) {
|
||||
if (!is_null($this->database->mysql_conf) || !empty($this->database->mysql_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||
@@ -165,7 +170,7 @@ class StartMysql
|
||||
}
|
||||
private function add_custom_mysql()
|
||||
{
|
||||
if (is_null($this->database->mysql_conf)) {
|
||||
if (is_null($this->database->mysql_conf) || empty($this->database->mysql_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'custom-config.cnf';
|
||||
|
||||
@@ -29,13 +29,13 @@ class StartPostgresql
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->generate_init_scripts();
|
||||
$this->add_custom_conf();
|
||||
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
@@ -78,7 +78,6 @@ class StartPostgresql
|
||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||
}
|
||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||
ray('Log Drain Enabled');
|
||||
$docker_compose['services'][$container_name]['logging'] = [
|
||||
'driver' => 'fluentd',
|
||||
'options' => [
|
||||
@@ -94,6 +93,11 @@ class StartPostgresql
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($persistent_file_volumes) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||
return "$item->fs_path:$item->mount_path";
|
||||
})->toArray();
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
@@ -107,7 +111,7 @@ class StartPostgresql
|
||||
];
|
||||
}
|
||||
}
|
||||
if (!is_null($this->database->postgres_conf)) {
|
||||
if (!is_null($this->database->postgres_conf) && !empty($this->database->postgres_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/custom-postgres.conf',
|
||||
@@ -165,8 +169,6 @@ class StartPostgresql
|
||||
private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
ray('Generate Environment Variables')->green();
|
||||
ray($this->database->runtime_environment_variables)->green();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
@@ -203,11 +205,16 @@ class StartPostgresql
|
||||
}
|
||||
private function add_custom_conf()
|
||||
{
|
||||
if (is_null($this->database->postgres_conf)) {
|
||||
if (is_null($this->database->postgres_conf) || empty($this->database->postgres_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'custom-postgres.conf';
|
||||
$content = $this->database->postgres_conf;
|
||||
if (!str($content)->contains('listen_addresses')) {
|
||||
$content .= "\nlisten_addresses = '*'";
|
||||
$this->database->postgres_conf = $content;
|
||||
$this->database->save();
|
||||
}
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ class StartRedis
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_redis();
|
||||
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
@@ -97,10 +97,15 @@ class StartRedis
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($persistent_file_volumes) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||
return "$item->fs_path:$item->mount_path";
|
||||
})->toArray();
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->redis_conf)) {
|
||||
if (!is_null($this->database->redis_conf) || !empty($this->database->redis_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/redis.conf',
|
||||
@@ -166,7 +171,7 @@ class StartRedis
|
||||
}
|
||||
private function add_custom_redis()
|
||||
{
|
||||
if (is_null($this->database->redis_conf)) {
|
||||
if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'redis.conf';
|
||||
|
||||
681
app/Actions/Docker/GetContainersStatus.php
Normal file
681
app/Actions/Docker/GetContainersStatus.php
Normal file
@@ -0,0 +1,681 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Docker;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Actions\Shared\ComplexStatusCheck;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Notifications\Container\ContainerRestarted;
|
||||
use App\Notifications\Container\ContainerStopped;
|
||||
use Illuminate\Support\Arr;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class GetContainersStatus
|
||||
{
|
||||
use AsAction;
|
||||
public $applications;
|
||||
public $server;
|
||||
|
||||
public function handle(Server $server)
|
||||
{
|
||||
// if (isDev()) {
|
||||
// $server = Server::find(0);
|
||||
// }
|
||||
$this->server = $server;
|
||||
if (!$this->server->isFunctional()) {
|
||||
return 'Server is not ready.';
|
||||
};
|
||||
$this->applications = $this->server->applications();
|
||||
$skip_these_applications = collect([]);
|
||||
foreach ($this->applications as $application) {
|
||||
if ($application->additional_servers->count() > 0) {
|
||||
$skip_these_applications->push($application);
|
||||
ComplexStatusCheck::run($application);
|
||||
$this->applications = $this->applications->filter(function ($value, $key) use ($application) {
|
||||
return $value->id !== $application->id;
|
||||
});
|
||||
}
|
||||
}
|
||||
$this->applications = $this->applications->filter(function ($value, $key) use ($skip_these_applications) {
|
||||
return !$skip_these_applications->pluck('id')->contains($value->id);
|
||||
});
|
||||
$this->old_way();
|
||||
// if ($this->server->isSwarm()) {
|
||||
// $this->old_way();
|
||||
// } else {
|
||||
// if (!$this->server->is_metrics_enabled) {
|
||||
// $this->old_way();
|
||||
// return;
|
||||
// }
|
||||
// $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this->server, false);
|
||||
// $sentinel_found = json_decode($sentinel_found, true);
|
||||
// $status = data_get($sentinel_found, '0.State.Status', 'exited');
|
||||
// if ($status === 'running') {
|
||||
// ray('Checking with Sentinel');
|
||||
// $this->sentinel();
|
||||
// } else {
|
||||
// ray('Checking the Old way');
|
||||
// $this->old_way();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private function sentinel()
|
||||
{
|
||||
try {
|
||||
$containers = $this->server->getContainers();
|
||||
if ($containers->count() === 0) {
|
||||
return;
|
||||
}
|
||||
$databases = $this->server->databases();
|
||||
$services = $this->server->services()->get();
|
||||
$previews = $this->server->previews();
|
||||
$foundApplications = [];
|
||||
$foundApplicationPreviews = [];
|
||||
$foundDatabases = [];
|
||||
$foundServices = [];
|
||||
|
||||
foreach ($containers as $container) {
|
||||
$labels = Arr::undot(data_get($container, 'labels'));
|
||||
$containerStatus = data_get($container, 'state');
|
||||
$containerHealth = data_get($container, 'health_status', 'unhealthy');
|
||||
$containerStatus = "$containerStatus ($containerHealth)";
|
||||
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||
if ($applicationId) {
|
||||
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||
if ($pullRequestId) {
|
||||
if (str($applicationId)->contains('-')) {
|
||||
$applicationId = str($applicationId)->before('-');
|
||||
}
|
||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||
if ($preview) {
|
||||
$foundApplicationPreviews[] = $preview->id;
|
||||
$statusFromDb = $preview->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$preview->update(['status' => $containerStatus]);
|
||||
}
|
||||
} else {
|
||||
//Notify user that this container should not be there.
|
||||
}
|
||||
} else {
|
||||
$application = $this->applications->where('id', $applicationId)->first();
|
||||
if ($application) {
|
||||
$foundApplications[] = $application->id;
|
||||
$statusFromDb = $application->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$application->update(['status' => $containerStatus]);
|
||||
}
|
||||
} else {
|
||||
//Notify user that this container should not be there.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||
$type = data_get($labels, 'coolify.type');
|
||||
if ($uuid) {
|
||||
if ($type === 'service') {
|
||||
$database_id = data_get($labels, 'coolify.service.subId');
|
||||
if ($database_id) {
|
||||
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
||||
if ($service_db) {
|
||||
$uuid = $service_db->service->uuid;
|
||||
$isPublic = data_get($service_db, 'is_public');
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
// TODO: fix this with sentinel
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'name') === "$uuid-proxy";
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($service_db);
|
||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$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()) {
|
||||
// TODO: fix this with sentinel
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'name') === "$uuid-proxy";
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($database);
|
||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Notify user that this container should not be there.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data_get($container, 'name') === 'coolify-db') {
|
||||
$foundDatabases[] = 0;
|
||||
}
|
||||
}
|
||||
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
||||
if ($serviceLabelId) {
|
||||
$subType = data_get($labels, 'coolify.service.subType');
|
||||
$subId = data_get($labels, 'coolify.service.subId');
|
||||
$service = $services->where('id', $serviceLabelId)->first();
|
||||
if (!$service) {
|
||||
continue;
|
||||
}
|
||||
if ($subType === 'application') {
|
||||
$service = $service->applications()->where('id', $subId)->first();
|
||||
} else {
|
||||
$service = $service->databases()->where('id', $subId)->first();
|
||||
}
|
||||
if ($service) {
|
||||
$foundServices[] = "$service->id-$service->name";
|
||||
$statusFromDb = $service->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
// ray('Updating status: ' . $containerStatus);
|
||||
$service->update(['status' => $containerStatus]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$exitedServices = collect([]);
|
||||
foreach ($services as $service) {
|
||||
$apps = $service->applications()->get();
|
||||
$dbs = $service->databases()->get();
|
||||
foreach ($apps as $app) {
|
||||
if (in_array("$app->id-$app->name", $foundServices)) {
|
||||
continue;
|
||||
} else {
|
||||
$exitedServices->push($app);
|
||||
}
|
||||
}
|
||||
foreach ($dbs as $db) {
|
||||
if (in_array("$db->id-$db->name", $foundServices)) {
|
||||
continue;
|
||||
} else {
|
||||
$exitedServices->push($db);
|
||||
}
|
||||
}
|
||||
}
|
||||
$exitedServices = $exitedServices->unique('id');
|
||||
foreach ($exitedServices as $exitedService) {
|
||||
if (str($exitedService->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$name = data_get($exitedService, 'name');
|
||||
$fqdn = data_get($exitedService, 'fqdn');
|
||||
if ($name) {
|
||||
if ($fqdn) {
|
||||
$containerName = "$name, available at $fqdn";
|
||||
} else {
|
||||
$containerName = $name;
|
||||
}
|
||||
} else {
|
||||
if ($fqdn) {
|
||||
$containerName = $fqdn;
|
||||
} else {
|
||||
$containerName = null;
|
||||
}
|
||||
}
|
||||
$projectUuid = data_get($service, 'environment.project.uuid');
|
||||
$serviceUuid = data_get($service, 'uuid');
|
||||
$environmentName = data_get($service, 'environment.name');
|
||||
|
||||
if ($projectUuid && $serviceUuid && $environmentName) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
$exitedService->update(['status' => 'exited']);
|
||||
}
|
||||
|
||||
$notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
|
||||
foreach ($notRunningApplications as $applicationId) {
|
||||
$application = $this->applications->where('id', $applicationId)->first();
|
||||
if (str($application->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$application->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($application, 'name');
|
||||
$fqdn = data_get($application, 'fqdn');
|
||||
|
||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||
|
||||
$projectUuid = data_get($application, 'environment.project.uuid');
|
||||
$applicationUuid = data_get($application, 'uuid');
|
||||
$environment = data_get($application, 'environment.name');
|
||||
|
||||
if ($projectUuid && $applicationUuid && $environment) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
|
||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
||||
foreach ($notRunningApplicationPreviews as $previewId) {
|
||||
$preview = $previews->where('id', $previewId)->first();
|
||||
if (str($preview->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$preview->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($preview, 'name');
|
||||
$fqdn = data_get($preview, 'fqdn');
|
||||
|
||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||
|
||||
$projectUuid = data_get($preview, 'application.environment.project.uuid');
|
||||
$environmentName = data_get($preview, 'application.environment.name');
|
||||
$applicationUuid = data_get($preview, 'application.uuid');
|
||||
|
||||
if ($projectUuid && $applicationUuid && $environmentName) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
|
||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
||||
foreach ($notRunningDatabases as $database) {
|
||||
$database = $databases->where('id', $database)->first();
|
||||
if (str($database->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$database->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($database, 'name');
|
||||
$fqdn = data_get($database, 'fqdn');
|
||||
|
||||
$containerName = $name;
|
||||
|
||||
$projectUuid = data_get($database, 'environment.project.uuid');
|
||||
$environmentName = data_get($database, 'environment.name');
|
||||
$databaseUuid = data_get($database, 'uuid');
|
||||
|
||||
if ($projectUuid && $databaseUuid && $environmentName) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
|
||||
// Check if proxy is running
|
||||
$this->server->proxyType();
|
||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||
if ($this->server->isSwarm()) {
|
||||
// TODO: fix this with sentinel
|
||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||
} else {
|
||||
return data_get($value, 'name') === 'coolify-proxy';
|
||||
}
|
||||
})->first();
|
||||
if (!$foundProxyContainer) {
|
||||
try {
|
||||
$shouldStart = CheckProxy::run($this->server);
|
||||
if ($shouldStart) {
|
||||
StartProxy::run($this->server, false);
|
||||
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
}
|
||||
} else {
|
||||
$this->server->proxy->status = data_get($foundProxyContainer, 'state');
|
||||
$this->server->save();
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
private function old_way()
|
||||
{
|
||||
if ($this->server->isSwarm()) {
|
||||
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
||||
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
||||
} else {
|
||||
// Precheck for containers
|
||||
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
||||
if (!$containers) {
|
||||
return;
|
||||
}
|
||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
||||
$containerReplicates = null;
|
||||
}
|
||||
if (is_null($containers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
if ($containerReplicates) {
|
||||
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
|
||||
foreach ($containerReplicates as $containerReplica) {
|
||||
$name = data_get($containerReplica, 'Name');
|
||||
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
||||
if (data_get($container, 'Spec.Name') === $name) {
|
||||
$replicas = data_get($containerReplica, 'Replicas');
|
||||
$running = str($replicas)->explode('/')[0];
|
||||
$total = str($replicas)->explode('/')[1];
|
||||
if ($running === $total) {
|
||||
data_set($container, 'State.Status', 'running');
|
||||
data_set($container, 'State.Health.Status', 'healthy');
|
||||
} else {
|
||||
data_set($container, 'State.Status', 'starting');
|
||||
data_set($container, 'State.Health.Status', 'unhealthy');
|
||||
}
|
||||
}
|
||||
return $container;
|
||||
});
|
||||
}
|
||||
}
|
||||
$databases = $this->server->databases();
|
||||
$services = $this->server->services()->get();
|
||||
$previews = $this->server->previews();
|
||||
$foundApplications = [];
|
||||
$foundApplicationPreviews = [];
|
||||
$foundDatabases = [];
|
||||
$foundServices = [];
|
||||
|
||||
foreach ($containers as $container) {
|
||||
if ($this->server->isSwarm()) {
|
||||
$labels = data_get($container, 'Spec.Labels');
|
||||
$uuid = data_get($labels, 'coolify.name');
|
||||
} else {
|
||||
$labels = data_get($container, 'Config.Labels');
|
||||
}
|
||||
$containerStatus = data_get($container, 'State.Status');
|
||||
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||
$containerStatus = "$containerStatus ($containerHealth)";
|
||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||
if ($applicationId) {
|
||||
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||
if ($pullRequestId) {
|
||||
if (str($applicationId)->contains('-')) {
|
||||
$applicationId = str($applicationId)->before('-');
|
||||
}
|
||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||
if ($preview) {
|
||||
$foundApplicationPreviews[] = $preview->id;
|
||||
$statusFromDb = $preview->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$preview->update(['status' => $containerStatus]);
|
||||
}
|
||||
} else {
|
||||
//Notify user that this container should not be there.
|
||||
}
|
||||
} else {
|
||||
$application = $this->applications->where('id', $applicationId)->first();
|
||||
if ($application) {
|
||||
$foundApplications[] = $application->id;
|
||||
$statusFromDb = $application->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$application->update(['status' => $containerStatus]);
|
||||
}
|
||||
} else {
|
||||
//Notify user that this container should not be there.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||
$type = data_get($labels, 'coolify.type');
|
||||
|
||||
if ($uuid) {
|
||||
if ($type === 'service') {
|
||||
$database_id = data_get($labels, 'coolify.service.subId');
|
||||
if ($database_id) {
|
||||
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
||||
if ($service_db) {
|
||||
$uuid = data_get($service_db, 'service.uuid');
|
||||
if ($uuid) {
|
||||
$isPublic = data_get($service_db, 'is_public');
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($service_db);
|
||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$database = $databases->where('uuid', $uuid)->first();
|
||||
if ($database) {
|
||||
$isPublic = data_get($database, 'is_public');
|
||||
$foundDatabases[] = $database->id;
|
||||
$statusFromDb = $database->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$database->update(['status' => $containerStatus]);
|
||||
}
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($database);
|
||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Notify user that this container should not be there.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data_get($container, 'Name') === '/coolify-db') {
|
||||
$foundDatabases[] = 0;
|
||||
}
|
||||
}
|
||||
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
||||
if ($serviceLabelId) {
|
||||
$subType = data_get($labels, 'coolify.service.subType');
|
||||
$subId = data_get($labels, 'coolify.service.subId');
|
||||
$service = $services->where('id', $serviceLabelId)->first();
|
||||
if (!$service) {
|
||||
continue;
|
||||
}
|
||||
if ($subType === 'application') {
|
||||
$service = $service->applications()->where('id', $subId)->first();
|
||||
} else {
|
||||
$service = $service->databases()->where('id', $subId)->first();
|
||||
}
|
||||
if ($service) {
|
||||
$foundServices[] = "$service->id-$service->name";
|
||||
$statusFromDb = $service->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
// ray('Updating status: ' . $containerStatus);
|
||||
$service->update(['status' => $containerStatus]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$exitedServices = collect([]);
|
||||
foreach ($services as $service) {
|
||||
$apps = $service->applications()->get();
|
||||
$dbs = $service->databases()->get();
|
||||
foreach ($apps as $app) {
|
||||
if (in_array("$app->id-$app->name", $foundServices)) {
|
||||
continue;
|
||||
} else {
|
||||
$exitedServices->push($app);
|
||||
}
|
||||
}
|
||||
foreach ($dbs as $db) {
|
||||
if (in_array("$db->id-$db->name", $foundServices)) {
|
||||
continue;
|
||||
} else {
|
||||
$exitedServices->push($db);
|
||||
}
|
||||
}
|
||||
}
|
||||
$exitedServices = $exitedServices->unique('id');
|
||||
foreach ($exitedServices as $exitedService) {
|
||||
if (str($exitedService->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$name = data_get($exitedService, 'name');
|
||||
$fqdn = data_get($exitedService, 'fqdn');
|
||||
if ($name) {
|
||||
if ($fqdn) {
|
||||
$containerName = "$name, available at $fqdn";
|
||||
} else {
|
||||
$containerName = $name;
|
||||
}
|
||||
} else {
|
||||
if ($fqdn) {
|
||||
$containerName = $fqdn;
|
||||
} else {
|
||||
$containerName = null;
|
||||
}
|
||||
}
|
||||
$projectUuid = data_get($service, 'environment.project.uuid');
|
||||
$serviceUuid = data_get($service, 'uuid');
|
||||
$environmentName = data_get($service, 'environment.name');
|
||||
|
||||
if ($projectUuid && $serviceUuid && $environmentName) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
$exitedService->update(['status' => 'exited']);
|
||||
}
|
||||
|
||||
$notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
|
||||
foreach ($notRunningApplications as $applicationId) {
|
||||
$application = $this->applications->where('id', $applicationId)->first();
|
||||
if (str($application->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$application->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($application, 'name');
|
||||
$fqdn = data_get($application, 'fqdn');
|
||||
|
||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||
|
||||
$projectUuid = data_get($application, 'environment.project.uuid');
|
||||
$applicationUuid = data_get($application, 'uuid');
|
||||
$environment = data_get($application, 'environment.name');
|
||||
|
||||
if ($projectUuid && $applicationUuid && $environment) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
|
||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
||||
foreach ($notRunningApplicationPreviews as $previewId) {
|
||||
$preview = $previews->where('id', $previewId)->first();
|
||||
if (str($preview->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$preview->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($preview, 'name');
|
||||
$fqdn = data_get($preview, 'fqdn');
|
||||
|
||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||
|
||||
$projectUuid = data_get($preview, 'application.environment.project.uuid');
|
||||
$environmentName = data_get($preview, 'application.environment.name');
|
||||
$applicationUuid = data_get($preview, 'application.uuid');
|
||||
|
||||
if ($projectUuid && $applicationUuid && $environmentName) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
|
||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
||||
foreach ($notRunningDatabases as $database) {
|
||||
$database = $databases->where('id', $database)->first();
|
||||
if (str($database->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$database->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($database, 'name');
|
||||
$fqdn = data_get($database, 'fqdn');
|
||||
|
||||
$containerName = $name;
|
||||
|
||||
$projectUuid = data_get($database, 'environment.project.uuid');
|
||||
$environmentName = data_get($database, 'environment.name');
|
||||
$databaseUuid = data_get($database, 'uuid');
|
||||
|
||||
if ($projectUuid && $databaseUuid && $environmentName) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
|
||||
// Check if proxy is running
|
||||
$this->server->proxyType();
|
||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||
} else {
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
}
|
||||
})->first();
|
||||
if (!$foundProxyContainer) {
|
||||
try {
|
||||
$shouldStart = CheckProxy::run($this->server);
|
||||
if ($shouldStart) {
|
||||
StartProxy::run($this->server, false);
|
||||
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
}
|
||||
} else {
|
||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||
$this->server->save();
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,18 @@ class CheckProxy
|
||||
use AsAction;
|
||||
public function handle(Server $server, $fromUI = false)
|
||||
{
|
||||
if ($server->proxyType() === 'NONE') {
|
||||
if (!$server->isFunctional()) {
|
||||
return false;
|
||||
}
|
||||
if ($server->isBuildServer()) {
|
||||
if ($server->proxy) {
|
||||
$server->proxy = null;
|
||||
$server->save();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$proxyType = $server->proxyType();
|
||||
if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop) {
|
||||
return false;
|
||||
}
|
||||
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
|
||||
|
||||
@@ -15,7 +15,7 @@ class StartProxy
|
||||
{
|
||||
try {
|
||||
$proxyType = $server->proxyType();
|
||||
if (is_null($proxyType) || $proxyType === 'NONE') {
|
||||
if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop || $server->isBuildServer()) {
|
||||
return 'OK';
|
||||
}
|
||||
$commands = collect([]);
|
||||
|
||||
22
app/Actions/Server/StartSentinel.php
Normal file
22
app/Actions/Server/StartSentinel.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use App\Models\Server;
|
||||
|
||||
class StartSentinel
|
||||
{
|
||||
use AsAction;
|
||||
public function handle(Server $server, $version = 'latest', bool $restart = false)
|
||||
{
|
||||
if ($restart) {
|
||||
instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
|
||||
}
|
||||
instant_remote_process([
|
||||
"docker run --rm --pull always -d -e \"SCHEDULER=true\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
|
||||
"chown -R 9999:root /data/coolify/metrics /data/coolify/logs",
|
||||
"chmod -R 700 /data/coolify/metrics /data/coolify/logs"
|
||||
], $server, false);
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,10 @@ class UpdateCoolify
|
||||
public ?Server $server = null;
|
||||
public ?string $latestVersion = null;
|
||||
public ?string $currentVersion = null;
|
||||
public bool $async = false;
|
||||
|
||||
public function handle(bool $force = false, bool $async = false)
|
||||
public function handle($manual_update = false)
|
||||
{
|
||||
try {
|
||||
$this->async = $async;
|
||||
$settings = InstanceSettings::get();
|
||||
ray('Running InstanceAutoUpdateJob');
|
||||
$this->server = Server::find(0);
|
||||
@@ -27,28 +25,19 @@ class UpdateCoolify
|
||||
CleanupDocker::run($this->server, false);
|
||||
$this->latestVersion = get_latest_version_of_coolify();
|
||||
$this->currentVersion = config('version');
|
||||
if ($settings->next_channel) {
|
||||
ray('next channel enabled');
|
||||
$this->latestVersion = 'next';
|
||||
}
|
||||
if ($force) {
|
||||
$this->update();
|
||||
} else {
|
||||
if (!$manual_update) {
|
||||
if (!$settings->is_auto_update_enabled) {
|
||||
return 'Auto update is disabled';
|
||||
return;
|
||||
}
|
||||
if ($this->latestVersion === $this->currentVersion) {
|
||||
return 'Already on latest version';
|
||||
return;
|
||||
}
|
||||
if (version_compare($this->latestVersion, $this->currentVersion, '<')) {
|
||||
return 'Latest version is lower than current version?!';
|
||||
return;
|
||||
}
|
||||
$this->update();
|
||||
}
|
||||
$this->update();
|
||||
} catch (\Throwable $e) {
|
||||
ray('InstanceAutoUpdateJob failed');
|
||||
ray($e->getMessage());
|
||||
send_internal_notification('InstanceAutoUpdateJob failed: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
@@ -56,34 +45,15 @@ class UpdateCoolify
|
||||
private function update()
|
||||
{
|
||||
if (isDev()) {
|
||||
ray("Running update on local docker container. Updating to $this->latestVersion");
|
||||
if ($this->async) {
|
||||
ray('Running async update');
|
||||
remote_process([
|
||||
"sleep 10"
|
||||
], $this->server);
|
||||
} else {
|
||||
instant_remote_process([
|
||||
"sleep 10"
|
||||
], $this->server);
|
||||
}
|
||||
ray('Update done');
|
||||
return;
|
||||
} else {
|
||||
ray('Running update on production server');
|
||||
if ($this->async) {
|
||||
remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||
], $this->server);
|
||||
} else {
|
||||
instant_remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||
], $this->server);
|
||||
}
|
||||
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
|
||||
remote_process([
|
||||
"sleep 10"
|
||||
], $this->server);
|
||||
return;
|
||||
}
|
||||
remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||
], $this->server);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
54
app/Console/Commands/AdminRemoveUser.php
Normal file
54
app/Console/Commands/AdminRemoveUser.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class AdminRemoveUser extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'admin:remove-user {email}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove User from database';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$email = $this->argument('email');
|
||||
$confirm = $this->confirm('Are you sure you want to remove user with email: ' . $email . '?');
|
||||
if (!$confirm) {
|
||||
$this->info('User removal cancelled.');
|
||||
return;
|
||||
}
|
||||
$this->info("Removing user with email: $email");
|
||||
$user = User::whereEmail($email)->firstOrFail();
|
||||
$teams = $user->teams;
|
||||
foreach ($teams as $team) {
|
||||
if ($team->members->count() > 1) {
|
||||
$this->error('User is a member of a team with more than one member. Please remove user from team first.');
|
||||
return;
|
||||
}
|
||||
$team->delete();
|
||||
}
|
||||
$user->delete();
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Failed to remove user.');
|
||||
$this->error($e->getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Cloud extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'cloud:unused-servers';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Get Unused Servers from Cloud';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended',true)->each(function($server){
|
||||
$this->info($server->name);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ class RootResetPassword extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//
|
||||
$this->info('You are about to reset the root password.');
|
||||
$password = password('Give me a new password for root user: ');
|
||||
$passwordAgain = password('Again');
|
||||
|
||||
@@ -26,7 +26,6 @@ class ServicesGenerate extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// ray()->clearAll();
|
||||
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
||||
$files = array_filter($files, function ($file) {
|
||||
return strpos($file, '.yaml') !== false;
|
||||
@@ -40,7 +39,7 @@ class ServicesGenerate extends Command
|
||||
$serviceTemplatesJson[$name] = $parsed;
|
||||
}
|
||||
}
|
||||
$serviceTemplatesJson = json_encode($serviceTemplatesJson, JSON_PRETTY_PRINT);
|
||||
$serviceTemplatesJson = json_encode($serviceTemplatesJson);
|
||||
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson);
|
||||
}
|
||||
|
||||
@@ -63,6 +62,7 @@ class ServicesGenerate extends Command
|
||||
$documentation = collect(preg_grep('/^# documentation:/', explode("\n", $content)))->values();
|
||||
if ($documentation->count() > 0) {
|
||||
$documentation = str($documentation[0])->after('# documentation:')->trim()->value();
|
||||
$documentation = str($documentation)->append('?utm_source=coolify.io');
|
||||
} else {
|
||||
$documentation = 'https://coolify.io/docs';
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ use App\Jobs\CheckLogDrainContainerJob;
|
||||
use App\Jobs\CleanupInstanceStuffsJob;
|
||||
use App\Jobs\DatabaseBackupJob;
|
||||
use App\Jobs\ScheduledTaskJob;
|
||||
use App\Jobs\InstanceAutoUpdateJob;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Jobs\PullCoolifyImageJob;
|
||||
use App\Jobs\PullHelperImageJob;
|
||||
use App\Jobs\PullSentinelImageJob;
|
||||
use App\Jobs\PullTemplatesFromCDN;
|
||||
use App\Jobs\ServerStatusJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledTask;
|
||||
use App\Models\Server;
|
||||
@@ -20,55 +21,59 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
private $all_servers;
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
$this->all_servers = Server::all();
|
||||
if (isDev()) {
|
||||
// Instance Jobs
|
||||
$schedule->command('horizon:snapshot')->everyMinute();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
||||
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||
$schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
|
||||
// Server Jobs
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
// $this->pull_helper_image($schedule);
|
||||
$this->check_scheduled_tasks($schedule);
|
||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||
} else {
|
||||
// Instance Jobs
|
||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||
$schedule->command('cleanup:unreachable-servers')->daily();
|
||||
|
||||
$schedule->job(new PullCoolifyImageJob)->everyTenMinutes()->onOneServer();
|
||||
$schedule->job(new PullTemplatesFromCDN)->everyThirtyMinutes()->onOneServer();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||
|
||||
// Server Jobs
|
||||
$this->instance_auto_update($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
$this->pull_helper_image($schedule);
|
||||
$this->pull_images($schedule);
|
||||
$this->check_scheduled_tasks($schedule);
|
||||
|
||||
$schedule->command('cleanup:database --yes')->daily();
|
||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||
}
|
||||
}
|
||||
private function pull_helper_image($schedule)
|
||||
private function pull_images($schedule)
|
||||
{
|
||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new PullHelperImageJob($server))->everyTenMinutes()->onOneServer();
|
||||
if (config('coolify.is_sentinel_enabled')) {
|
||||
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||
}
|
||||
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||
}
|
||||
}
|
||||
private function check_resources($schedule)
|
||||
{
|
||||
if (isCloud()) {
|
||||
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
||||
$servers = $this->all_servers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
||||
$own = Team::find(0)->servers;
|
||||
$servers = $servers->merge($own);
|
||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||
} else {
|
||||
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
||||
$servers = $this->all_servers->where('ip', '!=', '1.2.3.4');
|
||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||
}
|
||||
foreach ($containerServers as $server) {
|
||||
@@ -81,16 +86,6 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
||||
}
|
||||
}
|
||||
private function instance_auto_update($schedule)
|
||||
{
|
||||
if (isDev() || isCloud()) {
|
||||
return;
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
if ($settings->is_auto_update_enabled) {
|
||||
$schedule->job(new InstanceAutoUpdateJob)->everyTenMinutes()->onOneServer();
|
||||
}
|
||||
}
|
||||
private function check_scheduled_backups($schedule)
|
||||
{
|
||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||
@@ -134,7 +129,16 @@ class Kernel extends ConsoleKernel
|
||||
$scheduled_task->delete();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($application) {
|
||||
if (str($application->status)->contains('running') === false) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($service) {
|
||||
if (str($service->status())->contains('running') === false) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
|
||||
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class Controller extends BaseController
|
||||
public function email_verify(EmailVerificationRequest $request) {
|
||||
$request->fulfill();
|
||||
$name = request()->user()?->name;
|
||||
send_internal_notification("User {$name} verified their email address.");
|
||||
// send_internal_notification("User {$name} verified their email address.");
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
public function forgot_password(Request $request) {
|
||||
|
||||
@@ -47,7 +47,7 @@ class Bitbucket extends Controller
|
||||
if ($x_bitbucket_event === 'repo:push') {
|
||||
$branch = data_get($payload, 'push.changes.0.new.name');
|
||||
$full_name = data_get($payload, 'repository.full_name');
|
||||
|
||||
$commit = data_get($payload, 'push.changes.0.new.target.hash');
|
||||
if (!$branch) {
|
||||
return response([
|
||||
'status' => 'failed',
|
||||
@@ -82,7 +82,7 @@ class Bitbucket extends Controller
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Invalid token.',
|
||||
'message' => 'Invalid signature.',
|
||||
]);
|
||||
ray('Invalid signature');
|
||||
continue;
|
||||
@@ -104,6 +104,7 @@ class Bitbucket extends Controller
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
commit: $commit,
|
||||
force_rebuild: false,
|
||||
is_webhook: true
|
||||
);
|
||||
|
||||
222
app/Http/Controllers/Webhook/Gitea.php
Normal file
222
app/Http/Controllers/Webhook/Gitea.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Webhook;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class Gitea extends Controller
|
||||
{
|
||||
public function manual(Request $request)
|
||||
{
|
||||
try {
|
||||
$return_payloads = collect([]);
|
||||
$x_gitea_delivery = request()->header('X-Gitea-Delivery');
|
||||
if (app()->isDownForMaintenance()) {
|
||||
ray('Maintenance mode is on');
|
||||
$epoch = now()->valueOf();
|
||||
$files = Storage::disk('webhooks-during-maintenance')->files();
|
||||
$gitea_delivery_found = collect($files)->filter(function ($file) use ($x_gitea_delivery) {
|
||||
return Str::contains($file, $x_gitea_delivery);
|
||||
})->first();
|
||||
if ($gitea_delivery_found) {
|
||||
ray('Webhook already found');
|
||||
return;
|
||||
}
|
||||
$data = [
|
||||
'attributes' => $request->attributes->all(),
|
||||
'request' => $request->request->all(),
|
||||
'query' => $request->query->all(),
|
||||
'server' => $request->server->all(),
|
||||
'files' => $request->files->all(),
|
||||
'cookies' => $request->cookies->all(),
|
||||
'headers' => $request->headers->all(),
|
||||
'content' => $request->getContent(),
|
||||
];
|
||||
$json = json_encode($data);
|
||||
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitea::manual_{$x_gitea_delivery}", $json);
|
||||
return;
|
||||
}
|
||||
$x_gitea_event = Str::lower($request->header('X-Gitea-Event'));
|
||||
$x_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256=');
|
||||
$content_type = $request->header('Content-Type');
|
||||
$payload = $request->collect();
|
||||
if ($x_gitea_event === 'ping') {
|
||||
// Just pong
|
||||
return response('pong');
|
||||
}
|
||||
|
||||
if ($content_type !== 'application/json') {
|
||||
$payload = json_decode(data_get($payload, 'payload'), true);
|
||||
}
|
||||
if ($x_gitea_event === 'push') {
|
||||
$branch = data_get($payload, 'ref');
|
||||
$full_name = data_get($payload, 'repository.full_name');
|
||||
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||
$branch = Str::after($branch, 'refs/heads/');
|
||||
}
|
||||
$added_files = data_get($payload, 'commits.*.added');
|
||||
$removed_files = data_get($payload, 'commits.*.removed');
|
||||
$modified_files = data_get($payload, 'commits.*.modified');
|
||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||
ray($changed_files);
|
||||
ray('Manual Webhook Gitea Push Event with branch: ' . $branch);
|
||||
}
|
||||
if ($x_gitea_event === 'pull_request') {
|
||||
$action = data_get($payload, 'action');
|
||||
$full_name = data_get($payload, 'repository.full_name');
|
||||
$pull_request_id = data_get($payload, 'number');
|
||||
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
||||
$branch = data_get($payload, 'pull_request.head.ref');
|
||||
$base_branch = data_get($payload, 'pull_request.base.ref');
|
||||
ray('Webhook Gitea Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
|
||||
}
|
||||
if (!$branch) {
|
||||
return response('Nothing to do. No branch found in the request.');
|
||||
}
|
||||
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
||||
if ($x_gitea_event === 'push') {
|
||||
$applications = $applications->where('git_branch', $branch)->get();
|
||||
if ($applications->isEmpty()) {
|
||||
return response("Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.");
|
||||
}
|
||||
}
|
||||
if ($x_gitea_event === 'pull_request') {
|
||||
$applications = $applications->where('git_branch', $base_branch)->get();
|
||||
if ($applications->isEmpty()) {
|
||||
return response("Nothing to do. No applications found with branch '$base_branch'.");
|
||||
}
|
||||
}
|
||||
foreach ($applications as $application) {
|
||||
$webhook_secret = data_get($application, 'manual_webhook_secret_gitea');
|
||||
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
|
||||
if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) {
|
||||
ray('Invalid signature');
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Invalid signature.',
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
$isFunctional = $application->destination->server->isFunctional();
|
||||
if (!$isFunctional) {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Server is not functional.',
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
if ($x_gitea_event === 'push') {
|
||||
if ($application->isDeployable()) {
|
||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
commit: data_get($payload, 'after', 'HEAD'),
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
'status' => 'success',
|
||||
'message' => 'Deployment queued.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
} else {
|
||||
$paths = str($application->watch_paths)->explode("\n");
|
||||
$return_payloads->push([
|
||||
'status' => 'failed',
|
||||
'message' => 'Changed files do not match watch paths. Ignoring deployment.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'details' => [
|
||||
'changed_files' => $changed_files,
|
||||
'watch_paths' => $paths,
|
||||
],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$return_payloads->push([
|
||||
'status' => 'failed',
|
||||
'message' => 'Deployments disabled.',
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
]);
|
||||
}
|
||||
}
|
||||
if ($x_gitea_event === 'pull_request') {
|
||||
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
||||
if ($application->isPRDeployable()) {
|
||||
$deployment_uuid = new Cuid2(7);
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if (!$found) {
|
||||
ApplicationPreview::create([
|
||||
'git_type' => 'gitea',
|
||||
'application_id' => $application->id,
|
||||
'pull_request_id' => $pull_request_id,
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
]);
|
||||
}
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
pull_request_id: $pull_request_id,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
commit: data_get($payload, 'head.sha', 'HEAD'),
|
||||
is_webhook: true,
|
||||
git_type: 'gitea'
|
||||
);
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'success',
|
||||
'message' => 'Preview deployment queued.',
|
||||
]);
|
||||
} else {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Preview deployments disabled.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
if ($action === 'closed') {
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if ($found) {
|
||||
$found->delete();
|
||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||
// ray('Stopping container: ' . $container_name);
|
||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'success',
|
||||
'message' => 'Preview deployment closed.',
|
||||
]);
|
||||
} else {
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'No preview deployment found.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ray($return_payloads);
|
||||
return response($return_payloads);
|
||||
} catch (Exception $e) {
|
||||
ray($e->getMessage());
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,7 @@ class Github extends Controller
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Invalid token.',
|
||||
'message' => 'Invalid signature.',
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
@@ -129,6 +129,7 @@ class Github extends Controller
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
commit: data_get($payload, 'after', 'HEAD'),
|
||||
is_webhook: true,
|
||||
);
|
||||
$return_payloads->push([
|
||||
@@ -177,6 +178,7 @@ class Github extends Controller
|
||||
pull_request_id: $pull_request_id,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
commit: data_get($payload, 'head.sha', 'HEAD'),
|
||||
is_webhook: true,
|
||||
git_type: 'github'
|
||||
);
|
||||
@@ -338,6 +340,7 @@ class Github extends Controller
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
commit: data_get($payload, 'after', 'HEAD'),
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
@@ -387,6 +390,7 @@ class Github extends Controller
|
||||
pull_request_id: $pull_request_id,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
force_rebuild: false,
|
||||
commit: data_get($payload, 'head.sha', 'HEAD'),
|
||||
is_webhook: true,
|
||||
git_type: 'github'
|
||||
);
|
||||
@@ -406,11 +410,12 @@ class Github extends Controller
|
||||
if ($action === 'closed' || $action === 'close') {
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if ($found) {
|
||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||
|
||||
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
|
||||
$found->delete();
|
||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||
// ray('Stopping container: ' . $container_name);
|
||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'success',
|
||||
@@ -426,7 +431,6 @@ class Github extends Controller
|
||||
}
|
||||
}
|
||||
}
|
||||
ray($return_payloads);
|
||||
return response($return_payloads);
|
||||
} catch (Exception $e) {
|
||||
ray($e->getMessage());
|
||||
|
||||
@@ -38,6 +38,15 @@ class Gitlab extends Controller
|
||||
$headers = $request->headers->all();
|
||||
$x_gitlab_token = data_get($headers, 'x-gitlab-token.0');
|
||||
$x_gitlab_event = data_get($payload, 'object_kind');
|
||||
$allowed_events = ['push', 'merge_request'];
|
||||
if (!in_array($x_gitlab_event, $allowed_events)) {
|
||||
$return_payloads->push([
|
||||
'status' => 'failed',
|
||||
'message' => 'Event not allowed. Only push and merge_request events are allowed.',
|
||||
]);
|
||||
return response($return_payloads);
|
||||
}
|
||||
|
||||
if ($x_gitlab_event === 'push') {
|
||||
$branch = data_get($payload, 'ref');
|
||||
$full_name = data_get($payload, 'project.path_with_namespace');
|
||||
@@ -100,7 +109,7 @@ class Gitlab extends Controller
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
'status' => 'failed',
|
||||
'message' => 'Invalid token.',
|
||||
'message' => 'Invalid signature.',
|
||||
]);
|
||||
ray('Invalid signature');
|
||||
continue;
|
||||
@@ -124,6 +133,7 @@ class Gitlab extends Controller
|
||||
queue_application_deployment(
|
||||
application: $application,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
commit: data_get($payload, 'after', 'HEAD'),
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
);
|
||||
@@ -173,6 +183,7 @@ class Gitlab extends Controller
|
||||
application: $application,
|
||||
pull_request_id: $pull_request_id,
|
||||
deployment_uuid: $deployment_uuid,
|
||||
commit: data_get($payload, 'object_attributes.last_commit.id', 'HEAD'),
|
||||
force_rebuild: false,
|
||||
is_webhook: true,
|
||||
git_type: 'gitlab'
|
||||
@@ -191,7 +202,7 @@ class Gitlab extends Controller
|
||||
]);
|
||||
ray('Preview deployments disabled for ' . $application->name);
|
||||
}
|
||||
} else if ($action === 'closed' || $action === 'close') {
|
||||
} else if ($action === 'closed' || $action === 'close' || $action === 'merge') {
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if ($found) {
|
||||
$found->delete();
|
||||
|
||||
@@ -150,6 +150,10 @@ class Stripe extends Controller
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||
}
|
||||
if (!$subscription) {
|
||||
if ($status === 'incomplete_expired') {
|
||||
send_internal_notification('Subscription incomplete expired for customer: ' . $customerId);
|
||||
return response("Subscription incomplete expired", 200);
|
||||
}
|
||||
send_internal_notification('No subscription found for: ' . $customerId);
|
||||
return response("No subscription found", 400);
|
||||
}
|
||||
@@ -166,9 +170,11 @@ class Stripe extends Controller
|
||||
$quantity = data_get($data, 'items.data.0.quantity', 10);
|
||||
}
|
||||
$team = data_get($subscription, 'team');
|
||||
$team->update([
|
||||
'custom_server_limit' => $quantity,
|
||||
]);
|
||||
if ($team) {
|
||||
$team->update([
|
||||
'custom_server_limit' => $quantity,
|
||||
]);
|
||||
}
|
||||
ServerLimitCheckJob::dispatch($team);
|
||||
}
|
||||
$subscription->update([
|
||||
@@ -210,7 +216,9 @@ class Stripe extends Controller
|
||||
$customerId = data_get($data, 'customer');
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||
$team = data_get($subscription, 'team');
|
||||
$team->trialEnded();
|
||||
if ($team) {
|
||||
$team->trialEnded();
|
||||
}
|
||||
$subscription->update([
|
||||
'stripe_subscription_id' => null,
|
||||
'stripe_plan_id' => null,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
try {
|
||||
if ($this->application->is_public_repository()) {
|
||||
ray('Public repository. Skipping comment update.');
|
||||
return;
|
||||
}
|
||||
if ($this->status === ProcessStatus::CLOSED) {
|
||||
@@ -59,7 +60,7 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
throw $e;
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public function handle(): void
|
||||
public function handle()
|
||||
{
|
||||
// ray("checking log drain statuses for {$this->server->id}");
|
||||
try {
|
||||
@@ -70,7 +70,7 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
if (!$this->server->log_drain_notification_sent) {
|
||||
ray('Log drain container still unhealthy. Sending notification...');
|
||||
$this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
|
||||
// $this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
|
||||
$this->server->update(['log_drain_notification_sent' => true]);
|
||||
}
|
||||
} else {
|
||||
@@ -80,9 +80,9 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
||||
if (!isCloud()) send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
handleError($e);
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,8 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Actions\Shared\ComplexStatusCheck;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Notifications\Container\ContainerRestarted;
|
||||
use App\Notifications\Container\ContainerStopped;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@@ -18,7 +11,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
@@ -44,335 +36,6 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
|
||||
public function handle()
|
||||
{
|
||||
if (!$this->server->isFunctional()) {
|
||||
return 'Server is not ready.';
|
||||
};
|
||||
$applications = $this->server->applications();
|
||||
$skip_these_applications = collect([]);
|
||||
foreach ($applications as $application) {
|
||||
if ($application->additional_servers->count() > 0) {
|
||||
$skip_these_applications->push($application);
|
||||
ComplexStatusCheck::run($application);
|
||||
$applications = $applications->filter(function ($value, $key) use ($application) {
|
||||
return $value->id !== $application->id;
|
||||
});
|
||||
}
|
||||
}
|
||||
$applications = $applications->filter(function ($value, $key) use ($skip_these_applications) {
|
||||
return !$skip_these_applications->pluck('id')->contains($value->id);
|
||||
});
|
||||
try {
|
||||
if ($this->server->isSwarm()) {
|
||||
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
||||
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
||||
} else {
|
||||
// Precheck for containers
|
||||
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
||||
if (!$containers) {
|
||||
return;
|
||||
}
|
||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
||||
$containerReplicates = null;
|
||||
}
|
||||
if (is_null($containers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$containers = format_docker_command_output_to_json($containers);
|
||||
if ($containerReplicates) {
|
||||
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
|
||||
foreach ($containerReplicates as $containerReplica) {
|
||||
$name = data_get($containerReplica, 'Name');
|
||||
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
||||
if (data_get($container, 'Spec.Name') === $name) {
|
||||
$replicas = data_get($containerReplica, 'Replicas');
|
||||
$running = str($replicas)->explode('/')[0];
|
||||
$total = str($replicas)->explode('/')[1];
|
||||
if ($running === $total) {
|
||||
data_set($container, 'State.Status', 'running');
|
||||
data_set($container, 'State.Health.Status', 'healthy');
|
||||
} else {
|
||||
data_set($container, 'State.Status', 'starting');
|
||||
data_set($container, 'State.Health.Status', 'unhealthy');
|
||||
}
|
||||
}
|
||||
return $container;
|
||||
});
|
||||
}
|
||||
}
|
||||
$databases = $this->server->databases();
|
||||
$services = $this->server->services()->get();
|
||||
$previews = $this->server->previews();
|
||||
$foundApplications = [];
|
||||
$foundApplicationPreviews = [];
|
||||
$foundDatabases = [];
|
||||
$foundServices = [];
|
||||
|
||||
foreach ($containers as $container) {
|
||||
if ($this->server->isSwarm()) {
|
||||
$labels = data_get($container, 'Spec.Labels');
|
||||
$uuid = data_get($labels, 'coolify.name');
|
||||
} else {
|
||||
$labels = data_get($container, 'Config.Labels');
|
||||
}
|
||||
$containerStatus = data_get($container, 'State.Status');
|
||||
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||
$containerStatus = "$containerStatus ($containerHealth)";
|
||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||
if ($applicationId) {
|
||||
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||
if ($pullRequestId) {
|
||||
if (str($applicationId)->contains('-')) {
|
||||
$applicationId = str($applicationId)->before('-');
|
||||
}
|
||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||
if ($preview) {
|
||||
$foundApplicationPreviews[] = $preview->id;
|
||||
$statusFromDb = $preview->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$preview->update(['status' => $containerStatus]);
|
||||
}
|
||||
} else {
|
||||
//Notify user that this container should not be there.
|
||||
}
|
||||
} else {
|
||||
$application = $applications->where('id', $applicationId)->first();
|
||||
if ($application) {
|
||||
$foundApplications[] = $application->id;
|
||||
$statusFromDb = $application->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$application->update(['status' => $containerStatus]);
|
||||
}
|
||||
} else {
|
||||
//Notify user that this container should not be there.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||
$type = data_get($labels, 'coolify.type');
|
||||
|
||||
if ($uuid) {
|
||||
if ($type === 'service') {
|
||||
$database_id = data_get($labels, 'coolify.service.subId');
|
||||
if ($database_id) {
|
||||
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
||||
if ($service_db) {
|
||||
$uuid = $service_db->service->uuid;
|
||||
$isPublic = data_get($service_db, 'is_public');
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($service_db);
|
||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$database = $databases->where('uuid', $uuid)->first();
|
||||
if ($database) {
|
||||
$isPublic = data_get($database, 'is_public');
|
||||
$foundDatabases[] = $database->id;
|
||||
$statusFromDb = $database->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$database->update(['status' => $containerStatus]);
|
||||
}
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
}
|
||||
})->first();
|
||||
if (!$foundTcpProxy) {
|
||||
StartDatabaseProxy::run($database);
|
||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Notify user that this container should not be there.
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data_get($container, 'Name') === '/coolify-db') {
|
||||
$foundDatabases[] = 0;
|
||||
}
|
||||
}
|
||||
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
||||
if ($serviceLabelId) {
|
||||
$subType = data_get($labels, 'coolify.service.subType');
|
||||
$subId = data_get($labels, 'coolify.service.subId');
|
||||
$service = $services->where('id', $serviceLabelId)->first();
|
||||
if (!$service) {
|
||||
continue;
|
||||
}
|
||||
if ($subType === 'application') {
|
||||
$service = $service->applications()->where('id', $subId)->first();
|
||||
} else {
|
||||
$service = $service->databases()->where('id', $subId)->first();
|
||||
}
|
||||
if ($service) {
|
||||
$foundServices[] = "$service->id-$service->name";
|
||||
$statusFromDb = $service->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
// ray('Updating status: ' . $containerStatus);
|
||||
$service->update(['status' => $containerStatus]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$exitedServices = collect([]);
|
||||
foreach ($services as $service) {
|
||||
$apps = $service->applications()->get();
|
||||
$dbs = $service->databases()->get();
|
||||
foreach ($apps as $app) {
|
||||
if (in_array("$app->id-$app->name", $foundServices)) {
|
||||
continue;
|
||||
} else {
|
||||
$exitedServices->push($app);
|
||||
}
|
||||
}
|
||||
foreach ($dbs as $db) {
|
||||
if (in_array("$db->id-$db->name", $foundServices)) {
|
||||
continue;
|
||||
} else {
|
||||
$exitedServices->push($db);
|
||||
}
|
||||
}
|
||||
}
|
||||
$exitedServices = $exitedServices->unique('id');
|
||||
foreach ($exitedServices as $exitedService) {
|
||||
if (str($exitedService->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$name = data_get($exitedService, 'name');
|
||||
$fqdn = data_get($exitedService, 'fqdn');
|
||||
$containerName = $name ? "$name, available at $fqdn" : $fqdn;
|
||||
$projectUuid = data_get($service, 'environment.project.uuid');
|
||||
$serviceUuid = data_get($service, 'uuid');
|
||||
$environmentName = data_get($service, 'environment.name');
|
||||
|
||||
if ($projectUuid && $serviceUuid && $environmentName) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
$this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
$exitedService->update(['status' => 'exited']);
|
||||
}
|
||||
|
||||
$notRunningApplications = $applications->pluck('id')->diff($foundApplications);
|
||||
foreach ($notRunningApplications as $applicationId) {
|
||||
$application = $applications->where('id', $applicationId)->first();
|
||||
if (str($application->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$application->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($application, 'name');
|
||||
$fqdn = data_get($application, 'fqdn');
|
||||
|
||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||
|
||||
$projectUuid = data_get($application, 'environment.project.uuid');
|
||||
$applicationUuid = data_get($application, 'uuid');
|
||||
$environment = data_get($application, 'environment.name');
|
||||
|
||||
if ($projectUuid && $applicationUuid && $environment) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
|
||||
$this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
||||
foreach ($notRunningApplicationPreviews as $previewId) {
|
||||
$preview = $previews->where('id', $previewId)->first();
|
||||
if (str($preview->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$preview->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($preview, 'name');
|
||||
$fqdn = data_get($preview, 'fqdn');
|
||||
|
||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||
|
||||
$projectUuid = data_get($preview, 'application.environment.project.uuid');
|
||||
$environmentName = data_get($preview, 'application.environment.name');
|
||||
$applicationUuid = data_get($preview, 'application.uuid');
|
||||
|
||||
if ($projectUuid && $applicationUuid && $environmentName) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
|
||||
$this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
||||
foreach ($notRunningDatabases as $database) {
|
||||
$database = $databases->where('id', $database)->first();
|
||||
if (str($database->status)->startsWith('exited')) {
|
||||
continue;
|
||||
}
|
||||
$database->update(['status' => 'exited']);
|
||||
|
||||
$name = data_get($database, 'name');
|
||||
$fqdn = data_get($database, 'fqdn');
|
||||
|
||||
$containerName = $name;
|
||||
|
||||
$projectUuid = data_get($database, 'environment.project.uuid');
|
||||
$environmentName = data_get($database, 'environment.name');
|
||||
$databaseUuid = data_get($database, 'uuid');
|
||||
|
||||
if ($projectUuid && $databaseUuid && $environmentName) {
|
||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid;
|
||||
} else {
|
||||
$url = null;
|
||||
}
|
||||
$this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||
}
|
||||
|
||||
// Check if proxy is running
|
||||
$this->server->proxyType();
|
||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||
} else {
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
}
|
||||
})->first();
|
||||
if (!$foundProxyContainer) {
|
||||
try {
|
||||
$shouldStart = CheckProxy::run($this->server);
|
||||
if ($shouldStart) {
|
||||
StartProxy::run($this->server, false);
|
||||
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
}
|
||||
} else {
|
||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||
$this->server->save();
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
return handleError($e);
|
||||
}
|
||||
GetContainersStatus::run($this->server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ class InstanceAutoUpdateJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncr
|
||||
public $timeout = 600;
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(private bool $force = false)
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
UpdateCoolify::run(force: $this->force, async: false);
|
||||
UpdateCoolify::run();
|
||||
}
|
||||
}
|
||||
|
||||
59
app/Jobs/PullCoolifyImageJob.php
Normal file
59
app/Jobs/PullCoolifyImageJob.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class PullCoolifyImageJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $timeout = 1000;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
if (isDev() || isCloud()) {
|
||||
return;
|
||||
}
|
||||
$server = Server::findOrFail(0);
|
||||
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
|
||||
if ($response->successful()) {
|
||||
$versions = $response->json();
|
||||
File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT));
|
||||
}
|
||||
$latest_version = get_latest_version_of_coolify();
|
||||
instant_remote_process(["docker pull -q ghcr.io/coollabsio/coolify:{$latest_version}"], $server, false);
|
||||
|
||||
$settings = InstanceSettings::get();
|
||||
$current_version = config('version');
|
||||
if (!$settings->is_auto_update_enabled) {
|
||||
return;
|
||||
}
|
||||
if ($latest_version === $current_version) {
|
||||
return;
|
||||
}
|
||||
if (version_compare($latest_version, $current_version, '<')) {
|
||||
return;
|
||||
}
|
||||
instant_remote_process([
|
||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||
"bash /data/coolify/source/upgrade.sh $latest_version"
|
||||
], $server);
|
||||
} catch (\Throwable $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
56
app/Jobs/PullSentinelImageJob.php
Normal file
56
app/Jobs/PullSentinelImageJob.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Server\StartSentinel;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class PullSentinelImageJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $timeout = 1000;
|
||||
|
||||
public function middleware(): array
|
||||
{
|
||||
return [(new WithoutOverlapping($this->server->uuid))];
|
||||
}
|
||||
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return $this->server->uuid;
|
||||
}
|
||||
public function __construct(public Server $server)
|
||||
{
|
||||
}
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$version = get_latest_sentinel_version();
|
||||
if (!$version) {
|
||||
ray('Failed to get latest Sentinel version');
|
||||
return;
|
||||
}
|
||||
$local_version = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/version"'], $this->server, false);
|
||||
if (empty($local_version)) {
|
||||
$local_version = '0.0.0';
|
||||
}
|
||||
if (version_compare($local_version, $version, '<')) {
|
||||
StartSentinel::run($this->server, $version, true);
|
||||
return;
|
||||
}
|
||||
ray('Sentinel image is up to date');
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('PullSentinelImageJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
app/Jobs/PullTemplatesFromCDN.php
Normal file
42
app/Jobs/PullTemplatesFromCDN.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class PullTemplatesFromCDN implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $timeout = 10;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
if (!isDev()) {
|
||||
ray('PullTemplatesAndVersions service-templates');
|
||||
$response = Http::retry(3, 1000)->get(config('constants.services.official'));
|
||||
if ($response->successful()) {
|
||||
$services = $response->json();
|
||||
File::put(base_path('templates/service-templates.json'), json_encode($services));
|
||||
} else {
|
||||
send_internal_notification('PullTemplatesAndVersions failed with: ' . $response->status() . ' ' . $response->body());
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('PullTemplatesAndVersions failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
40
app/Jobs/PullVersionsFromCDN.php
Normal file
40
app/Jobs/PullVersionsFromCDN.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class PullVersionsFromCDN implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $timeout = 10;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
if (!isDev() && !isCloud()) {
|
||||
ray('PullTemplatesAndVersions versions.json');
|
||||
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
|
||||
if ($response->successful()) {
|
||||
$versions = $response->json();
|
||||
File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT));
|
||||
} else {
|
||||
send_internal_notification('PullTemplatesAndVersions failed with: ' . $response->status() . ' ' . $response->body());
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,13 @@ use App\Models\Server;
|
||||
use App\Models\Application;
|
||||
use App\Models\Service;
|
||||
use App\Models\Team;
|
||||
use App\Notifications\ScheduledTask\TaskFailed;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
use Throwable;
|
||||
|
||||
class ScheduledTaskJob implements ShouldQueue
|
||||
{
|
||||
@@ -77,8 +76,12 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
$this->containers[] = data_get($application, 'name') . '-' . data_get($this->resource, 'uuid');
|
||||
}
|
||||
});
|
||||
$this->resource->databases()->get()->each(function ($database) {
|
||||
if (str(data_get($database, 'status'))->contains('running')) {
|
||||
$this->containers[] = data_get($database, 'name') . '-' . data_get($this->resource, 'uuid');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (count($this->containers) == 0) {
|
||||
throw new \Exception('ScheduledTaskJob failed: No containers running.');
|
||||
}
|
||||
@@ -89,7 +92,7 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
|
||||
foreach ($this->containers as $containerName) {
|
||||
if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container . '-' . $this->resource->uuid)) {
|
||||
$cmd = 'sh -c "' . str_replace('"', '\"', $this->task->command) . '"';
|
||||
$cmd = "sh -c '" . str_replace("'", "'\''", $this->task->command) . "'";
|
||||
$exec = "docker exec {$containerName} {$cmd}";
|
||||
$this->task_output = instant_remote_process([$exec], $this->server, true);
|
||||
$this->task_log->update([
|
||||
@@ -110,6 +113,7 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
'message' => $this->task_output ?? $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
$this->team?->notify(new TaskFailed($this->task, $e->getMessage()));
|
||||
// send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ class SendMessageToDiscordJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$payload = [
|
||||
'content' => $this->text,
|
||||
];
|
||||
ray($payload);
|
||||
Http::post($this->webhookUrl, $payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class ServerLimitCheckJob implements ShouldQueue, ShouldBeEncrypted
|
||||
try {
|
||||
$servers = $this->team->servers;
|
||||
$servers_count = $servers->count();
|
||||
$limit = $this->team->limits['serverLimit'];
|
||||
$limit = data_get($this->team->limits, 'serverLimit', 2);
|
||||
$number_of_servers_to_disable = $servers_count - $limit;
|
||||
ray('ServerLimitCheckJob', $this->team->uuid, $servers_count, $limit, $number_of_servers_to_disable);
|
||||
if ($number_of_servers_to_disable > 0) {
|
||||
|
||||
@@ -17,7 +17,7 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public int|string|null $disk_usage = null;
|
||||
public $tries = 4;
|
||||
public $tries = 3;
|
||||
public function backoff(): int
|
||||
{
|
||||
return isDev() ? 1 : 3;
|
||||
@@ -38,20 +38,59 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
public function handle()
|
||||
{
|
||||
if (!$this->server->isServerReady($this->tries)) {
|
||||
return "Server is not ready yet.";
|
||||
throw new \RuntimeException('Server is not ready.');
|
||||
};
|
||||
try {
|
||||
if ($this->server->isFunctional()) {
|
||||
$this->cleanup(notify: false);
|
||||
$this->removeCoolifyYaml();
|
||||
$this->remove_unnecessary_coolify_yaml();
|
||||
if (config('coolify.is_sentinel_enabled')) {
|
||||
$this->server->checkSentinel();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
|
||||
ray($e->getMessage());
|
||||
return handleError($e);
|
||||
}
|
||||
try {
|
||||
// $this->check_docker_engine();
|
||||
} catch (\Throwable $e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
private function removeCoolifyYaml()
|
||||
private function check_docker_engine()
|
||||
{
|
||||
$version = instant_remote_process([
|
||||
"docker info",
|
||||
], $this->server, false);
|
||||
if (is_null($version)) {
|
||||
$os = instant_remote_process([
|
||||
"cat /etc/os-release | grep ^ID=",
|
||||
], $this->server, false);
|
||||
$os = str($os)->after('ID=')->trim();
|
||||
if ($os === 'ubuntu') {
|
||||
try {
|
||||
instant_remote_process([
|
||||
"systemctl start docker",
|
||||
], $this->server);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
return handleError($e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
instant_remote_process([
|
||||
"service docker start",
|
||||
], $this->server);
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private function remove_unnecessary_coolify_yaml()
|
||||
{
|
||||
// This will remote the coolify.yaml file from the server as it is not needed on cloud servers
|
||||
if (isCloud() && $this->server->id !== 0) {
|
||||
|
||||
@@ -17,5 +17,7 @@ class ProxyStartedNotification
|
||||
$this->server = data_get($event, 'data');
|
||||
$this->server->setupDefault404Redirect();
|
||||
$this->server->setupDynamicProxyConfiguration();
|
||||
$this->server->proxy->force_stop = false;
|
||||
$this->server->save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ class Index extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (auth()->user()?->isMember() && auth()->user()->currentTeam()->show_boarding === true) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->privateKeyName = generate_random_name();
|
||||
$this->remoteServerName = generate_random_name();
|
||||
if (isDev()) {
|
||||
|
||||
@@ -10,7 +10,7 @@ class Compose extends Component
|
||||
public string $base64 = '';
|
||||
public $services;
|
||||
public function mount() {
|
||||
$this->services = getServiceTemplates();
|
||||
$this->services = get_service_templates();
|
||||
}
|
||||
public function setService(string $selected) {
|
||||
$this->base64 = data_get($this->services, $selected . '.compose');
|
||||
|
||||
@@ -24,7 +24,7 @@ class ForcePasswordReset extends Component
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.force-password-reset');
|
||||
return view('livewire.force-password-reset')->layout('layouts.simple');
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ class Discord extends Component
|
||||
'team.discord_notifications_deployments' => 'nullable|boolean',
|
||||
'team.discord_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.discord_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.discord_notifications_scheduled_tasks' => 'nullable|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'team.discord_webhook_url' => 'Discord Webhook',
|
||||
|
||||
@@ -28,6 +28,7 @@ class Email extends Component
|
||||
'team.smtp_notifications_deployments' => 'nullable|boolean',
|
||||
'team.smtp_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.smtp_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.smtp_notifications_scheduled_tasks' => 'nullable|boolean',
|
||||
'team.use_instance_email_settings' => 'boolean',
|
||||
'team.resend_enabled' => 'nullable|boolean',
|
||||
'team.resend_api_key' => 'nullable',
|
||||
|
||||
@@ -18,10 +18,12 @@ class Telegram extends Component
|
||||
'team.telegram_notifications_deployments' => 'nullable|boolean',
|
||||
'team.telegram_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.telegram_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.telegram_notifications_scheduled_tasks' => 'nullable|boolean',
|
||||
'team.telegram_notifications_test_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_deployments_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_status_changes_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_database_backups_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_scheduled_tasks_thread_id' => 'nullable|string',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'team.telegram_token' => 'Token',
|
||||
|
||||
@@ -21,6 +21,7 @@ class Advanced extends Component
|
||||
'application.settings.is_gpu_enabled' => 'boolean|required',
|
||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||
'application.settings.is_consistent_container_name_enabled' => 'boolean|required',
|
||||
'application.settings.custom_internal_name' => 'string|nullable',
|
||||
'application.settings.is_gzip_enabled' => 'boolean|required',
|
||||
'application.settings.is_stripprefix_enabled' => 'boolean|required',
|
||||
'application.settings.gpu_driver' => 'string|required',
|
||||
@@ -30,7 +31,8 @@ class Advanced extends Component
|
||||
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
|
||||
'application.settings.connect_to_docker_network' => 'boolean|required',
|
||||
];
|
||||
public function mount() {
|
||||
public function mount()
|
||||
{
|
||||
$this->is_force_https_enabled = $this->application->isForceHttpsEnabled();
|
||||
$this->is_gzip_enabled = $this->application->isGzipEnabled();
|
||||
$this->is_stripprefix_enabled = $this->application->isStripprefixEnabled();
|
||||
@@ -65,7 +67,8 @@ class Advanced extends Component
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
public function submit() {
|
||||
public function submit()
|
||||
{
|
||||
if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) {
|
||||
$this->dispatch('error', 'You cannot set both GPU count and GPU device IDs.');
|
||||
$this->application->settings->gpu_count = null;
|
||||
@@ -76,6 +79,16 @@ class Advanced extends Component
|
||||
$this->application->settings->save();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
public function saveCustomName()
|
||||
{
|
||||
if (isset($this->application->settings->custom_internal_name)) {
|
||||
$this->application->settings->custom_internal_name = str($this->application->settings->custom_internal_name)->slug()->value();
|
||||
} else {
|
||||
$this->application->settings->custom_internal_name = null;
|
||||
}
|
||||
$this->application->settings->save();
|
||||
$this->dispatch('success', 'Custom name saved.');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.application.advanced');
|
||||
|
||||
@@ -22,6 +22,7 @@ class General extends Component
|
||||
public ?string $git_commit_sha = null;
|
||||
public string $build_pack;
|
||||
public ?string $ports_exposes = null;
|
||||
public bool $is_container_label_escape_enabled = true;
|
||||
|
||||
public $customLabels;
|
||||
public bool $labelsChanged = false;
|
||||
@@ -30,7 +31,7 @@ class General extends Component
|
||||
public ?string $initialDockerComposeLocation = null;
|
||||
public ?string $initialDockerComposePrLocation = null;
|
||||
|
||||
public $parsedServices = [];
|
||||
public null|Collection $parsedServices;
|
||||
public $parsedServiceDomains = [];
|
||||
|
||||
protected $listeners = [
|
||||
@@ -74,6 +75,7 @@ class General extends Component
|
||||
'application.post_deployment_command_container' => 'nullable',
|
||||
'application.settings.is_static' => 'boolean|required',
|
||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||
'application.settings.is_container_label_escape_enabled' => 'boolean|required',
|
||||
'application.watch_paths' => 'nullable',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
@@ -109,12 +111,17 @@ class General extends Component
|
||||
'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
|
||||
'application.settings.is_static' => 'Is static',
|
||||
'application.settings.is_build_server_enabled' => 'Is build server enabled',
|
||||
'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled',
|
||||
'application.watch_paths' => 'Watch paths',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
$this->parsedServices = $this->application->parseCompose();
|
||||
if (is_null($this->parsedServices) || empty($this->parsedServices)) {
|
||||
$this->dispatch('error', "Failed to parse your docker-compose file. Please check the syntax and try again.");
|
||||
return;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->dispatch('error', $e->getMessage());
|
||||
}
|
||||
@@ -124,6 +131,7 @@ class General extends Component
|
||||
}
|
||||
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
|
||||
$this->customLabels = $this->application->parseContainerLabels();
|
||||
if (!$this->customLabels && $this->application->destination->server->proxyType() !== 'NONE') {
|
||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||
@@ -133,7 +141,7 @@ class General extends Component
|
||||
$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...');
|
||||
$this->dispatch('info', 'Loading docker compose file.');
|
||||
}
|
||||
|
||||
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
||||
@@ -145,7 +153,7 @@ class General extends Component
|
||||
$this->application->settings->save();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->application->refresh();
|
||||
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
||||
if ($this->ports_exposes !== $this->application->ports_exposes || $this->is_container_label_escape_enabled !== $this->application->settings->is_container_label_escape_enabled) {
|
||||
$this->resetDefaultLabels(false);
|
||||
}
|
||||
}
|
||||
@@ -156,6 +164,10 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
|
||||
if (is_null($this->parsedServices)) {
|
||||
$this->dispatch('error', "Failed to parse your docker-compose file. Please check the syntax and try again.");
|
||||
return;
|
||||
}
|
||||
$compose = $this->application->parseCompose();
|
||||
$services = data_get($compose, 'services');
|
||||
if ($services) {
|
||||
@@ -204,6 +216,9 @@ class General extends Component
|
||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||
$this->application->save();
|
||||
$this->dispatch('success', 'Domain generated.');
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->loadComposeFile();
|
||||
}
|
||||
return $domain;
|
||||
}
|
||||
public function updatedApplicationBaseDirectory()
|
||||
@@ -255,12 +270,14 @@ class General extends Component
|
||||
}
|
||||
public function resetDefaultLabels()
|
||||
{
|
||||
ray('resetDefaultLabels');
|
||||
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
|
||||
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
|
||||
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||
$this->application->save();
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->loadComposeFile();
|
||||
}
|
||||
}
|
||||
|
||||
public function checkFqdns($showToaster = true)
|
||||
@@ -270,7 +287,7 @@ class General extends Component
|
||||
if ($this->application->additional_servers->count() === 0) {
|
||||
foreach ($domains as $domain) {
|
||||
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
||||
$showToaster && $this->dispatch('error', "Validating DNS ($domain) failed.", "Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
|
||||
$showToaster && $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.<br><br>$domain->{$this->application->destination->server->ip}<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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,10 +316,13 @@ class General extends Component
|
||||
}
|
||||
|
||||
if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
|
||||
$this->loadComposeFile();
|
||||
$compose_return = $this->loadComposeFile();
|
||||
if ($compose_return instanceof \Livewire\Features\SupportEvents\Event) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->validate();
|
||||
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
||||
if ($this->ports_exposes !== $this->application->ports_exposes || $this->is_container_label_escape_enabled !== $this->application->settings->is_container_label_escape_enabled) {
|
||||
$this->resetDefaultLabels();
|
||||
}
|
||||
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||
@@ -332,7 +352,7 @@ class General extends Component
|
||||
$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.");
|
||||
$showToaster && $this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.<br><br>$domain->{$this->application->destination->server->ip}<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);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Actions\Application\StopApplication;
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Events\ApplicationStatusChanged;
|
||||
use App\Jobs\ComplexContainerStatusJob;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Jobs\ServerStatusJob;
|
||||
use App\Models\Application;
|
||||
@@ -14,6 +14,8 @@ use Visus\Cuid2\Cuid2;
|
||||
class Heading extends Component
|
||||
{
|
||||
public Application $application;
|
||||
public ?string $lastDeploymentInfo = null;
|
||||
public ?string $lastDeploymentLink = null;
|
||||
public array $parameters;
|
||||
|
||||
protected string $deploymentUuid;
|
||||
@@ -23,17 +25,22 @@ class Heading extends Component
|
||||
return [
|
||||
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
|
||||
"compose_loaded" => '$refresh',
|
||||
"update_links" => '$refresh',
|
||||
];
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$lastDeployment = $this->application->get_last_successful_deployment();
|
||||
$this->lastDeploymentInfo = data_get_str($lastDeployment, 'commit')->limit(7) . ' ' . data_get($lastDeployment, 'commit_message');
|
||||
$this->lastDeploymentLink = $this->application->gitCommitLink(data_get($lastDeployment, 'commit'));
|
||||
}
|
||||
|
||||
public function check_status($showNotification = false)
|
||||
{
|
||||
if ($this->application->destination->server->isFunctional()) {
|
||||
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||
GetContainersStatus::dispatch($this->application->destination->server);
|
||||
// dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||
} else {
|
||||
dispatch(new ServerStatusJob($this->application->destination->server));
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class Previews extends Component
|
||||
@@ -16,6 +18,9 @@ class Previews extends Component
|
||||
public Collection $pull_requests;
|
||||
public int $rate_limit_remaining;
|
||||
|
||||
protected $rules = [
|
||||
'application.previews.*.fqdn' => 'string|nullable',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$this->pull_requests = collect();
|
||||
@@ -33,7 +38,88 @@ class Previews extends Component
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function save_preview($preview_id)
|
||||
{
|
||||
try {
|
||||
$success = true;
|
||||
$preview = $this->application->previews->find($preview_id);
|
||||
if (data_get_str($preview, 'fqdn')->isNotEmpty()) {
|
||||
$preview->fqdn = str($preview->fqdn)->replaceEnd(',', '')->trim();
|
||||
$preview->fqdn = str($preview->fqdn)->replaceStart(',', '')->trim();
|
||||
$preview->fqdn = str($preview->fqdn)->trim()->lower();
|
||||
if (!validate_dns_entry($preview->fqdn, $this->application->destination->server)) {
|
||||
$this->dispatch('error', "Validating DNS failed.", "Make sure you have added the DNS records correctly.<br><br>$preview->fqdn->{$this->application->destination->server->ip}<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.");
|
||||
$success = false;
|
||||
}
|
||||
check_domain_usage(resource: $this->application, domain: $preview->fqdn);
|
||||
}
|
||||
|
||||
if (!$preview) {
|
||||
throw new \Exception('Preview not found');
|
||||
}
|
||||
$success && $preview->save();
|
||||
$success && $this->dispatch('success', 'Preview saved.<br><br>Do not forget to redeploy the preview to apply the changes.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function generate_preview($preview_id)
|
||||
{
|
||||
$preview = $this->application->previews->find($preview_id);
|
||||
if (!$preview) {
|
||||
$this->dispatch('error', 'Preview not found.');
|
||||
return;
|
||||
}
|
||||
$fqdn = generateFqdn($this->application->destination->server, $this->application->uuid);
|
||||
|
||||
$url = Url::fromString($fqdn);
|
||||
$template = $this->application->preview_url_template;
|
||||
$host = $url->getHost();
|
||||
$schema = $url->getScheme();
|
||||
$random = new Cuid2(7);
|
||||
$preview_fqdn = str_replace('{{random}}', $random, $template);
|
||||
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
|
||||
$preview_fqdn = str_replace('{{pr_id}}', $preview->pull_request_id, $preview_fqdn);
|
||||
$preview_fqdn = "$schema://$preview_fqdn";
|
||||
$preview->fqdn = $preview_fqdn;
|
||||
$preview->save();
|
||||
$this->dispatch('success', 'Domain generated.');
|
||||
}
|
||||
public function add(int $pull_request_id, string|null $pull_request_html_url = null)
|
||||
{
|
||||
try {
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$this->setDeploymentUuid();
|
||||
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if (!$found && !is_null($pull_request_html_url)) {
|
||||
$found = ApplicationPreview::create([
|
||||
'application_id' => $this->application->id,
|
||||
'pull_request_id' => $pull_request_id,
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
'docker_compose_domains' => $this->application->docker_compose_domains,
|
||||
]);
|
||||
}
|
||||
$found->generate_preview_fqdn_compose();
|
||||
$this->application->refresh();
|
||||
} else {
|
||||
$this->setDeploymentUuid();
|
||||
$found = ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
if (!$found && !is_null($pull_request_html_url)) {
|
||||
$found = ApplicationPreview::create([
|
||||
'application_id' => $this->application->id,
|
||||
'pull_request_id' => $pull_request_id,
|
||||
'pull_request_html_url' => $pull_request_html_url,
|
||||
]);
|
||||
}
|
||||
$this->application->generate_preview_fqdn($pull_request_id);
|
||||
$this->application->refresh();
|
||||
$this->dispatch('update_links');
|
||||
$this->dispatch('success', 'Preview added.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function deploy(int $pull_request_id, string|null $pull_request_html_url = null)
|
||||
{
|
||||
try {
|
||||
@@ -82,17 +168,31 @@ class Previews extends Component
|
||||
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
||||
}
|
||||
}
|
||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
|
||||
$this->application->refresh();
|
||||
GetContainersStatus::dispatchSync($this->application->destination->server);
|
||||
$this->dispatch('reloadWindow');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function previewRefresh()
|
||||
public function delete(int $pull_request_id)
|
||||
{
|
||||
$this->application->previews->each(function ($preview) {
|
||||
$preview->refresh();
|
||||
});
|
||||
try {
|
||||
if ($this->application->destination->server->isSwarm()) {
|
||||
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
|
||||
} else {
|
||||
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
|
||||
foreach ($containers as $container) {
|
||||
$name = str_replace('/', '', $container['Names']);
|
||||
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
||||
}
|
||||
}
|
||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
|
||||
$this->application->refresh();
|
||||
$this->dispatch('update_links');
|
||||
$this->dispatch('success', 'Preview deleted.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
56
app/Livewire/Project/Application/PreviewsCompose.php
Normal file
56
app/Livewire/Project/Application/PreviewsCompose.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Models\ApplicationPreview;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class PreviewsCompose extends Component
|
||||
{
|
||||
public $service;
|
||||
public $serviceName;
|
||||
public ApplicationPreview $preview;
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.application.previews-compose');
|
||||
}
|
||||
public function save()
|
||||
{
|
||||
$domain = data_get($this->service, 'domain');
|
||||
$docker_compose_domains = data_get($this->preview, 'docker_compose_domains');
|
||||
$docker_compose_domains = json_decode($docker_compose_domains, true);
|
||||
$docker_compose_domains[$this->serviceName]['domain'] = $domain;
|
||||
$this->preview->docker_compose_domains = json_encode($docker_compose_domains);
|
||||
$this->preview->save();
|
||||
$this->dispatch('update_links');
|
||||
$this->dispatch('success', 'Domain saved.');
|
||||
}
|
||||
public function generate()
|
||||
{
|
||||
$domains = collect(json_decode($this->preview->application->docker_compose_domains)) ?? collect();
|
||||
$domain = $domains->first(function ($_, $key) {
|
||||
return $key === $this->serviceName;
|
||||
});
|
||||
if ($domain) {
|
||||
$domain = data_get($domain, 'domain');
|
||||
$url = Url::fromString($domain);
|
||||
$template = $this->preview->application->preview_url_template;
|
||||
$host = $url->getHost();
|
||||
$schema = $url->getScheme();
|
||||
$random = new Cuid2(7);
|
||||
$preview_fqdn = str_replace('{{random}}', $random, $template);
|
||||
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
|
||||
$preview_fqdn = str_replace('{{pr_id}}', $this->preview->pull_request_id, $preview_fqdn);
|
||||
$preview_fqdn = "$schema://$preview_fqdn";
|
||||
$docker_compose_domains = data_get($this->preview, 'docker_compose_domains');
|
||||
$docker_compose_domains = json_decode($docker_compose_domains, true);
|
||||
$docker_compose_domains[$this->serviceName]['domain'] = $this->service->domain = $preview_fqdn;
|
||||
$this->preview->docker_compose_domains = json_encode($docker_compose_domains);
|
||||
$this->preview->save();
|
||||
}
|
||||
$this->dispatch('update_links');
|
||||
$this->dispatch('success', 'Domain generated.');
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
namespace App\Livewire\Project\Database\Backup;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use Livewire\Component;
|
||||
|
||||
class Execution extends Component
|
||||
{
|
||||
public $database;
|
||||
public $backup;
|
||||
public ?ScheduledDatabaseBackup $backup;
|
||||
public $executions;
|
||||
public $s3s;
|
||||
public function mount()
|
||||
|
||||
@@ -7,7 +7,6 @@ use Livewire\Component;
|
||||
class Index extends Component
|
||||
{
|
||||
public $database;
|
||||
public $s3s;
|
||||
public function mount()
|
||||
{
|
||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||
@@ -36,7 +35,6 @@ class Index extends Component
|
||||
]);
|
||||
}
|
||||
$this->database = $database;
|
||||
$this->s3s = currentTeam()->s3s;
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
class BackupEdit extends Component
|
||||
{
|
||||
public $backup;
|
||||
public ?ScheduledDatabaseBackup $backup;
|
||||
public $s3s;
|
||||
public ?string $status = null;
|
||||
public array $parameters;
|
||||
@@ -35,8 +36,8 @@ class BackupEdit extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
if (is_null($this->backup->s3_storage_id)) {
|
||||
$this->backup->s3_storage_id = 'default';
|
||||
if (is_null(data_get($this->backup, 's3_storage_id'))) {
|
||||
data_set($this->backup, 's3_storage_id', 'default');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use Livewire\Component;
|
||||
|
||||
class BackupExecutions extends Component
|
||||
{
|
||||
public $backup;
|
||||
public ?ScheduledDatabaseBackup $backup = null;
|
||||
public $executions = [];
|
||||
public $setDeletableBackup;
|
||||
public function getListeners()
|
||||
@@ -20,8 +21,11 @@ class BackupExecutions extends Component
|
||||
|
||||
public function cleanupFailed()
|
||||
{
|
||||
$this->backup->executions()->where('status', 'failed')->delete();
|
||||
$this->refreshBackupExecutions();
|
||||
if ($this->backup) {
|
||||
$this->backup->executions()->where('status', 'failed')->delete();
|
||||
$this->refreshBackupExecutions();
|
||||
$this->dispatch('success', 'Failed backups cleaned up.');
|
||||
}
|
||||
}
|
||||
public function deleteBackup($exeuctionId)
|
||||
{
|
||||
@@ -45,6 +49,8 @@ class BackupExecutions extends Component
|
||||
}
|
||||
public function refreshBackupExecutions(): void
|
||||
{
|
||||
$this->executions = $this->backup->executions()->get()->sortByDesc('created_at');
|
||||
if ($this->backup) {
|
||||
$this->executions = $this->backup->executions()->get()->sortBy('created_at');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ namespace App\Livewire\Project\Database\Clickhouse;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
public Server $server;
|
||||
public StandaloneClickhouse $database;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
@@ -43,10 +45,11 @@ class General extends Component
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
$this->server = data_get($this->database,'destination.server');
|
||||
}
|
||||
public function instantSaveAdvanced() {
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
if (!$this->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;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class CreateScheduledBackup extends Component
|
||||
@@ -12,7 +13,7 @@ class CreateScheduledBackup extends Component
|
||||
public bool $enabled = true;
|
||||
public bool $save_s3 = false;
|
||||
public $s3_storage_id;
|
||||
public $s3s;
|
||||
public Collection $s3s;
|
||||
|
||||
protected $rules = [
|
||||
'frequency' => 'required|string',
|
||||
@@ -24,6 +25,7 @@ class CreateScheduledBackup extends Component
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$this->s3s = currentTeam()->s3s;
|
||||
if ($this->s3s->count() > 0) {
|
||||
$this->s3_storage_id = $this->s3s->first()->id;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Dragonfly;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
@@ -12,6 +13,7 @@ class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public Server $server;
|
||||
public StandaloneDragonfly $database;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
@@ -41,10 +43,11 @@ class General extends Component
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
$this->server = data_get($this->database,'destination.server');
|
||||
}
|
||||
public function instantSaveAdvanced() {
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
if (!$this->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;
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Actions\Database\StartMysql;
|
||||
use App\Actions\Database\StartPostgresql;
|
||||
use App\Actions\Database\StartRedis;
|
||||
use App\Actions\Database\StopDatabase;
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -44,7 +45,8 @@ class Heading extends Component
|
||||
|
||||
public function check_status($showNotification = false)
|
||||
{
|
||||
dispatch_sync(new ContainerStatusJob($this->database->destination->server));
|
||||
GetContainersStatus::run($this->database->destination->server);
|
||||
// dispatch_sync(new ContainerStatusJob($this->database->destination->server));
|
||||
$this->database->refresh();
|
||||
if ($showNotification) $this->dispatch('success', 'Database status updated.');
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Keydb;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
@@ -12,6 +13,7 @@ class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public Server $server;
|
||||
public StandaloneKeydb $database;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
@@ -43,10 +45,12 @@ class General extends Component
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
$this->server = data_get($this->database,'destination.server');
|
||||
|
||||
}
|
||||
public function instantSaveAdvanced() {
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
if (!$this->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;
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Mariadb;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
@@ -12,6 +13,7 @@ class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public Server $server;
|
||||
public StandaloneMariadb $database;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
@@ -50,10 +52,12 @@ class General extends Component
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
$this->server = data_get($this->database,'destination.server');
|
||||
|
||||
}
|
||||
public function instantSaveAdvanced() {
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
if (!$this->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;
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Mongodb;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
@@ -12,6 +13,7 @@ class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public Server $server;
|
||||
public StandaloneMongodb $database;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
@@ -48,11 +50,13 @@ class General extends Component
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
$this->server = data_get($this->database,'destination.server');
|
||||
|
||||
}
|
||||
public function instantSaveAdvanced()
|
||||
{
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
if (!$this->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;
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Mysql;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneMysql;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
@@ -13,6 +14,7 @@ class General extends Component
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public StandaloneMysql $database;
|
||||
public Server $server;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
|
||||
@@ -50,11 +52,12 @@ class General extends Component
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
$this->server = data_get($this->database,'destination.server');
|
||||
}
|
||||
public function instantSaveAdvanced()
|
||||
{
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
if (!$this->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;
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Postgresql;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
@@ -13,6 +14,7 @@ use function Aws\filter;
|
||||
class General extends Component
|
||||
{
|
||||
public StandalonePostgresql $database;
|
||||
public Server $server;
|
||||
public string $new_filename;
|
||||
public string $new_content;
|
||||
public ?string $db_url = null;
|
||||
@@ -57,11 +59,12 @@ class General extends Component
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
$this->server = data_get($this->database,'destination.server');
|
||||
}
|
||||
public function instantSaveAdvanced()
|
||||
{
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
if (!$this->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;
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Redis;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
@@ -12,6 +13,7 @@ class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public Server $server;
|
||||
public StandaloneRedis $database;
|
||||
public ?string $db_url = null;
|
||||
public ?string $db_url_public = null;
|
||||
@@ -43,10 +45,12 @@ class General extends Component
|
||||
if ($this->database->is_public) {
|
||||
$this->db_url_public = $this->database->get_db_url();
|
||||
}
|
||||
$this->server = data_get($this->database,'destination.server');
|
||||
|
||||
}
|
||||
public function instantSaveAdvanced() {
|
||||
try {
|
||||
if (!$this->database->destination->server->isLogDrainEnabled()) {
|
||||
if (!$this->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;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use Livewire\Component;
|
||||
|
||||
class ScheduledBackups extends Component
|
||||
@@ -9,7 +10,7 @@ class ScheduledBackups extends Component
|
||||
public $database;
|
||||
public $parameters;
|
||||
public $type;
|
||||
public $selectedBackup;
|
||||
public ?ScheduledDatabaseBackup $selectedBackup;
|
||||
public $selectedBackupId;
|
||||
public $s3s;
|
||||
protected $listeners = ['refreshScheduledBackups'];
|
||||
|
||||
@@ -150,7 +150,7 @@ class GithubPrivateRepository extends Component
|
||||
'repository_project_id' => $this->selected_repository_id,
|
||||
'git_repository' => "{$this->selected_repository_owner}/{$this->selected_repository_repo}",
|
||||
'git_branch' => $this->selected_branch_name,
|
||||
'build_pack' => 'nixpacks',
|
||||
'build_pack' => $this->build_pack,
|
||||
'ports_exposes' => $this->port,
|
||||
'publish_directory' => $this->publish_directory,
|
||||
'environment_id' => $environment->id,
|
||||
@@ -162,6 +162,9 @@ class GithubPrivateRepository extends Component
|
||||
$application->settings->is_static = $this->is_static;
|
||||
$application->settings->save();
|
||||
|
||||
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
|
||||
$application->health_check_enabled = false;
|
||||
}
|
||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||
$application->fqdn = $fqdn;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
public $current_step = 'private_keys';
|
||||
public $parameters;
|
||||
public $query;
|
||||
public $private_keys =[];
|
||||
public $private_keys = [];
|
||||
public int $private_key_id;
|
||||
|
||||
public int $port = 3000;
|
||||
@@ -125,7 +125,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
'name' => generate_random_name(),
|
||||
'git_repository' => $this->git_repository,
|
||||
'git_branch' => $this->branch,
|
||||
'build_pack' => 'nixpacks',
|
||||
'build_pack' => $this->build_pack,
|
||||
'ports_exposes' => $this->port,
|
||||
'publish_directory' => $this->publish_directory,
|
||||
'environment_id' => $environment->id,
|
||||
@@ -138,7 +138,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
'name' => generate_random_name(),
|
||||
'git_repository' => $this->git_repository,
|
||||
'git_branch' => $this->branch,
|
||||
'build_pack' => 'nixpacks',
|
||||
'build_pack' => $this->build_pack,
|
||||
'ports_exposes' => $this->port,
|
||||
'publish_directory' => $this->publish_directory,
|
||||
'environment_id' => $environment->id,
|
||||
@@ -149,7 +149,9 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
'source_type' => $this->git_source->getMorphClass()
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
|
||||
$application_init['health_check_enabled'] = false;
|
||||
}
|
||||
$application = Application::create($application_init);
|
||||
$application->settings->is_static = $this->is_static;
|
||||
$application->settings->save();
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Application;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\GitlabApp;
|
||||
use App\Models\Project;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Carbon\Carbon;
|
||||
@@ -33,6 +34,8 @@ class PublicGitRepository extends Component
|
||||
public $build_pack = 'nixpacks';
|
||||
public bool $show_is_static = true;
|
||||
|
||||
public bool $new_compose_services = false;
|
||||
|
||||
protected $rules = [
|
||||
'repository_url' => 'required|url',
|
||||
'port' => 'required|numeric',
|
||||
@@ -98,7 +101,8 @@ class PublicGitRepository extends Component
|
||||
(str($this->repository_url)->startsWith('https://') ||
|
||||
str($this->repository_url)->startsWith('http://')) &&
|
||||
!str($this->repository_url)->endsWith('.git') &&
|
||||
!str($this->repository_url)->contains('github.com')
|
||||
(!str($this->repository_url)->contains('github.com') ||
|
||||
!str($this->repository_url)->contains('git.sr.ht'))
|
||||
) {
|
||||
$this->repository_url = $this->repository_url . '.git';
|
||||
}
|
||||
@@ -176,6 +180,31 @@ class PublicGitRepository extends Component
|
||||
$project = Project::where('uuid', $project_uuid)->first();
|
||||
$environment = $project->load(['environments'])->environments->where('name', $environment_name)->first();
|
||||
|
||||
if ($this->build_pack === 'dockercompose' && isDev() && $this->new_compose_services ) {
|
||||
$server = $destination->server;
|
||||
$new_service = [
|
||||
'name' => 'service' . str()->random(10),
|
||||
'docker_compose_raw' => 'coolify',
|
||||
'environment_id' => $environment->id,
|
||||
'server_id' => $server->id,
|
||||
];
|
||||
if ($this->git_source === 'other') {
|
||||
$new_service['git_repository'] = $this->git_repository;
|
||||
$new_service['git_branch'] = $this->git_branch;
|
||||
} else {
|
||||
$new_service['git_repository'] = $this->git_repository;
|
||||
$new_service['git_branch'] = $this->git_branch;
|
||||
$new_service['source_id'] = $this->git_source->id;
|
||||
$new_service['source_type'] = $this->git_source->getMorphClass();
|
||||
}
|
||||
$service = Service::create($new_service);
|
||||
return redirect()->route('project.service.configuration', [
|
||||
'service_uuid' => $service->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'project_uuid' => $project->uuid,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
if ($this->git_source === 'other') {
|
||||
$application_init = [
|
||||
'name' => generate_random_name(),
|
||||
@@ -204,6 +233,9 @@ class PublicGitRepository extends Component
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->build_pack === 'dockerfile' || $this->build_pack === 'dockerimage') {
|
||||
$application_init['health_check_enabled'] = false;
|
||||
}
|
||||
$application = Application::create($application_init);
|
||||
|
||||
$application->settings->is_static = $this->is_static;
|
||||
|
||||
@@ -15,10 +15,10 @@ class Select extends Component
|
||||
public string $type;
|
||||
public string $server_id;
|
||||
public string $destination_uuid;
|
||||
public Countable|array|Server $allServers = [];
|
||||
public Countable|array|Server $servers = [];
|
||||
public Collection|array $standaloneDockers = [];
|
||||
public Collection|array $swarmDockers = [];
|
||||
public Collection|null|Server $allServers;
|
||||
public Collection|null|Server $servers;
|
||||
public ?Collection $standaloneDockers;
|
||||
public ?Collection $swarmDockers;
|
||||
public array $parameters;
|
||||
public Collection|array $services = [];
|
||||
public Collection|array $allServices = [];
|
||||
@@ -91,7 +91,7 @@ class Select extends Component
|
||||
});
|
||||
} else {
|
||||
$this->search = null;
|
||||
$this->allServices = getServiceTemplates();
|
||||
$this->allServices = get_service_templates($force);
|
||||
$this->services = $this->allServices->filter(function ($service, $key) {
|
||||
return str_contains(strtolower($key), strtolower($this->search));
|
||||
});
|
||||
@@ -107,7 +107,11 @@ class Select extends Component
|
||||
if ($this->includeSwarm) {
|
||||
$this->servers = $this->allServers;
|
||||
} else {
|
||||
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
|
||||
if ($this->allServers instanceof Collection) {
|
||||
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
|
||||
} else {
|
||||
$this->servers = $this->allServers;
|
||||
}
|
||||
}
|
||||
}
|
||||
public function setType(string $type)
|
||||
@@ -126,13 +130,21 @@ class Select extends Component
|
||||
case 'mongodb':
|
||||
$this->isDatabase = true;
|
||||
$this->includeSwarm = false;
|
||||
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
|
||||
if ($this->allServers instanceof Collection) {
|
||||
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
|
||||
} else {
|
||||
$this->servers = $this->allServers;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (str($type)->startsWith('one-click-service') || str($type)->startsWith('docker-compose-empty')) {
|
||||
$this->isDatabase = true;
|
||||
$this->includeSwarm = false;
|
||||
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
|
||||
if ($this->allServers instanceof Collection) {
|
||||
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
|
||||
} else {
|
||||
$this->servers = $this->allServers;
|
||||
}
|
||||
}
|
||||
if ($type === "existing-postgresql") {
|
||||
$this->current_step = $type;
|
||||
|
||||
@@ -10,9 +10,9 @@ use Livewire\Component;
|
||||
class Create extends Component
|
||||
{
|
||||
public $type;
|
||||
public $project;
|
||||
public function mount()
|
||||
{
|
||||
$services = getServiceTemplates();
|
||||
$type = str(request()->query('type'));
|
||||
$destination_uuid = request()->query('destination');
|
||||
$server_id = request()->query('server_id');
|
||||
@@ -21,87 +21,92 @@ class Create extends Component
|
||||
if (!$project) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->project = $project;
|
||||
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first();
|
||||
if (!$environment) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
if (in_array($type, DATABASE_TYPES)) {
|
||||
if ($type->value() === "postgresql") {
|
||||
$database = create_standalone_postgresql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'redis') {
|
||||
$database = create_standalone_redis($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mongodb') {
|
||||
$database = create_standalone_mongodb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mysql') {
|
||||
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mariadb') {
|
||||
$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', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'database_uuid' => $database->uuid,
|
||||
]);
|
||||
}
|
||||
if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) {
|
||||
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||
if ($oneClickDotEnvs) {
|
||||
$oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
|
||||
return !empty($value);
|
||||
});
|
||||
}
|
||||
if ($oneClickService) {
|
||||
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||
$service_payload = [
|
||||
'name' => "$oneClickServiceName-" . str()->random(10),
|
||||
'docker_compose_raw' => base64_decode($oneClickService),
|
||||
'environment_id' => $environment->id,
|
||||
'service_type' => $oneClickServiceName,
|
||||
'server_id' => (int) $server_id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
];
|
||||
if ($oneClickServiceName === 'cloudflared') {
|
||||
data_set($service_payload, 'connect_to_docker_network', true);
|
||||
if (isset($type) && isset($destination_uuid) && isset($server_id)) {
|
||||
$services = get_service_templates();
|
||||
|
||||
if (in_array($type, DATABASE_TYPES)) {
|
||||
if ($type->value() === "postgresql") {
|
||||
$database = create_standalone_postgresql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'redis') {
|
||||
$database = create_standalone_redis($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mongodb') {
|
||||
$database = create_standalone_mongodb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mysql') {
|
||||
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mariadb') {
|
||||
$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);
|
||||
}
|
||||
$service = Service::create($service_payload);
|
||||
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||
$service->save();
|
||||
if ($oneClickDotEnvs?->count() > 0) {
|
||||
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||
$key = str()->before($value, '=');
|
||||
$value = str(str()->after($value, '='));
|
||||
$generatedValue = $value;
|
||||
if ($value->contains('SERVICE_')) {
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
$generatedValue = generateEnvValue($command->value(), $service);
|
||||
}
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $generatedValue,
|
||||
'service_id' => $service->id,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
});
|
||||
}
|
||||
$service->parse(isNew: true);
|
||||
return redirect()->route('project.service.configuration', [
|
||||
'service_uuid' => $service->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
return redirect()->route('project.database.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'database_uuid' => $database->uuid,
|
||||
]);
|
||||
}
|
||||
if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) {
|
||||
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||
if ($oneClickDotEnvs) {
|
||||
$oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
|
||||
return !empty($value);
|
||||
});
|
||||
}
|
||||
if ($oneClickService) {
|
||||
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||
$service_payload = [
|
||||
'name' => "$oneClickServiceName-" . str()->random(10),
|
||||
'docker_compose_raw' => base64_decode($oneClickService),
|
||||
'environment_id' => $environment->id,
|
||||
'service_type' => $oneClickServiceName,
|
||||
'server_id' => (int) $server_id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
];
|
||||
if ($oneClickServiceName === 'cloudflared') {
|
||||
data_set($service_payload, 'connect_to_docker_network', true);
|
||||
}
|
||||
$service = Service::create($service_payload);
|
||||
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||
$service->save();
|
||||
if ($oneClickDotEnvs?->count() > 0) {
|
||||
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||
$key = str()->before($value, '=');
|
||||
$value = str(str()->after($value, '='));
|
||||
$generatedValue = $value;
|
||||
if ($value->contains('SERVICE_')) {
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
$generatedValue = generateEnvValue($command->value(), $service);
|
||||
}
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $generatedValue,
|
||||
'service_id' => $service->id,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
});
|
||||
}
|
||||
$service->parse(isNew: true);
|
||||
return redirect()->route('project.service.configuration', [
|
||||
'service_uuid' => $service->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'project_uuid' => $project->uuid,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$this->type = $type->value();
|
||||
}
|
||||
$this->type = $type->value();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Models\Service;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -64,9 +64,9 @@ class Configuration extends Component
|
||||
public function check_status()
|
||||
{
|
||||
try {
|
||||
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||
GetContainersStatus::run($this->service->server);
|
||||
// dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||
$this->dispatch('refresh')->self();
|
||||
$this->dispatch('updateStatus');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ class EditCompose extends Component
|
||||
protected $rules = [
|
||||
'service.docker_compose_raw' => 'required',
|
||||
'service.docker_compose' => 'required',
|
||||
'service.is_container_label_escape_enabled' => 'required',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
@@ -23,6 +24,14 @@ class EditCompose extends Component
|
||||
$this->dispatch('info', "Saving new docker compose...");
|
||||
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
$this->validate([
|
||||
'service.is_container_label_escape_enabled' => 'required',
|
||||
]);
|
||||
$this->service->save(['is_container_label_escape_enabled' => $this->service->is_container_label_escape_enabled]);
|
||||
$this->dispatch('success', "Service updated successfully");
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.edit-compose');
|
||||
|
||||
@@ -7,13 +7,20 @@ use App\Models\LocalFileVolume;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
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;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class FileStorage extends Component
|
||||
{
|
||||
public LocalFileVolume $fileStorage;
|
||||
public ServiceApplication|ServiceDatabase|StandaloneClickhouse|Application $resource;
|
||||
public ServiceApplication|StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase|Application $resource;
|
||||
public string $fs_path;
|
||||
public ?string $workdir = null;
|
||||
|
||||
@@ -27,7 +34,7 @@ class FileStorage extends Component
|
||||
{
|
||||
$this->resource = $this->fileStorage->service;
|
||||
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
|
||||
$this->workdir = $this->resource->service->workdir();
|
||||
$this->workdir = $this->resource->service?->workdir();
|
||||
$this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
|
||||
} else {
|
||||
$this->workdir = null;
|
||||
|
||||
@@ -30,7 +30,6 @@ class Navbar extends Component
|
||||
$userId = auth()->user()->id;
|
||||
return [
|
||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
|
||||
"updateStatus"=> '$refresh',
|
||||
];
|
||||
}
|
||||
public function serviceStarted()
|
||||
|
||||
@@ -42,7 +42,7 @@ class StackForm extends Component
|
||||
$this->validationAttributes["fields.$key.value"] = $fieldKey;
|
||||
}
|
||||
}
|
||||
$this->fields = $this->fields->sortDesc();
|
||||
$this->fields = $this->fields->sortBy('name');
|
||||
}
|
||||
public function saveCompose($raw)
|
||||
{
|
||||
@@ -52,7 +52,7 @@ class StackForm extends Component
|
||||
public function instantSave()
|
||||
{
|
||||
$this->service->save();
|
||||
$this->dispatch('success', 'Service settings saved.');
|
||||
$this->dispatch('success', 'Service settings saved.');
|
||||
}
|
||||
|
||||
public function submit()
|
||||
|
||||
@@ -24,6 +24,7 @@ class Danger extends Component
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
// $this->authorize('delete', $this->resource);
|
||||
$this->resource->delete();
|
||||
DeleteResourceJob::dispatch($this->resource, $this->delete_configurations);
|
||||
return redirect()->route('project.resource.index', [
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire\Project\Shared;
|
||||
|
||||
use App\Actions\Application\StopApplicationOneServer;
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Events\ApplicationStatusChanged;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Models\Server;
|
||||
@@ -90,7 +91,8 @@ class Destination extends Component
|
||||
}
|
||||
public function refreshServers()
|
||||
{
|
||||
ContainerStatusJob::dispatchSync($this->resource->destination->server);
|
||||
GetContainersStatus::run($this->resource->destination->server);
|
||||
// ContainerStatusJob::dispatchSync($this->resource->destination->server);
|
||||
$this->loadData();
|
||||
$this->dispatch('refresh');
|
||||
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||
|
||||
@@ -5,11 +5,11 @@ namespace App\Livewire\Project\Shared\EnvironmentVariable;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class All extends Component
|
||||
{
|
||||
public $resource;
|
||||
public string $resourceClass;
|
||||
public bool $showPreview = false;
|
||||
public ?string $modalId = null;
|
||||
public ?string $variables = null;
|
||||
@@ -19,17 +19,44 @@ class All extends Component
|
||||
'refreshEnvs',
|
||||
'saveKey' => 'submit',
|
||||
];
|
||||
protected $rules = [
|
||||
'resource.settings.is_env_sorting_enabled' => 'required|boolean',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$resourceClass = get_class($this->resource);
|
||||
$this->resourceClass = get_class($this->resource);
|
||||
$resourceWithPreviews = ['App\Models\Application'];
|
||||
$simpleDockerfile = !is_null(data_get($this->resource, 'dockerfile'));
|
||||
if (Str::of($resourceClass)->contains($resourceWithPreviews) && !$simpleDockerfile) {
|
||||
if (str($this->resourceClass)->contains($resourceWithPreviews) && !$simpleDockerfile) {
|
||||
$this->showPreview = true;
|
||||
}
|
||||
$this->modalId = new Cuid2(7);
|
||||
$this->sortMe();
|
||||
$this->getDevView();
|
||||
}
|
||||
|
||||
public function sortMe()
|
||||
{
|
||||
if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose') {
|
||||
if ($this->resource->settings->is_env_sorting_enabled) {
|
||||
$this->resource->environment_variables = $this->resource->environment_variables->sortBy('key');
|
||||
$this->resource->environment_variables_preview = $this->resource->environment_variables_preview->sortBy('key');
|
||||
} else {
|
||||
$this->resource->environment_variables = $this->resource->environment_variables->sortBy('id');
|
||||
$this->resource->environment_variables_preview = $this->resource->environment_variables_preview->sortBy('id');
|
||||
}
|
||||
}
|
||||
$this->getDevView();
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose') {
|
||||
$this->resource->settings->save();
|
||||
$this->dispatch('success', 'Environment variable settings updated.');
|
||||
$this->sortMe();
|
||||
}
|
||||
}
|
||||
public function getDevView()
|
||||
{
|
||||
$this->variables = $this->resource->environment_variables->map(function ($item) {
|
||||
@@ -40,7 +67,7 @@ class All extends Component
|
||||
return "$item->key=(multiline, edit in normal view)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
})->join('
|
||||
');
|
||||
if ($this->showPreview) {
|
||||
$this->variablesPreview = $this->resource->environment_variables_preview->map(function ($item) {
|
||||
@@ -51,13 +78,18 @@ class All extends Component
|
||||
return "$item->key=(multiline, edit in normal view)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
})->join('
|
||||
');
|
||||
}
|
||||
}
|
||||
public function switch()
|
||||
{
|
||||
$this->view = $this->view === 'normal' ? 'dev' : 'normal';
|
||||
if ($this->view === 'normal') {
|
||||
$this->view = 'dev';
|
||||
} else {
|
||||
$this->view = 'normal';
|
||||
}
|
||||
$this->sortMe();
|
||||
}
|
||||
public function saveVariables($isPreview)
|
||||
{
|
||||
@@ -66,6 +98,7 @@ class All extends Component
|
||||
$this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete();
|
||||
} else {
|
||||
$variables = parseEnvFormatToArray($this->variables);
|
||||
ray($variables, $this->variables);
|
||||
$this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete();
|
||||
}
|
||||
foreach ($variables as $key => $variable) {
|
||||
|
||||
@@ -122,7 +122,7 @@ class ExecuteContainerCommand extends Component
|
||||
if ($server->isForceDisabled()) {
|
||||
throw new \RuntimeException('Server is disabled.');
|
||||
}
|
||||
$cmd = 'sh -c "if [ -f ~/.profile ]; then . ~/.profile; fi; ' . str_replace('"', '\"', $this->command) . '"';
|
||||
$cmd = "sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; " . str_replace("'", "'\''", $this->command) . "'";
|
||||
if (!empty($this->workDir)) {
|
||||
$exec = "docker exec -w {$this->workDir} {$container_name} {$cmd}";
|
||||
} else {
|
||||
|
||||
@@ -43,6 +43,11 @@ class GetLogs extends Component
|
||||
$this->showTimeStamps = $this->resource->is_include_timestamps;
|
||||
}
|
||||
}
|
||||
if ($this->resource?->getMorphClass() === 'App\Models\Application') {
|
||||
if (str($this->container)->contains('-pr-')) {
|
||||
$this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public function doSomethingWithThisChunkOfOutput($output)
|
||||
@@ -77,13 +82,6 @@ class GetLogs extends Component
|
||||
if (!$this->server->isFunctional()) {
|
||||
return;
|
||||
}
|
||||
if ($this->resource?->getMorphClass() === 'App\Models\Application') {
|
||||
if (str($this->container)->contains('-pr-')) {
|
||||
$this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value();
|
||||
} else {
|
||||
$this->pull_request = 'branch';
|
||||
}
|
||||
}
|
||||
if (!$refresh && ($this->resource?->getMorphClass() === 'App\Models\Service' || str($this->container)->contains('-pr-'))) return;
|
||||
if (!$this->numberOfLines) {
|
||||
$this->numberOfLines = 1000;
|
||||
|
||||
@@ -27,7 +27,7 @@ class Logs extends Component
|
||||
public $query;
|
||||
public $status;
|
||||
public $serviceSubType;
|
||||
|
||||
public $cpu;
|
||||
public function loadContainers($server_id)
|
||||
{
|
||||
try {
|
||||
@@ -49,6 +49,14 @@ class Logs extends Component
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function loadMetrics()
|
||||
{
|
||||
return;
|
||||
$server = data_get($this->resource, 'destination.server');
|
||||
if ($server->isFunctional()) {
|
||||
$this->cpu = $server->getMetrics();
|
||||
}
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
@@ -95,6 +103,15 @@ class Logs extends Component
|
||||
}
|
||||
}
|
||||
$this->containers = $this->containers->sort();
|
||||
if (data_get($this->query,'pull_request_id')) {
|
||||
$this->containers = $this->containers->filter(function ($container) {
|
||||
return str_contains($container, $this->query['pull_request_id']);
|
||||
});
|
||||
ray($this->containers);
|
||||
|
||||
}
|
||||
|
||||
$this->loadMetrics();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class Add extends Component
|
||||
{
|
||||
public $parameters;
|
||||
public string $type;
|
||||
public Collection $containerNames;
|
||||
public string $name;
|
||||
public string $command;
|
||||
public string $frequency;
|
||||
@@ -29,6 +32,9 @@ class Add extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
if ($this->containerNames->count() > 0) {
|
||||
$this->container = $this->containerNames->first();
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
@@ -40,6 +46,11 @@ class Add extends Component
|
||||
$this->dispatch('error', 'Invalid Cron / Human expression.');
|
||||
return;
|
||||
}
|
||||
if (empty($this->container) || $this->container == 'null') {
|
||||
if ($this->type == 'service') {
|
||||
$this->container = $this->subServiceName;
|
||||
}
|
||||
}
|
||||
$this->dispatch('saveScheduledTask', [
|
||||
'name' => $this->name,
|
||||
'command' => $this->command,
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||
|
||||
use App\Models\ScheduledTask;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class All extends Component
|
||||
{
|
||||
public $resource;
|
||||
public string|null $modalId = null;
|
||||
public Collection $containerNames;
|
||||
public ?string $variables = null;
|
||||
public array $parameters;
|
||||
protected $listeners = ['refreshTasks', 'saveScheduledTask' => 'submit'];
|
||||
@@ -18,7 +17,18 @@ class All extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->modalId = new Cuid2(7);
|
||||
if ($this->resource->type() == 'service') {
|
||||
$this->containerNames = $this->resource->applications()->pluck('name');
|
||||
$this->containerNames = $this->containerNames->merge($this->resource->databases()->pluck('name'));
|
||||
} elseif ($this->resource->type() == 'application') {
|
||||
if ($this->resource->build_pack === 'dockercompose') {
|
||||
$parsed = $this->resource->parseCompose();
|
||||
$containers = collect(data_get($parsed,'services'))->keys();
|
||||
$this->containerNames = $containers;
|
||||
} else {
|
||||
$this->containerNames = collect([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function refreshTasks()
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ class Show extends Component
|
||||
public string $type;
|
||||
|
||||
protected $rules = [
|
||||
'task.enabled' => 'required|boolean',
|
||||
'task.name' => 'required|string',
|
||||
'task.command' => 'required|string',
|
||||
'task.frequency' => 'required|string',
|
||||
@@ -45,9 +46,18 @@ class Show extends Component
|
||||
$this->task = ModelsScheduledTask::where('uuid', request()->route('task_uuid'))->first();
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
$this->validateOnly('task.enabled');
|
||||
$this->task->save(['enabled' => $this->task->enabled]);
|
||||
$this->dispatch('success', 'Scheduled task updated.');
|
||||
$this->dispatch('refreshTasks');
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$this->task->name = str($this->task->name)->trim()->value();
|
||||
$this->task->container = str($this->task->container)->trim()->value();
|
||||
$this->task->save();
|
||||
$this->dispatch('success', 'Scheduled task updated.');
|
||||
$this->dispatch('refreshTasks');
|
||||
@@ -60,11 +70,9 @@ class Show extends Component
|
||||
|
||||
if ($this->type == 'application') {
|
||||
return redirect()->route('project.application.configuration', $this->parameters);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return redirect()->route('project.service.configuration', $this->parameters);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
|
||||
@@ -3,21 +3,31 @@
|
||||
namespace App\Livewire\Project\Shared\Storages;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\LocalFileVolume;
|
||||
use Livewire\Component;
|
||||
|
||||
class Add extends Component
|
||||
{
|
||||
public $resource;
|
||||
public $uuid;
|
||||
public $parameters;
|
||||
public $isSwarm = false;
|
||||
public string $name;
|
||||
public string $mount_path;
|
||||
public ?string $host_path = null;
|
||||
public string $file_storage_path;
|
||||
public ?string $file_storage_content = null;
|
||||
public string $file_storage_directory_source;
|
||||
public string $file_storage_directory_destination;
|
||||
|
||||
public $rules = [
|
||||
'name' => 'required|string',
|
||||
'mount_path' => 'required|string',
|
||||
'host_path' => 'string|nullable',
|
||||
'file_storage_path' => 'string',
|
||||
'file_storage_content' => 'nullable|string',
|
||||
'file_storage_directory_source' => 'string',
|
||||
'file_storage_directory_destination' => 'string',
|
||||
];
|
||||
|
||||
protected $listeners = ['clearAddStorage' => 'clear'];
|
||||
@@ -26,10 +36,16 @@ class Add extends Component
|
||||
'name' => 'name',
|
||||
'mount_path' => 'mount',
|
||||
'host_path' => 'host',
|
||||
'file_storage_path' => 'file storage path',
|
||||
'file_storage_content' => 'file storage content',
|
||||
'file_storage_directory_source' => 'file storage directory source',
|
||||
'file_storage_directory_destination' => 'file storage directory destination',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->file_storage_directory_source = application_configuration_dir() . "/{$this->resource->uuid}";
|
||||
$this->uuid = $this->resource->uuid;
|
||||
$this->parameters = get_route_parameters();
|
||||
if (data_get($this->parameters, 'application_uuid')) {
|
||||
$applicationUuid = $this->parameters['application_uuid'];
|
||||
@@ -43,18 +59,75 @@ class Add extends Component
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
public function submitFileStorage()
|
||||
{
|
||||
try {
|
||||
$this->validate($this->rules);
|
||||
$this->validate([
|
||||
'file_storage_path' => 'string',
|
||||
'file_storage_content' => 'nullable|string',
|
||||
]);
|
||||
$this->file_storage_path = trim($this->file_storage_path);
|
||||
$this->file_storage_path = str($this->file_storage_path)->start('/')->value();
|
||||
if ($this->resource->getMorphClass() === 'App\Models\Application') {
|
||||
$fs_path = application_configuration_dir() . '/' . $this->resource->uuid . $this->file_storage_path;
|
||||
}
|
||||
LocalFileVolume::create(
|
||||
[
|
||||
'fs_path' => $fs_path,
|
||||
'mount_path' => $this->file_storage_path,
|
||||
'content' => $this->file_storage_content,
|
||||
'is_directory' => false,
|
||||
'resource_id' => $this->resource->id,
|
||||
'resource_type' => get_class($this->resource)
|
||||
],
|
||||
);
|
||||
$this->dispatch('refresh_storages');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
}
|
||||
public function submitFileStorageDirectory()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'file_storage_directory_source' => 'string',
|
||||
'file_storage_directory_destination' => 'string',
|
||||
]);
|
||||
$this->file_storage_directory_source = trim($this->file_storage_directory_source);
|
||||
$this->file_storage_directory_source = str($this->file_storage_directory_source)->start('/')->value();
|
||||
$this->file_storage_directory_destination = trim($this->file_storage_directory_destination);
|
||||
$this->file_storage_directory_destination = str($this->file_storage_directory_destination)->start('/')->value();
|
||||
LocalFileVolume::create(
|
||||
[
|
||||
'fs_path' => $this->file_storage_directory_source,
|
||||
'mount_path' => $this->file_storage_directory_destination,
|
||||
'is_directory' => true,
|
||||
'resource_id' => $this->resource->id,
|
||||
'resource_type' => get_class($this->resource)
|
||||
],
|
||||
);
|
||||
$this->dispatch('refresh_storages');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
}
|
||||
public function submitPersistentVolume()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'name' => 'required|string',
|
||||
'mount_path' => 'required|string',
|
||||
'host_path' => 'string|nullable',
|
||||
]);
|
||||
$name = $this->uuid . '-' . $this->name;
|
||||
$this->dispatch('addNewVolume', [
|
||||
'name' => $name,
|
||||
'mount_path' => $this->mount_path,
|
||||
'host_path' => $this->host_path,
|
||||
]);
|
||||
$this->dispatch('closeStorageModal');
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ class Show extends Component
|
||||
public bool $isReadOnly = false;
|
||||
public ?string $modalId = null;
|
||||
public bool $isFirst = true;
|
||||
public bool $isService = false;
|
||||
public ?string $startedAt = null;
|
||||
|
||||
protected $rules = [
|
||||
'storage.name' => 'required|string',
|
||||
|
||||
@@ -11,10 +11,12 @@ class Webhooks extends Component
|
||||
public ?string $githubManualWebhook = null;
|
||||
public ?string $gitlabManualWebhook = null;
|
||||
public ?string $bitbucketManualWebhook = null;
|
||||
public ?string $giteaManualWebhook = null;
|
||||
protected $rules = [
|
||||
'resource.manual_webhook_secret_github' => 'nullable|string',
|
||||
'resource.manual_webhook_secret_gitlab' => 'nullable|string',
|
||||
'resource.manual_webhook_secret_bitbucket' => 'nullable|string',
|
||||
'resource.manual_webhook_secret_gitea' => 'nullable|string',
|
||||
];
|
||||
public function saveSecret()
|
||||
{
|
||||
@@ -32,6 +34,7 @@ class Webhooks extends Component
|
||||
$this->githubManualWebhook = generateGitManualWebhook($this->resource, 'github');
|
||||
$this->gitlabManualWebhook = generateGitManualWebhook($this->resource, 'gitlab');
|
||||
$this->bitbucketManualWebhook = generateGitManualWebhook($this->resource, 'bitbucket');
|
||||
$this->giteaManualWebhook = generateGitManualWebhook($this->resource, 'gitea');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
|
||||
@@ -58,6 +58,12 @@ class Form extends Component
|
||||
$this->server->refresh();
|
||||
$this->server->settings->refresh();
|
||||
}
|
||||
public function updatedServerSettingsIsBuildServer()
|
||||
{
|
||||
$this->dispatch('serverInstalled');
|
||||
$this->dispatch('serverRefresh');
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -97,6 +97,9 @@ class ByIp extends Component
|
||||
if ($this->is_swarm_worker) {
|
||||
$payload['swarm_cluster'] = $this->selected_swarm_cluster;
|
||||
}
|
||||
if ($this->is_build_server) {
|
||||
data_forget($payload, 'proxy');
|
||||
}
|
||||
$server = Server::create($payload);
|
||||
if ($this->is_build_server) {
|
||||
$this->is_swarm_manager = false;
|
||||
|
||||
@@ -72,6 +72,8 @@ class Deploy extends Component
|
||||
public function startProxy()
|
||||
{
|
||||
try {
|
||||
$this->server->proxy->force_stop = false;
|
||||
$this->server->save();
|
||||
$activity = StartProxy::run($this->server);
|
||||
$this->dispatch('activityMonitor', $activity->id, ProxyStatusChanged::class);
|
||||
} catch (\Throwable $e) {
|
||||
@@ -86,17 +88,15 @@ class Deploy extends Component
|
||||
instant_remote_process([
|
||||
"docker service rm coolify-proxy_traefik",
|
||||
], $this->server);
|
||||
$this->server->proxy->status = 'exited';
|
||||
$this->server->save();
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
} else {
|
||||
instant_remote_process([
|
||||
"docker rm -f coolify-proxy",
|
||||
], $this->server);
|
||||
$this->server->proxy->status = 'exited';
|
||||
$this->server->save();
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
}
|
||||
$this->server->proxy->status = 'exited';
|
||||
$this->server->proxy->force_stop = true;
|
||||
$this->server->save();
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Server\Proxy;
|
||||
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Models\Server;
|
||||
@@ -49,7 +50,8 @@ class Status extends Component
|
||||
public function getProxyStatus()
|
||||
{
|
||||
try {
|
||||
dispatch_sync(new ContainerStatusJob($this->server));
|
||||
GetContainersStatus::run($this->server);
|
||||
// dispatch_sync(new ContainerStatusJob($this->server));
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -129,6 +129,9 @@ class ValidateAndInstall extends Component
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->server->isBuildServer()) {
|
||||
return;
|
||||
}
|
||||
$this->dispatch('startProxy');
|
||||
}
|
||||
public function render()
|
||||
|
||||
@@ -36,7 +36,7 @@ class Backup extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->backup = $this->database?->scheduledBackups->first() ?? [];
|
||||
$this->backup = $this->database?->scheduledBackups->first() ?? null;
|
||||
$this->executions = $this->backup?->executions ?? [];
|
||||
}
|
||||
public function add_coolify_database()
|
||||
|
||||
@@ -13,7 +13,7 @@ class Configuration extends Component
|
||||
public bool $is_auto_update_enabled;
|
||||
public bool $is_registration_enabled;
|
||||
public bool $is_dns_validation_enabled;
|
||||
public bool $next_channel;
|
||||
// public bool $next_channel;
|
||||
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
|
||||
protected Server $server;
|
||||
|
||||
@@ -37,7 +37,7 @@ class Configuration extends Component
|
||||
$this->do_not_track = $this->settings->do_not_track;
|
||||
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
|
||||
$this->is_registration_enabled = $this->settings->is_registration_enabled;
|
||||
$this->next_channel = $this->settings->next_channel;
|
||||
// $this->next_channel = $this->settings->next_channel;
|
||||
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
|
||||
}
|
||||
|
||||
@@ -47,12 +47,12 @@ class Configuration extends Component
|
||||
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
|
||||
$this->settings->is_registration_enabled = $this->is_registration_enabled;
|
||||
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
|
||||
if ($this->next_channel) {
|
||||
$this->settings->next_channel = false;
|
||||
$this->next_channel = false;
|
||||
} else {
|
||||
$this->settings->next_channel = $this->next_channel;
|
||||
}
|
||||
// if ($this->next_channel) {
|
||||
// $this->settings->next_channel = false;
|
||||
// $this->next_channel = false;
|
||||
// } else {
|
||||
// $this->settings->next_channel = $this->next_channel;
|
||||
// }
|
||||
$this->settings->save();
|
||||
$this->dispatch('success', 'Settings updated!');
|
||||
}
|
||||
@@ -70,9 +70,8 @@ class Configuration extends Component
|
||||
$this->validate();
|
||||
|
||||
if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) {
|
||||
ray('asdf');
|
||||
if (!validate_dns_entry($this->settings->fqdn, $this->server)) {
|
||||
$this->dispatch('error', "Validating DNS ({$this->settings->fqdn}) failed.<br><br>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->dispatch('error', "Validating DNS failed.<br><br>Make sure you have added the DNS records correctly.<br><br>{$this->settings->fqdn}->{$this->server->ip}<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.");
|
||||
$error_show = true;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user