mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 20:59:24 +00:00
Compare commits
369 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09bcd693f5 | ||
|
|
0c15e45419 | ||
|
|
31cbf552a2 | ||
|
|
f7853ee174 | ||
|
|
de3a7b6eca | ||
|
|
b56c7c34cb | ||
|
|
49845f3da7 | ||
|
|
987409bae4 | ||
|
|
07d8461f96 | ||
|
|
f255a71434 | ||
|
|
2a2818ac0d | ||
|
|
fd3cdc2c7d | ||
|
|
84c3f832ae | ||
|
|
70c28fceeb | ||
|
|
f2c4f83f5a | ||
|
|
c8dd6f07ac | ||
|
|
561e424a7d | ||
|
|
c46d38907e | ||
|
|
5c334bbac6 | ||
|
|
9628072b0c | ||
|
|
39ecff9f90 | ||
|
|
d1daec060a | ||
|
|
fb5bea7f91 | ||
|
|
efc3ea6e40 | ||
|
|
ecefb9e1f5 | ||
|
|
a4dea2009a | ||
|
|
829e41f93f | ||
|
|
e7e3adc7fb | ||
|
|
a993fef235 | ||
|
|
81df71416c | ||
|
|
40af7aa025 | ||
|
|
050155328b | ||
|
|
376c081bed | ||
|
|
9f5e1fa9e3 | ||
|
|
7d139fd33b | ||
|
|
b75a2857a0 | ||
|
|
ab6c1ddc20 | ||
|
|
dc0b0980a9 | ||
|
|
788d1711db | ||
|
|
d92dc4c5e6 | ||
|
|
4bf34aea62 | ||
|
|
7e9a54ce67 | ||
|
|
f8c19e1fb3 | ||
|
|
27c1bda09b | ||
|
|
1af7ffcdc4 | ||
|
|
39647367a5 | ||
|
|
ae4b263810 | ||
|
|
8901bb5df8 | ||
|
|
053aa25d2c | ||
|
|
7a7157c155 | ||
|
|
0c5e8600bd | ||
|
|
048e153025 | ||
|
|
e7cafe6850 | ||
|
|
1385a86084 | ||
|
|
348923ae02 | ||
|
|
7d754558b0 | ||
|
|
744609e7e9 | ||
|
|
9bd05b65a3 | ||
|
|
d42934f258 | ||
|
|
ff752e2411 | ||
|
|
4120fba9a8 | ||
|
|
6ecb9c21ce | ||
|
|
01f7b07fa3 | ||
|
|
54d8cb9027 | ||
|
|
238337fecb | ||
|
|
7a51acbf8d | ||
|
|
fb478c79b3 | ||
|
|
abcc004953 | ||
|
|
2edf71a0dd | ||
|
|
cbec39099a | ||
|
|
dba5499182 | ||
|
|
2fdf52929c | ||
|
|
8128dfc061 | ||
|
|
2b394d6fea | ||
|
|
964ded1d0b | ||
|
|
838c3830d6 | ||
|
|
2db93bd9b9 | ||
|
|
2f82dedd4f | ||
|
|
8106602d15 | ||
|
|
3d0bf6b472 | ||
|
|
ba7a7e9695 | ||
|
|
dd0ad04384 | ||
|
|
910a1f43a9 | ||
|
|
e2f959ce4c | ||
|
|
68fe886fb0 | ||
|
|
4631c73809 | ||
|
|
77558b37da | ||
|
|
e060409a76 | ||
|
|
af01bc3e77 | ||
|
|
1e158badfc | ||
|
|
c620bb58ed | ||
|
|
8a91395472 | ||
|
|
c5f3398b73 | ||
|
|
3878527de8 | ||
|
|
4abcb2d5b9 | ||
|
|
a635e51486 | ||
|
|
b6ce2e9122 | ||
|
|
8c60dd5523 | ||
|
|
94e2d951c4 | ||
|
|
381e24bea5 | ||
|
|
2b1e35980f | ||
|
|
a42c8da344 | ||
|
|
7a0e415ecf | ||
|
|
d721f4809a | ||
|
|
22431eee9a | ||
|
|
c058c0a766 | ||
|
|
1724c0d3ff | ||
|
|
0b8f48230f | ||
|
|
e8d84b7067 | ||
|
|
5236bbc757 | ||
|
|
094e1d1bba | ||
|
|
68b25523d6 | ||
|
|
bdc478d5f5 | ||
|
|
002472d7c6 | ||
|
|
0d65bf62b9 | ||
|
|
01c7e76071 | ||
|
|
884ae0efb0 | ||
|
|
8e7040bf7c | ||
|
|
059e6a88eb | ||
|
|
9947158f7e | ||
|
|
61aa9e8766 | ||
|
|
75813a289c | ||
|
|
af11d8cf3d | ||
|
|
48990db699 | ||
|
|
da71353bfa | ||
|
|
0f5559bc61 | ||
|
|
1afb509c33 | ||
|
|
bccca6e874 | ||
|
|
083dc15053 | ||
|
|
1b6d376472 | ||
|
|
891deee05a | ||
|
|
5ffbba908b | ||
|
|
f762959c9f | ||
|
|
90a5a23fd9 | ||
|
|
94e87141ff | ||
|
|
fceaf3e94b | ||
|
|
3be554cb55 | ||
|
|
27b18fbedf | ||
|
|
5e7c6906b3 | ||
|
|
d05ffe32a3 | ||
|
|
f1298d1db4 | ||
|
|
408738e08d | ||
|
|
8d04fbdb74 | ||
|
|
dccb31d17e | ||
|
|
f61210287e | ||
|
|
18ad7220f0 | ||
|
|
79e0df1d43 | ||
|
|
a2f53085e5 | ||
|
|
c5782252ea | ||
|
|
bf3d88facd | ||
|
|
e45b0bf715 | ||
|
|
9db6c12eea | ||
|
|
3a391b69e8 | ||
|
|
cc1fb83c79 | ||
|
|
efa5dd28f1 | ||
|
|
f1eddae379 | ||
|
|
34febe670d | ||
|
|
3137131a1a | ||
|
|
1b6546d26c | ||
|
|
b9f820cef4 | ||
|
|
d8639f58d7 | ||
|
|
cf9be9355f | ||
|
|
e36bb11ba8 | ||
|
|
190beb3d3f | ||
|
|
890a6925d1 | ||
|
|
d03b8420f8 | ||
|
|
cbd3c880c3 | ||
|
|
6b24001876 | ||
|
|
6bb45430c9 | ||
|
|
bc6b4ed850 | ||
|
|
8a63ef5da9 | ||
|
|
e324866a27 | ||
|
|
0e5f733657 | ||
|
|
c5932ed337 | ||
|
|
ef428f844f | ||
|
|
eb8b752a6e | ||
|
|
ce0b38035c | ||
|
|
562a8f1fac | ||
|
|
68f1621757 | ||
|
|
e3087573bb | ||
|
|
7869f223a3 | ||
|
|
0e99f27108 | ||
|
|
f445a8c312 | ||
|
|
845fc191d4 | ||
|
|
95d0d72e0d | ||
|
|
76f695036c | ||
|
|
225bf06736 | ||
|
|
3a287ae974 | ||
|
|
e5c61b9f9f | ||
|
|
32bc876dfc | ||
|
|
c9b3d2a43d | ||
|
|
f343210e7c | ||
|
|
4a42bff0dc | ||
|
|
36931b5b18 | ||
|
|
3b080abada | ||
|
|
7feba4bbaa | ||
|
|
eef8c756df | ||
|
|
9ed30cb0dc | ||
|
|
6bc43bd999 | ||
|
|
e7683ee9a5 | ||
|
|
ee71aeaa36 | ||
|
|
404c664500 | ||
|
|
aa80392b46 | ||
|
|
c9509ef658 | ||
|
|
31e08a24c9 | ||
|
|
14b32e30cd | ||
|
|
5aaad66fe5 | ||
|
|
b6745c691b | ||
|
|
5ee29c6072 | ||
|
|
b69584fe26 | ||
|
|
4c3907c296 | ||
|
|
bf44b4b949 | ||
|
|
bee7a2357b | ||
|
|
98704fc3c2 | ||
|
|
e286e78ddc | ||
|
|
557e1407d0 | ||
|
|
3c99f24b5a | ||
|
|
512197021b | ||
|
|
e233ec05b5 | ||
|
|
d0e3a20a65 | ||
|
|
e2e6813632 | ||
|
|
963c519c38 | ||
|
|
d04513d817 | ||
|
|
64a7f27e37 | ||
|
|
65652142b2 | ||
|
|
7691319c59 | ||
|
|
206bd50d00 | ||
|
|
6159783a73 | ||
|
|
ed5f831c86 | ||
|
|
65be83e75d | ||
|
|
25a471b045 | ||
|
|
60c7a29aa8 | ||
|
|
11ab6669a0 | ||
|
|
53965ab8ed | ||
|
|
ea271ca079 | ||
|
|
f16d0f650f | ||
|
|
cb80341a78 | ||
|
|
83d96c8d11 | ||
|
|
a8ca57d095 | ||
|
|
2d936a4b22 | ||
|
|
0653eb8511 | ||
|
|
cc64132627 | ||
|
|
e0391e5abd | ||
|
|
025135bd2a | ||
|
|
5e5873a08d | ||
|
|
14652c2878 | ||
|
|
9bbe9567c7 | ||
|
|
7913a639b5 | ||
|
|
adecf328fc | ||
|
|
a7ef5c456d | ||
|
|
117f1d4155 | ||
|
|
075f3ce930 | ||
|
|
d5b3e88fc4 | ||
|
|
ba55e0c1bb | ||
|
|
54671354f0 | ||
|
|
a2c7e8d455 | ||
|
|
32dbdf5204 | ||
|
|
019887739c | ||
|
|
5596e41f2b | ||
|
|
6a73a00a1f | ||
|
|
4121c9dd8b | ||
|
|
e4781dc129 | ||
|
|
1c90f46f2a | ||
|
|
e78d851c85 | ||
|
|
52d05005ed | ||
|
|
f03aa57758 | ||
|
|
8c20c833ba | ||
|
|
2fe6766b7f | ||
|
|
d9599da4a8 | ||
|
|
3f453ba7c0 | ||
|
|
bd02c3055a | ||
|
|
7ea7d85d15 | ||
|
|
d38350c282 | ||
|
|
a83c70004c | ||
|
|
49e1404a2c | ||
|
|
76f23e7dbf | ||
|
|
ad8653f54d | ||
|
|
8939d77051 | ||
|
|
37be4a1796 | ||
|
|
e4c923e358 | ||
|
|
62ca3ffaa5 | ||
|
|
9af3ce4be5 | ||
|
|
fe143ef8a5 | ||
|
|
5fb5845e90 | ||
|
|
794cfbd8eb | ||
|
|
29ee9915f3 | ||
|
|
331d485213 | ||
|
|
665e3761c4 | ||
|
|
ac19f0e34f | ||
|
|
d7cfa0578f | ||
|
|
694169bb84 | ||
|
|
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 |
12
.env.windows-docker-desktop.example
Normal file
12
.env.windows-docker-desktop.example
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
IS_WINDOWS_DOCKER_DESKTOP=true
|
||||||
|
|
||||||
|
APP_ID=coolify-windows-docker-desktop
|
||||||
|
APP_NAME=Coolify
|
||||||
|
APP_KEY=base64:ssTlCmrIE/q7whnKMvT6DwURikg69COzGsAwFVROm80=
|
||||||
|
|
||||||
|
DB_PASSWORD=coolify
|
||||||
|
REDIS_PASSWORD=coolify
|
||||||
|
|
||||||
|
PUSHER_APP_ID=coolify
|
||||||
|
PUSHER_APP_KEY=coolify
|
||||||
|
PUSHER_APP_SECRET=coolify
|
||||||
84
.github/workflows/coolify-testing-host.yml
vendored
Normal file
84
.github/workflows/coolify-testing-host.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
name: Coolify Testing Host (v4-non-prod)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main", "next" ]
|
||||||
|
paths:
|
||||||
|
- .github/workflows/coolify-testing-host.yml
|
||||||
|
- docker/testing-host/Dockerfile
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME: "coollabsio/coolify-testing-host"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
amd64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Login to ghcr.io
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build image and push to registry
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
no-cache: true
|
||||||
|
context: .
|
||||||
|
file: docker/testing-host/Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
aarch64:
|
||||||
|
runs-on: [ self-hosted, arm64 ]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Login to ghcr.io
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build image and push to registry
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
no-cache: true
|
||||||
|
context: .
|
||||||
|
file: docker/testing-host/Dockerfile
|
||||||
|
platforms: linux/aarch64
|
||||||
|
push: true
|
||||||
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64
|
||||||
|
merge-manifest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
needs: [ amd64, aarch64 ]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Login to ghcr.io
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Create & publish manifest
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
- uses: sarisia/actions-status-discord@v1
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
||||||
28
.github/workflows/development-build.yml
vendored
28
.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
|
||||||
@@ -15,42 +15,42 @@ jobs:
|
|||||||
amd64:
|
amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
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@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
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:
|
||||||
@@ -59,20 +59,20 @@ jobs:
|
|||||||
needs: [amd64, aarch64]
|
needs: [amd64, aarch64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
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:
|
||||||
|
|||||||
22
.github/workflows/production-build.yml
vendored
22
.github/workflows/production-build.yml
vendored
@@ -12,9 +12,9 @@ jobs:
|
|||||||
amd64:
|
amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
@@ -34,9 +34,9 @@ jobs:
|
|||||||
aarch64:
|
aarch64:
|
||||||
runs-on: [self-hosted, arm64]
|
runs-on: [self-hosted, arm64]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
@@ -61,13 +61,13 @@ jobs:
|
|||||||
needs: [amd64, aarch64]
|
needs: [amd64, aarch64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -78,7 +78,7 @@ jobs:
|
|||||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
||||||
- name: Create & publish manifest
|
- name: Create & publish manifest
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
|
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|
||||||
|
|||||||
62
README.md
62
README.md
@@ -17,6 +17,40 @@ https://coolify.io/sponsorships
|
|||||||
|
|
||||||
Thank you so much!
|
Thank you so much!
|
||||||
|
|
||||||
|
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
||||||
|
|
||||||
|
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
||||||
|
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
||||||
|
|
||||||
|
## Github Sponsors ($15+)
|
||||||
|
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
|
||||||
|
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
|
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
|
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
|
||||||
|
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
|
||||||
|
<a href="https://github.com/whitesidest"><img src="https://avatars.githubusercontent.com/u/12365916?s=52&v=4" width="60px" alt="Tyler Whitesides" /></a>
|
||||||
|
<a href="https://github.com/aniftyco"><img src="https://github.com/aniftyco.png" width="60px" alt="NiftyCo" /></a>
|
||||||
|
<a href="https://github.com/iujlaki"><img src="https://github.com/iujlaki.png" width="60px" alt="Imre Ujlaki" /></a>
|
||||||
|
<a href="https://il.ly"><img src="https://github.com/Illyism.png" width="60px" alt="Ilias Ism" /></a>
|
||||||
|
<a href="https://github.com/urtho"><img src="https://github.com/urtho.png" width="60px" alt="Paweł Pierścionek" /></a>
|
||||||
|
<a href="https://github.com/monocursive"><img src="https://github.com/monocursive.png" width="60px" alt="Michael Mazurczak" /></a>
|
||||||
|
|
||||||
|
## Organizations
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/1/website"><img src="https://opencollective.com/coollabsio/organization/1/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/2/website"><img src="https://opencollective.com/coollabsio/organization/2/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/3/website"><img src="https://opencollective.com/coollabsio/organization/3/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/4/website"><img src="https://opencollective.com/coollabsio/organization/4/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/5/website"><img src="https://opencollective.com/coollabsio/organization/5/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/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>
|
||||||
|
|
||||||
|
## Individuals
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
||||||
|
|
||||||
# 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
|
||||||
@@ -59,33 +93,9 @@ 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
|
# Repo Activity
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|

|
||||||
|
|
||||||
## Organizations
|
|
||||||
|
|
||||||
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
|
||||||
|
|
||||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="appwrite logo" width="200"/></a>
|
|
||||||
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
|
||||||
|
|
||||||
Support this project with your organization. Your logo will show up here with a link to your website.
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/1/website"><img src="https://opencollective.com/coollabsio/organization/1/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/2/website"><img src="https://opencollective.com/coollabsio/organization/2/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/3/website"><img src="https://opencollective.com/coollabsio/organization/3/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/4/website"><img src="https://opencollective.com/coollabsio/organization/4/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/5/website"><img src="https://opencollective.com/coollabsio/organization/5/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/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>
|
|
||||||
|
|
||||||
## Individuals
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
|
||||||
|
|
||||||
# Star History
|
# Star History
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +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' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -69,6 +68,9 @@ class StartMariadb
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
@@ -138,7 +140,7 @@ class StartMariadb
|
|||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
||||||
|
|||||||
@@ -63,8 +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' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -76,6 +75,9 @@ class StartMongodb
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
@@ -121,7 +123,7 @@ class StartMongodb
|
|||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$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,callEventOnFinish: 'DatabaseStatusChanged');
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -154,7 +156,7 @@ class StartMongodb
|
|||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
||||||
|
|||||||
@@ -56,8 +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' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -69,6 +68,9 @@ class StartMysql
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
@@ -138,7 +140,7 @@ class StartMysql
|
|||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
||||||
|
|||||||
@@ -50,12 +50,8 @@ class StartPostgresql
|
|||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => [
|
'test' => [
|
||||||
'CMD-SHELL',
|
"CMD-SHELL",
|
||||||
'pg_isready',
|
"psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1"
|
||||||
'-d',
|
|
||||||
$this->database->postgres_db,
|
|
||||||
'-U',
|
|
||||||
$this->database->postgres_user,
|
|
||||||
],
|
],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
@@ -66,8 +62,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' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -79,6 +74,9 @@ class StartPostgresql
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
ray('Log Drain Enabled');
|
ray('Log Drain Enabled');
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
@@ -166,7 +164,7 @@ class StartPostgresql
|
|||||||
ray('Generate Environment Variables')->green();
|
ray('Generate Environment Variables')->green();
|
||||||
ray($this->database->runtime_environment_variables)->green();
|
ray($this->database->runtime_environment_variables)->green();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_USER'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_USER'))->isEmpty()) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
@@ -65,8 +66,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' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -78,6 +78,9 @@ class StartRedis
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
@@ -104,7 +107,7 @@ class StartRedis
|
|||||||
'target' => '/usr/local/etc/redis/redis.conf',
|
'target' => '/usr/local/etc/redis/redis.conf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
$docker_compose['services'][$container_name]['command'] = $startCommand . ' /usr/local/etc/redis/redis.conf';
|
$docker_compose['services'][$container_name]['command'] = "redis-server /usr/local/etc/redis/redis.conf --requirepass {$this->database->redis_password} --appendonly yes";
|
||||||
}
|
}
|
||||||
$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);
|
||||||
@@ -148,7 +151,7 @@ class StartRedis
|
|||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||||
@@ -163,8 +166,9 @@ class StartRedis
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'redis.conf';
|
$filename = 'redis.conf';
|
||||||
$content = $this->database->redis_conf;
|
Storage::disk('local')->put("tmp/redis.conf_{$this->database->uuid}", $this->database->redis_conf);
|
||||||
$content_base64 = base64_encode($content);
|
$path = Storage::path("tmp/redis.conf_{$this->database->uuid}");
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
instant_scp($path, "{$this->configuration_dir}/{$filename}", $this->database->destination->server);
|
||||||
|
Storage::disk('local')->delete("tmp/redis.conf_{$this->database->uuid}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ class CheckProxy
|
|||||||
$status = getContainerStatus($server, 'coolify-proxy_traefik');
|
$status = getContainerStatus($server, 'coolify-proxy_traefik');
|
||||||
$server->proxy->set('status', $status);
|
$server->proxy->set('status', $status);
|
||||||
$server->save();
|
$server->save();
|
||||||
|
if ($status === 'running') {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
$status = getContainerStatus($server, 'coolify-proxy');
|
$status = getContainerStatus($server, 'coolify-proxy');
|
||||||
if ($status === 'running') {
|
if ($status === 'running') {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
|
use App\Events\ProxyStatusChanged;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
@@ -13,7 +14,6 @@ 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();
|
||||||
|
|||||||
23
app/Actions/Server/CleanupDocker.php
Normal file
23
app/Actions/Server/CleanupDocker.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Server;
|
||||||
|
|
||||||
|
class CleanupDocker
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Server $server, bool $force = true)
|
||||||
|
{
|
||||||
|
if ($force) {
|
||||||
|
instant_remote_process(['docker image prune -af'], $server, false);
|
||||||
|
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false);
|
||||||
|
instant_remote_process(['docker builder prune -af'], $server, false);
|
||||||
|
} else {
|
||||||
|
instant_remote_process(['docker image prune -f'], $server, false);
|
||||||
|
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false);
|
||||||
|
instant_remote_process(['docker builder prune -f'], $server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,10 +18,11 @@ 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;
|
||||||
}
|
}
|
||||||
|
CleanupDocker::run($this->server, false);
|
||||||
$this->latestVersion = get_latest_version_of_coolify();
|
$this->latestVersion = get_latest_version_of_coolify();
|
||||||
$this->currentVersion = config('version');
|
$this->currentVersion = config('version');
|
||||||
ray('latest version:' . $this->latestVersion . " current version: " . $this->currentVersion . ' force: ' . $force);
|
ray('latest version:' . $this->latestVersion . " current version: " . $this->currentVersion . ' force: ' . $force);
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ class DeleteService
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
StopService::run($service);
|
|
||||||
$server = data_get($service, 'server');
|
$server = data_get($service, 'server');
|
||||||
|
if ($server->isFunctional()) {
|
||||||
|
StopService::run($service);
|
||||||
|
}
|
||||||
$storagesToDelete = collect([]);
|
$storagesToDelete = collect([]);
|
||||||
|
|
||||||
$service->environment_variables()->delete();
|
$service->environment_variables()->delete();
|
||||||
@@ -21,14 +23,14 @@ class DeleteService
|
|||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
}
|
}
|
||||||
$application->delete();
|
$application->forceDelete();
|
||||||
}
|
}
|
||||||
foreach ($service->databases()->get() as $database) {
|
foreach ($service->databases()->get() as $database) {
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
}
|
}
|
||||||
$database->delete();
|
$database->forceDelete();
|
||||||
}
|
}
|
||||||
foreach ($storagesToDelete as $storage) {
|
foreach ($storagesToDelete as $storage) {
|
||||||
$commands[] = "docker volume rm -f $storage->name";
|
$commands[] = "docker volume rm -f $storage->name";
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class StartService
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
$network = $service->destination->network;
|
ray('Starting service: ' . $service->name);
|
||||||
$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()}.'";
|
||||||
@@ -23,11 +23,14 @@ class StartService
|
|||||||
$commands[] = "echo 'Starting containers.'";
|
$commands[] = "echo 'Starting containers.'";
|
||||||
$commands[] = "docker compose up -d --remove-orphans --force-recreate --build";
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate --build";
|
||||||
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
|
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
|
||||||
|
if (data_get($service, 'connect_to_docker_network')) {
|
||||||
$compose = data_get($service, 'docker_compose', []);
|
$compose = data_get($service, 'docker_compose', []);
|
||||||
|
$network = $service->destination->network;
|
||||||
$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, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
|
$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);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
app/Console/Commands/CleanupQueue.php
Normal file
23
app/Console/Commands/CleanupQueue.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
|
|
||||||
|
class CleanupQueue extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:queue';
|
||||||
|
protected $description = 'Cleanup Queue';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
echo "Running queue cleanup...\n";
|
||||||
|
$prefix = config('database.redis.options.prefix');
|
||||||
|
$keys = Redis::connection()->keys('*:laravel*');
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$keyWithoutPrefix = str_replace($prefix, '', $key);
|
||||||
|
Redis::connection()->del($keyWithoutPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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', '.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,8 +9,6 @@ use App\Models\ScheduledDatabaseBackup;
|
|||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use App\Models\TeamInvitation;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Models\Waitlist;
|
use App\Models\Waitlist;
|
||||||
use App\Notifications\Application\DeploymentFailed;
|
use App\Notifications\Application\DeploymentFailed;
|
||||||
use App\Notifications\Application\DeploymentSuccess;
|
use App\Notifications\Application\DeploymentSuccess;
|
||||||
@@ -18,13 +16,11 @@ use App\Notifications\Application\StatusChanged;
|
|||||||
use App\Notifications\Database\BackupFailed;
|
use App\Notifications\Database\BackupFailed;
|
||||||
use App\Notifications\Database\BackupSuccess;
|
use App\Notifications\Database\BackupSuccess;
|
||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
use App\Notifications\TransactionalEmails\InvitationLink;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Mail\Message;
|
use Illuminate\Mail\Message;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Mail;
|
use Mail;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
use function Laravel\Prompts\confirm;
|
use function Laravel\Prompts\confirm;
|
||||||
use function Laravel\Prompts\select;
|
use function Laravel\Prompts\select;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ 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\InstanceSettings;
|
||||||
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
@@ -29,8 +30,11 @@ class Init extends Command
|
|||||||
$this->alive();
|
$this->alive();
|
||||||
$cleanup = $this->option('cleanup');
|
$cleanup = $this->option('cleanup');
|
||||||
if ($cleanup) {
|
if ($cleanup) {
|
||||||
echo "Running cleanup\n";
|
echo "Running cleanups...\n";
|
||||||
$this->cleanup_stucked_resources();
|
$this->cleanup_stucked_resources();
|
||||||
|
// Required for falsely deleted coolify db
|
||||||
|
$this->restore_coolify_db_backup();
|
||||||
|
|
||||||
// $this->cleanup_ssh();
|
// $this->cleanup_ssh();
|
||||||
}
|
}
|
||||||
$this->cleanup_in_progress_application_deployments();
|
$this->cleanup_in_progress_application_deployments();
|
||||||
@@ -50,6 +54,31 @@ class Init extends Command
|
|||||||
$settings->update(['is_auto_update_enabled' => false]);
|
$settings->update(['is_auto_update_enabled' => false]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->call('cleanup:queue');
|
||||||
|
}
|
||||||
|
private function restore_coolify_db_backup()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$database = StandalonePostgresql::withTrashed()->find(0);
|
||||||
|
if ($database && $database->trashed()) {
|
||||||
|
echo "Restoring coolify db backup\n";
|
||||||
|
$database->restore();
|
||||||
|
$scheduledBackup = ScheduledDatabaseBackup::find(0);
|
||||||
|
if (!$scheduledBackup) {
|
||||||
|
ScheduledDatabaseBackup::create([
|
||||||
|
'id' => 0,
|
||||||
|
'enabled' => true,
|
||||||
|
'save_s3' => false,
|
||||||
|
'frequency' => '0 0 * * *',
|
||||||
|
'database_id' => $database->id,
|
||||||
|
'database_type' => 'App\Models\StandalonePostgresql',
|
||||||
|
'team_id' => 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private function cleanup_stucked_helper_containers()
|
private function cleanup_stucked_helper_containers()
|
||||||
{
|
{
|
||||||
@@ -71,7 +100,7 @@ class Init extends Command
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Http::get("https://get.coollabs.io/coolify/v4/alive?appId=$id&version=$version");
|
Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version");
|
||||||
echo "I am alive!\n";
|
echo "I am alive!\n";
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in alive: {$e->getMessage()}\n";
|
echo "Error in alive: {$e->getMessage()}\n";
|
||||||
@@ -99,7 +128,7 @@ class Init extends Command
|
|||||||
// 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();
|
||||||
@@ -110,40 +139,129 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
private function cleanup_stucked_resources()
|
private function cleanup_stucked_resources()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
echo "Deleting stuck application: {$application->name}\n";
|
||||||
|
$application->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($postgresqls as $postgresql) {
|
||||||
|
echo "Deleting stuck postgresql: {$postgresql->name}\n";
|
||||||
|
$postgresql->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck postgresql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$redis = StandaloneRedis::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($redis as $redis) {
|
||||||
|
echo "Deleting stuck redis: {$redis->name}\n";
|
||||||
|
$redis->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($mongodbs as $mongodb) {
|
||||||
|
echo "Deleting stuck mongodb: {$mongodb->name}\n";
|
||||||
|
$mongodb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck mongodb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$mysqls = StandaloneMysql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($mysqls as $mysql) {
|
||||||
|
echo "Deleting stuck mysql: {$mysql->name}\n";
|
||||||
|
$mysql->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck mysql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$mariadbs = StandaloneMariadb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($mariadbs as $mariadb) {
|
||||||
|
echo "Deleting stuck mariadb: {$mariadb->name}\n";
|
||||||
|
$mariadb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck mariadb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$services = Service::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($services as $service) {
|
||||||
|
echo "Deleting stuck service: {$service->name}\n";
|
||||||
|
$service->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck service: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceApps = ServiceApplication::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($serviceApps as $serviceApp) {
|
||||||
|
echo "Deleting stuck serviceapp: {$serviceApp->name}\n";
|
||||||
|
$serviceApp->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck serviceapp: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceDbs = ServiceDatabase::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($serviceDbs as $serviceDb) {
|
||||||
|
echo "Deleting stuck serviceapp: {$serviceDb->name}\n";
|
||||||
|
$serviceDb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck serviceapp: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup any resources that are not attached to any environment or destination or server
|
// Cleanup any resources that are not attached to any environment or destination or server
|
||||||
try {
|
try {
|
||||||
$applications = Application::all();
|
$applications = Application::all();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
if (!data_get($application, 'environment')) {
|
if (!data_get($application, 'environment')) {
|
||||||
echo 'Application without environment' . $application->name . 'deleting\n';
|
echo 'Application without environment: ' . $application->name . ' soft deleting\n';
|
||||||
$application->delete();
|
$application->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$application->destination()) {
|
if (!$application->destination()) {
|
||||||
echo 'Application without destination' . $application->name . 'deleting\n';
|
echo 'Application without destination: ' . $application->name . ' soft deleting\n';
|
||||||
$application->delete();
|
$application->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($application, 'destination.server')) {
|
if (!data_get($application, 'destination.server')) {
|
||||||
echo 'Application without server' . $application->name . 'deleting\n';
|
echo 'Application without server: ' . $application->name . ' soft deleting\n';
|
||||||
$application->delete();
|
$application->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in application: {$e->getMessage()}\n";
|
echo "Error in application: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
$postgresqls = StandalonePostgresql::all();
|
$postgresqls = StandalonePostgresql::all()->where('id', '!=', 0);
|
||||||
foreach ($postgresqls as $postgresql) {
|
foreach ($postgresqls as $postgresql) {
|
||||||
if (!data_get($postgresql, 'environment')) {
|
if (!data_get($postgresql, 'environment')) {
|
||||||
echo 'Postgresql without environment' . $postgresql->name . 'deleting\n';
|
echo 'Postgresql without environment: ' . $postgresql->name . ' soft deleting\n';
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$postgresql->destination()) {
|
if (!$postgresql->destination()) {
|
||||||
echo 'Postgresql without destination' . $postgresql->name . 'deleting\n';
|
echo 'Postgresql without destination: ' . $postgresql->name . ' soft deleting\n';
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($postgresql, 'destination.server')) {
|
if (!data_get($postgresql, 'destination.server')) {
|
||||||
echo 'Postgresql without server' . $postgresql->name . 'deleting\n';
|
echo 'Postgresql without server: ' . $postgresql->name . ' soft deleting\n';
|
||||||
$postgresql->delete();
|
$postgresql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -153,16 +271,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')) {
|
||||||
echo 'Redis without environment' . $redis->name . 'deleting\n';
|
echo 'Redis without environment: ' . $redis->name . ' soft deleting\n';
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$redis->destination()) {
|
if (!$redis->destination()) {
|
||||||
echo 'Redis without destination' . $redis->name . 'deleting\n';
|
echo 'Redis without destination: ' . $redis->name . ' soft deleting\n';
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($redis, 'destination.server')) {
|
if (!data_get($redis, 'destination.server')) {
|
||||||
echo 'Redis without server' . $redis->name . 'deleting\n';
|
echo 'Redis without server: ' . $redis->name . ' soft deleting\n';
|
||||||
$redis->delete();
|
$redis->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -173,16 +294,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')) {
|
||||||
echo 'Mongodb without environment' . $mongodb->name . 'deleting\n';
|
echo 'Mongodb without environment: ' . $mongodb->name . ' soft deleting\n';
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mongodb->destination()) {
|
if (!$mongodb->destination()) {
|
||||||
echo 'Mongodb without destination' . $mongodb->name . 'deleting\n';
|
echo 'Mongodb without destination: ' . $mongodb->name . ' soft deleting\n';
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($mongodb, 'destination.server')) {
|
if (!data_get($mongodb, 'destination.server')) {
|
||||||
echo 'Mongodb without server' . $mongodb->name . 'deleting\n';
|
echo 'Mongodb without server: ' . $mongodb->name . ' soft deleting\n';
|
||||||
$mongodb->delete();
|
$mongodb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -193,16 +317,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')) {
|
||||||
echo 'Mysql without environment' . $mysql->name . 'deleting\n';
|
echo 'Mysql without environment: ' . $mysql->name . ' soft deleting\n';
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mysql->destination()) {
|
if (!$mysql->destination()) {
|
||||||
echo 'Mysql without destination' . $mysql->name . 'deleting\n';
|
echo 'Mysql without destination: ' . $mysql->name . ' soft deleting\n';
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($mysql, 'destination.server')) {
|
if (!data_get($mysql, 'destination.server')) {
|
||||||
echo 'Mysql without server' . $mysql->name . 'deleting\n';
|
echo 'Mysql without server: ' . $mysql->name . ' soft deleting\n';
|
||||||
$mysql->delete();
|
$mysql->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -213,16 +340,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')) {
|
||||||
echo 'Mariadb without environment' . $mariadb->name . 'deleting\n';
|
echo 'Mariadb without environment: ' . $mariadb->name . ' soft deleting\n';
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mariadb->destination()) {
|
if (!$mariadb->destination()) {
|
||||||
echo 'Mariadb without destination' . $mariadb->name . 'deleting\n';
|
echo 'Mariadb without destination: ' . $mariadb->name . ' soft deleting\n';
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($mariadb, 'destination.server')) {
|
if (!data_get($mariadb, 'destination.server')) {
|
||||||
echo 'Mariadb without server' . $mariadb->name . 'deleting\n';
|
echo 'Mariadb without server: ' . $mariadb->name . ' soft deleting\n';
|
||||||
$mariadb->delete();
|
$mariadb->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -233,16 +363,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')) {
|
||||||
echo 'Service without environment' . $service->name . 'deleting\n';
|
echo 'Service without environment: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!$service->destination()) {
|
if (!$service->destination()) {
|
||||||
echo 'Service without destination' . $service->name . 'deleting\n';
|
echo 'Service without destination: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($service, 'server')) {
|
if (!data_get($service, 'server')) {
|
||||||
echo 'Service without server' . $service->name . 'deleting\n';
|
echo 'Service without server: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -252,8 +385,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')) {
|
||||||
echo 'ServiceApplication without service' . $service->name . 'deleting\n';
|
echo 'ServiceApplication without service: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -263,8 +397,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')) {
|
||||||
echo 'ServiceDatabase without service' . $service->name . 'deleting\n';
|
echo 'ServiceDatabase without service: ' . $service->name . ' soft deleting\n';
|
||||||
$service->delete();
|
$service->delete();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\DeleteResourceJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
@@ -91,7 +92,7 @@ class ServicesDelete extends Command
|
|||||||
if (!$confirmed) {
|
if (!$confirmed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +116,7 @@ class ServicesDelete extends Command
|
|||||||
if (!$confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +140,7 @@ class ServicesDelete extends Command
|
|||||||
if (!$confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ namespace App\Console;
|
|||||||
use App\Jobs\CheckLogDrainContainerJob;
|
use App\Jobs\CheckLogDrainContainerJob;
|
||||||
use App\Jobs\CleanupInstanceStuffsJob;
|
use App\Jobs\CleanupInstanceStuffsJob;
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
|
use App\Jobs\ScheduledTaskJob;
|
||||||
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\Jobs\ServerStatusJob;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
|
use App\Models\ScheduledTask;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
@@ -30,6 +32,7 @@ class Kernel extends ConsoleKernel
|
|||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->pull_helper_image($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
|
$this->check_scheduled_tasks($schedule);
|
||||||
} else {
|
} else {
|
||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
@@ -41,6 +44,7 @@ class Kernel extends ConsoleKernel
|
|||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->pull_helper_image($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
|
$this->check_scheduled_tasks($schedule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function pull_helper_image($schedule)
|
private function pull_helper_image($schedule)
|
||||||
@@ -56,16 +60,20 @@ class Kernel extends ConsoleKernel
|
|||||||
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
$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;
|
$own = Team::find(0)->servers;
|
||||||
$servers = $servers->merge($own);
|
$servers = $servers->merge($own);
|
||||||
|
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||||
} else {
|
} else {
|
||||||
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
||||||
|
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||||
}
|
}
|
||||||
foreach ($servers as $server) {
|
foreach ($containerServers as $server) {
|
||||||
$schedule->job(new ServerStatusJob($server))->everyTenMinutes()->onOneServer();
|
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
if ($server->isLogDrainEnabled()) {
|
if ($server->isLogDrainEnabled()) {
|
||||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private function instance_auto_update($schedule)
|
private function instance_auto_update($schedule)
|
||||||
{
|
{
|
||||||
@@ -103,6 +111,32 @@ class Kernel extends ConsoleKernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function check_scheduled_tasks($schedule)
|
||||||
|
{
|
||||||
|
$scheduled_tasks = ScheduledTask::all();
|
||||||
|
if ($scheduled_tasks->isEmpty()) {
|
||||||
|
ray('no scheduled tasks');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($scheduled_tasks as $scheduled_task) {
|
||||||
|
$service = $scheduled_task->service()->get();
|
||||||
|
$application = $scheduled_task->application()->get();
|
||||||
|
|
||||||
|
if (!$application && !$service) {
|
||||||
|
ray('application/service attached to scheduled task does not exist');
|
||||||
|
$scheduled_task->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
|
||||||
|
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
|
||||||
|
}
|
||||||
|
$schedule->job(new ScheduledTaskJob(
|
||||||
|
task: $scheduled_task
|
||||||
|
))->cron($scheduled_task->frequency)->onOneServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function commands(): void
|
protected function commands(): void
|
||||||
{
|
{
|
||||||
$this->load(__DIR__ . '/Commands');
|
$this->load(__DIR__ . '/Commands');
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ enum ProcessStatus: string
|
|||||||
case ERROR = 'error';
|
case ERROR = 'error';
|
||||||
case KILLED = 'killed';
|
case KILLED = 'killed';
|
||||||
case CANCELLED = 'cancelled';
|
case CANCELLED = 'cancelled';
|
||||||
|
case CLOSED = 'closed';
|
||||||
}
|
}
|
||||||
|
|||||||
34
app/Events/ProxyStatusChanged.php
Normal file
34
app/Events/ProxyStatusChanged.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 ProxyStatusChanged 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}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,23 +2,68 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Events\TestEvent;
|
||||||
use App\Models\S3Storage;
|
|
||||||
use App\Models\StandalonePostgresql;
|
|
||||||
use App\Models\TeamInvitation;
|
use App\Models\TeamInvitation;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Laravel\Fortify\Fortify;
|
||||||
|
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse;
|
||||||
|
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse;
|
||||||
|
use Illuminate\Support\Facades\Password;
|
||||||
|
|
||||||
class Controller extends BaseController
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
use AuthorizesRequests, ValidatesRequests;
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
|
|
||||||
|
public function realtime_test() {
|
||||||
|
if (auth()->user()?->currentTeam()->id !== 0) {
|
||||||
|
return redirect(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
TestEvent::dispatch();
|
||||||
|
return 'Look at your other tab.';
|
||||||
|
}
|
||||||
|
public function verify() {
|
||||||
|
return view('auth.verify-email');
|
||||||
|
}
|
||||||
|
public function email_verify(EmailVerificationRequest $request) {
|
||||||
|
$request->fulfill();
|
||||||
|
$name = request()->user()?->name;
|
||||||
|
send_internal_notification("User {$name} verified their email address.");
|
||||||
|
return redirect(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
public function forgot_password(Request $request) {
|
||||||
|
if (is_transactional_emails_active()) {
|
||||||
|
$arrayOfRequest = $request->only(Fortify::email());
|
||||||
|
$request->merge([
|
||||||
|
'email' => Str::lower($arrayOfRequest['email']),
|
||||||
|
]);
|
||||||
|
$type = set_transanctional_email_settings();
|
||||||
|
if (!$type) {
|
||||||
|
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
||||||
|
}
|
||||||
|
$request->validate([Fortify::email() => 'required|email']);
|
||||||
|
$status = Password::broker(config('fortify.passwords'))->sendResetLink(
|
||||||
|
$request->only(Fortify::email())
|
||||||
|
);
|
||||||
|
if ($status == Password::RESET_LINK_SENT) {
|
||||||
|
return app(SuccessfulPasswordResetLinkRequestResponse::class, ['status' => $status]);
|
||||||
|
}
|
||||||
|
if ($status == Password::RESET_THROTTLED) {
|
||||||
|
return response('Already requested a password reset in the past minutes.', 400);
|
||||||
|
}
|
||||||
|
return app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]);
|
||||||
|
}
|
||||||
|
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
||||||
|
}
|
||||||
public function link()
|
public function link()
|
||||||
{
|
{
|
||||||
$token = request()->get('token');
|
$token = request()->get('token');
|
||||||
@@ -39,7 +84,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();
|
||||||
}
|
}
|
||||||
@@ -51,103 +96,32 @@ class Controller extends BaseController
|
|||||||
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function license()
|
public function accept_invitation()
|
||||||
{
|
|
||||||
if (!isCloud()) {
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
return view('settings.license', [
|
|
||||||
'settings' => InstanceSettings::get(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function force_passoword_reset()
|
|
||||||
{
|
|
||||||
return view('auth.force-password-reset');
|
|
||||||
}
|
|
||||||
public function boarding()
|
|
||||||
{
|
|
||||||
if (currentTeam()->boarding || isDev()) {
|
|
||||||
return view('boarding');
|
|
||||||
} else {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function settings()
|
|
||||||
{
|
|
||||||
if (isInstanceAdmin()) {
|
|
||||||
$settings = InstanceSettings::get();
|
|
||||||
$database = StandalonePostgresql::whereName('coolify-db')->first();
|
|
||||||
if ($database) {
|
|
||||||
if ($database->status !== 'running') {
|
|
||||||
$database->status = 'running';
|
|
||||||
$database->save();
|
|
||||||
}
|
|
||||||
$s3s = S3Storage::whereTeamId(0)->get();
|
|
||||||
}
|
|
||||||
return view('settings.configuration', [
|
|
||||||
'settings' => $settings,
|
|
||||||
'database' => $database,
|
|
||||||
's3s' => $s3s ?? [],
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function team()
|
|
||||||
{
|
|
||||||
$invitations = [];
|
|
||||||
if (auth()->user()->isAdminFromSession()) {
|
|
||||||
$invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
|
|
||||||
}
|
|
||||||
return view('team.index', [
|
|
||||||
'invitations' => $invitations,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function storages()
|
|
||||||
{
|
|
||||||
$s3 = S3Storage::ownedByCurrentTeam()->get();
|
|
||||||
return view('team.storages.all', [
|
|
||||||
's3' => $s3,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function storages_show()
|
|
||||||
{
|
|
||||||
$storage = S3Storage::ownedByCurrentTeam()->whereUuid(request()->storage_uuid)->firstOrFail();
|
|
||||||
return view('team.storages.show', [
|
|
||||||
'storage' => $storage,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function members()
|
|
||||||
{
|
|
||||||
$invitations = [];
|
|
||||||
if (auth()->user()->isAdminFromSession()) {
|
|
||||||
$invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
|
|
||||||
}
|
|
||||||
return view('team.members', [
|
|
||||||
'invitations' => $invitations,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
||||||
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
if ($resetPassword) {
|
||||||
refreshSession($invitation->team);
|
$user->update([
|
||||||
|
'password' => Hash::make($invitationUuid),
|
||||||
|
'force_password_reset' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($user->teams()->where('team_id', $invitation->team->id)->exists()) {
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
|
}
|
||||||
|
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
||||||
|
$invitation->delete();
|
||||||
|
if (auth()->user()?->id !== $user->id) {
|
||||||
|
return redirect()->route('login');
|
||||||
|
}
|
||||||
|
refreshSession($invitation->team);
|
||||||
|
return redirect()->route('team.index');
|
||||||
} else {
|
} else {
|
||||||
abort(401);
|
abort(401);
|
||||||
}
|
}
|
||||||
@@ -157,7 +131,7 @@ class Controller extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function revokeInvitation()
|
public function revoke_invitation()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
|
||||||
|
|
||||||
class DatabaseController extends Controller
|
|
||||||
{
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
|
||||||
if (!$database) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
return view('project.database.configuration', ['database' => $database]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function executions()
|
|
||||||
{
|
|
||||||
$backup_uuid = request()->route('backup_uuid');
|
|
||||||
$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');
|
|
||||||
}
|
|
||||||
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
|
||||||
if (!$database) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$backup = $database->scheduledBackups->where('uuid', $backup_uuid)->first();
|
|
||||||
if (!$backup) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$executions = collect($backup->executions)->sortByDesc('created_at');
|
|
||||||
return view('project.database.backups.executions', [
|
|
||||||
'database' => $database,
|
|
||||||
'backup' => $backup,
|
|
||||||
'executions' => $executions,
|
|
||||||
's3s' => currentTeam()->s3s,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function backups()
|
|
||||||
{
|
|
||||||
$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');
|
|
||||||
}
|
|
||||||
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
|
||||||
if (!$database) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
// No backups for redis
|
|
||||||
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
|
|
||||||
return redirect()->route('project.database.configuration', [
|
|
||||||
'project_uuid' => $project->uuid,
|
|
||||||
'environment_name' => $environment->name,
|
|
||||||
'database_uuid' => $database->uuid,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return view('project.database.backups.all', [
|
|
||||||
'database' => $database,
|
|
||||||
's3s' => currentTeam()->s3s,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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');
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
@@ -11,9 +12,12 @@ 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()->route('boarding');
|
||||||
}
|
}
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
@@ -21,27 +25,27 @@ class DecideWhatToDoWithUser
|
|||||||
if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) {
|
if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
return redirect('/verify');
|
return redirect()->route('verify.email');
|
||||||
}
|
}
|
||||||
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
||||||
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
||||||
if (Str::startsWith($request->path(), 'invitations')) {
|
if (Str::startsWith($request->path(), 'invitations')) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
return redirect('subscription');
|
return redirect()->route('subscription.index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
||||||
if (Str::startsWith($request->path(), 'invitations')) {
|
if (Str::startsWith($request->path(), 'invitations')) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
return redirect('boarding');
|
return redirect()->route('boarding');
|
||||||
}
|
}
|
||||||
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
|
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
|
||||||
return redirect('/');
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
if (isSubscriptionActive() && $request->path() === 'subscription') {
|
if (isSubscriptionActive() && $request->path() === 'subscription') {
|
||||||
return redirect('/');
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -17,38 +17,34 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public string $build_logs_url;
|
public string $build_logs_url;
|
||||||
public Application $application;
|
|
||||||
public ApplicationPreview $preview;
|
|
||||||
public string $body;
|
public string $body;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $application_id,
|
public Application $application,
|
||||||
public int $pull_request_id,
|
public ApplicationPreview $preview,
|
||||||
public string $deployment_uuid,
|
public ProcessStatus $status,
|
||||||
public string $status
|
public ?string $deployment_uuid = null
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->application = Application::findOrFail($this->application_id);
|
if ($this->status === ProcessStatus::CLOSED) {
|
||||||
$this->preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pull_request_id);
|
$this->delete_comment();
|
||||||
|
return;
|
||||||
$this->build_logs_url = base_url() . "/project/{$this->application->environment->project->uuid}/{$this->application->environment->name}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
} else if ($this->status === ProcessStatus::IN_PROGRESS) {
|
||||||
|
|
||||||
if ($this->status === ProcessStatus::IN_PROGRESS->value) {
|
|
||||||
$this->body = "The preview deployment is in progress. 🟡\n\n";
|
$this->body = "The preview deployment is in progress. 🟡\n\n";
|
||||||
}
|
} else if ($this->status === ProcessStatus::FINISHED) {
|
||||||
if ($this->status === ProcessStatus::FINISHED->value) {
|
|
||||||
$this->body = "The preview deployment is ready. 🟢\n\n";
|
$this->body = "The preview deployment is ready. 🟢\n\n";
|
||||||
if ($this->preview->fqdn) {
|
if ($this->preview->fqdn) {
|
||||||
$this->body .= "[Open Preview]({$this->preview->fqdn}) | ";
|
$this->body .= "[Open Preview]({$this->preview->fqdn}) | ";
|
||||||
}
|
}
|
||||||
}
|
} else if ($this->status === ProcessStatus::ERROR) {
|
||||||
if ($this->status === ProcessStatus::ERROR->value) {
|
|
||||||
$this->body = "The preview deployment failed. 🔴\n\n";
|
$this->body = "The preview deployment failed. 🔴\n\n";
|
||||||
}
|
}
|
||||||
|
$this->build_logs_url = base_url() . "/project/{$this->application->environment->project->uuid}/{$this->application->environment->name}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
||||||
|
|
||||||
$this->body .= "[Open Build Logs](" . $this->build_logs_url . ")\n\n\n";
|
$this->body .= "[Open Build Logs](" . $this->build_logs_url . ")\n\n\n";
|
||||||
$this->body .= "Last updated at: " . now()->toDateTimeString() . " CET";
|
$this->body .= "Last updated at: " . now()->toDateTimeString() . " CET";
|
||||||
|
|
||||||
@@ -77,10 +73,14 @@ class ApplicationPullRequestUpdateJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
private function create_comment()
|
private function create_comment()
|
||||||
{
|
{
|
||||||
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/{$this->pull_request_id}/comments", method: 'post', data: [
|
['data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/{$this->preview->pull_request_id}/comments", method: 'post', data: [
|
||||||
'body' => $this->body,
|
'body' => $this->body,
|
||||||
]);
|
]);
|
||||||
$this->preview->pull_request_issue_comment_id = $data['id'];
|
$this->preview->pull_request_issue_comment_id = $data['id'];
|
||||||
$this->preview->save();
|
$this->preview->save();
|
||||||
}
|
}
|
||||||
|
private function delete_comment()
|
||||||
|
{
|
||||||
|
githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/issues/comments/{$this->preview->pull_request_issue_comment_id}", method: 'delete');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
// ray("checking log drain statuses for {$this->server->id}");
|
// ray("checking log drain statuses for {$this->server->id}");
|
||||||
try {
|
try {
|
||||||
if (!$this->server->isServerReady()) {
|
if (!$this->server->isFunctional()) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
||||||
@@ -63,19 +63,19 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
Sleep::for(10)->seconds();
|
Sleep::for(10)->seconds();
|
||||||
if ($this->healthcheck()) {
|
if ($this->healthcheck()) {
|
||||||
if ($this->server->log_drain_notification_sent) {
|
if ($this->server->log_drain_notification_sent) {
|
||||||
$this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||||
$this->server->update(['log_drain_notification_sent' => false]);
|
$this->server->update(['log_drain_notification_sent' => false]);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!$this->server->log_drain_notification_sent) {
|
if (!$this->server->log_drain_notification_sent) {
|
||||||
ray('Log drain container still unhealthy. Sending notification...');
|
ray('Log drain container still unhealthy. Sending notification...');
|
||||||
$this->server->team->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
|
$this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
|
||||||
$this->server->update(['log_drain_notification_sent' => true]);
|
$this->server->update(['log_drain_notification_sent' => true]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($this->server->log_drain_notification_sent) {
|
if ($this->server->log_drain_notification_sent) {
|
||||||
$this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||||
$this->server->update(['log_drain_notification_sent' => false]);
|
$this->server->update(['log_drain_notification_sent' => false]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,32 +22,33 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $tries = 4;
|
||||||
|
public function backoff(): int
|
||||||
|
{
|
||||||
|
return isDev() ? 1 : 3;
|
||||||
|
}
|
||||||
|
public function __construct(public Server $server)
|
||||||
|
{
|
||||||
|
}
|
||||||
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 __construct(public Server $server)
|
|
||||||
{
|
|
||||||
$this->handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
// ray("checking container statuses for {$this->server->id}");
|
if (!$this->server->isFunctional()) {
|
||||||
try {
|
return 'Server is not ready.';
|
||||||
if (!$this->server->isServerReady()) {
|
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
||||||
$containerReplicase = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
||||||
} else {
|
} else {
|
||||||
// Precheck for containers
|
// Precheck for containers
|
||||||
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
$containers = instant_remote_process(["docker container ls -q"], $this->server, false);
|
||||||
@@ -55,15 +56,15 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
||||||
$containerReplicase = null;
|
$containerReplicates = null;
|
||||||
}
|
}
|
||||||
if (is_null($containers)) {
|
if (is_null($containers)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$containers = format_docker_command_output_to_json($containers);
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
if ($containerReplicase) {
|
if ($containerReplicates) {
|
||||||
$containerReplicase = format_docker_command_output_to_json($containerReplicase);
|
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
|
||||||
foreach ($containerReplicase as $containerReplica) {
|
foreach ($containerReplicates as $containerReplica) {
|
||||||
$name = data_get($containerReplica, 'Name');
|
$name = data_get($containerReplica, 'Name');
|
||||||
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
||||||
if (data_get($container, 'Spec.Name') === $name) {
|
if (data_get($container, 'Spec.Name') === $name) {
|
||||||
@@ -159,6 +160,10 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// Notify user that this container should not be there.
|
// Notify user that this container should not be there.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (data_get($container,'Name') === '/coolify-db') {
|
||||||
|
$foundDatabases[] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
||||||
if ($serviceLabelId) {
|
if ($serviceLabelId) {
|
||||||
@@ -209,7 +214,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
$name = data_get($exitedService, 'name');
|
$name = data_get($exitedService, 'name');
|
||||||
$fqdn = data_get($exitedService, 'fqdn');
|
$fqdn = data_get($exitedService, 'fqdn');
|
||||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
$containerName = $name ? "$name, available at $fqdn" : $fqdn;
|
||||||
$projectUuid = data_get($service, 'environment.project.uuid');
|
$projectUuid = data_get($service, 'environment.project.uuid');
|
||||||
$serviceUuid = data_get($service, 'uuid');
|
$serviceUuid = data_get($service, 'uuid');
|
||||||
$environmentName = data_get($service, 'environment.name');
|
$environmentName = data_get($service, 'environment.name');
|
||||||
|
|||||||
@@ -286,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,
|
||||||
@@ -302,7 +302,7 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,14 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$server = $this->resource->destination->server;
|
$server = $this->resource->destination->server;
|
||||||
if (!$server->isFunctional()) {
|
|
||||||
$this->resource->delete();
|
$this->resource->delete();
|
||||||
|
if (!$server->isFunctional()) {
|
||||||
|
if ($this->resource->type() === 'service') {
|
||||||
|
ray('dispatching delete service');
|
||||||
|
DeleteService::dispatch($this->resource);
|
||||||
|
} else {
|
||||||
|
$this->resource->forceDelete();
|
||||||
|
}
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
switch ($this->resource->type()) {
|
switch ($this->resource->type()) {
|
||||||
@@ -56,10 +62,9 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ($this->resource->type() === 'service') {
|
if ($this->resource->type() === 'service') {
|
||||||
$this->resource->delete();
|
|
||||||
DeleteService::dispatch($this->resource);
|
DeleteService::dispatch($this->resource);
|
||||||
} else {
|
} else {
|
||||||
$this->resource->delete();
|
$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());
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Actions\Server\CleanupDocker;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
@@ -43,9 +44,7 @@ class DockerCleanupJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
ray('Usage before: ' . $this->usageBefore);
|
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);
|
ray('Cleaning up ' . $this->server->name);
|
||||||
instant_remote_process(['docker image prune -af'], $this->server, false);
|
CleanupDocker::run($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, false);
|
|
||||||
$usageAfter = $this->server->getDiskUsage();
|
$usageAfter = $this->server->getDiskUsage();
|
||||||
if ($usageAfter < $this->usageBefore) {
|
if ($usageAfter < $this->usageBefore) {
|
||||||
ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
ray('Saved ' . ($this->usageBefore - $usageAfter) . '% disk space on ' . $this->server->name);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class PullHelperImageJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
|
return [(new WithoutOverlapping($this->server->uuid))];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uniqueId(): string
|
public function uniqueId(): string
|
||||||
|
|||||||
117
app/Jobs/ScheduledTaskJob.php
Normal file
117
app/Jobs/ScheduledTaskJob.php
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Models\ScheduledTask;
|
||||||
|
use App\Models\ScheduledTaskExecution;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\Team;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ScheduledTaskJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public ?Team $team = null;
|
||||||
|
public Server $server;
|
||||||
|
public ScheduledTask $task;
|
||||||
|
public Application|Service $resource;
|
||||||
|
|
||||||
|
public ?ScheduledTaskExecution $task_log = null;
|
||||||
|
public string $task_status = 'failed';
|
||||||
|
public ?string $task_output = null;
|
||||||
|
public array $containers = [];
|
||||||
|
|
||||||
|
public function __construct($task)
|
||||||
|
{
|
||||||
|
$this->task = $task;
|
||||||
|
if ($service = $task->service()->first()) {
|
||||||
|
$this->resource = $service;
|
||||||
|
} else if ($application = $task->application()->first()) {
|
||||||
|
$this->resource = $application;
|
||||||
|
} else {
|
||||||
|
throw new \Exception('ScheduledTaskJob failed: No resource found.');
|
||||||
|
}
|
||||||
|
$this->team = Team::find($task->team_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function middleware(): array
|
||||||
|
{
|
||||||
|
return [new WithoutOverlapping($this->task->id)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uniqueId(): int
|
||||||
|
{
|
||||||
|
return $this->task->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->task_log = ScheduledTaskExecution::create([
|
||||||
|
'scheduled_task_id' => $this->task->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->server = $this->resource->destination->server;
|
||||||
|
|
||||||
|
if ($this->resource->type() == 'application') {
|
||||||
|
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
|
||||||
|
if ($containers->count() > 0) {
|
||||||
|
$containers->each(function ($container) {
|
||||||
|
$this->containers[] = str_replace('/', '', $container['Names']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($this->resource->type() == 'service') {
|
||||||
|
$this->resource->applications()->get()->each(function ($application) {
|
||||||
|
if (str(data_get($application, 'status'))->contains('running')) {
|
||||||
|
$this->containers[] = data_get($application, 'name') . '-' . data_get($this->resource, 'uuid');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($this->containers) == 0) {
|
||||||
|
throw new \Exception('ScheduledTaskJob failed: No containers running.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($this->containers) > 1 && empty($this->task->container)) {
|
||||||
|
throw new \Exception('ScheduledTaskJob failed: More than one container exists but no container name was provided.');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->containers as $containerName) {
|
||||||
|
if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container . '-' . $this->resource->uuid)) {
|
||||||
|
$cmd = 'sh -c "' . str_replace('"', '\"', $this->task->command) . '"';
|
||||||
|
$exec = "docker exec {$containerName} {$cmd}";
|
||||||
|
$this->task_output = instant_remote_process([$exec], $this->server, true);
|
||||||
|
$this->task_log->update([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => $this->task_output,
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No valid container was found.
|
||||||
|
throw new \Exception('ScheduledTaskJob failed: No valid container was found. Is the container name correct?');
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
if ($this->task_log) {
|
||||||
|
$this->task_log->update([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => $this->task_output ?? $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,30 +17,37 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
public ?int $disk_usage = null;
|
public ?int $disk_usage = null;
|
||||||
|
public $tries = 4;
|
||||||
|
public function backoff(): int
|
||||||
|
{
|
||||||
|
return isDev() ? 1 : 3;
|
||||||
|
}
|
||||||
public function __construct(public Server $server)
|
public function __construct(public Server $server)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
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 handle()
|
||||||
{
|
{
|
||||||
ray("checking server status for {$this->server->id}");
|
if (!$this->server->isServerReady($this->tries)) {
|
||||||
|
throw new \RuntimeException('Server is not ready.');
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
if ($this->server->isServerReady()) {
|
if ($this->server->isFunctional()) {
|
||||||
$this->cleanup(notify: false);
|
$this->cleanup(notify: false);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
|
send_internal_notification('ServerStatusJob failed with: ' . $e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function cleanup(bool $notify = false): void
|
public function cleanup(bool $notify = false): void
|
||||||
@@ -54,7 +61,7 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} else {
|
} else {
|
||||||
$this->server->high_disk_usage_notification_sent = true;
|
$this->server->high_disk_usage_notification_sent = true;
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
$this->server->team->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage));
|
$this->server->team?->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DockerCleanupJob::dispatchSync($this->server);
|
DockerCleanupJob::dispatchSync($this->server);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Enums\ProcessStatus;
|
use App\Enums\ProcessStatus;
|
||||||
|
use App\Models\User;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
|
||||||
@@ -10,14 +11,16 @@ class ActivityMonitor extends Component
|
|||||||
{
|
{
|
||||||
public ?string $header = null;
|
public ?string $header = null;
|
||||||
public $activityId;
|
public $activityId;
|
||||||
|
public $eventToDispatch = 'activityFinished';
|
||||||
public $isPollingActive = false;
|
public $isPollingActive = false;
|
||||||
|
|
||||||
protected $activity;
|
protected $activity;
|
||||||
protected $listeners = ['newMonitorActivity'];
|
protected $listeners = ['newMonitorActivity'];
|
||||||
|
|
||||||
public function newMonitorActivity($activityId)
|
public function newMonitorActivity($activityId, $eventToDispatch = 'activityFinished')
|
||||||
{
|
{
|
||||||
$this->activityId = $activityId;
|
$this->activityId = $activityId;
|
||||||
|
$this->eventToDispatch = $eventToDispatch;
|
||||||
|
|
||||||
$this->hydrateActivity();
|
$this->hydrateActivity();
|
||||||
|
|
||||||
@@ -35,13 +38,28 @@ class ActivityMonitor extends Component
|
|||||||
// $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->dispatch('activityFinished');
|
if ($exit_code === 0) {
|
||||||
|
if ($this->eventToDispatch !== null) {
|
||||||
|
if (str($this->eventToDispatch)->startsWith('App\\Events\\')) {
|
||||||
|
$causer_id = data_get($this->activity, 'causer_id');
|
||||||
|
$user = User::find($causer_id);
|
||||||
|
if ($user) {
|
||||||
|
foreach($user->teams as $team) {
|
||||||
|
$teamId = $team->id;
|
||||||
|
$this->eventToDispatch::dispatch($teamId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->dispatch($this->eventToDispatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
{
|
{
|
||||||
$this->skipBoarding();
|
$this->skipBoarding();
|
||||||
return redirect()->route(
|
return redirect()->route(
|
||||||
'project.resources.new',
|
'project.resource.create',
|
||||||
[
|
[
|
||||||
'project_uuid' => $this->createdProject->uuid,
|
'project_uuid' => $this->createdProject->uuid,
|
||||||
'environment_name' => 'production',
|
'environment_name' => 'production',
|
||||||
|
|||||||
18
app/Livewire/CommandCenter/Index.php
Normal file
18
app/Livewire/CommandCenter/Index.php
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\CommandCenter;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Index extends Component
|
||||||
|
{
|
||||||
|
public $servers = [];
|
||||||
|
public function mount() {
|
||||||
|
$this->servers = Server::isReachable()->get();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.command-center.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,18 +2,35 @@
|
|||||||
|
|
||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Dashboard extends Component
|
class Dashboard extends Component
|
||||||
{
|
{
|
||||||
public $projects = [];
|
public $projects = [];
|
||||||
public $servers = [];
|
public Collection $servers;
|
||||||
|
public $deployments_per_server;
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->servers = Server::ownedByCurrentTeam()->get();
|
$this->servers = Server::ownedByCurrentTeam()->get();
|
||||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||||
|
$this->get_deployments();
|
||||||
|
}
|
||||||
|
public function get_deployments()
|
||||||
|
{
|
||||||
|
$this->deployments_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn("server_id", $this->servers->pluck("id"))->get([
|
||||||
|
"id",
|
||||||
|
"application_id",
|
||||||
|
"application_name",
|
||||||
|
"deployment_url",
|
||||||
|
"pull_request_id",
|
||||||
|
"server_name",
|
||||||
|
"server_id",
|
||||||
|
"status"
|
||||||
|
])->sortBy('id')->groupBy('server_name')->toArray();
|
||||||
}
|
}
|
||||||
// public function getIptables()
|
// public function getIptables()
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class Form extends Component
|
|||||||
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 $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,29 +4,32 @@ namespace App\Livewire\Destination\New;
|
|||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
||||||
|
use App\Models\SwarmDocker;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
class StandaloneDocker extends Component
|
class Docker extends Component
|
||||||
{
|
{
|
||||||
public string $name;
|
public string $name;
|
||||||
public string $network;
|
public string $network;
|
||||||
|
|
||||||
public Collection $servers;
|
public Collection $servers;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public int|null $server_id = null;
|
public ?int $server_id = null;
|
||||||
|
public bool $is_swarm = false;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'network' => 'required|string',
|
'network' => 'required|string',
|
||||||
'server_id' => 'required|integer'
|
'server_id' => 'required|integer',
|
||||||
|
'is_swarm' => 'boolean'
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'name' => 'name',
|
'name' => 'name',
|
||||||
'network' => 'network',
|
'network' => 'network',
|
||||||
'server_id' => 'server'
|
'server_id' => 'server',
|
||||||
|
'is_swarm' => 'swarm'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -43,13 +46,13 @@ class StandaloneDocker extends Component
|
|||||||
} else {
|
} else {
|
||||||
$this->network = new Cuid2(7);
|
$this->network = new Cuid2(7);
|
||||||
}
|
}
|
||||||
$this->name = Str::kebab("{$this->servers->first()->name}-{$this->network}");
|
$this->name = str("{$this->servers->first()->name}-{$this->network}")->kebab();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generate_name()
|
public function generate_name()
|
||||||
{
|
{
|
||||||
$this->server = Server::find($this->server_id);
|
$this->server = Server::find($this->server_id);
|
||||||
$this->name = Str::kebab("{$this->server->name}-{$this->network}");
|
$this->name = str("{$this->server->name}-{$this->network}")->kebab();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
@@ -57,9 +60,21 @@ class StandaloneDocker extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
try {
|
try {
|
||||||
$this->server = Server::find($this->server_id);
|
$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();
|
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
|
||||||
if ($found) {
|
if ($found) {
|
||||||
$this->createNetworkAndAttachToProxy();
|
|
||||||
$this->dispatch('error', 'Network already added to this server.');
|
$this->dispatch('error', 'Network already added to this server.');
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -69,8 +84,9 @@ class StandaloneDocker extends Component
|
|||||||
'server_id' => $this->server_id,
|
'server_id' => $this->server_id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
$this->createNetworkAndAttachToProxy();
|
$this->createNetworkAndAttachToProxy();
|
||||||
return $this->redirectRoute('destination.show', $docker->uuid, navigate: true);
|
return redirect()->route('destination.show', $docker->uuid);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,11 @@ class Show extends Component
|
|||||||
|
|
||||||
public function scan()
|
public function scan()
|
||||||
{
|
{
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
$alreadyAddedNetworks = $this->server->swarmDockers;
|
||||||
|
} else {
|
||||||
$alreadyAddedNetworks = $this->server->standaloneDockers;
|
$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';
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ class ForcePasswordReset extends Component
|
|||||||
{
|
{
|
||||||
$this->email = auth()->user()->email;
|
$this->email = auth()->user()->email;
|
||||||
}
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.force-password-reset')->layout('layouts.simple');
|
||||||
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -35,7 +39,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 $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class DiscordSettings extends Component
|
|||||||
|
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team->notify(new Test());
|
$this->team?->notify(new Test());
|
||||||
$this->dispatch('success', 'Test notification sent.');
|
$this->dispatch('success', 'Test notification sent.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class EmailSettings extends Component
|
|||||||
}
|
}
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team->notify(new Test($this->emails));
|
$this->team?->notify(new Test($this->emails));
|
||||||
$this->dispatch('success', 'Test Email sent successfully.');
|
$this->dispatch('success', 'Test Email sent successfully.');
|
||||||
}
|
}
|
||||||
public function instantSaveInstance()
|
public function instantSaveInstance()
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class TelegramSettings extends Component
|
|||||||
|
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->team->notify(new Test());
|
$this->team?->notify(new Test());
|
||||||
$this->dispatch('success', 'Test notification sent.');
|
$this->dispatch('success', 'Test notification sent.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,19 @@ namespace App\Livewire\Profile;
|
|||||||
use Livewire\Attributes\Validate;
|
use Livewire\Attributes\Validate;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Form extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
public int $userId;
|
public int $userId;
|
||||||
public string $email;
|
public string $email;
|
||||||
|
|
||||||
#[Validate('required')]
|
#[Validate('required')]
|
||||||
public string $name;
|
public string $name;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->userId = auth()->user()->id;
|
$this->userId = auth()->user()->id;
|
||||||
$this->name = auth()->user()->name;
|
$this->name = auth()->user()->name;
|
||||||
$this->email = auth()->user()->email;
|
$this->email = auth()->user()->email;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -34,4 +32,8 @@ class Form extends Component
|
|||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.profile.index');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ class AddEmpty extends Component
|
|||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
return $this->redirectRoute('project.show', $project->uuid, navigate: true);
|
return redirect()->route('project.show', $project->uuid);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ class AddEnvironment extends Component
|
|||||||
'project_id' => $this->project->id,
|
'project_id' => $this->project->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->redirectRoute('project.resources', [
|
return redirect()->route('project.resource.index', [
|
||||||
'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 {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class Advanced extends Component
|
|||||||
'application.settings.is_force_https_enabled' => 'boolean|required',
|
'application.settings.is_force_https_enabled' => 'boolean|required',
|
||||||
'application.settings.is_log_drain_enabled' => 'boolean|required',
|
'application.settings.is_log_drain_enabled' => 'boolean|required',
|
||||||
'application.settings.is_gpu_enabled' => 'boolean|required',
|
'application.settings.is_gpu_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||||
'application.settings.gpu_driver' => 'string|required',
|
'application.settings.gpu_driver' => 'string|required',
|
||||||
'application.settings.gpu_count' => 'string|required',
|
'application.settings.gpu_count' => 'string|required',
|
||||||
'application.settings.gpu_device_ids' => 'string|required',
|
'application.settings.gpu_device_ids' => 'string|required',
|
||||||
|
|||||||
@@ -4,26 +4,27 @@ namespace App\Livewire\Project\Application;
|
|||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Configuration extends Component
|
class Configuration extends Component
|
||||||
{
|
{
|
||||||
public Application $application;
|
public Application $application;
|
||||||
public $servers;
|
public $servers;
|
||||||
|
protected $listeners = ['build_pack_updated' => '$refresh'];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
return $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
|
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
|
||||||
if (!$environment) {
|
if (!$environment) {
|
||||||
return $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
|
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
|
||||||
if (!$application) {
|
if (!$application) {
|
||||||
return $this->redirectRoute('dashboard', navigate: true);
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$this->application = $application;
|
$this->application = $application;
|
||||||
$mainServer = $this->application->destination->server;
|
$mainServer = $this->application->destination->server;
|
||||||
|
|||||||
97
app/Livewire/Project/Application/Deployment/Index.php
Normal file
97
app/Livewire/Project/Application/Deployment/Index.php
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Application\Deployment;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Index extends Component
|
||||||
|
{
|
||||||
|
public Application $application;
|
||||||
|
public array|Collection $deployments = [];
|
||||||
|
public int $deployments_count = 0;
|
||||||
|
public string $current_url;
|
||||||
|
public int $skip = 0;
|
||||||
|
public int $default_take = 40;
|
||||||
|
public bool $show_next = false;
|
||||||
|
public bool $show_prev = false;
|
||||||
|
public ?string $pull_request_id = null;
|
||||||
|
protected $queryString = ['pull_request_id'];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$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');
|
||||||
|
}
|
||||||
|
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
|
||||||
|
$this->application = $application;
|
||||||
|
$this->deployments = $deployments;
|
||||||
|
$this->deployments_count = $count;
|
||||||
|
$this->current_url = url()->current();
|
||||||
|
$this->show_pull_request_only();
|
||||||
|
$this->show_more();
|
||||||
|
}
|
||||||
|
private function show_pull_request_only()
|
||||||
|
{
|
||||||
|
if ($this->pull_request_id) {
|
||||||
|
$this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function show_more()
|
||||||
|
{
|
||||||
|
if (count($this->deployments) !== 0) {
|
||||||
|
$this->show_next = true;
|
||||||
|
if (count($this->deployments) < $this->default_take) {
|
||||||
|
$this->show_next = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reload_deployments()
|
||||||
|
{
|
||||||
|
$this->load_deployments();
|
||||||
|
}
|
||||||
|
public function previous_page(?int $take = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if ($take) {
|
||||||
|
$this->skip = $this->skip - $take;
|
||||||
|
}
|
||||||
|
$this->skip = $this->skip - $this->default_take;
|
||||||
|
if ($this->skip < 0) {
|
||||||
|
$this->show_prev = false;
|
||||||
|
$this->skip = 0;
|
||||||
|
}
|
||||||
|
$this->load_deployments();
|
||||||
|
}
|
||||||
|
public function next_page(?int $take = null)
|
||||||
|
{
|
||||||
|
if ($take) {
|
||||||
|
$this->skip = $this->skip + $take;
|
||||||
|
}
|
||||||
|
$this->show_prev = true;
|
||||||
|
$this->load_deployments();
|
||||||
|
}
|
||||||
|
public function load_deployments()
|
||||||
|
{
|
||||||
|
['deployments' => $deployments, 'count' => $count] = $this->application->deployments($this->skip, $this->default_take);
|
||||||
|
$this->deployments = $deployments;
|
||||||
|
$this->deployments_count = $count;
|
||||||
|
$this->show_pull_request_only();
|
||||||
|
$this->show_more();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.application.deployment.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Livewire\Project\Application\Deployment;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Livewire\Component;
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
|
||||||
|
|
||||||
class ApplicationController extends Controller
|
class Show extends Component
|
||||||
{
|
{
|
||||||
use AuthorizesRequests, ValidatesRequests;
|
public Application $application;
|
||||||
|
public ApplicationDeploymentQueue $application_deployment_queue;
|
||||||
|
public string $deployment_uuid;
|
||||||
|
public $isKeepAliveOn = true;
|
||||||
|
protected $listeners = ['refreshQueue'];
|
||||||
|
|
||||||
public function deployments()
|
public function mount() {
|
||||||
{
|
|
||||||
$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');
|
|
||||||
}
|
|
||||||
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
|
|
||||||
return view('project.application.deployments', ['application' => $application, 'deployments' => $deployments, 'deployments_count' => $count]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deployment()
|
|
||||||
{
|
|
||||||
$deploymentUuid = request()->route('deployment_uuid');
|
$deploymentUuid = request()->route('deployment_uuid');
|
||||||
|
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
@@ -46,7 +31,7 @@ class ApplicationController extends Controller
|
|||||||
}
|
}
|
||||||
// $activity = Activity::where('properties->type_uuid', '=', $deploymentUuid)->first();
|
// $activity = Activity::where('properties->type_uuid', '=', $deploymentUuid)->first();
|
||||||
// if (!$activity) {
|
// if (!$activity) {
|
||||||
// return redirect()->route('project.application.deployments', [
|
// return redirect()->route('project.application.deployment.index', [
|
||||||
// 'project_uuid' => $project->uuid,
|
// 'project_uuid' => $project->uuid,
|
||||||
// 'environment_name' => $environment->name,
|
// 'environment_name' => $environment->name,
|
||||||
// 'application_uuid' => $application->uuid,
|
// 'application_uuid' => $application->uuid,
|
||||||
@@ -54,17 +39,32 @@ class ApplicationController extends Controller
|
|||||||
// }
|
// }
|
||||||
$application_deployment_queue = ApplicationDeploymentQueue::where('deployment_uuid', $deploymentUuid)->first();
|
$application_deployment_queue = ApplicationDeploymentQueue::where('deployment_uuid', $deploymentUuid)->first();
|
||||||
if (!$application_deployment_queue) {
|
if (!$application_deployment_queue) {
|
||||||
return redirect()->route('project.application.deployments', [
|
return redirect()->route('project.application.deployment.index', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return view('project.application.deployment', [
|
$this->application = $application;
|
||||||
'application' => $application,
|
$this->application_deployment_queue = $application_deployment_queue;
|
||||||
// 'activity' => $activity,
|
$this->deployment_uuid = $deploymentUuid;
|
||||||
'application_deployment_queue' => $application_deployment_queue,
|
}
|
||||||
'deployment_uuid' => $deploymentUuid,
|
|
||||||
]);
|
public function refreshQueue()
|
||||||
|
{
|
||||||
|
$this->application_deployment_queue->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function polling()
|
||||||
|
{
|
||||||
|
$this->dispatch('deploymentFinished');
|
||||||
|
$this->application_deployment_queue->refresh();
|
||||||
|
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
|
||||||
|
$this->isKeepAliveOn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.application.deployment.show');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Livewire\Project\Application;
|
|
||||||
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class DeploymentLogs extends Component
|
|
||||||
{
|
|
||||||
public ApplicationDeploymentQueue $application_deployment_queue;
|
|
||||||
public $isKeepAliveOn = true;
|
|
||||||
protected $listeners = ['refreshQueue'];
|
|
||||||
|
|
||||||
public function refreshQueue()
|
|
||||||
{
|
|
||||||
$this->application_deployment_queue->refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function polling()
|
|
||||||
{
|
|
||||||
$this->dispatch('deploymentFinished');
|
|
||||||
$this->application_deployment_queue->refresh();
|
|
||||||
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
|
|
||||||
$this->isKeepAliveOn = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -41,13 +41,10 @@ class DeploymentNavbar extends Component
|
|||||||
public function cancel()
|
public function cancel()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$kill_command = "kill -9 {$this->application_deployment_queue->current_process_id}";
|
$kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}";
|
||||||
if ($this->application_deployment_queue->current_process_id) {
|
if ($this->application_deployment_queue->logs) {
|
||||||
$process = Process::run("ps -p {$this->application_deployment_queue->current_process_id} -o command --no-headers");
|
|
||||||
if (Str::of($process->output())->contains([$this->server->ip, 'EOF-COOLIFY-SSH'])) {
|
|
||||||
Process::run($kill_command);
|
|
||||||
}
|
|
||||||
$previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR);
|
$previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
$new_log_entry = [
|
$new_log_entry = [
|
||||||
'command' => $kill_command,
|
'command' => $kill_command,
|
||||||
'output' => "Deployment cancelled by user.",
|
'output' => "Deployment cancelled by user.",
|
||||||
@@ -60,15 +57,17 @@ class DeploymentNavbar extends Component
|
|||||||
$this->application_deployment_queue->update([
|
$this->application_deployment_queue->update([
|
||||||
'logs' => json_encode($previous_logs, flags: JSON_THROW_ON_ERROR),
|
'logs' => json_encode($previous_logs, flags: JSON_THROW_ON_ERROR),
|
||||||
]);
|
]);
|
||||||
|
instant_remote_process([$kill_command], $this->server);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->application_deployment_queue->update([
|
$this->application_deployment_queue->update([
|
||||||
'current_process_id' => null,
|
'current_process_id' => null,
|
||||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||||
]);
|
]);
|
||||||
queue_next_deployment($this->application);
|
// queue_next_deployment($this->application);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Livewire\Project\Application;
|
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Deployments extends Component
|
|
||||||
{
|
|
||||||
public Application $application;
|
|
||||||
public Array|Collection $deployments = [];
|
|
||||||
public int $deployments_count = 0;
|
|
||||||
public string $current_url;
|
|
||||||
public int $skip = 0;
|
|
||||||
public int $default_take = 40;
|
|
||||||
public bool $show_next = false;
|
|
||||||
public ?string $pull_request_id = null;
|
|
||||||
protected $queryString = ['pull_request_id'];
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->current_url = url()->current();
|
|
||||||
$this->show_pull_request_only();
|
|
||||||
$this->show_more();
|
|
||||||
}
|
|
||||||
private function show_pull_request_only() {
|
|
||||||
if ($this->pull_request_id) {
|
|
||||||
$this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private function show_more()
|
|
||||||
{
|
|
||||||
if (count($this->deployments) !== 0) {
|
|
||||||
$this->show_next = true;
|
|
||||||
if (count($this->deployments) < $this->default_take) {
|
|
||||||
$this->show_next = false;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reload_deployments()
|
|
||||||
{
|
|
||||||
$this->load_deployments();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function load_deployments(int|null $take = null)
|
|
||||||
{
|
|
||||||
if ($take) {
|
|
||||||
$this->skip = $this->skip + $take;
|
|
||||||
}
|
|
||||||
$take = $this->default_take;
|
|
||||||
|
|
||||||
['deployments' => $deployments, 'count' => $count] = $this->application->deployments($this->skip, $take);
|
|
||||||
$this->deployments = $deployments;
|
|
||||||
$this->deployments_count = $count;
|
|
||||||
$this->show_pull_request_only();
|
|
||||||
$this->show_more();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -64,6 +64,10 @@ class General extends Component
|
|||||||
'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.settings.is_static' => 'boolean|required',
|
||||||
|
'application.docker_compose_custom_start_command' => 'nullable',
|
||||||
|
'application.docker_compose_custom_build_command' => 'nullable',
|
||||||
|
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
|
||||||
|
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@@ -94,6 +98,10 @@ class General extends Component
|
|||||||
'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.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',
|
||||||
|
'application.settings.is_raw_compose_deployment_enabled' => 'Is raw compose deployment enabled',
|
||||||
|
'application.settings.is_build_server_enabled' => 'Is build server enabled',
|
||||||
];
|
];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -109,24 +117,12 @@ class General extends Component
|
|||||||
$this->application->isConfigurationChanged(true);
|
$this->application->isConfigurationChanged(true);
|
||||||
}
|
}
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
|
$this->customLabels = $this->application->parseContainerLabels();
|
||||||
if (data_get($this->application, 'custom_labels')) {
|
if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
|
||||||
if (base64_encode(base64_decode(data_get($this->application, 'custom_labels'), true)) === data_get($this->application, 'custom_labels')) {
|
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||||
ray('custom_labels is base64 encoded');
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
} else {
|
|
||||||
ray('custom_labels is not base64 encoded');
|
|
||||||
$this->application->custom_labels = str($this->application->custom_labels)->replace(',', "\n");
|
|
||||||
$this->application->custom_labels = base64_encode(data_get($this->application, 'custom_labels'));
|
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
}
|
}
|
||||||
$this->customLabels = base64_decode(data_get($this->application, 'custom_labels'));
|
|
||||||
// // Fix for non-ascii characters
|
|
||||||
if (mb_detect_encoding($this->customLabels, 'ASCII', true) === false) {
|
|
||||||
ray('custom_labels contains non-ascii characters');
|
|
||||||
$this->resetDefaultLabels(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||||
$this->checkLabelUpdates();
|
$this->checkLabelUpdates();
|
||||||
}
|
}
|
||||||
@@ -163,7 +159,6 @@ class General extends Component
|
|||||||
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
|
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
|
||||||
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->dispatch('success', 'Domain generated.');
|
|
||||||
}
|
}
|
||||||
return $domain;
|
return $domain;
|
||||||
}
|
}
|
||||||
@@ -172,16 +167,24 @@ class General extends Component
|
|||||||
if ($this->application->build_pack !== 'nixpacks') {
|
if ($this->application->build_pack !== 'nixpacks') {
|
||||||
$this->application->settings->is_static = false;
|
$this->application->settings->is_static = false;
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
|
} else {
|
||||||
|
$this->application->ports_exposes = $this->ports_exposes = 3000;
|
||||||
|
$this->resetDefaultLabels(false);
|
||||||
}
|
}
|
||||||
if ($this->application->build_pack === 'dockercompose') {
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
$this->application->fqdn = null;
|
$this->application->fqdn = null;
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
}
|
}
|
||||||
|
if ($this->application->build_pack === 'static') {
|
||||||
|
$this->application->ports_exposes = $this->ports_exposes = 80;
|
||||||
|
$this->resetDefaultLabels(false);
|
||||||
|
}
|
||||||
$this->submit();
|
$this->submit();
|
||||||
|
$this->dispatch('build_pack_updated');
|
||||||
}
|
}
|
||||||
public function checkLabelUpdates()
|
public function checkLabelUpdates()
|
||||||
{
|
{
|
||||||
if (md5($this->application->custom_labels) !== md5(implode(",", generateLabelsApplication($this->application)))) {
|
if (md5($this->application->custom_labels) !== md5(implode("|", generateLabelsApplication($this->application)))) {
|
||||||
$this->labelsChanged = true;
|
$this->labelsChanged = true;
|
||||||
} else {
|
} else {
|
||||||
$this->labelsChanged = false;
|
$this->labelsChanged = false;
|
||||||
@@ -200,7 +203,7 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
public function resetDefaultLabels($showToaster = true)
|
public function resetDefaultLabels($showToaster = true)
|
||||||
{
|
{
|
||||||
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||||
$this->ports_exposes = $this->application->ports_exposes;
|
$this->ports_exposes = $this->application->ports_exposes;
|
||||||
$this->submit($showToaster);
|
$this->submit($showToaster);
|
||||||
}
|
}
|
||||||
@@ -208,12 +211,18 @@ class General extends Component
|
|||||||
public function updatedApplicationFqdn()
|
public function updatedApplicationFqdn()
|
||||||
{
|
{
|
||||||
$this->resetDefaultLabels(false);
|
$this->resetDefaultLabels(false);
|
||||||
$this->dispatch('success', 'Labels reseted to default!');
|
// $this->dispatch('success', 'Labels reset to default!');
|
||||||
}
|
}
|
||||||
public function submit($showToaster = true)
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if ($this->application->build_pack === 'dockercompose' && ($this->initialDockerComposeLocation !== $this->application->docker_compose_location || $this->initialDockerComposePrLocation !== $this->application->docker_compose_pr_location)) {
|
if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
|
||||||
|
$this->customLabels = str(implode("|", generateLabelsApplication($this->application)))->replace("|", "\n");
|
||||||
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
|
$this->application->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
|
||||||
$this->loadComposeFile();
|
$this->loadComposeFile();
|
||||||
}
|
}
|
||||||
$this->validate();
|
$this->validate();
|
||||||
@@ -227,9 +236,16 @@ class General extends Component
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if (data_get($this->application, 'fqdn')) {
|
if (data_get($this->application, 'fqdn')) {
|
||||||
$domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||||
return Str::of($domain)->trim()->lower();
|
$domains = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||||
|
return str($domain)->trim()->lower();
|
||||||
});
|
});
|
||||||
|
$domains = $domains->unique();
|
||||||
|
foreach ($domains as $domain) {
|
||||||
|
if (!validate_dns_entry($domain, $this->application->destination->server)) {
|
||||||
|
$showToaster && $this->dispatch('error', "Validating DNS settings for: $domain failed.<br>Make sure you have added the DNS records correctly.<br><br>Check this <a target='_blank' class='underline' href='https://coolify.io/docs/dns-settings'>documentation</a> for further help.");
|
||||||
|
}
|
||||||
|
}
|
||||||
$this->application->fqdn = $domains->implode(',');
|
$this->application->fqdn = $domains->implode(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Livewire\Project\Application;
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Actions\Application\StopApplication;
|
use App\Actions\Application\StopApplication;
|
||||||
use App\Events\ApplicationStatusChanged;
|
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Jobs\ServerStatusJob;
|
use App\Jobs\ServerStatusJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
@@ -39,7 +38,7 @@ class Heading extends Component
|
|||||||
} else {
|
} else {
|
||||||
dispatch(new ServerStatusJob($this->application->destination->server));
|
dispatch(new ServerStatusJob($this->application->destination->server));
|
||||||
}
|
}
|
||||||
if ($showNotification) $this->dispatch('success', 'Application status updated.');
|
if ($showNotification) $this->dispatch('success', "Application status updated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function force_deploy_without_cache()
|
public function force_deploy_without_cache()
|
||||||
@@ -55,17 +54,17 @@ class Heading extends Component
|
|||||||
}
|
}
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application_id: $this->application->id,
|
application: $this->application,
|
||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
is_new_deployment: true,
|
is_new_deployment: true,
|
||||||
);
|
);
|
||||||
return $this->redirectRoute('project.application.deployment', [
|
return redirect()->route('project.application.deployment.show', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
public function deploy(bool $force_rebuild = false)
|
public function deploy(bool $force_rebuild = false)
|
||||||
{
|
{
|
||||||
@@ -73,18 +72,26 @@ class Heading extends Component
|
|||||||
$this->dispatch('error', 'Please load a Compose file first.');
|
$this->dispatch('error', 'Please load a Compose file first.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ($this->application->destination->server->isSwarm() && is_null($this->application->docker_registry_image_name)) {
|
||||||
|
$this->dispatch('error', 'To deploy to a Swarm cluster you must set a Docker image name first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data_get($this->application, 'settings.is_build_server_enabled') && is_null($this->application->docker_registry_image_name)) {
|
||||||
|
$this->dispatch('error', 'To use a build server you must set a Docker image name first.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application_id: $this->application->id,
|
application: $this->application,
|
||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
force_rebuild: $force_rebuild,
|
force_rebuild: $force_rebuild,
|
||||||
);
|
);
|
||||||
$this->redirectRoute('project.application.deployment', [
|
return redirect()->route('project.application.deployment.show', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setDeploymentUuid()
|
protected function setDeploymentUuid()
|
||||||
@@ -104,31 +111,31 @@ class Heading extends Component
|
|||||||
{
|
{
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application_id: $this->application->id,
|
application: $this->application,
|
||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
restart_only: true,
|
restart_only: true,
|
||||||
is_new_deployment: true,
|
is_new_deployment: true,
|
||||||
);
|
);
|
||||||
return $this->redirectRoute('project.application.deployment', [
|
return redirect()->route('project.application.deployment.show', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
public function restart()
|
public function restart()
|
||||||
{
|
{
|
||||||
$this->setDeploymentUuid();
|
$this->setDeploymentUuid();
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application_id: $this->application->id,
|
application: $this->application,
|
||||||
deployment_uuid: $this->deploymentUuid,
|
deployment_uuid: $this->deploymentUuid,
|
||||||
restart_only: true,
|
restart_only: true,
|
||||||
);
|
);
|
||||||
return $this->redirectRoute('project.application.deployment', [
|
return redirect()->route('project.application.deployment.show', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,17 +47,18 @@ class Previews extends Component
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application_id: $this->application->id,
|
application: $this->application,
|
||||||
deployment_uuid: $this->deployment_uuid,
|
deployment_uuid: $this->deployment_uuid,
|
||||||
force_rebuild: true,
|
force_rebuild: false,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
|
git_type: $found->git_type ?? null,
|
||||||
);
|
);
|
||||||
return $this->redirectRoute('project.application.deployment', [
|
return redirect()->route('project.application.deployment.show', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deployment_uuid,
|
'deployment_uuid' => $this->deployment_uuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -72,11 +73,15 @@ class Previews extends Component
|
|||||||
public function stop(int $pull_request_id)
|
public function stop(int $pull_request_id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
if ($this->application->destination->server->isSwarm()) {
|
||||||
|
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
|
||||||
|
} else {
|
||||||
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
|
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
|
||||||
foreach ($containers as $container) {
|
foreach ($containers as $container) {
|
||||||
$name = str_replace('/', '', $container['Names']);
|
$name = str_replace('/', '', $container['Names']);
|
||||||
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
|
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -24,17 +24,17 @@ class Rollback extends Component
|
|||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application_id: $this->application->id,
|
application: $this->application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
commit: $commit,
|
commit: $commit,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
);
|
);
|
||||||
return $this->redirectRoute('project.application.deployment', [
|
return redirect()->route('project.application.deployment.show', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $deployment_uuid,
|
'deployment_uuid' => $deployment_uuid,
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadImages($showToast = false)
|
public function loadImages($showToast = false)
|
||||||
|
|||||||
51
app/Livewire/Project/Application/Swarm.php
Normal file
51
app/Livewire/Project/Application/Swarm.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Application;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Swarm extends Component
|
||||||
|
{
|
||||||
|
public Application $application;
|
||||||
|
public string $swarm_placement_constraints = '';
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'application.swarm_replicas' => 'required',
|
||||||
|
'application.swarm_placement_constraints' => 'nullable',
|
||||||
|
'application.settings.is_swarm_only_worker_nodes' => 'required',
|
||||||
|
];
|
||||||
|
public function mount() {
|
||||||
|
if ($this->application->swarm_placement_constraints) {
|
||||||
|
$this->swarm_placement_constraints = base64_decode($this->application->swarm_placement_constraints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave() {
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->application->settings->save();
|
||||||
|
$this->dispatch('success', 'Swarm settings updated.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function submit() {
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
if ($this->swarm_placement_constraints) {
|
||||||
|
$this->application->swarm_placement_constraints = base64_encode($this->swarm_placement_constraints);
|
||||||
|
} else {
|
||||||
|
$this->application->swarm_placement_constraints = null;
|
||||||
|
}
|
||||||
|
$this->application->save();
|
||||||
|
|
||||||
|
$this->dispatch('success', 'Swarm settings updated.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.application.swarm');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ use App\Models\Server;
|
|||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
class CloneProject extends Component
|
class CloneMe extends Component
|
||||||
{
|
{
|
||||||
public string $project_uuid;
|
public string $project_uuid;
|
||||||
public string $environment_name;
|
public string $environment_name;
|
||||||
@@ -19,12 +19,14 @@ class CloneProject extends Component
|
|||||||
public $servers;
|
public $servers;
|
||||||
public ?Environment $environment = null;
|
public ?Environment $environment = null;
|
||||||
public ?int $selectedServer = null;
|
public ?int $selectedServer = null;
|
||||||
|
public ?int $selectedDestination = null;
|
||||||
public ?Server $server = null;
|
public ?Server $server = null;
|
||||||
public $resources = [];
|
public $resources = [];
|
||||||
public string $newProjectName = '';
|
public string $newProjectName = '';
|
||||||
|
|
||||||
protected $messages = [
|
protected $messages = [
|
||||||
'selectedServer' => 'Please select a server.',
|
'selectedServer' => 'Please select a server.',
|
||||||
|
'selectedDestination' => 'Please select a server & destination.',
|
||||||
'newProjectName' => 'Please enter a name for the new project.',
|
'newProjectName' => 'Please enter a name for the new project.',
|
||||||
];
|
];
|
||||||
public function mount($project_uuid)
|
public function mount($project_uuid)
|
||||||
@@ -34,17 +36,18 @@ class CloneProject extends Component
|
|||||||
$this->environment = $this->project->environments->where('name', $this->environment_name)->first();
|
$this->environment = $this->project->environments->where('name', $this->environment_name)->first();
|
||||||
$this->project_id = $this->project->id;
|
$this->project_id = $this->project->id;
|
||||||
$this->servers = currentTeam()->servers;
|
$this->servers = currentTeam()->servers;
|
||||||
$this->newProjectName = $this->project->name . ' (clone)';
|
$this->newProjectName = str($this->project->name . '-clone-' . (string)new Cuid2(7))->slug();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.project.clone-project');
|
return view('livewire.project.clone-me');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function selectServer($server_id)
|
public function selectServer($server_id, $destination_id)
|
||||||
{
|
{
|
||||||
$this->selectedServer = $server_id;
|
$this->selectedServer = $server_id;
|
||||||
|
$this->selectedDestination = $destination_id;
|
||||||
$this->server = $this->servers->where('id', $server_id)->first();
|
$this->server = $this->servers->where('id', $server_id)->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +55,7 @@ class CloneProject extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'selectedServer' => 'required',
|
'selectedDestination' => 'required',
|
||||||
'newProjectName' => 'required',
|
'newProjectName' => 'required',
|
||||||
]);
|
]);
|
||||||
$foundProject = Project::where('name', $this->newProjectName)->first();
|
$foundProject = Project::where('name', $this->newProjectName)->first();
|
||||||
@@ -81,7 +84,8 @@ class CloneProject extends Component
|
|||||||
'fqdn' => generateFqdn($this->server, $uuid),
|
'fqdn' => generateFqdn($this->server, $uuid),
|
||||||
'status' => 'exited',
|
'status' => 'exited',
|
||||||
'environment_id' => $newEnvironment->id,
|
'environment_id' => $newEnvironment->id,
|
||||||
'destination_id' => $this->selectedServer,
|
// This is not correct, but we need to set it to something
|
||||||
|
'destination_id' => $this->selectedDestination,
|
||||||
]);
|
]);
|
||||||
$newApplication->save();
|
$newApplication->save();
|
||||||
$environmentVaribles = $application->environment_variables()->get();
|
$environmentVaribles = $application->environment_variables()->get();
|
||||||
@@ -107,7 +111,7 @@ class CloneProject extends Component
|
|||||||
'status' => 'exited',
|
'status' => 'exited',
|
||||||
'started_at' => null,
|
'started_at' => null,
|
||||||
'environment_id' => $newEnvironment->id,
|
'environment_id' => $newEnvironment->id,
|
||||||
'destination_id' => $this->selectedServer,
|
'destination_id' => $this->selectedDestination,
|
||||||
]);
|
]);
|
||||||
$newDatabase->save();
|
$newDatabase->save();
|
||||||
$environmentVaribles = $database->environment_variables()->get();
|
$environmentVaribles = $database->environment_variables()->get();
|
||||||
@@ -133,7 +137,7 @@ class CloneProject extends Component
|
|||||||
$newService = $service->replicate()->fill([
|
$newService = $service->replicate()->fill([
|
||||||
'uuid' => $uuid,
|
'uuid' => $uuid,
|
||||||
'environment_id' => $newEnvironment->id,
|
'environment_id' => $newEnvironment->id,
|
||||||
'destination_id' => $this->selectedServer,
|
'destination_id' => $this->selectedDestination,
|
||||||
]);
|
]);
|
||||||
$newService->save();
|
$newService->save();
|
||||||
foreach ($newService->applications() as $application) {
|
foreach ($newService->applications() as $application) {
|
||||||
@@ -148,10 +152,10 @@ class CloneProject extends Component
|
|||||||
}
|
}
|
||||||
$newService->parse();
|
$newService->parse();
|
||||||
}
|
}
|
||||||
return $this->redirectRoute('project.resources', [
|
return redirect()->route('project.resource.index', [
|
||||||
'project_uuid' => $newProject->uuid,
|
'project_uuid' => $newProject->uuid,
|
||||||
'environment_name' => $newEnvironment->name,
|
'environment_name' => $newEnvironment->name,
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
41
app/Livewire/Project/Database/Backup/Execution.php
Normal file
41
app/Livewire/Project/Database/Backup/Execution.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Database\Backup;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Execution extends Component
|
||||||
|
{
|
||||||
|
public $database;
|
||||||
|
public $backup;
|
||||||
|
public $executions;
|
||||||
|
public $s3s;
|
||||||
|
public function mount() {
|
||||||
|
$backup_uuid = request()->route('backup_uuid');
|
||||||
|
$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');
|
||||||
|
}
|
||||||
|
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
||||||
|
if (!$database) {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
$backup = $database->scheduledBackups->where('uuid', $backup_uuid)->first();
|
||||||
|
if (!$backup) {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
$executions = collect($backup->executions)->sortByDesc('created_at');
|
||||||
|
$this->database = $database;
|
||||||
|
$this->backup = $backup;
|
||||||
|
$this->executions = $executions;
|
||||||
|
$this->s3s = currentTeam()->s3s;
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.database.backup.execution');
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Livewire/Project/Database/Backup/Index.php
Normal file
39
app/Livewire/Project/Database/Backup/Index.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Database\Backup;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Index extends Component
|
||||||
|
{
|
||||||
|
public $database;
|
||||||
|
public $s3s;
|
||||||
|
public function mount() {
|
||||||
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
||||||
|
if (!$database) {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
// No backups for redis
|
||||||
|
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
|
||||||
|
return redirect()->route('project.database.configuration', [
|
||||||
|
'project_uuid' => $project->uuid,
|
||||||
|
'environment_name' => $environment->name,
|
||||||
|
'database_uuid' => $database->uuid,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->database = $database;
|
||||||
|
$this->s3s = currentTeam()->s3s;
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.database.backup.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,9 +50,9 @@ class BackupEdit extends Component
|
|||||||
$url = $url->withoutQueryParameter('selectedBackupId');
|
$url = $url->withoutQueryParameter('selectedBackupId');
|
||||||
$url = $url->withFragment('backups');
|
$url = $url->withFragment('backups');
|
||||||
$url = $url->getPath() . "#{$url->getFragment()}";
|
$url = $url->getPath() . "#{$url->getFragment()}";
|
||||||
return $this->redirect($url,navigate: true);
|
return redirect($url);
|
||||||
} else {
|
} else {
|
||||||
return $this->redirectRoute('project.database.backups.all', $this->parameters);
|
return redirect()->route('project.database.backup.index', $this->parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use Livewire\Component;
|
|||||||
class BackupExecutions extends Component
|
class BackupExecutions extends Component
|
||||||
{
|
{
|
||||||
public $backup;
|
public $backup;
|
||||||
public $executions;
|
public $executions = [];
|
||||||
public $setDeletableBackup;
|
public $setDeletableBackup;
|
||||||
public function getListeners()
|
public function getListeners()
|
||||||
{
|
{
|
||||||
@@ -65,6 +65,6 @@ class BackupExecutions extends Component
|
|||||||
}
|
}
|
||||||
public function refreshBackupExecutions(): void
|
public function refreshBackupExecutions(): void
|
||||||
{
|
{
|
||||||
$this->executions = $this->backup->executions;
|
$this->executions = data_get($this->backup, 'executions', []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
app/Livewire/Project/Database/Configuration.php
Normal file
29
app/Livewire/Project/Database/Configuration.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Configuration extends Component
|
||||||
|
{
|
||||||
|
public $database;
|
||||||
|
public function mount() {
|
||||||
|
$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');
|
||||||
|
}
|
||||||
|
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
||||||
|
if (!$database) {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
$this->database = $database;
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.database.configuration');
|
||||||
|
}
|
||||||
|
}
|
||||||
139
app/Livewire/Project/Database/Import.php
Normal file
139
app/Livewire/Project/Database/Import.php
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Database;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\WithFileUploads;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\StandaloneMariadb;
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use App\Models\StandaloneMysql;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
class Import extends Component
|
||||||
|
{
|
||||||
|
use WithFileUploads;
|
||||||
|
|
||||||
|
public $file;
|
||||||
|
public $resource;
|
||||||
|
public $parameters;
|
||||||
|
public $containers;
|
||||||
|
public bool $validated = true;
|
||||||
|
public bool $scpInProgress = false;
|
||||||
|
public bool $importRunning = false;
|
||||||
|
public string $validationMsg = '';
|
||||||
|
public Server $server;
|
||||||
|
public string $container;
|
||||||
|
public array $importCommands = [];
|
||||||
|
public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB';
|
||||||
|
public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p $MYSQL_PASSWORD $MYSQL_DATABASE';
|
||||||
|
public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p $MARIADB_PASSWORD $MARIADB_DATABASE';
|
||||||
|
|
||||||
|
public function getListeners()
|
||||||
|
{
|
||||||
|
$userId = auth()->user()->id;
|
||||||
|
return [
|
||||||
|
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->getContainers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContainers()
|
||||||
|
{
|
||||||
|
$this->containers = collect();
|
||||||
|
if (!data_get($this->parameters, 'database_uuid')) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
|
if (is_null($resource)) {
|
||||||
|
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
|
if (is_null($resource)) {
|
||||||
|
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
|
if (is_null($resource)) {
|
||||||
|
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
|
if (is_null($resource)) {
|
||||||
|
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
|
if (is_null($resource)) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->resource = $resource;
|
||||||
|
$this->server = $this->resource->destination->server;
|
||||||
|
$this->container = $this->resource->uuid;
|
||||||
|
if (str(data_get($this, 'resource.status'))->startsWith('running')) {
|
||||||
|
$this->containers->push($this->container);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->containers->count() > 1) {
|
||||||
|
$this->validated = false;
|
||||||
|
$this->validationMsg = 'The database service has more than one container running. Cannot import.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$this->resource->getMorphClass() == 'App\Models\StandaloneRedis'
|
||||||
|
|| $this->resource->getMorphClass() == 'App\Models\StandaloneMongodb'
|
||||||
|
) {
|
||||||
|
$this->validated = false;
|
||||||
|
$this->validationMsg = 'This database type is not currently supported.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runImport()
|
||||||
|
{
|
||||||
|
$this->validate([
|
||||||
|
'file' => 'required|file|max:102400'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->importRunning = true;
|
||||||
|
$this->scpInProgress = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$uploadedFilename = $this->file->store('backup-import');
|
||||||
|
$path = Storage::path($uploadedFilename);
|
||||||
|
$tmpPath = '/tmp/' . basename($uploadedFilename);
|
||||||
|
|
||||||
|
// SCP the backup file to the server.
|
||||||
|
instant_scp($path, $tmpPath, $this->server);
|
||||||
|
$this->scpInProgress = false;
|
||||||
|
|
||||||
|
$this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}";
|
||||||
|
|
||||||
|
switch ($this->resource->getMorphClass()) {
|
||||||
|
case 'App\Models\StandaloneMariadb':
|
||||||
|
$this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mariadbRestoreCommand} < {$tmpPath}'";
|
||||||
|
$this->importCommands[] = "rm {$tmpPath}";
|
||||||
|
break;
|
||||||
|
case 'App\Models\StandaloneMysql':
|
||||||
|
$this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mysqlRestoreCommand} < {$tmpPath}'";
|
||||||
|
$this->importCommands[] = "rm {$tmpPath}";
|
||||||
|
break;
|
||||||
|
case 'App\Models\StandalonePostgresql':
|
||||||
|
$this->importCommands[] = "docker exec {$this->container} sh -c '{$this->postgresqlRestoreCommand} {$tmpPath}'";
|
||||||
|
$this->importCommands[] = "rm {$tmpPath}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->importCommands[] = "docker exec {$this->container} sh -c 'rm {$tmpPath}'";
|
||||||
|
$this->importCommands[] = "docker exec {$this->container} sh -c 'echo \"Import finished with exit code $?\"'";
|
||||||
|
|
||||||
|
if (!empty($this->importCommands)) {
|
||||||
|
$activity = remote_process($this->importCommands, $this->server, ignore_errors: true);
|
||||||
|
$this->dispatch('newMonitorActivity', $activity->id);
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->validated = false;
|
||||||
|
$this->validationMsg = $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ class DeleteEnvironment extends Component
|
|||||||
$environment = Environment::findOrFail($this->environment_id);
|
$environment = Environment::findOrFail($this->environment_id);
|
||||||
if ($environment->isEmpty()) {
|
if ($environment->isEmpty()) {
|
||||||
$environment->delete();
|
$environment->delete();
|
||||||
return $this->redirectRoute('project.show', ['project_uuid' => $this->parameters['project_uuid']], navigate: true);
|
return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
|
||||||
}
|
}
|
||||||
return $this->dispatch('error', 'Environment has defined resources, please delete them first.');
|
return $this->dispatch('error', 'Environment has defined resources, please delete them first.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,6 @@ class DeleteProject extends Component
|
|||||||
return $this->dispatch('error', 'Project has resources defined, please delete them first.');
|
return $this->dispatch('error', 'Project has resources defined, please delete them first.');
|
||||||
}
|
}
|
||||||
$project->delete();
|
$project->delete();
|
||||||
return $this->redirectRoute('projects', navigate: true);
|
return redirect()->route('project.index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,32 @@ class Edit extends Component
|
|||||||
'project.name' => 'required|min:3|max:255',
|
'project.name' => 'required|min:3|max:255',
|
||||||
'project.description' => 'nullable|string|max:255',
|
'project.description' => 'nullable|string|max:255',
|
||||||
];
|
];
|
||||||
|
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||||
|
|
||||||
|
public function saveKey($data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->project->environment_variables()->create([
|
||||||
|
'key' => $data['key'],
|
||||||
|
'value' => $data['value'],
|
||||||
|
'type' => 'project',
|
||||||
|
'team_id' => currentTeam()->id,
|
||||||
|
]);
|
||||||
|
$this->project->refresh();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$projectUuid = request()->route('project_uuid');
|
||||||
|
$teamId = currentTeam()->id;
|
||||||
|
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
|
||||||
|
if (!$project) {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
$this->project = $project;
|
||||||
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
|
|||||||
57
app/Livewire/Project/EnvironmentEdit.php
Normal file
57
app/Livewire/Project/EnvironmentEdit.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Project;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class EnvironmentEdit extends Component
|
||||||
|
{
|
||||||
|
public Project $project;
|
||||||
|
public Application $application;
|
||||||
|
public $environment;
|
||||||
|
public array $parameters;
|
||||||
|
protected $rules = [
|
||||||
|
'environment.name' => 'required|min:3|max:255',
|
||||||
|
'environment.description' => 'nullable|min:3|max:255',
|
||||||
|
];
|
||||||
|
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||||
|
|
||||||
|
public function saveKey($data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->environment->environment_variables()->create([
|
||||||
|
'key' => $data['key'],
|
||||||
|
'value' => $data['value'],
|
||||||
|
'type' => 'environment',
|
||||||
|
'team_id' => currentTeam()->id,
|
||||||
|
]);
|
||||||
|
$this->environment->refresh();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->first();
|
||||||
|
$this->environment = $this->project->environments()->where('name', request()->route('environment_name'))->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
try {
|
||||||
|
$this->environment->save();
|
||||||
|
return redirect()->route('project.environment.edit', ['project_uuid' => $this->project->uuid, 'environment_name' => $this->environment->name]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.environment-edit');
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Livewire/Project/Index.php
Normal file
21
app/Livewire/Project/Index.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project;
|
||||||
|
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Index extends Component
|
||||||
|
{
|
||||||
|
public $projects;
|
||||||
|
public $servers;
|
||||||
|
public function mount() {
|
||||||
|
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||||
|
$this->servers = Server::ownedByCurrentTeam()->count();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,7 +69,7 @@ class DockerCompose extends Component
|
|||||||
|
|
||||||
$service->parse(isNew: true);
|
$service->parse(isNew: true);
|
||||||
|
|
||||||
return $this->redirectRoute('project.service.configuration', [
|
return redirect()->route('project.service.configuration', [
|
||||||
'service_uuid' => $service->uuid,
|
'service_uuid' => $service->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
|
|||||||
@@ -64,11 +64,11 @@ class DockerImage extends Component
|
|||||||
'name' => 'docker-image-' . $application->uuid,
|
'name' => 'docker-image-' . $application->uuid,
|
||||||
'fqdn' => $fqdn
|
'fqdn' => $fqdn
|
||||||
]);
|
]);
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ class EmptyProject extends Component
|
|||||||
'name' => generate_random_name(),
|
'name' => generate_random_name(),
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
return $this->redirectRoute('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production'], navigate: true);
|
return redirect()->route('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ class GithubPrivateRepository extends Component
|
|||||||
public bool $is_static = false;
|
public bool $is_static = false;
|
||||||
public string|null $publish_directory = null;
|
public string|null $publish_directory = null;
|
||||||
protected int $page = 1;
|
protected int $page = 1;
|
||||||
|
public $build_pack = 'nixpacks';
|
||||||
|
public bool $show_is_static = true;
|
||||||
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -49,6 +51,20 @@ class GithubPrivateRepository extends Component
|
|||||||
$this->repositories = $this->branches = collect();
|
$this->repositories = $this->branches = collect();
|
||||||
$this->github_apps = GithubApp::private();
|
$this->github_apps = GithubApp::private();
|
||||||
}
|
}
|
||||||
|
public function updatedBuildPack()
|
||||||
|
{
|
||||||
|
if ($this->build_pack === 'nixpacks') {
|
||||||
|
$this->show_is_static = true;
|
||||||
|
$this->port = 3000;
|
||||||
|
} else if ($this->build_pack === 'static') {
|
||||||
|
$this->show_is_static = false;
|
||||||
|
$this->is_static = false;
|
||||||
|
$this->port = 80;
|
||||||
|
} else {
|
||||||
|
$this->show_is_static = false;
|
||||||
|
$this->is_static = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public function loadRepositories($github_app_id)
|
public function loadRepositories($github_app_id)
|
||||||
{
|
{
|
||||||
$this->repositories = collect();
|
$this->repositories = collect();
|
||||||
@@ -95,7 +111,7 @@ class GithubPrivateRepository extends Component
|
|||||||
$this->loadBranchByPage();
|
$this->loadBranchByPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->selected_branch_name = data_get($this->branches,'0.name');
|
$this->selected_branch_name = data_get($this->branches, '0.name', 'main');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function loadBranchByPage()
|
protected function loadBranchByPage()
|
||||||
@@ -151,11 +167,11 @@ class GithubPrivateRepository extends Component
|
|||||||
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
|
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,17 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
|
|
||||||
public string $repository_url;
|
public string $repository_url;
|
||||||
public string $branch;
|
public string $branch;
|
||||||
|
|
||||||
|
public $build_pack = 'nixpacks';
|
||||||
|
public bool $show_is_static = true;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'repository_url' => 'required',
|
'repository_url' => 'required',
|
||||||
'branch' => 'required|string',
|
'branch' => 'required|string',
|
||||||
'port' => 'required|numeric',
|
'port' => 'required|numeric',
|
||||||
'is_static' => 'required|boolean',
|
'is_static' => 'required|boolean',
|
||||||
'publish_directory' => 'nullable|string',
|
'publish_directory' => 'nullable|string',
|
||||||
|
'build_pack' => 'required|string',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'repository_url' => 'Repository',
|
'repository_url' => 'Repository',
|
||||||
@@ -42,6 +47,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
'port' => 'Port',
|
'port' => 'Port',
|
||||||
'is_static' => 'Is static',
|
'is_static' => 'Is static',
|
||||||
'publish_directory' => 'Publish directory',
|
'publish_directory' => 'Publish directory',
|
||||||
|
'build_pack' => 'Build pack',
|
||||||
];
|
];
|
||||||
private object $repository_url_parsed;
|
private object $repository_url_parsed;
|
||||||
private GithubApp|GitlabApp|string $git_source = 'other';
|
private GithubApp|GitlabApp|string $git_source = 'other';
|
||||||
@@ -55,9 +61,27 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
}
|
}
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
$this->query = request()->query();
|
$this->query = request()->query();
|
||||||
|
if (isDev()) {
|
||||||
|
$this->private_keys = PrivateKey::where('team_id', currentTeam()->id)->get();
|
||||||
|
} else {
|
||||||
$this->private_keys = PrivateKey::where('team_id', currentTeam()->id)->where('id', '!=', 0)->get();
|
$this->private_keys = PrivateKey::where('team_id', currentTeam()->id)->where('id', '!=', 0)->get();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedBuildPack()
|
||||||
|
{
|
||||||
|
if ($this->build_pack === 'nixpacks') {
|
||||||
|
$this->show_is_static = true;
|
||||||
|
$this->port = 3000;
|
||||||
|
} else if ($this->build_pack === 'static') {
|
||||||
|
$this->show_is_static = false;
|
||||||
|
$this->is_static = false;
|
||||||
|
$this->port = 80;
|
||||||
|
} else {
|
||||||
|
$this->show_is_static = false;
|
||||||
|
$this->is_static = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
if ($this->is_static) {
|
if ($this->is_static) {
|
||||||
@@ -132,11 +156,11 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
$application->name = generate_random_name($application->uuid);
|
$application->name = generate_random_name($application->uuid);
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,18 +30,22 @@ class PublicGitRepository extends Component
|
|||||||
public GithubApp|GitlabApp|string $git_source = 'other';
|
public GithubApp|GitlabApp|string $git_source = 'other';
|
||||||
public string $git_host;
|
public string $git_host;
|
||||||
public string $git_repository;
|
public string $git_repository;
|
||||||
|
public $build_pack = 'nixpacks';
|
||||||
|
public bool $show_is_static = true;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'repository_url' => 'required|url',
|
'repository_url' => 'required|url',
|
||||||
'port' => 'required|numeric',
|
'port' => 'required|numeric',
|
||||||
'is_static' => 'required|boolean',
|
'is_static' => 'required|boolean',
|
||||||
'publish_directory' => 'nullable|string',
|
'publish_directory' => 'nullable|string',
|
||||||
|
'build_pack' => 'required|string',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'repository_url' => 'repository',
|
'repository_url' => 'repository',
|
||||||
'port' => 'port',
|
'port' => 'port',
|
||||||
'is_static' => 'static',
|
'is_static' => 'static',
|
||||||
'publish_directory' => 'publish directory',
|
'publish_directory' => 'publish directory',
|
||||||
|
'build_pack' => 'build pack',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -53,7 +57,20 @@ class PublicGitRepository extends Component
|
|||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
$this->query = request()->query();
|
$this->query = request()->query();
|
||||||
}
|
}
|
||||||
|
public function updatedBuildPack()
|
||||||
|
{
|
||||||
|
if ($this->build_pack === 'nixpacks') {
|
||||||
|
$this->show_is_static = true;
|
||||||
|
$this->port = 3000;
|
||||||
|
} else if ($this->build_pack === 'static') {
|
||||||
|
$this->show_is_static = false;
|
||||||
|
$this->is_static = false;
|
||||||
|
$this->port = 80;
|
||||||
|
} else {
|
||||||
|
$this->show_is_static = false;
|
||||||
|
$this->is_static = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
if ($this->is_static) {
|
if ($this->is_static) {
|
||||||
@@ -157,6 +174,7 @@ class PublicGitRepository extends Component
|
|||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'destination_id' => $destination->id,
|
'destination_id' => $destination->id,
|
||||||
'destination_type' => $destination_class,
|
'destination_type' => $destination_class,
|
||||||
|
'build_pack' => $this->build_pack,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$application_init = [
|
$application_init = [
|
||||||
@@ -170,7 +188,8 @@ class PublicGitRepository extends Component
|
|||||||
'destination_id' => $destination->id,
|
'destination_id' => $destination->id,
|
||||||
'destination_type' => $destination_class,
|
'destination_type' => $destination_class,
|
||||||
'source_id' => $this->git_source->id,
|
'source_id' => $this->git_source->id,
|
||||||
'source_type' => $this->git_source->getMorphClass()
|
'source_type' => $this->git_source->getMorphClass(),
|
||||||
|
'build_pack' => $this->build_pack,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,11 +203,11 @@ class PublicGitRepository extends Component
|
|||||||
$application->fqdn = $fqdn;
|
$application->fqdn = $fqdn;
|
||||||
$application->save();
|
$application->save();
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,24 @@ use App\Models\Project;
|
|||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Countable;
|
use Countable;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Select extends Component
|
class Select extends Component
|
||||||
{
|
{
|
||||||
public $current_step = 'type';
|
public $current_step = 'type';
|
||||||
public ?int $server = null;
|
public ?Server $server = null;
|
||||||
public string $type;
|
public string $type;
|
||||||
public string $server_id;
|
public string $server_id;
|
||||||
public string $destination_uuid;
|
public string $destination_uuid;
|
||||||
|
public Countable|array|Server $allServers = [];
|
||||||
public Countable|array|Server $servers = [];
|
public Countable|array|Server $servers = [];
|
||||||
public Collection|array $standaloneDockers = [];
|
public Collection|array $standaloneDockers = [];
|
||||||
public Collection|array $swarmDockers = [];
|
public Collection|array $swarmDockers = [];
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public Collection|array $services = [];
|
public Collection|array $services = [];
|
||||||
public Collection|array $allServices = [];
|
public Collection|array $allServices = [];
|
||||||
|
public bool $isDatabase = false;
|
||||||
|
public bool $includeSwarm = true;
|
||||||
|
|
||||||
public bool $loadingServices = true;
|
public bool $loadingServices = true;
|
||||||
public bool $loading = false;
|
public bool $loading = false;
|
||||||
@@ -31,7 +33,7 @@ class Select extends Component
|
|||||||
|
|
||||||
public ?string $search = null;
|
public ?string $search = null;
|
||||||
protected $queryString = [
|
protected $queryString = [
|
||||||
'server',
|
'server_id',
|
||||||
'search'
|
'search'
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -53,10 +55,10 @@ class Select extends Component
|
|||||||
|
|
||||||
public function updatedSelectedEnvironment()
|
public function updatedSelectedEnvironment()
|
||||||
{
|
{
|
||||||
return $this->redirectRoute('project.resources.new', [
|
return redirect()->route('project.resource.create', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'environment_name' => $this->selectedEnvironment,
|
'environment_name' => $this->selectedEnvironment,
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public function addExistingPostgresql()
|
// public function addExistingPostgresql()
|
||||||
@@ -97,21 +99,45 @@ class Select extends Component
|
|||||||
$this->loadingServices = false;
|
$this->loadingServices = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
if ($this->includeSwarm) {
|
||||||
|
$this->servers = $this->allServers;
|
||||||
|
} else {
|
||||||
|
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
public function setType(string $type)
|
public function setType(string $type)
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
|
||||||
if ($this->loading) return;
|
if ($this->loading) return;
|
||||||
$this->loading = true;
|
$this->loading = true;
|
||||||
|
$this->type = $type;
|
||||||
|
switch ($type) {
|
||||||
|
case 'postgresql':
|
||||||
|
case 'mysql':
|
||||||
|
case 'mariadb':
|
||||||
|
case 'redis':
|
||||||
|
case 'mongodb':
|
||||||
|
$this->isDatabase = true;
|
||||||
|
$this->includeSwarm = false;
|
||||||
|
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (str($type)->startsWith('one-click-service') || str($type)->startsWith('docker-compose-empty')) {
|
||||||
|
$this->isDatabase = true;
|
||||||
|
$this->includeSwarm = false;
|
||||||
|
$this->servers = $this->allServers->where('settings.is_swarm_worker', false)->where('settings.is_swarm_manager', false)->where('settings.is_build_server', false);
|
||||||
|
}
|
||||||
if ($type === "existing-postgresql") {
|
if ($type === "existing-postgresql") {
|
||||||
$this->current_step = $type;
|
$this->current_step = $type;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (count($this->servers) === 1) {
|
// if (count($this->servers) === 1) {
|
||||||
$server = $this->servers->first();
|
// $server = $this->servers->first();
|
||||||
$this->setServer($server);
|
// $this->setServer($server);
|
||||||
}
|
// }
|
||||||
if (!is_null($this->server)) {
|
if (!is_null($this->server)) {
|
||||||
$foundServer = $this->servers->where('id', $this->server)->first();
|
$foundServer = $this->servers->where('id', $this->server->id)->first();
|
||||||
if ($foundServer) {
|
if ($foundServer) {
|
||||||
return $this->setServer($foundServer);
|
return $this->setServer($foundServer);
|
||||||
}
|
}
|
||||||
@@ -122,6 +148,7 @@ class Select extends Component
|
|||||||
public function setServer(Server $server)
|
public function setServer(Server $server)
|
||||||
{
|
{
|
||||||
$this->server_id = $server->id;
|
$this->server_id = $server->id;
|
||||||
|
$this->server = $server;
|
||||||
$this->standaloneDockers = $server->standaloneDockers;
|
$this->standaloneDockers = $server->standaloneDockers;
|
||||||
$this->swarmDockers = $server->swarmDockers;
|
$this->swarmDockers = $server->swarmDockers;
|
||||||
$this->current_step = 'destinations';
|
$this->current_step = 'destinations';
|
||||||
@@ -130,7 +157,7 @@ class Select extends Component
|
|||||||
public function setDestination(string $destination_uuid)
|
public function setDestination(string $destination_uuid)
|
||||||
{
|
{
|
||||||
$this->destination_uuid = $destination_uuid;
|
$this->destination_uuid = $destination_uuid;
|
||||||
return redirect()->route('project.resources.new', [
|
return redirect()->route('project.resource.create', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
'type' => $this->type,
|
'type' => $this->type,
|
||||||
@@ -142,5 +169,6 @@ class Select extends Component
|
|||||||
public function loadServers()
|
public function loadServers()
|
||||||
{
|
{
|
||||||
$this->servers = Server::isUsable()->get();
|
$this->servers = Server::isUsable()->get();
|
||||||
|
$this->allServers = $this->servers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,10 +70,10 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
'fqdn' => $fqdn
|
'fqdn' => $fqdn
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->redirectRoute('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
], navigate: true);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Livewire\Project\Resource;
|
||||||
|
|
||||||
use App\Models\EnvironmentVariable;
|
use App\Models\EnvironmentVariable;
|
||||||
use App\Models\Project;
|
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use Illuminate\Support\Str;
|
use Livewire\Component;
|
||||||
|
|
||||||
class ProjectController extends Controller
|
class Create extends Component
|
||||||
{
|
{
|
||||||
public function all()
|
public $type;
|
||||||
{
|
public function mount() {
|
||||||
return view('projects', [
|
|
||||||
'projects' => Project::ownedByCurrentTeam()->get(),
|
|
||||||
'servers' => Server::ownedByCurrentTeam()->count(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function edit()
|
|
||||||
{
|
|
||||||
$projectUuid = request()->route('project_uuid');
|
|
||||||
$teamId = currentTeam()->id;
|
|
||||||
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
|
|
||||||
if (!$project) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
return view('project.edit', ['project' => $project]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function show()
|
|
||||||
{
|
|
||||||
$projectUuid = request()->route('project_uuid');
|
|
||||||
$teamId = currentTeam()->id;
|
|
||||||
|
|
||||||
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
|
|
||||||
if (!$project) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$project->load(['environments']);
|
|
||||||
return view('project.show', ['project' => $project]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function new()
|
|
||||||
{
|
|
||||||
$services = getServiceTemplates();
|
$services = getServiceTemplates();
|
||||||
$type = Str::of(request()->query('type'));
|
$type = str(request()->query('type'));
|
||||||
$destination_uuid = request()->query('destination');
|
$destination_uuid = request()->query('destination');
|
||||||
$server_id = request()->query('server_id');
|
$server_id = request()->query('server_id');
|
||||||
|
|
||||||
@@ -81,14 +47,14 @@ class ProjectController extends Controller
|
|||||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||||
if ($oneClickDotEnvs) {
|
if ($oneClickDotEnvs) {
|
||||||
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
|
$oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
|
||||||
return !empty($value);
|
return !empty($value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($oneClickService) {
|
if ($oneClickService) {
|
||||||
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||||
$service = Service::create([
|
$service = Service::create([
|
||||||
'name' => "$oneClickServiceName-" . Str::random(10),
|
'name' => "$oneClickServiceName-" . str()->random(10),
|
||||||
'docker_compose_raw' => base64_decode($oneClickService),
|
'docker_compose_raw' => base64_decode($oneClickService),
|
||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'server_id' => (int) $server_id,
|
'server_id' => (int) $server_id,
|
||||||
@@ -99,8 +65,8 @@ class ProjectController extends Controller
|
|||||||
$service->save();
|
$service->save();
|
||||||
if ($oneClickDotEnvs?->count() > 0) {
|
if ($oneClickDotEnvs?->count() > 0) {
|
||||||
$oneClickDotEnvs->each(function ($value) use ($service) {
|
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||||
$key = Str::before($value, '=');
|
$key = str()->before($value, '=');
|
||||||
$value = Str::of(Str::after($value, '='));
|
$value = str(str()->after($value, '='));
|
||||||
$generatedValue = $value;
|
$generatedValue = $value;
|
||||||
if ($value->contains('SERVICE_')) {
|
if ($value->contains('SERVICE_')) {
|
||||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
@@ -123,24 +89,10 @@ class ProjectController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return view('project.new', [
|
$this->type = $type->value();
|
||||||
'type' => $type->value()
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
public function render()
|
||||||
public function resources()
|
|
||||||
{
|
{
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
return view('livewire.project.resource.create');
|
||||||
if (!$project) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first();
|
|
||||||
if (!$environment) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
return view('project.resources', [
|
|
||||||
'project' => $project,
|
|
||||||
'environment' => $environment
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
115
app/Livewire/Project/Resource/Index.php
Normal file
115
app/Livewire/Project/Resource/Index.php
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Resource;
|
||||||
|
|
||||||
|
use App\Models\Environment;
|
||||||
|
use App\Models\Project;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Index extends Component
|
||||||
|
{
|
||||||
|
public Project $project;
|
||||||
|
public Environment $environment;
|
||||||
|
public $applications = [];
|
||||||
|
public $postgresqls = [];
|
||||||
|
public $redis = [];
|
||||||
|
public $mongodbs = [];
|
||||||
|
public $mysqls = [];
|
||||||
|
public $mariadbs = [];
|
||||||
|
public $services = [];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$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();
|
||||||
|
if (!$environment) {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
$this->project = $project;
|
||||||
|
$this->environment = $environment;
|
||||||
|
$this->applications = $environment->applications->sortBy('name');
|
||||||
|
$this->applications = $this->applications->map(function ($application) {
|
||||||
|
if (data_get($application, 'environment.project.uuid')) {
|
||||||
|
$application->hrefLink = route('project.application.configuration', [
|
||||||
|
'project_uuid' => data_get($application, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($application, 'environment.name'),
|
||||||
|
'application_uuid' => data_get($application, 'uuid')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $application;
|
||||||
|
});
|
||||||
|
$this->postgresqls = $environment->postgresqls->sortBy('name');
|
||||||
|
$this->postgresqls = $this->postgresqls->map(function ($postgresql) {
|
||||||
|
if (data_get($postgresql, 'environment.project.uuid')) {
|
||||||
|
$postgresql->hrefLink = route('project.database.configuration', [
|
||||||
|
'project_uuid' => data_get($postgresql, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($postgresql, 'environment.name'),
|
||||||
|
'database_uuid' => data_get($postgresql, 'uuid')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $postgresql;
|
||||||
|
});
|
||||||
|
$this->redis = $environment->redis->sortBy('name');
|
||||||
|
$this->redis = $this->redis->map(function ($redis) {
|
||||||
|
if (data_get($redis, 'environment.project.uuid')) {
|
||||||
|
$redis->hrefLink = route('project.database.configuration', [
|
||||||
|
'project_uuid' => data_get($redis, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($redis, 'environment.name'),
|
||||||
|
'database_uuid' => data_get($redis, 'uuid')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $redis;
|
||||||
|
});
|
||||||
|
$this->mongodbs = $environment->mongodbs->sortBy('name');
|
||||||
|
$this->mongodbs = $this->mongodbs->map(function ($mongodb) {
|
||||||
|
if (data_get($mongodb, 'environment.project.uuid')) {
|
||||||
|
$mongodb->hrefLink = route('project.database.configuration', [
|
||||||
|
'project_uuid' => data_get($mongodb, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($mongodb, 'environment.name'),
|
||||||
|
'database_uuid' => data_get($mongodb, 'uuid')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $mongodb;
|
||||||
|
});
|
||||||
|
$this->mysqls = $environment->mysqls->sortBy('name');
|
||||||
|
$this->mysqls = $this->mysqls->map(function ($mysql) {
|
||||||
|
if (data_get($mysql, 'environment.project.uuid')) {
|
||||||
|
$mysql->hrefLink = route('project.database.configuration', [
|
||||||
|
'project_uuid' => data_get($mysql, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($mysql, 'environment.name'),
|
||||||
|
'database_uuid' => data_get($mysql, 'uuid')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $mysql;
|
||||||
|
});
|
||||||
|
$this->mariadbs = $environment->mariadbs->sortBy('name');
|
||||||
|
$this->mariadbs = $this->mariadbs->map(function ($mariadb) {
|
||||||
|
if (data_get($mariadb, 'environment.project.uuid')) {
|
||||||
|
$mariadb->hrefLink = route('project.database.configuration', [
|
||||||
|
'project_uuid' => data_get($mariadb, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($mariadb, 'environment.name'),
|
||||||
|
'database_uuid' => data_get($mariadb, 'uuid')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $mariadb;
|
||||||
|
});
|
||||||
|
$this->services = $environment->services->sortBy('name');
|
||||||
|
$this->services = $this->services->map(function ($service) {
|
||||||
|
if (data_get($service, 'environment.project.uuid')) {
|
||||||
|
$service->hrefLink = route('project.service.configuration', [
|
||||||
|
'project_uuid' => data_get($service, 'environment.project.uuid'),
|
||||||
|
'environment_name' => data_get($service, 'environment.name'),
|
||||||
|
'service_uuid' => data_get($service, 'uuid')
|
||||||
|
]);
|
||||||
|
$service->status = serviceStatus($service);
|
||||||
|
}
|
||||||
|
return $service;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.resource.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ class Application extends Component
|
|||||||
try {
|
try {
|
||||||
$this->application->delete();
|
$this->application->delete();
|
||||||
$this->dispatch('success', 'Application deleted successfully.');
|
$this->dispatch('success', 'Application deleted successfully.');
|
||||||
return $this->redirectRoute('project.service.configuration', $this->parameters, navigate: true);
|
return redirect()->route('project.service.configuration', $this->parameters);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
57
app/Livewire/Project/Service/Configuration.php
Normal file
57
app/Livewire/Project/Service/Configuration.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Configuration extends Component
|
||||||
|
{
|
||||||
|
public Service $service;
|
||||||
|
public $applications;
|
||||||
|
public $databases;
|
||||||
|
public array $parameters;
|
||||||
|
public array $query;
|
||||||
|
public function getListeners()
|
||||||
|
{
|
||||||
|
$userId = auth()->user()->id;
|
||||||
|
return [
|
||||||
|
"echo-private:user.{$userId},ServiceStatusChanged" => 'checkStatus',
|
||||||
|
"refreshStacks",
|
||||||
|
"checkStatus",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.configuration');
|
||||||
|
}
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
$this->service = Service::whereUuid($this->parameters['service_uuid'])->first();
|
||||||
|
if (!$this->service) {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
$this->applications = $this->service->applications->sort();
|
||||||
|
$this->databases = $this->service->databases->sort();
|
||||||
|
}
|
||||||
|
public function checkStatus()
|
||||||
|
{
|
||||||
|
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||||
|
$this->refreshStacks();
|
||||||
|
$this->dispatch('serviceStatusChanged');
|
||||||
|
}
|
||||||
|
public function refreshStacks()
|
||||||
|
{
|
||||||
|
$this->applications = $this->service->applications->sort();
|
||||||
|
$this->applications->each(function ($application) {
|
||||||
|
$application->refresh();
|
||||||
|
});
|
||||||
|
$this->databases = $this->service->databases->sort();
|
||||||
|
$this->databases->each(function ($database) {
|
||||||
|
$database->refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,53 +2,51 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Service;
|
namespace App\Livewire\Project\Service;
|
||||||
|
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
public Service $service;
|
public Service $service;
|
||||||
public $applications;
|
public ?ServiceApplication $serviceApplication = null;
|
||||||
public $databases;
|
public ?ServiceDatabase $serviceDatabase = null;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public array $query;
|
public array $query;
|
||||||
public function getListeners()
|
public Collection $services;
|
||||||
|
public $s3s;
|
||||||
|
|
||||||
|
protected $listeners = ['generateDockerCompose'];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
{
|
{
|
||||||
$userId = auth()->user()->id;
|
try {
|
||||||
return [
|
$this->services = collect([]);
|
||||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'checkStatus',
|
$this->parameters = get_route_parameters();
|
||||||
"refreshStacks",
|
$this->query = request()->query();
|
||||||
"checkStatus",
|
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||||
];
|
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
|
||||||
|
if ($service) {
|
||||||
|
$this->serviceApplication = $service;
|
||||||
|
$this->serviceApplication->getFilesFromServer();
|
||||||
|
} else {
|
||||||
|
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
||||||
|
$this->serviceDatabase->getFilesFromServer();
|
||||||
|
}
|
||||||
|
$this->s3s = currentTeam()->s3s;
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public function generateDockerCompose()
|
||||||
|
{
|
||||||
|
$this->service->parse();
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.project.service.index');
|
return view('livewire.project.service.index');
|
||||||
}
|
}
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
$this->query = request()->query();
|
|
||||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
|
||||||
$this->applications = $this->service->applications->sort();
|
|
||||||
$this->databases = $this->service->databases->sort();
|
|
||||||
}
|
|
||||||
public function checkStatus()
|
|
||||||
{
|
|
||||||
dispatch_sync(new ContainerStatusJob($this->service->server));
|
|
||||||
$this->refreshStacks();
|
|
||||||
$this->dispatch('serviceStatusChanged');
|
|
||||||
}
|
|
||||||
public function refreshStacks()
|
|
||||||
{
|
|
||||||
$this->applications = $this->service->applications->sort();
|
|
||||||
$this->applications->each(function ($application) {
|
|
||||||
$application->refresh();
|
|
||||||
});
|
|
||||||
$this->databases = $this->service->databases->sort();
|
|
||||||
$this->databases->each(function ($database) {
|
|
||||||
$database->refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Service;
|
namespace App\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Actions\Shared\PullImage;
|
||||||
use App\Actions\Service\StartService;
|
use App\Actions\Service\StartService;
|
||||||
use App\Actions\Service\StopService;
|
use App\Actions\Service\StopService;
|
||||||
use App\Events\ServiceStatusChanged;
|
use App\Events\ServiceStatusChanged;
|
||||||
@@ -69,4 +70,18 @@ class Navbar extends Component
|
|||||||
}
|
}
|
||||||
ServiceStatusChanged::dispatch();
|
ServiceStatusChanged::dispatch();
|
||||||
}
|
}
|
||||||
|
public function restart()
|
||||||
|
{
|
||||||
|
$this->checkDeployments();
|
||||||
|
if ($this->isDeploymentProgress) {
|
||||||
|
$this->dispatch('error', 'There is a deployment in progress.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PullImage::run($this->service);
|
||||||
|
$this->dispatch('image-pulled');
|
||||||
|
StopService::run($this->service);
|
||||||
|
$this->service->parse();
|
||||||
|
$activity = StartService::run($this->service);
|
||||||
|
$this->dispatch('newMonitorActivity', $activity->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Livewire\Project\Service;
|
|
||||||
|
|
||||||
use App\Models\Service;
|
|
||||||
use App\Models\ServiceApplication;
|
|
||||||
use App\Models\ServiceDatabase;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Show extends Component
|
|
||||||
{
|
|
||||||
public Service $service;
|
|
||||||
public ?ServiceApplication $serviceApplication = null;
|
|
||||||
public ?ServiceDatabase $serviceDatabase = null;
|
|
||||||
public array $parameters;
|
|
||||||
public array $query;
|
|
||||||
public Collection $services;
|
|
||||||
public $s3s;
|
|
||||||
|
|
||||||
protected $listeners = ['generateDockerCompose'];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->services = collect([]);
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
$this->query = request()->query();
|
|
||||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
|
||||||
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
|
|
||||||
if ($service) {
|
|
||||||
$this->serviceApplication = $service;
|
|
||||||
$this->serviceApplication->getFilesFromServer();
|
|
||||||
} else {
|
|
||||||
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
|
||||||
$this->serviceDatabase->getFilesFromServer();
|
|
||||||
}
|
|
||||||
$this->s3s = currentTeam()->s3s;
|
|
||||||
} catch(\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
public function generateDockerCompose()
|
|
||||||
{
|
|
||||||
$this->service->parse();
|
|
||||||
}
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.show');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,7 @@ class StackForm extends Component
|
|||||||
'service.docker_compose' => 'required',
|
'service.docker_compose' => 'required',
|
||||||
'service.name' => 'required',
|
'service.name' => 'required',
|
||||||
'service.description' => 'nullable',
|
'service.description' => 'nullable',
|
||||||
|
'service.connect_to_docker_network' => 'nullable',
|
||||||
];
|
];
|
||||||
public $validationAttributes = [];
|
public $validationAttributes = [];
|
||||||
public function mount()
|
public function mount()
|
||||||
@@ -44,6 +45,9 @@ class StackForm extends Component
|
|||||||
$this->service->docker_compose_raw = $raw;
|
$this->service->docker_compose_raw = $raw;
|
||||||
$this->submit();
|
$this->submit();
|
||||||
}
|
}
|
||||||
|
public function instantSave() {
|
||||||
|
$this->service->save();
|
||||||
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ class Danger extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
DeleteResourceJob::dispatchSync($this->resource);
|
DeleteResourceJob::dispatchSync($this->resource);
|
||||||
return $this->redirectRoute('project.resources', [
|
return redirect()->route('project.resource.index', [
|
||||||
'project_uuid' => $this->projectUuid,
|
'project_uuid' => $this->projectUuid,
|
||||||
'environment_name' => $this->environmentName
|
'environment_name' => $this->environmentName
|
||||||
], navigate: true);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ class Destination extends Component
|
|||||||
{
|
{
|
||||||
public $resource;
|
public $resource;
|
||||||
public $servers = [];
|
public $servers = [];
|
||||||
public $additionalServers = [];
|
public $additional_servers = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ class Add extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
ray($this->key, $this->value, $this->is_build_time);
|
|
||||||
$this->dispatch('saveKey', [
|
$this->dispatch('saveKey', [
|
||||||
'key' => $this->key,
|
'key' => $this->key,
|
||||||
'value' => $this->value,
|
'value' => $this->value,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user