mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
523 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab853cac87 | ||
|
|
51db2f797d | ||
|
|
0c90d3d0a1 | ||
|
|
51efe23690 | ||
|
|
e3ee84105c | ||
|
|
6cbd61ac6c | ||
|
|
638d0c8c99 | ||
|
|
aecc81fe9d | ||
|
|
c9a1437870 | ||
|
|
66b41b3d4c | ||
|
|
c41cfe2a2f | ||
|
|
5f2ad56529 | ||
|
|
cd842bc1b2 | ||
|
|
27b6aad53a | ||
|
|
64b58b7661 | ||
|
|
94960d96a9 | ||
|
|
2549244f97 | ||
|
|
5bfffce33b | ||
|
|
3a4f19f368 | ||
|
|
50e17ed932 | ||
|
|
a8fcd7aee4 | ||
|
|
87036cc49b | ||
|
|
e48842c6ec | ||
|
|
b9e405c497 | ||
|
|
f75effe022 | ||
|
|
a745f568f3 | ||
|
|
e2e3ad0358 | ||
|
|
ba769f5fb7 | ||
|
|
0126286731 | ||
|
|
7952202435 | ||
|
|
798acb8ee5 | ||
|
|
ef595dd4c2 | ||
|
|
70c662daf8 | ||
|
|
8ae385b9f9 | ||
|
|
802a0f7684 | ||
|
|
62c38c9859 | ||
|
|
27c36bec83 | ||
|
|
c6b8eabe10 | ||
|
|
967fca9eca | ||
|
|
40a239ddda | ||
|
|
99d07981cf | ||
|
|
b3ee6b7144 | ||
|
|
468ad7d904 | ||
|
|
f1aa97e374 | ||
|
|
3b6d3343c7 | ||
|
|
ab2f9f073f | ||
|
|
67131152cc | ||
|
|
3bda289428 | ||
|
|
fadfa0ad8e | ||
|
|
11a957c6c9 | ||
|
|
b46de99af9 | ||
|
|
03420076c9 | ||
|
|
549446abdf | ||
|
|
06ab2145ca | ||
|
|
5d088e530e | ||
|
|
123e6eddd7 | ||
|
|
2c17e431ac | ||
|
|
fe6073ba7d | ||
|
|
8a9ad04744 | ||
|
|
75d1ec4f42 | ||
|
|
a0abde8652 | ||
|
|
db13dd9304 | ||
|
|
638bcf9732 | ||
|
|
b06b465ffa | ||
|
|
02c8b9f471 | ||
|
|
1ff1664b6c | ||
|
|
52d84c5e9e | ||
|
|
e0289e2949 | ||
|
|
ff8d8371ad | ||
|
|
69343f974a | ||
|
|
2dc175be63 | ||
|
|
d93bf97919 | ||
|
|
4ea8916d53 | ||
|
|
f7fca69a23 | ||
|
|
f954ee15c3 | ||
|
|
3c54e01d87 | ||
|
|
00d708610d | ||
|
|
f3b04c1ef9 | ||
|
|
6b751f965b | ||
|
|
d3c9894479 | ||
|
|
f68aace445 | ||
|
|
f042c70b3c | ||
|
|
2116d79aad | ||
|
|
4bc63e283c | ||
|
|
d5804f99c2 | ||
|
|
dfc353ce54 | ||
|
|
8f65ddb754 | ||
|
|
29e750f0b2 | ||
|
|
b24661b876 | ||
|
|
bbbd605f32 | ||
|
|
ff13cb4e26 | ||
|
|
5cb572b6a5 | ||
|
|
d8b97e06cf | ||
|
|
becb4df950 | ||
|
|
20dc2b47fe | ||
|
|
67df166c20 | ||
|
|
87c3d0048c | ||
|
|
1c71ac78e2 | ||
|
|
41181cac12 | ||
|
|
41b6df0e6e | ||
|
|
4f3f98be0a | ||
|
|
7a97a4b69c | ||
|
|
6ae87466ca | ||
|
|
5159d47159 | ||
|
|
0138d04080 | ||
|
|
c803768e5f | ||
|
|
60c8e0d625 | ||
|
|
dd99ad0af8 | ||
|
|
24a1f02af5 | ||
|
|
601a1e128e | ||
|
|
ccb9769e67 | ||
|
|
d79da996d3 | ||
|
|
4f800f5331 | ||
|
|
a19a58338c | ||
|
|
8a80dbd5d8 | ||
|
|
ec5cca7b3e | ||
|
|
ce721c1764 | ||
|
|
f4d7c4f942 | ||
|
|
40716550ec | ||
|
|
f0ee26cd86 | ||
|
|
423dfc6280 | ||
|
|
6d9a66ff1b | ||
|
|
17c8872130 | ||
|
|
3ffa2b6b8d | ||
|
|
35134f2327 | ||
|
|
8ed4b540e1 | ||
|
|
47202a7951 | ||
|
|
fe6e76ad0d | ||
|
|
e9920f05f5 | ||
|
|
57a39f12bb | ||
|
|
ba85e3bc8b | ||
|
|
1fd12832ca | ||
|
|
a8807b8d09 | ||
|
|
dbc55233cb | ||
|
|
af6d94c0d8 | ||
|
|
e022492770 | ||
|
|
5e03979f9c | ||
|
|
956416b522 | ||
|
|
2846e049fa | ||
|
|
3ffd3fc819 | ||
|
|
63dff5961e | ||
|
|
4b024017ad | ||
|
|
720bb8c478 | ||
|
|
771dc30b81 | ||
|
|
6f97af096d | ||
|
|
3d28669cad | ||
|
|
efe043ec9d | ||
|
|
6bb79e10bc | ||
|
|
ba2e4c06f1 | ||
|
|
1bfb9637ba | ||
|
|
d7e9821582 | ||
|
|
08585f7e9a | ||
|
|
3eedb43b66 | ||
|
|
26aca6a4c0 | ||
|
|
3cbf8c281d | ||
|
|
d931d57373 | ||
|
|
4e680deb93 | ||
|
|
6a6275d4fa | ||
|
|
efd9087b74 | ||
|
|
6882ce8d0f | ||
|
|
eccd41217f | ||
|
|
538de9bd81 | ||
|
|
a249ee1b1f | ||
|
|
828fec9448 | ||
|
|
205995cabe | ||
|
|
4071e096bc | ||
|
|
14bac0f0d7 | ||
|
|
69c124032c | ||
|
|
b55bd298f2 | ||
|
|
86c2415210 | ||
|
|
82f3d54bc3 | ||
|
|
a6e76dfabc | ||
|
|
c1f6bf41f5 | ||
|
|
f934dfef33 | ||
|
|
19c66c6628 | ||
|
|
d7d948caf6 | ||
|
|
1b34337fe8 | ||
|
|
718603e37e | ||
|
|
9df0a2e545 | ||
|
|
7ffebd71f3 | ||
|
|
2f286a6595 | ||
|
|
13701f6030 | ||
|
|
91f224ddea | ||
|
|
8cffae10b2 | ||
|
|
1158b2f4db | ||
|
|
f542bcf428 | ||
|
|
22178df8ae | ||
|
|
acfe1daf9b | ||
|
|
fb3f71881f | ||
|
|
0f7546a4dc | ||
|
|
6ccafacc87 | ||
|
|
852c2df12e | ||
|
|
4c752951ab | ||
|
|
f32191b889 | ||
|
|
31251ef6cb | ||
|
|
e9365aa09b | ||
|
|
e5c860319f | ||
|
|
ceedd5225f | ||
|
|
61efdfb7c1 | ||
|
|
fcef5d902e | ||
|
|
2073b8949b | ||
|
|
5c59a752e3 | ||
|
|
caf6e3a23e | ||
|
|
8eb1c4da46 | ||
|
|
bab2f391ed | ||
|
|
131c6df82a | ||
|
|
9f44d0c47a | ||
|
|
dff7ed5b7b | ||
|
|
54b6472f3b | ||
|
|
04a36e5c90 | ||
|
|
b2f851272b | ||
|
|
778915599f | ||
|
|
a6f9527157 | ||
|
|
db976709c7 | ||
|
|
7e4947ba07 | ||
|
|
e2578a7dd0 | ||
|
|
7d028b15f5 | ||
|
|
d240bfda8b | ||
|
|
d59ec2548a | ||
|
|
e0cefc787a | ||
|
|
0496198d0f | ||
|
|
cbf7f1fa41 | ||
|
|
66587d864a | ||
|
|
b106705c8d | ||
|
|
5cc22a8271 | ||
|
|
cd22863a8c | ||
|
|
118a02f70d | ||
|
|
862177d61a | ||
|
|
41280aa780 | ||
|
|
3a987c8a6e | ||
|
|
29ca461a9a | ||
|
|
d48b72c160 | ||
|
|
ce49f26c53 | ||
|
|
02989678be | ||
|
|
810b55163c | ||
|
|
ac13ba0957 | ||
|
|
a3e088199f | ||
|
|
42ee4ca032 | ||
|
|
17deff4d86 | ||
|
|
8b6323b906 | ||
|
|
a696b5271a | ||
|
|
31959f26d4 | ||
|
|
45d2f80f69 | ||
|
|
53975fcf61 | ||
|
|
76296c1f19 | ||
|
|
c25baf69e1 | ||
|
|
f952512615 | ||
|
|
c6557eada8 | ||
|
|
2c2d74c0d6 | ||
|
|
028a2eb275 | ||
|
|
ce7fad5bef | ||
|
|
cd7852e4f9 | ||
|
|
7b022a2482 | ||
|
|
12d9b6538b | ||
|
|
335788c2d6 | ||
|
|
2352e4a71d | ||
|
|
dc03179bd1 | ||
|
|
cc72f416e8 | ||
|
|
3b67d0a8de | ||
|
|
0135ba7e89 | ||
|
|
a28a28cd23 | ||
|
|
b52680a2d8 | ||
|
|
0670e6c1d6 | ||
|
|
c3882b75c1 | ||
|
|
64b6f86a36 | ||
|
|
b9efc22253 | ||
|
|
e3d9eb0154 | ||
|
|
66f3967479 | ||
|
|
c54439e84c | ||
|
|
db4a4c74fc | ||
|
|
5c7ef80219 | ||
|
|
243d1c06fc | ||
|
|
ef25f7d800 | ||
|
|
45640ffdb1 | ||
|
|
378291b209 | ||
|
|
3583e552f1 | ||
|
|
d7dfeaf988 | ||
|
|
7fe5eca661 | ||
|
|
0dff57e69f | ||
|
|
f4803ad58b | ||
|
|
2d7bbbe300 | ||
|
|
928b68043b | ||
|
|
b21add0210 | ||
|
|
c41ffd6bfb | ||
|
|
b4874c7df3 | ||
|
|
c505a6ce9c | ||
|
|
706e4b13ee | ||
|
|
4af471ee31 | ||
|
|
87062e4e22 | ||
|
|
500ba0fab8 | ||
|
|
1c72c127d5 | ||
|
|
69bb4ae5ee | ||
|
|
5f8b8bd730 | ||
|
|
44f6d93639 | ||
|
|
e35b8a0f96 | ||
|
|
b26e23e7c3 | ||
|
|
e6f7e32037 | ||
|
|
1c386db41d | ||
|
|
085b655d9f | ||
|
|
2788fcf4e1 | ||
|
|
d058e04213 | ||
|
|
066f171163 | ||
|
|
2001be07d0 | ||
|
|
39552cc42f | ||
|
|
7f5d7e0eb0 | ||
|
|
0eda49b104 | ||
|
|
636995d0e4 | ||
|
|
4c0623f022 | ||
|
|
3e2e1080f5 | ||
|
|
3f866a07d8 | ||
|
|
23571ae104 | ||
|
|
c1710c8f7b | ||
|
|
d4d2cc71a0 | ||
|
|
8d86d53292 | ||
|
|
fae97e4dee | ||
|
|
8d0c3abf2e | ||
|
|
d396f649df | ||
|
|
ec21155c9e | ||
|
|
58111f53b9 | ||
|
|
2cbe1e8489 | ||
|
|
10e5a58b9e | ||
|
|
6f886e8b6f | ||
|
|
f96a91eb31 | ||
|
|
65a1961722 | ||
|
|
c5a932ab88 | ||
|
|
d1e10dacc0 | ||
|
|
96327af838 | ||
|
|
1cb6d594d0 | ||
|
|
16261fc36e | ||
|
|
cff694b0c4 | ||
|
|
97fd56b9e4 | ||
|
|
72cfa3e7b0 | ||
|
|
3cf41e1e23 | ||
|
|
2a7a63a672 | ||
|
|
7fb9e672cf | ||
|
|
9012f6b953 | ||
|
|
407eba8b76 | ||
|
|
68f6ab5796 | ||
|
|
3dd36a2271 | ||
|
|
7f69eb3c2e | ||
|
|
6ccbf911b2 | ||
|
|
5c77cec68f | ||
|
|
25a0489f7f | ||
|
|
5e27b88bef | ||
|
|
ec98afe707 | ||
|
|
ce26127705 | ||
|
|
ef7fc1b260 | ||
|
|
f58e6766e1 | ||
|
|
4a21102983 | ||
|
|
e78b6758d8 | ||
|
|
16eb7f4fb4 | ||
|
|
4974ce6eda | ||
|
|
6cdba17aca | ||
|
|
30f8e8f232 | ||
|
|
608f0b7840 | ||
|
|
d0366c4054 | ||
|
|
f88e3c5b29 | ||
|
|
e33fec0e1a | ||
|
|
912b0a263e | ||
|
|
8f963adbd4 | ||
|
|
8f2c24d7e9 | ||
|
|
9f3dbc3cbb | ||
|
|
8a9ee84925 | ||
|
|
689480003a | ||
|
|
3b20eee909 | ||
|
|
e8cadc176b | ||
|
|
b0c96e64c9 | ||
|
|
9ce3b43e09 | ||
|
|
4c2b3df861 | ||
|
|
467471f54a | ||
|
|
60171093c5 | ||
|
|
38f2a2dac7 | ||
|
|
307ee52ac0 | ||
|
|
b66c9835b7 | ||
|
|
d38d50dca2 | ||
|
|
40023be4ea | ||
|
|
48d7c6e76f | ||
|
|
debacfe2f7 | ||
|
|
d430813230 | ||
|
|
e30c37b041 | ||
|
|
8c73068cc7 | ||
|
|
2c4e69ad50 | ||
|
|
5ae08d009e | ||
|
|
673b944647 | ||
|
|
16281248ac | ||
|
|
8670b41671 | ||
|
|
9c69044da5 | ||
|
|
ebc4ab9af5 | ||
|
|
57738198ad | ||
|
|
b8252b85b0 | ||
|
|
479c2743bd | ||
|
|
81e6482d7a | ||
|
|
88c5d87084 | ||
|
|
ccb972dcb9 | ||
|
|
6c7e091e1b | ||
|
|
91e3d33c0b | ||
|
|
aa00389824 | ||
|
|
b4e54ab3e3 | ||
|
|
8f3c5d4bd3 | ||
|
|
26668c71a1 | ||
|
|
bd7637c696 | ||
|
|
cff54f48a3 | ||
|
|
5c0f239f62 | ||
|
|
d56c28c8d9 | ||
|
|
2b666ff121 | ||
|
|
fb42c43953 | ||
|
|
81437e6822 | ||
|
|
2fe429fe92 | ||
|
|
4f0b214042 | ||
|
|
c866213f34 | ||
|
|
7cec6330cf | ||
|
|
f5de21a343 | ||
|
|
ecbfc4d790 | ||
|
|
55ff00e028 | ||
|
|
a0fc2bbb85 | ||
|
|
51a704b22a | ||
|
|
6d49678842 | ||
|
|
0459b3a115 | ||
|
|
82592c8222 | ||
|
|
25bf8895e2 | ||
|
|
f4f7bdf7d5 | ||
|
|
c008564aa3 | ||
|
|
b825d98b2d | ||
|
|
1f711d9281 | ||
|
|
1de850f640 | ||
|
|
f176247b02 | ||
|
|
3f3a1283df | ||
|
|
087bfcad08 | ||
|
|
efd2899ae3 | ||
|
|
e4b2195932 | ||
|
|
0590ed7b2e | ||
|
|
3a3c9448a4 | ||
|
|
36d65ad5a8 | ||
|
|
8db66952e8 | ||
|
|
45fa88ca4d | ||
|
|
84b74f0b57 | ||
|
|
423cf62d92 | ||
|
|
c4d9deabef | ||
|
|
776b1cb68d | ||
|
|
fc3025398e | ||
|
|
457c16c4dc | ||
|
|
ccf63c67e8 | ||
|
|
945157b30c | ||
|
|
13798392be | ||
|
|
0d05b0a3d6 | ||
|
|
e0d2f88d99 | ||
|
|
e260bfae02 | ||
|
|
5abd4a6d78 | ||
|
|
9dff1e5631 | ||
|
|
02332ade1b | ||
|
|
486de58d5b | ||
|
|
606aeb2b61 | ||
|
|
3fc264560c | ||
|
|
3dd9182281 | ||
|
|
c838ff7198 | ||
|
|
ca6db9c1a9 | ||
|
|
f27e00e80e | ||
|
|
60cf296f31 | ||
|
|
ea64e9d5ad | ||
|
|
55846c5635 | ||
|
|
7763594e6e | ||
|
|
6b5339c1c1 | ||
|
|
f2980738e4 | ||
|
|
f0e3ad0461 | ||
|
|
187050e098 | ||
|
|
9e7823795d | ||
|
|
239459dfa8 | ||
|
|
ce0f560c44 | ||
|
|
95baec99dd | ||
|
|
363e8fc0b5 | ||
|
|
e49caba920 | ||
|
|
30db2b2a09 | ||
|
|
285666e181 | ||
|
|
003934ee1d | ||
|
|
44c7958aa6 | ||
|
|
35b1a81dfe | ||
|
|
e40f397cc7 | ||
|
|
9fd8cd7e6c | ||
|
|
a94b7ee611 | ||
|
|
fc68bf50b5 | ||
|
|
0f99ee787c | ||
|
|
95777e978e | ||
|
|
fb0b9dbfed | ||
|
|
9617000daa | ||
|
|
1818404172 | ||
|
|
d9a966fd98 | ||
|
|
763ce5fc14 | ||
|
|
df021760a7 | ||
|
|
fb2598f2e4 | ||
|
|
7af07b2718 | ||
|
|
23a94c9378 | ||
|
|
ed34fc9645 | ||
|
|
cafd9e0ab2 | ||
|
|
e882477e21 | ||
|
|
db0e3cfcc4 | ||
|
|
b3c4429028 | ||
|
|
87ab4bd71e | ||
|
|
61e1fdede9 | ||
|
|
b189919f97 | ||
|
|
8f5b084931 | ||
|
|
eb96a5ae7b | ||
|
|
f0fb9dbb94 | ||
|
|
cb2d4b4a0a | ||
|
|
3aace2d4f9 | ||
|
|
8c2ed75653 | ||
|
|
8c8aafbc65 | ||
|
|
a2c39fd07e | ||
|
|
9698a051d9 | ||
|
|
51423394ba | ||
|
|
8e5e36dd5b | ||
|
|
c1dd05dcd8 | ||
|
|
dd1ce6ee6c | ||
|
|
fe4c6d396c | ||
|
|
8db54ec069 | ||
|
|
3abc720926 | ||
|
|
64cc0b63f1 | ||
|
|
c78068466b | ||
|
|
88e407756d | ||
|
|
1538116e6e | ||
|
|
aba47d58a4 | ||
|
|
e7f184dd82 | ||
|
|
2ad8d7812b | ||
|
|
8212bb99a1 |
@@ -4,3 +4,7 @@ APP_KEY=
|
|||||||
|
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
REDIS_PASSWORD=
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
|||||||
94
.github/workflows/development-build.yml
vendored
94
.github/workflows/development-build.yml
vendored
@@ -2,7 +2,7 @@ name: Development Build (v4)
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["next"]
|
branches-ignore: ["main", "v3"]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- .github/workflows/coolify-helper.yml
|
- .github/workflows/coolify-helper.yml
|
||||||
- docker/coolify-helper/Dockerfile
|
- docker/coolify-helper/Dockerfile
|
||||||
@@ -29,51 +29,51 @@ jobs:
|
|||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
aarch64:
|
aarch64:
|
||||||
runs-on: [self-hosted, arm64]
|
runs-on: [self-hosted, arm64]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/aarch64
|
platforms: linux/aarch64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
|
||||||
merge-manifest:
|
merge-manifest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
needs: [amd64, aarch64]
|
needs: [amd64, aarch64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Create & publish manifest
|
- name: Create & publish manifest
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
|
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
||||||
|
|||||||
22
.tinkerwell/snippets/DeleteUser.php
Normal file
22
.tinkerwell/snippets/DeleteUser.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
$email = 'test@example.com';
|
||||||
|
$user = User::whereEmail($email)->first();
|
||||||
|
$teams = $user->teams;
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
$servers = $team->servers;
|
||||||
|
if ($servers->count() > 0) {
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
dump($server);
|
||||||
|
$server->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dump($team);
|
||||||
|
$team->delete();
|
||||||
|
}
|
||||||
|
if ($user) {
|
||||||
|
dump($user);
|
||||||
|
$user->delete();
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ use Illuminate\Support\Facades\Mail;
|
|||||||
|
|
||||||
set_transanctional_email_settings();
|
set_transanctional_email_settings();
|
||||||
|
|
||||||
$users = User::whereEmail('andras.bacsai@gmail.com');
|
$users = User::whereEmail('test@example.com');
|
||||||
foreach ($users as $user) {
|
foreach ($users as $user) {
|
||||||
Mail::send([], [], function ($message) use ($user) {
|
Mail::send([], [], function ($message) use ($user) {
|
||||||
$message
|
$message
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ You can ask for guidance anytime on our
|
|||||||
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
||||||
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
||||||
|
|
||||||
- Run `./scripts/run setup:dev` - This will generate a secret key for you, delete any existing database layouts, migrate database to the new layout, and seed your database.
|
|
||||||
|
|
||||||
### 4) Start development
|
### 4) Start development
|
||||||
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||||
|
|
||||||
@@ -31,7 +29,6 @@ Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if y
|
|||||||
|
|
||||||
Mails are caught by Mailpit: `localhost:8025`
|
Mails are caught by Mailpit: `localhost:8025`
|
||||||
|
|
||||||
|
|
||||||
## New Service Contribution
|
## New Service Contribution
|
||||||
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
|
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
|
||||||
|
|
||||||
|
|||||||
35
README.md
35
README.md
@@ -10,35 +10,40 @@ No vendor lock-in, which means that all the configuration for your applications/
|
|||||||
|
|
||||||
For more information, take a look at our landing page [here](https://coolify.io).
|
For more information, take a look at our landing page [here](https://coolify.io).
|
||||||
|
|
||||||
> If you are looking for previous (v3) version, it is [here](https://github.com/coollabsio/coolify/tree/v3).
|
# Donations
|
||||||
|
To stay completely free, open-source, no feature behind paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the future development of the project.
|
||||||
|
|
||||||
|
https://coolify.io/sponsorships
|
||||||
|
|
||||||
|
Thank you so much!
|
||||||
|
|
||||||
# Cloud
|
# Cloud
|
||||||
|
|
||||||
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
||||||
|
|
||||||
You can easily attach your own servers, get all the automations, free email notifications, etc.
|
|
||||||
|
|
||||||
For more information & pricing, take a look at our landing page [here](https://coolify.io).
|
For more information & pricing, take a look at our landing page [here](https://coolify.io).
|
||||||
|
|
||||||
# Beta
|
## Why should I use the Cloud version?
|
||||||
|
The recommended way to use Coolify is to have one server for Coolify and one (or more) for the resources you are deploying. A server is around 4-5$/month.
|
||||||
|
|
||||||
The latest version (v4) is still in beta. That does not mean it is unstable. All the features that are available are stable enough be usable in real-life.
|
By subscribing to the cloud version, you get the Coolify server for the same price, but with:
|
||||||
|
- High-availability
|
||||||
There are hundreds of people using it for managing their client's applications, freelancers, hobbyists, businesses.
|
- Free email notifications
|
||||||
|
- Better support
|
||||||
|
- Less maintenance for you
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||||
```
|
```
|
||||||
|
You can find the installation script source [here](./scripts/install.sh).
|
||||||
|
|
||||||
You can find the installation script [here](./scripts/install.sh).
|
# Support
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
Contact us [here](https://coolify.io/docs/contact).
|
Contact us [here](https://coolify.io/docs/contact).
|
||||||
|
|
||||||
## Recognitions
|
# Recognitions
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://news.ycombinator.com/item?id=26624341">
|
<a href="https://news.ycombinator.com/item?id=26624341">
|
||||||
@@ -54,11 +59,11 @@ Contact us [here](https://coolify.io/docs/contact).
|
|||||||
|
|
||||||
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
## 💰 Financial Contributors
|
# 💰 Financial Contributors
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
||||||
|
|
||||||
### Organizations
|
## Organizations
|
||||||
|
|
||||||
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
||||||
|
|
||||||
@@ -78,10 +83,10 @@ Support this project with your organization. Your logo will show up here with a
|
|||||||
<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/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>
|
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
||||||
|
|
||||||
### Individuals
|
## Individuals
|
||||||
|
|
||||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
||||||
|
|
||||||
## Star History
|
# Star History
|
||||||
|
|
||||||
[](https://star-history.com/#coollabsio/coolify&Date)
|
[](https://star-history.com/#coollabsio/coolify&Date)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Actions\Application;
|
namespace App\Actions\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Notifications\Application\StatusChanged;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StopApplication
|
class StopApplication
|
||||||
@@ -12,19 +11,34 @@ class StopApplication
|
|||||||
public function handle(Application $application)
|
public function handle(Application $application)
|
||||||
{
|
{
|
||||||
$server = $application->destination->server;
|
$server = $application->destination->server;
|
||||||
$containers = getCurrentApplicationContainerStatus($server, $application->id);
|
if ($server->isSwarm()) {
|
||||||
if ($containers->count() > 0) {
|
instant_remote_process(["docker stack rm {$application->uuid}" ], $server);
|
||||||
foreach ($containers as $container) {
|
} else {
|
||||||
$containerName = data_get($container, 'Names');
|
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
||||||
if ($containerName) {
|
if ($containers->count() > 0) {
|
||||||
instant_remote_process(
|
foreach ($containers as $container) {
|
||||||
["docker rm -f {$containerName}"],
|
$containerName = data_get($container, 'Names');
|
||||||
$server
|
if ($containerName) {
|
||||||
);
|
instant_remote_process(
|
||||||
|
["docker rm -f {$containerName}"],
|
||||||
|
$server
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: make notification for application
|
||||||
|
// $application->environment->project->team->notify(new StatusChanged($application));
|
||||||
|
}
|
||||||
|
// Delete Preview Deployments
|
||||||
|
$previewDeployments = $application->previews;
|
||||||
|
foreach ($previewDeployments as $previewDeployment) {
|
||||||
|
$containers = getCurrentApplicationContainerStatus($server, $application->id, $previewDeployment->pull_request_id);
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$name = str_replace('/', '', $container['Names']);
|
||||||
|
instant_remote_process(["docker rm -f $name"], $application->destination->server, throwError: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: make notification for application
|
|
||||||
// $application->environment->project->team->notify(new StatusChanged($application));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class PrepareCoolifyTask
|
|||||||
|
|
||||||
public function __invoke(): Activity
|
public function __invoke(): Activity
|
||||||
{
|
{
|
||||||
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors);
|
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish);
|
||||||
dispatch($job);
|
dispatch($job);
|
||||||
$this->activity->refresh();
|
$this->activity->refresh();
|
||||||
return $this->activity;
|
return $this->activity;
|
||||||
|
|||||||
@@ -17,24 +17,24 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
public bool $hide_from_output;
|
public bool $hide_from_output;
|
||||||
|
|
||||||
public bool $is_finished;
|
|
||||||
|
|
||||||
public bool $ignore_errors;
|
public bool $ignore_errors;
|
||||||
|
|
||||||
|
public $call_event_on_finish = null;
|
||||||
|
|
||||||
protected $time_start;
|
protected $time_start;
|
||||||
|
|
||||||
protected $current_time;
|
protected $current_time;
|
||||||
|
|
||||||
protected $last_write_at = 0;
|
protected $last_write_at = 0;
|
||||||
|
|
||||||
protected $throttle_interval_ms = 500;
|
protected $throttle_interval_ms = 200;
|
||||||
|
|
||||||
protected int $counter = 1;
|
protected int $counter = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*/
|
*/
|
||||||
public function __construct(Activity $activity, bool $hide_from_output = false, bool $is_finished = false, bool $ignore_errors = false)
|
public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) {
|
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) {
|
||||||
@@ -43,8 +43,8 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
$this->activity = $activity;
|
$this->activity = $activity;
|
||||||
$this->hide_from_output = $hide_from_output;
|
$this->hide_from_output = $hide_from_output;
|
||||||
$this->is_finished = $is_finished;
|
|
||||||
$this->ignore_errors = $ignore_errors;
|
$this->ignore_errors = $ignore_errors;
|
||||||
|
$this->call_event_on_finish = $call_event_on_finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function decodeOutput(?Activity $activity = null): string
|
public static function decodeOutput(?Activity $activity = null): string
|
||||||
@@ -74,17 +74,29 @@ class RunRemoteProcess
|
|||||||
$this->time_start = hrtime(true);
|
$this->time_start = hrtime(true);
|
||||||
|
|
||||||
$status = ProcessStatus::IN_PROGRESS;
|
$status = ProcessStatus::IN_PROGRESS;
|
||||||
$processResult = Process::forever()->run($this->getCommand(), $this->handleOutput(...));
|
$timeout = config('constants.ssh.command_timeout');
|
||||||
|
$process = Process::timeout($timeout)->start($this->getCommand(), $this->handleOutput(...));
|
||||||
|
$this->activity->properties = $this->activity->properties->merge([
|
||||||
|
'process_id' => $process->id(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$processResult = $process->wait();
|
||||||
|
// $processResult = Process::timeout($timeout)->run($this->getCommand(), $this->handleOutput(...));
|
||||||
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
|
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
|
||||||
$status = ProcessStatus::ERROR;
|
$status = ProcessStatus::ERROR;
|
||||||
} else {
|
} else {
|
||||||
if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
|
if ($processResult->exitCode() == 0) {
|
||||||
$status = ProcessStatus::FINISHED;
|
$status = ProcessStatus::FINISHED;
|
||||||
}
|
}
|
||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
$status = ProcessStatus::ERROR;
|
$status = ProcessStatus::ERROR;
|
||||||
}
|
}
|
||||||
|
// if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
|
||||||
|
// $status = ProcessStatus::FINISHED;
|
||||||
|
// }
|
||||||
|
// if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
|
// $status = ProcessStatus::ERROR;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->activity->properties = $this->activity->properties->merge([
|
$this->activity->properties = $this->activity->properties->merge([
|
||||||
@@ -97,7 +109,15 @@ class RunRemoteProcess
|
|||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
||||||
}
|
}
|
||||||
|
if ($this->call_event_on_finish) {
|
||||||
|
try {
|
||||||
|
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
||||||
|
'userId' => $this->activity->causer_id,
|
||||||
|
]));
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
return $processResult;
|
return $processResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +137,6 @@ class RunRemoteProcess
|
|||||||
}
|
}
|
||||||
$this->current_time = $this->elapsedTime();
|
$this->current_time = $this->elapsedTime();
|
||||||
$this->activity->description = $this->encodeOutput($type, $output);
|
$this->activity->description = $this->encodeOutput($type, $output);
|
||||||
|
|
||||||
if ($this->isAfterLastThrottle()) {
|
if ($this->isAfterLastThrottle()) {
|
||||||
// Let's write to database.
|
// Let's write to database.
|
||||||
DB::transaction(function () {
|
DB::transaction(function () {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -14,21 +15,53 @@ class StartDatabaseProxy
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
||||||
{
|
{
|
||||||
$internalPort = null;
|
$internalPort = null;
|
||||||
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
|
$type = $database->getMorphClass();
|
||||||
|
$network = data_get($database, 'destination.network');
|
||||||
|
$server = data_get($database, 'destination.server');
|
||||||
|
$containerName = data_get($database, 'uuid');
|
||||||
|
$proxyContainerName = "{$database->uuid}-proxy";
|
||||||
|
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
|
$databaseType = $database->databaseType();
|
||||||
|
$network = data_get($database, 'service.destination.network');
|
||||||
|
$server = data_get($database, 'service.destination.server');
|
||||||
|
$proxyContainerName = "{$database->service->uuid}-proxy";
|
||||||
|
switch ($databaseType) {
|
||||||
|
case 'standalone-mariadb':
|
||||||
|
$type = 'App\Models\StandaloneMariadb';
|
||||||
|
$containerName = "mariadb-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
|
case 'standalone-mongodb':
|
||||||
|
$type = 'App\Models\StandaloneMongodb';
|
||||||
|
$containerName = "mongodb-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
|
case 'standalone-mysql':
|
||||||
|
$type = 'App\Models\StandaloneMysql';
|
||||||
|
$containerName = "mysql-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
|
case 'standalone-postgresql':
|
||||||
|
$type = 'App\Models\StandalonePostgresql';
|
||||||
|
$containerName = "postgresql-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
|
case 'standalone-redis':
|
||||||
|
$type = 'App\Models\StandaloneRedis';
|
||||||
|
$containerName = "redis-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($type === 'App\Models\StandaloneRedis') {
|
||||||
$internalPort = 6379;
|
$internalPort = 6379;
|
||||||
} else if ($database->getMorphClass() === 'App\Models\StandalonePostgresql') {
|
} else if ($type === 'App\Models\StandalonePostgresql') {
|
||||||
$internalPort = 5432;
|
$internalPort = 5432;
|
||||||
} else if ($database->getMorphClass() === 'App\Models\StandaloneMongodb') {
|
} else if ($type === 'App\Models\StandaloneMongodb') {
|
||||||
$internalPort = 27017;
|
$internalPort = 27017;
|
||||||
} else if ($database->getMorphClass() === 'App\Models\StandaloneMysql') {
|
} else if ($type === 'App\Models\StandaloneMysql') {
|
||||||
$internalPort = 3306;
|
$internalPort = 3306;
|
||||||
} else if ($database->getMorphClass() === 'App\Models\StandaloneMariadb') {
|
} else if ($type === 'App\Models\StandaloneMariadb') {
|
||||||
$internalPort = 3306;
|
$internalPort = 3306;
|
||||||
}
|
}
|
||||||
$containerName = "{$database->uuid}-proxy";
|
|
||||||
$configuration_dir = database_proxy_dir($database->uuid);
|
$configuration_dir = database_proxy_dir($database->uuid);
|
||||||
$nginxconf = <<<EOF
|
$nginxconf = <<<EOF
|
||||||
user nginx;
|
user nginx;
|
||||||
@@ -42,7 +75,7 @@ class StartDatabaseProxy
|
|||||||
stream {
|
stream {
|
||||||
server {
|
server {
|
||||||
listen $database->public_port;
|
listen $database->public_port;
|
||||||
proxy_pass $database->uuid:$internalPort;
|
proxy_pass $containerName:$internalPort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF;
|
EOF;
|
||||||
@@ -54,19 +87,19 @@ class StartDatabaseProxy
|
|||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
$containerName => [
|
$proxyContainerName => [
|
||||||
'build' => [
|
'build' => [
|
||||||
'context' => $configuration_dir,
|
'context' => $configuration_dir,
|
||||||
'dockerfile' => 'Dockerfile',
|
'dockerfile' => 'Dockerfile',
|
||||||
],
|
],
|
||||||
'image' => "nginx:stable-alpine",
|
'image' => "nginx:stable-alpine",
|
||||||
'container_name' => $containerName,
|
'container_name' => $proxyContainerName,
|
||||||
'restart' => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
'ports' => [
|
'ports' => [
|
||||||
"$database->public_port:$database->public_port",
|
"$database->public_port:$database->public_port",
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$database->destination->network,
|
$network,
|
||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => [
|
'test' => [
|
||||||
@@ -81,9 +114,9 @@ class StartDatabaseProxy
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$database->destination->network => [
|
$network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $database->destination->network,
|
'name' => $network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@@ -96,7 +129,8 @@ class StartDatabaseProxy
|
|||||||
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||||
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
||||||
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
||||||
|
"docker compose --project-directory {$configuration_dir} pull",
|
||||||
"docker compose --project-directory {$configuration_dir} up --build -d",
|
"docker compose --project-directory {$configuration_dir} up --build -d",
|
||||||
], $database->destination->server);
|
], $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class StartMariadb
|
|||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo '####### Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
"mkdir -p $this->configuration_dir",
|
"mkdir -p $this->configuration_dir",
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ class StartMariadb
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => $this->database->limits_cpus,
|
'cpus' => (int) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
@@ -69,6 +69,16 @@ class StartMariadb
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
@@ -91,9 +101,11 @@ class StartMariadb
|
|||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '####### {$database->name} started.'";
|
$this->commands[] = "echo '{$database->name} started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class StartMongodb
|
|||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo '####### Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
"mkdir -p $this->configuration_dir",
|
"mkdir -p $this->configuration_dir",
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ class StartMongodb
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => $this->database->limits_cpus,
|
'cpus' => (int) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
@@ -76,6 +76,16 @@ class StartMongodb
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
@@ -107,9 +117,11 @@ class StartMongodb
|
|||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '####### {$database->name} started.'";
|
$this->commands[] = "echo '{$database->name} started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server,callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class StartMysql
|
|||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo '####### Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
"mkdir -p $this->configuration_dir",
|
"mkdir -p $this->configuration_dir",
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ class StartMysql
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => $this->database->limits_cpus,
|
'cpus' => (int) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
@@ -69,6 +69,16 @@ class StartMysql
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
@@ -91,9 +101,11 @@ class StartMysql
|
|||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '####### {$database->name} started.'";
|
$this->commands[] = "echo '{$database->name} started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server,callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class StartPostgresql
|
|||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo '####### Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
"mkdir -p $this->configuration_dir",
|
"mkdir -p $this->configuration_dir",
|
||||||
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/"
|
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/"
|
||||||
];
|
];
|
||||||
@@ -32,6 +32,8 @@ class StartPostgresql
|
|||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->generate_init_scripts();
|
$this->generate_init_scripts();
|
||||||
|
$this->add_custom_conf();
|
||||||
|
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
@@ -64,7 +66,7 @@ class StartPostgresql
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => $this->database->limits_cpus,
|
'cpus' => (int) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
@@ -77,6 +79,17 @@ class StartPostgresql
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
ray('Log Drain Enabled');
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
@@ -96,14 +109,29 @@ class StartPostgresql
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!is_null($this->database->postgres_conf)) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
|
'type' => 'bind',
|
||||||
|
'source' => $this->configuration_dir . '/custom-postgres.conf',
|
||||||
|
'target' => '/etc/postgresql/postgresql.conf',
|
||||||
|
'read_only' => true,
|
||||||
|
];
|
||||||
|
$docker_compose['services'][$container_name]['command'] = [
|
||||||
|
'postgres',
|
||||||
|
'-c',
|
||||||
|
'config_file=/etc/postgresql/postgresql.conf',
|
||||||
|
];
|
||||||
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '####### {$database->name} started.'";
|
$this->commands[] = "echo '{$database->name} started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -171,4 +199,14 @@ class StartPostgresql
|
|||||||
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private function add_custom_conf()
|
||||||
|
{
|
||||||
|
if (is_null($this->database->postgres_conf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$filename = 'custom-postgres.conf';
|
||||||
|
$content = $this->database->postgres_conf;
|
||||||
|
$content_base64 = base64_encode($content);
|
||||||
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class StartRedis
|
|||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo '####### Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
"mkdir -p $this->configuration_dir",
|
"mkdir -p $this->configuration_dir",
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ class StartRedis
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => $this->database->limits_cpus,
|
'cpus' => (int) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
@@ -78,6 +78,16 @@ class StartRedis
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
|
'fluentd-async' => "true",
|
||||||
|
'fluentd-sub-second-precision' => "true",
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
}
|
}
|
||||||
@@ -101,9 +111,11 @@ class StartRedis
|
|||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo '####### {$database->name} started.'";
|
$this->commands[] = "echo '{$database->name} started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -154,6 +166,5 @@ class StartRedis
|
|||||||
$content = $this->database->redis_conf;
|
$content = $this->database->redis_conf;
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Events\DatabaseStatusChanged;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -13,9 +14,13 @@ class StopDatabaseProxy
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
||||||
{
|
{
|
||||||
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server);
|
$server = data_get($database, 'destination.server');
|
||||||
|
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
|
$server = data_get($database, 'service.server');
|
||||||
|
}
|
||||||
|
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $server);
|
||||||
$database->is_public = false;
|
$database->is_public = false;
|
||||||
$database->save();
|
$database->save();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,35 +17,45 @@ class CheckProxy
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$status = getContainerStatus($server, 'coolify-proxy');
|
if ($server->isSwarm()) {
|
||||||
if ($status === 'running') {
|
$status = getContainerStatus($server, 'coolify-proxy_traefik');
|
||||||
$server->proxy->set('status', 'running');
|
$server->proxy->set('status', $status);
|
||||||
$server->save();
|
$server->save();
|
||||||
return false;
|
if ($status === 'running') {
|
||||||
}
|
return false;
|
||||||
$ip = $server->ip;
|
}
|
||||||
if ($server->id === 0) {
|
return true;
|
||||||
$ip = 'host.docker.internal';
|
} else {
|
||||||
}
|
$status = getContainerStatus($server, 'coolify-proxy');
|
||||||
|
if ($status === 'running') {
|
||||||
|
$server->proxy->set('status', 'running');
|
||||||
|
$server->save();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$ip = $server->ip;
|
||||||
|
if ($server->id === 0) {
|
||||||
|
$ip = 'host.docker.internal';
|
||||||
|
}
|
||||||
|
|
||||||
$connection80 = @fsockopen($ip, '80');
|
$connection80 = @fsockopen($ip, '80');
|
||||||
$connection443 = @fsockopen($ip, '443');
|
$connection443 = @fsockopen($ip, '443');
|
||||||
$port80 = is_resource($connection80) && fclose($connection80);
|
$port80 = is_resource($connection80) && fclose($connection80);
|
||||||
$port443 = is_resource($connection443) && fclose($connection443);
|
$port443 = is_resource($connection443) && fclose($connection443);
|
||||||
if ($port80) {
|
if ($port80) {
|
||||||
if ($fromUI) {
|
if ($fromUI) {
|
||||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if ($port443) {
|
||||||
if ($port443) {
|
if ($fromUI) {
|
||||||
if ($fromUI) {
|
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
} else {
|
||||||
} else {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class StartProxy
|
|||||||
public function handle(Server $server, bool $async = true): string|Activity
|
public function handle(Server $server, bool $async = true): string|Activity
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
||||||
$proxyType = $server->proxyType();
|
$proxyType = $server->proxyType();
|
||||||
$commands = collect([]);
|
$commands = collect([]);
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
@@ -24,18 +25,29 @@ class StartProxy
|
|||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
$server->save();
|
$server->save();
|
||||||
$commands = $commands->merge([
|
if ($server->isSwarm()) {
|
||||||
"mkdir -p $proxy_path && cd $proxy_path",
|
$commands = $commands->merge([
|
||||||
"echo 'Creating required Docker Compose file.'",
|
"mkdir -p $proxy_path && cd $proxy_path",
|
||||||
"echo 'Pulling docker image.'",
|
"echo 'Creating required Docker Compose file.'",
|
||||||
'docker compose pull',
|
"echo 'Starting coolify-proxy.'",
|
||||||
"echo 'Stopping existing coolify-proxy.'",
|
"cd $proxy_path && docker stack deploy -c docker-compose.yml coolify-proxy",
|
||||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
"echo 'Proxy started successfully.'"
|
||||||
"echo 'Starting coolify-proxy.'",
|
]);
|
||||||
'docker compose up -d --remove-orphans',
|
} else {
|
||||||
"echo 'Proxy started successfully.'"
|
$commands = $commands->merge([
|
||||||
]);
|
"mkdir -p $proxy_path && cd $proxy_path",
|
||||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
"echo 'Creating required Docker Compose file.'",
|
||||||
|
"echo 'Pulling docker image.'",
|
||||||
|
'docker compose pull',
|
||||||
|
"echo 'Stopping existing coolify-proxy.'",
|
||||||
|
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||||
|
"echo 'Starting coolify-proxy.'",
|
||||||
|
'docker compose up -d --remove-orphans',
|
||||||
|
"echo 'Proxy started successfully.'"
|
||||||
|
]);
|
||||||
|
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||||
|
}
|
||||||
|
|
||||||
if ($async) {
|
if ($async) {
|
||||||
$activity = remote_process($commands, $server);
|
$activity = remote_process($commands, $server);
|
||||||
return $activity;
|
return $activity;
|
||||||
@@ -46,11 +58,9 @@ class StartProxy
|
|||||||
$server->save();
|
$server->save();
|
||||||
return 'OK';
|
return 'OK';
|
||||||
}
|
}
|
||||||
} catch(\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ class InstallDocker
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Server $server)
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
|
$supported_os_type = $server->validateOS();
|
||||||
|
if (!$supported_os_type) {
|
||||||
|
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/servers#install-docker-engine-manually">documentation</a>.');
|
||||||
|
}
|
||||||
|
ray('Installing Docker on server: ' . $server->name . ' (' . $server->ip . ')' . ' with OS type: ' . $supported_os_type);
|
||||||
$dockerVersion = '24.0';
|
$dockerVersion = '24.0';
|
||||||
$config = base64_encode('{
|
$config = base64_encode('{
|
||||||
"log-driver": "json-file",
|
"log-driver": "json-file",
|
||||||
@@ -27,36 +32,65 @@ class InstallDocker
|
|||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
$command = collect([]);
|
||||||
if (isDev() && $server->id === 0) {
|
if (isDev() && $server->id === 0) {
|
||||||
$command = [
|
$command = $command->merge([
|
||||||
"echo '####### Installing Prerequisites...'",
|
"echo 'Installing Prerequisites...'",
|
||||||
"sleep 1",
|
"sleep 1",
|
||||||
"echo '####### Installing/updating Docker Engine...'",
|
"echo 'Installing Docker Engine...'",
|
||||||
"echo '####### Configuring Docker Engine (merging existing configuration with the required)...'",
|
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||||
"sleep 4",
|
"sleep 4",
|
||||||
"echo '####### Restarting Docker Engine...'",
|
"echo 'Restarting Docker Engine...'",
|
||||||
"ls -l /tmp"
|
"ls -l /tmp"
|
||||||
];
|
]);
|
||||||
} else {
|
} else {
|
||||||
$command = [
|
if ($supported_os_type->contains('debian')) {
|
||||||
"echo '####### Installing Prerequisites...'",
|
$command = $command->merge([
|
||||||
"command -v jq >/dev/null || apt-get update",
|
"echo 'Installing Prerequisites...'",
|
||||||
"command -v jq >/dev/null || apt install -y jq",
|
"command -v jq >/dev/null || apt-get update -y",
|
||||||
"echo '####### Installing/updating Docker Engine...'",
|
"command -v jq >/dev/null || apt install -y curl wget git jq",
|
||||||
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh",
|
|
||||||
"echo '####### Configuring Docker Engine (merging existing configuration with the required)...'",
|
]);
|
||||||
|
} else if ($supported_os_type->contains('rhel')) {
|
||||||
|
$command = $command->merge([
|
||||||
|
"echo 'Installing Prerequisites...'",
|
||||||
|
"command -v jq >/dev/null || dnf install -y curl wget git jq",
|
||||||
|
]);
|
||||||
|
} else if ($supported_os_type->contains('sles')) {
|
||||||
|
$command = $command->merge([
|
||||||
|
"echo 'Installing Prerequisites...'",
|
||||||
|
"command -v jq >/dev/null || zypper update -y",
|
||||||
|
"command -v jq >/dev/null || zypper install -y curl wget git jq",
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
throw new \Exception('Unsupported OS');
|
||||||
|
}
|
||||||
|
$command = $command->merge([
|
||||||
|
"echo 'Installing Docker Engine...'",
|
||||||
|
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}",
|
||||||
|
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||||
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
|
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
|
||||||
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
||||||
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
|
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
|
||||||
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
|
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
|
||||||
"echo '####### Restarting Docker Engine...'",
|
"echo 'Restarting Docker Engine...'",
|
||||||
|
"systemctl enable docker >/dev/null 2>&1 || true",
|
||||||
"systemctl restart docker",
|
"systemctl restart docker",
|
||||||
"echo '####### Creating default Docker network (coolify)...'",
|
]);
|
||||||
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
if ($server->isSwarm()) {
|
||||||
"echo '####### Done!'"
|
$command = $command->merge([
|
||||||
];
|
"docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true",
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$command = $command->merge([
|
||||||
|
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
||||||
|
]);
|
||||||
|
$command = $command->merge([
|
||||||
|
"echo 'Done!'",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return remote_process($command, $server);
|
||||||
}
|
}
|
||||||
return remote_process($command, $server);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
211
app/Actions/Server/InstallLogDrain.php
Normal file
211
app/Actions/Server/InstallLogDrain.php
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Server;
|
||||||
|
|
||||||
|
class InstallLogDrain
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Server $server)
|
||||||
|
{
|
||||||
|
if ($server->settings->is_logdrain_newrelic_enabled) {
|
||||||
|
$type = 'newrelic';
|
||||||
|
} else if ($server->settings->is_logdrain_highlight_enabled) {
|
||||||
|
$type = 'highlight';
|
||||||
|
} else if ($server->settings->is_logdrain_axiom_enabled) {
|
||||||
|
$type = 'axiom';
|
||||||
|
} else if ($server->settings->is_logdrain_custom_enabled) {
|
||||||
|
$type = 'custom';
|
||||||
|
} else {
|
||||||
|
$type = 'none';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if ($type === 'none') {
|
||||||
|
$command = [
|
||||||
|
"echo 'Stopping old Fluent Bit'",
|
||||||
|
"docker rm -f coolify-log-drain || true",
|
||||||
|
];
|
||||||
|
return instant_remote_process($command, $server);
|
||||||
|
} else if ($type === 'newrelic') {
|
||||||
|
if (!$server->settings->is_logdrain_newrelic_enabled) {
|
||||||
|
throw new \Exception('New Relic log drain is not enabled.');
|
||||||
|
}
|
||||||
|
$config = base64_encode("
|
||||||
|
[SERVICE]
|
||||||
|
Flush 5
|
||||||
|
Daemon off
|
||||||
|
Tag container_logs
|
||||||
|
Log_Level debug
|
||||||
|
Parsers_File parsers.conf
|
||||||
|
[INPUT]
|
||||||
|
Name forward
|
||||||
|
Buffer_Chunk_Size 1M
|
||||||
|
Buffer_Max_Size 6M
|
||||||
|
[FILTER]
|
||||||
|
Name grep
|
||||||
|
Match *
|
||||||
|
Exclude log 127.0.0.1
|
||||||
|
[FILTER]
|
||||||
|
Name modify
|
||||||
|
Match *
|
||||||
|
Set server_name {$server->name}
|
||||||
|
[OUTPUT]
|
||||||
|
Name nrlogs
|
||||||
|
Match *
|
||||||
|
license_key \${LICENSE_KEY}
|
||||||
|
# https://log-api.eu.newrelic.com/log/v1 - EU
|
||||||
|
# https://log-api.newrelic.com/log/v1 - US
|
||||||
|
base_uri \${BASE_URI}
|
||||||
|
");
|
||||||
|
} else if ($type === 'highlight') {
|
||||||
|
if (!$server->settings->is_logdrain_highlight_enabled) {
|
||||||
|
throw new \Exception('Highlight log drain is not enabled.');
|
||||||
|
}
|
||||||
|
$config = base64_encode("
|
||||||
|
[SERVICE]
|
||||||
|
Flush 5
|
||||||
|
Daemon off
|
||||||
|
Log_Level debug
|
||||||
|
Parsers_File parsers.conf
|
||||||
|
[INPUT]
|
||||||
|
Name forward
|
||||||
|
tag \${HIGHLIGHT_PROJECT_ID}
|
||||||
|
Buffer_Chunk_Size 1M
|
||||||
|
Buffer_Max_Size 6M
|
||||||
|
[OUTPUT]
|
||||||
|
Name forward
|
||||||
|
Match *
|
||||||
|
Host otel.highlight.io
|
||||||
|
Port 24224
|
||||||
|
");
|
||||||
|
} else if ($type === 'axiom') {
|
||||||
|
if (!$server->settings->is_logdrain_axiom_enabled) {
|
||||||
|
throw new \Exception('Axiom log drain is not enabled.');
|
||||||
|
}
|
||||||
|
$config = base64_encode("
|
||||||
|
[SERVICE]
|
||||||
|
Flush 5
|
||||||
|
Daemon off
|
||||||
|
Log_Level debug
|
||||||
|
Parsers_File parsers.conf
|
||||||
|
[INPUT]
|
||||||
|
Name forward
|
||||||
|
Buffer_Chunk_Size 1M
|
||||||
|
Buffer_Max_Size 6M
|
||||||
|
[FILTER]
|
||||||
|
Name grep
|
||||||
|
Match *
|
||||||
|
Exclude log 127.0.0.1
|
||||||
|
[FILTER]
|
||||||
|
Name modify
|
||||||
|
Match *
|
||||||
|
Set server_name {$server->name}
|
||||||
|
[OUTPUT]
|
||||||
|
Name http
|
||||||
|
Match *
|
||||||
|
Host api.axiom.co
|
||||||
|
Port 443
|
||||||
|
URI /v1/datasets/\${AXIOM_DATASET_NAME}/ingest
|
||||||
|
# Authorization Bearer should be an API token
|
||||||
|
Header Authorization Bearer \${AXIOM_API_KEY}
|
||||||
|
compress gzip
|
||||||
|
format json
|
||||||
|
json_date_key _time
|
||||||
|
json_date_format iso8601
|
||||||
|
tls On
|
||||||
|
");
|
||||||
|
} else if ($type === 'custom') {
|
||||||
|
if (!$server->settings->is_logdrain_custom_enabled) {
|
||||||
|
throw new \Exception('Custom log drain is not enabled.');
|
||||||
|
}
|
||||||
|
$config = base64_encode($server->settings->logdrain_custom_config);
|
||||||
|
$parsers = base64_encode($server->settings->logdrain_custom_config_parser);
|
||||||
|
} else {
|
||||||
|
throw new \Exception('Unknown log drain type.');
|
||||||
|
}
|
||||||
|
if ($type !== 'custom') {
|
||||||
|
$parsers = base64_encode("
|
||||||
|
[PARSER]
|
||||||
|
Name empty_line_skipper
|
||||||
|
Format regex
|
||||||
|
Regex /^(?!\s*$).+/
|
||||||
|
");
|
||||||
|
}
|
||||||
|
$compose = base64_encode("
|
||||||
|
services:
|
||||||
|
coolify-log-drain:
|
||||||
|
image: cr.fluentbit.io/fluent/fluent-bit:2.0
|
||||||
|
container_name: coolify-log-drain
|
||||||
|
command: -c /fluent-bit.conf
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
volumes:
|
||||||
|
- ./fluent-bit.conf:/fluent-bit.conf
|
||||||
|
- ./parsers.conf:/parsers.conf
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:24224:24224
|
||||||
|
restart: unless-stopped
|
||||||
|
");
|
||||||
|
$readme = base64_encode('# New Relic Log Drain
|
||||||
|
This log drain is based on [Fluent Bit](https://fluentbit.io/) and New Relic Log Forwarder.
|
||||||
|
|
||||||
|
Files:
|
||||||
|
- `fluent-bit.conf` - configuration file for Fluent Bit
|
||||||
|
- `docker-compose.yml` - docker-compose file to run Fluent Bit
|
||||||
|
- `.env` - environment variables for Fluent Bit
|
||||||
|
');
|
||||||
|
$license_key = $server->settings->logdrain_newrelic_license_key;
|
||||||
|
$base_uri = $server->settings->logdrain_newrelic_base_uri;
|
||||||
|
$base_path = config('coolify.base_config_path');
|
||||||
|
|
||||||
|
$config_path = $base_path . '/log-drains';
|
||||||
|
$fluent_bit_config = $config_path . '/fluent-bit.conf';
|
||||||
|
$parsers_config = $config_path . '/parsers.conf';
|
||||||
|
$compose_path = $config_path . '/docker-compose.yml';
|
||||||
|
$readme_path = $config_path . '/README.md';
|
||||||
|
$command = [
|
||||||
|
"echo 'Saving configuration'",
|
||||||
|
"mkdir -p $config_path",
|
||||||
|
"echo '{$parsers}' | base64 -d > $parsers_config",
|
||||||
|
"echo '{$config}' | base64 -d > $fluent_bit_config",
|
||||||
|
"echo '{$compose}' | base64 -d > $compose_path",
|
||||||
|
"echo '{$readme}' | base64 -d > $readme_path",
|
||||||
|
"test -f $config_path/.env && rm $config_path/.env",
|
||||||
|
|
||||||
|
];
|
||||||
|
if ($type === 'newrelic') {
|
||||||
|
$add_envs_command = [
|
||||||
|
"echo LICENSE_KEY=$license_key >> $config_path/.env",
|
||||||
|
"echo BASE_URI=$base_uri >> $config_path/.env",
|
||||||
|
];
|
||||||
|
} else if ($type === 'highlight') {
|
||||||
|
$add_envs_command = [
|
||||||
|
"echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env",
|
||||||
|
];
|
||||||
|
} else if ($type === 'axiom') {
|
||||||
|
$add_envs_command = [
|
||||||
|
"echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env",
|
||||||
|
"echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env",
|
||||||
|
];
|
||||||
|
} else if ($type === 'custom') {
|
||||||
|
$add_envs_command = [
|
||||||
|
"touch $config_path/.env"
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
throw new \Exception('Unknown log drain type.');
|
||||||
|
}
|
||||||
|
$restart_command = [
|
||||||
|
"echo 'Stopping old Fluent Bit'",
|
||||||
|
"cd $config_path && docker rm -f coolify-log-drain || true",
|
||||||
|
"echo 'Starting Fluent Bit'",
|
||||||
|
"cd $config_path && docker compose up -d --remove-orphans",
|
||||||
|
];
|
||||||
|
$command = array_merge($command, $add_envs_command, $restart_command);
|
||||||
|
return instant_remote_process($command, $server);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ class UpdateCoolify
|
|||||||
try {
|
try {
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
ray('Running InstanceAutoUpdateJob');
|
ray('Running InstanceAutoUpdateJob');
|
||||||
$this->server = Server::find(0)->first();
|
$this->server = Server::find(0);
|
||||||
if (!$this->server) {
|
if (!$this->server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
42
app/Actions/Service/DeleteService.php
Normal file
42
app/Actions/Service/DeleteService.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Service;
|
||||||
|
|
||||||
|
class DeleteService
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Service $service)
|
||||||
|
{
|
||||||
|
StopService::run($service);
|
||||||
|
$server = data_get($service, 'server');
|
||||||
|
$storagesToDelete = collect([]);
|
||||||
|
|
||||||
|
$service->environment_variables()->delete();
|
||||||
|
$commands = [];
|
||||||
|
foreach ($service->applications()->get() as $application) {
|
||||||
|
$storages = $application->persistentStorages()->get();
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
$storagesToDelete->push($storage);
|
||||||
|
}
|
||||||
|
$application->delete();
|
||||||
|
}
|
||||||
|
foreach ($service->databases()->get() as $database) {
|
||||||
|
$storages = $database->persistentStorages()->get();
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
$storagesToDelete->push($storage);
|
||||||
|
}
|
||||||
|
$database->delete();
|
||||||
|
}
|
||||||
|
foreach ($storagesToDelete as $storage) {
|
||||||
|
$commands[] = "docker volume rm -f $storage->name";
|
||||||
|
}
|
||||||
|
$commands[] = "docker rm -f $service->uuid";
|
||||||
|
|
||||||
|
instant_remote_process($commands, $server, false);
|
||||||
|
|
||||||
|
$service->forceDelete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,24 +11,25 @@ class StartService
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
|
ray('Starting service: ' . $service->name);
|
||||||
$network = $service->destination->network;
|
$network = $service->destination->network;
|
||||||
$service->saveComposeConfigs();
|
$service->saveComposeConfigs();
|
||||||
$commands[] = "cd " . $service->workdir();
|
$commands[] = "cd " . $service->workdir();
|
||||||
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
||||||
$commands[] = "echo '####### Creating Docker network.'";
|
$commands[] = "echo 'Creating Docker network.'";
|
||||||
$commands[] = "docker network create --attachable '{$service->uuid}' >/dev/null || true";
|
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid >/dev/null 2>&1 || true";
|
||||||
$commands[] = "echo '####### Starting service {$service->name} on {$service->server->name}.'";
|
$commands[] = "echo 'Starting service $service->name on {$service->server->name}.'";
|
||||||
$commands[] = "echo '####### Pulling images.'";
|
$commands[] = "echo 'Pulling images.'";
|
||||||
$commands[] = "docker compose pull";
|
$commands[] = "docker compose pull";
|
||||||
$commands[] = "echo '####### Starting containers.'";
|
$commands[] = "echo 'Starting containers.'";
|
||||||
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate --build";
|
||||||
$commands[] = "docker network connect $service->uuid coolify-proxy || true";
|
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
|
||||||
$compose = data_get($service,'docker_compose',[]);
|
$compose = data_get($service, 'docker_compose', []);
|
||||||
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
|
$serviceNames = data_get(Yaml::parse($compose), 'services', []);
|
||||||
foreach($serviceNames as $serviceName => $serviceConfig){
|
foreach ($serviceNames as $serviceName => $serviceConfig) {
|
||||||
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} || true";
|
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} || true";
|
||||||
}
|
}
|
||||||
$activity = remote_process($commands, $service->server);
|
$activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
|
||||||
return $activity;
|
return $activity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class StopService
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
|
ray('Stopping service: ' . $service->name);
|
||||||
$applications = $service->applications()->get();
|
$applications = $service->applications()->get();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server);
|
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server);
|
||||||
|
|||||||
25
app/Actions/Shared/PullImage.php
Normal file
25
app/Actions/Shared/PullImage.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Shared;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class PullImage
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Service $resource)
|
||||||
|
{
|
||||||
|
$resource->saveComposeConfigs();
|
||||||
|
|
||||||
|
$commands[] = "cd " . $resource->workdir();
|
||||||
|
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
|
||||||
|
$commands[] = "docker compose pull";
|
||||||
|
|
||||||
|
$server = data_get($resource, 'server');
|
||||||
|
|
||||||
|
if (!$server) return;
|
||||||
|
|
||||||
|
instant_remote_process($commands, $resource->server);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Console/Commands/Dev.php
Normal file
33
app/Console/Commands/Dev.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
|
||||||
|
class Dev extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'dev:init';
|
||||||
|
protected $description = 'Init the app in dev mode';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
// Generate APP_KEY if not exists
|
||||||
|
if (empty(env('APP_KEY'))) {
|
||||||
|
echo "Generating APP_KEY.\n";
|
||||||
|
Artisan::call('key:generate');
|
||||||
|
}
|
||||||
|
// Seed database if it's empty
|
||||||
|
$settings = InstanceSettings::find(0);
|
||||||
|
if (!$settings) {
|
||||||
|
echo "Initializing instance, seeding database.\n";
|
||||||
|
Artisan::call('migrate --seed');
|
||||||
|
} else {
|
||||||
|
echo "Instance already initialized.\n";
|
||||||
|
}
|
||||||
|
// Set permissions
|
||||||
|
Process::run(['chmod', '-R', 'o+rwx', '.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,11 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
|
use App\Jobs\CleanupHelperContainersJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
@@ -14,7 +17,7 @@ use App\Models\StandaloneMysql;
|
|||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class Init extends Command
|
class Init extends Command
|
||||||
{
|
{
|
||||||
@@ -23,36 +26,80 @@ class Init extends Command
|
|||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
ray()->clearAll();
|
$this->alive();
|
||||||
$cleanup = $this->option('cleanup');
|
$cleanup = $this->option('cleanup');
|
||||||
if ($cleanup) {
|
if ($cleanup) {
|
||||||
|
echo "Running cleanup\n";
|
||||||
$this->cleanup_stucked_resources();
|
$this->cleanup_stucked_resources();
|
||||||
$this->cleanup_ssh();
|
// $this->cleanup_ssh();
|
||||||
}
|
}
|
||||||
$this->cleanup_in_progress_application_deployments();
|
$this->cleanup_in_progress_application_deployments();
|
||||||
}
|
$this->cleanup_stucked_helper_containers();
|
||||||
|
|
||||||
private function cleanup_ssh()
|
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
$files = Storage::allFiles('ssh/keys');
|
setup_dynamic_configuration();
|
||||||
foreach ($files as $file) {
|
|
||||||
Storage::delete($file);
|
|
||||||
}
|
|
||||||
$files = Storage::allFiles('ssh/mux');
|
|
||||||
foreach ($files as $file) {
|
|
||||||
Storage::delete($file);
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning ssh: {$e->getMessage()}\n";
|
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if (!is_null(env('AUTOUPDATE', null))) {
|
||||||
|
if (env('AUTOUPDATE') == true) {
|
||||||
|
$settings->update(['is_auto_update_enabled' => true]);
|
||||||
|
} else {
|
||||||
|
$settings->update(['is_auto_update_enabled' => false]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private function cleanup_stucked_helper_containers()
|
||||||
|
{
|
||||||
|
$servers = Server::all();
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
if ($server->isFunctional()) {
|
||||||
|
CleanupHelperContainersJob::dispatch($server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function alive()
|
||||||
|
{
|
||||||
|
$id = config('app.id');
|
||||||
|
$version = config('version');
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
$do_not_track = data_get($settings, 'do_not_track');
|
||||||
|
if ($do_not_track == true) {
|
||||||
|
echo "Skipping alive as do_not_track is enabled\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Http::get("https://get.coollabs.io/coolify/v4/alive?appId=$id&version=$version");
|
||||||
|
echo "I am alive!\n";
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in alive: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// private function cleanup_ssh()
|
||||||
|
// {
|
||||||
|
|
||||||
|
// TODO: it will cleanup id.root@host.docker.internal
|
||||||
|
// try {
|
||||||
|
// $files = Storage::allFiles('ssh/keys');
|
||||||
|
// foreach ($files as $file) {
|
||||||
|
// Storage::delete($file);
|
||||||
|
// }
|
||||||
|
// $files = Storage::allFiles('ssh/mux');
|
||||||
|
// foreach ($files as $file) {
|
||||||
|
// Storage::delete($file);
|
||||||
|
// }
|
||||||
|
// } catch (\Throwable $e) {
|
||||||
|
// echo "Error in cleaning ssh: {$e->getMessage()}\n";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
private function cleanup_in_progress_application_deployments()
|
private function cleanup_in_progress_application_deployments()
|
||||||
{
|
{
|
||||||
// Cleanup any failed deployments
|
// Cleanup any failed deployments
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$halted_deployments = ApplicationDeploymentQueue::where('status', '==', 'in_progress')->get();
|
$halted_deployments = ApplicationDeploymentQueue::where('status', '==', ApplicationDeploymentStatus::IN_PROGRESS)->where('status', '==', ApplicationDeploymentStatus::QUEUED)->get();
|
||||||
foreach ($halted_deployments as $deployment) {
|
foreach ($halted_deployments as $deployment) {
|
||||||
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
||||||
$deployment->save();
|
$deployment->save();
|
||||||
@@ -68,16 +115,19 @@ class Init extends Command
|
|||||||
$applications = Application::all();
|
$applications = Application::all();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
if (!data_get($application, 'environment')) {
|
if (!data_get($application, 'environment')) {
|
||||||
ray('Application without environment', $application->name);
|
echo 'Application without environment: ' . $application->name . ' soft deleting\n';
|
||||||
$application->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($application, 'destination.server')) {
|
|
||||||
ray('Application without server', $application->name);
|
|
||||||
$application->delete();
|
$application->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$application->destination()) {
|
if (!$application->destination()) {
|
||||||
ray('Application without destination', $application->name);
|
echo 'Application without destination: ' . $application->name . ' soft deleting\n';
|
||||||
$application->delete();
|
$application->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($application, 'destination.server')) {
|
||||||
|
echo 'Application without server: ' . $application->name . ' soft deleting\n';
|
||||||
|
$application->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -87,16 +137,19 @@ class Init extends Command
|
|||||||
$postgresqls = StandalonePostgresql::all();
|
$postgresqls = StandalonePostgresql::all();
|
||||||
foreach ($postgresqls as $postgresql) {
|
foreach ($postgresqls as $postgresql) {
|
||||||
if (!data_get($postgresql, 'environment')) {
|
if (!data_get($postgresql, 'environment')) {
|
||||||
ray('Postgresql without environment', $postgresql->name);
|
echo 'Postgresql without environment: ' . $postgresql->name . ' soft deleting\n';
|
||||||
$postgresql->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($postgresql, 'destination.server')) {
|
|
||||||
ray('Postgresql without server', $postgresql->name);
|
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$postgresql->destination()) {
|
if (!$postgresql->destination()) {
|
||||||
ray('Postgresql without destination', $postgresql->name);
|
echo 'Postgresql without destination: ' . $postgresql->name . ' soft deleting\n';
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($postgresql, 'destination.server')) {
|
||||||
|
echo 'Postgresql without server: ' . $postgresql->name . ' soft deleting\n';
|
||||||
|
$postgresql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -106,16 +159,19 @@ class Init extends Command
|
|||||||
$redis = StandaloneRedis::all();
|
$redis = StandaloneRedis::all();
|
||||||
foreach ($redis as $redis) {
|
foreach ($redis as $redis) {
|
||||||
if (!data_get($redis, 'environment')) {
|
if (!data_get($redis, 'environment')) {
|
||||||
ray('Redis without environment', $redis->name);
|
echo 'Redis without environment: ' . $redis->name . ' soft deleting\n';
|
||||||
$redis->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($redis, 'destination.server')) {
|
|
||||||
ray('Redis without server', $redis->name);
|
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$redis->destination()) {
|
if (!$redis->destination()) {
|
||||||
ray('Redis without destination', $redis->name);
|
echo 'Redis without destination: ' . $redis->name . ' soft deleting\n';
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($redis, 'destination.server')) {
|
||||||
|
echo 'Redis without server: ' . $redis->name . ' soft deleting\n';
|
||||||
|
$redis->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -126,16 +182,19 @@ class Init extends Command
|
|||||||
$mongodbs = StandaloneMongodb::all();
|
$mongodbs = StandaloneMongodb::all();
|
||||||
foreach ($mongodbs as $mongodb) {
|
foreach ($mongodbs as $mongodb) {
|
||||||
if (!data_get($mongodb, 'environment')) {
|
if (!data_get($mongodb, 'environment')) {
|
||||||
ray('Mongodb without environment', $mongodb->name);
|
echo 'Mongodb without environment: ' . $mongodb->name . ' soft deleting\n';
|
||||||
$mongodb->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($mongodb, 'destination.server')) {
|
|
||||||
ray('Mongodb without server', $mongodb->name);
|
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mongodb->destination()) {
|
if (!$mongodb->destination()) {
|
||||||
ray('Mongodb without destination', $mongodb->name);
|
echo 'Mongodb without destination: ' . $mongodb->name . ' soft deleting\n';
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($mongodb, 'destination.server')) {
|
||||||
|
echo 'Mongodb without server: ' . $mongodb->name . ' soft deleting\n';
|
||||||
|
$mongodb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -146,16 +205,19 @@ class Init extends Command
|
|||||||
$mysqls = StandaloneMysql::all();
|
$mysqls = StandaloneMysql::all();
|
||||||
foreach ($mysqls as $mysql) {
|
foreach ($mysqls as $mysql) {
|
||||||
if (!data_get($mysql, 'environment')) {
|
if (!data_get($mysql, 'environment')) {
|
||||||
ray('Mysql without environment', $mysql->name);
|
echo 'Mysql without environment: ' . $mysql->name . ' soft deleting\n';
|
||||||
$mysql->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($mysql, 'destination.server')) {
|
|
||||||
ray('Mysql without server', $mysql->name);
|
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mysql->destination()) {
|
if (!$mysql->destination()) {
|
||||||
ray('Mysql without destination', $mysql->name);
|
echo 'Mysql without destination: ' . $mysql->name . ' soft deleting\n';
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($mysql, 'destination.server')) {
|
||||||
|
echo 'Mysql without server: ' . $mysql->name . ' soft deleting\n';
|
||||||
|
$mysql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -166,16 +228,19 @@ class Init extends Command
|
|||||||
$mariadbs = StandaloneMariadb::all();
|
$mariadbs = StandaloneMariadb::all();
|
||||||
foreach ($mariadbs as $mariadb) {
|
foreach ($mariadbs as $mariadb) {
|
||||||
if (!data_get($mariadb, 'environment')) {
|
if (!data_get($mariadb, 'environment')) {
|
||||||
ray('Mariadb without environment', $mariadb->name);
|
echo 'Mariadb without environment: ' . $mariadb->name . ' soft deleting\n';
|
||||||
$mariadb->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($mariadb, 'destination.server')) {
|
|
||||||
ray('Mariadb without server', $mariadb->name);
|
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mariadb->destination()) {
|
if (!$mariadb->destination()) {
|
||||||
ray('Mariadb without destination', $mariadb->name);
|
echo 'Mariadb without destination: ' . $mariadb->name . ' soft deleting\n';
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($mariadb, 'destination.server')) {
|
||||||
|
echo 'Mariadb without server: ' . $mariadb->name . ' soft deleting\n';
|
||||||
|
$mariadb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -186,16 +251,19 @@ class Init extends Command
|
|||||||
$services = Service::all();
|
$services = Service::all();
|
||||||
foreach ($services as $service) {
|
foreach ($services as $service) {
|
||||||
if (!data_get($service, 'environment')) {
|
if (!data_get($service, 'environment')) {
|
||||||
ray('Service without environment', $service->name);
|
echo 'Service without environment: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($service, 'server')) {
|
|
||||||
ray('Service without server', $service->name);
|
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$service->destination()) {
|
if (!$service->destination()) {
|
||||||
ray('Service without destination', $service->name);
|
echo 'Service without destination: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($service, 'server')) {
|
||||||
|
echo 'Service without server: ' . $service->name . ' soft deleting\n';
|
||||||
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -205,8 +273,9 @@ class Init extends Command
|
|||||||
$serviceApplications = ServiceApplication::all();
|
$serviceApplications = ServiceApplication::all();
|
||||||
foreach ($serviceApplications as $service) {
|
foreach ($serviceApplications as $service) {
|
||||||
if (!data_get($service, 'service')) {
|
if (!data_get($service, 'service')) {
|
||||||
ray('ServiceApplication without service', $service->name);
|
echo 'ServiceApplication without service: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -216,8 +285,9 @@ class Init extends Command
|
|||||||
$serviceDatabases = ServiceDatabase::all();
|
$serviceDatabases = ServiceDatabase::all();
|
||||||
foreach ($serviceDatabases as $service) {
|
foreach ($serviceDatabases as $service) {
|
||||||
if (!data_get($service, 'service')) {
|
if (!data_get($service, 'service')) {
|
||||||
ray('ServiceDatabase without service', $service->name);
|
echo 'ServiceDatabase without service: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
41
app/Console/Commands/RootChangeEmail.php
Normal file
41
app/Console/Commands/RootChangeEmail.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class RootChangeEmail extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'root:change-email';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Change Root Email';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
$this->info('You are about to change the root user\'s email.');
|
||||||
|
$email = $this->ask('Give me a new email for root user');
|
||||||
|
$this->info('Updating root email...');
|
||||||
|
try {
|
||||||
|
User::find(0)->update(['email' => $email]);
|
||||||
|
$this->info('Root user\'s email updated successfully.');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('Failed to update root user\'s email.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,14 +8,14 @@ use Illuminate\Support\Facades\Hash;
|
|||||||
|
|
||||||
use function Laravel\Prompts\password;
|
use function Laravel\Prompts\password;
|
||||||
|
|
||||||
class UsersResetRoot extends Command
|
class RootResetPassword extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'users:reset-root';
|
protected $signature = 'root:reset-password';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -12,21 +12,21 @@ use function Laravel\Prompts\confirm;
|
|||||||
use function Laravel\Prompts\multiselect;
|
use function Laravel\Prompts\multiselect;
|
||||||
use function Laravel\Prompts\select;
|
use function Laravel\Prompts\select;
|
||||||
|
|
||||||
class ResourcesDelete extends Command
|
class ServicesDelete extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'resources:delete';
|
protected $signature = 'services:delete';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $description = 'Delete a resource from the database';
|
protected $description = 'Delete a service from the database';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
@@ -34,7 +34,7 @@ class ResourcesDelete extends Command
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$resource = select(
|
$resource = select(
|
||||||
'What resource do you want to delete?',
|
'What service do you want to delete?',
|
||||||
['Application', 'Database', 'Service', 'Server'],
|
['Application', 'Database', 'Service', 'Server'],
|
||||||
);
|
);
|
||||||
if ($resource === 'Application') {
|
if ($resource === 'Application') {
|
||||||
@@ -26,7 +26,7 @@ class ServicesGenerate extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
ray()->clearAll();
|
// ray()->clearAll();
|
||||||
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
||||||
$files = array_filter($files, function ($file) {
|
$files = array_filter($files, function ($file) {
|
||||||
return strpos($file, '.yaml') !== false;
|
return strpos($file, '.yaml') !== false;
|
||||||
|
|||||||
@@ -71,6 +71,15 @@ class SyncBunny extends Command
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
|
if (!$only_template && !$only_version) {
|
||||||
|
$this->info('About to sync files (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.');
|
||||||
|
}
|
||||||
|
if ($only_template) {
|
||||||
|
$this->info('About to sync service-templates.json to BunnyCDN.');
|
||||||
|
}
|
||||||
|
if ($only_version) {
|
||||||
|
$this->info('About to sync versions.json to BunnyCDN.');
|
||||||
|
}
|
||||||
$confirmed = confirm('Are you sure you want to sync?');
|
$confirmed = confirm('Are you sure you want to sync?');
|
||||||
if (!$confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Console;
|
namespace App\Console;
|
||||||
|
|
||||||
use App\Jobs\CheckResaleLicenseJob;
|
use App\Jobs\CheckLogDrainContainerJob;
|
||||||
use App\Jobs\CleanupInstanceStuffsJob;
|
use App\Jobs\CleanupInstanceStuffsJob;
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
use App\Jobs\DockerCleanupJob;
|
|
||||||
use App\Jobs\InstanceAutoUpdateJob;
|
use App\Jobs\InstanceAutoUpdateJob;
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Jobs\PullHelperImageJob;
|
use App\Jobs\PullHelperImageJob;
|
||||||
|
use App\Jobs\ServerStatusJob;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\Team;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
@@ -27,7 +28,6 @@ class Kernel extends ConsoleKernel
|
|||||||
// Server Jobs
|
// Server Jobs
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->cleanup_servers($schedule);
|
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->pull_helper_image($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
} else {
|
} else {
|
||||||
@@ -40,33 +40,35 @@ class Kernel extends ConsoleKernel
|
|||||||
$this->instance_auto_update($schedule);
|
$this->instance_auto_update($schedule);
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->cleanup_servers($schedule);
|
|
||||||
$this->pull_helper_image($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function pull_helper_image($schedule)
|
private function pull_helper_image($schedule)
|
||||||
{
|
{
|
||||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new PullHelperImageJob($server))->everyTenMinutes()->onOneServer();
|
$schedule->job(new PullHelperImageJob($server))->everyTenMinutes()->onOneServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function cleanup_servers($schedule)
|
|
||||||
{
|
|
||||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
|
||||||
foreach ($servers as $server) {
|
|
||||||
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private function check_resources($schedule)
|
private function check_resources($schedule)
|
||||||
{
|
{
|
||||||
if (isCloud()) {
|
if (isCloud()) {
|
||||||
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false);
|
$servers = Server::all()->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);
|
||||||
} else {
|
} else {
|
||||||
$servers = Server::all();
|
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
||||||
|
$containerServers = $servers->where('settings.is_swarm_worker', false);
|
||||||
|
}
|
||||||
|
foreach ($containerServers as $server) {
|
||||||
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
|
if ($server->isLogDrainEnabled()) {
|
||||||
|
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ServerStatusJob($server))->everyTenMinutes()->onOneServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function instance_auto_update($schedule)
|
private function instance_auto_update($schedule)
|
||||||
|
|||||||
@@ -16,9 +16,11 @@ class CoolifyTaskArgs extends Data
|
|||||||
public string $command,
|
public string $command,
|
||||||
public string $type,
|
public string $type,
|
||||||
public ?string $type_uuid = null,
|
public ?string $type_uuid = null,
|
||||||
|
public ?int $process_id = null,
|
||||||
public ?Model $model = null,
|
public ?Model $model = null,
|
||||||
public ?string $status = null ,
|
public ?string $status = null ,
|
||||||
public bool $ignore_errors = false,
|
public bool $ignore_errors = false,
|
||||||
|
public $call_event_on_finish = null,
|
||||||
) {
|
) {
|
||||||
if(is_null($status)){
|
if(is_null($status)){
|
||||||
$this->status = ProcessStatus::QUEUED->value;
|
$this->status = ProcessStatus::QUEUED->value;
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ enum ProcessStatus: string
|
|||||||
case IN_PROGRESS = 'in_progress';
|
case IN_PROGRESS = 'in_progress';
|
||||||
case FINISHED = 'finished';
|
case FINISHED = 'finished';
|
||||||
case ERROR = 'error';
|
case ERROR = 'error';
|
||||||
|
case KILLED = 'killed';
|
||||||
case CANCELLED = 'cancelled';
|
case CANCELLED = 'cancelled';
|
||||||
}
|
}
|
||||||
|
|||||||
34
app/Events/ApplicationStatusChanged.php
Normal file
34
app/Events/ApplicationStatusChanged.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ApplicationStatusChanged implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $teamId;
|
||||||
|
public function __construct($teamId = null)
|
||||||
|
{
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
throw new \Exception("Team id is null");
|
||||||
|
}
|
||||||
|
$this->teamId = $teamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/BackupCreated.php
Normal file
34
app/Events/BackupCreated.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class BackupCreated implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $teamId;
|
||||||
|
public function __construct($teamId = null)
|
||||||
|
{
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
throw new \Exception("Team id is null");
|
||||||
|
}
|
||||||
|
$this->teamId = $teamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/DatabaseStatusChanged.php
Normal file
34
app/Events/DatabaseStatusChanged.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class DatabaseStatusChanged implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $userId;
|
||||||
|
public function __construct($userId = null)
|
||||||
|
{
|
||||||
|
if (is_null($userId)) {
|
||||||
|
$userId = auth()->user()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($userId)) {
|
||||||
|
throw new \Exception("User id is null");
|
||||||
|
}
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("user.{$this->userId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/ServiceStatusChanged.php
Normal file
34
app/Events/ServiceStatusChanged.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ServiceStatusChanged implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $userId;
|
||||||
|
public function __construct($userId = null)
|
||||||
|
{
|
||||||
|
if (is_null($userId)) {
|
||||||
|
$userId = auth()->user()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($userId)) {
|
||||||
|
throw new \Exception("User id is null");
|
||||||
|
}
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("user.{$this->userId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/Events/TestEvent.php
Normal file
28
app/Events/TestEvent.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class TestEvent implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $teamId;
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->teamId = auth()->user()->currentTeam()->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@ namespace App\Exceptions;
|
|||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Auth\AuthenticationException;
|
||||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
|
use RuntimeException;
|
||||||
use Sentry\Laravel\Integration;
|
use Sentry\Laravel\Integration;
|
||||||
use Sentry\State\Scope;
|
use Sentry\State\Scope;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@@ -40,6 +42,13 @@ class Handler extends ExceptionHandler
|
|||||||
];
|
];
|
||||||
private InstanceSettings $settings;
|
private InstanceSettings $settings;
|
||||||
|
|
||||||
|
protected function unauthenticated($request, AuthenticationException $exception)
|
||||||
|
{
|
||||||
|
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
|
||||||
|
return response()->json(['message' => $exception->getMessage()], 401);
|
||||||
|
}
|
||||||
|
return redirect()->guest($exception->redirectTo() ?? route('login'));
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Register the exception handling callbacks for the application.
|
* Register the exception handling callbacks for the application.
|
||||||
*/
|
*/
|
||||||
@@ -47,6 +56,9 @@ class Handler extends ExceptionHandler
|
|||||||
{
|
{
|
||||||
$this->reportable(function (Throwable $e) {
|
$this->reportable(function (Throwable $e) {
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
|
// return;
|
||||||
|
}
|
||||||
|
if ($e instanceof RuntimeException) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->settings = InstanceSettings::get();
|
$this->settings = InstanceSettings::get();
|
||||||
@@ -65,6 +77,7 @@ class Handler extends ExceptionHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
ray('reporting to sentry');
|
||||||
Integration::captureUnhandledException($e);
|
Integration::captureUnhandledException($e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,23 +10,6 @@ class ApplicationController extends Controller
|
|||||||
{
|
{
|
||||||
use AuthorizesRequests, ValidatesRequests;
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
|
|
||||||
public function configuration()
|
|
||||||
{
|
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
|
||||||
if (!$project) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
|
|
||||||
if (!$environment) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
|
|
||||||
if (!$application) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
return view('project.application.configuration', ['application' => $application]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deployments()
|
public function deployments()
|
||||||
{
|
{
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class Controller extends BaseController
|
|||||||
} else {
|
} else {
|
||||||
$team = $user->teams()->first();
|
$team = $user->teams()->first();
|
||||||
}
|
}
|
||||||
if (is_null(data_get($user, 'email_verified_at'))){
|
if (is_null(data_get($user, 'email_verified_at'))) {
|
||||||
$user->email_verified_at = now();
|
$user->email_verified_at = now();
|
||||||
$user->save();
|
$user->save();
|
||||||
}
|
}
|
||||||
@@ -80,6 +80,10 @@ class Controller extends BaseController
|
|||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
$database = StandalonePostgresql::whereName('coolify-db')->first();
|
$database = StandalonePostgresql::whereName('coolify-db')->first();
|
||||||
if ($database) {
|
if ($database) {
|
||||||
|
if ($database->status !== 'running') {
|
||||||
|
$database->status = 'running';
|
||||||
|
$database->save();
|
||||||
|
}
|
||||||
$s3s = S3Storage::whereTeamId(0)->get();
|
$s3s = S3Storage::whereTeamId(0)->get();
|
||||||
}
|
}
|
||||||
return view('settings.configuration', [
|
return view('settings.configuration', [
|
||||||
@@ -133,16 +137,28 @@ class Controller extends BaseController
|
|||||||
public function acceptInvitation()
|
public function acceptInvitation()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
$resetPassword = request()->query('reset-password');
|
||||||
|
$invitationUuid = request()->route('uuid');
|
||||||
|
$invitation = TeamInvitation::whereUuid($invitationUuid)->firstOrFail();
|
||||||
$user = User::whereEmail($invitation->email)->firstOrFail();
|
$user = User::whereEmail($invitation->email)->firstOrFail();
|
||||||
if (auth()->user()->id !== $user->id) {
|
|
||||||
abort(401);
|
|
||||||
}
|
|
||||||
$invitationValid = $invitation->isValid();
|
$invitationValid = $invitation->isValid();
|
||||||
if ($invitationValid) {
|
if ($invitationValid) {
|
||||||
|
if ($resetPassword) {
|
||||||
|
$user->update([
|
||||||
|
'password' => Hash::make($invitationUuid),
|
||||||
|
'force_password_reset' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($user->teams()->where('team_id', $invitation->team->id)->exists()) {
|
||||||
|
$invitation->delete();
|
||||||
|
return redirect()->route('team.index');
|
||||||
|
}
|
||||||
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
||||||
refreshSession($invitation->team);
|
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
|
if (auth()->user()?->id !== $user->id) {
|
||||||
|
return redirect()->route('login');
|
||||||
|
}
|
||||||
|
refreshSession($invitation->team);
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
} else {
|
} else {
|
||||||
abort(401);
|
abort(401);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class ProjectController extends Controller
|
|||||||
$database = create_standalone_mongodb($environment->id, $destination_uuid);
|
$database = create_standalone_mongodb($environment->id, $destination_uuid);
|
||||||
} else if ($type->value() === 'mysql') {
|
} else if ($type->value() === 'mysql') {
|
||||||
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
||||||
}else if ($type->value() === 'mariadb') {
|
} else if ($type->value() === 'mariadb') {
|
||||||
$database = create_standalone_mariadb($environment->id, $destination_uuid);
|
$database = create_standalone_mariadb($environment->id, $destination_uuid);
|
||||||
}
|
}
|
||||||
return redirect()->route('project.database.configuration', [
|
return redirect()->route('project.database.configuration', [
|
||||||
@@ -104,27 +104,7 @@ class ProjectController extends Controller
|
|||||||
$generatedValue = $value;
|
$generatedValue = $value;
|
||||||
if ($value->contains('SERVICE_')) {
|
if ($value->contains('SERVICE_')) {
|
||||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
// TODO: make it shared with Service.php
|
$generatedValue = generateEnvValue($command->value());
|
||||||
switch ($command->value()) {
|
|
||||||
case 'PASSWORD':
|
|
||||||
$generatedValue = Str::password(symbols: false);
|
|
||||||
break;
|
|
||||||
case 'PASSWORD_64':
|
|
||||||
$generatedValue = Str::password(length: 64, symbols: false);
|
|
||||||
break;
|
|
||||||
case 'BASE64_64':
|
|
||||||
$generatedValue = Str::random(64);
|
|
||||||
break;
|
|
||||||
case 'BASE64_128':
|
|
||||||
$generatedValue = Str::random(128);
|
|
||||||
break;
|
|
||||||
case 'BASE64':
|
|
||||||
$generatedValue = Str::random(32);
|
|
||||||
break;
|
|
||||||
case 'USER':
|
|
||||||
$generatedValue = Str::random(16);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
EnvironmentVariable::create([
|
EnvironmentVariable::create([
|
||||||
'key' => $key,
|
'key' => $key,
|
||||||
@@ -136,8 +116,7 @@ class ProjectController extends Controller
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
$service->parse(isNew: true);
|
$service->parse(isNew: true);
|
||||||
|
return redirect()->route('project.service.configuration', [
|
||||||
return redirect()->route('project.service', [
|
|
||||||
'service_uuid' => $service->uuid,
|
'service_uuid' => $service->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Destination\New;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class StandaloneDocker extends Component
|
|
||||||
{
|
|
||||||
public string $name;
|
|
||||||
public string $network;
|
|
||||||
|
|
||||||
public Collection $servers;
|
|
||||||
public Server $server;
|
|
||||||
public int|null $server_id = null;
|
|
||||||
|
|
||||||
protected $rules = [
|
|
||||||
'name' => 'required|string',
|
|
||||||
'network' => 'required|string',
|
|
||||||
'server_id' => 'required|integer'
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'name' => 'name',
|
|
||||||
'network' => 'network',
|
|
||||||
'server_id' => 'server'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
if (request()->query('server_id')) {
|
|
||||||
$this->server_id = request()->query('server_id');
|
|
||||||
} else {
|
|
||||||
if ($this->servers->count() > 0) {
|
|
||||||
$this->server_id = $this->servers->first()->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (request()->query('network_name')) {
|
|
||||||
$this->network = request()->query('network_name');
|
|
||||||
} else {
|
|
||||||
$this->network = new Cuid2(7);
|
|
||||||
}
|
|
||||||
$this->name = Str::kebab("{$this->servers->first()->name}-{$this->network}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generate_name()
|
|
||||||
{
|
|
||||||
$this->server = Server::find($this->server_id);
|
|
||||||
$this->name = Str::kebab("{$this->server->name}-{$this->network}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->validate();
|
|
||||||
try {
|
|
||||||
$this->server = Server::find($this->server_id);
|
|
||||||
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
|
|
||||||
if ($found) {
|
|
||||||
$this->createNetworkAndAttachToProxy();
|
|
||||||
$this->emit('error', 'Network already added to this server.');
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
$docker = ModelsStandaloneDocker::create([
|
|
||||||
'name' => $this->name,
|
|
||||||
'network' => $this->network,
|
|
||||||
'server_id' => $this->server_id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$this->createNetworkAndAttachToProxy();
|
|
||||||
return redirect()->route('destination.show', $docker->uuid);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createNetworkAndAttachToProxy()
|
|
||||||
{
|
|
||||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
|
||||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
|
||||||
|
|
||||||
use App\Actions\Application\StopApplication;
|
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use App\Models\Application;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class Heading extends Component
|
|
||||||
{
|
|
||||||
public Application $application;
|
|
||||||
public array $parameters;
|
|
||||||
|
|
||||||
protected string $deploymentUuid;
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function check_status()
|
|
||||||
{
|
|
||||||
if ($this->application->destination->server->isFunctional()) {
|
|
||||||
dispatch(new ContainerStatusJob($this->application->destination->server));
|
|
||||||
$this->application->refresh();
|
|
||||||
$this->application->previews->each(function ($preview) {
|
|
||||||
$preview->refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function force_deploy_without_cache()
|
|
||||||
{
|
|
||||||
$this->deploy(force_rebuild: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deploy(bool $force_rebuild = false)
|
|
||||||
{
|
|
||||||
$this->setDeploymentUuid();
|
|
||||||
queue_application_deployment(
|
|
||||||
application_id: $this->application->id,
|
|
||||||
deployment_uuid: $this->deploymentUuid,
|
|
||||||
force_rebuild: $force_rebuild,
|
|
||||||
);
|
|
||||||
return redirect()->route('project.application.deployment', [
|
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function setDeploymentUuid()
|
|
||||||
{
|
|
||||||
$this->deploymentUuid = new Cuid2(7);
|
|
||||||
$this->parameters['deployment_uuid'] = $this->deploymentUuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function stop()
|
|
||||||
{
|
|
||||||
StopApplication::run($this->application);
|
|
||||||
$this->application->status = 'exited';
|
|
||||||
$this->application->save();
|
|
||||||
$this->application->refresh();
|
|
||||||
}
|
|
||||||
public function restart() {
|
|
||||||
$this->setDeploymentUuid();
|
|
||||||
queue_application_deployment(
|
|
||||||
application_id: $this->application->id,
|
|
||||||
deployment_uuid: $this->deploymentUuid,
|
|
||||||
restart_only: true,
|
|
||||||
);
|
|
||||||
return redirect()->route('project.application.deployment', [
|
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class Rollback extends Component
|
|
||||||
{
|
|
||||||
public Application $application;
|
|
||||||
public $images = [];
|
|
||||||
public string|null $current;
|
|
||||||
public array $parameters;
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rollbackImage($commit)
|
|
||||||
{
|
|
||||||
$deployment_uuid = new Cuid2(7);
|
|
||||||
|
|
||||||
queue_application_deployment(
|
|
||||||
application_id: $this->application->id,
|
|
||||||
deployment_uuid: $deployment_uuid,
|
|
||||||
commit: $commit,
|
|
||||||
force_rebuild: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
return redirect()->route('project.application.deployment', [
|
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
|
||||||
'deployment_uuid' => $deployment_uuid,
|
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadImages()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$image = $this->application->uuid;
|
|
||||||
$output = instant_remote_process([
|
|
||||||
"docker inspect --format='{{.Config.Image}}' {$this->application->uuid}",
|
|
||||||
], $this->application->destination->server, throwError: false);
|
|
||||||
$current_tag = Str::of($output)->trim()->explode(":");
|
|
||||||
$this->current = data_get($current_tag, 1);
|
|
||||||
|
|
||||||
$output = instant_remote_process([
|
|
||||||
"docker images --format '{{.Repository}}#{{.Tag}}#{{.CreatedAt}}'",
|
|
||||||
], $this->application->destination->server);
|
|
||||||
$this->images = Str::of($output)->trim()->explode("\n")->filter(function ($item) use ($image) {
|
|
||||||
return Str::of($item)->contains($image);
|
|
||||||
})->map(function ($item) {
|
|
||||||
$item = Str::of($item)->explode('#');
|
|
||||||
if ($item[1] === $this->current) {
|
|
||||||
// $is_current = true;
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
'tag' => $item[1],
|
|
||||||
'created_at' => $item[2],
|
|
||||||
'is_current' => $is_current ?? null,
|
|
||||||
];
|
|
||||||
})->toArray();
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\New;
|
|
||||||
|
|
||||||
use App\Models\EnvironmentVariable;
|
|
||||||
use App\Models\Project;
|
|
||||||
use App\Models\Service;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class DockerCompose extends Component
|
|
||||||
{
|
|
||||||
public string $dockerComposeRaw = '';
|
|
||||||
public string $envFile = '';
|
|
||||||
public array $parameters;
|
|
||||||
public array $query;
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
$this->query = request()->query();
|
|
||||||
if (isDev()) {
|
|
||||||
$this->dockerComposeRaw = 'services:
|
|
||||||
ghost:
|
|
||||||
image: ghost:5
|
|
||||||
volumes:
|
|
||||||
- ~/configs:/etc/configs/:ro
|
|
||||||
- ./var/lib/ghost/content:/tmp/ghost2/content:ro
|
|
||||||
- /var/lib/ghost/content:/tmp/ghost/content:rw
|
|
||||||
- ghost-content-data:/var/lib/ghost/content
|
|
||||||
- type: volume
|
|
||||||
source: mydata
|
|
||||||
target: /data
|
|
||||||
- type: bind
|
|
||||||
source: ./var/lib/ghost/data
|
|
||||||
target: /data
|
|
||||||
- type: bind
|
|
||||||
source: /tmp
|
|
||||||
target: /tmp
|
|
||||||
labels:
|
|
||||||
- "test.label=true"
|
|
||||||
ports:
|
|
||||||
- "3000"
|
|
||||||
- "3000-3005"
|
|
||||||
- "8000:8000"
|
|
||||||
- "9090-9091:8080-8081"
|
|
||||||
- "49100:22"
|
|
||||||
- "127.0.0.1:8001:8001"
|
|
||||||
- "127.0.0.1:5000-5010:5000-5010"
|
|
||||||
- "127.0.0.1::5000"
|
|
||||||
- "6060:6060/udp"
|
|
||||||
- "12400-12500:1240"
|
|
||||||
- target: 80
|
|
||||||
published: 8080
|
|
||||||
protocol: tcp
|
|
||||||
mode: host
|
|
||||||
networks:
|
|
||||||
- some-network
|
|
||||||
- other-network
|
|
||||||
environment:
|
|
||||||
- database__client=${DATABASE_CLIENT:-mysql}
|
|
||||||
- database__connection__database=${MYSQL_DATABASE:-ghost}
|
|
||||||
- database__connection__host=${DATABASE_CONNECTION_HOST:-mysql}
|
|
||||||
- test=${TEST:?true}
|
|
||||||
- url=$SERVICE_FQDN_GHOST
|
|
||||||
- database__connection__user=$SERVICE_USER_MYSQL
|
|
||||||
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
|
||||||
depends_on:
|
|
||||||
- mysql
|
|
||||||
mysql:
|
|
||||||
image: mysql:8.0
|
|
||||||
volumes:
|
|
||||||
- ghost-mysql-data:/var/lib/mysql
|
|
||||||
environment:
|
|
||||||
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
|
||||||
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
|
||||||
- MYSQL_DATABASE=$MYSQL_DATABASE
|
|
||||||
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
|
||||||
- SESSION_SECRET
|
|
||||||
minio:
|
|
||||||
image: minio/minio
|
|
||||||
environment:
|
|
||||||
RACK_ENV: development
|
|
||||||
A: $A
|
|
||||||
SHOW: ${SHOW}
|
|
||||||
SHOW1: ${SHOW2-show1}
|
|
||||||
SHOW2: ${SHOW3:-show2}
|
|
||||||
SHOW3: ${SHOW4?show3}
|
|
||||||
SHOW4: ${SHOW5:?show4}
|
|
||||||
SHOW5: ${SERVICE_USER_MINIO}
|
|
||||||
SHOW6: ${SERVICE_PASSWORD_MINIO}
|
|
||||||
SHOW7: ${SERVICE_PASSWORD_64_MINIO}
|
|
||||||
SHOW8: ${SERVICE_BASE64_64_MINIO}
|
|
||||||
SHOW9: ${SERVICE_BASE64_128_MINIO}
|
|
||||||
SHOW10: ${SERVICE_BASE64_MINIO}
|
|
||||||
SHOW11:
|
|
||||||
';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->validate([
|
|
||||||
'dockerComposeRaw' => 'required'
|
|
||||||
]);
|
|
||||||
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
|
||||||
$server_id = $this->query['server_id'];
|
|
||||||
|
|
||||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
|
||||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
|
||||||
$service = Service::create([
|
|
||||||
'name' => 'service' . Str::random(10),
|
|
||||||
'docker_compose_raw' => $this->dockerComposeRaw,
|
|
||||||
'environment_id' => $environment->id,
|
|
||||||
'server_id' => (int) $server_id,
|
|
||||||
]);
|
|
||||||
$variables = parseEnvFormatToArray($this->envFile);
|
|
||||||
foreach ($variables as $key => $variable) {
|
|
||||||
EnvironmentVariable::create([
|
|
||||||
'key' => $key,
|
|
||||||
'value' => $variable,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
'service_id' => $service->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$service->name = "service-$service->uuid";
|
|
||||||
|
|
||||||
$service->parse(isNew: true);
|
|
||||||
|
|
||||||
return redirect()->route('project.service', [
|
|
||||||
'service_uuid' => $service->uuid,
|
|
||||||
'environment_name' => $environment->name,
|
|
||||||
'project_uuid' => $project->uuid,
|
|
||||||
]);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class ComposeModal extends Component
|
|
||||||
{
|
|
||||||
public ?string $raw = null;
|
|
||||||
public ?string $actual = null;
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.compose-modal');
|
|
||||||
}
|
|
||||||
public function submit() {
|
|
||||||
$this->emit('warning', "Saving new docker compose...");
|
|
||||||
$this->emit('saveCompose', $this->raw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
|
||||||
|
|
||||||
use App\Models\ServiceDatabase;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Database extends Component
|
|
||||||
{
|
|
||||||
public ServiceDatabase $database;
|
|
||||||
public $fileStorages;
|
|
||||||
protected $listeners = ["refreshFileStorages"];
|
|
||||||
protected $rules = [
|
|
||||||
'database.human_name' => 'nullable',
|
|
||||||
'database.description' => 'nullable',
|
|
||||||
'database.image' => 'required',
|
|
||||||
'database.exclude_from_status' => 'required|boolean',
|
|
||||||
];
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.database');
|
|
||||||
}
|
|
||||||
public function mount() {
|
|
||||||
$this->refreshFileStorages();
|
|
||||||
}
|
|
||||||
public function instantSave() {
|
|
||||||
$this->submit();
|
|
||||||
}
|
|
||||||
public function refreshFileStorages()
|
|
||||||
{
|
|
||||||
$this->fileStorages = $this->database->fileStorages()->get();
|
|
||||||
}
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->validate();
|
|
||||||
$this->database->save();
|
|
||||||
updateCompose($this->database);
|
|
||||||
$this->emit('success', 'Database saved successfully.');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
ray($e);
|
|
||||||
} finally {
|
|
||||||
$this->emit('generateDockerCompose');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
|
||||||
|
|
||||||
use App\Actions\Service\StartService;
|
|
||||||
use App\Actions\Service\StopService;
|
|
||||||
use App\Models\Service;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Navbar extends Component
|
|
||||||
{
|
|
||||||
public Service $service;
|
|
||||||
public array $parameters;
|
|
||||||
public array $query;
|
|
||||||
protected $listeners = ["checkStatus"];
|
|
||||||
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.navbar');
|
|
||||||
}
|
|
||||||
public function checkStatus() {
|
|
||||||
$this->service->refresh();
|
|
||||||
}
|
|
||||||
public function deploy()
|
|
||||||
{
|
|
||||||
$this->service->parse();
|
|
||||||
$activity = StartService::run($this->service);
|
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
|
||||||
}
|
|
||||||
public function stop(bool $forceCleanup = false)
|
|
||||||
{
|
|
||||||
StopService::run($this->service);
|
|
||||||
$this->service->refresh();
|
|
||||||
if ($forceCleanup) {
|
|
||||||
$this->emit('success', 'Force cleanup service successfully.');
|
|
||||||
} else {
|
|
||||||
$this->emit('success', 'Service stopped successfully.');
|
|
||||||
}
|
|
||||||
$this->emit('checkStatus');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class StackForm extends Component
|
|
||||||
{
|
|
||||||
public $service;
|
|
||||||
protected $listeners = ["saveCompose"];
|
|
||||||
protected $rules = [
|
|
||||||
'service.docker_compose_raw' => 'required',
|
|
||||||
'service.docker_compose' => 'required',
|
|
||||||
'service.name' => 'required',
|
|
||||||
'service.description' => 'nullable',
|
|
||||||
];
|
|
||||||
public function saveCompose($raw)
|
|
||||||
{
|
|
||||||
$this->service->docker_compose_raw = $raw;
|
|
||||||
$this->submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->validate();
|
|
||||||
$this->service->save();
|
|
||||||
$this->service->parse();
|
|
||||||
$this->service->refresh();
|
|
||||||
$this->service->saveComposeConfigs();
|
|
||||||
$this->emit('refreshStacks');
|
|
||||||
$this->emit('refreshEnvs');
|
|
||||||
$this->emit('success', 'Service saved successfully.');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.stack-form');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
|
||||||
|
|
||||||
use App\Jobs\DeleteResourceJob;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class Danger extends Component
|
|
||||||
{
|
|
||||||
public $resource;
|
|
||||||
public array $parameters;
|
|
||||||
public ?string $modalId = null;
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->modalId = new Cuid2(7);
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
DeleteResourceJob::dispatchSync($this->resource);
|
|
||||||
return redirect()->route('project.resources', [
|
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
|
||||||
'environment_name' => $this->parameters['environment_name']
|
|
||||||
]);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Destination extends Component
|
|
||||||
{
|
|
||||||
public $destination;
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class GetLogs extends Component
|
|
||||||
{
|
|
||||||
public string $outputs = '';
|
|
||||||
public string $errors = '';
|
|
||||||
public Server $server;
|
|
||||||
public ?string $container = null;
|
|
||||||
public ?bool $streamLogs = false;
|
|
||||||
public ?bool $showTimeStamps = true;
|
|
||||||
public int $numberOfLines = 100;
|
|
||||||
public function doSomethingWithThisChunkOfOutput($output)
|
|
||||||
{
|
|
||||||
$this->outputs .= removeAnsiColors($output);
|
|
||||||
}
|
|
||||||
public function instantSave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public function getLogs($refresh = false)
|
|
||||||
{
|
|
||||||
if ($this->container) {
|
|
||||||
if ($this->showTimeStamps) {
|
|
||||||
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
|
|
||||||
} else {
|
|
||||||
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} {$this->container}");
|
|
||||||
}
|
|
||||||
if ($refresh) {
|
|
||||||
$this->outputs = '';
|
|
||||||
}
|
|
||||||
Process::run($sshCommand, function (string $type, string $output) {
|
|
||||||
$this->doSomethingWithThisChunkOfOutput($output);
|
|
||||||
});
|
|
||||||
if ($this->showTimeStamps) {
|
|
||||||
$this->outputs = str($this->outputs)->split('/\n/')->sort(function ($a, $b) {
|
|
||||||
$a = explode(' ', $a);
|
|
||||||
$b = explode(' ', $b);
|
|
||||||
return $a[0] <=> $b[0];
|
|
||||||
})->join("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.shared.get-logs');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared\Storages;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Add extends Component
|
|
||||||
{
|
|
||||||
public $uuid;
|
|
||||||
public $parameters;
|
|
||||||
public string $name;
|
|
||||||
public string $mount_path;
|
|
||||||
public string|null $host_path = null;
|
|
||||||
|
|
||||||
protected $listeners = ['clearAddStorage' => 'clear'];
|
|
||||||
protected $rules = [
|
|
||||||
'name' => 'required|string',
|
|
||||||
'mount_path' => 'required|string',
|
|
||||||
'host_path' => 'string|nullable',
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'name' => 'name',
|
|
||||||
'mount_path' => 'mount',
|
|
||||||
'host_path' => 'host',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->validate();
|
|
||||||
$name = $this->uuid . '-' . $this->name;
|
|
||||||
$this->emit('addNewVolume', [
|
|
||||||
'name' => $name,
|
|
||||||
'mount_path' => $this->mount_path,
|
|
||||||
'host_path' => $this->host_path,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clear()
|
|
||||||
{
|
|
||||||
$this->name = '';
|
|
||||||
$this->mount_path = '';
|
|
||||||
$this->host_path = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Webhooks extends Component
|
|
||||||
{
|
|
||||||
public $resource;
|
|
||||||
public ?string $deploywebhook = null;
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->deploywebhook = generateDeployWebhook($this->resource);
|
|
||||||
}
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.shared.webhooks');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Settings;
|
|
||||||
|
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use App\Models\InstanceSettings as ModelsInstanceSettings;
|
|
||||||
use App\Models\Server;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Spatie\Url\Url;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class Configuration extends Component
|
|
||||||
{
|
|
||||||
public ModelsInstanceSettings $settings;
|
|
||||||
public bool $do_not_track;
|
|
||||||
public bool $is_auto_update_enabled;
|
|
||||||
public bool $is_registration_enabled;
|
|
||||||
public bool $next_channel;
|
|
||||||
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
|
|
||||||
protected Server $server;
|
|
||||||
|
|
||||||
protected $rules = [
|
|
||||||
'settings.fqdn' => 'nullable',
|
|
||||||
'settings.resale_license' => 'nullable',
|
|
||||||
'settings.public_port_min' => 'required',
|
|
||||||
'settings.public_port_max' => 'required',
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'settings.fqdn' => 'FQDN',
|
|
||||||
'settings.resale_license' => 'Resale License',
|
|
||||||
'settings.public_port_min' => 'Public port min',
|
|
||||||
'settings.public_port_max' => 'Public port max',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function instantSave()
|
|
||||||
{
|
|
||||||
$this->settings->do_not_track = $this->do_not_track;
|
|
||||||
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
|
|
||||||
$this->settings->is_registration_enabled = $this->is_registration_enabled;
|
|
||||||
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->emit('success', 'Settings updated!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->resetErrorBag();
|
|
||||||
if ($this->settings->public_port_min > $this->settings->public_port_max) {
|
|
||||||
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->validate();
|
|
||||||
$this->settings->save();
|
|
||||||
$this->server = Server::findOrFail(0);
|
|
||||||
$this->setup_instance_fqdn();
|
|
||||||
$this->emit('success', 'Instance settings updated successfully!');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setup_instance_fqdn()
|
|
||||||
{
|
|
||||||
$file = "$this->dynamic_config_path/coolify.yaml";
|
|
||||||
if (empty($this->settings->fqdn)) {
|
|
||||||
instant_remote_process([
|
|
||||||
"rm -f $file",
|
|
||||||
], $this->server);
|
|
||||||
} else {
|
|
||||||
$url = Url::fromString($this->settings->fqdn);
|
|
||||||
$host = $url->getHost();
|
|
||||||
$schema = $url->getScheme();
|
|
||||||
$traefik_dynamic_conf = [
|
|
||||||
'http' =>
|
|
||||||
[
|
|
||||||
'routers' =>
|
|
||||||
[
|
|
||||||
'coolify-http' =>
|
|
||||||
[
|
|
||||||
'entryPoints' => [
|
|
||||||
0 => 'http',
|
|
||||||
],
|
|
||||||
'service' => 'coolify',
|
|
||||||
'rule' => "Host(`{$host}`)",
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'services' =>
|
|
||||||
[
|
|
||||||
'coolify' =>
|
|
||||||
[
|
|
||||||
'loadBalancer' =>
|
|
||||||
[
|
|
||||||
'servers' =>
|
|
||||||
[
|
|
||||||
0 =>
|
|
||||||
[
|
|
||||||
'url' => 'http://coolify:80',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
if ($schema === 'https') {
|
|
||||||
$traefik_dynamic_conf['http']['routers']['coolify-http']['middlewares'] = [
|
|
||||||
0 => 'redirect-to-https@docker',
|
|
||||||
];
|
|
||||||
$traefik_dynamic_conf['http']['routers']['coolify-https'] = [
|
|
||||||
'entryPoints' => [
|
|
||||||
0 => 'https',
|
|
||||||
],
|
|
||||||
'service' => 'coolify',
|
|
||||||
'rule' => "Host(`{$host}`)",
|
|
||||||
'tls' => [
|
|
||||||
'certresolver' => 'letsencrypt',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
$this->save_configuration_to_disk($traefik_dynamic_conf, $file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function save_configuration_to_disk(array $traefik_dynamic_conf, string $file)
|
|
||||||
{
|
|
||||||
$yaml = Yaml::dump($traefik_dynamic_conf, 12, 2);
|
|
||||||
$yaml =
|
|
||||||
"# This file is automatically generated by Coolify.\n" .
|
|
||||||
"# Do not edit it manually (only if you know what are you doing).\n\n" .
|
|
||||||
$yaml;
|
|
||||||
|
|
||||||
$base64 = base64_encode($yaml);
|
|
||||||
instant_remote_process([
|
|
||||||
"mkdir -p $this->dynamic_config_path",
|
|
||||||
"echo '$base64' | base64 -d > $file",
|
|
||||||
], $this->server);
|
|
||||||
|
|
||||||
if (config('app.env') == 'local') {
|
|
||||||
ray($yaml);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,7 @@ class CheckForcePasswordReset
|
|||||||
}
|
}
|
||||||
$force_password_reset = auth()->user()->force_password_reset;
|
$force_password_reset = auth()->user()->force_password_reset;
|
||||||
if ($force_password_reset) {
|
if ($force_password_reset) {
|
||||||
if ($request->routeIs('auth.force-password-reset') || $request->path() === 'livewire/message/force-password-reset') {
|
if ($request->routeIs('auth.force-password-reset') || $request->path() === 'force-password-reset' || $request->path() === 'livewire/update' || $request->path() === 'logout') {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
return redirect()->route('auth.force-password-reset');
|
return redirect()->route('auth.force-password-reset');
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ class DecideWhatToDoWithUser
|
|||||||
{
|
{
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
if(auth()?->user()?->currentTeam()){
|
||||||
|
refreshSession(auth()->user()->currentTeam());
|
||||||
|
}
|
||||||
if (!auth()->user() || !isCloud() || isInstanceAdmin()) {
|
if (!auth()->user() || !isCloud() || isInstanceAdmin()) {
|
||||||
if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
||||||
return redirect('boarding');
|
return redirect('boarding');
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class IsBoardingFlow
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle an incoming request.
|
|
||||||
*
|
|
||||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
|
||||||
*/
|
|
||||||
public function handle(Request $request, Closure $next): Response
|
|
||||||
{
|
|
||||||
// ray()->showQueries()->color('orange');
|
|
||||||
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
|
||||||
if (Str::startsWith($request->path(), 'invitations')) {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
return redirect('boarding');
|
|
||||||
}
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class IsSubscriptionValid
|
|
||||||
{
|
|
||||||
public function handle(Request $request, Closure $next): Response
|
|
||||||
{
|
|
||||||
if (isInstanceAdmin()) {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
if (!auth()->user() || !isCloud()) {
|
|
||||||
if ($request->path() === 'subscription') {
|
|
||||||
return redirect('/');
|
|
||||||
} else {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isSubscriptionActive() && $request->path() === 'subscription') {
|
|
||||||
// ray('active subscription Middleware');
|
|
||||||
return redirect('/');
|
|
||||||
}
|
|
||||||
if (isSubscriptionOnGracePeriod()) {
|
|
||||||
// ray('is_subscription_in_grace_period Middleware');
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
|
||||||
// ray('SubscriptionValid Middleware');
|
|
||||||
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
|
||||||
if (Str::startsWith($request->path(), 'invitations')) {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
return redirect('subscription');
|
|
||||||
} else {
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
180
app/Jobs/ApplicationDeploymentNewJob.php
Normal file
180
app/Jobs/ApplicationDeploymentNewJob.php
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Traits\ExecuteRemoteCommand;
|
||||||
|
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 RuntimeException;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ApplicationDeploymentNewJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ExecuteRemoteCommand;
|
||||||
|
|
||||||
|
public $timeout = 3600;
|
||||||
|
public $tries = 1;
|
||||||
|
|
||||||
|
public static int $batch_counter = 0;
|
||||||
|
public Server $mainServer;
|
||||||
|
public $servers;
|
||||||
|
public string $basedir;
|
||||||
|
public string $workdir;
|
||||||
|
|
||||||
|
public string $deploymentUuid;
|
||||||
|
public int $pullRequestId = 0;
|
||||||
|
|
||||||
|
// Git related
|
||||||
|
public string $gitImportCommands;
|
||||||
|
public ?string $gitType = null;
|
||||||
|
public string $gitRepository;
|
||||||
|
public string $gitBranch;
|
||||||
|
public int $gitPort;
|
||||||
|
public string $gitFullRepoUrl;
|
||||||
|
|
||||||
|
public function __construct(public ApplicationDeploymentQueue $deployment, public Application $application)
|
||||||
|
{
|
||||||
|
$this->mainServer = data_get($this->application, 'destination.server');
|
||||||
|
$this->deploymentUuid = data_get($this->deployment, 'deployment_uuid');
|
||||||
|
$this->pullRequestId = data_get($this->deployment, 'pull_request_id', 0);
|
||||||
|
$this->gitType = data_get($this->deployment, 'git_type');
|
||||||
|
|
||||||
|
$this->basedir = $this->application->generateBaseDir($this->deploymentUuid);
|
||||||
|
$this->workdir = $this->basedir . rtrim($this->application->base_directory, '/');
|
||||||
|
}
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ray()->clearAll();
|
||||||
|
$this->deployment->setStatus(ApplicationDeploymentStatus::IN_PROGRESS->value);
|
||||||
|
|
||||||
|
$hostIpMappings = $this->mainServer->getHostIPMappings($this->application->destination->network);
|
||||||
|
if ($this->application->dockerfile_target_build) {
|
||||||
|
$buildTarget = " --target {$this->application->dockerfile_target_build} ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the git repository and port (custom port or default port)
|
||||||
|
[
|
||||||
|
'repository' => $this->gitRepository,
|
||||||
|
'port' => $this->gitPort
|
||||||
|
] = $this->application->customRepository();
|
||||||
|
|
||||||
|
// Get the git branch and git import commands
|
||||||
|
[
|
||||||
|
'commands' => $this->gitImportCommands,
|
||||||
|
'branch' => $this->gitBranch,
|
||||||
|
'fullRepoUrl' => $this->gitFullRepoUrl
|
||||||
|
] = $this->application->generateGitImportCommands($this->deploymentUuid, $this->pullRequestId, $this->gitType);
|
||||||
|
|
||||||
|
$this->servers = $this->application->servers();
|
||||||
|
|
||||||
|
if ($this->deployment->restart_only) {
|
||||||
|
if ($this->application->build_pack === 'dockerimage') {
|
||||||
|
throw new \Exception('Restart only is not supported for docker image based deployments');
|
||||||
|
}
|
||||||
|
$this->deployment->addLogEntry("Starting deployment of {$this->application->name}.");
|
||||||
|
$this->servers->each(function ($server) {
|
||||||
|
$this->deployment->addLogEntry("Restarting {$this->application->name} on {$server->name}.");
|
||||||
|
$this->restartOnly($server);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||||
|
} catch (Throwable $exception) {
|
||||||
|
$this->fail($exception);
|
||||||
|
} finally {
|
||||||
|
$this->servers->each(function ($server) {
|
||||||
|
$this->deployment->addLogEntry("Cleaning up temporary containers on {$server->name}.");
|
||||||
|
$server->executeRemoteCommand(
|
||||||
|
commands: collect([])->push([
|
||||||
|
"command" => "docker rm -f {$this->deploymentUuid}",
|
||||||
|
"hidden" => true,
|
||||||
|
"ignoreErrors" => true,
|
||||||
|
]),
|
||||||
|
loggingModel: $this->deployment
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function restartOnly(Server $server)
|
||||||
|
{
|
||||||
|
$server->executeRemoteCommand(
|
||||||
|
commands: $this->application->prepareHelperImage($this->deploymentUuid),
|
||||||
|
loggingModel: $this->deployment
|
||||||
|
);
|
||||||
|
|
||||||
|
$privateKey = data_get($this->application, 'private_key.private_key', null);
|
||||||
|
$gitLsRemoteCommand = collect([]);
|
||||||
|
if ($privateKey) {
|
||||||
|
$privateKey = base64_decode($privateKey);
|
||||||
|
$gitLsRemoteCommand
|
||||||
|
->push([
|
||||||
|
"command" => executeInDocker($this->deploymentUuid, "mkdir -p /root/.ssh")
|
||||||
|
])
|
||||||
|
->push([
|
||||||
|
"command" => executeInDocker($this->deploymentUuid, "echo '{$privateKey}' | base64 -d > /root/.ssh/id_rsa")
|
||||||
|
])
|
||||||
|
->push([
|
||||||
|
"command" => executeInDocker($this->deploymentUuid, "chmod 600 /root/.ssh/id_rsa")
|
||||||
|
])
|
||||||
|
->push([
|
||||||
|
"name" => "git_commit_sha",
|
||||||
|
"command" => executeInDocker($this->deploymentUuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->gitPort} -o Port={$this->gitPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->gitFullRepoUrl} {$this->gitBranch}"),
|
||||||
|
"hidden" => true,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$gitLsRemoteCommand->push([
|
||||||
|
"name" => "git_commit_sha",
|
||||||
|
"command" => executeInDocker($this->deploymentUuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->gitPort} -o Port={$this->gitPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->gitFullRepoUrl} {$this->gitBranch}"),
|
||||||
|
"hidden" => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->deployment->addLogEntry("Checking if there is any new commit on {$this->gitBranch} branch.");
|
||||||
|
|
||||||
|
$server->executeRemoteCommand(
|
||||||
|
commands: $gitLsRemoteCommand,
|
||||||
|
loggingModel: $this->deployment
|
||||||
|
);
|
||||||
|
$commit = str($this->deployment->getOutput('git_commit_sha'))->before("\t");
|
||||||
|
|
||||||
|
[
|
||||||
|
'productionImageName' => $productionImageName
|
||||||
|
] = $this->application->generateImageNames($commit, $this->pullRequestId);
|
||||||
|
|
||||||
|
$this->deployment->addLogEntry("Checking if the image {$productionImageName} already exists.");
|
||||||
|
$server->checkIfDockerImageExists($productionImageName, $this->deployment);
|
||||||
|
|
||||||
|
if (str($this->deployment->getOutput('local_image_found'))->isNotEmpty()) {
|
||||||
|
$this->deployment->addLogEntry("Image {$productionImageName} already exists. Skipping the build.");
|
||||||
|
|
||||||
|
$server->createWorkDirForDeployment($this->workdir, $this->deployment);
|
||||||
|
|
||||||
|
$this->application->generateDockerComposeFile($server, $this->deployment, $this->workdir);
|
||||||
|
$this->application->rollingUpdateApplication($server, $this->deployment, $this->workdir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new RuntimeException('Cannot find image anywhere. Please redeploy the application.');
|
||||||
|
}
|
||||||
|
public function failed(Throwable $exception): void
|
||||||
|
{
|
||||||
|
ray($exception);
|
||||||
|
$this->next(ApplicationDeploymentStatus::FAILED->value);
|
||||||
|
}
|
||||||
|
private function next(string $status)
|
||||||
|
{
|
||||||
|
// If the deployment is cancelled by the user, don't update the status
|
||||||
|
if ($this->deployment->status !== ApplicationDeploymentStatus::CANCELLED_BY_USER->value) {
|
||||||
|
$this->deployment->update([
|
||||||
|
'status' => $status,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
queue_next_deployment($this->application, isNew: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/Jobs/ApplicationRestartJob.php
Normal file
28
app/Jobs/ApplicationRestartJob.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Traits\ExecuteRemoteCommand;
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationRestartJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, ExecuteRemoteCommand;
|
||||||
|
|
||||||
|
public $timeout = 3600;
|
||||||
|
public $tries = 1;
|
||||||
|
public string $applicationDeploymentQueueId;
|
||||||
|
public function __construct(string $applicationDeploymentQueueId)
|
||||||
|
{
|
||||||
|
$this->applicationDeploymentQueueId = $applicationDeploymentQueueId;
|
||||||
|
}
|
||||||
|
public function handle() {
|
||||||
|
ray('Restarting application');
|
||||||
|
}
|
||||||
|
}
|
||||||
88
app/Jobs/CheckLogDrainContainerJob.php
Normal file
88
app/Jobs/CheckLogDrainContainerJob.php
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Actions\Server\InstallLogDrain;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Notifications\Container\ContainerRestarted;
|
||||||
|
use App\Notifications\Container\ContainerStopped;
|
||||||
|
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;
|
||||||
|
use Illuminate\Support\Sleep;
|
||||||
|
|
||||||
|
class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function middleware(): array
|
||||||
|
{
|
||||||
|
return [(new WithoutOverlapping($this->server->id))->dontRelease()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uniqueId(): int
|
||||||
|
{
|
||||||
|
return $this->server->id;
|
||||||
|
}
|
||||||
|
public function healthcheck()
|
||||||
|
{
|
||||||
|
$status = instant_remote_process(["docker inspect --format='{{json .State.Status}}' coolify-log-drain"], $this->server, false);
|
||||||
|
if (str($status)->contains('running')) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
// ray("checking log drain statuses for {$this->server->id}");
|
||||||
|
try {
|
||||||
|
if (!$this->server->isFunctional()) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
$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);
|
||||||
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
|
|
||||||
|
$foundLogDrainContainer = $containers->filter(function ($value, $key) {
|
||||||
|
return data_get($value, 'Name') === '/coolify-log-drain';
|
||||||
|
})->first();
|
||||||
|
if (!$foundLogDrainContainer || !$this->healthcheck()) {
|
||||||
|
ray('Log drain container not found or unhealthy. Restarting...');
|
||||||
|
InstallLogDrain::run($this->server);
|
||||||
|
Sleep::for(10)->seconds();
|
||||||
|
if ($this->healthcheck()) {
|
||||||
|
if ($this->server->log_drain_notification_sent) {
|
||||||
|
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||||
|
$this->server->update(['log_drain_notification_sent' => false]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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->update(['log_drain_notification_sent' => true]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($this->server->log_drain_notification_sent) {
|
||||||
|
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||||
|
$this->server->update(['log_drain_notification_sent' => false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/Jobs/CleanupHelperContainersJob.php
Normal file
40
app/Jobs/CleanupHelperContainersJob.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class CleanupHelperContainersJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ray('Cleaning up helper containers on ' . $this->server->name);
|
||||||
|
$containers = instant_remote_process(['docker container ps --filter "ancestor=ghcr.io/coollabsio/coolify-helper:next" --filter "ancestor=ghcr.io/coollabsio/coolify-helper:latest" --format \'{{json .}}\''], $this->server, false);
|
||||||
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
|
if ($containers->count() > 0) {
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$containerId = data_get($container,'ID');
|
||||||
|
ray('Removing container ' . $containerId);
|
||||||
|
instant_remote_process(['docker container rm -f ' . $containerId], $this->server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
send_internal_notification('CleanupHelperContainersJob failed with error: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,14 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
use App\Actions\Proxy\CheckProxy;
|
use App\Actions\Proxy\CheckProxy;
|
||||||
use App\Actions\Proxy\StartProxy;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Notifications\Container\ContainerRestarted;
|
use App\Notifications\Container\ContainerRestarted;
|
||||||
use App\Notifications\Container\ContainerStopped;
|
use App\Notifications\Container\ContainerStopped;
|
||||||
use App\Notifications\Server\Revived;
|
|
||||||
use App\Notifications\Server\Unreachable;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
@@ -23,142 +22,96 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public function __construct(public Server $server)
|
public $tries = 4;
|
||||||
|
public function backoff(): int
|
||||||
{
|
{
|
||||||
$this->handle();
|
return isDev() ? 1 : 3;
|
||||||
}
|
}
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping($this->server->id))->dontRelease()];
|
return [(new WithoutOverlapping($this->server->uuid))];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uniqueId(): int
|
public function uniqueId(): int
|
||||||
{
|
{
|
||||||
return $this->server->id;
|
return $this->server->uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(): void
|
public function __construct(public Server $server)
|
||||||
{
|
{
|
||||||
// ray("checking server status for {$this->server->id}");
|
// $this->handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
if (!$this->server->isServerReady($this->tries)) {
|
||||||
|
throw new \RuntimeException('Server is not reachable.');
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
// ray()->clearAll();
|
if ($this->server->isSwarm()) {
|
||||||
$serverUptimeCheckNumber = $this->server->unreachable_count;
|
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
||||||
$serverUptimeCheckNumberMax = 3;
|
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
||||||
|
|
||||||
// ray('checking # ' . $serverUptimeCheckNumber);
|
|
||||||
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
|
||||||
if ($this->server->unreachable_email_sent === false) {
|
|
||||||
ray('Server unreachable, sending notification...');
|
|
||||||
$this->server->team->notify(new Unreachable($this->server));
|
|
||||||
$this->server->update(['unreachable_email_sent' => true]);
|
|
||||||
}
|
|
||||||
$this->server->settings()->update([
|
|
||||||
'is_reachable' => false,
|
|
||||||
]);
|
|
||||||
$this->server->update([
|
|
||||||
'unreachable_count' => 0,
|
|
||||||
]);
|
|
||||||
// Update all applications, databases and services to exited
|
|
||||||
foreach ($this->server->applications() as $application) {
|
|
||||||
$application->update(['status' => 'exited']);
|
|
||||||
}
|
|
||||||
foreach ($this->server->databases() as $database) {
|
|
||||||
$database->update(['status' => 'exited']);
|
|
||||||
}
|
|
||||||
foreach ($this->server->services() as $service) {
|
|
||||||
$apps = $service->applications()->get();
|
|
||||||
$dbs = $service->databases()->get();
|
|
||||||
foreach ($apps as $app) {
|
|
||||||
$app->update(['status' => 'exited']);
|
|
||||||
}
|
|
||||||
foreach ($dbs as $db) {
|
|
||||||
$db->update(['status' => 'exited']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$result = $this->server->validateConnection();
|
|
||||||
if ($result) {
|
|
||||||
$this->server->settings()->update([
|
|
||||||
'is_reachable' => true,
|
|
||||||
]);
|
|
||||||
$this->server->update([
|
|
||||||
'unreachable_count' => 0,
|
|
||||||
]);
|
|
||||||
} else {
|
} else {
|
||||||
$serverUptimeCheckNumber++;
|
// Precheck for containers
|
||||||
$this->server->settings()->update([
|
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
||||||
'is_reachable' => false,
|
if (!$containers) {
|
||||||
]);
|
return;
|
||||||
$this->server->update([
|
}
|
||||||
'unreachable_count' => $serverUptimeCheckNumber,
|
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
||||||
]);
|
$containerReplicates = null;
|
||||||
|
}
|
||||||
|
if (is_null($containers)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_get($this->server, 'unreachable_email_sent') === true) {
|
|
||||||
ray('Server is reachable again, sending notification...');
|
|
||||||
$this->server->team->notify(new Revived($this->server));
|
|
||||||
$this->server->update(['unreachable_email_sent' => false]);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
data_get($this->server, 'settings.is_reachable') === false ||
|
|
||||||
data_get($this->server, 'settings.is_usable') === false
|
|
||||||
) {
|
|
||||||
$this->server->settings()->update([
|
|
||||||
'is_reachable' => true,
|
|
||||||
'is_usable' => true
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
// $this->server->validateDockerEngine(true);
|
|
||||||
$containers = instant_remote_process(["docker container ls -q"], $this->server);
|
|
||||||
if (!$containers) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server);
|
|
||||||
$containers = format_docker_command_output_to_json($containers);
|
$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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
$applications = $this->server->applications();
|
$applications = $this->server->applications();
|
||||||
$databases = $this->server->databases();
|
$databases = $this->server->databases();
|
||||||
$services = $this->server->services()->get();
|
$services = $this->server->services()->get();
|
||||||
$previews = $this->server->previews();
|
$previews = $this->server->previews();
|
||||||
$this->server->proxyType();
|
|
||||||
/// Check if proxy is running
|
|
||||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
|
||||||
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));
|
|
||||||
} else {
|
|
||||||
ray('Proxy could not be started.');
|
|
||||||
}
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
$foundApplications = [];
|
$foundApplications = [];
|
||||||
$foundApplicationPreviews = [];
|
$foundApplicationPreviews = [];
|
||||||
$foundDatabases = [];
|
$foundDatabases = [];
|
||||||
$foundServices = [];
|
$foundServices = [];
|
||||||
|
|
||||||
foreach ($containers as $container) {
|
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');
|
$containerStatus = data_get($container, 'State.Status');
|
||||||
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||||
$containerStatus = "$containerStatus ($containerHealth)";
|
$containerStatus = "$containerStatus ($containerHealth)";
|
||||||
$labels = data_get($container, 'Config.Labels');
|
|
||||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||||
$applicationId = data_get($labels, 'coolify.applicationId');
|
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||||
if ($applicationId) {
|
if ($applicationId) {
|
||||||
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||||
if ($pullRequestId) {
|
if ($pullRequestId) {
|
||||||
|
if (str($applicationId)->contains('-')) {
|
||||||
|
$applicationId = str($applicationId)->before('-');
|
||||||
|
}
|
||||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||||
if ($preview) {
|
if ($preview) {
|
||||||
$foundApplicationPreviews[] = $preview->id;
|
$foundApplicationPreviews[] = $preview->id;
|
||||||
@@ -186,11 +139,25 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($uuid) {
|
if ($uuid) {
|
||||||
$database = $databases->where('uuid', $uuid)->first();
|
$database = $databases->where('uuid', $uuid)->first();
|
||||||
if ($database) {
|
if ($database) {
|
||||||
|
$isPublic = data_get($database, 'is_public');
|
||||||
$foundDatabases[] = $database->id;
|
$foundDatabases[] = $database->id;
|
||||||
$statusFromDb = $database->status;
|
$statusFromDb = $database->status;
|
||||||
if ($statusFromDb !== $containerStatus) {
|
if ($statusFromDb !== $containerStatus) {
|
||||||
$database->update(['status' => $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 {
|
} else {
|
||||||
// Notify user that this container should not be there.
|
// Notify user that this container should not be there.
|
||||||
}
|
}
|
||||||
@@ -252,8 +219,10 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
if ($projectUuid && $serviceUuid && $environmentName) {
|
if ($projectUuid && $serviceUuid && $environmentName) {
|
||||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid;
|
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/service/" . $serviceUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
}
|
}
|
||||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
$this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
$exitedService->update(['status' => 'exited']);
|
$exitedService->update(['status' => 'exited']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,9 +245,11 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
if ($projectUuid && $applicationUuid && $environment) {
|
if ($projectUuid && $applicationUuid && $environment) {
|
||||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid;
|
$url = base_url() . '/project/' . $projectUuid . "/" . $environment . "/application/" . $applicationUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
$this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
}
|
}
|
||||||
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
||||||
foreach ($notRunningApplicationPreviews as $previewId) {
|
foreach ($notRunningApplicationPreviews as $previewId) {
|
||||||
@@ -299,9 +270,11 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
if ($projectUuid && $applicationUuid && $environmentName) {
|
if ($projectUuid && $applicationUuid && $environmentName) {
|
||||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid;
|
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/application/" . $applicationUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
$this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
}
|
}
|
||||||
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
||||||
foreach ($notRunningDatabases as $database) {
|
foreach ($notRunningDatabases as $database) {
|
||||||
@@ -322,13 +295,41 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
if ($projectUuid && $databaseUuid && $environmentName) {
|
if ($projectUuid && $databaseUuid && $environmentName) {
|
||||||
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid;
|
$url = base_url() . '/project/' . $projectUuid . "/" . $environmentName . "/database/" . $databaseUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
}
|
}
|
||||||
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
|
$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) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('ContainerStatusJob failed with: ' . $e->getMessage());
|
send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class CoolifyTask implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
public Activity $activity,
|
public Activity $activity,
|
||||||
public bool $ignore_errors = false,
|
public bool $ignore_errors = false,
|
||||||
|
public $call_event_on_finish = null
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ class CoolifyTask implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$remote_process = resolve(RunRemoteProcess::class, [
|
$remote_process = resolve(RunRemoteProcess::class, [
|
||||||
'activity' => $this->activity,
|
'activity' => $this->activity,
|
||||||
'ignore_errors' => $this->ignore_errors,
|
'ignore_errors' => $this->ignore_errors,
|
||||||
|
'call_event_on_finish' => $this->call_event_on_finish
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$remote_process();
|
$remote_process();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Actions\Database\StopDatabase;
|
use App\Actions\Database\StopDatabase;
|
||||||
|
use App\Events\BackupCreated;
|
||||||
use App\Models\S3Storage;
|
use App\Models\S3Storage;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledDatabaseBackupExecution;
|
use App\Models\ScheduledDatabaseBackupExecution;
|
||||||
@@ -36,6 +37,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database;
|
public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database;
|
||||||
|
|
||||||
public ?string $container_name = null;
|
public ?string $container_name = null;
|
||||||
|
public ?string $directory_name = null;
|
||||||
public ?ScheduledDatabaseBackupExecution $backup_log = null;
|
public ?ScheduledDatabaseBackupExecution $backup_log = null;
|
||||||
public string $backup_status = 'failed';
|
public string $backup_status = 'failed';
|
||||||
public ?string $backup_location = null;
|
public ?string $backup_location = null;
|
||||||
@@ -73,6 +75,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
BackupCreated::dispatch($this->team->id);
|
||||||
// Check if team is exists
|
// Check if team is exists
|
||||||
if (is_null($this->team)) {
|
if (is_null($this->team)) {
|
||||||
$this->backup->update(['status' => 'failed']);
|
$this->backup->update(['status' => 'failed']);
|
||||||
@@ -88,27 +91,97 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||||
$databaseType = $this->database->databaseType();
|
$databaseType = $this->database->databaseType();
|
||||||
$serviceUuid = $this->database->service->uuid;
|
$serviceUuid = $this->database->service->uuid;
|
||||||
|
$serviceName = str($this->database->service->name)->slug();
|
||||||
if ($databaseType === 'standalone-postgresql') {
|
if ($databaseType === 'standalone-postgresql') {
|
||||||
$this->container_name = "postgresql-$serviceUuid";
|
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||||
|
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||||
$commands[] = "docker exec $this->container_name env | grep POSTGRES_";
|
$commands[] = "docker exec $this->container_name env | grep POSTGRES_";
|
||||||
$envs = instant_remote_process($commands, $this->server);
|
$envs = instant_remote_process($commands, $this->server);
|
||||||
$databasesToBackup = Str::of($envs)->after('POSTGRES_DB=')->before("\n")->value();
|
$envs = str($envs)->explode("\n");
|
||||||
$this->database->postgres_user = Str::of($envs)->after('POSTGRES_USER=')->before("\n")->value();
|
|
||||||
|
$user = $envs->filter(function ($env) {
|
||||||
|
return str($env)->startsWith('POSTGRES_USER=');
|
||||||
|
})->first();
|
||||||
|
if ($user) {
|
||||||
|
$this->database->postgres_user = str($user)->after('POSTGRES_USER=')->value();
|
||||||
|
} else {
|
||||||
|
$this->database->postgres_user = 'postgres';
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = $envs->filter(function ($env) {
|
||||||
|
return str($env)->startsWith('POSTGRES_DB=');
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($db) {
|
||||||
|
$databasesToBackup = str($db)->after('POSTGRES_DB=')->value();
|
||||||
|
} else {
|
||||||
|
$databasesToBackup = $this->database->postgres_user;
|
||||||
|
}
|
||||||
} else if ($databaseType === 'standalone-mysql') {
|
} else if ($databaseType === 'standalone-mysql') {
|
||||||
$this->container_name = "mysql-$serviceUuid";
|
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||||
|
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||||
$commands[] = "docker exec $this->container_name env | grep MYSQL_";
|
$commands[] = "docker exec $this->container_name env | grep MYSQL_";
|
||||||
$envs = instant_remote_process($commands, $this->server);
|
$envs = instant_remote_process($commands, $this->server);
|
||||||
$databasesToBackup = Str::of($envs)->after('MYSQL_DATABASE=')->before("\n")->value();
|
$envs = str($envs)->explode("\n");
|
||||||
$this->database->mysql_root_password = Str::of($envs)->after('MYSQL_ROOT_PASSWORD=')->before("\n")->value();
|
|
||||||
|
$rootPassword = $envs->filter(function ($env) {
|
||||||
|
return str($env)->startsWith('MYSQL_ROOT_PASSWORD=');
|
||||||
|
})->first();
|
||||||
|
if ($rootPassword) {
|
||||||
|
$this->database->mysql_root_password = str($rootPassword)->after('MYSQL_ROOT_PASSWORD=')->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = $envs->filter(function ($env) {
|
||||||
|
return str($env)->startsWith('MYSQL_DATABASE=');
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($db) {
|
||||||
|
$databasesToBackup = str($db)->after('MYSQL_DATABASE=')->value();
|
||||||
|
} else {
|
||||||
|
throw new \Exception('MYSQL_DATABASE not found');
|
||||||
|
}
|
||||||
} else if ($databaseType === 'standalone-mariadb') {
|
} else if ($databaseType === 'standalone-mariadb') {
|
||||||
$this->container_name = "mariadb-$serviceUuid";
|
$this->container_name = "{$this->database->name}-$serviceUuid";
|
||||||
$commands[] = "docker exec $this->container_name env | grep MARIADB_";
|
$this->directory_name = $serviceName . '-' . $this->container_name;
|
||||||
|
$commands[] = "docker exec $this->container_name env";
|
||||||
$envs = instant_remote_process($commands, $this->server);
|
$envs = instant_remote_process($commands, $this->server);
|
||||||
$databasesToBackup = Str::of($envs)->after('MARIADB_DATABASE=')->before("\n")->value();
|
$envs = str($envs)->explode("\n");
|
||||||
$this->database->mysql_root_password = Str::of($envs)->after('MARIADB_ROOT_PASSWORD=')->before("\n")->value();
|
$rootPassword = $envs->filter(function ($env) {
|
||||||
|
return str($env)->startsWith('MARIADB_ROOT_PASSWORD=');
|
||||||
|
})->first();
|
||||||
|
if ($rootPassword) {
|
||||||
|
$this->database->mariadb_root_password = str($rootPassword)->after('MARIADB_ROOT_PASSWORD=')->value();
|
||||||
|
} else {
|
||||||
|
$rootPassword = $envs->filter(function ($env) {
|
||||||
|
return str($env)->startsWith('MYSQL_ROOT_PASSWORD=');
|
||||||
|
})->first();
|
||||||
|
if ($rootPassword) {
|
||||||
|
$this->database->mariadb_root_password = str($rootPassword)->after('MYSQL_ROOT_PASSWORD=')->value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = $envs->filter(function ($env) {
|
||||||
|
return str($env)->startsWith('MARIADB_DATABASE=');
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($db) {
|
||||||
|
$databasesToBackup = str($db)->after('MARIADB_DATABASE=')->value();
|
||||||
|
} else {
|
||||||
|
$db = $envs->filter(function ($env) {
|
||||||
|
return str($env)->startsWith('MYSQL_DATABASE=');
|
||||||
|
})->first();
|
||||||
|
|
||||||
|
if ($db) {
|
||||||
|
$databasesToBackup = str($db)->after('MYSQL_DATABASE=')->value();
|
||||||
|
} else {
|
||||||
|
throw new \Exception('MARIADB_DATABASE or MYSQL_DATABASE not found');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
$databaseName = str($this->database->name)->slug()->value();
|
||||||
$this->container_name = $this->database->uuid;
|
$this->container_name = $this->database->uuid;
|
||||||
|
$this->directory_name = $databaseName . '-' . $this->container_name;
|
||||||
$databaseType = $this->database->type();
|
$databaseType = $this->database->type();
|
||||||
$databasesToBackup = data_get($this->backup, 'databases_to_backup');
|
$databasesToBackup = data_get($this->backup, 'databases_to_backup');
|
||||||
}
|
}
|
||||||
@@ -147,11 +220,11 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->backup_dir = backup_dir() . "/databases/" . Str::of($this->team->name)->slug() . '-' . $this->team->id . '/' . $this->container_name;
|
$this->backup_dir = backup_dir() . "/databases/" . Str::of($this->team->name)->slug() . '-' . $this->team->id . '/' . $this->directory_name;
|
||||||
|
|
||||||
if ($this->database->name === 'coolify-db') {
|
if ($this->database->name === 'coolify-db') {
|
||||||
$databasesToBackup = ['coolify'];
|
$databasesToBackup = ['coolify'];
|
||||||
$this->container_name = "coolify-db";
|
$this->directory_name = $this->container_name = "coolify-db";
|
||||||
$ip = Str::slug($this->server->ip);
|
$ip = Str::slug($this->server->ip);
|
||||||
$this->backup_dir = backup_dir() . "/coolify" . "/coolify-db-$ip";
|
$this->backup_dir = backup_dir() . "/coolify" . "/coolify-db-$ip";
|
||||||
}
|
}
|
||||||
@@ -213,7 +286,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
if ($this->backup->save_s3) {
|
if ($this->backup->save_s3) {
|
||||||
$this->upload_to_s3();
|
$this->upload_to_s3();
|
||||||
}
|
}
|
||||||
$this->team->notify(new BackupSuccess($this->backup, $this->database));
|
$this->team?->notify(new BackupSuccess($this->backup, $this->database));
|
||||||
$this->backup_log->update([
|
$this->backup_log->update([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => $this->backup_output,
|
'message' => $this->backup_output,
|
||||||
@@ -229,13 +302,15 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||||
$this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
$this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
|
} finally {
|
||||||
|
BackupCreated::dispatch($this->team->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function backup_standalone_mongodb(string $databaseWithCollections): void
|
private function backup_standalone_mongodb(string $databaseWithCollections): void
|
||||||
@@ -363,7 +438,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// $region = $this->s3->region;
|
// $region = $this->s3->region;
|
||||||
$bucket = $this->s3->bucket;
|
$bucket = $this->s3->bucket;
|
||||||
$endpoint = $this->s3->endpoint;
|
$endpoint = $this->s3->endpoint;
|
||||||
$this->s3->testConnection();
|
$this->s3->testConnection(shouldSave: true);
|
||||||
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||||
$network = $this->database->service->destination->network;
|
$network = $this->database->service->destination->network;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace App\Jobs;
|
|||||||
|
|
||||||
use App\Actions\Application\StopApplication;
|
use App\Actions\Application\StopApplication;
|
||||||
use App\Actions\Database\StopDatabase;
|
use App\Actions\Database\StopDatabase;
|
||||||
use App\Actions\Service\StopService;
|
use App\Actions\Service\DeleteService;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
@@ -32,9 +32,10 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
try {
|
try {
|
||||||
$server = $this->resource->destination->server;
|
$server = $this->resource->destination->server;
|
||||||
if (!$server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
$this->resource->delete();
|
$this->resource->forceDelete();
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
|
$this->resource->delete();
|
||||||
switch ($this->resource->type()) {
|
switch ($this->resource->type()) {
|
||||||
case 'application':
|
case 'application':
|
||||||
StopApplication::run($this->resource);
|
StopApplication::run($this->resource);
|
||||||
@@ -54,11 +55,12 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
case 'standalone-mariadb':
|
case 'standalone-mariadb':
|
||||||
StopDatabase::run($this->resource);
|
StopDatabase::run($this->resource);
|
||||||
break;
|
break;
|
||||||
case 'service':
|
|
||||||
StopService::run($this->resource);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
$this->resource->delete();
|
if ($this->resource->type() === 'service') {
|
||||||
|
DeleteService::dispatch($this->resource);
|
||||||
|
} else {
|
||||||
|
$this->resource->forceDelete();
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
|
|||||||
@@ -10,68 +10,58 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
|||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use RuntimeException;
|
||||||
|
|
||||||
class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted
|
class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public $timeout = 1000;
|
public $timeout = 300;
|
||||||
public ?string $dockerRootFilesystem = null;
|
|
||||||
public ?int $usageBefore = null;
|
public ?int $usageBefore = null;
|
||||||
|
|
||||||
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 __construct(public Server $server)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$queuedCount = 0;
|
|
||||||
$this->server->applications()->each(function ($application) use ($queuedCount) {
|
|
||||||
$count = data_get($application->deployments(), 'count', 0);
|
|
||||||
$queuedCount += $count;
|
|
||||||
});
|
|
||||||
if ($queuedCount > 0) {
|
|
||||||
ray('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping')->color('orange');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
|
$isInprogress = false;
|
||||||
|
$this->server->applications()->each(function ($application) use (&$isInprogress) {
|
||||||
|
if ($application->isDeploymentInprogress()) {
|
||||||
|
$isInprogress = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if ($isInprogress) {
|
||||||
|
throw new RuntimeException('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...');
|
||||||
|
}
|
||||||
if (!$this->server->isFunctional()) {
|
if (!$this->server->isFunctional()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->dockerRootFilesystem = "/";
|
$this->usageBefore = $this->server->getDiskUsage();
|
||||||
$this->usageBefore = $this->getFilesystemUsage();
|
ray('Usage before: ' . $this->usageBefore);
|
||||||
if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) {
|
if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) {
|
||||||
ray('Cleaning up ' . $this->server->name)->color('orange');
|
ray('Cleaning up ' . $this->server->name);
|
||||||
instant_remote_process(['docker image prune -af'], $this->server);
|
instant_remote_process(['docker image prune -af'], $this->server, false);
|
||||||
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $this->server);
|
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $this->server, false);
|
||||||
instant_remote_process(['docker builder prune -af'], $this->server);
|
instant_remote_process(['docker builder prune -af'], $this->server, false);
|
||||||
$usageAfter = $this->getFilesystemUsage();
|
$usageAfter = $this->server->getDiskUsage();
|
||||||
if ($usageAfter < $this->usageBefore) {
|
if ($usageAfter < $this->usageBefore) {
|
||||||
ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name)->color('orange');
|
ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||||
send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
send_internal_notification('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||||
|
Log::info('DockerCleanupJob done: Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||||
} else {
|
} else {
|
||||||
ray('DockerCleanupJob failed to save disk space on ' . $this->server->name)->color('orange');
|
Log::info('DockerCleanupJob failed to save disk space on ' . $this->server->name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ray('No need to clean up ' . $this->server->name)->color('orange');
|
ray('No need to clean up ' . $this->server->name);
|
||||||
|
Log::info('No need to clean up ' . $this->server->name);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage());
|
send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage());
|
||||||
ray($e->getMessage())->color('orange');
|
ray($e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getFilesystemUsage()
|
|
||||||
{
|
|
||||||
return instant_remote_process(["df '{$this->dockerRootFilesystem}'| tail -1 | awk '{ print $5}' | sed 's/%//g'"], $this->server, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
76
app/Jobs/ServerStatusJob.php
Normal file
76
app/Jobs/ServerStatusJob.php
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Notifications\Server\HighDiskUsage;
|
||||||
|
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 ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public ?int $disk_usage = null;
|
||||||
|
public $tries = 4;
|
||||||
|
public function backoff(): int
|
||||||
|
{
|
||||||
|
return isDev() ? 1 : 3;
|
||||||
|
}
|
||||||
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function middleware(): array
|
||||||
|
{
|
||||||
|
return [(new WithoutOverlapping($this->server->uuid))];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uniqueId(): int
|
||||||
|
{
|
||||||
|
return $this->server->uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
ray("checking server status for {$this->server->id}");
|
||||||
|
if (!$this->server->isServerReady(4)) {
|
||||||
|
throw new \RuntimeException('Server is not reachable.');
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
if ($this->server->isFunctional()) {
|
||||||
|
$this->cleanup(notify: false);
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function cleanup(bool $notify = false): void
|
||||||
|
{
|
||||||
|
$this->disk_usage = $this->server->getDiskUsage();
|
||||||
|
if ($this->disk_usage >= $this->server->settings->cleanup_after_percentage) {
|
||||||
|
if ($notify) {
|
||||||
|
if ($this->server->high_disk_usage_notification_sent) {
|
||||||
|
ray('high disk usage notification already sent');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$this->server->high_disk_usage_notification_sent = true;
|
||||||
|
$this->server->save();
|
||||||
|
$this->server->team?->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DockerCleanupJob::dispatchSync($this->server);
|
||||||
|
$this->cleanup(notify: true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->server->high_disk_usage_notification_sent = false;
|
||||||
|
$this->server->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Enums\ProcessStatus;
|
use App\Enums\ProcessStatus;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -8,7 +8,7 @@ use Spatie\Activitylog\Models\Activity;
|
|||||||
|
|
||||||
class ActivityMonitor extends Component
|
class ActivityMonitor extends Component
|
||||||
{
|
{
|
||||||
public string|null $header = null;
|
public ?string $header = null;
|
||||||
public $activityId;
|
public $activityId;
|
||||||
public $isPollingActive = false;
|
public $isPollingActive = false;
|
||||||
|
|
||||||
@@ -26,31 +26,30 @@ class ActivityMonitor extends Component
|
|||||||
|
|
||||||
public function hydrateActivity()
|
public function hydrateActivity()
|
||||||
{
|
{
|
||||||
$this->activity = Activity::query()
|
$this->activity = Activity::find($this->activityId);
|
||||||
->find($this->activityId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function polling()
|
public function polling()
|
||||||
{
|
{
|
||||||
$this->hydrateActivity();
|
$this->hydrateActivity();
|
||||||
$this->setStatus(ProcessStatus::IN_PROGRESS);
|
// $this->setStatus(ProcessStatus::IN_PROGRESS);
|
||||||
$exit_code = data_get($this->activity, 'properties.exitCode');
|
$exit_code = data_get($this->activity, 'properties.exitCode');
|
||||||
if ($exit_code !== null) {
|
if ($exit_code !== null) {
|
||||||
if ($exit_code === 0) {
|
if ($exit_code === 0) {
|
||||||
$this->setStatus(ProcessStatus::FINISHED);
|
// $this->setStatus(ProcessStatus::FINISHED);
|
||||||
} else {
|
} else {
|
||||||
$this->setStatus(ProcessStatus::ERROR);
|
// $this->setStatus(ProcessStatus::ERROR);
|
||||||
}
|
}
|
||||||
$this->isPollingActive = false;
|
$this->isPollingActive = false;
|
||||||
$this->emit('activityFinished');
|
$this->dispatch('activityFinished');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setStatus($status)
|
// protected function setStatus($status)
|
||||||
{
|
// {
|
||||||
$this->activity->properties = $this->activity->properties->merge([
|
// $this->activity->properties = $this->activity->properties->merge([
|
||||||
'status' => $status,
|
// 'status' => $status,
|
||||||
]);
|
// ]);
|
||||||
$this->activity->save();
|
// $this->activity->save();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Boarding;
|
namespace App\Livewire\Boarding;
|
||||||
|
|
||||||
use App\Actions\Server\InstallDocker;
|
use App\Actions\Server\InstallDocker;
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
@@ -31,9 +31,11 @@ class Index extends Component
|
|||||||
public ?string $remoteServerHost = null;
|
public ?string $remoteServerHost = null;
|
||||||
public ?int $remoteServerPort = 22;
|
public ?int $remoteServerPort = 22;
|
||||||
public ?string $remoteServerUser = 'root';
|
public ?string $remoteServerUser = 'root';
|
||||||
|
public bool $isSwarmManager = false;
|
||||||
|
public bool $isCloudflareTunnel = false;
|
||||||
public ?Server $createdServer = null;
|
public ?Server $createdServer = null;
|
||||||
|
|
||||||
public Collection|array $projects = [];
|
public Collection $projects;
|
||||||
public ?int $selectedExistingProject = null;
|
public ?int $selectedExistingProject = null;
|
||||||
public ?Project $createdProject = null;
|
public ?Project $createdProject = null;
|
||||||
|
|
||||||
@@ -86,7 +88,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
if ($this->selectedServerType === 'localhost') {
|
if ($this->selectedServerType === 'localhost') {
|
||||||
$this->createdServer = Server::find(0);
|
$this->createdServer = Server::find(0);
|
||||||
if (!$this->createdServer) {
|
if (!$this->createdServer) {
|
||||||
return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
|
return $this->dispatch('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
|
||||||
}
|
}
|
||||||
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
||||||
return $this->validateServer('localhost');
|
return $this->validateServer('localhost');
|
||||||
@@ -108,7 +110,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
{
|
{
|
||||||
$this->createdServer = Server::find($this->selectedExistingServer);
|
$this->createdServer = Server::find($this->selectedExistingServer);
|
||||||
if (!$this->createdServer) {
|
if (!$this->createdServer) {
|
||||||
$this->emit('error', 'Server is not found.');
|
$this->dispatch('error', 'Server is not found.');
|
||||||
$this->currentState = 'private-key';
|
$this->currentState = 'private-key';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -171,7 +173,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->privateKey = formatPrivateKey($this->privateKey);
|
$this->privateKey = formatPrivateKey($this->privateKey);
|
||||||
$foundServer = Server::whereIp($this->remoteServerHost)->first();
|
$foundServer = Server::whereIp($this->remoteServerHost)->first();
|
||||||
if ($foundServer) {
|
if ($foundServer) {
|
||||||
return $this->emit('error', 'IP address is already in use by another team.');
|
return $this->dispatch('error', 'IP address is already in use by another team.');
|
||||||
}
|
}
|
||||||
$this->createdServer = Server::create([
|
$this->createdServer = Server::create([
|
||||||
'name' => $this->remoteServerName,
|
'name' => $this->remoteServerName,
|
||||||
@@ -182,13 +184,15 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
'private_key_id' => $this->createdPrivateKey->id,
|
'private_key_id' => $this->createdPrivateKey->id,
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
$this->createdServer->save();
|
$this->createdServer->settings->is_swarm_manager = $this->isSwarmManager;
|
||||||
|
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
|
||||||
|
$this->createdServer->settings->save();
|
||||||
|
$this->createdServer->addInitialNetwork();
|
||||||
$this->validateServer();
|
$this->validateServer();
|
||||||
}
|
}
|
||||||
public function validateServer()
|
public function validateServer()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$customErrorMessage = "Server is not reachable:";
|
|
||||||
config()->set('coolify.mux_enabled', false);
|
config()->set('coolify.mux_enabled', false);
|
||||||
|
|
||||||
instant_remote_process(['uptime'], $this->createdServer, true);
|
instant_remote_process(['uptime'], $this->createdServer, true);
|
||||||
@@ -198,7 +202,8 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
]);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->serverReachable = false;
|
$this->serverReachable = false;
|
||||||
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
|
$this->createdServer->delete();
|
||||||
|
return handleError(error: $e, livewire: $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -206,7 +211,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||||
if (is_null($dockerVersion)) {
|
if (is_null($dockerVersion)) {
|
||||||
$this->currentState = 'install-docker';
|
$this->currentState = 'install-docker';
|
||||||
throw new \Exception('Docker version is not supported or not installed.');
|
throw new \Exception('Docker not found or old version is installed.');
|
||||||
}
|
}
|
||||||
$this->createdServer->settings()->update([
|
$this->createdServer->settings()->update([
|
||||||
'is_usable' => true,
|
'is_usable' => true,
|
||||||
@@ -214,14 +219,20 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->getProxyType();
|
$this->getProxyType();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
// $this->dockerInstallationStarted = false;
|
// $this->dockerInstallationStarted = false;
|
||||||
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
|
return handleError(error: $e, livewire: $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function installDocker()
|
public function installDocker()
|
||||||
{
|
{
|
||||||
$this->dockerInstallationStarted = true;
|
try {
|
||||||
$activity = InstallDocker::run($this->createdServer);
|
$this->dockerInstallationStarted = true;
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$activity = InstallDocker::run($this->createdServer);
|
||||||
|
$this->dispatch('installDocker');
|
||||||
|
$this->dispatch('newMonitorActivity', $activity->id);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->dockerInstallationStarted = false;
|
||||||
|
return handleError(error: $e, livewire: $this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public function dockerInstalledOrSkipped()
|
public function dockerInstalledOrSkipped()
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Actions\License\CheckResaleLicense;
|
use App\Actions\License\CheckResaleLicense;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
@@ -33,11 +33,11 @@ class CheckLicense extends Component
|
|||||||
if ($this->settings->resale_license) {
|
if ($this->settings->resale_license) {
|
||||||
try {
|
try {
|
||||||
CheckResaleLicense::run();
|
CheckResaleLicense::run();
|
||||||
$this->emit('reloadWindow');
|
$this->dispatch('reloadWindow');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
return redirect()->to('/settings/license');
|
return $this->redirect('/settings/license', navigate: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\S3Storage;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -11,7 +10,6 @@ class Dashboard extends Component
|
|||||||
{
|
{
|
||||||
public $projects = [];
|
public $projects = [];
|
||||||
public $servers = [];
|
public $servers = [];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->servers = Server::ownedByCurrentTeam()->get();
|
$this->servers = Server::ownedByCurrentTeam()->get();
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Destination;
|
namespace App\Livewire\Destination;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -30,13 +30,13 @@ class Form extends Component
|
|||||||
try {
|
try {
|
||||||
if ($this->destination->getMorphClass() === 'App\Models\StandaloneDocker') {
|
if ($this->destination->getMorphClass() === 'App\Models\StandaloneDocker') {
|
||||||
if ($this->destination->attachedTo()) {
|
if ($this->destination->attachedTo()) {
|
||||||
return $this->emit('error', 'You must delete all resources before deleting this destination.');
|
return $this->dispatch('error', 'You must delete all resources before deleting this destination.');
|
||||||
}
|
}
|
||||||
instant_remote_process(["docker network disconnect {$this->destination->network} coolify-proxy"], $this->destination->server, throwError: false);
|
instant_remote_process(["docker network disconnect {$this->destination->network} coolify-proxy"], $this->destination->server, throwError: false);
|
||||||
instant_remote_process(['docker network rm -f ' . $this->destination->network], $this->destination->server);
|
instant_remote_process(['docker network rm -f ' . $this->destination->network], $this->destination->server);
|
||||||
}
|
}
|
||||||
$this->destination->delete();
|
$this->destination->delete();
|
||||||
return redirect()->route('dashboard');
|
return $this->redirectRoute('dashboard', navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
100
app/Livewire/Destination/New/Docker.php
Normal file
100
app/Livewire/Destination/New/Docker.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Destination\New;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
||||||
|
use App\Models\SwarmDocker;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Docker extends Component
|
||||||
|
{
|
||||||
|
public string $name;
|
||||||
|
public string $network;
|
||||||
|
|
||||||
|
public Collection $servers;
|
||||||
|
public Server $server;
|
||||||
|
public ?int $server_id = null;
|
||||||
|
public bool $is_swarm = false;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'name' => 'required|string',
|
||||||
|
'network' => 'required|string',
|
||||||
|
'server_id' => 'required|integer',
|
||||||
|
'is_swarm' => 'boolean'
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'name' => 'name',
|
||||||
|
'network' => 'network',
|
||||||
|
'server_id' => 'server',
|
||||||
|
'is_swarm' => 'swarm'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
if (request()->query('server_id')) {
|
||||||
|
$this->server_id = request()->query('server_id');
|
||||||
|
} else {
|
||||||
|
if ($this->servers->count() > 0) {
|
||||||
|
$this->server_id = $this->servers->first()->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (request()->query('network_name')) {
|
||||||
|
$this->network = request()->query('network_name');
|
||||||
|
} else {
|
||||||
|
$this->network = new Cuid2(7);
|
||||||
|
}
|
||||||
|
$this->name = str("{$this->servers->first()->name}-{$this->network}")->kebab();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate_name()
|
||||||
|
{
|
||||||
|
$this->server = Server::find($this->server_id);
|
||||||
|
$this->name = str("{$this->server->name}-{$this->network}")->kebab();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
try {
|
||||||
|
$this->server = Server::find($this->server_id);
|
||||||
|
if ($this->is_swarm) {
|
||||||
|
$found = $this->server->swarmDockers()->where('network', $this->network)->first();
|
||||||
|
if ($found) {
|
||||||
|
$this->dispatch('error', 'Network already added to this server.');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$docker = SwarmDocker::create([
|
||||||
|
'name' => $this->name,
|
||||||
|
'network' => $this->network,
|
||||||
|
'server_id' => $this->server_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
|
||||||
|
if ($found) {
|
||||||
|
$this->dispatch('error', 'Network already added to this server.');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$docker = ModelsStandaloneDocker::create([
|
||||||
|
'name' => $this->name,
|
||||||
|
'network' => $this->network,
|
||||||
|
'server_id' => $this->server_id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->createNetworkAndAttachToProxy();
|
||||||
|
return $this->redirectRoute('destination.show', $docker->uuid, navigate: true);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createNetworkAndAttachToProxy()
|
||||||
|
{
|
||||||
|
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||||
|
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Destination;
|
namespace App\Livewire\Destination;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -13,7 +13,11 @@ class Show extends Component
|
|||||||
|
|
||||||
public function scan()
|
public function scan()
|
||||||
{
|
{
|
||||||
$alreadyAddedNetworks = $this->server->standaloneDockers;
|
if ($this->server->isSwarm()) {
|
||||||
|
$alreadyAddedNetworks = $this->server->swarmDockers;
|
||||||
|
} else {
|
||||||
|
$alreadyAddedNetworks = $this->server->standaloneDockers;
|
||||||
|
}
|
||||||
$networks = instant_remote_process(['docker network ls --format "{{json .}}"'], $this->server, false);
|
$networks = instant_remote_process(['docker network ls --format "{{json .}}"'], $this->server, false);
|
||||||
$this->networks = format_docker_command_output_to_json($networks)->filter(function ($network) {
|
$this->networks = format_docker_command_output_to_json($networks)->filter(function ($network) {
|
||||||
return $network['Name'] !== 'bridge' && $network['Name'] !== 'host' && $network['Name'] !== 'none';
|
return $network['Name'] !== 'bridge' && $network['Name'] !== 'host' && $network['Name'] !== 'none';
|
||||||
@@ -21,7 +25,7 @@ class Show extends Component
|
|||||||
return !$alreadyAddedNetworks->contains('network', $network['Name']);
|
return !$alreadyAddedNetworks->contains('network', $network['Name']);
|
||||||
});
|
});
|
||||||
if ($this->networks->count() === 0) {
|
if ($this->networks->count() === 0) {
|
||||||
$this->emit('success', 'No new networks found.');
|
$this->dispatch('success', 'No new networks found.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Dev;
|
namespace App\Livewire\Dev;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
@@ -35,7 +35,7 @@ class ForcePasswordReset extends Component
|
|||||||
if ($firstLogin) {
|
if ($firstLogin) {
|
||||||
send_internal_notification('First login for ' . auth()->user()->email);
|
send_internal_notification('First login for ' . auth()->user()->email);
|
||||||
}
|
}
|
||||||
return redirect()->route('dashboard');
|
return $this->redirectRoute('dashboard', navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
@@ -42,7 +42,7 @@ class Help extends Component
|
|||||||
);
|
);
|
||||||
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
||||||
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
||||||
$this->emit('success', 'Your message has been sent successfully. <br>We will get in touch with you as soon as possible.');
|
$this->dispatch('success', 'Your message has been sent successfully. <br>We will get in touch with you as soon as possible.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
28
app/Livewire/Modal/EditCompose.php
Normal file
28
app/Livewire/Modal/EditCompose.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Modal;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use LivewireUI\Modal\ModalComponent;
|
||||||
|
|
||||||
|
class EditCompose extends ModalComponent
|
||||||
|
{
|
||||||
|
public Service $service;
|
||||||
|
public $serviceId;
|
||||||
|
protected $rules = [
|
||||||
|
'service.docker_compose_raw' => 'required',
|
||||||
|
'service.docker_compose' => 'required',
|
||||||
|
];
|
||||||
|
public function mount() {
|
||||||
|
$this->service = Service::find($this->serviceId);
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.modal.edit-compose');
|
||||||
|
}
|
||||||
|
public function submit() {
|
||||||
|
$this->dispatch('warning', "Saving new docker compose...");
|
||||||
|
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
|
||||||
|
$this->closeModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Notifications;
|
namespace App\Livewire\Notifications;
|
||||||
|
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
@@ -47,12 +47,12 @@ class DiscordSettings extends Component
|
|||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team->notify(new Test());
|
$this->team?->notify(new Test());
|
||||||
$this->emit('success', 'Test notification sent.');
|
$this->dispatch('success', 'Test notification sent.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Notifications;
|
namespace App\Livewire\Notifications;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
@@ -63,15 +63,15 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team->notify(new Test($this->emails));
|
$this->team?->notify(new Test($this->emails));
|
||||||
$this->emit('success', 'Test Email sent successfully.');
|
$this->dispatch('success', 'Test Email sent successfully.');
|
||||||
}
|
}
|
||||||
public function instantSaveInstance()
|
public function instantSaveInstance()
|
||||||
{
|
{
|
||||||
@@ -83,7 +83,7 @@ class EmailSettings extends Component
|
|||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ class EmailSettings extends Component
|
|||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
@@ -131,7 +131,7 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -148,7 +148,7 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->dispatch('success', 'Settings saved successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
@@ -173,7 +173,7 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->team = $team;
|
$this->team = $team;
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($settings->resend_enabled) {
|
if ($settings->resend_enabled) {
|
||||||
@@ -184,9 +184,9 @@ class EmailSettings extends Component
|
|||||||
]);
|
]);
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->team = $team;
|
$this->team = $team;
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->emit('error', 'Instance SMTP/Resend settings are not enabled.');
|
$this->dispatch('error', 'Instance SMTP/Resend settings are not enabled.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Notifications;
|
namespace App\Livewire\Notifications;
|
||||||
|
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
@@ -53,12 +53,12 @@ class TelegramSettings extends Component
|
|||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team->notify(new Test());
|
$this->team?->notify(new Test());
|
||||||
$this->emit('success', 'Test notification sent.');
|
$this->dispatch('success', 'Test notification sent.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\PrivateKey;
|
namespace App\Livewire\PrivateKey;
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -35,9 +35,9 @@ class Change extends Component
|
|||||||
if ($this->private_key->isEmpty()) {
|
if ($this->private_key->isEmpty()) {
|
||||||
$this->private_key->delete();
|
$this->private_key->delete();
|
||||||
currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get();
|
currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get();
|
||||||
return redirect()->route('security.private-key.index');
|
return $this->redirectRoute('security.private-key.index', navigate: true);
|
||||||
}
|
}
|
||||||
$this->emit('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.');
|
$this->dispatch('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\PrivateKey;
|
namespace App\Livewire\PrivateKey;
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
@@ -67,9 +67,9 @@ class Create extends Component
|
|||||||
'team_id' => currentTeam()->id
|
'team_id' => currentTeam()->id
|
||||||
]);
|
]);
|
||||||
if ($this->from === 'server') {
|
if ($this->from === 'server') {
|
||||||
return redirect()->route('server.create');
|
return $this->redirectRoute('server.create', navigate: true);
|
||||||
}
|
}
|
||||||
return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
return $this->redirectRoute('security.private-key.show', ['private_key_uuid' => $private_key->uuid], navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Profile;
|
namespace App\Livewire\Profile;
|
||||||
|
|
||||||
use App\Models\User;
|
use Livewire\Attributes\Validate;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Form extends Component
|
class Form extends Component
|
||||||
{
|
{
|
||||||
public int $userId;
|
public int $userId;
|
||||||
public string $name;
|
|
||||||
public string $email;
|
public string $email;
|
||||||
|
|
||||||
protected $rules = [
|
#[Validate('required')]
|
||||||
'name' => 'required',
|
public string $name;
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'name' => 'name',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -30,9 +25,11 @@ class Form extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
User::where('id', $this->userId)->update([
|
auth()->user()->update([
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->dispatch('success', 'Profile updated successfully.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project;
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -27,7 +27,7 @@ class AddEmpty extends Component
|
|||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
return redirect()->route('project.show', $project->uuid);
|
return $this->redirectRoute('project.show', $project->uuid, navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project;
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
use App\Models\Environment;
|
use App\Models\Environment;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
@@ -27,10 +27,10 @@ class AddEnvironment extends Component
|
|||||||
'project_id' => $this->project->id,
|
'project_id' => $this->project->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('project.resources', [
|
return $this->redirectRoute('project.resources', [
|
||||||
'project_uuid' => $this->project->uuid,
|
'project_uuid' => $this->project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
]);
|
], navigate: true);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
handleError($e, $this);
|
handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
54
app/Livewire/Project/Application/Advanced.php
Normal file
54
app/Livewire/Project/Application/Advanced.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Advanced extends Component
|
||||||
|
{
|
||||||
|
public Application $application;
|
||||||
|
protected $rules = [
|
||||||
|
'application.settings.is_git_submodules_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_git_lfs_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_preview_deployments_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_auto_deploy_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_force_https_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_log_drain_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_gpu_enabled' => 'boolean|required',
|
||||||
|
'application.settings.gpu_driver' => 'string|required',
|
||||||
|
'application.settings.gpu_count' => 'string|required',
|
||||||
|
'application.settings.gpu_device_ids' => 'string|required',
|
||||||
|
'application.settings.gpu_options' => 'string|required',
|
||||||
|
];
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
if ($this->application->isLogDrainEnabled()) {
|
||||||
|
if (!$this->application->destination->server->isLogDrainEnabled()) {
|
||||||
|
$this->application->settings->is_log_drain_enabled = false;
|
||||||
|
$this->dispatch('error', 'Log drain is not enabled on this server.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->application->settings->is_force_https_enabled) {
|
||||||
|
$this->dispatch('resetDefaultLabels', false);
|
||||||
|
}
|
||||||
|
$this->application->settings->save();
|
||||||
|
$this->dispatch('success', 'Settings saved.');
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
$this->application->settings->gpu_device_ids = null;
|
||||||
|
$this->application->settings->save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->application->settings->save();
|
||||||
|
$this->dispatch('success', 'Settings saved.');
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.application.advanced');
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Livewire/Project/Application/Configuration.php
Normal file
39
app/Livewire/Project/Application/Configuration.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\StandaloneDocker;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Configuration extends Component
|
||||||
|
{
|
||||||
|
public Application $application;
|
||||||
|
public $servers;
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
|
if (!$project) {
|
||||||
|
return $this->redirectRoute('dashboard', navigate: true);
|
||||||
|
}
|
||||||
|
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
|
||||||
|
if (!$environment) {
|
||||||
|
return $this->redirectRoute('dashboard', navigate: true);
|
||||||
|
}
|
||||||
|
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
|
||||||
|
if (!$application) {
|
||||||
|
return $this->redirectRoute('dashboard', navigate: true);
|
||||||
|
}
|
||||||
|
$this->application = $application;
|
||||||
|
$mainServer = $this->application->destination->server;
|
||||||
|
$servers = Server::ownedByCurrentTeam()->get();
|
||||||
|
$this->servers = $servers->filter(function ($server) use ($mainServer) {
|
||||||
|
return $server->id != $mainServer->id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.application.configuration');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@@ -18,7 +18,7 @@ class DeploymentLogs extends Component
|
|||||||
|
|
||||||
public function polling()
|
public function polling()
|
||||||
{
|
{
|
||||||
$this->emit('deploymentFinished');
|
$this->dispatch('deploymentFinished');
|
||||||
$this->application_deployment_queue->refresh();
|
$this->application_deployment_queue->refresh();
|
||||||
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
|
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
|
||||||
$this->isKeepAliveOn = false;
|
$this->isKeepAliveOn = false;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
@@ -18,7 +18,6 @@ class DeploymentNavbar extends Component
|
|||||||
public Server $server;
|
public Server $server;
|
||||||
public bool $is_debug_enabled = false;
|
public bool $is_debug_enabled = false;
|
||||||
protected $listeners = ['deploymentFinished'];
|
protected $listeners = ['deploymentFinished'];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->application = Application::find($this->application_deployment_queue->application_id);
|
$this->application = Application::find($this->application_deployment_queue->application_id);
|
||||||
@@ -36,7 +35,7 @@ class DeploymentNavbar extends Component
|
|||||||
$this->application->settings->is_debug_enabled = !$this->application->settings->is_debug_enabled;
|
$this->application->settings->is_debug_enabled = !$this->application->settings->is_debug_enabled;
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
$this->emit('refreshQueue');
|
$this->dispatch('refreshQueue');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cancel()
|
public function cancel()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
class General extends Component
|
class General extends Component
|
||||||
{
|
{
|
||||||
@@ -25,15 +26,15 @@ class General extends Component
|
|||||||
public bool $labelsChanged = false;
|
public bool $labelsChanged = false;
|
||||||
public bool $isConfigurationChanged = false;
|
public bool $isConfigurationChanged = false;
|
||||||
|
|
||||||
public bool $is_static;
|
public ?string $initialDockerComposeLocation = null;
|
||||||
public bool $is_git_submodules_enabled;
|
public ?string $initialDockerComposePrLocation = null;
|
||||||
public bool $is_git_lfs_enabled;
|
|
||||||
public bool $is_debug_enabled;
|
|
||||||
public bool $is_preview_deployments_enabled;
|
|
||||||
public bool $is_auto_deploy_enabled;
|
|
||||||
public bool $is_force_https_enabled;
|
|
||||||
|
|
||||||
|
public $parsedServices = [];
|
||||||
|
public $parsedServiceDomains = [];
|
||||||
|
|
||||||
|
protected $listeners = [
|
||||||
|
'resetDefaultLabels'
|
||||||
|
];
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'application.name' => 'required',
|
'application.name' => 'required',
|
||||||
'application.description' => 'nullable',
|
'application.description' => 'nullable',
|
||||||
@@ -54,8 +55,17 @@ class General extends Component
|
|||||||
'application.docker_registry_image_name' => 'nullable',
|
'application.docker_registry_image_name' => 'nullable',
|
||||||
'application.docker_registry_image_tag' => 'nullable',
|
'application.docker_registry_image_tag' => 'nullable',
|
||||||
'application.dockerfile_location' => 'nullable',
|
'application.dockerfile_location' => 'nullable',
|
||||||
|
'application.docker_compose_location' => 'nullable',
|
||||||
|
'application.docker_compose_pr_location' => 'nullable',
|
||||||
|
'application.docker_compose' => 'nullable',
|
||||||
|
'application.docker_compose_pr' => 'nullable',
|
||||||
|
'application.docker_compose_raw' => 'nullable',
|
||||||
|
'application.docker_compose_pr_raw' => 'nullable',
|
||||||
'application.custom_labels' => 'nullable',
|
'application.custom_labels' => 'nullable',
|
||||||
'application.dockerfile_target_build' => 'nullable',
|
'application.dockerfile_target_build' => 'nullable',
|
||||||
|
'application.settings.is_static' => 'boolean|required',
|
||||||
|
'application.docker_compose_custom_start_command' => 'nullable',
|
||||||
|
'application.docker_compose_custom_build_command' => 'nullable',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@@ -77,37 +87,80 @@ class General extends Component
|
|||||||
'application.docker_registry_image_name' => 'Docker registry image name',
|
'application.docker_registry_image_name' => 'Docker registry image name',
|
||||||
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
||||||
'application.dockerfile_location' => 'Dockerfile location',
|
'application.dockerfile_location' => 'Dockerfile location',
|
||||||
|
'application.docker_compose_location' => 'Docker compose location',
|
||||||
|
'application.docker_compose_pr_location' => 'Docker compose location',
|
||||||
|
'application.docker_compose' => 'Docker compose',
|
||||||
|
'application.docker_compose_pr' => 'Docker compose',
|
||||||
|
'application.docker_compose_raw' => 'Docker compose raw',
|
||||||
|
'application.docker_compose_pr_raw' => 'Docker compose raw',
|
||||||
'application.custom_labels' => 'Custom labels',
|
'application.custom_labels' => 'Custom labels',
|
||||||
'application.dockerfile_target_build' => 'Dockerfile target build',
|
'application.dockerfile_target_build' => 'Dockerfile target build',
|
||||||
|
'application.settings.is_static' => 'Is static',
|
||||||
|
'application.docker_compose_custom_start_command' => 'Docker compose custom start command',
|
||||||
|
'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
|
$this->parsedServices = $this->application->parseCompose();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->dispatch('error', $e->getMessage());
|
||||||
|
}
|
||||||
|
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
|
||||||
|
|
||||||
$this->ports_exposes = $this->application->ports_exposes;
|
$this->ports_exposes = $this->application->ports_exposes;
|
||||||
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
||||||
$this->application->isConfigurationChanged(true);
|
$this->application->isConfigurationChanged(true);
|
||||||
}
|
}
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
if (is_null(data_get($this->application, 'custom_labels'))) {
|
$this->customLabels = $this->application->parseContainerLabels();
|
||||||
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||||
} else {
|
|
||||||
$this->customLabels = str($this->application->custom_labels)->replace(',', "\n");
|
|
||||||
}
|
|
||||||
if (data_get($this->application, 'settings')) {
|
|
||||||
$this->is_static = $this->application->settings->is_static;
|
|
||||||
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
|
||||||
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
|
||||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
|
||||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
|
||||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
|
||||||
}
|
|
||||||
$this->checkLabelUpdates();
|
$this->checkLabelUpdates();
|
||||||
}
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->application->settings->save();
|
||||||
|
$this->dispatch('success', 'Settings saved.');
|
||||||
|
$this->application->refresh();
|
||||||
|
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
||||||
|
$this->resetDefaultLabels(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function loadComposeFile($isInit = false)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($isInit && $this->application->docker_compose_raw) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation, 'initialDockerComposePrLocation' => $this->initialDockerComposePrLocation] = $this->application->loadComposeFile($isInit);
|
||||||
|
$this->dispatch('success', 'Docker compose file loaded.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->application->docker_compose_location = $this->initialDockerComposeLocation;
|
||||||
|
$this->application->docker_compose_pr_location = $this->initialDockerComposePrLocation;
|
||||||
|
$this->application->save();
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function generateDomain(string $serviceName)
|
||||||
|
{
|
||||||
|
$domain = $this->parsedServiceDomains[$serviceName]['domain'] ?? null;
|
||||||
|
if (!$domain) {
|
||||||
|
$uuid = new Cuid2(7);
|
||||||
|
$domain = generateFqdn($this->application->destination->server, $uuid);
|
||||||
|
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
|
||||||
|
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||||
|
$this->application->save();
|
||||||
|
}
|
||||||
|
return $domain;
|
||||||
|
}
|
||||||
public function updatedApplicationBuildPack()
|
public function updatedApplicationBuildPack()
|
||||||
{
|
{
|
||||||
if ($this->application->build_pack !== 'nixpacks') {
|
if ($this->application->build_pack !== 'nixpacks') {
|
||||||
$this->application->settings->is_static = $this->is_static = false;
|
$this->application->settings->is_static = false;
|
||||||
|
$this->application->settings->save();
|
||||||
|
}
|
||||||
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
|
$this->application->fqdn = null;
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
}
|
}
|
||||||
$this->submit();
|
$this->submit();
|
||||||
@@ -120,32 +173,6 @@ class General extends Component
|
|||||||
$this->labelsChanged = false;
|
$this->labelsChanged = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSave()
|
|
||||||
{
|
|
||||||
// @TODO: find another way - if possible
|
|
||||||
$force_https = $this->application->settings->is_force_https_enabled;
|
|
||||||
$this->application->settings->is_static = $this->is_static;
|
|
||||||
if ($this->is_static) {
|
|
||||||
$this->application->ports_exposes = 80;
|
|
||||||
} else {
|
|
||||||
$this->application->ports_exposes = 3000;
|
|
||||||
}
|
|
||||||
$this->application->settings->is_git_submodules_enabled = $this->is_git_submodules_enabled;
|
|
||||||
$this->application->settings->is_git_lfs_enabled = $this->is_git_lfs_enabled;
|
|
||||||
$this->application->settings->is_debug_enabled = $this->is_debug_enabled;
|
|
||||||
$this->application->settings->is_preview_deployments_enabled = $this->is_preview_deployments_enabled;
|
|
||||||
$this->application->settings->is_auto_deploy_enabled = $this->is_auto_deploy_enabled;
|
|
||||||
$this->application->settings->is_force_https_enabled = $this->is_force_https_enabled;
|
|
||||||
$this->application->settings->save();
|
|
||||||
$this->application->save();
|
|
||||||
$this->application->refresh();
|
|
||||||
$this->emit('success', 'Application settings updated!');
|
|
||||||
$this->checkLabelUpdates();
|
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
|
||||||
if ($force_https !== $this->is_force_https_enabled) {
|
|
||||||
$this->resetDefaultLabels(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getWildcardDomain()
|
public function getWildcardDomain()
|
||||||
{
|
{
|
||||||
@@ -167,11 +194,15 @@ class General extends Component
|
|||||||
public function updatedApplicationFqdn()
|
public function updatedApplicationFqdn()
|
||||||
{
|
{
|
||||||
$this->resetDefaultLabels(false);
|
$this->resetDefaultLabels(false);
|
||||||
$this->emit('success', 'Labels reseted to default!');
|
$this->dispatch('success', 'Labels reseted to default!');
|
||||||
}
|
}
|
||||||
public function submit($showToaster = true)
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
ray($this->initialDockerComposeLocation, $this->application->docker_compose_location);
|
||||||
|
if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
|
||||||
|
$this->loadComposeFile();
|
||||||
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
||||||
$this->resetDefaultLabels(false);
|
$this->resetDefaultLabels(false);
|
||||||
@@ -188,6 +219,7 @@ class General extends Component
|
|||||||
});
|
});
|
||||||
$this->application->fqdn = $domains->implode(',');
|
$this->application->fqdn = $domains->implode(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_get($this->application, 'dockerfile')) {
|
if (data_get($this->application, 'dockerfile')) {
|
||||||
$port = get_port_from_dockerfile($this->application->dockerfile);
|
$port = get_port_from_dockerfile($this->application->dockerfile);
|
||||||
if ($port && !$this->application->ports_exposes) {
|
if ($port && !$this->application->ports_exposes) {
|
||||||
@@ -200,12 +232,13 @@ class General extends Component
|
|||||||
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
||||||
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
||||||
}
|
}
|
||||||
if (gettype($this->customLabels) === 'string') {
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
$this->customLabels = str($this->customLabels)->replace(',', "\n");
|
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||||
|
$this->parsedServices = $this->application->parseCompose();
|
||||||
}
|
}
|
||||||
$this->application->custom_labels = $this->customLabels->explode("\n")->implode(',');
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$showToaster && $this->emit('success', 'Application settings updated!');
|
$showToaster && $this->dispatch('success', 'Application settings updated!');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
138
app/Livewire/Project/Application/Heading.php
Normal file
138
app/Livewire/Project/Application/Heading.php
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
|
use App\Actions\Application\StopApplication;
|
||||||
|
use App\Events\ApplicationStatusChanged;
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
|
use App\Jobs\ServerStatusJob;
|
||||||
|
use App\Models\Application;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Heading extends Component
|
||||||
|
{
|
||||||
|
public Application $application;
|
||||||
|
public array $parameters;
|
||||||
|
|
||||||
|
protected string $deploymentUuid;
|
||||||
|
public function getListeners()
|
||||||
|
{
|
||||||
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
|
return [
|
||||||
|
"echo-private:team.{$teamId},ApplicationStatusChanged" => 'check_status',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check_status($showNotification = false)
|
||||||
|
{
|
||||||
|
if ($this->application->destination->server->isFunctional()) {
|
||||||
|
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||||
|
$this->application->refresh();
|
||||||
|
$this->application->previews->each(function ($preview) {
|
||||||
|
$preview->refresh();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dispatch(new ServerStatusJob($this->application->destination->server));
|
||||||
|
}
|
||||||
|
if ($showNotification) $this->dispatch('success', 'Application status updated.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function force_deploy_without_cache()
|
||||||
|
{
|
||||||
|
$this->deploy(force_rebuild: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deployNew()
|
||||||
|
{
|
||||||
|
if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) {
|
||||||
|
$this->dispatch('error', 'Please load a Compose file first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->setDeploymentUuid();
|
||||||
|
queue_application_deployment(
|
||||||
|
application_id: $this->application->id,
|
||||||
|
deployment_uuid: $this->deploymentUuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_new_deployment: true,
|
||||||
|
);
|
||||||
|
return $this->redirectRoute('project.application.deployment', [
|
||||||
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
|
], navigate: true);
|
||||||
|
}
|
||||||
|
public function deploy(bool $force_rebuild = false)
|
||||||
|
{
|
||||||
|
if ($this->application->build_pack === 'dockercompose' && is_null($this->application->docker_compose_raw)) {
|
||||||
|
$this->dispatch('error', 'Please load a Compose file first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($this->application->destination->server->isSwarm() && is_null($this->application->docker_registry_image_name)) {
|
||||||
|
$this->dispatch('error', 'Please set a Docker image name first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->setDeploymentUuid();
|
||||||
|
queue_application_deployment(
|
||||||
|
application_id: $this->application->id,
|
||||||
|
deployment_uuid: $this->deploymentUuid,
|
||||||
|
force_rebuild: $force_rebuild,
|
||||||
|
);
|
||||||
|
$this->redirectRoute('project.application.deployment', [
|
||||||
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
|
], navigate: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setDeploymentUuid()
|
||||||
|
{
|
||||||
|
$this->deploymentUuid = new Cuid2(7);
|
||||||
|
$this->parameters['deployment_uuid'] = $this->deploymentUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stop()
|
||||||
|
{
|
||||||
|
StopApplication::run($this->application);
|
||||||
|
$this->application->status = 'exited';
|
||||||
|
$this->application->save();
|
||||||
|
$this->application->refresh();
|
||||||
|
}
|
||||||
|
public function restartNew()
|
||||||
|
{
|
||||||
|
$this->setDeploymentUuid();
|
||||||
|
queue_application_deployment(
|
||||||
|
application_id: $this->application->id,
|
||||||
|
deployment_uuid: $this->deploymentUuid,
|
||||||
|
restart_only: true,
|
||||||
|
is_new_deployment: true,
|
||||||
|
);
|
||||||
|
return $this->redirectRoute('project.application.deployment', [
|
||||||
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
|
], navigate: true);
|
||||||
|
}
|
||||||
|
public function restart()
|
||||||
|
{
|
||||||
|
$this->setDeploymentUuid();
|
||||||
|
queue_application_deployment(
|
||||||
|
application_id: $this->application->id,
|
||||||
|
deployment_uuid: $this->deploymentUuid,
|
||||||
|
restart_only: true,
|
||||||
|
);
|
||||||
|
return $this->redirectRoute('project.application.deployment', [
|
||||||
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
|
], navigate: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user