mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
955 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce3b2de5e7 | ||
|
|
1782f59a96 | ||
|
|
3612096b56 | ||
|
|
97aa6139ea | ||
|
|
e5d915a7a9 | ||
|
|
5bc31c305c | ||
|
|
8c7590a249 | ||
|
|
d54d524cef | ||
|
|
a3d3ada500 | ||
|
|
ca43d197f9 | ||
|
|
73692a0c73 | ||
|
|
50191221b9 | ||
|
|
61236b45fb | ||
|
|
837545f4e7 | ||
|
|
31810477b2 | ||
|
|
4c652b5818 | ||
|
|
5201818f52 | ||
|
|
7e9e333d24 | ||
|
|
ab3b72bd1f | ||
|
|
f2c8a6bac5 | ||
|
|
758fab9976 | ||
|
|
3c2b985769 | ||
|
|
73bc07c7fb | ||
|
|
8298aa7124 | ||
|
|
92049cba92 | ||
|
|
83d8963051 | ||
|
|
bedcf6c058 | ||
|
|
f8b7c17efd | ||
|
|
76ba365325 | ||
|
|
a3255f3ab0 | ||
|
|
44af75613f | ||
|
|
df53e8beda | ||
|
|
003f97af24 | ||
|
|
c897aaafa7 | ||
|
|
76a0659335 | ||
|
|
4acdd8df12 | ||
|
|
cdabdb4558 | ||
|
|
f43fc1e376 | ||
|
|
70cd5d364c | ||
|
|
62aa807d0f | ||
|
|
5f3fed3c8f | ||
|
|
f95e879a58 | ||
|
|
34508a2fd1 | ||
|
|
cd85094113 | ||
|
|
72033279c2 | ||
|
|
9919224226 | ||
|
|
c6a1eac586 | ||
|
|
b657aa33fa | ||
|
|
834562190e | ||
|
|
453b28baf7 | ||
|
|
28522418ff | ||
|
|
18bab41605 | ||
|
|
c17079e045 | ||
|
|
f5cea7d9e3 | ||
|
|
c5083ea897 | ||
|
|
f607aa1233 | ||
|
|
db39458295 | ||
|
|
3d75e0773f | ||
|
|
bdcb467208 | ||
|
|
575a789d1d | ||
|
|
fcb3d71cb4 | ||
|
|
93c890ce41 | ||
|
|
2421f7c35c | ||
|
|
44920944db | ||
|
|
1b135be3c5 | ||
|
|
fff7ec9ba7 | ||
|
|
0468f255e7 | ||
|
|
5da5158b14 | ||
|
|
6493ce3fe0 | ||
|
|
078772495a | ||
|
|
c81ad5cd03 | ||
|
|
439bee1203 | ||
|
|
0eccbf64f4 | ||
|
|
5232ce6e52 | ||
|
|
ada278d8da | ||
|
|
24d5fef20f | ||
|
|
b7fd1b9f4c | ||
|
|
b16cd1ec5c | ||
|
|
278ec88dd0 | ||
|
|
b91699f317 | ||
|
|
105ed56dfc | ||
|
|
f9267a96ed | ||
|
|
1135f45dc9 | ||
|
|
b74a75f4c6 | ||
|
|
8a787f48b3 | ||
|
|
e956642982 | ||
|
|
d3d08168de | ||
|
|
919f56a292 | ||
|
|
b5be17c2d2 | ||
|
|
5221aa0ab7 | ||
|
|
49ad8b7cc1 | ||
|
|
fa99cbce4a | ||
|
|
07e55b2636 | ||
|
|
d8984c42b5 | ||
|
|
36f251e710 | ||
|
|
f3beacdc3f | ||
|
|
e023be1f25 | ||
|
|
d1a5f97f59 | ||
|
|
a43de75b42 | ||
|
|
5530f7263f | ||
|
|
d688244664 | ||
|
|
c8155c8a32 | ||
|
|
ce15f8f1dd | ||
|
|
23ed697b98 | ||
|
|
55854653b0 | ||
|
|
83983bbb32 | ||
|
|
768c917a0e | ||
|
|
039df94b86 | ||
|
|
f46cb7a670 | ||
|
|
9d05276f94 | ||
|
|
dc4916dc19 | ||
|
|
1d0a1ab16a | ||
|
|
1ae6106782 | ||
|
|
af38d0cc07 | ||
|
|
5ec2cbdf19 | ||
|
|
f6f44d8e8f | ||
|
|
2b6fc16637 | ||
|
|
e0a2e3bd0c | ||
|
|
1ecd0307ed | ||
|
|
f10f3456d7 | ||
|
|
b17be37aee | ||
|
|
ddbef494e3 | ||
|
|
49d91c498e | ||
|
|
ef1d06b8dc | ||
|
|
f1562ccfd5 | ||
|
|
bfe2fb6da8 | ||
|
|
6f23f6352d | ||
|
|
a47816f45d | ||
|
|
30ac392af4 | ||
|
|
e4ee149085 | ||
|
|
a521d8549a | ||
|
|
566faba6e3 | ||
|
|
e4e9de0a53 | ||
|
|
07ae971ae1 | ||
|
|
4f5102b2dc | ||
|
|
04d1915121 | ||
|
|
c35f6e926d | ||
|
|
dffcee6cf1 | ||
|
|
7485c1240b | ||
|
|
95d3ebdc2d | ||
|
|
6bb565ee67 | ||
|
|
19b38074a5 | ||
|
|
25649f578d | ||
|
|
7a63a17b66 | ||
|
|
3424cb1f29 | ||
|
|
5c8277ea1d | ||
|
|
9e3ffea22c | ||
|
|
9592076d45 | ||
|
|
2e01665340 | ||
|
|
bba0ef522c | ||
|
|
019cdd2b3a | ||
|
|
ce24352974 | ||
|
|
407b8de2b1 | ||
|
|
f332a73122 | ||
|
|
2335abac91 | ||
|
|
5cf6804615 | ||
|
|
58b04b5fc8 | ||
|
|
c58f468dc9 | ||
|
|
e922bc207a | ||
|
|
784cfb8fba | ||
|
|
b53bb44e42 | ||
|
|
1876e80094 | ||
|
|
d20b3a5b8b | ||
|
|
6d9454b351 | ||
|
|
a138bb61bc | ||
|
|
85d313a791 | ||
|
|
de7380fb0c | ||
|
|
35bf4a111c | ||
|
|
d5be00d29d | ||
|
|
e63710dfb4 | ||
|
|
8b95b3c1bf | ||
|
|
b8ec3f5704 | ||
|
|
5dc79159b4 | ||
|
|
5368c49e68 | ||
|
|
8731d0416b | ||
|
|
d05151466c | ||
|
|
367a373904 | ||
|
|
533587ce4e | ||
|
|
db0ddee9f4 | ||
|
|
d5bad67e17 | ||
|
|
9e4c8c52d2 | ||
|
|
8ca84ee6f4 | ||
|
|
5ebbd769e8 | ||
|
|
47051127af | ||
|
|
7ad2e1ca05 | ||
|
|
7345ccbbee | ||
|
|
e282686f97 | ||
|
|
5524f80ea3 | ||
|
|
d86274cc37 | ||
|
|
41fb6a1fc9 | ||
|
|
8e9842df14 | ||
|
|
f4904047b5 | ||
|
|
4083f4db9f | ||
|
|
a125c0032b | ||
|
|
2e4fd1530c | ||
|
|
be08a2650c | ||
|
|
e08dc777df | ||
|
|
239f7fb35d | ||
|
|
1cb8354aca | ||
|
|
2465b4ffd7 | ||
|
|
4f73ea0879 | ||
|
|
f4eb17f616 | ||
|
|
b5ce738ba2 | ||
|
|
3155d4afdb | ||
|
|
f444696b84 | ||
|
|
bdde9b063c | ||
|
|
ef98bd107f | ||
|
|
dfb259d822 | ||
|
|
13c34fd26d | ||
|
|
665cd454ef | ||
|
|
b3bde5782a | ||
|
|
cc8f09f05e | ||
|
|
2beda08717 | ||
|
|
b455d153ae | ||
|
|
5fddf01820 | ||
|
|
c80434141d | ||
|
|
82e7348cf2 | ||
|
|
d1128c7a1e | ||
|
|
5674879e23 | ||
|
|
aae81313a6 | ||
|
|
4667f96b40 | ||
|
|
d6c2c7ef02 | ||
|
|
2af1ccd8b2 | ||
|
|
55e2e29696 | ||
|
|
b60f8df17a | ||
|
|
28c320ae97 | ||
|
|
00ed54799f | ||
|
|
fae77231a5 | ||
|
|
2083941361 | ||
|
|
5ec517e3bc | ||
|
|
780a3b1827 | ||
|
|
61aa086cb5 | ||
|
|
0a08f9d3f8 | ||
|
|
c3de13e0d8 | ||
|
|
45017efe00 | ||
|
|
a20290cac8 | ||
|
|
31e02a154c | ||
|
|
023ee5db99 | ||
|
|
05d2e15ab5 | ||
|
|
7d6590c60a | ||
|
|
3152ce183b | ||
|
|
d9f1a7c4d0 | ||
|
|
952aed3c49 | ||
|
|
ab3c433450 | ||
|
|
2b5e4a34d4 | ||
|
|
35cea852ca | ||
|
|
88581c8983 | ||
|
|
a7a9aab189 | ||
|
|
370c9b63cf | ||
|
|
7cb08849de | ||
|
|
7f052163e3 | ||
|
|
277d939033 | ||
|
|
26fbdcfab0 | ||
|
|
6d63ba9d4d | ||
|
|
8963f4fd62 | ||
|
|
463021a9f3 | ||
|
|
f71a8e9fef | ||
|
|
2dd5be1b4e | ||
|
|
608838045f | ||
|
|
899d506faa | ||
|
|
21b3e3ea05 | ||
|
|
a68951541c | ||
|
|
7fd0deedb1 | ||
|
|
e9e12ad843 | ||
|
|
4fd3185d12 | ||
|
|
f5ccebfd41 | ||
|
|
294721eef9 | ||
|
|
8af509992d | ||
|
|
11fccb8e89 | ||
|
|
1e126dd2c3 | ||
|
|
cfe2f889a4 | ||
|
|
1bd76b0e07 | ||
|
|
6d8c935cc7 | ||
|
|
7144cee0f6 | ||
|
|
f75a8d56f2 | ||
|
|
2f321bcfd9 | ||
|
|
a157f4f17b | ||
|
|
7723c623d5 | ||
|
|
bc3bb78916 | ||
|
|
0ebf5e49fb | ||
|
|
c340921fbb | ||
|
|
30e26b101c | ||
|
|
1b17cab663 | ||
|
|
ab039adf97 | ||
|
|
62fe10df31 | ||
|
|
2004a751dd | ||
|
|
ace127acf4 | ||
|
|
82c5497a06 | ||
|
|
dbb7989027 | ||
|
|
103f677a93 | ||
|
|
cb6bf78595 | ||
|
|
62334ddef7 | ||
|
|
778f67f2e4 | ||
|
|
48737f8e60 | ||
|
|
94de62e503 | ||
|
|
0445052898 | ||
|
|
ccde90ea91 | ||
|
|
ed94355019 | ||
|
|
02fcd1b5fc | ||
|
|
ca934e7cdf | ||
|
|
0ddd5f0a79 | ||
|
|
099d6801b7 | ||
|
|
62293926ec | ||
|
|
c96daad12c | ||
|
|
9cc3be152f | ||
|
|
f841c0d4ba | ||
|
|
3cd1d8135e | ||
|
|
86474d9f90 | ||
|
|
9bf87a3033 | ||
|
|
71e32520cf | ||
|
|
e3e938c8eb | ||
|
|
03aa440424 | ||
|
|
85ca38be90 | ||
|
|
7c9790dff0 | ||
|
|
40a71a11cb | ||
|
|
46a500f5e5 | ||
|
|
2d1d03bf8e | ||
|
|
1092d00c7a | ||
|
|
cd58e0d01e | ||
|
|
30a9783348 | ||
|
|
c839cf50af | ||
|
|
f8d607b06f | ||
|
|
cfadeb07b1 | ||
|
|
072850be0b | ||
|
|
71d120bc4e | ||
|
|
68d3cea528 | ||
|
|
07e801f44d | ||
|
|
ee5c694aa2 | ||
|
|
efa5eb1770 | ||
|
|
42e37246f3 | ||
|
|
dabb08ff4a | ||
|
|
66b0e04cc6 | ||
|
|
74824b7737 | ||
|
|
df2bcdb854 | ||
|
|
a8e9ee2e95 | ||
|
|
5093697b27 | ||
|
|
5bacd63805 | ||
|
|
1d5932e63f | ||
|
|
022762c0c9 | ||
|
|
e16bd194a3 | ||
|
|
a3765c19e3 | ||
|
|
a845d92d88 | ||
|
|
668c9e5a64 | ||
|
|
aaa06f4120 | ||
|
|
e26f4ce707 | ||
|
|
a8c3a2d991 | ||
|
|
6d52cef73a | ||
|
|
edacfcdec7 | ||
|
|
683872ef4e | ||
|
|
7a299ba1f9 | ||
|
|
f5eaedfc72 | ||
|
|
11b6afbe09 | ||
|
|
cd7340915b | ||
|
|
b38bb3df5d | ||
|
|
1f7725ada3 | ||
|
|
622095ebc4 | ||
|
|
397b7fefe3 | ||
|
|
10d38b709b | ||
|
|
98985690f0 | ||
|
|
f50c483c64 | ||
|
|
a15eca137d | ||
|
|
ba4be02e75 | ||
|
|
d4f6a86a57 | ||
|
|
6a058372bb | ||
|
|
e6cce350bd | ||
|
|
7c0c1e6cf8 | ||
|
|
39f787b7db | ||
|
|
424437446d | ||
|
|
60a1859d89 | ||
|
|
ad5c1639e8 | ||
|
|
92828b22fa | ||
|
|
e470096e4e | ||
|
|
3c41608ee9 | ||
|
|
035e145cd1 | ||
|
|
2f621279c2 | ||
|
|
c30185c6ae | ||
|
|
908c74eb27 | ||
|
|
10c17fc9a9 | ||
|
|
a6a0cb928a | ||
|
|
8bca988520 | ||
|
|
0af0af8d8a | ||
|
|
97da13c3c4 | ||
|
|
7134b46cdc | ||
|
|
aecdf7a3d3 | ||
|
|
37e37d1998 | ||
|
|
6103a8590d | ||
|
|
ba62dadc00 | ||
|
|
26073b82fd | ||
|
|
9b73bca79d | ||
|
|
0a0bb0ca13 | ||
|
|
652df47c5c | ||
|
|
9248be2177 | ||
|
|
b3800fc42e | ||
|
|
15f304736f | ||
|
|
21cdf59065 | ||
|
|
e3693afb75 | ||
|
|
009a753585 | ||
|
|
5c72541044 | ||
|
|
a01e604443 | ||
|
|
52b339d0b8 | ||
|
|
579ed5b9c0 | ||
|
|
63e64b8bcc | ||
|
|
64f8583975 | ||
|
|
75e8064044 | ||
|
|
6f3e38e392 | ||
|
|
cb4d244f19 | ||
|
|
de3b8a10a0 | ||
|
|
8feece702c | ||
|
|
900308afec | ||
|
|
b47925a319 | ||
|
|
853325d9fd | ||
|
|
494be37715 | ||
|
|
d35cb5d072 | ||
|
|
df9ec711c5 | ||
|
|
d9d0837024 | ||
|
|
086138fbd9 | ||
|
|
bdbd4b57b7 | ||
|
|
5475448af5 | ||
|
|
c3da3f11d9 | ||
|
|
244c81587c | ||
|
|
a3877a2cb1 | ||
|
|
206df82d63 | ||
|
|
54e1e7684d | ||
|
|
27eef36677 | ||
|
|
d91953e70b | ||
|
|
bbc5a49054 | ||
|
|
f89fe9fbab | ||
|
|
0c042bfe50 | ||
|
|
1a7894b15e | ||
|
|
543f983e41 | ||
|
|
97e7e473b8 | ||
|
|
79c30f7a94 | ||
|
|
cccf86d388 | ||
|
|
c102c23831 | ||
|
|
80ada9c90a | ||
|
|
6c3b4070ba | ||
|
|
4b287b758d | ||
|
|
b6d129a5c1 | ||
|
|
4d08147647 | ||
|
|
ace7c17f2b | ||
|
|
10f3d8aa0f | ||
|
|
3237ca0d97 | ||
|
|
5682ab9570 | ||
|
|
a3d73634e7 | ||
|
|
98b6aec203 | ||
|
|
7feb788ed3 | ||
|
|
bea490081b | ||
|
|
7adc3ca003 | ||
|
|
f8cbc63ab0 | ||
|
|
418590fb35 | ||
|
|
56144482f1 | ||
|
|
59f681e6af | ||
|
|
d3296f5180 | ||
|
|
c6fff0aa13 | ||
|
|
41e0c42282 | ||
|
|
ede2274816 | ||
|
|
ead672afb2 | ||
|
|
73bc7b045e | ||
|
|
3281502c25 | ||
|
|
ca35e536db | ||
|
|
bb8e0eb7bf | ||
|
|
bb451ac3b5 | ||
|
|
b0b7842f9c | ||
|
|
aad661cb65 | ||
|
|
ed9b63520d | ||
|
|
0d89d4d0d3 | ||
|
|
5de1246827 | ||
|
|
edc3b014cd | ||
|
|
fec98f45ce | ||
|
|
94810d5066 | ||
|
|
431cc796d8 | ||
|
|
e9d2dbcc92 | ||
|
|
7a618ef89c | ||
|
|
5b56249d12 | ||
|
|
e2ba5abe76 | ||
|
|
70a4b7c863 | ||
|
|
10fde1b1ef | ||
|
|
6131746180 | ||
|
|
bf953bf1b5 | ||
|
|
d81da10dfa | ||
|
|
ed9cdc1ab7 | ||
|
|
9f506eb83a | ||
|
|
36e8c52b01 | ||
|
|
e6d1233bfe | ||
|
|
6847459022 | ||
|
|
285623a02b | ||
|
|
fbf64f8037 | ||
|
|
2b74ca2746 | ||
|
|
b106a82308 | ||
|
|
d71a0ddd66 | ||
|
|
5700f2f78a | ||
|
|
c5ca6abb90 | ||
|
|
6826b6e1f8 | ||
|
|
27c4fa2fcf | ||
|
|
6ef1aff991 | ||
|
|
57a026a7a1 | ||
|
|
cfc785358e | ||
|
|
c2d2b3f3b2 | ||
|
|
4aa153fcf1 | ||
|
|
824f63a3ad | ||
|
|
2446dc6950 | ||
|
|
f98405188d | ||
|
|
a5cf24773c | ||
|
|
01c1e4f8cb | ||
|
|
0759fb6436 | ||
|
|
ed5188069b | ||
|
|
aa0a9bde76 | ||
|
|
56a450a936 | ||
|
|
1e01106b94 | ||
|
|
b9a755d6d3 | ||
|
|
1d9d6c899d | ||
|
|
444dffb458 | ||
|
|
3f1b7192ff | ||
|
|
b992b19c66 | ||
|
|
e341121f61 | ||
|
|
cd3e2963b3 | ||
|
|
346faf1d07 | ||
|
|
1e09b2bbd8 | ||
|
|
0ffba45517 | ||
|
|
32ff346154 | ||
|
|
0008f44255 | ||
|
|
a3c519a061 | ||
|
|
5b8a923cb5 | ||
|
|
576cbc0b90 | ||
|
|
e95e2cf152 | ||
|
|
317dc10af4 | ||
|
|
f06065337c | ||
|
|
cc870ca302 | ||
|
|
80f5506b18 | ||
|
|
5321a75272 | ||
|
|
b70a78b7aa | ||
|
|
5ad08791ea | ||
|
|
9dc3ec0bf8 | ||
|
|
69dd9d0cac | ||
|
|
24f923e88e | ||
|
|
b5552a216d | ||
|
|
f3fe4433ae | ||
|
|
1988c617a0 | ||
|
|
5e531d6f96 | ||
|
|
1fb7e97700 | ||
|
|
64d27156f5 | ||
|
|
b528a0f4ec | ||
|
|
7f265a6692 | ||
|
|
55e00e35c1 | ||
|
|
d94e1ba55b | ||
|
|
00e7167174 | ||
|
|
9d7b69fc0e | ||
|
|
db9a68e9c9 | ||
|
|
b5d9d6e268 | ||
|
|
5ff0c563ec | ||
|
|
e2131523ec | ||
|
|
1f5f51e3e5 | ||
|
|
1bb0d54dce | ||
|
|
094bb37049 | ||
|
|
58601db5ef | ||
|
|
b5bef98a9b | ||
|
|
1026f1efa5 | ||
|
|
0c673fb524 | ||
|
|
7ee2c9478d | ||
|
|
4b51f8251b | ||
|
|
e91a64b1cc | ||
|
|
8920762fc5 | ||
|
|
ba40f93386 | ||
|
|
781bf52a8e | ||
|
|
cca1a9832e | ||
|
|
1a152a5597 | ||
|
|
76a5290351 | ||
|
|
829e17ef2b | ||
|
|
bc5d3bea14 | ||
|
|
edf2a2e68d | ||
|
|
5df443a016 | ||
|
|
f6396f2e74 | ||
|
|
c618e58a11 | ||
|
|
2ea27acdde | ||
|
|
331cad276e | ||
|
|
b74eab8377 | ||
|
|
fb80318553 | ||
|
|
3eb4aed867 | ||
|
|
f6f959a897 | ||
|
|
2b422a542a | ||
|
|
d0e9d58a43 | ||
|
|
8a1933b9b2 | ||
|
|
96a587f343 | ||
|
|
2bb6a71874 | ||
|
|
94acd12f1c | ||
|
|
5e44a61068 | ||
|
|
a54f0ed94d | ||
|
|
662c6f3cc2 | ||
|
|
d93c635a0a | ||
|
|
17b73aaf91 | ||
|
|
c194911458 | ||
|
|
92e99e3fb4 | ||
|
|
848e6102a1 | ||
|
|
ef37bf9b1a | ||
|
|
eb41e023c7 | ||
|
|
6b4987bf39 | ||
|
|
d46ff76887 | ||
|
|
d53a9e672c | ||
|
|
80ef99a24b | ||
|
|
6062a1f8c7 | ||
|
|
d974bd9a07 | ||
|
|
fb8c9566d5 | ||
|
|
7431bd69e9 | ||
|
|
5d7f393e94 | ||
|
|
ef25d100e1 | ||
|
|
17f82c8972 | ||
|
|
05c937743c | ||
|
|
bf2e7ff130 | ||
|
|
1b30ee606f | ||
|
|
3235907266 | ||
|
|
99c7e417d6 | ||
|
|
d81906d348 | ||
|
|
296872d2e4 | ||
|
|
7cd02b4916 | ||
|
|
0e217f48be | ||
|
|
61fdf4b6c7 | ||
|
|
91dbf1f01a | ||
|
|
d52aac76c0 | ||
|
|
6102e441d6 | ||
|
|
1a5fec39c0 | ||
|
|
efa5091b98 | ||
|
|
be4386658a | ||
|
|
0cddce7a37 | ||
|
|
a86d13632e | ||
|
|
d71682a3f7 | ||
|
|
c901ace21a | ||
|
|
418398a870 | ||
|
|
baca57062e | ||
|
|
52df8e6e8b | ||
|
|
b51747378a | ||
|
|
424a6b0428 | ||
|
|
4549223d6d | ||
|
|
91dd3468d5 | ||
|
|
83a3871d43 | ||
|
|
8b6023c45a | ||
|
|
a401d4e760 | ||
|
|
a8520f6593 | ||
|
|
9a95d207cf | ||
|
|
c75d779f85 | ||
|
|
b8f2066408 | ||
|
|
cc48b6cd96 | ||
|
|
b36c735445 | ||
|
|
5f0f26a3ac | ||
|
|
6b81eaa65d | ||
|
|
644f15e80d | ||
|
|
a8ddf4c2df | ||
|
|
3f9833117e | ||
|
|
6140d0c849 | ||
|
|
f4cb7cea21 | ||
|
|
140ec1f90f | ||
|
|
31d7220785 | ||
|
|
38e1d36684 | ||
|
|
6397655f7d | ||
|
|
692047e4c8 | ||
|
|
821bfe68c1 | ||
|
|
596eaa7590 | ||
|
|
08fd0f0fa9 | ||
|
|
77c925ce4b | ||
|
|
3338352b41 | ||
|
|
e3aa4143c7 | ||
|
|
0a93808076 | ||
|
|
0cc21f9b99 | ||
|
|
d3543ea291 | ||
|
|
e824f7a28c | ||
|
|
56be0744f0 | ||
|
|
d874c71e0b | ||
|
|
487177af87 | ||
|
|
0452a4e1ac | ||
|
|
959a03214a | ||
|
|
fe22dfc531 | ||
|
|
139e258664 | ||
|
|
d1ab14966b | ||
|
|
40ff3c731e | ||
|
|
75d77a3648 | ||
|
|
921da7242d | ||
|
|
67c9937e67 | ||
|
|
bdcb3af6fa | ||
|
|
17e81ab6bd | ||
|
|
70e1ec2cd2 | ||
|
|
c6a7fd405b | ||
|
|
4f0d1704c4 | ||
|
|
a966a5097e | ||
|
|
080db4d462 | ||
|
|
4af766162e | ||
|
|
57d67bc4a8 | ||
|
|
16278f36ec | ||
|
|
9cb981f068 | ||
|
|
05c6d67cab | ||
|
|
0c516843d8 | ||
|
|
d400ac57d5 | ||
|
|
2644efd9f7 | ||
|
|
11baa97ef6 | ||
|
|
c36636bd2d | ||
|
|
6bb05a6780 | ||
|
|
360f5db2cf | ||
|
|
bbbeacee4d | ||
|
|
94ad56fc96 | ||
|
|
3df80f2653 | ||
|
|
bb6c9cf49e | ||
|
|
ae12222687 | ||
|
|
094dfde048 | ||
|
|
714b887274 | ||
|
|
8cfa88eff8 | ||
|
|
3976b57100 | ||
|
|
bbcf484f7f | ||
|
|
249f35f948 | ||
|
|
b71f1a79c8 | ||
|
|
e4aa3d310f | ||
|
|
99f57269fb | ||
|
|
93def3a557 | ||
|
|
a04674d93d | ||
|
|
539cc187a8 | ||
|
|
fcdd975751 | ||
|
|
8a4c2bf208 | ||
|
|
50c5d533b0 | ||
|
|
f952553c76 | ||
|
|
9a9be466f7 | ||
|
|
bbad029aa1 | ||
|
|
eb748554c5 | ||
|
|
c8b494e909 | ||
|
|
bd7fd725f6 | ||
|
|
3e0440ba53 | ||
|
|
83a29f8d85 | ||
|
|
8551e6e74a | ||
|
|
3582cb3f46 | ||
|
|
23de13b82c | ||
|
|
93acd4f18f | ||
|
|
96883dabe3 | ||
|
|
da521020f6 | ||
|
|
ac78738061 | ||
|
|
b78ca71c0f | ||
|
|
cd7272d00a | ||
|
|
cd7489c569 | ||
|
|
1f3f369df0 | ||
|
|
911e29f2ea | ||
|
|
d7b359e432 | ||
|
|
0b2c92cbe5 | ||
|
|
f626d964c5 | ||
|
|
b0e0b38549 | ||
|
|
94af4b2c7b | ||
|
|
0b235e146f | ||
|
|
40f3301324 | ||
|
|
4fe90e59a8 | ||
|
|
b28611ed08 | ||
|
|
672ad22e4f | ||
|
|
c17bd5ec3a | ||
|
|
2770755f9d | ||
|
|
ee69cdbf7b | ||
|
|
a72a25640f | ||
|
|
e86f8ddd09 | ||
|
|
66bdb39f1a | ||
|
|
aa368c3a63 | ||
|
|
b499a2ab87 | ||
|
|
49e4482947 | ||
|
|
6e7e4250e6 | ||
|
|
5a8ea15c4f | ||
|
|
f9aa029e8e | ||
|
|
6653e379b3 | ||
|
|
930a611374 | ||
|
|
51e918be6d | ||
|
|
b0170eea8f | ||
|
|
144b34ca2e | ||
|
|
676c022e41 | ||
|
|
7c0b98bb70 | ||
|
|
7779713392 | ||
|
|
5e1396025c | ||
|
|
d09c88f71c | ||
|
|
7980f21b1b | ||
|
|
3843994a05 | ||
|
|
a1b08ca037 | ||
|
|
144cdd11ec | ||
|
|
b6531cdb10 | ||
|
|
0eef4a5fa1 | ||
|
|
42baaf8f2d | ||
|
|
08d9dff8eb | ||
|
|
01b3aab9bc | ||
|
|
fde34ef178 | ||
|
|
5195abec94 | ||
|
|
aa79acd09e | ||
|
|
67011ccd72 | ||
|
|
e2cd7fe17e | ||
|
|
4c0624f489 | ||
|
|
8c033250b1 | ||
|
|
05b5a6fddf | ||
|
|
df5bde7b8e | ||
|
|
ca5f52c48c | ||
|
|
3656adf059 | ||
|
|
4381792b05 | ||
|
|
0a3c20b08a | ||
|
|
4ab3f41665 | ||
|
|
c75ce9cbba | ||
|
|
10356a4376 | ||
|
|
4fb4e19e99 | ||
|
|
98c2056f53 | ||
|
|
74e8ae4d79 | ||
|
|
6b890cf3b3 | ||
|
|
d62b471afe | ||
|
|
b97240963e | ||
|
|
55ed0f2374 | ||
|
|
2c1b3d5790 | ||
|
|
9c3b757578 | ||
|
|
bc04ad96b5 | ||
|
|
35b07a9c18 | ||
|
|
17955fc419 | ||
|
|
ac98f5862a | ||
|
|
63758c6679 | ||
|
|
7c765c47e6 | ||
|
|
58523b0000 | ||
|
|
41f4b36593 | ||
|
|
379212b8fe | ||
|
|
a04c7831b1 | ||
|
|
1aec16a240 | ||
|
|
498626748d | ||
|
|
f24ec97607 | ||
|
|
f0ad260eab | ||
|
|
92ee2be021 | ||
|
|
02e4528f3e | ||
|
|
f871353acc | ||
|
|
faa1120e14 | ||
|
|
8a8000c80a | ||
|
|
8164610105 | ||
|
|
3935a3c885 | ||
|
|
15ec39bc56 | ||
|
|
5dd2b4c439 | ||
|
|
1ee05dda4b | ||
|
|
5a86588cb5 | ||
|
|
af3269b362 | ||
|
|
a7a4baa06e | ||
|
|
9a497b9bba | ||
|
|
d2a1c449da | ||
|
|
504524ea56 | ||
|
|
51e22b406c | ||
|
|
1ec81482cc | ||
|
|
fd610414fd | ||
|
|
1802d5d5da | ||
|
|
e7e6c76b4e | ||
|
|
84c7bd63f5 | ||
|
|
f7187bb798 | ||
|
|
00db5b69ab | ||
|
|
b16109ca56 | ||
|
|
a00b5aa136 | ||
|
|
0e43e5d295 | ||
|
|
9f1e2d6192 | ||
|
|
52c794a259 | ||
|
|
fcf4c5f328 | ||
|
|
85b33a60b3 | ||
|
|
c99bb4cfd7 | ||
|
|
5b0a942b42 | ||
|
|
d94c9e522c | ||
|
|
8fab15abd3 | ||
|
|
7717304549 | ||
|
|
b3f1cc3edd | ||
|
|
16f9c727f2 | ||
|
|
007ba5fce9 | ||
|
|
5b36f07493 | ||
|
|
cbd2580736 | ||
|
|
fe872a59ca | ||
|
|
c4dfd99a8c | ||
|
|
5a34fe4704 | ||
|
|
2fc3dcce7e | ||
|
|
1a932fb749 | ||
|
|
f8a2ee9a8d | ||
|
|
a972d456ba | ||
|
|
e9f4c9e687 | ||
|
|
58f7cdc15e | ||
|
|
3f9b0643ee | ||
|
|
24bd4da94b | ||
|
|
a18b4ffd0f | ||
|
|
c9e032e350 | ||
|
|
e1679ad433 | ||
|
|
933e395945 | ||
|
|
1f64a00bbf | ||
|
|
7eebc0c3a5 | ||
|
|
a2c9dabd77 | ||
|
|
6187c826b4 | ||
|
|
b8987963b3 | ||
|
|
dc7f56b7aa | ||
|
|
2295ddddee | ||
|
|
3328d20b2d | ||
|
|
d444211c68 | ||
|
|
2de8cd1d6f | ||
|
|
115813ac44 | ||
|
|
7c0e8d9421 | ||
|
|
1c349cf55c | ||
|
|
7b4d8a8f05 | ||
|
|
6e7e2de490 | ||
|
|
0168ef55f0 | ||
|
|
0a9a92ee7d | ||
|
|
3bfac7ef3b | ||
|
|
1581e0e439 | ||
|
|
6773848874 | ||
|
|
3e9219173c | ||
|
|
d46a565e6c | ||
|
|
6267804d38 | ||
|
|
25c4d282ce | ||
|
|
45281360d5 | ||
|
|
7a32b8d1d2 | ||
|
|
7b0018b661 | ||
|
|
9032879e20 | ||
|
|
8b668cf8b7 | ||
|
|
3323533fd0 | ||
|
|
50e96baea1 | ||
|
|
2ac92df9d3 | ||
|
|
63e64cea60 | ||
|
|
0267c76de2 | ||
|
|
2382a10bba | ||
|
|
a8db40e99a | ||
|
|
8553fffffe | ||
|
|
d9d38a27ff | ||
|
|
1e7a5562ab | ||
|
|
e71032a8fc | ||
|
|
484a3dbe5c | ||
|
|
9c7f40e4fe | ||
|
|
f35b7ab6f4 | ||
|
|
a38dd3d5c8 | ||
|
|
00ad35fc3e | ||
|
|
0d78b44c80 | ||
|
|
fb614637a9 | ||
|
|
d0482d66c2 | ||
|
|
ef178dfd24 | ||
|
|
6cdc6e0780 | ||
|
|
c961e6d9c7 | ||
|
|
c6844ff47a | ||
|
|
605a630411 | ||
|
|
2b75cf1508 | ||
|
|
007054301b | ||
|
|
cf01d32237 | ||
|
|
5ec553f68d | ||
|
|
d95e5a169d | ||
|
|
8debefddad | ||
|
|
bd25860ccf | ||
|
|
b21cb5c0e9 | ||
|
|
ff79a2d3f4 | ||
|
|
9ae3743a58 | ||
|
|
ffcdbcc802 | ||
|
|
3ff78a3a47 | ||
|
|
4d78ac4789 | ||
|
|
be24f2d520 | ||
|
|
c3da3499d8 | ||
|
|
4d8553f959 | ||
|
|
eee1534da7 | ||
|
|
38c20f5737 | ||
|
|
ba85c5159d | ||
|
|
0388c5d5bc | ||
|
|
3714c80adb | ||
|
|
735c1a23ea | ||
|
|
88cfc96bd3 | ||
|
|
11f389289d | ||
|
|
553c1eacfc | ||
|
|
0032d3cf6c | ||
|
|
bd81cc1cc4 | ||
|
|
bc36fcb722 | ||
|
|
11cd553949 | ||
|
|
45a3f82c8d | ||
|
|
e9c9a51b8d |
19
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
19
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
@@ -1,17 +1,28 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
description: Create a new bug report
|
description: 'Create a new bug report.'
|
||||||
title: '[Bug]: '
|
title: '[Bug]: '
|
||||||
body:
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: >-
|
||||||
|
# 💎 Bounty program (with
|
||||||
|
[algora.io](https://console.algora.io/org/coollabsio/bounties/new))
|
||||||
|
|
||||||
|
|
||||||
|
If you would like to prioritize the issue resolution, you can add bounty
|
||||||
|
to this issue.
|
||||||
|
|
||||||
|
|
||||||
|
Click [here](https://console.algora.io/org/coollabsio/bounties/new) to
|
||||||
|
get started.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
description: A clear and concise description of the problem
|
description: A clear and concise description of the problem
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Minimal Reproduction (if possible, example repository)
|
label: Minimal Reproduction (if possible, example repository)
|
||||||
description: Please provide a step by step guide to reproduce the issue
|
description: Please provide a step by step guide to reproduce the issue.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
1
.github/pull_request_template.md
vendored
Normal file
1
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
> Always use `next` branch as destination branch for PRs, not `main`
|
||||||
4
.github/workflows/development-build.yml
vendored
4
.github/workflows/development-build.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod/Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod/Dockerfile
|
||||||
platforms: linux/aarch64
|
platforms: linux/aarch64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
|
||||||
|
|||||||
25
.github/workflows/fix-php-code-style-issues.yml
vendored
Normal file
25
.github/workflows/fix-php-code-style-issues.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Fix PHP code style issues
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
php-code-styling:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 5
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.head_ref }}
|
||||||
|
|
||||||
|
- name: Fix PHP code style issues
|
||||||
|
uses: aglipanci/laravel-pint-action@2.4
|
||||||
|
|
||||||
|
- name: Commit changes
|
||||||
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
|
with:
|
||||||
|
commit_message: Fix styling
|
||||||
6
.github/workflows/production-build.yml
vendored
6
.github/workflows/production-build.yml
vendored
@@ -3,6 +3,8 @@ name: Production Build (v4)
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
|
paths-ignore:
|
||||||
|
- templates/service-templates.json
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
@@ -27,7 +29,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod/Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
|
||||||
@@ -49,7 +51,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod/Dockerfile
|
||||||
platforms: linux/aarch64
|
platforms: linux/aarch64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ tasks:
|
|||||||
# Fix because of https://github.com/gitpod-io/gitpod/issues/16614
|
# Fix because of https://github.com/gitpod-io/gitpod/issues/16614
|
||||||
before: sudo curl -o /usr/local/bin/docker-compose -fsSL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-$(uname -m)
|
before: sudo curl -o /usr/local/bin/docker-compose -fsSL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-$(uname -m)
|
||||||
init: |
|
init: |
|
||||||
cp .env.example .env &&
|
cp .env.development.example .env &&
|
||||||
sed -i "s#APP_URL=http://localhost#APP_URL=$(gp url 8000)#g" .env
|
sed -i "s#APP_URL=http://localhost#APP_URL=$(gp url 8000)#g" .env
|
||||||
sed -i "s#USERID=#USERID=33333#g" .env
|
sed -i "s#USERID=#USERID=33333#g" .env
|
||||||
sed -i "s#GROUPID=#GROUPID=33333#g" .env
|
sed -i "s#GROUPID=#GROUPID=33333#g" .env
|
||||||
@@ -20,7 +20,7 @@ tasks:
|
|||||||
echo "Waiting for Sail environment to boot up."
|
echo "Waiting for Sail environment to boot up."
|
||||||
gp sync-await spin-is-ready
|
gp sync-await spin-is-ready
|
||||||
./vendor/bin/spin exec vite npm install
|
./vendor/bin/spin exec vite npm install
|
||||||
./vendor/bin/spin exec vite npm run dev
|
./vendor/bin/spin exec vite npm run dev -- --host
|
||||||
|
|
||||||
- name: Laravel Queue Worker, listening to code changes
|
- name: Laravel Queue Worker, listening to code changes
|
||||||
command: |
|
command: |
|
||||||
|
|||||||
@@ -30,5 +30,5 @@ Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if y
|
|||||||
Mails are caught by Mailpit: `localhost:8025`
|
Mails are caught by Mailpit: `localhost:8025`
|
||||||
|
|
||||||
## New Service Contribution
|
## New Service Contribution
|
||||||
Check out the docs [here](https://coolify.io/docs/resources/services/add-service).
|
Check out the docs [here](https://coolify.io/docs/knowledge-base/add-a-service).
|
||||||
|
|
||||||
|
|||||||
60
README.md
60
README.md
@@ -1,14 +1,21 @@
|
|||||||
|

|
||||||
|
|
||||||
|
[](https://console.algora.io/org/coollabsio/bounties/new)
|
||||||
|
[](https://console.algora.io/org/coollabsio/bounties?status=open)
|
||||||
|
[](https://console.algora.io/org/coollabsio/bounties?status=completed)
|
||||||
|
|
||||||
# About the Project
|
# About the Project
|
||||||
|
|
||||||
Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc.
|
Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc.
|
||||||
|
|
||||||
It helps you to manage your servers, applications, databases on your own hardware, all you need is SSH connection. You can manage VPS, Bare Metal, Raspberry PI's anything.
|
It helps you manage your servers, applications, and databases on your own hardware; you only need an SSH connection. You can manage VPS, Bare Metal, Raspberry PIs, and anything else.
|
||||||
|
|
||||||
Imagine if you could have the ease of a cloud but with your own servers. That is **Coolify**.
|
Imagine having the ease of a cloud but with your own servers. That is **Coolify**.
|
||||||
|
|
||||||
No vendor lock-in, which means that all the configuration for your applications/databases/etc are saved to your server. So if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You just lose the automations and all the magic. 🪄️
|
No vendor lock-in, which means that all the configurations for your applications/databases/etc are saved to your server. So, if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You lose the automations and all the magic. 🪄️
|
||||||
|
|
||||||
For more information, take a look at our landing page [here](https://coolify.io).
|
For more information, take a look at our landing page at [coolify.io](https://coolify.io).
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
@@ -19,26 +26,44 @@ You can find the installation script source [here](./scripts/install.sh).
|
|||||||
|
|
||||||
# Support
|
# Support
|
||||||
|
|
||||||
Contact us [here](https://coolify.io/docs/contact).
|
Contact us at [coolify.io/docs/contact](https://coolify.io/docs/contact).
|
||||||
|
|
||||||
# Donations
|
# Donations
|
||||||
To stay completely free, open-source, no feature behind paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the future development of the project.
|
To stay completely free and open-source, with no feature behind the paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the project's future development.
|
||||||
|
|
||||||
https://coolify.io/sponsorships
|
[coolify.io/sponsorships](https://coolify.io/sponsorships)
|
||||||
|
|
||||||
Thank you so much!
|
Thank you so much!
|
||||||
|
|
||||||
Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
|
Special thanks to our biggest sponsors!
|
||||||
|
|
||||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
||||||
|
<a href="http://htznr.li/CoolifyXHetzner" target="_blank"><img src="./other/logos/hetzner.jpg" alt="hetzner logo" width="150"/></a>
|
||||||
|
<a href="https://logto.io/?ref=coolify" target="_blank"><img src="./other/logos/logto.webp" alt="logto logo" width="150"/></a>
|
||||||
|
<a href="https://bc.direct/?ref=coolify.io" target="_blank"><img src="./other/logos/bc.png" alt="bc direct logo" width="200"/></a>
|
||||||
|
<a href="https://www.quantcdn.io/?ref=coolify.io" target="_blank"><img src="./other/logos/quant.svg" alt="quantcdn logo" width="150"/></a>
|
||||||
|
<a href="https://arcjet.com/?ref=coolify.io" target="_blank"><img src="./other/logos/arcjet.svg" alt="arcjet logo" width="200"/></a>
|
||||||
|
<a href="https://supa.guide/?ref=coolify.io" target="_blank"><img src="./other/logos/supaguide.png" alt="supaguide logo" width="200"/></a>
|
||||||
|
<a href="https://tigrisdata.com/?ref=coolify.io" target="_blank"><img src="./other/logos/tigris.svg" alt="tigris logo" width="140"/></a>
|
||||||
|
<a href="https://fractalnetworks.co/?ref=coolify.io" target="_blank"><img src="./other/logos/fractal.svg" alt="fractal logo" width="180"/></a>
|
||||||
|
<a href="https://coolify.ad.vin/?ref=coolify.io" target="_blank"><img src="./other/logos/advin.png" alt="advin logo" width="250"/></a>
|
||||||
|
<a href="https://trieve.ai/?ref=coolify.io" target="_blank"><img src="./other/logos/trieve_bg.png" alt="trieve logo" width="180"/></a>
|
||||||
|
<a href="https://blacksmith.sh/?ref=coolify.io" target="_blank"><img src="./other/logos/blacksmith.svg" alt="blacksmith logo" width="200"/></a>
|
||||||
|
|
||||||
## Github Sponsors ($40+)
|
## Github Sponsors ($40+)
|
||||||
<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
|
<a href="https://serpapi.com/?ref=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
|
||||||
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
<a href="https://typebot.io/?ref=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
||||||
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
<a href="https://www.runpod.io/?ref=coolify.io">
|
||||||
<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>
|
<svg style="width:60px;height:60px;background:#fff;" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 200 200"><g><path d="M74.5 51.1c-25.4 14.9-27 16-29.6 20.2-1.8 3-1.9 5.3-1.9 32.3 0 21.7.3 29.4 1.3 30.6 1.9 2.5 46.7 27.9 48.5 27.6 1.5-.3 1.7-3.1 2-27.7.2-21.9 0-27.8-1.1-29.5-.8-1.2-9.9-6.8-20.2-12.6-10.3-5.8-19.4-11.5-20.2-12.7-1.8-2.6-.9-5.9 1.8-7.4 1.6-.8 6.3 0 21.8 4C87.8 78.7 98 81 99.6 81c4.4 0 49.9-25.9 49.9-28.4 0-1.6-3.4-2.8-24-8.2-13.2-3.5-25.1-6.3-26.5-6.3-1.4.1-12.4 5.9-24.5 13z"></path><path d="m137.2 68.1-3.3 2.1 6.3 3.7c3.5 2 6.3 4.3 6.3 5.1 0 .9-8 6.1-19.4 12.6-10.6 6-20 11.9-20.7 12.9-1.2 1.6-1.4 7.2-1.2 29.4.3 24.8.5 27.6 2 27.9 1.8.3 46.6-25.1 48.6-27.6.9-1.2 1.2-8.8 1.2-30.2s-.3-29-1.2-30.2c-1.6-1.9-12.1-7.8-13.9-7.8-.8 0-2.9 1-4.7 2.1z"></path></g></svg></a>
|
||||||
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
|
<a href="https://lightspeed.run/?ref=coolify.io"><img src="https://github.com/lightspeedrun.png" width="60px" alt="Lightspeed.run"/></a>
|
||||||
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
|
<a href="https://www.flint.sh/en/home?ref=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></a>
|
||||||
|
<a href="https://americancloud.com/?ref=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
|
||||||
|
<a href="https://cryptojobslist.com/?ref=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
||||||
|
<a href="https://codext.link/coolify-io?ref=coolify.io"><img src="./other/logos/codext.jpg" width="60px" alt="Codext" /></a>
|
||||||
|
<a href="https://x.com/mrsmith9ja?ref=coolify.io"><img width="60px" alt="Thompson Edolo" src="https://github.com/verygreenboi.png"/></a>
|
||||||
|
<a href="https://www.uxwizz.com/?ref=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
|
||||||
|
<a href="https://github.com/Flowko"><img src="https://barrad.me/_ipx/f_webp&s_300x300/younes.jpg" width="60px" alt="Younes Barrad" /></a>
|
||||||
|
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Automaze" /></a>
|
||||||
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
|
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
|
||||||
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
|
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
|
||||||
@@ -61,15 +86,16 @@ Special thanks to our biggest sponsor, [CCCareers](https://cccareers.org/)!
|
|||||||
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
||||||
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
||||||
|
|
||||||
|
|
||||||
## Individuals
|
## Individuals
|
||||||
|
|
||||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
||||||
|
|
||||||
# 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: [app.coolify.io](https://app.coolify.io)
|
||||||
|
|
||||||
For more information & pricing, take a look at our landing page [here](https://coolify.io).
|
For more information & pricing, take a look at our landing page [coolify.io](https://coolify.io).
|
||||||
|
|
||||||
## Why should I use the Cloud version?
|
## Why should I use the Cloud version?
|
||||||
The recommended way to use Coolify is to have one server for Coolify and one (or more) for the resources you are deploying. A server is around 4-5$/month.
|
The recommended way to use Coolify is to have one server for Coolify and one (or more) for the resources you are deploying. A server is around 4-5$/month.
|
||||||
@@ -93,7 +119,7 @@ By subscribing to the cloud version, you get the Coolify server for the same pri
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a href="https://www.producthunt.com/posts/coolify?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An open-source & self-hostable Heroku, Netlify alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://www.producthunt.com/posts/coolify?ref=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An open-source & self-hostable Heroku, Netlify alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></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>
|
<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>
|
||||||
|
|
||||||
|
|||||||
16
SECURITY.md
Normal file
16
SECURITY.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Use this section to tell people about which versions of your project are
|
||||||
|
currently being supported with security updates.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| > 4 | :white_check_mark: |
|
||||||
|
| 3 | :x: |
|
||||||
|
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you have any vulnerability please report at hi@coollabs.io
|
||||||
@@ -3,17 +3,17 @@
|
|||||||
namespace App\Actions\Application;
|
namespace App\Actions\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\StandaloneDocker;
|
|
||||||
use App\Notifications\Application\StatusChanged;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StopApplication
|
class StopApplication
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Application $application)
|
public function handle(Application $application)
|
||||||
{
|
{
|
||||||
if ($application->destination->server->isSwarm()) {
|
if ($application->destination->server->isSwarm()) {
|
||||||
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
|
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ class StopApplication
|
|||||||
$servers->push($server);
|
$servers->push($server);
|
||||||
});
|
});
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
if (!$server->isFunctional()) {
|
if (! $server->isFunctional()) {
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ use Lorisleiva\Actions\Concerns\AsAction;
|
|||||||
class StopApplicationOneServer
|
class StopApplicationOneServer
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Application $application, Server $server)
|
public function handle(Application $application, Server $server)
|
||||||
{
|
{
|
||||||
if ($application->destination->server->isSwarm()) {
|
if ($application->destination->server->isSwarm()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!$server->isFunctional()) {
|
if (! $server->isFunctional()) {
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -32,6 +33,7 @@ class StopApplicationOneServer
|
|||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use Spatie\Activitylog\Models\Activity;
|
|||||||
class PrepareCoolifyTask
|
class PrepareCoolifyTask
|
||||||
{
|
{
|
||||||
protected Activity $activity;
|
protected Activity $activity;
|
||||||
|
|
||||||
protected CoolifyTaskArgs $remoteProcessArgs;
|
protected CoolifyTaskArgs $remoteProcessArgs;
|
||||||
|
|
||||||
public function __construct(CoolifyTaskArgs $remoteProcessArgs)
|
public function __construct(CoolifyTaskArgs $remoteProcessArgs)
|
||||||
@@ -28,12 +29,12 @@ class PrepareCoolifyTask
|
|||||||
->withProperties($properties)
|
->withProperties($properties)
|
||||||
->performedOn($remoteProcessArgs->model)
|
->performedOn($remoteProcessArgs->model)
|
||||||
->event($remoteProcessArgs->type)
|
->event($remoteProcessArgs->type)
|
||||||
->log("[]");
|
->log('[]');
|
||||||
} else {
|
} else {
|
||||||
$this->activity = activity()
|
$this->activity = activity()
|
||||||
->withProperties($remoteProcessArgs->toArray())
|
->withProperties($remoteProcessArgs->toArray())
|
||||||
->event($remoteProcessArgs->type)
|
->event($remoteProcessArgs->type)
|
||||||
->log("[]");
|
->log('[]');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ class PrepareCoolifyTask
|
|||||||
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish, call_event_data: $this->remoteProcessArgs->call_event_data);
|
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish, call_event_data: $this->remoteProcessArgs->call_event_data);
|
||||||
dispatch($job);
|
dispatch($job);
|
||||||
$this->activity->refresh();
|
$this->activity->refresh();
|
||||||
|
|
||||||
return $this->activity;
|
return $this->activity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class RunRemoteProcess
|
|||||||
$decoded = json_decode(
|
$decoded = json_decode(
|
||||||
data_get($activity, 'description'),
|
data_get($activity, 'description'),
|
||||||
associative: true,
|
associative: true,
|
||||||
flags: JSON_THROW_ON_ERROR
|
flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE
|
||||||
);
|
);
|
||||||
} catch (\JsonException $exception) {
|
} catch (\JsonException $exception) {
|
||||||
return '';
|
return '';
|
||||||
@@ -69,7 +69,7 @@ class RunRemoteProcess
|
|||||||
return collect($decoded)
|
return collect($decoded)
|
||||||
->sortBy(fn ($i) => $i['order'])
|
->sortBy(fn ($i) => $i['order'])
|
||||||
->map(fn ($i) => $i['output'])
|
->map(fn ($i) => $i['output'])
|
||||||
->implode("");
|
->implode('');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(): ProcessResult
|
public function __invoke(): ProcessResult
|
||||||
@@ -91,7 +91,7 @@ class RunRemoteProcess
|
|||||||
if ($processResult->exitCode() == 0) {
|
if ($processResult->exitCode() == 0) {
|
||||||
$status = ProcessStatus::FINISHED;
|
$status = ProcessStatus::FINISHED;
|
||||||
}
|
}
|
||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && ! $this->ignore_errors) {
|
||||||
$status = ProcessStatus::ERROR;
|
$status = ProcessStatus::ERROR;
|
||||||
}
|
}
|
||||||
// if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
|
// if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
|
||||||
@@ -109,14 +109,14 @@ class RunRemoteProcess
|
|||||||
'status' => $status->value,
|
'status' => $status->value,
|
||||||
]);
|
]);
|
||||||
$this->activity->save();
|
$this->activity->save();
|
||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && ! $this->ignore_errors) {
|
||||||
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
||||||
}
|
}
|
||||||
if ($this->call_event_on_finish) {
|
if ($this->call_event_on_finish) {
|
||||||
try {
|
try {
|
||||||
if ($this->call_event_data) {
|
if ($this->call_event_data) {
|
||||||
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
||||||
"data" => $this->call_event_data,
|
'data' => $this->call_event_data,
|
||||||
]));
|
]));
|
||||||
} else {
|
} else {
|
||||||
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
||||||
@@ -127,6 +127,7 @@ class RunRemoteProcess
|
|||||||
ray($e);
|
ray($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $processResult;
|
return $processResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,8 +165,7 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
public function encodeOutput($type, $output)
|
public function encodeOutput($type, $output)
|
||||||
{
|
{
|
||||||
$outputStack = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR);
|
$outputStack = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
$outputStack[] = [
|
$outputStack[] = [
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'output' => $output,
|
'output' => $output,
|
||||||
@@ -174,15 +174,16 @@ class RunRemoteProcess
|
|||||||
'order' => $this->getLatestCounter(),
|
'order' => $this->getLatestCounter(),
|
||||||
];
|
];
|
||||||
|
|
||||||
return json_encode($outputStack, flags: JSON_THROW_ON_ERROR);
|
return json_encode($outputStack, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getLatestCounter(): int
|
protected function getLatestCounter(): int
|
||||||
{
|
{
|
||||||
$description = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR);
|
$description = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
|
||||||
if ($description === null || count($description) === 0) {
|
if ($description === null || count($description) === 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return end($description)['order'] + 1;
|
return end($description)['order'] + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
168
app/Actions/Database/StartClickhouse.php
Normal file
168
app/Actions/Database/StartClickhouse.php
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class StartClickhouse
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public StandaloneClickhouse $database;
|
||||||
|
|
||||||
|
public array $commands = [];
|
||||||
|
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
public function handle(StandaloneClickhouse $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
$container_name = $this->database->uuid;
|
||||||
|
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||||
|
|
||||||
|
$this->commands = [
|
||||||
|
"echo 'Starting {$database->name}.'",
|
||||||
|
"mkdir -p $this->configuration_dir",
|
||||||
|
];
|
||||||
|
|
||||||
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||||
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
|
$environment_variables = $this->generate_environment_variables();
|
||||||
|
|
||||||
|
$docker_compose = [
|
||||||
|
'services' => [
|
||||||
|
$container_name => [
|
||||||
|
'image' => $this->database->image,
|
||||||
|
'container_name' => $container_name,
|
||||||
|
'environment' => $environment_variables,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network,
|
||||||
|
],
|
||||||
|
'ulimits' => [
|
||||||
|
'nofile' => [
|
||||||
|
'soft' => 262144,
|
||||||
|
'hard' => 262144,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'labels' => [
|
||||||
|
'coolify.managed' => 'true',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => "clickhouse-client --password {$this->database->clickhouse_admin_password} --query 'SELECT 1'",
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 10,
|
||||||
|
'start_period' => '5s',
|
||||||
|
],
|
||||||
|
'mem_limit' => $this->database->limits_memory,
|
||||||
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $this->database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
if (! is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
|
'fluentd-async' => 'true',
|
||||||
|
'fluentd-sub-second-precision' => 'true',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
|
}
|
||||||
|
if (count($persistent_storages) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
|
}
|
||||||
|
if (count($persistent_file_volumes) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||||
|
return "$item->fs_path:$item->mount_path";
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
|
if (count($volume_names) > 0) {
|
||||||
|
$docker_compose['volumes'] = $volume_names;
|
||||||
|
}
|
||||||
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||||
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
|
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $local_persistent_volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes_only_volume_names()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes_names = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes_names[$name] = [
|
||||||
|
'name' => $name,
|
||||||
|
'external' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $local_persistent_volumes_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_environment_variables()
|
||||||
|
{
|
||||||
|
$environment_variables = collect();
|
||||||
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_USER'))->isEmpty()) {
|
||||||
|
$environment_variables->push("CLICKHOUSE_ADMIN_USER={$this->database->clickhouse_admin_user}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('CLICKHOUSE_ADMIN_PASSWORD'))->isEmpty()) {
|
||||||
|
$environment_variables->push("CLICKHOUSE_ADMIN_PASSWORD={$this->database->clickhouse_admin_password}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $environment_variables->all();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,9 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -15,7 +18,7 @@ class StartDatabaseProxy
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase $database)
|
||||||
{
|
{
|
||||||
$internalPort = null;
|
$internalPort = null;
|
||||||
$type = $database->getMorphClass();
|
$type = $database->getMorphClass();
|
||||||
@@ -50,18 +53,36 @@ class StartDatabaseProxy
|
|||||||
$type = 'App\Models\StandaloneRedis';
|
$type = 'App\Models\StandaloneRedis';
|
||||||
$containerName = "redis-{$database->service->uuid}";
|
$containerName = "redis-{$database->service->uuid}";
|
||||||
break;
|
break;
|
||||||
|
case 'standalone-keydb':
|
||||||
|
$type = 'App\Models\StandaloneKeydb';
|
||||||
|
$containerName = "keydb-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
|
case 'standalone-dragonfly':
|
||||||
|
$type = 'App\Models\StandaloneDragonfly';
|
||||||
|
$containerName = "dragonfly-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
|
case 'standalone-clickhouse':
|
||||||
|
$type = 'App\Models\StandaloneClickhouse';
|
||||||
|
$containerName = "clickhouse-{$database->service->uuid}";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($type === 'App\Models\StandaloneRedis') {
|
if ($type === 'App\Models\StandaloneRedis') {
|
||||||
$internalPort = 6379;
|
$internalPort = 6379;
|
||||||
} else if ($type === 'App\Models\StandalonePostgresql') {
|
} elseif ($type === 'App\Models\StandalonePostgresql') {
|
||||||
$internalPort = 5432;
|
$internalPort = 5432;
|
||||||
} else if ($type === 'App\Models\StandaloneMongodb') {
|
} elseif ($type === 'App\Models\StandaloneMongodb') {
|
||||||
$internalPort = 27017;
|
$internalPort = 27017;
|
||||||
} else if ($type === 'App\Models\StandaloneMysql') {
|
} elseif ($type === 'App\Models\StandaloneMysql') {
|
||||||
$internalPort = 3306;
|
$internalPort = 3306;
|
||||||
} else if ($type === 'App\Models\StandaloneMariadb') {
|
} elseif ($type === 'App\Models\StandaloneMariadb') {
|
||||||
$internalPort = 3306;
|
$internalPort = 3306;
|
||||||
|
} elseif ($type === 'App\Models\StandaloneKeydb') {
|
||||||
|
$internalPort = 6379;
|
||||||
|
} elseif ($type === 'App\Models\StandaloneDragonfly') {
|
||||||
|
$internalPort = 6379;
|
||||||
|
} elseif ($type === 'App\Models\StandaloneClickhouse') {
|
||||||
|
$internalPort = 9000;
|
||||||
}
|
}
|
||||||
$configuration_dir = database_proxy_dir($database->uuid);
|
$configuration_dir = database_proxy_dir($database->uuid);
|
||||||
$nginxconf = <<<EOF
|
$nginxconf = <<<EOF
|
||||||
@@ -80,20 +101,19 @@ class StartDatabaseProxy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF;
|
EOF;
|
||||||
$dockerfile = <<< EOF
|
$dockerfile = <<< 'EOF'
|
||||||
FROM nginx:stable-alpine
|
FROM nginx:stable-alpine
|
||||||
|
|
||||||
COPY nginx.conf /etc/nginx/nginx.conf
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
EOF;
|
EOF;
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
|
||||||
'services' => [
|
'services' => [
|
||||||
$proxyContainerName => [
|
$proxyContainerName => [
|
||||||
'build' => [
|
'build' => [
|
||||||
'context' => $configuration_dir,
|
'context' => $configuration_dir,
|
||||||
'dockerfile' => 'Dockerfile',
|
'dockerfile' => 'Dockerfile',
|
||||||
],
|
],
|
||||||
'image' => "nginx:stable-alpine",
|
'image' => 'nginx:stable-alpine',
|
||||||
'container_name' => $proxyContainerName,
|
'container_name' => $proxyContainerName,
|
||||||
'restart' => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
'ports' => [
|
'ports' => [
|
||||||
@@ -110,17 +130,17 @@ class StartDatabaseProxy
|
|||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 3,
|
'retries' => 3,
|
||||||
'start_period' => '1s'
|
'start_period' => '1s',
|
||||||
],
|
],
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$network => [
|
$network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $network,
|
'name' => $network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
||||||
$nginxconf_base64 = base64_encode($nginxconf);
|
$nginxconf_base64 = base64_encode($nginxconf);
|
||||||
@@ -128,9 +148,9 @@ class StartDatabaseProxy
|
|||||||
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
|
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
|
||||||
instant_remote_process([
|
instant_remote_process([
|
||||||
"mkdir -p $configuration_dir",
|
"mkdir -p $configuration_dir",
|
||||||
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
"echo '{$dockerfile_base64}' | base64 -d | tee $configuration_dir/Dockerfile > /dev/null",
|
||||||
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
"echo '{$nginxconf_base64}' | base64 -d | tee $configuration_dir/nginx.conf > /dev/null",
|
||||||
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
"echo '{$dockercompose_base64}' | base64 -d | tee $configuration_dir/docker-compose.yaml > /dev/null",
|
||||||
"docker compose --project-directory {$configuration_dir} pull",
|
"docker compose --project-directory {$configuration_dir} pull",
|
||||||
"docker compose --project-directory {$configuration_dir} up --build -d",
|
"docker compose --project-directory {$configuration_dir} up --build -d",
|
||||||
], $server);
|
], $server);
|
||||||
|
|||||||
164
app/Actions/Database/StartDragonfly.php
Normal file
164
app/Actions/Database/StartDragonfly.php
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class StartDragonfly
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public StandaloneDragonfly $database;
|
||||||
|
|
||||||
|
public array $commands = [];
|
||||||
|
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
public function handle(StandaloneDragonfly $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
$startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}";
|
||||||
|
|
||||||
|
$container_name = $this->database->uuid;
|
||||||
|
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||||
|
|
||||||
|
$this->commands = [
|
||||||
|
"echo 'Starting {$database->name}.'",
|
||||||
|
"mkdir -p $this->configuration_dir",
|
||||||
|
];
|
||||||
|
|
||||||
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||||
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
|
$environment_variables = $this->generate_environment_variables();
|
||||||
|
|
||||||
|
$docker_compose = [
|
||||||
|
'services' => [
|
||||||
|
$container_name => [
|
||||||
|
'image' => $this->database->image,
|
||||||
|
'command' => $startCommand,
|
||||||
|
'container_name' => $container_name,
|
||||||
|
'environment' => $environment_variables,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network,
|
||||||
|
],
|
||||||
|
'ulimits' => [
|
||||||
|
'memlock' => '-1',
|
||||||
|
],
|
||||||
|
'labels' => [
|
||||||
|
'coolify.managed' => 'true',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => "redis-cli -a {$this->database->dragonfly_password} ping",
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 10,
|
||||||
|
'start_period' => '5s',
|
||||||
|
],
|
||||||
|
'mem_limit' => $this->database->limits_memory,
|
||||||
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $this->database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
if (! is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
|
'fluentd-async' => 'true',
|
||||||
|
'fluentd-sub-second-precision' => 'true',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
|
}
|
||||||
|
if (count($persistent_storages) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
|
}
|
||||||
|
if (count($persistent_file_volumes) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||||
|
return "$item->fs_path:$item->mount_path";
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
|
if (count($volume_names) > 0) {
|
||||||
|
$docker_compose['volumes'] = $volume_names;
|
||||||
|
}
|
||||||
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||||
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
|
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $local_persistent_volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes_only_volume_names()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes_names = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes_names[$name] = [
|
||||||
|
'name' => $name,
|
||||||
|
'external' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $local_persistent_volumes_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_environment_variables()
|
||||||
|
{
|
||||||
|
$environment_variables = collect();
|
||||||
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||||
|
$environment_variables->push("REDIS_PASSWORD={$this->database->dragonfly_password}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $environment_variables->all();
|
||||||
|
}
|
||||||
|
}
|
||||||
184
app/Actions/Database/StartKeydb.php
Normal file
184
app/Actions/Database/StartKeydb.php
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class StartKeydb
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public StandaloneKeydb $database;
|
||||||
|
|
||||||
|
public array $commands = [];
|
||||||
|
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
public function handle(StandaloneKeydb $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
$startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes";
|
||||||
|
|
||||||
|
$container_name = $this->database->uuid;
|
||||||
|
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||||
|
|
||||||
|
$this->commands = [
|
||||||
|
"echo 'Starting {$database->name}.'",
|
||||||
|
"mkdir -p $this->configuration_dir",
|
||||||
|
];
|
||||||
|
|
||||||
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||||
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
|
$environment_variables = $this->generate_environment_variables();
|
||||||
|
$this->add_custom_keydb();
|
||||||
|
|
||||||
|
$docker_compose = [
|
||||||
|
'services' => [
|
||||||
|
$container_name => [
|
||||||
|
'image' => $this->database->image,
|
||||||
|
'command' => $startCommand,
|
||||||
|
'container_name' => $container_name,
|
||||||
|
'environment' => $environment_variables,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network,
|
||||||
|
],
|
||||||
|
'labels' => [
|
||||||
|
'coolify.managed' => 'true',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => "keydb-cli --pass {$this->database->keydb_password} ping",
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 10,
|
||||||
|
'start_period' => '5s',
|
||||||
|
],
|
||||||
|
'mem_limit' => $this->database->limits_memory,
|
||||||
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $this->database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
if (! is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
|
'driver' => 'fluentd',
|
||||||
|
'options' => [
|
||||||
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
|
'fluentd-async' => 'true',
|
||||||
|
'fluentd-sub-second-precision' => 'true',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
|
}
|
||||||
|
if (count($persistent_storages) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
|
}
|
||||||
|
if (count($persistent_file_volumes) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||||
|
return "$item->fs_path:$item->mount_path";
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
|
if (count($volume_names) > 0) {
|
||||||
|
$docker_compose['volumes'] = $volume_names;
|
||||||
|
}
|
||||||
|
if (! is_null($this->database->keydb_conf) || ! empty($this->database->keydb_conf)) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
|
'type' => 'bind',
|
||||||
|
'source' => $this->configuration_dir.'/keydb.conf',
|
||||||
|
'target' => '/etc/keydb/keydb.conf',
|
||||||
|
'read_only' => true,
|
||||||
|
];
|
||||||
|
$docker_compose['services'][$container_name]['command'] = "keydb-server /etc/keydb/keydb.conf --requirepass {$this->database->keydb_password} --appendonly yes";
|
||||||
|
}
|
||||||
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||||
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
|
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $local_persistent_volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes_only_volume_names()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes_names = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes_names[$name] = [
|
||||||
|
'name' => $name,
|
||||||
|
'external' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $local_persistent_volumes_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_environment_variables()
|
||||||
|
{
|
||||||
|
$environment_variables = collect();
|
||||||
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||||
|
$environment_variables->push("REDIS_PASSWORD={$this->database->keydb_password}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $environment_variables->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function add_custom_keydb()
|
||||||
|
{
|
||||||
|
if (is_null($this->database->keydb_conf) || empty($this->database->keydb_conf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$filename = 'keydb.conf';
|
||||||
|
Storage::disk('local')->put("tmp/keydb.conf_{$this->database->uuid}", $this->database->keydb_conf);
|
||||||
|
$path = Storage::path("tmp/keydb.conf_{$this->database->uuid}");
|
||||||
|
instant_scp($path, "{$this->configuration_dir}/{$filename}", $this->database->destination->server);
|
||||||
|
Storage::disk('local')->delete("tmp/keydb.conf_{$this->database->uuid}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,15 +4,17 @@ namespace App\Actions\Database;
|
|||||||
|
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartMariadb
|
class StartMariadb
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandaloneMariadb $database;
|
public StandaloneMariadb $database;
|
||||||
|
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
|
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function handle(StandaloneMariadb $database)
|
public function handle(StandaloneMariadb $database)
|
||||||
@@ -20,7 +22,7 @@ class StartMariadb
|
|||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
@@ -28,11 +30,11 @@ class StartMariadb
|
|||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->add_custom_mysql();
|
$this->add_custom_mysql();
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@@ -46,11 +48,11 @@ class StartMariadb
|
|||||||
'coolify.managed' => 'true',
|
'coolify.managed' => 'true',
|
||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"],
|
'test' => ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized'],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s'
|
'start_period' => '5s',
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@@ -58,27 +60,27 @@ class StartMariadb
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
if (!is_null($this->database->limits_cpuset)) {
|
if (! is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $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',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => "tcp://127.0.0.1:24224",
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
'fluentd-async' => "true",
|
'fluentd-async' => 'true',
|
||||||
'fluentd-sub-second-precision' => "true",
|
'fluentd-sub-second-precision' => 'true',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@@ -87,26 +89,32 @@ class StartMariadb
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
|
if (count($persistent_file_volumes) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||||
|
return "$item->fs_path:$item->mount_path";
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
if (!is_null($this->database->mariadb_conf)) {
|
if (! is_null($this->database->mariadb_conf) || ! empty($this->database->mariadb_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
'source' => $this->configuration_dir.'/custom-config.cnf',
|
||||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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 started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,9 +122,14 @@ class StartMariadb
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +146,7 @@ class StartMariadb
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,16 +171,18 @@ class StartMariadb
|
|||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
|
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_mysql()
|
private function add_custom_mysql()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->mariadb_conf)) {
|
if (is_null($this->database->mariadb_conf) || empty($this->database->mariadb_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'custom-config.cnf';
|
$filename = 'custom-config.cnf';
|
||||||
$content = $this->database->mariadb_conf;
|
$content = $this->database->mariadb_conf;
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,25 +4,27 @@ namespace App\Actions\Database;
|
|||||||
|
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartMongodb
|
class StartMongodb
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandaloneMongodb $database;
|
public StandaloneMongodb $database;
|
||||||
|
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
|
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function handle(StandaloneMongodb $database)
|
public function handle(StandaloneMongodb $database)
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
|
|
||||||
$startCommand = "mongod";
|
$startCommand = 'mongod';
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
@@ -30,12 +32,12 @@ class StartMongodb
|
|||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->add_custom_mongo_conf();
|
$this->add_custom_mongo_conf();
|
||||||
|
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@@ -51,13 +53,14 @@ class StartMongodb
|
|||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => [
|
'test' => [
|
||||||
'CMD-SHELL',
|
'CMD',
|
||||||
'mongosh --eval "printjson(db.runCommand(\"ping\"))"'
|
'echo',
|
||||||
|
'ok',
|
||||||
],
|
],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s'
|
'start_period' => '5s',
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@@ -65,27 +68,27 @@ class StartMongodb
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
if (!is_null($this->database->limits_cpuset)) {
|
if (! is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $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',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => "tcp://127.0.0.1:24224",
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
'fluentd-async' => "true",
|
'fluentd-async' => 'true',
|
||||||
'fluentd-sub-second-precision' => "true",
|
'fluentd-sub-second-precision' => 'true',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@@ -94,35 +97,41 @@ class StartMongodb
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
|
if (count($persistent_file_volumes) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||||
|
return "$item->fs_path:$item->mount_path";
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
if (!is_null($this->database->mongo_conf)) {
|
if (! is_null($this->database->mongo_conf) || ! empty($this->database->mongo_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir . '/mongod.conf',
|
'source' => $this->configuration_dir.'/mongod.conf',
|
||||||
'target' => '/etc/mongo/mongod.conf',
|
'target' => '/etc/mongo/mongod.conf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
$docker_compose['services'][$container_name]['command'] = $startCommand . ' --config /etc/mongo/mongod.conf';
|
$docker_compose['services'][$container_name]['command'] = $startCommand.' --config /etc/mongo/mongod.conf';
|
||||||
}
|
}
|
||||||
$this->add_default_database();
|
$this->add_default_database();
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d',
|
'source' => $this->configuration_dir.'/docker-entrypoint-initdb.d',
|
||||||
'target' => '/docker-entrypoint-initdb.d',
|
'target' => '/docker-entrypoint-initdb.d',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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 started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,9 +139,14 @@ class StartMongodb
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +163,7 @@ class StartMongodb
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,23 +185,26 @@ class StartMongodb
|
|||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
|
||||||
$environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}");
|
$environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_mongo_conf()
|
private function add_custom_mongo_conf()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->mongo_conf)) {
|
if (is_null($this->database->mongo_conf) || empty($this->database->mongo_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'mongod.conf';
|
$filename = 'mongod.conf';
|
||||||
$content = $this->database->mongo_conf;
|
$content = $this->database->mongo_conf;
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_default_database()
|
private function add_default_database()
|
||||||
{
|
{
|
||||||
$content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
|
$content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
|
$this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js > /dev/null";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ namespace App\Actions\Database;
|
|||||||
|
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartMysql
|
class StartMysql
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandaloneMysql $database;
|
public StandaloneMysql $database;
|
||||||
|
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
|
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function handle(StandaloneMysql $database)
|
public function handle(StandaloneMysql $database)
|
||||||
@@ -20,7 +22,7 @@ class StartMysql
|
|||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
@@ -28,11 +30,11 @@ class StartMysql
|
|||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->add_custom_mysql();
|
$this->add_custom_mysql();
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@@ -46,11 +48,11 @@ class StartMysql
|
|||||||
'coolify.managed' => 'true',
|
'coolify.managed' => 'true',
|
||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p{$this->database->mysql_root_password}"],
|
'test' => ['CMD', 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', "-p{$this->database->mysql_root_password}"],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s'
|
'start_period' => '5s',
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@@ -58,27 +60,27 @@ class StartMysql
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
if (!is_null($this->database->limits_cpuset)) {
|
if (! is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $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',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => "tcp://127.0.0.1:24224",
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
'fluentd-async' => "true",
|
'fluentd-async' => 'true',
|
||||||
'fluentd-sub-second-precision' => "true",
|
'fluentd-sub-second-precision' => 'true',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@@ -87,36 +89,47 @@ class StartMysql
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
|
if (count($persistent_file_volumes) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||||
|
return "$item->fs_path:$item->mount_path";
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
if (!is_null($this->database->mysql_conf)) {
|
if (! is_null($this->database->mysql_conf) || ! empty($this->database->mysql_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
'source' => $this->configuration_dir.'/custom-config.cnf',
|
||||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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 started.'";
|
$this->commands[] = "echo 'Database 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()
|
||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +146,7 @@ class StartMysql
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,16 +171,18 @@ class StartMysql
|
|||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
|
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_mysql()
|
private function add_custom_mysql()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->mysql_conf)) {
|
if (is_null($this->database->mysql_conf) || empty($this->database->mysql_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'custom-config.cnf';
|
$filename = 'custom-config.cnf';
|
||||||
$content = $this->database->mysql_conf;
|
$content = $this->database->mysql_conf;
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,38 +4,41 @@ namespace App\Actions\Database;
|
|||||||
|
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartPostgresql
|
class StartPostgresql
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandalonePostgresql $database;
|
public StandalonePostgresql $database;
|
||||||
|
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
|
|
||||||
public array $init_scripts = [];
|
public array $init_scripts = [];
|
||||||
|
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function handle(StandalonePostgresql $database)
|
public function handle(StandalonePostgresql $database)
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
"mkdir -p $this->configuration_dir",
|
"mkdir -p $this->configuration_dir",
|
||||||
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/"
|
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/",
|
||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->generate_init_scripts();
|
$this->generate_init_scripts();
|
||||||
$this->add_custom_conf();
|
$this->add_custom_conf();
|
||||||
|
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@@ -50,13 +53,13 @@ class StartPostgresql
|
|||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => [
|
'test' => [
|
||||||
"CMD-SHELL",
|
'CMD-SHELL',
|
||||||
"psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1"
|
"psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1",
|
||||||
],
|
],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s'
|
'start_period' => '5s',
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@@ -64,28 +67,27 @@ class StartPostgresql
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
if (!is_null($this->database->limits_cpuset)) {
|
if (! is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $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');
|
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => "tcp://127.0.0.1:24224",
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
'fluentd-async' => "true",
|
'fluentd-async' => 'true',
|
||||||
'fluentd-sub-second-precision' => "true",
|
'fluentd-sub-second-precision' => 'true',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@@ -94,6 +96,11 @@ class StartPostgresql
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
|
if (count($persistent_file_volumes) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||||
|
return "$item->fs_path:$item->mount_path";
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
@@ -102,15 +109,15 @@ class StartPostgresql
|
|||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $init_script,
|
'source' => $init_script,
|
||||||
'target' => '/docker-entrypoint-initdb.d/' . basename($init_script),
|
'target' => '/docker-entrypoint-initdb.d/'.basename($init_script),
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!is_null($this->database->postgres_conf)) {
|
if (! is_null($this->database->postgres_conf) && ! empty($this->database->postgres_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir . '/custom-postgres.conf',
|
'source' => $this->configuration_dir.'/custom-postgres.conf',
|
||||||
'target' => '/etc/postgresql/postgresql.conf',
|
'target' => '/etc/postgresql/postgresql.conf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
@@ -122,13 +129,14 @@ class StartPostgresql
|
|||||||
}
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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 started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,9 +144,14 @@ class StartPostgresql
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,14 +168,13 @@ class StartPostgresql
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_environment_variables()
|
private function generate_environment_variables()
|
||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
ray('Generate 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->real_value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
@@ -181,6 +193,7 @@ class StartPostgresql
|
|||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_DB'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_DB'))->isEmpty()) {
|
||||||
$environment_variables->push("POSTGRES_DB={$this->database->postgres_db}");
|
$environment_variables->push("POSTGRES_DB={$this->database->postgres_db}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,18 +206,24 @@ class StartPostgresql
|
|||||||
$filename = data_get($init_script, 'filename');
|
$filename = data_get($init_script, 'filename');
|
||||||
$content = data_get($init_script, 'content');
|
$content = data_get($init_script, 'content');
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/{$filename} > /dev/null";
|
||||||
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_conf()
|
private function add_custom_conf()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->postgres_conf)) {
|
if (is_null($this->database->postgres_conf) || empty($this->database->postgres_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'custom-postgres.conf';
|
$filename = 'custom-postgres.conf';
|
||||||
$content = $this->database->postgres_conf;
|
$content = $this->database->postgres_conf;
|
||||||
|
if (! str($content)->contains('listen_addresses')) {
|
||||||
|
$content .= "\nlisten_addresses = '*'";
|
||||||
|
$this->database->postgres_conf = $content;
|
||||||
|
$this->database->save();
|
||||||
|
}
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,18 @@ namespace App\Actions\Database;
|
|||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartRedis
|
class StartRedis
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandaloneRedis $database;
|
public StandaloneRedis $database;
|
||||||
public array $commands = [];
|
|
||||||
public string $configuration_dir;
|
|
||||||
|
|
||||||
|
public array $commands = [];
|
||||||
|
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function handle(StandaloneRedis $database)
|
public function handle(StandaloneRedis $database)
|
||||||
{
|
{
|
||||||
@@ -24,7 +25,7 @@ class StartRedis
|
|||||||
$startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
|
$startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
@@ -32,12 +33,12 @@ class StartRedis
|
|||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$persistent_file_volumes = $this->database->fileStorages()->get();
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->add_custom_redis();
|
$this->add_custom_redis();
|
||||||
|
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@@ -55,12 +56,12 @@ class StartRedis
|
|||||||
'test' => [
|
'test' => [
|
||||||
'CMD-SHELL',
|
'CMD-SHELL',
|
||||||
'redis-cli',
|
'redis-cli',
|
||||||
'ping'
|
'ping',
|
||||||
],
|
],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s'
|
'start_period' => '5s',
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@@ -68,27 +69,27 @@ class StartRedis
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
]
|
],
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
if (!is_null($this->database->limits_cpuset)) {
|
if (! is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $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',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => "tcp://127.0.0.1:24224",
|
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
||||||
'fluentd-async' => "true",
|
'fluentd-async' => 'true',
|
||||||
'fluentd-sub-second-precision' => "true",
|
'fluentd-sub-second-precision' => 'true',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@@ -97,13 +98,18 @@ class StartRedis
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
|
if (count($persistent_file_volumes) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
||||||
|
return "$item->fs_path:$item->mount_path";
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
if (!is_null($this->database->redis_conf)) {
|
if (! is_null($this->database->redis_conf) || ! empty($this->database->redis_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir . '/redis.conf',
|
'source' => $this->configuration_dir.'/redis.conf',
|
||||||
'target' => '/usr/local/etc/redis/redis.conf',
|
'target' => '/usr/local/etc/redis/redis.conf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
@@ -111,13 +117,14 @@ class StartRedis
|
|||||||
}
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml 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 started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,9 +132,14 @@ class StartRedis
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
||||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
||||||
|
} else {
|
||||||
|
$volume_name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +156,7 @@ class StartRedis
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,9 +173,10 @@ class StartRedis
|
|||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_redis()
|
private function add_custom_redis()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->redis_conf)) {
|
if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'redis.conf';
|
$filename = 'redis.conf';
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Events\DatabaseStatusChanged;
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -14,10 +16,10 @@ class StopDatabase
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
|
||||||
{
|
{
|
||||||
$server = $database->destination->server;
|
$server = $database->destination->server;
|
||||||
if (!$server->isFunctional()) {
|
if (! $server->isFunctional()) {
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
instant_remote_process(
|
instant_remote_process(
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Events\DatabaseStatusChanged;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -14,7 +18,7 @@ class StopDatabaseProxy
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|ServiceDatabase|StandaloneDragonfly|StandaloneClickhouse $database)
|
||||||
{
|
{
|
||||||
$server = data_get($database, 'destination.server');
|
$server = data_get($database, 'destination.server');
|
||||||
$uuid = $database->uuid;
|
$uuid = $database->uuid;
|
||||||
@@ -25,5 +29,6 @@ class StopDatabaseProxy
|
|||||||
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
|
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
|
||||||
$database->is_public = false;
|
$database->is_public = false;
|
||||||
$database->save();
|
$database->save();
|
||||||
|
DatabaseStatusChanged::dispatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
686
app/Actions/Docker/GetContainersStatus.php
Normal file
686
app/Actions/Docker/GetContainersStatus.php
Normal file
@@ -0,0 +1,686 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Docker;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
|
use App\Actions\Proxy\CheckProxy;
|
||||||
|
use App\Actions\Proxy\StartProxy;
|
||||||
|
use App\Actions\Shared\ComplexStatusCheck;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Notifications\Container\ContainerRestarted;
|
||||||
|
use App\Notifications\Container\ContainerStopped;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class GetContainersStatus
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public $applications;
|
||||||
|
|
||||||
|
public $server;
|
||||||
|
|
||||||
|
public function handle(Server $server)
|
||||||
|
{
|
||||||
|
// if (isDev()) {
|
||||||
|
// $server = Server::find(0);
|
||||||
|
// }
|
||||||
|
$this->server = $server;
|
||||||
|
if (! $this->server->isFunctional()) {
|
||||||
|
return 'Server is not ready.';
|
||||||
|
}
|
||||||
|
$this->applications = $this->server->applications();
|
||||||
|
$skip_these_applications = collect([]);
|
||||||
|
foreach ($this->applications as $application) {
|
||||||
|
if ($application->additional_servers->count() > 0) {
|
||||||
|
$skip_these_applications->push($application);
|
||||||
|
ComplexStatusCheck::run($application);
|
||||||
|
$this->applications = $this->applications->filter(function ($value, $key) use ($application) {
|
||||||
|
return $value->id !== $application->id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->applications = $this->applications->filter(function ($value, $key) use ($skip_these_applications) {
|
||||||
|
return ! $skip_these_applications->pluck('id')->contains($value->id);
|
||||||
|
});
|
||||||
|
$this->old_way();
|
||||||
|
// if ($this->server->isSwarm()) {
|
||||||
|
// $this->old_way();
|
||||||
|
// } else {
|
||||||
|
// if (!$this->server->is_metrics_enabled) {
|
||||||
|
// $this->old_way();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this->server, false);
|
||||||
|
// $sentinel_found = json_decode($sentinel_found, true);
|
||||||
|
// $status = data_get($sentinel_found, '0.State.Status', 'exited');
|
||||||
|
// if ($status === 'running') {
|
||||||
|
// ray('Checking with Sentinel');
|
||||||
|
// $this->sentinel();
|
||||||
|
// } else {
|
||||||
|
// ray('Checking the Old way');
|
||||||
|
// $this->old_way();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sentinel()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$containers = $this->server->getContainers();
|
||||||
|
if ($containers->count() === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$databases = $this->server->databases();
|
||||||
|
$services = $this->server->services()->get();
|
||||||
|
$previews = $this->server->previews();
|
||||||
|
$foundApplications = [];
|
||||||
|
$foundApplicationPreviews = [];
|
||||||
|
$foundDatabases = [];
|
||||||
|
$foundServices = [];
|
||||||
|
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$labels = Arr::undot(data_get($container, 'labels'));
|
||||||
|
$containerStatus = data_get($container, 'state');
|
||||||
|
$containerHealth = data_get($container, 'health_status', 'unhealthy');
|
||||||
|
$containerStatus = "$containerStatus ($containerHealth)";
|
||||||
|
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||||
|
if ($applicationId) {
|
||||||
|
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||||
|
if ($pullRequestId) {
|
||||||
|
if (str($applicationId)->contains('-')) {
|
||||||
|
$applicationId = str($applicationId)->before('-');
|
||||||
|
}
|
||||||
|
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||||
|
if ($preview) {
|
||||||
|
$foundApplicationPreviews[] = $preview->id;
|
||||||
|
$statusFromDb = $preview->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$preview->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Notify user that this container should not be there.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$application = $this->applications->where('id', $applicationId)->first();
|
||||||
|
if ($application) {
|
||||||
|
$foundApplications[] = $application->id;
|
||||||
|
$statusFromDb = $application->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$application->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Notify user that this container should not be there.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||||
|
$type = data_get($labels, 'coolify.type');
|
||||||
|
if ($uuid) {
|
||||||
|
if ($type === 'service') {
|
||||||
|
$database_id = data_get($labels, 'coolify.service.subId');
|
||||||
|
if ($database_id) {
|
||||||
|
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
||||||
|
if ($service_db) {
|
||||||
|
$uuid = $service_db->service->uuid;
|
||||||
|
$isPublic = data_get($service_db, 'is_public');
|
||||||
|
if ($isPublic) {
|
||||||
|
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
// TODO: fix this with sentinel
|
||||||
|
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||||
|
} else {
|
||||||
|
return data_get($value, 'name') === "$uuid-proxy";
|
||||||
|
}
|
||||||
|
})->first();
|
||||||
|
if (! $foundTcpProxy) {
|
||||||
|
StartDatabaseProxy::run($service_db);
|
||||||
|
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$database = $databases->where('uuid', $uuid)->first();
|
||||||
|
if ($database) {
|
||||||
|
$isPublic = data_get($database, 'is_public');
|
||||||
|
$foundDatabases[] = $database->id;
|
||||||
|
$statusFromDb = $database->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$database->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
if ($isPublic) {
|
||||||
|
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
// TODO: fix this with sentinel
|
||||||
|
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||||
|
} else {
|
||||||
|
return data_get($value, 'name') === "$uuid-proxy";
|
||||||
|
}
|
||||||
|
})->first();
|
||||||
|
if (! $foundTcpProxy) {
|
||||||
|
StartDatabaseProxy::run($database);
|
||||||
|
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Notify user that this container should not be there.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data_get($container, 'name') === 'coolify-db') {
|
||||||
|
$foundDatabases[] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
||||||
|
if ($serviceLabelId) {
|
||||||
|
$subType = data_get($labels, 'coolify.service.subType');
|
||||||
|
$subId = data_get($labels, 'coolify.service.subId');
|
||||||
|
$service = $services->where('id', $serviceLabelId)->first();
|
||||||
|
if (! $service) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($subType === 'application') {
|
||||||
|
$service = $service->applications()->where('id', $subId)->first();
|
||||||
|
} else {
|
||||||
|
$service = $service->databases()->where('id', $subId)->first();
|
||||||
|
}
|
||||||
|
if ($service) {
|
||||||
|
$foundServices[] = "$service->id-$service->name";
|
||||||
|
$statusFromDb = $service->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
// ray('Updating status: ' . $containerStatus);
|
||||||
|
$service->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$exitedServices = collect([]);
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$apps = $service->applications()->get();
|
||||||
|
$dbs = $service->databases()->get();
|
||||||
|
foreach ($apps as $app) {
|
||||||
|
if (in_array("$app->id-$app->name", $foundServices)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$exitedServices->push($app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($dbs as $db) {
|
||||||
|
if (in_array("$db->id-$db->name", $foundServices)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$exitedServices->push($db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$exitedServices = $exitedServices->unique('id');
|
||||||
|
foreach ($exitedServices as $exitedService) {
|
||||||
|
if (str($exitedService->status)->startsWith('exited')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = data_get($exitedService, 'name');
|
||||||
|
$fqdn = data_get($exitedService, 'fqdn');
|
||||||
|
if ($name) {
|
||||||
|
if ($fqdn) {
|
||||||
|
$containerName = "$name, available at $fqdn";
|
||||||
|
} else {
|
||||||
|
$containerName = $name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($fqdn) {
|
||||||
|
$containerName = $fqdn;
|
||||||
|
} else {
|
||||||
|
$containerName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$projectUuid = data_get($service, 'environment.project.uuid');
|
||||||
|
$serviceUuid = data_get($service, 'uuid');
|
||||||
|
$environmentName = data_get($service, 'environment.name');
|
||||||
|
|
||||||
|
if ($projectUuid && $serviceUuid && $environmentName) {
|
||||||
|
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
|
}
|
||||||
|
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
$exitedService->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
|
||||||
|
foreach ($notRunningApplications as $applicationId) {
|
||||||
|
$application = $this->applications->where('id', $applicationId)->first();
|
||||||
|
if (str($application->status)->startsWith('exited')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$application->update(['status' => 'exited']);
|
||||||
|
|
||||||
|
$name = data_get($application, 'name');
|
||||||
|
$fqdn = data_get($application, 'fqdn');
|
||||||
|
|
||||||
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
|
||||||
|
$projectUuid = data_get($application, 'environment.project.uuid');
|
||||||
|
$applicationUuid = data_get($application, 'uuid');
|
||||||
|
$environment = data_get($application, 'environment.name');
|
||||||
|
|
||||||
|
if ($projectUuid && $applicationUuid && $environment) {
|
||||||
|
$url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
||||||
|
foreach ($notRunningApplicationPreviews as $previewId) {
|
||||||
|
$preview = $previews->where('id', $previewId)->first();
|
||||||
|
if (str($preview->status)->startsWith('exited')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$preview->update(['status' => 'exited']);
|
||||||
|
|
||||||
|
$name = data_get($preview, 'name');
|
||||||
|
$fqdn = data_get($preview, 'fqdn');
|
||||||
|
|
||||||
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
|
||||||
|
$projectUuid = data_get($preview, 'application.environment.project.uuid');
|
||||||
|
$environmentName = data_get($preview, 'application.environment.name');
|
||||||
|
$applicationUuid = data_get($preview, 'application.uuid');
|
||||||
|
|
||||||
|
if ($projectUuid && $applicationUuid && $environmentName) {
|
||||||
|
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
||||||
|
foreach ($notRunningDatabases as $database) {
|
||||||
|
$database = $databases->where('id', $database)->first();
|
||||||
|
if (str($database->status)->startsWith('exited')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$database->update(['status' => 'exited']);
|
||||||
|
|
||||||
|
$name = data_get($database, 'name');
|
||||||
|
$fqdn = data_get($database, 'fqdn');
|
||||||
|
|
||||||
|
$containerName = $name;
|
||||||
|
|
||||||
|
$projectUuid = data_get($database, 'environment.project.uuid');
|
||||||
|
$environmentName = data_get($database, 'environment.name');
|
||||||
|
$databaseUuid = data_get($database, 'uuid');
|
||||||
|
|
||||||
|
if ($projectUuid && $databaseUuid && $environmentName) {
|
||||||
|
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
|
}
|
||||||
|
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if proxy is running
|
||||||
|
$this->server->proxyType();
|
||||||
|
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
// TODO: fix this with sentinel
|
||||||
|
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||||
|
} else {
|
||||||
|
return data_get($value, 'name') === 'coolify-proxy';
|
||||||
|
}
|
||||||
|
})->first();
|
||||||
|
if (! $foundProxyContainer) {
|
||||||
|
try {
|
||||||
|
$shouldStart = CheckProxy::run($this->server);
|
||||||
|
if ($shouldStart) {
|
||||||
|
StartProxy::run($this->server, false);
|
||||||
|
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->server->proxy->status = data_get($foundProxyContainer, 'state');
|
||||||
|
$this->server->save();
|
||||||
|
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||||
|
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function old_way()
|
||||||
|
{
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
||||||
|
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
||||||
|
} else {
|
||||||
|
// Precheck for containers
|
||||||
|
$containers = instant_remote_process(['docker container ls -q'], $this->server, false);
|
||||||
|
if (! $containers) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
||||||
|
$containerReplicates = null;
|
||||||
|
}
|
||||||
|
if (is_null($containers)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
|
if ($containerReplicates) {
|
||||||
|
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
|
||||||
|
foreach ($containerReplicates as $containerReplica) {
|
||||||
|
$name = data_get($containerReplica, 'Name');
|
||||||
|
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
||||||
|
if (data_get($container, 'Spec.Name') === $name) {
|
||||||
|
$replicas = data_get($containerReplica, 'Replicas');
|
||||||
|
$running = str($replicas)->explode('/')[0];
|
||||||
|
$total = str($replicas)->explode('/')[1];
|
||||||
|
if ($running === $total) {
|
||||||
|
data_set($container, 'State.Status', 'running');
|
||||||
|
data_set($container, 'State.Health.Status', 'healthy');
|
||||||
|
} else {
|
||||||
|
data_set($container, 'State.Status', 'starting');
|
||||||
|
data_set($container, 'State.Health.Status', 'unhealthy');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $container;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$databases = $this->server->databases();
|
||||||
|
$services = $this->server->services()->get();
|
||||||
|
$previews = $this->server->previews();
|
||||||
|
$foundApplications = [];
|
||||||
|
$foundApplicationPreviews = [];
|
||||||
|
$foundDatabases = [];
|
||||||
|
$foundServices = [];
|
||||||
|
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
$labels = data_get($container, 'Spec.Labels');
|
||||||
|
$uuid = data_get($labels, 'coolify.name');
|
||||||
|
} else {
|
||||||
|
$labels = data_get($container, 'Config.Labels');
|
||||||
|
}
|
||||||
|
$containerStatus = data_get($container, 'State.Status');
|
||||||
|
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||||
|
$containerStatus = "$containerStatus ($containerHealth)";
|
||||||
|
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||||
|
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||||
|
if ($applicationId) {
|
||||||
|
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||||
|
if ($pullRequestId) {
|
||||||
|
if (str($applicationId)->contains('-')) {
|
||||||
|
$applicationId = str($applicationId)->before('-');
|
||||||
|
}
|
||||||
|
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||||
|
if ($preview) {
|
||||||
|
$foundApplicationPreviews[] = $preview->id;
|
||||||
|
$statusFromDb = $preview->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$preview->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Notify user that this container should not be there.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$application = $this->applications->where('id', $applicationId)->first();
|
||||||
|
if ($application) {
|
||||||
|
$foundApplications[] = $application->id;
|
||||||
|
$statusFromDb = $application->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$application->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Notify user that this container should not be there.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||||
|
$type = data_get($labels, 'coolify.type');
|
||||||
|
|
||||||
|
if ($uuid) {
|
||||||
|
if ($type === 'service') {
|
||||||
|
$database_id = data_get($labels, 'coolify.service.subId');
|
||||||
|
if ($database_id) {
|
||||||
|
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
||||||
|
if ($service_db) {
|
||||||
|
$uuid = data_get($service_db, 'service.uuid');
|
||||||
|
if ($uuid) {
|
||||||
|
$isPublic = data_get($service_db, 'is_public');
|
||||||
|
if ($isPublic) {
|
||||||
|
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||||
|
} else {
|
||||||
|
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||||
|
}
|
||||||
|
})->first();
|
||||||
|
if (! $foundTcpProxy) {
|
||||||
|
StartDatabaseProxy::run($service_db);
|
||||||
|
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$database = $databases->where('uuid', $uuid)->first();
|
||||||
|
if ($database) {
|
||||||
|
$isPublic = data_get($database, 'is_public');
|
||||||
|
$foundDatabases[] = $database->id;
|
||||||
|
$statusFromDb = $database->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$database->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
if ($isPublic) {
|
||||||
|
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||||
|
} else {
|
||||||
|
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||||
|
}
|
||||||
|
})->first();
|
||||||
|
if (! $foundTcpProxy) {
|
||||||
|
StartDatabaseProxy::run($database);
|
||||||
|
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Notify user that this container should not be there.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data_get($container, 'Name') === '/coolify-db') {
|
||||||
|
$foundDatabases[] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
||||||
|
if ($serviceLabelId) {
|
||||||
|
$subType = data_get($labels, 'coolify.service.subType');
|
||||||
|
$subId = data_get($labels, 'coolify.service.subId');
|
||||||
|
$service = $services->where('id', $serviceLabelId)->first();
|
||||||
|
if (! $service) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($subType === 'application') {
|
||||||
|
$service = $service->applications()->where('id', $subId)->first();
|
||||||
|
} else {
|
||||||
|
$service = $service->databases()->where('id', $subId)->first();
|
||||||
|
}
|
||||||
|
if ($service) {
|
||||||
|
$foundServices[] = "$service->id-$service->name";
|
||||||
|
$statusFromDb = $service->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
// ray('Updating status: ' . $containerStatus);
|
||||||
|
$service->update(['status' => $containerStatus]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$exitedServices = collect([]);
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$apps = $service->applications()->get();
|
||||||
|
$dbs = $service->databases()->get();
|
||||||
|
foreach ($apps as $app) {
|
||||||
|
if (in_array("$app->id-$app->name", $foundServices)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$exitedServices->push($app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($dbs as $db) {
|
||||||
|
if (in_array("$db->id-$db->name", $foundServices)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$exitedServices->push($db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$exitedServices = $exitedServices->unique('id');
|
||||||
|
foreach ($exitedServices as $exitedService) {
|
||||||
|
if (str($exitedService->status)->startsWith('exited')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = data_get($exitedService, 'name');
|
||||||
|
$fqdn = data_get($exitedService, 'fqdn');
|
||||||
|
if ($name) {
|
||||||
|
if ($fqdn) {
|
||||||
|
$containerName = "$name, available at $fqdn";
|
||||||
|
} else {
|
||||||
|
$containerName = $name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($fqdn) {
|
||||||
|
$containerName = $fqdn;
|
||||||
|
} else {
|
||||||
|
$containerName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$projectUuid = data_get($service, 'environment.project.uuid');
|
||||||
|
$serviceUuid = data_get($service, 'uuid');
|
||||||
|
$environmentName = data_get($service, 'environment.name');
|
||||||
|
|
||||||
|
if ($projectUuid && $serviceUuid && $environmentName) {
|
||||||
|
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
|
}
|
||||||
|
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
$exitedService->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
|
||||||
|
foreach ($notRunningApplications as $applicationId) {
|
||||||
|
$application = $this->applications->where('id', $applicationId)->first();
|
||||||
|
if (str($application->status)->startsWith('exited')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$application->update(['status' => 'exited']);
|
||||||
|
|
||||||
|
$name = data_get($application, 'name');
|
||||||
|
$fqdn = data_get($application, 'fqdn');
|
||||||
|
|
||||||
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
|
||||||
|
$projectUuid = data_get($application, 'environment.project.uuid');
|
||||||
|
$applicationUuid = data_get($application, 'uuid');
|
||||||
|
$environment = data_get($application, 'environment.name');
|
||||||
|
|
||||||
|
if ($projectUuid && $applicationUuid && $environment) {
|
||||||
|
$url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
||||||
|
foreach ($notRunningApplicationPreviews as $previewId) {
|
||||||
|
$preview = $previews->where('id', $previewId)->first();
|
||||||
|
if (str($preview->status)->startsWith('exited')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$preview->update(['status' => 'exited']);
|
||||||
|
|
||||||
|
$name = data_get($preview, 'name');
|
||||||
|
$fqdn = data_get($preview, 'fqdn');
|
||||||
|
|
||||||
|
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
||||||
|
|
||||||
|
$projectUuid = data_get($preview, 'application.environment.project.uuid');
|
||||||
|
$environmentName = data_get($preview, 'application.environment.name');
|
||||||
|
$applicationUuid = data_get($preview, 'application.uuid');
|
||||||
|
|
||||||
|
if ($projectUuid && $applicationUuid && $environmentName) {
|
||||||
|
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
||||||
|
foreach ($notRunningDatabases as $database) {
|
||||||
|
$database = $databases->where('id', $database)->first();
|
||||||
|
if (str($database->status)->startsWith('exited')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$database->update(['status' => 'exited']);
|
||||||
|
|
||||||
|
$name = data_get($database, 'name');
|
||||||
|
$fqdn = data_get($database, 'fqdn');
|
||||||
|
|
||||||
|
$containerName = $name;
|
||||||
|
|
||||||
|
$projectUuid = data_get($database, 'environment.project.uuid');
|
||||||
|
$environmentName = data_get($database, 'environment.name');
|
||||||
|
$databaseUuid = data_get($database, 'uuid');
|
||||||
|
|
||||||
|
if ($projectUuid && $databaseUuid && $environmentName) {
|
||||||
|
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
|
||||||
|
} else {
|
||||||
|
$url = null;
|
||||||
|
}
|
||||||
|
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if proxy is running
|
||||||
|
$this->server->proxyType();
|
||||||
|
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
||||||
|
if ($this->server->isSwarm()) {
|
||||||
|
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||||
|
} else {
|
||||||
|
return data_get($value, 'Name') === '/coolify-proxy';
|
||||||
|
}
|
||||||
|
})->first();
|
||||||
|
if (! $foundProxyContainer) {
|
||||||
|
try {
|
||||||
|
$shouldStart = CheckProxy::run($this->server);
|
||||||
|
if ($shouldStart) {
|
||||||
|
StartProxy::run($this->server, false);
|
||||||
|
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||||
|
$this->server->save();
|
||||||
|
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||||
|
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,12 +16,12 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
/**
|
/**
|
||||||
* Validate and create a newly registered user.
|
* Validate and create a newly registered user.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
public function create(array $input): User
|
public function create(array $input): User
|
||||||
{
|
{
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
if (!$settings->is_registration_enabled) {
|
if (! $settings->is_registration_enabled) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
Validator::make($input, [
|
Validator::make($input, [
|
||||||
@@ -66,6 +66,7 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
}
|
}
|
||||||
// Set session variable
|
// Set session variable
|
||||||
session(['currentTeam' => $user->currentTeam = $team]);
|
session(['currentTeam' => $user->currentTeam = $team]);
|
||||||
|
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class ResetUserPassword implements ResetsUserPasswords
|
|||||||
/**
|
/**
|
||||||
* Validate and reset the user's forgotten password.
|
* Validate and reset the user's forgotten password.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
public function reset(User $user, array $input): void
|
public function reset(User $user, array $input): void
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class UpdateUserPassword implements UpdatesUserPasswords
|
|||||||
/**
|
/**
|
||||||
* Validate and update the user's password.
|
* Validate and update the user's password.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
public function update(User $user, array $input): void
|
public function update(User $user, array $input): void
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
|||||||
/**
|
/**
|
||||||
* Validate and update the given user's profile information.
|
* Validate and update the given user's profile information.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
public function update(User $user, array $input): void
|
public function update(User $user, array $input): void
|
||||||
{
|
{
|
||||||
@@ -45,7 +45,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
|||||||
/**
|
/**
|
||||||
* Update the given verified user's profile information.
|
* Update the given verified user's profile information.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
protected function updateVerifiedUser(User $user, array $input): void
|
protected function updateVerifiedUser(User $user, array $input): void
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ use App\Models\InstanceSettings;
|
|||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
|
||||||
class CheckResaleLicense
|
class CheckResaleLicense
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -18,6 +18,7 @@ class CheckResaleLicense
|
|||||||
$settings->update([
|
$settings->update([
|
||||||
'is_resale_license_active' => true,
|
'is_resale_license_active' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if (!$settings->resale_license) {
|
// if (!$settings->resale_license) {
|
||||||
@@ -38,6 +39,7 @@ class CheckResaleLicense
|
|||||||
$settings->update([
|
$settings->update([
|
||||||
'is_resale_license_active' => true,
|
'is_resale_license_active' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$data = Http::withHeaders([
|
$data = Http::withHeaders([
|
||||||
@@ -51,6 +53,7 @@ class CheckResaleLicense
|
|||||||
$settings->update([
|
$settings->update([
|
||||||
'is_resale_license_active' => true,
|
'is_resale_license_active' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data_get($data, 'license_key.status') === 'active') {
|
if (data_get($data, 'license_key.status') === 'active') {
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class CheckConfiguration
|
class CheckConfiguration
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server, bool $reset = false)
|
public function handle(Server $server, bool $reset = false)
|
||||||
{
|
{
|
||||||
$proxyType = $server->proxyType();
|
$proxyType = $server->proxyType();
|
||||||
@@ -16,18 +17,19 @@ class CheckConfiguration
|
|||||||
return 'OK';
|
return 'OK';
|
||||||
}
|
}
|
||||||
$proxy_path = $server->proxyPath();
|
$proxy_path = $server->proxyPath();
|
||||||
|
$payload = [
|
||||||
$proxy_configuration = instant_remote_process([
|
|
||||||
"mkdir -p $proxy_path",
|
"mkdir -p $proxy_path",
|
||||||
"cat $proxy_path/docker-compose.yml",
|
"cat $proxy_path/docker-compose.yml",
|
||||||
], $server, false);
|
];
|
||||||
|
$proxy_configuration = instant_remote_process($payload, $server, false);
|
||||||
|
|
||||||
if ($reset || !$proxy_configuration || is_null($proxy_configuration)) {
|
if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) {
|
||||||
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
||||||
}
|
}
|
||||||
if (!$proxy_configuration || is_null($proxy_configuration)) {
|
if (! $proxy_configuration || is_null($proxy_configuration)) {
|
||||||
throw new \Exception("Could not generate proxy configuration");
|
throw new \Exception('Could not generate proxy configuration');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $proxy_configuration;
|
return $proxy_configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,31 @@ use Lorisleiva\Actions\Concerns\AsAction;
|
|||||||
class CheckProxy
|
class CheckProxy
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server, $fromUI = false)
|
public function handle(Server $server, $fromUI = false)
|
||||||
{
|
{
|
||||||
if ($server->proxyType() === 'NONE') {
|
if (! $server->isFunctional()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!$server->validateConnection()) {
|
if ($server->isBuildServer()) {
|
||||||
throw new \Exception("Server Connection Error");
|
if ($server->proxy) {
|
||||||
|
$server->proxy = null;
|
||||||
|
$server->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (!$server->isProxyShouldRun()) {
|
$proxyType = $server->proxyType();
|
||||||
|
if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
|
||||||
|
if (! $uptime) {
|
||||||
|
throw new \Exception($error);
|
||||||
|
}
|
||||||
|
if (! $server->isProxyShouldRun()) {
|
||||||
if ($fromUI) {
|
if ($fromUI) {
|
||||||
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
throw new \Exception('Proxy should not run. You selected the Custom Proxy.');
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -30,12 +44,14 @@ class CheckProxy
|
|||||||
if ($status === 'running') {
|
if ($status === 'running') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
$status = getContainerStatus($server, 'coolify-proxy');
|
$status = getContainerStatus($server, 'coolify-proxy');
|
||||||
if ($status === 'running') {
|
if ($status === 'running') {
|
||||||
$server->proxy->set('status', 'running');
|
$server->proxy->set('status', 'running');
|
||||||
$server->save();
|
$server->save();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ($server->settings->is_cloudflare_tunnel) {
|
if ($server->settings->is_cloudflare_tunnel) {
|
||||||
@@ -64,6 +80,7 @@ class CheckProxy
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class SaveConfiguration
|
|||||||
|
|
||||||
return instant_remote_process([
|
return instant_remote_process([
|
||||||
"mkdir -p $proxy_path",
|
"mkdir -p $proxy_path",
|
||||||
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
"echo '$docker_compose_yml_base64' | base64 -d | tee $proxy_path/docker-compose.yml > /dev/null",
|
||||||
], $server);
|
], $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,18 +11,19 @@ use Spatie\Activitylog\Models\Activity;
|
|||||||
class StartProxy
|
class StartProxy
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
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();
|
||||||
if (is_null($proxyType) || $proxyType === 'NONE') {
|
if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop || $server->isBuildServer()) {
|
||||||
return 'OK';
|
return 'OK';
|
||||||
}
|
}
|
||||||
$commands = collect([]);
|
$commands = collect([]);
|
||||||
$proxy_path = $server->proxyPath();
|
$proxy_path = $server->proxyPath();
|
||||||
$configuration = CheckConfiguration::run($server);
|
$configuration = CheckConfiguration::run($server);
|
||||||
if (!$configuration) {
|
if (! $configuration) {
|
||||||
throw new \Exception("Configuration is not synced");
|
throw new \Exception('Configuration is not synced');
|
||||||
}
|
}
|
||||||
SaveConfiguration::run($server, $configuration);
|
SaveConfiguration::run($server, $configuration);
|
||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||||
@@ -30,31 +31,34 @@ class StartProxy
|
|||||||
$server->save();
|
$server->save();
|
||||||
if ($server->isSwarm()) {
|
if ($server->isSwarm()) {
|
||||||
$commands = $commands->merge([
|
$commands = $commands->merge([
|
||||||
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
"mkdir -p $proxy_path/dynamic",
|
||||||
|
"cd $proxy_path",
|
||||||
"echo 'Creating required Docker Compose file.'",
|
"echo 'Creating required Docker Compose file.'",
|
||||||
"echo 'Starting coolify-proxy.'",
|
"echo 'Starting coolify-proxy.'",
|
||||||
"cd $proxy_path && docker stack deploy -c docker-compose.yml coolify-proxy",
|
'docker stack deploy -c docker-compose.yml coolify-proxy',
|
||||||
"echo 'Proxy started successfully.'"
|
"echo 'Proxy started successfully.'",
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$caddfile = "import /dynamic/*.caddy";
|
$caddfile = 'import /dynamic/*.caddy';
|
||||||
$commands = $commands->merge([
|
$commands = $commands->merge([
|
||||||
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
"mkdir -p $proxy_path/dynamic",
|
||||||
|
"cd $proxy_path",
|
||||||
"echo '$caddfile' > $proxy_path/dynamic/Caddyfile",
|
"echo '$caddfile' > $proxy_path/dynamic/Caddyfile",
|
||||||
"echo 'Creating required Docker Compose file.'",
|
"echo 'Creating required Docker Compose file.'",
|
||||||
"echo 'Pulling docker image.'",
|
"echo 'Pulling docker image.'",
|
||||||
'docker compose pull',
|
'docker compose pull',
|
||||||
"echo 'Stopping existing coolify-proxy.'",
|
"echo 'Stopping existing coolify-proxy.'",
|
||||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
'docker compose down -v --remove-orphans > /dev/null 2>&1',
|
||||||
"echo 'Starting coolify-proxy.'",
|
"echo 'Starting coolify-proxy.'",
|
||||||
'docker compose up -d --remove-orphans',
|
'docker compose up -d --remove-orphans',
|
||||||
"echo 'Proxy started successfully.'"
|
"echo 'Proxy started successfully.'",
|
||||||
]);
|
]);
|
||||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($async) {
|
if ($async) {
|
||||||
$activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server);
|
$activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server);
|
||||||
|
|
||||||
return $activity;
|
return $activity;
|
||||||
} else {
|
} else {
|
||||||
instant_remote_process($commands, $server);
|
instant_remote_process($commands, $server);
|
||||||
@@ -62,6 +66,7 @@ class StartProxy
|
|||||||
$server->proxy->set('type', $proxyType);
|
$server->proxy->set('type', $proxyType);
|
||||||
$server->save();
|
$server->save();
|
||||||
ProxyStarted::dispatch($server);
|
ProxyStarted::dispatch($server);
|
||||||
|
|
||||||
return 'OK';
|
return 'OK';
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class CleanupDocker
|
class CleanupDocker
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server, bool $force = true)
|
public function handle(Server $server, bool $force = true)
|
||||||
{
|
{
|
||||||
if ($force) {
|
if ($force) {
|
||||||
|
|||||||
@@ -9,18 +9,19 @@ use Symfony\Component\Yaml\Yaml;
|
|||||||
class ConfigureCloudflared
|
class ConfigureCloudflared
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server, string $cloudflare_token)
|
public function handle(Server $server, string $cloudflare_token)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$config = [
|
$config = [
|
||||||
"services" => [
|
'services' => [
|
||||||
"coolify-cloudflared" => [
|
'coolify-cloudflared' => [
|
||||||
"container_name" => "coolify-cloudflared",
|
'container_name' => 'coolify-cloudflared',
|
||||||
"image" => "cloudflare/cloudflared:latest",
|
'image' => 'cloudflare/cloudflared:latest',
|
||||||
"restart" => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
"network_mode" => "host",
|
'network_mode' => 'host',
|
||||||
"command" => "tunnel run",
|
'command' => 'tunnel run',
|
||||||
"environment" => [
|
'environment' => [
|
||||||
"TUNNEL_TOKEN={$cloudflare_token}",
|
"TUNNEL_TOKEN={$cloudflare_token}",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -29,16 +30,22 @@ class ConfigureCloudflared
|
|||||||
$config = Yaml::dump($config, 12, 2);
|
$config = Yaml::dump($config, 12, 2);
|
||||||
$docker_compose_yml_base64 = base64_encode($config);
|
$docker_compose_yml_base64 = base64_encode($config);
|
||||||
$commands = collect([
|
$commands = collect([
|
||||||
"mkdir -p /tmp/cloudflared && cd /tmp/cloudflared",
|
'mkdir -p /tmp/cloudflared',
|
||||||
"echo '$docker_compose_yml_base64' | base64 -d > docker-compose.yml",
|
'cd /tmp/cloudflared',
|
||||||
"docker compose pull",
|
"echo '$docker_compose_yml_base64' | base64 -d | tee docker-compose.yml > /dev/null",
|
||||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
'docker compose pull',
|
||||||
"docker compose up -d --remove-orphans",
|
'docker compose down -v --remove-orphans > /dev/null 2>&1',
|
||||||
|
'docker compose up -d --remove-orphans',
|
||||||
]);
|
]);
|
||||||
instant_remote_process($commands, $server);
|
instant_remote_process($commands, $server);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
throw $e;
|
throw $e;
|
||||||
|
} finally {
|
||||||
|
$commands = collect([
|
||||||
|
'rm -fr /tmp/cloudflared',
|
||||||
|
]);
|
||||||
|
instant_remote_process($commands, $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,21 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class InstallDocker
|
class InstallDocker
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server)
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
$supported_os_type = $server->validateOS();
|
$supported_os_type = $server->validateOS();
|
||||||
if (!$supported_os_type) {
|
if (! $supported_os_type) {
|
||||||
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
|
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
|
||||||
}
|
}
|
||||||
ray('Installing Docker on server: ' . $server->name . ' (' . $server->ip . ')' . ' with OS type: ' . $supported_os_type);
|
ray('Installing Docker on server: '.$server->name.' ('.$server->ip.')'.' with OS type: '.$supported_os_type);
|
||||||
$dockerVersion = '24.0';
|
$dockerVersion = '24.0';
|
||||||
$config = base64_encode('{
|
$config = base64_encode('{
|
||||||
"log-driver": "json-file",
|
"log-driver": "json-file",
|
||||||
@@ -36,32 +37,41 @@ class InstallDocker
|
|||||||
if (isDev() && $server->id === 0) {
|
if (isDev() && $server->id === 0) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Prerequisites...'",
|
"echo 'Installing Prerequisites...'",
|
||||||
"sleep 1",
|
'sleep 1',
|
||||||
"echo 'Installing Docker Engine...'",
|
"echo 'Installing Docker Engine...'",
|
||||||
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||||
"sleep 4",
|
'sleep 4',
|
||||||
"echo 'Restarting Docker Engine...'",
|
"echo 'Restarting Docker Engine...'",
|
||||||
"ls -l /tmp"
|
'ls -l /tmp',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return remote_process($command, $server);
|
return remote_process($command, $server);
|
||||||
} else {
|
} else {
|
||||||
if ($supported_os_type->contains('debian')) {
|
if ($supported_os_type->contains('debian')) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Prerequisites...'",
|
"echo 'Installing Prerequisites...'",
|
||||||
"command -v jq >/dev/null || apt-get update -y",
|
'apt-get update -y',
|
||||||
"command -v jq >/dev/null || apt install -y curl wget git jq",
|
'command -v curl >/dev/null || apt install -y curl',
|
||||||
|
'command -v wget >/dev/null || apt install -y wget',
|
||||||
|
'command -v git >/dev/null || apt install -y git',
|
||||||
|
'command -v jq >/dev/null || apt install -y jq',
|
||||||
]);
|
]);
|
||||||
} else if ($supported_os_type->contains('rhel')) {
|
} elseif ($supported_os_type->contains('rhel')) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Prerequisites...'",
|
"echo 'Installing Prerequisites...'",
|
||||||
"command -v jq >/dev/null || dnf install -y curl wget git jq",
|
'command -v curl >/dev/null || dnf install -y curl',
|
||||||
|
'command -v wget >/dev/null || dnf install -y wget',
|
||||||
|
'command -v git >/dev/null || dnf install -y git',
|
||||||
|
'command -v jq >/dev/null || dnf install -y jq',
|
||||||
]);
|
]);
|
||||||
} else if ($supported_os_type->contains('sles')) {
|
} elseif ($supported_os_type->contains('sles')) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Prerequisites...'",
|
"echo 'Installing Prerequisites...'",
|
||||||
"command -v jq >/dev/null || zypper update -y",
|
'zypper update -y',
|
||||||
"command -v jq >/dev/null || zypper install -y curl wget git jq",
|
'command -v curl >/dev/null || zypper install -y curl',
|
||||||
|
'command -v wget >/dev/null || zypper install -y wget',
|
||||||
|
'command -v git >/dev/null || zypper install -y git',
|
||||||
|
'command -v jq >/dev/null || zypper install -y jq',
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
throw new \Exception('Unsupported OS');
|
throw new \Exception('Unsupported OS');
|
||||||
@@ -70,26 +80,30 @@ class InstallDocker
|
|||||||
"echo 'Installing Docker Engine...'",
|
"echo 'Installing Docker Engine...'",
|
||||||
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}",
|
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}",
|
||||||
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||||
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
|
'test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json "/etc/docker/daemon.json.original-$(date +"%Y%m%d-%H%M%S")"',
|
||||||
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
"test ! -s /etc/docker/daemon.json && echo '{$config}' | base64 -d | tee /etc/docker/daemon.json > /dev/null",
|
||||||
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
|
"echo '{$config}' | base64 -d | tee /etc/docker/daemon.json.coolify > /dev/null",
|
||||||
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
|
'jq . /etc/docker/daemon.json.coolify | tee /etc/docker/daemon.json.coolify.pretty > /dev/null',
|
||||||
|
'mv /etc/docker/daemon.json.coolify.pretty /etc/docker/daemon.json.coolify',
|
||||||
|
"jq -s '.[0] * .[1]' /etc/docker/daemon.json.coolify /etc/docker/daemon.json | tee /etc/docker/daemon.json.appended > /dev/null",
|
||||||
|
'mv /etc/docker/daemon.json.appended /etc/docker/daemon.json',
|
||||||
"echo 'Restarting Docker Engine...'",
|
"echo 'Restarting Docker Engine...'",
|
||||||
"systemctl enable docker >/dev/null 2>&1 || true",
|
'systemctl enable docker >/dev/null 2>&1 || true',
|
||||||
"systemctl restart docker",
|
'systemctl restart docker',
|
||||||
]);
|
]);
|
||||||
if ($server->isSwarm()) {
|
if ($server->isSwarm()) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true",
|
'docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true',
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
'docker network create --attachable coolify >/dev/null 2>&1 || true',
|
||||||
]);
|
]);
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Done!'",
|
"echo 'Done!'",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return remote_process($command, $server);
|
return remote_process($command, $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,22 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class InstallLogDrain
|
class InstallLogDrain
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server)
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
if ($server->settings->is_logdrain_newrelic_enabled) {
|
if ($server->settings->is_logdrain_newrelic_enabled) {
|
||||||
$type = 'newrelic';
|
$type = 'newrelic';
|
||||||
} else if ($server->settings->is_logdrain_highlight_enabled) {
|
} elseif ($server->settings->is_logdrain_highlight_enabled) {
|
||||||
$type = 'highlight';
|
$type = 'highlight';
|
||||||
} else if ($server->settings->is_logdrain_axiom_enabled) {
|
} elseif ($server->settings->is_logdrain_axiom_enabled) {
|
||||||
$type = 'axiom';
|
$type = 'axiom';
|
||||||
} else if ($server->settings->is_logdrain_custom_enabled) {
|
} elseif ($server->settings->is_logdrain_custom_enabled) {
|
||||||
$type = 'custom';
|
$type = 'custom';
|
||||||
} else {
|
} else {
|
||||||
$type = 'none';
|
$type = 'none';
|
||||||
@@ -25,11 +26,12 @@ class InstallLogDrain
|
|||||||
if ($type === 'none') {
|
if ($type === 'none') {
|
||||||
$command = [
|
$command = [
|
||||||
"echo 'Stopping old Fluent Bit'",
|
"echo 'Stopping old Fluent Bit'",
|
||||||
"docker rm -f coolify-log-drain || true",
|
'docker rm -f coolify-log-drain || true',
|
||||||
];
|
];
|
||||||
|
|
||||||
return instant_remote_process($command, $server);
|
return instant_remote_process($command, $server);
|
||||||
} else if ($type === 'newrelic') {
|
} elseif ($type === 'newrelic') {
|
||||||
if (!$server->settings->is_logdrain_newrelic_enabled) {
|
if (! $server->settings->is_logdrain_newrelic_enabled) {
|
||||||
throw new \Exception('New Relic log drain is not enabled.');
|
throw new \Exception('New Relic log drain is not enabled.');
|
||||||
}
|
}
|
||||||
$config = base64_encode("
|
$config = base64_encode("
|
||||||
@@ -59,11 +61,11 @@ class InstallLogDrain
|
|||||||
# https://log-api.newrelic.com/log/v1 - US
|
# https://log-api.newrelic.com/log/v1 - US
|
||||||
base_uri \${BASE_URI}
|
base_uri \${BASE_URI}
|
||||||
");
|
");
|
||||||
} else if ($type === 'highlight') {
|
} elseif ($type === 'highlight') {
|
||||||
if (!$server->settings->is_logdrain_highlight_enabled) {
|
if (! $server->settings->is_logdrain_highlight_enabled) {
|
||||||
throw new \Exception('Highlight log drain is not enabled.');
|
throw new \Exception('Highlight log drain is not enabled.');
|
||||||
}
|
}
|
||||||
$config = base64_encode("
|
$config = base64_encode('
|
||||||
[SERVICE]
|
[SERVICE]
|
||||||
Flush 5
|
Flush 5
|
||||||
Daemon off
|
Daemon off
|
||||||
@@ -71,7 +73,7 @@ class InstallLogDrain
|
|||||||
Parsers_File parsers.conf
|
Parsers_File parsers.conf
|
||||||
[INPUT]
|
[INPUT]
|
||||||
Name forward
|
Name forward
|
||||||
tag \${HIGHLIGHT_PROJECT_ID}
|
tag ${HIGHLIGHT_PROJECT_ID}
|
||||||
Buffer_Chunk_Size 1M
|
Buffer_Chunk_Size 1M
|
||||||
Buffer_Max_Size 6M
|
Buffer_Max_Size 6M
|
||||||
[OUTPUT]
|
[OUTPUT]
|
||||||
@@ -79,9 +81,9 @@ class InstallLogDrain
|
|||||||
Match *
|
Match *
|
||||||
Host otel.highlight.io
|
Host otel.highlight.io
|
||||||
Port 24224
|
Port 24224
|
||||||
");
|
');
|
||||||
} else if ($type === 'axiom') {
|
} elseif ($type === 'axiom') {
|
||||||
if (!$server->settings->is_logdrain_axiom_enabled) {
|
if (! $server->settings->is_logdrain_axiom_enabled) {
|
||||||
throw new \Exception('Axiom log drain is not enabled.');
|
throw new \Exception('Axiom log drain is not enabled.');
|
||||||
}
|
}
|
||||||
$config = base64_encode("
|
$config = base64_encode("
|
||||||
@@ -116,8 +118,8 @@ class InstallLogDrain
|
|||||||
json_date_format iso8601
|
json_date_format iso8601
|
||||||
tls On
|
tls On
|
||||||
");
|
");
|
||||||
} else if ($type === 'custom') {
|
} elseif ($type === 'custom') {
|
||||||
if (!$server->settings->is_logdrain_custom_enabled) {
|
if (! $server->settings->is_logdrain_custom_enabled) {
|
||||||
throw new \Exception('Custom log drain is not enabled.');
|
throw new \Exception('Custom log drain is not enabled.');
|
||||||
}
|
}
|
||||||
$config = base64_encode($server->settings->logdrain_custom_config);
|
$config = base64_encode($server->settings->logdrain_custom_config);
|
||||||
@@ -133,7 +135,7 @@ class InstallLogDrain
|
|||||||
Regex /^(?!\s*$).+/
|
Regex /^(?!\s*$).+/
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
$compose = base64_encode("
|
$compose = base64_encode('
|
||||||
services:
|
services:
|
||||||
coolify-log-drain:
|
coolify-log-drain:
|
||||||
image: cr.fluentbit.io/fluent/fluent-bit:2.0
|
image: cr.fluentbit.io/fluent/fluent-bit:2.0
|
||||||
@@ -147,7 +149,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:24224:24224
|
- 127.0.0.1:24224:24224
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
");
|
');
|
||||||
$readme = base64_encode('# New Relic Log Drain
|
$readme = base64_encode('# New Relic Log Drain
|
||||||
This log drain is based on [Fluent Bit](https://fluentbit.io/) and New Relic Log Forwarder.
|
This log drain is based on [Fluent Bit](https://fluentbit.io/) and New Relic Log Forwarder.
|
||||||
|
|
||||||
@@ -160,18 +162,18 @@ Files:
|
|||||||
$base_uri = $server->settings->logdrain_newrelic_base_uri;
|
$base_uri = $server->settings->logdrain_newrelic_base_uri;
|
||||||
$base_path = config('coolify.base_config_path');
|
$base_path = config('coolify.base_config_path');
|
||||||
|
|
||||||
$config_path = $base_path . '/log-drains';
|
$config_path = $base_path.'/log-drains';
|
||||||
$fluent_bit_config = $config_path . '/fluent-bit.conf';
|
$fluent_bit_config = $config_path.'/fluent-bit.conf';
|
||||||
$parsers_config = $config_path . '/parsers.conf';
|
$parsers_config = $config_path.'/parsers.conf';
|
||||||
$compose_path = $config_path . '/docker-compose.yml';
|
$compose_path = $config_path.'/docker-compose.yml';
|
||||||
$readme_path = $config_path . '/README.md';
|
$readme_path = $config_path.'/README.md';
|
||||||
$command = [
|
$command = [
|
||||||
"echo 'Saving configuration'",
|
"echo 'Saving configuration'",
|
||||||
"mkdir -p $config_path",
|
"mkdir -p $config_path",
|
||||||
"echo '{$parsers}' | base64 -d > $parsers_config",
|
"echo '{$parsers}' | base64 -d | tee $parsers_config > /dev/null",
|
||||||
"echo '{$config}' | base64 -d > $fluent_bit_config",
|
"echo '{$config}' | base64 -d | tee $fluent_bit_config > /dev/null",
|
||||||
"echo '{$compose}' | base64 -d > $compose_path",
|
"echo '{$compose}' | base64 -d | tee $compose_path > /dev/null",
|
||||||
"echo '{$readme}' | base64 -d > $readme_path",
|
"echo '{$readme}' | base64 -d | tee $readme_path > /dev/null",
|
||||||
"test -f $config_path/.env && rm $config_path/.env",
|
"test -f $config_path/.env && rm $config_path/.env",
|
||||||
|
|
||||||
];
|
];
|
||||||
@@ -180,18 +182,18 @@ Files:
|
|||||||
"echo LICENSE_KEY=$license_key >> $config_path/.env",
|
"echo LICENSE_KEY=$license_key >> $config_path/.env",
|
||||||
"echo BASE_URI=$base_uri >> $config_path/.env",
|
"echo BASE_URI=$base_uri >> $config_path/.env",
|
||||||
];
|
];
|
||||||
} else if ($type === 'highlight') {
|
} elseif ($type === 'highlight') {
|
||||||
$add_envs_command = [
|
$add_envs_command = [
|
||||||
"echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env",
|
"echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env",
|
||||||
];
|
];
|
||||||
} else if ($type === 'axiom') {
|
} elseif ($type === 'axiom') {
|
||||||
$add_envs_command = [
|
$add_envs_command = [
|
||||||
"echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env",
|
"echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env",
|
||||||
"echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env",
|
"echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env",
|
||||||
];
|
];
|
||||||
} else if ($type === 'custom') {
|
} elseif ($type === 'custom') {
|
||||||
$add_envs_command = [
|
$add_envs_command = [
|
||||||
"touch $config_path/.env"
|
"touch $config_path/.env",
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
throw new \Exception('Unknown log drain type.');
|
throw new \Exception('Unknown log drain type.');
|
||||||
@@ -203,6 +205,7 @@ Files:
|
|||||||
"cd $config_path && docker compose up -d --remove-orphans",
|
"cd $config_path && docker compose up -d --remove-orphans",
|
||||||
];
|
];
|
||||||
$command = array_merge($command, $add_envs_command, $restart_command);
|
$command = array_merge($command, $add_envs_command, $restart_command);
|
||||||
|
|
||||||
return instant_remote_process($command, $server);
|
return instant_remote_process($command, $server);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
|
|||||||
26
app/Actions/Server/StartSentinel.php
Normal file
26
app/Actions/Server/StartSentinel.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StartSentinel
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public function handle(Server $server, $version = 'latest', bool $restart = false)
|
||||||
|
{
|
||||||
|
if ($restart) {
|
||||||
|
StopSentinel::run($server);
|
||||||
|
}
|
||||||
|
$metrics_history = $server->settings->metrics_history_days;
|
||||||
|
$refresh_rate = $server->settings->metrics_refresh_rate_seconds;
|
||||||
|
$token = $server->settings->metrics_token;
|
||||||
|
instant_remote_process([
|
||||||
|
"docker run --rm --pull always -d -e \"TOKEN={$token}\" -e \"SCHEDULER=true\" -e \"METRICS_HISTORY={$metrics_history}\" -e \"REFRESH_RATE={$refresh_rate}\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
|
||||||
|
'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
|
||||||
|
'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
|
||||||
|
], $server, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Actions/Server/StopSentinel.php
Normal file
16
app/Actions/Server/StopSentinel.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StopSentinel
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public function handle(Server $server)
|
||||||
|
{
|
||||||
|
instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,53 +2,45 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class UpdateCoolify
|
class UpdateCoolify
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
public ?Server $server = null;
|
|
||||||
public ?string $latestVersion = null;
|
|
||||||
public ?string $currentVersion = null;
|
|
||||||
public bool $async = false;
|
|
||||||
|
|
||||||
public function handle(bool $force = false, bool $async = false)
|
public ?Server $server = null;
|
||||||
|
|
||||||
|
public ?string $latestVersion = null;
|
||||||
|
|
||||||
|
public ?string $currentVersion = null;
|
||||||
|
|
||||||
|
public function handle($manual_update = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->async = $async;
|
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
ray('Running InstanceAutoUpdateJob');
|
ray('Running InstanceAutoUpdateJob');
|
||||||
$this->server = Server::find(0);
|
$this->server = Server::find(0);
|
||||||
if (!$this->server) {
|
if (! $this->server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CleanupDocker::run($this->server, false);
|
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');
|
||||||
if ($settings->next_channel) {
|
if (! $manual_update) {
|
||||||
ray('next channel enabled');
|
if (! $settings->is_auto_update_enabled) {
|
||||||
$this->latestVersion = 'next';
|
return;
|
||||||
}
|
|
||||||
if ($force) {
|
|
||||||
$this->update();
|
|
||||||
} else {
|
|
||||||
if (!$settings->is_auto_update_enabled) {
|
|
||||||
return 'Auto update is disabled';
|
|
||||||
}
|
}
|
||||||
if ($this->latestVersion === $this->currentVersion) {
|
if ($this->latestVersion === $this->currentVersion) {
|
||||||
return 'Already on latest version';
|
return;
|
||||||
}
|
}
|
||||||
if (version_compare($this->latestVersion, $this->currentVersion, '<')) {
|
if (version_compare($this->latestVersion, $this->currentVersion, '<')) {
|
||||||
return 'Latest version is lower than current version?!';
|
return;
|
||||||
}
|
}
|
||||||
$this->update();
|
|
||||||
}
|
}
|
||||||
|
$this->update();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray('InstanceAutoUpdateJob failed');
|
|
||||||
ray($e->getMessage());
|
|
||||||
send_internal_notification('InstanceAutoUpdateJob failed: ' . $e->getMessage());
|
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,34 +48,16 @@ class UpdateCoolify
|
|||||||
private function update()
|
private function update()
|
||||||
{
|
{
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
ray("Running update on local docker container. Updating to $this->latestVersion");
|
remote_process([
|
||||||
if ($this->async) {
|
'sleep 10',
|
||||||
ray('Running async update');
|
], $this->server);
|
||||||
remote_process([
|
|
||||||
"sleep 10"
|
|
||||||
], $this->server);
|
|
||||||
} else {
|
|
||||||
instant_remote_process([
|
|
||||||
"sleep 10"
|
|
||||||
], $this->server);
|
|
||||||
}
|
|
||||||
ray('Update done');
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
ray('Running update on production server');
|
|
||||||
if ($this->async) {
|
|
||||||
remote_process([
|
|
||||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
|
||||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
|
||||||
], $this->server);
|
|
||||||
} else {
|
|
||||||
instant_remote_process([
|
|
||||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
|
||||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
|
||||||
], $this->server);
|
|
||||||
}
|
|
||||||
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
remote_process([
|
||||||
|
'curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh',
|
||||||
|
"bash /data/coolify/source/upgrade.sh $this->latestVersion",
|
||||||
|
], $this->server);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Actions\Service;
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class DeleteService
|
class DeleteService
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,26 +2,27 @@
|
|||||||
|
|
||||||
namespace App\Actions\Service;
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartService
|
class StartService
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
ray('Starting service: ' . $service->name);
|
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()}.'";
|
||||||
$commands[] = "echo 'Creating Docker network.'";
|
$commands[] = "echo 'Creating Docker network.'";
|
||||||
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid";
|
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid";
|
||||||
$commands[] = "echo Starting service.";
|
$commands[] = 'echo Starting service.';
|
||||||
$commands[] = "echo 'Pulling images.'";
|
$commands[] = "echo 'Pulling images.'";
|
||||||
$commands[] = "docker compose pull";
|
$commands[] = 'docker compose pull';
|
||||||
$commands[] = "echo 'Starting containers.'";
|
$commands[] = "echo 'Starting containers.'";
|
||||||
$commands[] = "docker compose up -d --remove-orphans --force-recreate --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')) {
|
if (data_get($service, 'connect_to_docker_network')) {
|
||||||
$compose = data_get($service, 'docker_compose', []);
|
$compose = data_get($service, 'docker_compose', []);
|
||||||
@@ -32,6 +33,7 @@ class StartService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,21 @@
|
|||||||
|
|
||||||
namespace App\Actions\Service;
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StopService
|
class StopService
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$server = $service->destination->server;
|
$server = $service->destination->server;
|
||||||
if (!$server->isFunctional()) {
|
if (! $server->isFunctional()) {
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
ray('Stopping service: ' . $service->name);
|
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);
|
||||||
@@ -33,6 +34,7 @@ class StopService
|
|||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
echo $e->getMessage();
|
echo $e->getMessage();
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,18 +8,21 @@ use Lorisleiva\Actions\Concerns\AsAction;
|
|||||||
class ComplexStatusCheck
|
class ComplexStatusCheck
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Application $application)
|
public function handle(Application $application)
|
||||||
{
|
{
|
||||||
$servers = $application->additional_servers;
|
$servers = $application->additional_servers;
|
||||||
$servers->push($application->destination->server);
|
$servers->push($application->destination->server);
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$is_main_server = $application->destination->server->id === $server->id;
|
$is_main_server = $application->destination->server->id === $server->id;
|
||||||
if (!$server->isFunctional()) {
|
if (! $server->isFunctional()) {
|
||||||
if ($is_main_server) {
|
if ($is_main_server) {
|
||||||
$application->update(['status' => 'exited:unhealthy']);
|
$application->update(['status' => 'exited:unhealthy']);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,9 +47,11 @@ class ComplexStatusCheck
|
|||||||
} else {
|
} else {
|
||||||
if ($is_main_server) {
|
if ($is_main_server) {
|
||||||
$application->update(['status' => 'exited:unhealthy']);
|
$application->update(['status' => 'exited:unhealthy']);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,20 @@ use Lorisleiva\Actions\Concerns\AsAction;
|
|||||||
class PullImage
|
class PullImage
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Service $resource)
|
public function handle(Service $resource)
|
||||||
{
|
{
|
||||||
$resource->saveComposeConfigs();
|
$resource->saveComposeConfigs();
|
||||||
|
|
||||||
$commands[] = "cd " . $resource->workdir();
|
$commands[] = 'cd '.$resource->workdir();
|
||||||
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
|
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
|
||||||
$commands[] = "docker compose pull";
|
$commands[] = 'docker compose pull';
|
||||||
|
|
||||||
$server = data_get($resource, 'server');
|
$server = data_get($resource, 'server');
|
||||||
|
|
||||||
if (!$server) return;
|
if (! $server) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
instant_remote_process($commands, $resource->server);
|
instant_remote_process($commands, $resource->server);
|
||||||
}
|
}
|
||||||
|
|||||||
56
app/Console/Commands/AdminRemoveUser.php
Normal file
56
app/Console/Commands/AdminRemoveUser.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class AdminRemoveUser extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'admin:remove-user {email}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Remove User from database';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$email = $this->argument('email');
|
||||||
|
$confirm = $this->confirm('Are you sure you want to remove user with email: '.$email.'?');
|
||||||
|
if (! $confirm) {
|
||||||
|
$this->info('User removal cancelled.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->info("Removing user with email: $email");
|
||||||
|
$user = User::whereEmail($email)->firstOrFail();
|
||||||
|
$teams = $user->teams;
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
if ($team->members->count() > 1) {
|
||||||
|
$this->error('User is a member of a team with more than one member. Please remove user from team first.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$team->delete();
|
||||||
|
}
|
||||||
|
$user->delete();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('Failed to remove user.');
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ use Illuminate\Console\Command;
|
|||||||
class CleanupApplicationDeploymentQueue extends Command
|
class CleanupApplicationDeploymentQueue extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
|
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
|
||||||
|
|
||||||
protected $description = 'CleanupApplicationDeploymentQueue';
|
protected $description = 'CleanupApplicationDeploymentQueue';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
@@ -15,10 +16,10 @@ class CleanupApplicationDeploymentQueue extends Command
|
|||||||
$team_id = $this->option('team-id');
|
$team_id = $this->option('team-id');
|
||||||
$servers = \App\Models\Server::where('team_id', $team_id)->get();
|
$servers = \App\Models\Server::where('team_id', $team_id)->get();
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$deployments = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->where("server_id", $server->id)->get();
|
$deployments = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->where('server_id', $server->id)->get();
|
||||||
foreach ($deployments as $deployment) {
|
foreach ($deployments as $deployment) {
|
||||||
$deployment->update(['status' => 'failed']);
|
$deployment->update(['status' => 'failed']);
|
||||||
instant_remote_process(['docker rm -f ' . $deployment->deployment_uuid], $server, false);
|
instant_remote_process(['docker rm -f '.$deployment->deployment_uuid], $server, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Illuminate\Support\Facades\DB;
|
|||||||
class CleanupDatabase extends Command
|
class CleanupDatabase extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'cleanup:database {--yes}';
|
protected $signature = 'cleanup:database {--yes}';
|
||||||
|
|
||||||
protected $description = 'Cleanup database';
|
protected $description = 'Cleanup database';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Redis;
|
|||||||
class CleanupQueue extends Command
|
class CleanupQueue extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'cleanup:queue';
|
protected $signature = 'cleanup:queue';
|
||||||
|
|
||||||
protected $description = 'Cleanup Queue';
|
protected $description = 'Cleanup Queue';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ use App\Models\ScheduledTask;
|
|||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneClickhouse;
|
||||||
|
use App\Models\StandaloneDragonfly;
|
||||||
|
use App\Models\StandaloneKeydb;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -17,6 +20,7 @@ use Illuminate\Console\Command;
|
|||||||
class CleanupStuckedResources extends Command
|
class CleanupStuckedResources extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'cleanup:stucked-resources';
|
protected $signature = 'cleanup:stucked-resources';
|
||||||
|
|
||||||
protected $description = 'Cleanup Stucked Resources';
|
protected $description = 'Cleanup Stucked Resources';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
@@ -25,6 +29,7 @@ class CleanupStuckedResources extends Command
|
|||||||
echo "Running cleanup stucked resources.\n";
|
echo "Running cleanup stucked resources.\n";
|
||||||
$this->cleanup_stucked_resources();
|
$this->cleanup_stucked_resources();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanup_stucked_resources()
|
private function cleanup_stucked_resources()
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -55,6 +60,33 @@ class CleanupStuckedResources extends Command
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
$keydbs = StandaloneKeydb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($keydbs as $keydb) {
|
||||||
|
echo "Deleting stuck keydb: {$keydb->name}\n";
|
||||||
|
$keydb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck keydb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$dragonflies = StandaloneDragonfly::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($dragonflies as $dragonfly) {
|
||||||
|
echo "Deleting stuck dragonfly: {$dragonfly->name}\n";
|
||||||
|
$dragonfly->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck dragonfly: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$clickhouses = StandaloneClickhouse::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($clickhouses as $clickhouse) {
|
||||||
|
echo "Deleting stuck clickhouse: {$clickhouse->name}\n";
|
||||||
|
$clickhouse->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck clickhouse: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($mongodbs as $mongodb) {
|
foreach ($mongodbs as $mongodb) {
|
||||||
@@ -112,7 +144,7 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$scheduled_tasks = ScheduledTask::all();
|
$scheduled_tasks = ScheduledTask::all();
|
||||||
foreach ($scheduled_tasks as $scheduled_task) {
|
foreach ($scheduled_tasks as $scheduled_task) {
|
||||||
if (!$scheduled_task->service && !$scheduled_task->application) {
|
if (! $scheduled_task->service && ! $scheduled_task->application) {
|
||||||
echo "Deleting stuck scheduledtask: {$scheduled_task->name}\n";
|
echo "Deleting stuck scheduledtask: {$scheduled_task->name}\n";
|
||||||
$scheduled_task->delete();
|
$scheduled_task->delete();
|
||||||
}
|
}
|
||||||
@@ -125,19 +157,22 @@ class CleanupStuckedResources extends Command
|
|||||||
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 . '\n';
|
echo 'Application without environment: '.$application->name.'\n';
|
||||||
$application->forceDelete();
|
$application->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!$application->destination()) {
|
if (! $application->destination()) {
|
||||||
echo 'Application without destination: ' . $application->name . '\n';
|
echo 'Application without destination: '.$application->name.'\n';
|
||||||
$application->forceDelete();
|
$application->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($application, 'destination.server')) {
|
if (! data_get($application, 'destination.server')) {
|
||||||
echo 'Application without server: ' . $application->name . '\n';
|
echo 'Application without server: '.$application->name.'\n';
|
||||||
$application->forceDelete();
|
$application->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,19 +182,22 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$postgresqls = StandalonePostgresql::all()->where('id', '!=', 0);
|
$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 . '\n';
|
echo 'Postgresql without environment: '.$postgresql->name.'\n';
|
||||||
$postgresql->forceDelete();
|
$postgresql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!$postgresql->destination()) {
|
if (! $postgresql->destination()) {
|
||||||
echo 'Postgresql without destination: ' . $postgresql->name . '\n';
|
echo 'Postgresql without destination: '.$postgresql->name.'\n';
|
||||||
$postgresql->forceDelete();
|
$postgresql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($postgresql, 'destination.server')) {
|
if (! data_get($postgresql, 'destination.server')) {
|
||||||
echo 'Postgresql without server: ' . $postgresql->name . '\n';
|
echo 'Postgresql without server: '.$postgresql->name.'\n';
|
||||||
$postgresql->forceDelete();
|
$postgresql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,19 +207,22 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$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 . '\n';
|
echo 'Redis without environment: '.$redis->name.'\n';
|
||||||
$redis->forceDelete();
|
$redis->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!$redis->destination()) {
|
if (! $redis->destination()) {
|
||||||
echo 'Redis without destination: ' . $redis->name . '\n';
|
echo 'Redis without destination: '.$redis->name.'\n';
|
||||||
$redis->forceDelete();
|
$redis->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($redis, 'destination.server')) {
|
if (! data_get($redis, 'destination.server')) {
|
||||||
echo 'Redis without server: ' . $redis->name . '\n';
|
echo 'Redis without server: '.$redis->name.'\n';
|
||||||
$redis->forceDelete();
|
$redis->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,19 +233,22 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$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 . '\n';
|
echo 'Mongodb without environment: '.$mongodb->name.'\n';
|
||||||
$mongodb->forceDelete();
|
$mongodb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mongodb->destination()) {
|
if (! $mongodb->destination()) {
|
||||||
echo 'Mongodb without destination: ' . $mongodb->name . '\n';
|
echo 'Mongodb without destination: '.$mongodb->name.'\n';
|
||||||
$mongodb->forceDelete();
|
$mongodb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($mongodb, 'destination.server')) {
|
if (! data_get($mongodb, 'destination.server')) {
|
||||||
echo 'Mongodb without server: ' . $mongodb->name . '\n';
|
echo 'Mongodb without server: '.$mongodb->name.'\n';
|
||||||
$mongodb->forceDelete();
|
$mongodb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,19 +259,22 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$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 . '\n';
|
echo 'Mysql without environment: '.$mysql->name.'\n';
|
||||||
$mysql->forceDelete();
|
$mysql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mysql->destination()) {
|
if (! $mysql->destination()) {
|
||||||
echo 'Mysql without destination: ' . $mysql->name . '\n';
|
echo 'Mysql without destination: '.$mysql->name.'\n';
|
||||||
$mysql->forceDelete();
|
$mysql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($mysql, 'destination.server')) {
|
if (! data_get($mysql, 'destination.server')) {
|
||||||
echo 'Mysql without server: ' . $mysql->name . '\n';
|
echo 'Mysql without server: '.$mysql->name.'\n';
|
||||||
$mysql->forceDelete();
|
$mysql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,19 +285,22 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$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 . '\n';
|
echo 'Mariadb without environment: '.$mariadb->name.'\n';
|
||||||
$mariadb->forceDelete();
|
$mariadb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!$mariadb->destination()) {
|
if (! $mariadb->destination()) {
|
||||||
echo 'Mariadb without destination: ' . $mariadb->name . '\n';
|
echo 'Mariadb without destination: '.$mariadb->name.'\n';
|
||||||
$mariadb->forceDelete();
|
$mariadb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($mariadb, 'destination.server')) {
|
if (! data_get($mariadb, 'destination.server')) {
|
||||||
echo 'Mariadb without server: ' . $mariadb->name . '\n';
|
echo 'Mariadb without server: '.$mariadb->name.'\n';
|
||||||
$mariadb->forceDelete();
|
$mariadb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,19 +311,22 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$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 . '\n';
|
echo 'Service without environment: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!$service->destination()) {
|
if (! $service->destination()) {
|
||||||
echo 'Service without destination: ' . $service->name . '\n';
|
echo 'Service without destination: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!data_get($service, 'server')) {
|
if (! data_get($service, 'server')) {
|
||||||
echo 'Service without server: ' . $service->name . '\n';
|
echo 'Service without server: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,9 +336,10 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$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 . '\n';
|
echo 'ServiceApplication without service: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,9 +349,10 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$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 . '\n';
|
echo 'ServiceDatabase without service: '.$service->name.'\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Illuminate\Console\Command;
|
|||||||
class CleanupUnreachableServers extends Command
|
class CleanupUnreachableServers extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'cleanup:unreachable-servers';
|
protected $signature = 'cleanup:unreachable-servers';
|
||||||
|
|
||||||
protected $description = 'Cleanup Unreachable Servers (7 days)';
|
protected $description = 'Cleanup Unreachable Servers (7 days)';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
@@ -19,7 +20,7 @@ class CleanupUnreachableServers extends Command
|
|||||||
echo "Cleanup unreachable server ($server->id) with name $server->name";
|
echo "Cleanup unreachable server ($server->id) with name $server->name";
|
||||||
send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up...");
|
send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up...");
|
||||||
$server->update([
|
$server->update([
|
||||||
'ip' => '1.2.3.4'
|
'ip' => '1.2.3.4',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class Cloud extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'cloud:unused-servers';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Get Unused Servers from Cloud';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended',true)->each(function($server){
|
|
||||||
$this->info($server->name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Process;
|
|||||||
class Dev extends Command
|
class Dev extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'dev:init';
|
protected $signature = 'dev:init';
|
||||||
|
|
||||||
protected $description = 'Init the app in dev mode';
|
protected $description = 'Init the app in dev mode';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
@@ -21,7 +22,7 @@ class Dev extends Command
|
|||||||
}
|
}
|
||||||
// Seed database if it's empty
|
// Seed database if it's empty
|
||||||
$settings = InstanceSettings::find(0);
|
$settings = InstanceSettings::find(0);
|
||||||
if (!$settings) {
|
if (! $settings) {
|
||||||
echo "Initializing instance, seeding database.\n";
|
echo "Initializing instance, seeding database.\n";
|
||||||
Artisan::call('migrate --seed');
|
Artisan::call('migrate --seed');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Jobs\DatabaseBackupStatusJob;
|
|
||||||
use App\Jobs\SendConfirmationForWaitlistJob;
|
use App\Jobs\SendConfirmationForWaitlistJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledDatabaseBackupExecution;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
@@ -49,7 +47,9 @@ class Emails extends Command
|
|||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*/
|
*/
|
||||||
private ?MailMessage $mail = null;
|
private ?MailMessage $mail = null;
|
||||||
|
|
||||||
private ?string $email = null;
|
private ?string $email = null;
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$type = select(
|
$type = select(
|
||||||
@@ -73,21 +73,22 @@ class Emails extends Command
|
|||||||
);
|
);
|
||||||
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$this->email = "test@example.com";
|
$this->email = 'test@example.com';
|
||||||
} else {
|
} else {
|
||||||
if (!in_array($type, $emailsGathered)) {
|
if (! in_array($type, $emailsGathered)) {
|
||||||
$this->email = text('Email Address to send to:');
|
$this->email = text('Email Address to send to:');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set_transanctional_email_settings();
|
set_transanctional_email_settings();
|
||||||
|
|
||||||
$this->mail = new MailMessage();
|
$this->mail = new MailMessage();
|
||||||
$this->mail->subject("Test Email");
|
$this->mail->subject('Test Email');
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'updates':
|
case 'updates':
|
||||||
$teams = Team::all();
|
$teams = Team::all();
|
||||||
if (!$teams || $teams->isEmpty()) {
|
if (! $teams || $teams->isEmpty()) {
|
||||||
echo 'No teams found.' . PHP_EOL;
|
echo 'No teams found.'.PHP_EOL;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$emails = [];
|
$emails = [];
|
||||||
@@ -99,7 +100,7 @@ class Emails extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$emails = array_unique($emails);
|
$emails = array_unique($emails);
|
||||||
$this->info("Sending to " . count($emails) . " emails.");
|
$this->info('Sending to '.count($emails).' emails.');
|
||||||
foreach ($emails as $email) {
|
foreach ($emails as $email) {
|
||||||
$this->info($email);
|
$this->info($email);
|
||||||
}
|
}
|
||||||
@@ -111,7 +112,7 @@ class Emails extends Command
|
|||||||
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
||||||
'token' => encrypt($email),
|
'token' => encrypt($email),
|
||||||
]);
|
]);
|
||||||
$this->mail->view('emails.updates', ["unsubscribeUrl" => $unsubscribeUrl]);
|
$this->mail->view('emails.updates', ['unsubscribeUrl' => $unsubscribeUrl]);
|
||||||
$this->sendEmail($email);
|
$this->sendEmail($email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,7 +158,7 @@ class Emails extends Command
|
|||||||
case 'application-deployment-failed':
|
case 'application-deployment-failed':
|
||||||
$application = Application::all()->first();
|
$application = Application::all()->first();
|
||||||
$preview = ApplicationPreview::all()->first();
|
$preview = ApplicationPreview::all()->first();
|
||||||
if (!$preview) {
|
if (! $preview) {
|
||||||
$preview = ApplicationPreview::create([
|
$preview = ApplicationPreview::create([
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => 1,
|
'pull_request_id' => 1,
|
||||||
@@ -178,7 +179,7 @@ class Emails extends Command
|
|||||||
case 'backup-failed':
|
case 'backup-failed':
|
||||||
$backup = ScheduledDatabaseBackup::all()->first();
|
$backup = ScheduledDatabaseBackup::all()->first();
|
||||||
$db = StandalonePostgresql::all()->first();
|
$db = StandalonePostgresql::all()->first();
|
||||||
if (!$backup) {
|
if (! $backup) {
|
||||||
$backup = ScheduledDatabaseBackup::create([
|
$backup = ScheduledDatabaseBackup::create([
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'frequency' => 'daily',
|
'frequency' => 'daily',
|
||||||
@@ -188,14 +189,14 @@ class Emails extends Command
|
|||||||
'team_id' => 0,
|
'team_id' => 0,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$output = 'Because of an error, the backup of the database ' . $db->name . ' failed.';
|
$output = 'Because of an error, the backup of the database '.$db->name.' failed.';
|
||||||
$this->mail = (new BackupFailed($backup, $db, $output))->toMail();
|
$this->mail = (new BackupFailed($backup, $db, $output))->toMail();
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
break;
|
break;
|
||||||
case 'backup-success':
|
case 'backup-success':
|
||||||
$backup = ScheduledDatabaseBackup::all()->first();
|
$backup = ScheduledDatabaseBackup::all()->first();
|
||||||
$db = StandalonePostgresql::all()->first();
|
$db = StandalonePostgresql::all()->first();
|
||||||
if (!$backup) {
|
if (! $backup) {
|
||||||
$backup = ScheduledDatabaseBackup::create([
|
$backup = ScheduledDatabaseBackup::create([
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'frequency' => 'daily',
|
'frequency' => 'daily',
|
||||||
@@ -244,8 +245,9 @@ class Emails extends Command
|
|||||||
$this->mail->view('emails.before-trial-conversion');
|
$this->mail->view('emails.before-trial-conversion');
|
||||||
$this->mail->subject('Trial period has been added for all subscription plans.');
|
$this->mail->subject('Trial period has been added for all subscription plans.');
|
||||||
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
|
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
|
||||||
if (!$teams || $teams->isEmpty()) {
|
if (! $teams || $teams->isEmpty()) {
|
||||||
echo 'No teams found.' . PHP_EOL;
|
echo 'No teams found.'.PHP_EOL;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$emails = [];
|
$emails = [];
|
||||||
@@ -257,7 +259,7 @@ class Emails extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$emails = array_unique($emails);
|
$emails = array_unique($emails);
|
||||||
$this->info("Sending to " . count($emails) . " emails.");
|
$this->info('Sending to '.count($emails).' emails.');
|
||||||
foreach ($emails as $email) {
|
foreach ($emails as $email) {
|
||||||
$this->info($email);
|
$this->info($email);
|
||||||
}
|
}
|
||||||
@@ -271,7 +273,7 @@ class Emails extends Command
|
|||||||
case 'realusers-server-lost-connection':
|
case 'realusers-server-lost-connection':
|
||||||
$serverId = text('Server Id');
|
$serverId = text('Server Id');
|
||||||
$server = Server::find($serverId);
|
$server = Server::find($serverId);
|
||||||
if (!$server) {
|
if (! $server) {
|
||||||
throw new Exception('Server not found');
|
throw new Exception('Server not found');
|
||||||
}
|
}
|
||||||
$admins = [];
|
$admins = [];
|
||||||
@@ -281,7 +283,7 @@ class Emails extends Command
|
|||||||
$admins[] = $member->email;
|
$admins[] = $member->email;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->info('Sending to ' . count($admins) . ' admins.');
|
$this->info('Sending to '.count($admins).' admins.');
|
||||||
foreach ($admins as $admin) {
|
foreach ($admins as $admin) {
|
||||||
$this->info($admin);
|
$this->info($admin);
|
||||||
}
|
}
|
||||||
@@ -289,14 +291,15 @@ class Emails extends Command
|
|||||||
$this->mail->view('emails.server-lost-connection', [
|
$this->mail->view('emails.server-lost-connection', [
|
||||||
'name' => $server->name,
|
'name' => $server->name,
|
||||||
]);
|
]);
|
||||||
$this->mail->subject('Action required: Server ' . $server->name . ' lost connection.');
|
$this->mail->subject('Action required: Server '.$server->name.' lost connection.');
|
||||||
foreach ($admins as $email) {
|
foreach ($admins as $email) {
|
||||||
$this->sendEmail($email);
|
$this->sendEmail($email);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function sendEmail(string $email = null)
|
|
||||||
|
private function sendEmail(?string $email = null)
|
||||||
{
|
{
|
||||||
if ($email) {
|
if ($email) {
|
||||||
$this->email = $email;
|
$this->email = $email;
|
||||||
@@ -307,7 +310,7 @@ class Emails extends Command
|
|||||||
fn (Message $message) => $message
|
fn (Message $message) => $message
|
||||||
->to($this->email)
|
->to($this->email)
|
||||||
->subject($this->mail->subject)
|
->subject($this->mail->subject)
|
||||||
->html((string)$this->mail->render())
|
->html((string) $this->mail->render())
|
||||||
);
|
);
|
||||||
$this->info("Email sent to $this->email successfully. 📧");
|
$this->info("Email sent to $this->email successfully. 📧");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ use Illuminate\Console\Command;
|
|||||||
class Horizon extends Command
|
class Horizon extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'start:horizon';
|
protected $signature = 'start:horizon';
|
||||||
|
|
||||||
protected $description = 'Start Horizon';
|
protected $description = 'Start Horizon';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
if (config('coolify.is_horizon_enabled')) {
|
if (config('coolify.is_horizon_enabled')) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Console\Commands;
|
|||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Jobs\CleanupHelperContainersJob;
|
use App\Jobs\CleanupHelperContainersJob;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
use App\Models\Environment;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@@ -15,16 +16,21 @@ use Illuminate\Support\Facades\Http;
|
|||||||
class Init extends Command
|
class Init extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments}';
|
protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments}';
|
||||||
|
|
||||||
protected $description = 'Cleanup instance related stuffs';
|
protected $description = 'Cleanup instance related stuffs';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->alive();
|
$this->alive();
|
||||||
|
get_public_ips();
|
||||||
$full_cleanup = $this->option('full-cleanup');
|
$full_cleanup = $this->option('full-cleanup');
|
||||||
$cleanup_deployments = $this->option('cleanup-deployments');
|
$cleanup_deployments = $this->option('cleanup-deployments');
|
||||||
|
|
||||||
|
$this->replace_slash_in_environment_name();
|
||||||
if ($cleanup_deployments) {
|
if ($cleanup_deployments) {
|
||||||
echo "Running cleanup deployments.\n";
|
echo "Running cleanup deployments.\n";
|
||||||
$this->cleanup_in_progress_application_deployments();
|
$this->cleanup_in_progress_application_deployments();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($full_cleanup) {
|
if ($full_cleanup) {
|
||||||
@@ -34,7 +40,7 @@ class Init extends Command
|
|||||||
$this->cleanup_stucked_helper_containers();
|
$this->cleanup_stucked_helper_containers();
|
||||||
$this->call('cleanup:queue');
|
$this->call('cleanup:queue');
|
||||||
$this->call('cleanup:stucked-resources');
|
$this->call('cleanup:stucked-resources');
|
||||||
if (!isCloud()) {
|
if (! isCloud()) {
|
||||||
try {
|
try {
|
||||||
$server = Server::find(0)->first();
|
$server = Server::find(0)->first();
|
||||||
$server->setupDynamicProxyConfiguration();
|
$server->setupDynamicProxyConfiguration();
|
||||||
@@ -44,18 +50,20 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
if (!is_null(env('AUTOUPDATE', null))) {
|
if (! is_null(env('AUTOUPDATE', null))) {
|
||||||
if (env('AUTOUPDATE') == true) {
|
if (env('AUTOUPDATE') == true) {
|
||||||
$settings->update(['is_auto_update_enabled' => true]);
|
$settings->update(['is_auto_update_enabled' => true]);
|
||||||
} else {
|
} else {
|
||||||
$settings->update(['is_auto_update_enabled' => false]);
|
$settings->update(['is_auto_update_enabled' => false]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->cleanup_stucked_helper_containers();
|
$this->cleanup_stucked_helper_containers();
|
||||||
$this->call('cleanup:stucked-resources');
|
$this->call('cleanup:stucked-resources');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function restore_coolify_db_backup()
|
private function restore_coolify_db_backup()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -64,7 +72,7 @@ class Init extends Command
|
|||||||
echo "Restoring coolify db backup\n";
|
echo "Restoring coolify db backup\n";
|
||||||
$database->restore();
|
$database->restore();
|
||||||
$scheduledBackup = ScheduledDatabaseBackup::find(0);
|
$scheduledBackup = ScheduledDatabaseBackup::find(0);
|
||||||
if (!$scheduledBackup) {
|
if (! $scheduledBackup) {
|
||||||
ScheduledDatabaseBackup::create([
|
ScheduledDatabaseBackup::create([
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
@@ -80,6 +88,7 @@ class Init extends Command
|
|||||||
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
|
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanup_stucked_helper_containers()
|
private function cleanup_stucked_helper_containers()
|
||||||
{
|
{
|
||||||
$servers = Server::all();
|
$servers = Server::all();
|
||||||
@@ -89,6 +98,7 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function alive()
|
private function alive()
|
||||||
{
|
{
|
||||||
$id = config('app.id');
|
$id = config('app.id');
|
||||||
@@ -97,6 +107,7 @@ class Init extends Command
|
|||||||
$do_not_track = data_get($settings, 'do_not_track');
|
$do_not_track = data_get($settings, 'do_not_track');
|
||||||
if ($do_not_track == true) {
|
if ($do_not_track == true) {
|
||||||
echo "Skipping alive as do_not_track is enabled\n";
|
echo "Skipping alive as do_not_track is enabled\n";
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -142,4 +153,15 @@ class Init extends Command
|
|||||||
echo "Error: {$e->getMessage()}\n";
|
echo "Error: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function replace_slash_in_environment_name()
|
||||||
|
{
|
||||||
|
$environments = Environment::all();
|
||||||
|
foreach ($environments as $environment) {
|
||||||
|
if (str_contains($environment->name, '/')) {
|
||||||
|
$environment->name = str_replace('/', '-', $environment->name);
|
||||||
|
$environment->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
use function Termwind\ask;
|
use function Termwind\ask;
|
||||||
use function Termwind\render;
|
use function Termwind\render;
|
||||||
use function Termwind\style;
|
use function Termwind\style;
|
||||||
@@ -32,6 +33,7 @@ class NotifyDemo extends Command
|
|||||||
|
|
||||||
if (blank($channel)) {
|
if (blank($channel)) {
|
||||||
$this->showHelp();
|
$this->showHelp();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class RootChangeEmail extends Command
|
|||||||
$this->info('Root user\'s email updated successfully.');
|
$this->info('Root user\'s email updated successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->error('Failed to update root user\'s email.');
|
$this->error('Failed to update root user\'s email.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ class RootResetPassword extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
//
|
|
||||||
$this->info('You are about to reset the root password.');
|
$this->info('You are about to reset the root password.');
|
||||||
$password = password('Give me a new password for root user: ');
|
$password = password('Give me a new password for root user: ');
|
||||||
$passwordAgain = password('Again');
|
$passwordAgain = password('Again');
|
||||||
if ($password != $passwordAgain) {
|
if ($password != $passwordAgain) {
|
||||||
$this->error('Passwords do not match.');
|
$this->error('Passwords do not match.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->info('Updating root password...');
|
$this->info('Updating root password...');
|
||||||
@@ -43,6 +43,7 @@ class RootResetPassword extends Command
|
|||||||
$this->info('Root password updated successfully.');
|
$this->info('Root password updated successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->error('Failed to update root password.');
|
$this->error('Failed to update root password.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ use Illuminate\Console\Command;
|
|||||||
class Scheduler extends Command
|
class Scheduler extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'start:scheduler';
|
protected $signature = 'start:scheduler';
|
||||||
|
|
||||||
protected $description = 'Start Scheduler';
|
protected $description = 'Start Scheduler';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
if (config('coolify.is_scheduler_enabled')) {
|
if (config('coolify.is_scheduler_enabled')) {
|
||||||
|
|||||||
@@ -48,11 +48,13 @@ class ServicesDelete extends Command
|
|||||||
$this->deleteServer();
|
$this->deleteServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deleteServer()
|
private function deleteServer()
|
||||||
{
|
{
|
||||||
$servers = Server::all();
|
$servers = Server::all();
|
||||||
if ($servers->count() === 0) {
|
if ($servers->count() === 0) {
|
||||||
$this->error('There are no applications to delete.');
|
$this->error('There are no applications to delete.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$serversToDelete = multiselect(
|
$serversToDelete = multiselect(
|
||||||
@@ -64,19 +66,21 @@ class ServicesDelete extends Command
|
|||||||
$toDelete = $servers->where('id', $server)->first();
|
$toDelete = $servers->where('id', $server)->first();
|
||||||
if ($toDelete) {
|
if ($toDelete) {
|
||||||
$this->info($toDelete);
|
$this->info($toDelete);
|
||||||
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
$confirmed = confirm('Are you sure you want to delete all selected resources?');
|
||||||
if (!$confirmed) {
|
if (! $confirmed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
$toDelete->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deleteApplication()
|
private function deleteApplication()
|
||||||
{
|
{
|
||||||
$applications = Application::all();
|
$applications = Application::all();
|
||||||
if ($applications->count() === 0) {
|
if ($applications->count() === 0) {
|
||||||
$this->error('There are no applications to delete.');
|
$this->error('There are no applications to delete.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$applicationsToDelete = multiselect(
|
$applicationsToDelete = multiselect(
|
||||||
@@ -88,19 +92,21 @@ class ServicesDelete extends Command
|
|||||||
$toDelete = $applications->where('id', $application)->first();
|
$toDelete = $applications->where('id', $application)->first();
|
||||||
if ($toDelete) {
|
if ($toDelete) {
|
||||||
$this->info($toDelete);
|
$this->info($toDelete);
|
||||||
$confirmed = confirm("Are you sure you want to delete all selected resources? ");
|
$confirmed = confirm('Are you sure you want to delete all selected resources? ');
|
||||||
if (!$confirmed) {
|
if (! $confirmed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DeleteResourceJob::dispatch($toDelete);
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deleteDatabase()
|
private function deleteDatabase()
|
||||||
{
|
{
|
||||||
$databases = StandalonePostgresql::all();
|
$databases = StandalonePostgresql::all();
|
||||||
if ($databases->count() === 0) {
|
if ($databases->count() === 0) {
|
||||||
$this->error('There are no databases to delete.');
|
$this->error('There are no databases to delete.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$databasesToDelete = multiselect(
|
$databasesToDelete = multiselect(
|
||||||
@@ -112,19 +118,21 @@ class ServicesDelete extends Command
|
|||||||
$toDelete = $databases->where('id', $database)->first();
|
$toDelete = $databases->where('id', $database)->first();
|
||||||
if ($toDelete) {
|
if ($toDelete) {
|
||||||
$this->info($toDelete);
|
$this->info($toDelete);
|
||||||
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
$confirmed = confirm('Are you sure you want to delete all selected resources?');
|
||||||
if (!$confirmed) {
|
if (! $confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DeleteResourceJob::dispatch($toDelete);
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deleteService()
|
private function deleteService()
|
||||||
{
|
{
|
||||||
$services = Service::all();
|
$services = Service::all();
|
||||||
if ($services->count() === 0) {
|
if ($services->count() === 0) {
|
||||||
$this->error('There are no services to delete.');
|
$this->error('There are no services to delete.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$servicesToDelete = multiselect(
|
$servicesToDelete = multiselect(
|
||||||
@@ -136,8 +144,8 @@ class ServicesDelete extends Command
|
|||||||
$toDelete = $services->where('id', $service)->first();
|
$toDelete = $services->where('id', $service)->first();
|
||||||
if ($toDelete) {
|
if ($toDelete) {
|
||||||
$this->info($toDelete);
|
$this->info($toDelete);
|
||||||
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
$confirmed = confirm('Are you sure you want to delete all selected resources?');
|
||||||
if (!$confirmed) {
|
if (! $confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DeleteResourceJob::dispatch($toDelete);
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ class ServicesGenerate extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
// ray()->clearAll();
|
|
||||||
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
||||||
$files = array_filter($files, function ($file) {
|
$files = array_filter($files, function ($file) {
|
||||||
return strpos($file, '.yaml') !== false;
|
return strpos($file, '.yaml') !== false;
|
||||||
@@ -40,7 +39,7 @@ class ServicesGenerate extends Command
|
|||||||
$serviceTemplatesJson[$name] = $parsed;
|
$serviceTemplatesJson[$name] = $parsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$serviceTemplatesJson = json_encode($serviceTemplatesJson, JSON_PRETTY_PRINT);
|
$serviceTemplatesJson = json_encode($serviceTemplatesJson);
|
||||||
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson);
|
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,18 +50,20 @@ class ServicesGenerate extends Command
|
|||||||
// $this->info($content);
|
// $this->info($content);
|
||||||
$ignore = collect(preg_grep('/^# ignore:/', explode("\n", $content)))->values();
|
$ignore = collect(preg_grep('/^# ignore:/', explode("\n", $content)))->values();
|
||||||
if ($ignore->count() > 0) {
|
if ($ignore->count() > 0) {
|
||||||
$ignore = (bool)str($ignore[0])->after('# ignore:')->trim()->value();
|
$ignore = (bool) str($ignore[0])->after('# ignore:')->trim()->value();
|
||||||
} else {
|
} else {
|
||||||
$ignore = false;
|
$ignore = false;
|
||||||
}
|
}
|
||||||
if ($ignore) {
|
if ($ignore) {
|
||||||
$this->info("Ignoring $file");
|
$this->info("Ignoring $file");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->info("Processing $file");
|
$this->info("Processing $file");
|
||||||
$documentation = collect(preg_grep('/^# documentation:/', explode("\n", $content)))->values();
|
$documentation = collect(preg_grep('/^# documentation:/', explode("\n", $content)))->values();
|
||||||
if ($documentation->count() > 0) {
|
if ($documentation->count() > 0) {
|
||||||
$documentation = str($documentation[0])->after('# documentation:')->trim()->value();
|
$documentation = str($documentation[0])->after('# documentation:')->trim()->value();
|
||||||
|
$documentation = str($documentation)->append('?utm_source=coolify.io');
|
||||||
} else {
|
} else {
|
||||||
$documentation = 'https://coolify.io/docs';
|
$documentation = 'https://coolify.io/docs';
|
||||||
}
|
}
|
||||||
@@ -125,6 +126,7 @@ class ServicesGenerate extends Command
|
|||||||
$env_file_base64 = base64_encode($env_file_content);
|
$env_file_base64 = base64_encode($env_file_content);
|
||||||
$payload['envs'] = $env_file_base64;
|
$payload['envs'] = $env_file_base64;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $payload;
|
return $payload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,30 +33,31 @@ class SyncBunny extends Command
|
|||||||
$that = $this;
|
$that = $this;
|
||||||
$only_template = $this->option('templates');
|
$only_template = $this->option('templates');
|
||||||
$only_version = $this->option('release');
|
$only_version = $this->option('release');
|
||||||
$bunny_cdn = "https://cdn.coollabs.io";
|
$bunny_cdn = 'https://cdn.coollabs.io';
|
||||||
$bunny_cdn_path = "coolify";
|
$bunny_cdn_path = 'coolify';
|
||||||
$bunny_cdn_storage_name = "coolcdn";
|
$bunny_cdn_storage_name = 'coolcdn';
|
||||||
|
|
||||||
$parent_dir = realpath(dirname(__FILE__) . '/../../..');
|
$parent_dir = realpath(dirname(__FILE__).'/../../..');
|
||||||
|
|
||||||
$compose_file = "docker-compose.yml";
|
$compose_file = 'docker-compose.yml';
|
||||||
$compose_file_prod = "docker-compose.prod.yml";
|
$compose_file_prod = 'docker-compose.prod.yml';
|
||||||
$install_script = "install.sh";
|
$install_script = 'install.sh';
|
||||||
$upgrade_script = "upgrade.sh";
|
$upgrade_script = 'upgrade.sh';
|
||||||
$production_env = ".env.production";
|
$production_env = '.env.production';
|
||||||
$service_template = "service-templates.json";
|
$service_template = 'service-templates.json';
|
||||||
|
|
||||||
$versions = "versions.json";
|
$versions = 'versions.json';
|
||||||
|
|
||||||
PendingRequest::macro('storage', function ($fileName) use ($that) {
|
PendingRequest::macro('storage', function ($fileName) use ($that) {
|
||||||
$headers = [
|
$headers = [
|
||||||
'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
|
'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
'Content-Type' => 'application/octet-stream'
|
'Content-Type' => 'application/octet-stream',
|
||||||
];
|
];
|
||||||
$fileStream = fopen($fileName, "r");
|
$fileStream = fopen($fileName, 'r');
|
||||||
$file = fread($fileStream, filesize($fileName));
|
$file = fread($fileStream, filesize($fileName));
|
||||||
$that->info('Uploading: ' . $fileName);
|
$that->info('Uploading: '.$fileName);
|
||||||
|
|
||||||
return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
|
return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
|
||||||
});
|
});
|
||||||
PendingRequest::macro('purge', function ($url) use ($that) {
|
PendingRequest::macro('purge', function ($url) use ($that) {
|
||||||
@@ -64,20 +65,21 @@ class SyncBunny extends Command
|
|||||||
'AccessKey' => env('BUNNY_API_KEY'),
|
'AccessKey' => env('BUNNY_API_KEY'),
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
];
|
];
|
||||||
$that->info('Purging: ' . $url);
|
$that->info('Purging: '.$url);
|
||||||
|
|
||||||
return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [
|
return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [
|
||||||
"url" => $url,
|
'url' => $url,
|
||||||
"async" => false
|
'async' => false,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
if (!$only_template && !$only_version) {
|
if (! $only_template && ! $only_version) {
|
||||||
$this->info('About to sync files (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.');
|
$this->info('About to sync files (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.');
|
||||||
}
|
}
|
||||||
if ($only_template) {
|
if ($only_template) {
|
||||||
$this->info('About to sync service-templates.json to BunnyCDN.');
|
$this->info('About to sync service-templates.json to BunnyCDN.');
|
||||||
$confirmed = confirm("Are you sure you want to sync?");
|
$confirmed = confirm('Are you sure you want to sync?');
|
||||||
if (!$confirmed) {
|
if (! $confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
@@ -85,15 +87,16 @@ class SyncBunny extends Command
|
|||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"),
|
||||||
]);
|
]);
|
||||||
$this->info('Service template uploaded & purged...');
|
$this->info('Service template uploaded & purged...');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else if ($only_version) {
|
} elseif ($only_version) {
|
||||||
$this->info('About to sync versions.json to BunnyCDN.');
|
$this->info('About to sync versions.json to BunnyCDN.');
|
||||||
$file = file_get_contents("$parent_dir/$versions");
|
$file = file_get_contents("$parent_dir/$versions");
|
||||||
$json = json_decode($file, true);
|
$json = json_decode($file, true);
|
||||||
$actual_version = data_get($json, 'coolify.v4.version');
|
$actual_version = data_get($json, 'coolify.v4.version');
|
||||||
|
|
||||||
$confirmed = confirm("Are you sure you want to sync to {$actual_version}?");
|
$confirmed = confirm("Are you sure you want to sync to {$actual_version}?");
|
||||||
if (!$confirmed) {
|
if (! $confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
@@ -101,10 +104,10 @@ class SyncBunny extends Command
|
|||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
||||||
]);
|
]);
|
||||||
$this->info('versions.json uploaded & purged...');
|
$this->info('versions.json uploaded & purged...');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->storage(fileName: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
|
$pool->storage(fileName: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
|
||||||
$pool->storage(fileName: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
$pool->storage(fileName: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
||||||
@@ -119,9 +122,9 @@ class SyncBunny extends Command
|
|||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
||||||
]);
|
]);
|
||||||
$this->info("All files uploaded & purged...");
|
$this->info('All files uploaded & purged...');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->error("Error: " . $e->getMessage());
|
$this->error('Error: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ use Illuminate\Support\Str;
|
|||||||
class WaitlistInvite extends Command
|
class WaitlistInvite extends Command
|
||||||
{
|
{
|
||||||
public Waitlist|User|null $next_patient = null;
|
public Waitlist|User|null $next_patient = null;
|
||||||
public string|null $password = null;
|
|
||||||
|
public ?string $password = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
@@ -38,7 +40,9 @@ class WaitlistInvite extends Command
|
|||||||
$this->main();
|
$this->main();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function main() {
|
|
||||||
|
private function main()
|
||||||
|
{
|
||||||
if ($this->argument('email')) {
|
if ($this->argument('email')) {
|
||||||
if ($this->option('only-email')) {
|
if ($this->option('only-email')) {
|
||||||
$this->next_patient = User::whereEmail($this->argument('email'))->first();
|
$this->next_patient = User::whereEmail($this->argument('email'))->first();
|
||||||
@@ -50,8 +54,9 @@ class WaitlistInvite extends Command
|
|||||||
} else {
|
} else {
|
||||||
$this->next_patient = Waitlist::where('email', $this->argument('email'))->first();
|
$this->next_patient = Waitlist::where('email', $this->argument('email'))->first();
|
||||||
}
|
}
|
||||||
if (!$this->next_patient) {
|
if (! $this->next_patient) {
|
||||||
$this->error("{$this->argument('email')} not found in the waitlist.");
|
$this->error("{$this->argument('email')} not found in the waitlist.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -60,6 +65,7 @@ class WaitlistInvite extends Command
|
|||||||
if ($this->next_patient) {
|
if ($this->next_patient) {
|
||||||
if ($this->option('only-email')) {
|
if ($this->option('only-email')) {
|
||||||
$this->send_email();
|
$this->send_email();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->register_user();
|
$this->register_user();
|
||||||
@@ -69,10 +75,11 @@ class WaitlistInvite extends Command
|
|||||||
$this->info('No verified user found in the waitlist. 👀');
|
$this->info('No verified user found in the waitlist. 👀');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function register_user()
|
private function register_user()
|
||||||
{
|
{
|
||||||
$already_registered = User::whereEmail($this->next_patient->email)->first();
|
$already_registered = User::whereEmail($this->next_patient->email)->first();
|
||||||
if (!$already_registered) {
|
if (! $already_registered) {
|
||||||
$this->password = Str::password();
|
$this->password = Str::password();
|
||||||
User::create([
|
User::create([
|
||||||
'name' => Str::of($this->next_patient->email)->before('@'),
|
'name' => Str::of($this->next_patient->email)->before('@'),
|
||||||
@@ -85,11 +92,13 @@ class WaitlistInvite extends Command
|
|||||||
throw new \Exception('User already registered');
|
throw new \Exception('User already registered');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function remove_from_waitlist()
|
private function remove_from_waitlist()
|
||||||
{
|
{
|
||||||
$this->next_patient->delete();
|
$this->next_patient->delete();
|
||||||
$this->info("User removed from waitlist successfully.");
|
$this->info('User removed from waitlist successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function send_email()
|
private function send_email()
|
||||||
{
|
{
|
||||||
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
||||||
@@ -100,6 +109,6 @@ class WaitlistInvite extends Command
|
|||||||
]);
|
]);
|
||||||
$mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
$mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
||||||
send_user_an_email($mail, $this->next_patient->email);
|
send_user_an_email($mail, $this->next_patient->email);
|
||||||
$this->info("Email sent successfully. 📧");
|
$this->info('Email sent successfully. 📧');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,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\ScheduledTaskJob;
|
|
||||||
use App\Jobs\InstanceAutoUpdateJob;
|
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
|
use App\Jobs\DatabaseBackupJob;
|
||||||
|
use App\Jobs\PullCoolifyImageJob;
|
||||||
use App\Jobs\PullHelperImageJob;
|
use App\Jobs\PullHelperImageJob;
|
||||||
|
use App\Jobs\PullSentinelImageJob;
|
||||||
|
use App\Jobs\PullTemplatesFromCDN;
|
||||||
|
use App\Jobs\ScheduledTaskJob;
|
||||||
use App\Jobs\ServerStatusJob;
|
use App\Jobs\ServerStatusJob;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@@ -20,75 +21,75 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
|||||||
|
|
||||||
class Kernel extends ConsoleKernel
|
class Kernel extends ConsoleKernel
|
||||||
{
|
{
|
||||||
|
private $all_servers;
|
||||||
|
|
||||||
protected function schedule(Schedule $schedule): void
|
protected function schedule(Schedule $schedule): void
|
||||||
{
|
{
|
||||||
|
$this->all_servers = Server::all();
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
$schedule->command('horizon:snapshot')->everyMinute();
|
$schedule->command('horizon:snapshot')->everyMinute();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
||||||
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
$schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
|
||||||
// Server Jobs
|
// Server Jobs
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->pull_helper_image($schedule);
|
|
||||||
$this->check_scheduled_tasks($schedule);
|
$this->check_scheduled_tasks($schedule);
|
||||||
|
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||||
} else {
|
} else {
|
||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
$schedule->command('cleanup:unreachable-servers')->daily();
|
$schedule->command('cleanup:unreachable-servers')->daily();
|
||||||
|
$schedule->job(new PullCoolifyImageJob)->everyTenMinutes()->onOneServer();
|
||||||
|
$schedule->job(new PullTemplatesFromCDN)->everyThirtyMinutes()->onOneServer();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||||
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
|
|
||||||
// Server Jobs
|
// Server Jobs
|
||||||
$this->instance_auto_update($schedule);
|
|
||||||
$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_images($schedule);
|
||||||
$this->check_scheduled_tasks($schedule);
|
$this->check_scheduled_tasks($schedule);
|
||||||
|
|
||||||
$schedule->command('cleanup:database --yes')->daily();
|
$schedule->command('cleanup:database --yes')->daily();
|
||||||
|
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function pull_helper_image($schedule)
|
|
||||||
|
private function pull_images($schedule)
|
||||||
{
|
{
|
||||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new PullHelperImageJob($server))->everyTenMinutes()->onOneServer();
|
if ($server->isSentinelEnabled()) {
|
||||||
|
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||||
|
}
|
||||||
|
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function check_resources($schedule)
|
private function check_resources($schedule)
|
||||||
{
|
{
|
||||||
if (isCloud()) {
|
if (isCloud()) {
|
||||||
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
$servers = $this->all_servers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
||||||
$own = Team::find(0)->servers;
|
$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);
|
$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 = $this->all_servers->where('ip', '!=', '1.2.3.4');
|
||||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||||
}
|
}
|
||||||
foreach ($containerServers as $server) {
|
foreach ($containerServers as $server) {
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
if ($server->isLogDrainEnabled()) {
|
if ($server->isLogDrainEnabled()) {
|
||||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyTwoMinutes()->onOneServer();
|
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ServerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
}
|
|
||||||
}
|
|
||||||
private function instance_auto_update($schedule)
|
|
||||||
{
|
|
||||||
if (isDev()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$settings = InstanceSettings::get();
|
|
||||||
if ($settings->is_auto_update_enabled) {
|
|
||||||
$schedule->job(new InstanceAutoUpdateJob)->everyTenMinutes()->onOneServer();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function check_scheduled_backups($schedule)
|
private function check_scheduled_backups($schedule)
|
||||||
{
|
{
|
||||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||||
@@ -96,12 +97,13 @@ class Kernel extends ConsoleKernel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach ($scheduled_backups as $scheduled_backup) {
|
foreach ($scheduled_backups as $scheduled_backup) {
|
||||||
if (!$scheduled_backup->enabled) {
|
if (! $scheduled_backup->enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (is_null(data_get($scheduled_backup, 'database'))) {
|
if (is_null(data_get($scheduled_backup, 'database'))) {
|
||||||
ray('database not found');
|
ray('database not found');
|
||||||
$scheduled_backup->delete();
|
$scheduled_backup->delete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,15 +123,28 @@ class Kernel extends ConsoleKernel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach ($scheduled_tasks as $scheduled_task) {
|
foreach ($scheduled_tasks as $scheduled_task) {
|
||||||
|
if ($scheduled_task->enabled === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$service = $scheduled_task->service;
|
$service = $scheduled_task->service;
|
||||||
$application = $scheduled_task->application;
|
$application = $scheduled_task->application;
|
||||||
|
|
||||||
if (!$application && !$service) {
|
if (! $application && ! $service) {
|
||||||
ray('application/service attached to scheduled task does not exist');
|
ray('application/service attached to scheduled task does not exist');
|
||||||
$scheduled_task->delete();
|
$scheduled_task->delete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if ($application) {
|
||||||
|
if (str($application->status)->contains('running') === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($service) {
|
||||||
|
if (str($service->status())->contains('running') === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
|
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
|
||||||
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
|
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
|
||||||
}
|
}
|
||||||
@@ -141,7 +156,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
protected function commands(): void
|
protected function commands(): void
|
||||||
{
|
{
|
||||||
$this->load(__DIR__ . '/Commands');
|
$this->load(__DIR__.'/Commands');
|
||||||
|
|
||||||
require base_path('routes/console.php');
|
require base_path('routes/console.php');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,18 +12,18 @@ use Spatie\LaravelData\Data;
|
|||||||
class CoolifyTaskArgs extends Data
|
class CoolifyTaskArgs extends Data
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $server_uuid,
|
public string $server_uuid,
|
||||||
public string $command,
|
public string $command,
|
||||||
public string $type,
|
public string $type,
|
||||||
public ?string $type_uuid = null,
|
public ?string $type_uuid = null,
|
||||||
public ?int $process_id = null,
|
public ?int $process_id = null,
|
||||||
public ?Model $model = null,
|
public ?Model $model = null,
|
||||||
public ?string $status = null ,
|
public ?string $status = null,
|
||||||
public bool $ignore_errors = false,
|
public bool $ignore_errors = false,
|
||||||
public $call_event_on_finish = null,
|
public $call_event_on_finish = null,
|
||||||
public $call_event_data = null
|
public $call_event_data = null
|
||||||
) {
|
) {
|
||||||
if(is_null($status)){
|
if (is_null($status)) {
|
||||||
$this->status = ProcessStatus::QUEUED->value;
|
$this->status = ProcessStatus::QUEUED->value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ use Spatie\LaravelData\Data;
|
|||||||
class ServerMetadata extends Data
|
class ServerMetadata extends Data
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public ?ProxyTypes $type,
|
public ?ProxyTypes $type,
|
||||||
public ?ProxyStatus $status
|
public ?ProxyStatus $status
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Broadcasting\PresenceChannel;
|
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@@ -13,14 +11,16 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class ApplicationStatusChanged implements ShouldBroadcast
|
class ApplicationStatusChanged implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public $teamId;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
throw new \Exception("Team id is null");
|
throw new \Exception('Team id is null');
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Broadcasting\PresenceChannel;
|
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@@ -13,14 +11,16 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class BackupCreated implements ShouldBroadcast
|
class BackupCreated implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public $teamId;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
throw new \Exception("Team id is null");
|
throw new \Exception('Team id is null');
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Broadcasting\PresenceChannel;
|
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@@ -13,14 +11,16 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class DatabaseStatusChanged implements ShouldBroadcast
|
class DatabaseStatusChanged implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $userId;
|
public $userId;
|
||||||
|
|
||||||
public function __construct($userId = null)
|
public function __construct($userId = null)
|
||||||
{
|
{
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
$userId = auth()->user()->id ?? null;
|
$userId = auth()->user()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
throw new \Exception("User id is null");
|
throw new \Exception('User id is null');
|
||||||
}
|
}
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class ProxyStarted
|
class ProxyStarted
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
public function __construct(public $data)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
public function __construct(public $data) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Broadcasting\PresenceChannel;
|
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@@ -13,14 +11,16 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class ProxyStatusChanged implements ShouldBroadcast
|
class ProxyStatusChanged implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public $teamId;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
throw new \Exception("Team id is null");
|
throw new \Exception('Team id is null');
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Broadcasting\PresenceChannel;
|
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@@ -13,14 +11,16 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class ServiceStatusChanged implements ShouldBroadcast
|
class ServiceStatusChanged implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $userId;
|
public $userId;
|
||||||
|
|
||||||
public function __construct($userId = null)
|
public function __construct($userId = null)
|
||||||
{
|
{
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
$userId = auth()->user()->id ?? null;
|
$userId = auth()->user()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
throw new \Exception("User id is null");
|
throw new \Exception('User id is null');
|
||||||
}
|
}
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Broadcasting\PresenceChannel;
|
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@@ -13,7 +11,9 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class TestEvent implements ShouldBroadcast
|
class TestEvent implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public $teamId;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->teamId = auth()->user()->currentTeam()->id;
|
$this->teamId = auth()->user()->currentTeam()->id;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ use Throwable;
|
|||||||
|
|
||||||
class Handler extends ExceptionHandler
|
class Handler extends ExceptionHandler
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of exception types with their corresponding custom log levels.
|
* A list of exception types with their corresponding custom log levels.
|
||||||
*
|
*
|
||||||
@@ -22,14 +21,16 @@ class Handler extends ExceptionHandler
|
|||||||
protected $levels = [
|
protected $levels = [
|
||||||
//
|
//
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of the exception types that are not reported.
|
* A list of the exception types that are not reported.
|
||||||
*
|
*
|
||||||
* @var array<int, class-string<\Throwable>>
|
* @var array<int, class-string<\Throwable>>
|
||||||
*/
|
*/
|
||||||
protected $dontReport = [
|
protected $dontReport = [
|
||||||
ProcessException::class
|
ProcessException::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of the inputs that are never flashed to the session on validation exceptions.
|
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||||
*
|
*
|
||||||
@@ -40,6 +41,7 @@ class Handler extends ExceptionHandler
|
|||||||
'password',
|
'password',
|
||||||
'password_confirmation',
|
'password_confirmation',
|
||||||
];
|
];
|
||||||
|
|
||||||
private InstanceSettings $settings;
|
private InstanceSettings $settings;
|
||||||
|
|
||||||
protected function unauthenticated($request, AuthenticationException $exception)
|
protected function unauthenticated($request, AuthenticationException $exception)
|
||||||
@@ -47,8 +49,10 @@ class Handler extends ExceptionHandler
|
|||||||
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
|
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
|
||||||
return response()->json(['message' => $exception->getMessage()], 401);
|
return response()->json(['message' => $exception->getMessage()], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->guest($exception->redirectTo() ?? route('login'));
|
return redirect()->guest($exception->redirectTo() ?? route('login'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the exception handling callbacks for the application.
|
* Register the exception handling callbacks for the application.
|
||||||
*/
|
*/
|
||||||
@@ -56,7 +60,7 @@ class Handler extends ExceptionHandler
|
|||||||
{
|
{
|
||||||
$this->reportable(function (Throwable $e) {
|
$this->reportable(function (Throwable $e) {
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
// return;
|
return;
|
||||||
}
|
}
|
||||||
if ($e instanceof RuntimeException) {
|
if ($e instanceof RuntimeException) {
|
||||||
return;
|
return;
|
||||||
@@ -72,7 +76,7 @@ class Handler extends ExceptionHandler
|
|||||||
$scope->setUser(
|
$scope->setUser(
|
||||||
[
|
[
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'instanceAdmin' => $instanceAdmin
|
'instanceAdmin' => $instanceAdmin,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,4 @@ namespace App\Exceptions;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
class ProcessException extends Exception
|
class ProcessException extends Exception {}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
183
app/Http/Controllers/Api/Applications.php
Normal file
183
app/Http/Controllers/Api/Applications.php
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Actions\Application\StopApplication;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Project;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Applications extends Controller
|
||||||
|
{
|
||||||
|
public function applications(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$projects = Project::where('team_id', $teamId)->get();
|
||||||
|
$applications = collect();
|
||||||
|
$applications->push($projects->pluck('applications')->flatten());
|
||||||
|
$applications = $applications->flatten();
|
||||||
|
|
||||||
|
return response()->json($applications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function application_by_uuid(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $uuid)->first();
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json(['error' => 'Application not found.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json($application);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update_by_uuid(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->collect()->count() == 0) {
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'No data provided.',
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $request->uuid)->first();
|
||||||
|
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Application not found',
|
||||||
|
], 404);
|
||||||
|
}
|
||||||
|
ray($request->collect());
|
||||||
|
|
||||||
|
// if ($request->has('domains')) {
|
||||||
|
// $existingDomains = explode(',', $application->fqdn);
|
||||||
|
// $newDomains = $request->domains;
|
||||||
|
// $filteredNewDomains = array_filter($newDomains, function ($domain) use ($existingDomains) {
|
||||||
|
// return ! in_array($domain, $existingDomains);
|
||||||
|
// });
|
||||||
|
// $mergedDomains = array_unique(array_merge($existingDomains, $filteredNewDomains));
|
||||||
|
// $application->fqdn = implode(',', $mergedDomains);
|
||||||
|
// $application->custom_labels = base64_encode(implode("\n ", generateLabelsApplication($application)));
|
||||||
|
// $application->save();
|
||||||
|
// }
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'Application updated successfully.',
|
||||||
|
'application' => serialize_api_response($application),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function action_deploy(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$force = $request->query->get('force') ?? false;
|
||||||
|
$instant_deploy = $request->query->get('instant_deploy') ?? false;
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $uuid)->first();
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json(['error' => 'Application not found.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: $force,
|
||||||
|
is_api: true,
|
||||||
|
no_questions_asked: $instant_deploy
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json(
|
||||||
|
[
|
||||||
|
'message' => 'Deployment request queued.',
|
||||||
|
'deployment_uuid' => $deployment_uuid->toString(),
|
||||||
|
'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function action_stop(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
$sync = $request->query->get('sync') ?? false;
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $uuid)->first();
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json(['error' => 'Application not found.'], 404);
|
||||||
|
}
|
||||||
|
if ($sync) {
|
||||||
|
StopApplication::run($application);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Stopped the application.'], 200);
|
||||||
|
} else {
|
||||||
|
StopApplication::dispatch($application);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Stopping request queued.'], 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function action_restart(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$application = Application::where('uuid', $uuid)->first();
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json(['error' => 'Application not found.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
restart_only: true,
|
||||||
|
is_api: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json(
|
||||||
|
[
|
||||||
|
'message' => 'Restart request queued.',
|
||||||
|
'deployment_uuid' => $deployment_uuid->toString(),
|
||||||
|
'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
|
||||||
|
],
|
||||||
|
200
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartClickhouse;
|
||||||
|
use App\Actions\Database\StartDragonfly;
|
||||||
|
use App\Actions\Database\StartKeydb;
|
||||||
use App\Actions\Database\StartMariadb;
|
use App\Actions\Database\StartMariadb;
|
||||||
use App\Actions\Database\StartMongodb;
|
use App\Actions\Database\StartMongodb;
|
||||||
use App\Actions\Database\StartMysql;
|
use App\Actions\Database\StartMysql;
|
||||||
@@ -24,18 +27,38 @@ class Deploy extends Controller
|
|||||||
return invalid_token();
|
return invalid_token();
|
||||||
}
|
}
|
||||||
$servers = Server::whereTeamId($teamId)->get();
|
$servers = Server::whereTeamId($teamId)->get();
|
||||||
$deployments_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn("server_id", $servers->pluck("id"))->get([
|
$deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get([
|
||||||
"id",
|
'id',
|
||||||
"application_id",
|
'application_id',
|
||||||
"application_name",
|
'application_name',
|
||||||
"deployment_url",
|
'deployment_url',
|
||||||
"pull_request_id",
|
'pull_request_id',
|
||||||
"server_name",
|
'server_name',
|
||||||
"server_id",
|
'server_id',
|
||||||
"status"
|
'status',
|
||||||
])->sortBy('id')->toArray();
|
])->sortBy('id')->toArray();
|
||||||
return response()->json($deployments_per_server, 200);
|
|
||||||
|
return response()->json(serialize_api_response($deployments_per_server), 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deployment_by_uuid(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->route('uuid');
|
||||||
|
if (! $uuid) {
|
||||||
|
return response()->json(['error' => 'UUID is required.'], 400);
|
||||||
|
}
|
||||||
|
$deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first()->makeHidden('logs');
|
||||||
|
if (! $deployment) {
|
||||||
|
return response()->json(['error' => 'Deployment not found.'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json(serialize_api_response($deployment), 200);
|
||||||
|
}
|
||||||
|
|
||||||
public function deploy(Request $request)
|
public function deploy(Request $request)
|
||||||
{
|
{
|
||||||
$teamId = get_team_id_from_token();
|
$teamId = get_team_id_from_token();
|
||||||
@@ -51,11 +74,13 @@ class Deploy extends Controller
|
|||||||
}
|
}
|
||||||
if ($tags) {
|
if ($tags) {
|
||||||
return $this->by_tags($tags, $teamId, $force);
|
return $this->by_tags($tags, $teamId, $force);
|
||||||
} else if ($uuids) {
|
} elseif ($uuids) {
|
||||||
return $this->by_uuids($uuids, $teamId, $force);
|
return $this->by_uuids($uuids, $teamId, $force);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
return response()->json(['error' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function by_uuids(string $uuid, int $teamId, bool $force = false)
|
private function by_uuids(string $uuid, int $teamId, bool $force = false)
|
||||||
{
|
{
|
||||||
$uuids = explode(',', $uuid);
|
$uuids = explode(',', $uuid);
|
||||||
@@ -79,10 +104,13 @@ class Deploy extends Controller
|
|||||||
}
|
}
|
||||||
if ($deployments->count() > 0) {
|
if ($deployments->count() > 0) {
|
||||||
$payload->put('deployments', $deployments->toArray());
|
$payload->put('deployments', $deployments->toArray());
|
||||||
|
|
||||||
return response()->json($payload->toArray(), 200);
|
return response()->json($payload->toArray(), 200);
|
||||||
}
|
}
|
||||||
return response()->json(['error' => "No resources found.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
|
|
||||||
|
return response()->json(['error' => 'No resources found.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function by_tags(string $tags, int $team_id, bool $force = false)
|
public function by_tags(string $tags, int $team_id, bool $force = false)
|
||||||
{
|
{
|
||||||
$tags = explode(',', $tags);
|
$tags = explode(',', $tags);
|
||||||
@@ -96,7 +124,7 @@ class Deploy extends Controller
|
|||||||
$payload = collect();
|
$payload = collect();
|
||||||
foreach ($tags as $tag) {
|
foreach ($tags as $tag) {
|
||||||
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
|
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
|
||||||
if (!$found_tag) {
|
if (! $found_tag) {
|
||||||
// $message->push("Tag {$tag} not found.");
|
// $message->push("Tag {$tag} not found.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -104,6 +132,7 @@ class Deploy extends Controller
|
|||||||
$services = $found_tag->services()->get();
|
$services = $found_tag->services()->get();
|
||||||
if ($applications->count() === 0 && $services->count() === 0) {
|
if ($applications->count() === 0 && $services->count() === 0) {
|
||||||
$message->push("No resources found for tag {$tag}.");
|
$message->push("No resources found for tag {$tag}.");
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
foreach ($applications as $resource) {
|
foreach ($applications as $resource) {
|
||||||
@@ -124,11 +153,13 @@ class Deploy extends Controller
|
|||||||
if ($deployments->count() > 0) {
|
if ($deployments->count() > 0) {
|
||||||
$payload->put('details', $deployments->toArray());
|
$payload->put('details', $deployments->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json($payload->toArray(), 200);
|
return response()->json($payload->toArray(), 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['error' => "No resources found with this tag.", 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
|
return response()->json(['error' => 'No resources found with this tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deploy_resource($resource, bool $force = false): array
|
public function deploy_resource($resource, bool $force = false): array
|
||||||
{
|
{
|
||||||
$message = null;
|
$message = null;
|
||||||
@@ -145,40 +176,59 @@ class Deploy extends Controller
|
|||||||
force_rebuild: $force,
|
force_rebuild: $force,
|
||||||
);
|
);
|
||||||
$message = "Application {$resource->name} deployment queued.";
|
$message = "Application {$resource->name} deployment queued.";
|
||||||
} else if ($type === 'App\Models\StandalonePostgresql') {
|
} elseif ($type === 'App\Models\StandalonePostgresql') {
|
||||||
StartPostgresql::run($resource);
|
StartPostgresql::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message = "Database {$resource->name} started.";
|
||||||
} else if ($type === 'App\Models\StandaloneRedis') {
|
} elseif ($type === 'App\Models\StandaloneRedis') {
|
||||||
StartRedis::run($resource);
|
StartRedis::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message = "Database {$resource->name} started.";
|
||||||
} else if ($type === 'App\Models\StandaloneMongodb') {
|
} elseif ($type === 'App\Models\StandaloneKeydb') {
|
||||||
|
StartKeydb::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} elseif ($type === 'App\Models\StandaloneDragonfly') {
|
||||||
|
StartDragonfly::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} elseif ($type === 'App\Models\StandaloneClickhouse') {
|
||||||
|
StartClickhouse::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} elseif ($type === 'App\Models\StandaloneMongodb') {
|
||||||
StartMongodb::run($resource);
|
StartMongodb::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message = "Database {$resource->name} started.";
|
||||||
} else if ($type === 'App\Models\StandaloneMysql') {
|
} elseif ($type === 'App\Models\StandaloneMysql') {
|
||||||
StartMysql::run($resource);
|
StartMysql::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message = "Database {$resource->name} started.";
|
||||||
} else if ($type === 'App\Models\StandaloneMariadb') {
|
} elseif ($type === 'App\Models\StandaloneMariadb') {
|
||||||
StartMariadb::run($resource);
|
StartMariadb::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message = "Database {$resource->name} started.";
|
||||||
} else if ($type === 'App\Models\Service') {
|
} elseif ($type === 'App\Models\Service') {
|
||||||
StartService::run($resource);
|
StartService::run($resource);
|
||||||
$message = "Service {$resource->name} started. It could take a while, be patient.";
|
$message = "Service {$resource->name} started. It could take a while, be patient.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
|
return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,102 +3,52 @@
|
|||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\Application;
|
||||||
use App\Models\Project as ModelsProject;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
class Domains extends Controller
|
class Domains extends Controller
|
||||||
{
|
{
|
||||||
public function domains(Request $request)
|
public function deleteDomains(Request $request)
|
||||||
{
|
{
|
||||||
$teamId = get_team_id_from_token();
|
$teamId = get_team_id_from_token();
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
return invalid_token();
|
return invalid_token();
|
||||||
}
|
}
|
||||||
$projects = ModelsProject::where('team_id', $teamId)->get();
|
$validator = Validator::make($request->all(), [
|
||||||
$domains = collect();
|
'uuid' => 'required|string|exists:applications,uuid',
|
||||||
$applications = $projects->pluck('applications')->flatten();
|
'domains' => 'required|array',
|
||||||
$settings = InstanceSettings::get();
|
'domains.*' => 'required|string|distinct',
|
||||||
if ($applications->count() > 0) {
|
]);
|
||||||
foreach ($applications as $application) {
|
|
||||||
$ip = $application->destination->server->ip;
|
|
||||||
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
|
|
||||||
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
|
|
||||||
});
|
|
||||||
if ($ip === 'host.docker.internal') {
|
|
||||||
if ($settings->public_ipv4) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $settings->public_ipv4,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if ($settings->public_ipv6) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $settings->public_ipv6,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (!$settings->public_ipv4 && !$settings->public_ipv6) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $ip,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $ip,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$services = $projects->pluck('services')->flatten();
|
|
||||||
if ($services->count() > 0) {
|
|
||||||
foreach ($services as $service) {
|
|
||||||
$service_applications = $service->applications;
|
|
||||||
if ($service_applications->count() > 0) {
|
|
||||||
foreach ($service_applications as $application) {
|
|
||||||
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
|
|
||||||
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
|
|
||||||
});
|
|
||||||
if ($ip === 'host.docker.internal') {
|
|
||||||
if ($settings->public_ipv4) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $settings->public_ipv4,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if ($settings->public_ipv6) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $settings->public_ipv6,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (!$settings->public_ipv4 && !$settings->public_ipv6) {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $ip,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$domains->push([
|
|
||||||
'domain' => $fqdn,
|
|
||||||
'ip' => $ip,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$domains = $domains->groupBy('ip')->map(function ($domain) {
|
|
||||||
return $domain->pluck('domain')->flatten();
|
|
||||||
})->map(function ($domain, $ip) {
|
|
||||||
return [
|
|
||||||
'ip' => $ip,
|
|
||||||
'domains' => $domain,
|
|
||||||
];
|
|
||||||
})->values();
|
|
||||||
|
|
||||||
return response()->json($domains);
|
if ($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Validation failed',
|
||||||
|
'errors' => $validator->errors(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$application = Application::where('uuid', $request->uuid)->first();
|
||||||
|
|
||||||
|
if (! $application) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Application not found',
|
||||||
|
], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingDomains = explode(',', $application->fqdn);
|
||||||
|
$domainsToDelete = $request->domains;
|
||||||
|
$updatedDomains = array_diff($existingDomains, $domainsToDelete);
|
||||||
|
$application->fqdn = implode(',', $updatedDomains);
|
||||||
|
$application->custom_labels = base64_encode(implode("\n ", generateLabelsApplication($application)));
|
||||||
|
$application->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Domains updated successfully',
|
||||||
|
'application' => $application,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,10 @@ class Project extends Controller
|
|||||||
return invalid_token();
|
return invalid_token();
|
||||||
}
|
}
|
||||||
$projects = ModelsProject::whereTeamId($teamId)->select('id', 'name', 'uuid')->get();
|
$projects = ModelsProject::whereTeamId($teamId)->select('id', 'name', 'uuid')->get();
|
||||||
|
|
||||||
return response()->json($projects);
|
return response()->json($projects);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function project_by_uuid(Request $request)
|
public function project_by_uuid(Request $request)
|
||||||
{
|
{
|
||||||
$teamId = get_team_id_from_token();
|
$teamId = get_team_id_from_token();
|
||||||
@@ -24,8 +26,10 @@ class Project extends Controller
|
|||||||
return invalid_token();
|
return invalid_token();
|
||||||
}
|
}
|
||||||
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']);
|
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']);
|
||||||
|
|
||||||
return response()->json($project);
|
return response()->json($project);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function environment_details(Request $request)
|
public function environment_details(Request $request)
|
||||||
{
|
{
|
||||||
$teamId = get_team_id_from_token();
|
$teamId = get_team_id_from_token();
|
||||||
@@ -34,6 +38,7 @@ class Project extends Controller
|
|||||||
}
|
}
|
||||||
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
|
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
|
||||||
$environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']);
|
$environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']);
|
||||||
|
|
||||||
return response()->json($environment);
|
return response()->json($environment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ class Resources extends Controller
|
|||||||
$payload['status'] = $resource->status;
|
$payload['status'] = $resource->status;
|
||||||
}
|
}
|
||||||
$payload['type'] = $resource->type();
|
$payload['type'] = $resource->type();
|
||||||
|
|
||||||
return $payload;
|
return $payload;
|
||||||
});
|
});
|
||||||
|
|
||||||
return response()->json($resources);
|
return response()->json($resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\Project;
|
||||||
use App\Models\Server as ModelsServer;
|
use App\Models\Server as ModelsServer;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
@@ -17,10 +20,13 @@ class Server extends Controller
|
|||||||
$servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) {
|
$servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) {
|
||||||
$server['is_reachable'] = $server->settings->is_reachable;
|
$server['is_reachable'] = $server->settings->is_reachable;
|
||||||
$server['is_usable'] = $server->settings->is_usable;
|
$server['is_usable'] = $server->settings->is_usable;
|
||||||
|
|
||||||
return $server;
|
return $server;
|
||||||
});
|
});
|
||||||
|
|
||||||
return response()->json($servers);
|
return response()->json($servers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function server_by_uuid(Request $request)
|
public function server_by_uuid(Request $request)
|
||||||
{
|
{
|
||||||
$with_resources = $request->query('resources');
|
$with_resources = $request->query('resources');
|
||||||
@@ -47,11 +53,115 @@ class Server extends Controller
|
|||||||
} else {
|
} else {
|
||||||
$payload['status'] = $resource->status;
|
$payload['status'] = $resource->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $payload;
|
return $payload;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$server->load(['settings']);
|
$server->load(['settings']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json($server);
|
return response()->json($server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function get_domains_by_server(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$uuid = $request->query->get('uuid');
|
||||||
|
if ($uuid) {
|
||||||
|
$domains = Application::getDomainsByUuid($uuid);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'uuid' => $uuid,
|
||||||
|
'domains' => $domains,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$projects = Project::where('team_id', $teamId)->get();
|
||||||
|
$domains = collect();
|
||||||
|
$applications = $projects->pluck('applications')->flatten();
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if ($applications->count() > 0) {
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$ip = $application->destination->server->ip;
|
||||||
|
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
|
||||||
|
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
|
||||||
|
});
|
||||||
|
if ($ip === 'host.docker.internal') {
|
||||||
|
if ($settings->public_ipv4) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $settings->public_ipv4,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($settings->public_ipv6) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $settings->public_ipv6,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $ip,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $ip,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$services = $projects->pluck('services')->flatten();
|
||||||
|
if ($services->count() > 0) {
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$service_applications = $service->applications;
|
||||||
|
if ($service_applications->count() > 0) {
|
||||||
|
foreach ($service_applications as $application) {
|
||||||
|
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
|
||||||
|
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
|
||||||
|
});
|
||||||
|
if ($ip === 'host.docker.internal') {
|
||||||
|
if ($settings->public_ipv4) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $settings->public_ipv4,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($settings->public_ipv6) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $settings->public_ipv6,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $ip,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$domains->push([
|
||||||
|
'domain' => $fqdn,
|
||||||
|
'ip' => $ip,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$domains = $domains->groupBy('ip')->map(function ($domain) {
|
||||||
|
return $domain->pluck('domain')->flatten();
|
||||||
|
})->map(function ($domain, $ip) {
|
||||||
|
return [
|
||||||
|
'ip' => $ip,
|
||||||
|
'domains' => $domain,
|
||||||
|
];
|
||||||
|
})->values();
|
||||||
|
|
||||||
|
return response()->json($domains);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ class Team extends Controller
|
|||||||
return invalid_token();
|
return invalid_token();
|
||||||
}
|
}
|
||||||
$teams = auth()->user()->teams;
|
$teams = auth()->user()->teams;
|
||||||
|
|
||||||
return response()->json($teams);
|
return response()->json($teams);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function team_by_id(Request $request)
|
public function team_by_id(Request $request)
|
||||||
{
|
{
|
||||||
$id = $request->id;
|
$id = $request->id;
|
||||||
@@ -26,10 +28,12 @@ class Team extends Controller
|
|||||||
$teams = auth()->user()->teams;
|
$teams = auth()->user()->teams;
|
||||||
$team = $teams->where('id', $id)->first();
|
$team = $teams->where('id', $id)->first();
|
||||||
if (is_null($team)) {
|
if (is_null($team)) {
|
||||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid"], 404);
|
return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json($team);
|
return response()->json($team);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function members_by_id(Request $request)
|
public function members_by_id(Request $request)
|
||||||
{
|
{
|
||||||
$id = $request->id;
|
$id = $request->id;
|
||||||
@@ -40,10 +44,12 @@ class Team extends Controller
|
|||||||
$teams = auth()->user()->teams;
|
$teams = auth()->user()->teams;
|
||||||
$team = $teams->where('id', $id)->first();
|
$team = $teams->where('id', $id)->first();
|
||||||
if (is_null($team)) {
|
if (is_null($team)) {
|
||||||
return response()->json(['error' => 'Team not found.', "docs" => "https://coolify.io/docs/api-reference/get-team-by-teamid-members"], 404);
|
return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid-members'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json($team->members);
|
return response()->json($team->members);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function current_team(Request $request)
|
public function current_team(Request $request)
|
||||||
{
|
{
|
||||||
$teamId = get_team_id_from_token();
|
$teamId = get_team_id_from_token();
|
||||||
@@ -51,8 +57,10 @@ class Team extends Controller
|
|||||||
return invalid_token();
|
return invalid_token();
|
||||||
}
|
}
|
||||||
$team = auth()->user()->currentTeam();
|
$team = auth()->user()->currentTeam();
|
||||||
|
|
||||||
return response()->json($team);
|
return response()->json($team);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function current_team_members(Request $request)
|
public function current_team_members(Request $request)
|
||||||
{
|
{
|
||||||
$teamId = get_team_id_from_token();
|
$teamId = get_team_id_from_token();
|
||||||
@@ -60,6 +68,7 @@ class Team extends Controller
|
|||||||
return invalid_token();
|
return invalid_token();
|
||||||
}
|
}
|
||||||
$team = auth()->user()->currentTeam();
|
$team = auth()->user()->currentTeam();
|
||||||
|
|
||||||
return response()->json($team->members);
|
return response()->json($team->members);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,40 +14,49 @@ 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\Facades\Password;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Laravel\Fortify\Fortify;
|
|
||||||
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse;
|
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse;
|
||||||
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse;
|
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse;
|
||||||
use Illuminate\Support\Facades\Password;
|
use Laravel\Fortify\Fortify;
|
||||||
|
|
||||||
class Controller extends BaseController
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
use AuthorizesRequests, ValidatesRequests;
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
|
|
||||||
public function realtime_test() {
|
public function realtime_test()
|
||||||
|
{
|
||||||
if (auth()->user()?->currentTeam()->id !== 0) {
|
if (auth()->user()?->currentTeam()->id !== 0) {
|
||||||
return redirect(RouteServiceProvider::HOME);
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
TestEvent::dispatch();
|
TestEvent::dispatch();
|
||||||
|
|
||||||
return 'Look at your other tab.';
|
return 'Look at your other tab.';
|
||||||
}
|
}
|
||||||
public function verify() {
|
|
||||||
|
public function verify()
|
||||||
|
{
|
||||||
return view('auth.verify-email');
|
return view('auth.verify-email');
|
||||||
}
|
}
|
||||||
public function email_verify(EmailVerificationRequest $request) {
|
|
||||||
|
public function email_verify(EmailVerificationRequest $request)
|
||||||
|
{
|
||||||
$request->fulfill();
|
$request->fulfill();
|
||||||
$name = request()->user()?->name;
|
$name = request()->user()?->name;
|
||||||
send_internal_notification("User {$name} verified their email address.");
|
|
||||||
|
// send_internal_notification("User {$name} verified their email address.");
|
||||||
return redirect(RouteServiceProvider::HOME);
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
public function forgot_password(Request $request) {
|
|
||||||
|
public function forgot_password(Request $request)
|
||||||
|
{
|
||||||
if (is_transactional_emails_active()) {
|
if (is_transactional_emails_active()) {
|
||||||
$arrayOfRequest = $request->only(Fortify::email());
|
$arrayOfRequest = $request->only(Fortify::email());
|
||||||
$request->merge([
|
$request->merge([
|
||||||
'email' => Str::lower($arrayOfRequest['email']),
|
'email' => Str::lower($arrayOfRequest['email']),
|
||||||
]);
|
]);
|
||||||
$type = set_transanctional_email_settings();
|
$type = set_transanctional_email_settings();
|
||||||
if (!$type) {
|
if (! $type) {
|
||||||
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
||||||
}
|
}
|
||||||
$request->validate([Fortify::email() => 'required|email']);
|
$request->validate([Fortify::email() => 'required|email']);
|
||||||
@@ -60,10 +69,13 @@ class Controller extends BaseController
|
|||||||
if ($status == Password::RESET_THROTTLED) {
|
if ($status == Password::RESET_THROTTLED) {
|
||||||
return response('Already requested a password reset in the past minutes.', 400);
|
return response('Already requested a password reset in the past minutes.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]);
|
return app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function link()
|
public function link()
|
||||||
{
|
{
|
||||||
$token = request()->get('token');
|
$token = request()->get('token');
|
||||||
@@ -72,7 +84,7 @@ class Controller extends BaseController
|
|||||||
$email = Str::of($decrypted)->before('@@@');
|
$email = Str::of($decrypted)->before('@@@');
|
||||||
$password = Str::of($decrypted)->after('@@@');
|
$password = Str::of($decrypted)->after('@@@');
|
||||||
$user = User::whereEmail($email)->first();
|
$user = User::whereEmail($email)->first();
|
||||||
if (!$user) {
|
if (! $user) {
|
||||||
return redirect()->route('login');
|
return redirect()->route('login');
|
||||||
}
|
}
|
||||||
if (Hash::check($password, $user->password)) {
|
if (Hash::check($password, $user->password)) {
|
||||||
@@ -90,9 +102,11 @@ class Controller extends BaseController
|
|||||||
}
|
}
|
||||||
Auth::login($user);
|
Auth::login($user);
|
||||||
session(['currentTeam' => $team]);
|
session(['currentTeam' => $team]);
|
||||||
|
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,11 +122,12 @@ class Controller extends BaseController
|
|||||||
if ($resetPassword) {
|
if ($resetPassword) {
|
||||||
$user->update([
|
$user->update([
|
||||||
'password' => Hash::make($invitationUuid),
|
'password' => Hash::make($invitationUuid),
|
||||||
'force_password_reset' => true
|
'force_password_reset' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if ($user->teams()->where('team_id', $invitation->team->id)->exists()) {
|
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]);
|
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
||||||
@@ -121,6 +136,7 @@ class Controller extends BaseController
|
|||||||
return redirect()->route('login');
|
return redirect()->route('login');
|
||||||
}
|
}
|
||||||
refreshSession($invitation->team);
|
refreshSession($invitation->team);
|
||||||
|
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
} else {
|
} else {
|
||||||
abort(401);
|
abort(401);
|
||||||
@@ -143,6 +159,7 @@ class Controller extends BaseController
|
|||||||
abort(401);
|
abort(401);
|
||||||
}
|
}
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
|
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
throw $e;
|
throw $e;
|
||||||
|
|||||||
@@ -12,34 +12,35 @@ class MagicController extends Controller
|
|||||||
public function servers()
|
public function servers()
|
||||||
{
|
{
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'servers' => Server::isUsable()->get()
|
'servers' => Server::isUsable()->get(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destinations()
|
public function destinations()
|
||||||
{
|
{
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'destinations' => Server::destinationsByServer(request()->query('server_id'))->sortBy('name')
|
'destinations' => Server::destinationsByServer(request()->query('server_id'))->sortBy('name'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function projects()
|
public function projects()
|
||||||
{
|
{
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'projects' => Project::ownedByCurrentTeam()->get()
|
'projects' => Project::ownedByCurrentTeam()->get(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function environments()
|
public function environments()
|
||||||
{
|
{
|
||||||
$project = Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first();
|
$project = Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first();
|
||||||
if (!$project) {
|
if (! $project) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'environments' => []
|
'environments' => [],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'environments' => $project->environments
|
'environments' => $project->environments,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,8 +50,9 @@ class MagicController extends Controller
|
|||||||
['name' => request()->query('name') ?? generate_random_name()],
|
['name' => request()->query('name') ?? generate_random_name()],
|
||||||
['team_id' => currentTeam()->id]
|
['team_id' => currentTeam()->id]
|
||||||
);
|
);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'project_uuid' => $project->uuid
|
'project_uuid' => $project->uuid,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ class MagicController extends Controller
|
|||||||
['name' => request()->query('name') ?? generate_random_name()],
|
['name' => request()->query('name') ?? generate_random_name()],
|
||||||
['project_id' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->firstOrFail()->id]
|
['project_id' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->firstOrFail()->id]
|
||||||
);
|
);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
]);
|
]);
|
||||||
@@ -75,6 +78,7 @@ class MagicController extends Controller
|
|||||||
);
|
);
|
||||||
auth()->user()->teams()->attach($team, ['role' => 'admin']);
|
auth()->user()->teams()->attach($team, ['role' => 'admin']);
|
||||||
refreshSession();
|
refreshSession();
|
||||||
|
|
||||||
return redirect(request()->header('Referer'));
|
return redirect(request()->header('Referer'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
|
|
||||||
class OauthController extends Controller {
|
class OauthController extends Controller
|
||||||
|
{
|
||||||
public function redirect(string $provider)
|
public function redirect(string $provider)
|
||||||
{
|
{
|
||||||
$socialite_provider = get_socialite_provider($provider);
|
$socialite_provider = get_socialite_provider($provider);
|
||||||
|
|
||||||
return $socialite_provider->redirect();
|
return $socialite_provider->redirect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,17 +21,26 @@ class OauthController extends Controller {
|
|||||||
try {
|
try {
|
||||||
$oauthUser = get_socialite_provider($provider)->user();
|
$oauthUser = get_socialite_provider($provider)->user();
|
||||||
$user = User::whereEmail($oauthUser->email)->first();
|
$user = User::whereEmail($oauthUser->email)->first();
|
||||||
if (!$user) {
|
if (! $user) {
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if (! $settings->is_registration_enabled) {
|
||||||
|
abort(403, 'Registration is disabled');
|
||||||
|
}
|
||||||
|
|
||||||
$user = User::create([
|
$user = User::create([
|
||||||
'name' => $oauthUser->name,
|
'name' => $oauthUser->name,
|
||||||
'email' => $oauthUser->email,
|
'email' => $oauthUser->email,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
Auth::login($user);
|
Auth::login($user);
|
||||||
|
|
||||||
return redirect('/');
|
return redirect('/');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
return redirect()->route('login')->withErrors([__('auth.failed.callback')]);
|
|
||||||
|
$errorCode = $e instanceof HttpException ? 'auth.failed' : 'auth.failed.callback';
|
||||||
|
|
||||||
|
return redirect()->route('login')->withErrors([__($errorCode)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
app/Http/Controllers/UploadController.php
Normal file
82
app/Http/Controllers/UploadController.php
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
|
||||||
|
use Pion\Laravel\ChunkUpload\Handler\HandlerFactory;
|
||||||
|
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
|
||||||
|
|
||||||
|
class UploadController extends BaseController
|
||||||
|
{
|
||||||
|
public function upload(Request $request)
|
||||||
|
{
|
||||||
|
$resource = getResourceByUuid(request()->route('databaseUuid'), data_get(auth()->user()->currentTeam(), 'id'));
|
||||||
|
if (is_null($resource)) {
|
||||||
|
return response()->json(['error' => 'You do not have permission for this database'], 500);
|
||||||
|
}
|
||||||
|
$receiver = new FileReceiver('file', $request, HandlerFactory::classFromRequest($request));
|
||||||
|
|
||||||
|
if ($receiver->isUploaded() === false) {
|
||||||
|
throw new UploadMissingFileException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$save = $receiver->receive();
|
||||||
|
|
||||||
|
if ($save->isFinished()) {
|
||||||
|
return $this->saveFile($save->getFile(), $resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
$handler = $save->handler();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'done' => $handler->getPercentageDone(),
|
||||||
|
'status' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// protected function saveFileToS3($file)
|
||||||
|
// {
|
||||||
|
// $fileName = $this->createFilename($file);
|
||||||
|
|
||||||
|
// $disk = Storage::disk('s3');
|
||||||
|
// // It's better to use streaming Streaming (laravel 5.4+)
|
||||||
|
// $disk->putFileAs('photos', $file, $fileName);
|
||||||
|
|
||||||
|
// // for older laravel
|
||||||
|
// // $disk->put($fileName, file_get_contents($file), 'public');
|
||||||
|
// $mime = str_replace('/', '-', $file->getMimeType());
|
||||||
|
|
||||||
|
// // We need to delete the file when uploaded to s3
|
||||||
|
// unlink($file->getPathname());
|
||||||
|
|
||||||
|
// return response()->json([
|
||||||
|
// 'path' => $disk->url($fileName),
|
||||||
|
// 'name' => $fileName,
|
||||||
|
// 'mime_type' => $mime
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
protected function saveFile(UploadedFile $file, $resource)
|
||||||
|
{
|
||||||
|
$mime = str_replace('/', '-', $file->getMimeType());
|
||||||
|
$filePath = "upload/{$resource->uuid}";
|
||||||
|
$finalPath = storage_path('app/'.$filePath);
|
||||||
|
$file->move($finalPath, 'restore');
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'mime_type' => $mime,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createFilename(UploadedFile $file)
|
||||||
|
{
|
||||||
|
$extension = $file->getClientOriginalExtension();
|
||||||
|
$filename = str_replace('.'.$extension, '', $file->getClientOriginalName()); // Filename without extension
|
||||||
|
|
||||||
|
$filename .= '_'.md5(time()).'.'.$extension;
|
||||||
|
|
||||||
|
return $filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,25 +20,26 @@ class Bitbucket extends Controller
|
|||||||
$epoch = now()->valueOf();
|
$epoch = now()->valueOf();
|
||||||
$data = [
|
$data = [
|
||||||
'attributes' => $request->attributes->all(),
|
'attributes' => $request->attributes->all(),
|
||||||
'request' => $request->request->all(),
|
'request' => $request->request->all(),
|
||||||
'query' => $request->query->all(),
|
'query' => $request->query->all(),
|
||||||
'server' => $request->server->all(),
|
'server' => $request->server->all(),
|
||||||
'files' => $request->files->all(),
|
'files' => $request->files->all(),
|
||||||
'cookies' => $request->cookies->all(),
|
'cookies' => $request->cookies->all(),
|
||||||
'headers' => $request->headers->all(),
|
'headers' => $request->headers->all(),
|
||||||
'content' => $request->getContent(),
|
'content' => $request->getContent(),
|
||||||
];
|
];
|
||||||
$json = json_encode($data);
|
$json = json_encode($data);
|
||||||
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Bitbicket::manual_bitbucket", $json);
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Bitbicket::manual_bitbucket", $json);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$return_payloads = collect([]);
|
$return_payloads = collect([]);
|
||||||
$payload = $request->collect();
|
$payload = $request->collect();
|
||||||
$headers = $request->headers->all();
|
$headers = $request->headers->all();
|
||||||
$x_bitbucket_token = data_get($headers, 'x-hub-signature.0', "");
|
$x_bitbucket_token = data_get($headers, 'x-hub-signature.0', '');
|
||||||
$x_bitbucket_event = data_get($headers, 'x-event-key.0', "");
|
$x_bitbucket_event = data_get($headers, 'x-event-key.0', '');
|
||||||
$handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']);
|
$handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']);
|
||||||
if (!$handled_events->contains($x_bitbucket_event)) {
|
if (! $handled_events->contains($x_bitbucket_event)) {
|
||||||
return response([
|
return response([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Nothing to do. Event not handled.',
|
'message' => 'Nothing to do. Event not handled.',
|
||||||
@@ -47,14 +48,14 @@ class Bitbucket extends Controller
|
|||||||
if ($x_bitbucket_event === 'repo:push') {
|
if ($x_bitbucket_event === 'repo:push') {
|
||||||
$branch = data_get($payload, 'push.changes.0.new.name');
|
$branch = data_get($payload, 'push.changes.0.new.name');
|
||||||
$full_name = data_get($payload, 'repository.full_name');
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
|
$commit = data_get($payload, 'push.changes.0.new.target.hash');
|
||||||
if (!$branch) {
|
if (! $branch) {
|
||||||
return response([
|
return response([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Nothing to do. No branch found in the request.',
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
ray('Manual webhook bitbucket push event with branch: ' . $branch);
|
ray('Manual webhook bitbucket push event with branch: '.$branch);
|
||||||
}
|
}
|
||||||
if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
|
if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
|
||||||
$branch = data_get($payload, 'pullrequest.destination.branch.name');
|
$branch = data_get($payload, 'pullrequest.destination.branch.name');
|
||||||
@@ -76,34 +77,37 @@ class Bitbucket extends Controller
|
|||||||
$webhook_secret = data_get($application, 'manual_webhook_secret_bitbucket');
|
$webhook_secret = data_get($application, 'manual_webhook_secret_bitbucket');
|
||||||
$payload = $request->getContent();
|
$payload = $request->getContent();
|
||||||
|
|
||||||
list($algo, $hash) = explode('=', $x_bitbucket_token, 2);
|
[$algo, $hash] = explode('=', $x_bitbucket_token, 2);
|
||||||
$payloadHash = hash_hmac($algo, $payload, $webhook_secret);
|
$payloadHash = hash_hmac($algo, $payload, $webhook_secret);
|
||||||
if (!hash_equals($hash, $payloadHash) && !isDev()) {
|
if (! hash_equals($hash, $payloadHash) && ! isDev()) {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Invalid token.',
|
'message' => 'Invalid signature.',
|
||||||
]);
|
]);
|
||||||
ray('Invalid signature');
|
ray('Invalid signature');
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$isFunctional = $application->destination->server->isFunctional();
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
if (!$isFunctional) {
|
if (! $isFunctional) {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Server is not functional.',
|
'message' => 'Server is not functional.',
|
||||||
]);
|
]);
|
||||||
ray('Server is not functional: ' . $application->destination->server->name);
|
ray('Server is not functional: '.$application->destination->server->name);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($x_bitbucket_event === 'repo:push') {
|
if ($x_bitbucket_event === 'repo:push') {
|
||||||
if ($application->isDeployable()) {
|
if ($application->isDeployable()) {
|
||||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
ray('Deploying '.$application->name.' with branch '.$branch);
|
||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
|
commit: $commit,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
is_webhook: true
|
is_webhook: true
|
||||||
);
|
);
|
||||||
@@ -122,16 +126,27 @@ class Bitbucket extends Controller
|
|||||||
}
|
}
|
||||||
if ($x_bitbucket_event === 'pullrequest:created') {
|
if ($x_bitbucket_event === 'pullrequest:created') {
|
||||||
if ($application->isPRDeployable()) {
|
if ($application->isPRDeployable()) {
|
||||||
ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id);
|
ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id);
|
||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (!$found) {
|
if (! $found) {
|
||||||
ApplicationPreview::create([
|
if ($application->build_pack === 'dockercompose') {
|
||||||
'git_type' => 'bitbucket',
|
$pr_app = ApplicationPreview::create([
|
||||||
'application_id' => $application->id,
|
'git_type' => 'bitbucket',
|
||||||
'pull_request_id' => $pull_request_id,
|
'application_id' => $application->id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_id' => $pull_request_id,
|
||||||
]);
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'bitbucket',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
@@ -177,9 +192,11 @@ class Bitbucket extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ray($return_payloads);
|
ray($return_payloads);
|
||||||
|
|
||||||
return response($return_payloads);
|
return response($return_payloads);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
|
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
240
app/Http/Controllers/Webhook/Gitea.php
Normal file
240
app/Http/Controllers/Webhook/Gitea.php
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Gitea extends Controller
|
||||||
|
{
|
||||||
|
public function manual(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$return_payloads = collect([]);
|
||||||
|
$x_gitea_delivery = request()->header('X-Gitea-Delivery');
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$files = Storage::disk('webhooks-during-maintenance')->files();
|
||||||
|
$gitea_delivery_found = collect($files)->filter(function ($file) use ($x_gitea_delivery) {
|
||||||
|
return Str::contains($file, $x_gitea_delivery);
|
||||||
|
})->first();
|
||||||
|
if ($gitea_delivery_found) {
|
||||||
|
ray('Webhook already found');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$data = [
|
||||||
|
'attributes' => $request->attributes->all(),
|
||||||
|
'request' => $request->request->all(),
|
||||||
|
'query' => $request->query->all(),
|
||||||
|
'server' => $request->server->all(),
|
||||||
|
'files' => $request->files->all(),
|
||||||
|
'cookies' => $request->cookies->all(),
|
||||||
|
'headers' => $request->headers->all(),
|
||||||
|
'content' => $request->getContent(),
|
||||||
|
];
|
||||||
|
$json = json_encode($data);
|
||||||
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitea::manual_{$x_gitea_delivery}", $json);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$x_gitea_event = Str::lower($request->header('X-Gitea-Event'));
|
||||||
|
$x_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256=');
|
||||||
|
$content_type = $request->header('Content-Type');
|
||||||
|
$payload = $request->collect();
|
||||||
|
if ($x_gitea_event === 'ping') {
|
||||||
|
// Just pong
|
||||||
|
return response('pong');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($content_type !== 'application/json') {
|
||||||
|
$payload = json_decode(data_get($payload, 'payload'), true);
|
||||||
|
}
|
||||||
|
if ($x_gitea_event === 'push') {
|
||||||
|
$branch = data_get($payload, 'ref');
|
||||||
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
|
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||||
|
$branch = Str::after($branch, 'refs/heads/');
|
||||||
|
}
|
||||||
|
$added_files = data_get($payload, 'commits.*.added');
|
||||||
|
$removed_files = data_get($payload, 'commits.*.removed');
|
||||||
|
$modified_files = data_get($payload, 'commits.*.modified');
|
||||||
|
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||||
|
ray($changed_files);
|
||||||
|
ray('Manual Webhook Gitea Push Event with branch: '.$branch);
|
||||||
|
}
|
||||||
|
if ($x_gitea_event === 'pull_request') {
|
||||||
|
$action = data_get($payload, 'action');
|
||||||
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
|
$pull_request_id = data_get($payload, 'number');
|
||||||
|
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
||||||
|
$branch = data_get($payload, 'pull_request.head.ref');
|
||||||
|
$base_branch = data_get($payload, 'pull_request.base.ref');
|
||||||
|
ray('Webhook Gitea Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
|
||||||
|
}
|
||||||
|
if (! $branch) {
|
||||||
|
return response('Nothing to do. No branch found in the request.');
|
||||||
|
}
|
||||||
|
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
||||||
|
if ($x_gitea_event === 'push') {
|
||||||
|
$applications = $applications->where('git_branch', $branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
return response("Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_gitea_event === 'pull_request') {
|
||||||
|
$applications = $applications->where('git_branch', $base_branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
return response("Nothing to do. No applications found with branch '$base_branch'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$webhook_secret = data_get($application, 'manual_webhook_secret_gitea');
|
||||||
|
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
|
||||||
|
if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) {
|
||||||
|
ray('Invalid signature');
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Invalid signature.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
|
if (! $isFunctional) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Server is not functional.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($x_gitea_event === 'push') {
|
||||||
|
if ($application->isDeployable()) {
|
||||||
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
|
ray('Deploying '.$application->name.' with branch '.$branch);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
commit: data_get($payload, 'after', 'HEAD'),
|
||||||
|
is_webhook: true,
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'application_name' => $application->name,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$paths = str($application->watch_paths)->explode("\n");
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Changed files do not match watch paths. Ignoring deployment.',
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'application_name' => $application->name,
|
||||||
|
'details' => [
|
||||||
|
'changed_files' => $changed_files,
|
||||||
|
'watch_paths' => $paths,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Deployments disabled.',
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'application_name' => $application->name,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_gitea_event === 'pull_request') {
|
||||||
|
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
||||||
|
if ($application->isPRDeployable()) {
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if (! $found) {
|
||||||
|
if ($application->build_pack === 'dockercompose') {
|
||||||
|
$pr_app = ApplicationPreview::create([
|
||||||
|
'git_type' => 'gitea',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'gitea',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
pull_request_id: $pull_request_id,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
commit: data_get($payload, 'head.sha', 'HEAD'),
|
||||||
|
is_webhook: true,
|
||||||
|
git_type: 'gitea'
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Preview deployments disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($action === 'closed') {
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if ($found) {
|
||||||
|
$found->delete();
|
||||||
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
|
// ray('Stopping container: ' . $container_name);
|
||||||
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment closed.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'No preview deployment found.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ray($return_payloads);
|
||||||
|
|
||||||
|
return response($return_payloads);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
|
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,20 +33,22 @@ class Github extends Controller
|
|||||||
})->first();
|
})->first();
|
||||||
if ($github_delivery_found) {
|
if ($github_delivery_found) {
|
||||||
ray('Webhook already found');
|
ray('Webhook already found');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$data = [
|
$data = [
|
||||||
'attributes' => $request->attributes->all(),
|
'attributes' => $request->attributes->all(),
|
||||||
'request' => $request->request->all(),
|
'request' => $request->request->all(),
|
||||||
'query' => $request->query->all(),
|
'query' => $request->query->all(),
|
||||||
'server' => $request->server->all(),
|
'server' => $request->server->all(),
|
||||||
'files' => $request->files->all(),
|
'files' => $request->files->all(),
|
||||||
'cookies' => $request->cookies->all(),
|
'cookies' => $request->cookies->all(),
|
||||||
'headers' => $request->headers->all(),
|
'headers' => $request->headers->all(),
|
||||||
'content' => $request->getContent(),
|
'content' => $request->getContent(),
|
||||||
];
|
];
|
||||||
$json = json_encode($data);
|
$json = json_encode($data);
|
||||||
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::manual_{$x_github_delivery}", $json);
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::manual_{$x_github_delivery}", $json);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$x_github_event = Str::lower($request->header('X-GitHub-Event'));
|
$x_github_event = Str::lower($request->header('X-GitHub-Event'));
|
||||||
@@ -71,7 +73,7 @@ class Github extends Controller
|
|||||||
$removed_files = data_get($payload, 'commits.*.removed');
|
$removed_files = data_get($payload, 'commits.*.removed');
|
||||||
$modified_files = data_get($payload, 'commits.*.modified');
|
$modified_files = data_get($payload, 'commits.*.modified');
|
||||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||||
ray('Manual Webhook GitHub Push Event with branch: ' . $branch);
|
ray('Manual Webhook GitHub Push Event with branch: '.$branch);
|
||||||
}
|
}
|
||||||
if ($x_github_event === 'pull_request') {
|
if ($x_github_event === 'pull_request') {
|
||||||
$action = data_get($payload, 'action');
|
$action = data_get($payload, 'action');
|
||||||
@@ -80,9 +82,9 @@ class Github extends Controller
|
|||||||
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
||||||
$branch = data_get($payload, 'pull_request.head.ref');
|
$branch = data_get($payload, 'pull_request.head.ref');
|
||||||
$base_branch = data_get($payload, 'pull_request.base.ref');
|
$base_branch = data_get($payload, 'pull_request.base.ref');
|
||||||
ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
|
ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
|
||||||
}
|
}
|
||||||
if (!$branch) {
|
if (! $branch) {
|
||||||
return response('Nothing to do. No branch found in the request.');
|
return response('Nothing to do. No branch found in the request.');
|
||||||
}
|
}
|
||||||
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
||||||
@@ -101,34 +103,37 @@ class Github extends Controller
|
|||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
$webhook_secret = data_get($application, 'manual_webhook_secret_github');
|
$webhook_secret = data_get($application, 'manual_webhook_secret_github');
|
||||||
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
|
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
|
||||||
if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) {
|
if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) {
|
||||||
ray('Invalid signature');
|
ray('Invalid signature');
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Invalid token.',
|
'message' => 'Invalid signature.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$isFunctional = $application->destination->server->isFunctional();
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
if (!$isFunctional) {
|
if (! $isFunctional) {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Server is not functional.',
|
'message' => 'Server is not functional.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($x_github_event === 'push') {
|
if ($x_github_event === 'push') {
|
||||||
if ($application->isDeployable()) {
|
if ($application->isDeployable()) {
|
||||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
ray('Deploying '.$application->name.' with branch '.$branch);
|
||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
|
commit: data_get($payload, 'after', 'HEAD'),
|
||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
);
|
);
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
@@ -164,19 +169,31 @@ class Github extends Controller
|
|||||||
if ($application->isPRDeployable()) {
|
if ($application->isPRDeployable()) {
|
||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (!$found) {
|
if (! $found) {
|
||||||
ApplicationPreview::create([
|
if ($application->build_pack === 'dockercompose') {
|
||||||
'git_type' => 'github',
|
$pr_app = ApplicationPreview::create([
|
||||||
'application_id' => $application->id,
|
'git_type' => 'github',
|
||||||
'pull_request_id' => $pull_request_id,
|
'application_id' => $application->id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_id' => $pull_request_id,
|
||||||
]);
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'github',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
|
commit: data_get($payload, 'head.sha', 'HEAD'),
|
||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
git_type: 'github'
|
git_type: 'github'
|
||||||
);
|
);
|
||||||
@@ -216,12 +233,15 @@ class Github extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ray($return_payloads);
|
ray($return_payloads);
|
||||||
|
|
||||||
return response($return_payloads);
|
return response($return_payloads);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function normal(Request $request)
|
public function normal(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -237,20 +257,22 @@ class Github extends Controller
|
|||||||
})->first();
|
})->first();
|
||||||
if ($github_delivery_found) {
|
if ($github_delivery_found) {
|
||||||
ray('Webhook already found');
|
ray('Webhook already found');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$data = [
|
$data = [
|
||||||
'attributes' => $request->attributes->all(),
|
'attributes' => $request->attributes->all(),
|
||||||
'request' => $request->request->all(),
|
'request' => $request->request->all(),
|
||||||
'query' => $request->query->all(),
|
'query' => $request->query->all(),
|
||||||
'server' => $request->server->all(),
|
'server' => $request->server->all(),
|
||||||
'files' => $request->files->all(),
|
'files' => $request->files->all(),
|
||||||
'cookies' => $request->cookies->all(),
|
'cookies' => $request->cookies->all(),
|
||||||
'headers' => $request->headers->all(),
|
'headers' => $request->headers->all(),
|
||||||
'content' => $request->getContent(),
|
'content' => $request->getContent(),
|
||||||
];
|
];
|
||||||
$json = json_encode($data);
|
$json = json_encode($data);
|
||||||
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::normal_{$x_github_delivery}", $json);
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::normal_{$x_github_delivery}", $json);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$x_github_event = Str::lower($request->header('X-GitHub-Event'));
|
$x_github_event = Str::lower($request->header('X-GitHub-Event'));
|
||||||
@@ -268,7 +290,7 @@ class Github extends Controller
|
|||||||
$webhook_secret = data_get($github_app, 'webhook_secret');
|
$webhook_secret = data_get($github_app, 'webhook_secret');
|
||||||
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
|
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
|
||||||
if (config('app.env') !== 'local') {
|
if (config('app.env') !== 'local') {
|
||||||
if (!hash_equals($x_hub_signature_256, $hmac)) {
|
if (! hash_equals($x_hub_signature_256, $hmac)) {
|
||||||
return response('Invalid signature.');
|
return response('Invalid signature.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,6 +300,7 @@ class Github extends Controller
|
|||||||
if ($action === 'new_permissions_accepted') {
|
if ($action === 'new_permissions_accepted') {
|
||||||
GithubAppPermissionJob::dispatch($github_app);
|
GithubAppPermissionJob::dispatch($github_app);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response('cool');
|
return response('cool');
|
||||||
}
|
}
|
||||||
if ($x_github_event === 'push') {
|
if ($x_github_event === 'push') {
|
||||||
@@ -290,7 +313,7 @@ class Github extends Controller
|
|||||||
$removed_files = data_get($payload, 'commits.*.removed');
|
$removed_files = data_get($payload, 'commits.*.removed');
|
||||||
$modified_files = data_get($payload, 'commits.*.modified');
|
$modified_files = data_get($payload, 'commits.*.modified');
|
||||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||||
ray('Webhook GitHub Push Event: ' . $id . ' with branch: ' . $branch);
|
ray('Webhook GitHub Push Event: '.$id.' with branch: '.$branch);
|
||||||
}
|
}
|
||||||
if ($x_github_event === 'pull_request') {
|
if ($x_github_event === 'pull_request') {
|
||||||
$action = data_get($payload, 'action');
|
$action = data_get($payload, 'action');
|
||||||
@@ -299,9 +322,9 @@ class Github extends Controller
|
|||||||
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
$pull_request_html_url = data_get($payload, 'pull_request.html_url');
|
||||||
$branch = data_get($payload, 'pull_request.head.ref');
|
$branch = data_get($payload, 'pull_request.head.ref');
|
||||||
$base_branch = data_get($payload, 'pull_request.base.ref');
|
$base_branch = data_get($payload, 'pull_request.base.ref');
|
||||||
ray('Webhook GitHub Pull Request Event: ' . $id . ' with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
|
ray('Webhook GitHub Pull Request Event: '.$id.' with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
|
||||||
}
|
}
|
||||||
if (!$id || !$branch) {
|
if (! $id || ! $branch) {
|
||||||
return response('Nothing to do. No id or branch found.');
|
return response('Nothing to do. No id or branch found.');
|
||||||
}
|
}
|
||||||
$applications = Application::where('repository_project_id', $id)->whereRelation('source', 'is_public', false);
|
$applications = Application::where('repository_project_id', $id)->whereRelation('source', 'is_public', false);
|
||||||
@@ -320,24 +343,26 @@ class Github extends Controller
|
|||||||
|
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
$isFunctional = $application->destination->server->isFunctional();
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
if (!$isFunctional) {
|
if (! $isFunctional) {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Server is not functional.',
|
'message' => 'Server is not functional.',
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'application_name' => $application->name,
|
'application_name' => $application->name,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($x_github_event === 'push') {
|
if ($x_github_event === 'push') {
|
||||||
if ($application->isDeployable()) {
|
if ($application->isDeployable()) {
|
||||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
ray('Deploying '.$application->name.' with branch '.$branch);
|
||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
|
commit: data_get($payload, 'after', 'HEAD'),
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
);
|
);
|
||||||
@@ -374,7 +399,7 @@ class Github extends Controller
|
|||||||
if ($application->isPRDeployable()) {
|
if ($application->isPRDeployable()) {
|
||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (!$found) {
|
if (! $found) {
|
||||||
ApplicationPreview::create([
|
ApplicationPreview::create([
|
||||||
'git_type' => 'github',
|
'git_type' => 'github',
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
@@ -387,6 +412,7 @@ class Github extends Controller
|
|||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
|
commit: data_get($payload, 'head.sha', 'HEAD'),
|
||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
git_type: 'github'
|
git_type: 'github'
|
||||||
);
|
);
|
||||||
@@ -406,11 +432,12 @@ class Github extends Controller
|
|||||||
if ($action === 'closed' || $action === 'close') {
|
if ($action === 'closed' || $action === 'close') {
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if ($found) {
|
if ($found) {
|
||||||
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
|
|
||||||
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
|
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
|
||||||
$found->delete();
|
$found->delete();
|
||||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
|
||||||
// ray('Stopping container: ' . $container_name);
|
|
||||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
@@ -426,13 +453,15 @@ class Github extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ray($return_payloads);
|
|
||||||
return response($return_payloads);
|
return response($return_payloads);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function redirect(Request $request)
|
public function redirect(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -460,11 +489,13 @@ class Github extends Controller
|
|||||||
$github_app->webhook_secret = $webhook_secret;
|
$github_app->webhook_secret = $webhook_secret;
|
||||||
$github_app->private_key_id = $private_key->id;
|
$github_app->private_key_id = $private_key->id;
|
||||||
$github_app->save();
|
$github_app->save();
|
||||||
|
|
||||||
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function install(Request $request)
|
public function install(Request $request)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -474,16 +505,17 @@ class Github extends Controller
|
|||||||
$epoch = now()->valueOf();
|
$epoch = now()->valueOf();
|
||||||
$data = [
|
$data = [
|
||||||
'attributes' => $request->attributes->all(),
|
'attributes' => $request->attributes->all(),
|
||||||
'request' => $request->request->all(),
|
'request' => $request->request->all(),
|
||||||
'query' => $request->query->all(),
|
'query' => $request->query->all(),
|
||||||
'server' => $request->server->all(),
|
'server' => $request->server->all(),
|
||||||
'files' => $request->files->all(),
|
'files' => $request->files->all(),
|
||||||
'cookies' => $request->cookies->all(),
|
'cookies' => $request->cookies->all(),
|
||||||
'headers' => $request->headers->all(),
|
'headers' => $request->headers->all(),
|
||||||
'content' => $request->getContent(),
|
'content' => $request->getContent(),
|
||||||
];
|
];
|
||||||
$json = json_encode($data);
|
$json = json_encode($data);
|
||||||
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::install_{$installation_id}", $json);
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Github::install_{$installation_id}", $json);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$source = $request->get('source');
|
$source = $request->get('source');
|
||||||
@@ -493,6 +525,7 @@ class Github extends Controller
|
|||||||
$github_app->installation_id = $installation_id;
|
$github_app->installation_id = $installation_id;
|
||||||
$github_app->save();
|
$github_app->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
|
|||||||
@@ -21,16 +21,17 @@ class Gitlab extends Controller
|
|||||||
$epoch = now()->valueOf();
|
$epoch = now()->valueOf();
|
||||||
$data = [
|
$data = [
|
||||||
'attributes' => $request->attributes->all(),
|
'attributes' => $request->attributes->all(),
|
||||||
'request' => $request->request->all(),
|
'request' => $request->request->all(),
|
||||||
'query' => $request->query->all(),
|
'query' => $request->query->all(),
|
||||||
'server' => $request->server->all(),
|
'server' => $request->server->all(),
|
||||||
'files' => $request->files->all(),
|
'files' => $request->files->all(),
|
||||||
'cookies' => $request->cookies->all(),
|
'cookies' => $request->cookies->all(),
|
||||||
'headers' => $request->headers->all(),
|
'headers' => $request->headers->all(),
|
||||||
'content' => $request->getContent(),
|
'content' => $request->getContent(),
|
||||||
];
|
];
|
||||||
$json = json_encode($data);
|
$json = json_encode($data);
|
||||||
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitlab::manual_gitlab", $json);
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Gitlab::manual_gitlab", $json);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$return_payloads = collect([]);
|
$return_payloads = collect([]);
|
||||||
@@ -38,24 +39,35 @@ class Gitlab extends Controller
|
|||||||
$headers = $request->headers->all();
|
$headers = $request->headers->all();
|
||||||
$x_gitlab_token = data_get($headers, 'x-gitlab-token.0');
|
$x_gitlab_token = data_get($headers, 'x-gitlab-token.0');
|
||||||
$x_gitlab_event = data_get($payload, 'object_kind');
|
$x_gitlab_event = data_get($payload, 'object_kind');
|
||||||
|
$allowed_events = ['push', 'merge_request'];
|
||||||
|
if (! in_array($x_gitlab_event, $allowed_events)) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Event not allowed. Only push and merge_request events are allowed.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response($return_payloads);
|
||||||
|
}
|
||||||
|
|
||||||
if ($x_gitlab_event === 'push') {
|
if ($x_gitlab_event === 'push') {
|
||||||
$branch = data_get($payload, 'ref');
|
$branch = data_get($payload, 'ref');
|
||||||
$full_name = data_get($payload, 'project.path_with_namespace');
|
$full_name = data_get($payload, 'project.path_with_namespace');
|
||||||
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||||
$branch = Str::after($branch, 'refs/heads/');
|
$branch = Str::after($branch, 'refs/heads/');
|
||||||
}
|
}
|
||||||
if (!$branch) {
|
if (! $branch) {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Nothing to do. No branch found in the request.',
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response($return_payloads);
|
return response($return_payloads);
|
||||||
}
|
}
|
||||||
$added_files = data_get($payload, 'commits.*.added');
|
$added_files = data_get($payload, 'commits.*.added');
|
||||||
$removed_files = data_get($payload, 'commits.*.removed');
|
$removed_files = data_get($payload, 'commits.*.removed');
|
||||||
$modified_files = data_get($payload, 'commits.*.modified');
|
$modified_files = data_get($payload, 'commits.*.modified');
|
||||||
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
$changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten();
|
||||||
ray('Manual Webhook GitLab Push Event with branch: ' . $branch);
|
ray('Manual Webhook GitLab Push Event with branch: '.$branch);
|
||||||
}
|
}
|
||||||
if ($x_gitlab_event === 'merge_request') {
|
if ($x_gitlab_event === 'merge_request') {
|
||||||
$action = data_get($payload, 'object_attributes.action');
|
$action = data_get($payload, 'object_attributes.action');
|
||||||
@@ -64,14 +76,15 @@ class Gitlab extends Controller
|
|||||||
$full_name = data_get($payload, 'project.path_with_namespace');
|
$full_name = data_get($payload, 'project.path_with_namespace');
|
||||||
$pull_request_id = data_get($payload, 'object_attributes.iid');
|
$pull_request_id = data_get($payload, 'object_attributes.iid');
|
||||||
$pull_request_html_url = data_get($payload, 'object_attributes.url');
|
$pull_request_html_url = data_get($payload, 'object_attributes.url');
|
||||||
if (!$branch) {
|
if (! $branch) {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Nothing to do. No branch found in the request.',
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response($return_payloads);
|
return response($return_payloads);
|
||||||
}
|
}
|
||||||
ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
|
ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id);
|
||||||
}
|
}
|
||||||
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
||||||
if ($x_gitlab_event === 'push') {
|
if ($x_gitlab_event === 'push') {
|
||||||
@@ -81,6 +94,7 @@ class Gitlab extends Controller
|
|||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => "Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.",
|
'message' => "Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response($return_payloads);
|
return response($return_payloads);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,6 +105,7 @@ class Gitlab extends Controller
|
|||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => "Nothing to do. No applications found with branch '$base_branch'.",
|
'message' => "Nothing to do. No applications found with branch '$base_branch'.",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response($return_payloads);
|
return response($return_payloads);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,30 +115,33 @@ class Gitlab extends Controller
|
|||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Invalid token.',
|
'message' => 'Invalid signature.',
|
||||||
]);
|
]);
|
||||||
ray('Invalid signature');
|
ray('Invalid signature');
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$isFunctional = $application->destination->server->isFunctional();
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
if (!$isFunctional) {
|
if (! $isFunctional) {
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Server is not functional',
|
'message' => 'Server is not functional',
|
||||||
]);
|
]);
|
||||||
ray('Server is not functional: ' . $application->destination->server->name);
|
ray('Server is not functional: '.$application->destination->server->name);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($x_gitlab_event === 'push') {
|
if ($x_gitlab_event === 'push') {
|
||||||
if ($application->isDeployable()) {
|
if ($application->isDeployable()) {
|
||||||
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
|
||||||
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
|
||||||
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
ray('Deploying '.$application->name.' with branch '.$branch);
|
||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
|
commit: data_get($payload, 'after', 'HEAD'),
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
);
|
);
|
||||||
@@ -153,7 +171,7 @@ class Gitlab extends Controller
|
|||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'application_name' => $application->name,
|
'application_name' => $application->name,
|
||||||
]);
|
]);
|
||||||
ray('Deployments disabled for ' . $application->name);
|
ray('Deployments disabled for '.$application->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($x_gitlab_event === 'merge_request') {
|
if ($x_gitlab_event === 'merge_request') {
|
||||||
@@ -161,23 +179,35 @@ class Gitlab extends Controller
|
|||||||
if ($application->isPRDeployable()) {
|
if ($application->isPRDeployable()) {
|
||||||
$deployment_uuid = new Cuid2(7);
|
$deployment_uuid = new Cuid2(7);
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if (!$found) {
|
if (! $found) {
|
||||||
ApplicationPreview::create([
|
if ($application->build_pack === 'dockercompose') {
|
||||||
'git_type' => 'gitlab',
|
$pr_app = ApplicationPreview::create([
|
||||||
'application_id' => $application->id,
|
'git_type' => 'gitlab',
|
||||||
'pull_request_id' => $pull_request_id,
|
'application_id' => $application->id,
|
||||||
'pull_request_html_url' => $pull_request_html_url,
|
'pull_request_id' => $pull_request_id,
|
||||||
]);
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
'docker_compose_domains' => $application->docker_compose_domains,
|
||||||
|
]);
|
||||||
|
$pr_app->generate_preview_fqdn_compose();
|
||||||
|
} else {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'gitlab',
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => $pull_request_id,
|
||||||
|
'pull_request_html_url' => $pull_request_html_url,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $application,
|
application: $application,
|
||||||
pull_request_id: $pull_request_id,
|
pull_request_id: $pull_request_id,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
|
commit: data_get($payload, 'object_attributes.last_commit.id', 'HEAD'),
|
||||||
force_rebuild: false,
|
force_rebuild: false,
|
||||||
is_webhook: true,
|
is_webhook: true,
|
||||||
git_type: 'gitlab'
|
git_type: 'gitlab'
|
||||||
);
|
);
|
||||||
ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id);
|
ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id);
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
@@ -189,9 +219,9 @@ class Gitlab extends Controller
|
|||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Preview deployments disabled',
|
'message' => 'Preview deployments disabled',
|
||||||
]);
|
]);
|
||||||
ray('Preview deployments disabled for ' . $application->name);
|
ray('Preview deployments disabled for '.$application->name);
|
||||||
}
|
}
|
||||||
} else if ($action === 'closed' || $action === 'close') {
|
} elseif ($action === 'closed' || $action === 'close' || $action === 'merge') {
|
||||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
if ($found) {
|
if ($found) {
|
||||||
$found->delete();
|
$found->delete();
|
||||||
@@ -203,6 +233,7 @@ class Gitlab extends Controller
|
|||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => 'Preview Deployment closed',
|
'message' => 'Preview Deployment closed',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response($return_payloads);
|
return response($return_payloads);
|
||||||
}
|
}
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
@@ -219,9 +250,11 @@ class Gitlab extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response($return_payloads);
|
return response($return_payloads);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,16 +26,17 @@ class Stripe extends Controller
|
|||||||
$epoch = now()->valueOf();
|
$epoch = now()->valueOf();
|
||||||
$data = [
|
$data = [
|
||||||
'attributes' => $request->attributes->all(),
|
'attributes' => $request->attributes->all(),
|
||||||
'request' => $request->request->all(),
|
'request' => $request->request->all(),
|
||||||
'query' => $request->query->all(),
|
'query' => $request->query->all(),
|
||||||
'server' => $request->server->all(),
|
'server' => $request->server->all(),
|
||||||
'files' => $request->files->all(),
|
'files' => $request->files->all(),
|
||||||
'cookies' => $request->cookies->all(),
|
'cookies' => $request->cookies->all(),
|
||||||
'headers' => $request->headers->all(),
|
'headers' => $request->headers->all(),
|
||||||
'content' => $request->getContent(),
|
'content' => $request->getContent(),
|
||||||
];
|
];
|
||||||
$json = json_encode($data);
|
$json = json_encode($data);
|
||||||
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Stripe::events_stripe", $json);
|
Storage::disk('webhooks-during-maintenance')->put("{$epoch}_Stripe::events_stripe", $json);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$webhookSecret = config('subscription.stripe_webhook_secret');
|
$webhookSecret = config('subscription.stripe_webhook_secret');
|
||||||
@@ -48,7 +49,7 @@ class Stripe extends Controller
|
|||||||
);
|
);
|
||||||
$webhook = Webhook::create([
|
$webhook = Webhook::create([
|
||||||
'type' => 'stripe',
|
'type' => 'stripe',
|
||||||
'payload' => $request->getContent()
|
'payload' => $request->getContent(),
|
||||||
]);
|
]);
|
||||||
$type = data_get($event, 'type');
|
$type = data_get($event, 'type');
|
||||||
$data = data_get($event, 'data.object');
|
$data = data_get($event, 'data.object');
|
||||||
@@ -65,20 +66,20 @@ class Stripe extends Controller
|
|||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
$team = Team::find($teamId);
|
$team = Team::find($teamId);
|
||||||
$found = $team->members->where('id', $userId)->first();
|
$found = $team->members->where('id', $userId)->first();
|
||||||
if (!$found->isAdmin()) {
|
if (! $found->isAdmin()) {
|
||||||
send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
|
send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
|
||||||
throw new Exception("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
|
throw new Exception("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
|
||||||
}
|
}
|
||||||
$subscription = Subscription::where('team_id', $teamId)->first();
|
$subscription = Subscription::where('team_id', $teamId)->first();
|
||||||
if ($subscription) {
|
if ($subscription) {
|
||||||
send_internal_notification('Old subscription activated for team: ' . $teamId);
|
// send_internal_notification('Old subscription activated for team: '.$teamId);
|
||||||
$subscription->update([
|
$subscription->update([
|
||||||
'stripe_subscription_id' => $subscriptionId,
|
'stripe_subscription_id' => $subscriptionId,
|
||||||
'stripe_customer_id' => $customerId,
|
'stripe_customer_id' => $customerId,
|
||||||
'stripe_invoice_paid' => true,
|
'stripe_invoice_paid' => true,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
send_internal_notification('New subscription for team: ' . $teamId);
|
// send_internal_notification('New subscription for team: '.$teamId);
|
||||||
Subscription::create([
|
Subscription::create([
|
||||||
'team_id' => $teamId,
|
'team_id' => $teamId,
|
||||||
'stripe_subscription_id' => $subscriptionId,
|
'stripe_subscription_id' => $subscriptionId,
|
||||||
@@ -91,11 +92,11 @@ class Stripe extends Controller
|
|||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
$planId = data_get($data, 'lines.data.0.plan.id');
|
$planId = data_get($data, 'lines.data.0.plan.id');
|
||||||
if (Str::contains($excludedPlans, $planId)) {
|
if (Str::contains($excludedPlans, $planId)) {
|
||||||
send_internal_notification('Subscription excluded.');
|
// send_internal_notification('Subscription excluded.');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
if (!$subscription) {
|
if (! $subscription) {
|
||||||
Sleep::for(5)->seconds();
|
Sleep::for(5)->seconds();
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
}
|
}
|
||||||
@@ -106,34 +107,38 @@ class Stripe extends Controller
|
|||||||
case 'invoice.payment_failed':
|
case 'invoice.payment_failed':
|
||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
if (!$subscription) {
|
if (! $subscription) {
|
||||||
send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: ' . $customerId);
|
// send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: '.$customerId);
|
||||||
|
|
||||||
return response('No subscription found in Coolify.');
|
return response('No subscription found in Coolify.');
|
||||||
}
|
}
|
||||||
$team = data_get($subscription, 'team');
|
$team = data_get($subscription, 'team');
|
||||||
if (!$team) {
|
if (! $team) {
|
||||||
send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: ' . $customerId);
|
// send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: '.$customerId);
|
||||||
|
|
||||||
return response('No team found in Coolify.');
|
return response('No team found in Coolify.');
|
||||||
}
|
}
|
||||||
if (!$subscription->stripe_invoice_paid) {
|
if (! $subscription->stripe_invoice_paid) {
|
||||||
SubscriptionInvoiceFailedJob::dispatch($team);
|
SubscriptionInvoiceFailedJob::dispatch($team);
|
||||||
send_internal_notification('Invoice payment failed: ' . $customerId);
|
// send_internal_notification('Invoice payment failed: '.$customerId);
|
||||||
} else {
|
} else {
|
||||||
send_internal_notification('Invoice payment failed but already paid: ' . $customerId);
|
// send_internal_notification('Invoice payment failed but already paid: '.$customerId);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'payment_intent.payment_failed':
|
case 'payment_intent.payment_failed':
|
||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
if (!$subscription) {
|
if (! $subscription) {
|
||||||
send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: ' . $customerId);
|
// send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: '.$customerId);
|
||||||
|
|
||||||
return response('No subscription found in Coolify.');
|
return response('No subscription found in Coolify.');
|
||||||
}
|
}
|
||||||
if ($subscription->stripe_invoice_paid) {
|
if ($subscription->stripe_invoice_paid) {
|
||||||
send_internal_notification('payment_intent.payment_failed but invoice is active for customer: ' . $customerId);
|
// send_internal_notification('payment_intent.payment_failed but invoice is active for customer: '.$customerId);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
send_internal_notification('Subscription payment failed for customer: ' . $customerId);
|
send_internal_notification('Subscription payment failed for customer: '.$customerId);
|
||||||
break;
|
break;
|
||||||
case 'customer.subscription.updated':
|
case 'customer.subscription.updated':
|
||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
@@ -141,17 +146,23 @@ class Stripe extends Controller
|
|||||||
$subscriptionId = data_get($data, 'items.data.0.subscription');
|
$subscriptionId = data_get($data, 'items.data.0.subscription');
|
||||||
$planId = data_get($data, 'items.data.0.plan.id');
|
$planId = data_get($data, 'items.data.0.plan.id');
|
||||||
if (Str::contains($excludedPlans, $planId)) {
|
if (Str::contains($excludedPlans, $planId)) {
|
||||||
send_internal_notification('Subscription excluded.');
|
// send_internal_notification('Subscription excluded.');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
if (!$subscription) {
|
if (! $subscription) {
|
||||||
Sleep::for(5)->seconds();
|
Sleep::for(5)->seconds();
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
}
|
}
|
||||||
if (!$subscription) {
|
if (! $subscription) {
|
||||||
send_internal_notification('No subscription found for: ' . $customerId);
|
if ($status === 'incomplete_expired') {
|
||||||
return response("No subscription found", 400);
|
// send_internal_notification('Subscription incomplete expired for customer: '.$customerId);
|
||||||
|
|
||||||
|
return response('Subscription incomplete expired', 200);
|
||||||
|
}
|
||||||
|
// send_internal_notification('No subscription found for: '.$customerId);
|
||||||
|
|
||||||
|
return response('No subscription found', 400);
|
||||||
}
|
}
|
||||||
$trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended');
|
$trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended');
|
||||||
$cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end');
|
$cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end');
|
||||||
@@ -159,12 +170,18 @@ class Stripe extends Controller
|
|||||||
$feedback = data_get($data, 'cancellation_details.feedback');
|
$feedback = data_get($data, 'cancellation_details.feedback');
|
||||||
$comment = data_get($data, 'cancellation_details.comment');
|
$comment = data_get($data, 'cancellation_details.comment');
|
||||||
$lookup_key = data_get($data, 'items.data.0.price.lookup_key');
|
$lookup_key = data_get($data, 'items.data.0.price.lookup_key');
|
||||||
if (str($lookup_key)->contains('ultimate')) {
|
if (str($lookup_key)->contains('ultimate') || str($lookup_key)->contains('dynamic')) {
|
||||||
$quantity = data_get($data, 'items.data.0.quantity', 10);
|
if (str($lookup_key)->contains('dynamic')) {
|
||||||
|
$quantity = data_get($data, 'items.data.0.quantity', 2);
|
||||||
|
} else {
|
||||||
|
$quantity = data_get($data, 'items.data.0.quantity', 10);
|
||||||
|
}
|
||||||
$team = data_get($subscription, 'team');
|
$team = data_get($subscription, 'team');
|
||||||
$team->update([
|
if ($team) {
|
||||||
'custom_server_limit' => $quantity,
|
$team->update([
|
||||||
]);
|
'custom_server_limit' => $quantity,
|
||||||
|
]);
|
||||||
|
}
|
||||||
ServerLimitCheckJob::dispatch($team);
|
ServerLimitCheckJob::dispatch($team);
|
||||||
}
|
}
|
||||||
$subscription->update([
|
$subscription->update([
|
||||||
@@ -177,7 +194,7 @@ class Stripe extends Controller
|
|||||||
$subscription->update([
|
$subscription->update([
|
||||||
'stripe_invoice_paid' => false,
|
'stripe_invoice_paid' => false,
|
||||||
]);
|
]);
|
||||||
send_internal_notification('Subscription paused or incomplete for customer: ' . $customerId);
|
// send_internal_notification('Subscription paused or incomplete for customer: '.$customerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trial ended but subscribed, reactive servers
|
// Trial ended but subscribed, reactive servers
|
||||||
@@ -187,17 +204,17 @@ class Stripe extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($feedback) {
|
if ($feedback) {
|
||||||
$reason = "Cancellation feedback for {$customerId}: '" . $feedback . "'";
|
$reason = "Cancellation feedback for {$customerId}: '".$feedback."'";
|
||||||
if ($comment) {
|
if ($comment) {
|
||||||
$reason .= ' with comment: \'' . $comment . "'";
|
$reason .= ' with comment: \''.$comment."'";
|
||||||
}
|
}
|
||||||
send_internal_notification($reason);
|
// send_internal_notification($reason);
|
||||||
}
|
}
|
||||||
if ($alreadyCancelAtPeriodEnd !== $cancelAtPeriodEnd) {
|
if ($alreadyCancelAtPeriodEnd !== $cancelAtPeriodEnd) {
|
||||||
if ($cancelAtPeriodEnd) {
|
if ($cancelAtPeriodEnd) {
|
||||||
// send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id);
|
// send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id);
|
||||||
} else {
|
} else {
|
||||||
send_internal_notification('customer.subscription.updated for customer: ' . $customerId);
|
// send_internal_notification('customer.subscription.updated for customer: '.$customerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -206,7 +223,9 @@ class Stripe extends Controller
|
|||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
$team = data_get($subscription, 'team');
|
$team = data_get($subscription, 'team');
|
||||||
$team->trialEnded();
|
if ($team) {
|
||||||
|
$team->trialEnded();
|
||||||
|
}
|
||||||
$subscription->update([
|
$subscription->update([
|
||||||
'stripe_subscription_id' => null,
|
'stripe_subscription_id' => null,
|
||||||
'stripe_plan_id' => null,
|
'stripe_plan_id' => null,
|
||||||
@@ -214,15 +233,15 @@ class Stripe extends Controller
|
|||||||
'stripe_invoice_paid' => false,
|
'stripe_invoice_paid' => false,
|
||||||
'stripe_trial_already_ended' => true,
|
'stripe_trial_already_ended' => true,
|
||||||
]);
|
]);
|
||||||
send_internal_notification('customer.subscription.deleted for customer: ' . $customerId);
|
// send_internal_notification('customer.subscription.deleted for customer: '.$customerId);
|
||||||
break;
|
break;
|
||||||
case 'customer.subscription.trial_will_end':
|
case 'customer.subscription.trial_will_end':
|
||||||
// Not used for now
|
// Not used for now
|
||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
$team = data_get($subscription, 'team');
|
$team = data_get($subscription, 'team');
|
||||||
if (!$team) {
|
if (! $team) {
|
||||||
throw new Exception('No team found for subscription: ' . $subscription->id);
|
throw new Exception('No team found for subscription: '.$subscription->id);
|
||||||
}
|
}
|
||||||
SubscriptionTrialEndsSoonJob::dispatch($team);
|
SubscriptionTrialEndsSoonJob::dispatch($team);
|
||||||
break;
|
break;
|
||||||
@@ -230,8 +249,8 @@ class Stripe extends Controller
|
|||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
$team = data_get($subscription, 'team');
|
$team = data_get($subscription, 'team');
|
||||||
if (!$team) {
|
if (! $team) {
|
||||||
throw new Exception('No team found for subscription: ' . $subscription->id);
|
throw new Exception('No team found for subscription: '.$subscription->id);
|
||||||
}
|
}
|
||||||
$team->trialEnded();
|
$team->trialEnded();
|
||||||
$subscription->update([
|
$subscription->update([
|
||||||
@@ -239,19 +258,20 @@ class Stripe extends Controller
|
|||||||
'stripe_invoice_paid' => false,
|
'stripe_invoice_paid' => false,
|
||||||
]);
|
]);
|
||||||
SubscriptionTrialEndedJob::dispatch($team);
|
SubscriptionTrialEndedJob::dispatch($team);
|
||||||
send_internal_notification('Subscription paused for customer: ' . $customerId);
|
// send_internal_notification('Subscription paused for customer: '.$customerId);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Unhandled event type
|
// Unhandled event type
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
if ($type !== 'payment_intent.payment_failed') {
|
if ($type !== 'payment_intent.payment_failed') {
|
||||||
send_internal_notification("Subscription webhook ($type) failed: " . $e->getMessage());
|
send_internal_notification("Subscription webhook ($type) failed: ".$e->getMessage());
|
||||||
}
|
}
|
||||||
$webhook->update([
|
$webhook->update([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'failure_reason' => $e->getMessage(),
|
'failure_reason' => $e->getMessage(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return response($e->getMessage(), 400);
|
return response($e->getMessage(), 400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,41 +17,49 @@ class Waitlist extends Controller
|
|||||||
try {
|
try {
|
||||||
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
|
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
|
||||||
if ($found) {
|
if ($found) {
|
||||||
if (!$found->verified) {
|
if (! $found->verified) {
|
||||||
if ($found->created_at > now()->subMinutes(config('constants.waitlist.expiration'))) {
|
if ($found->created_at > now()->subMinutes(config('constants.waitlist.expiration'))) {
|
||||||
$found->verified = true;
|
$found->verified = true;
|
||||||
$found->save();
|
$found->save();
|
||||||
send_internal_notification('Waitlist confirmed: ' . $email);
|
send_internal_notification('Waitlist confirmed: '.$email);
|
||||||
|
|
||||||
return 'Thank you for confirming your email address. We will notify you when you are next in line.';
|
return 'Thank you for confirming your email address. We will notify you when you are next in line.';
|
||||||
} else {
|
} else {
|
||||||
$found->delete();
|
$found->delete();
|
||||||
send_internal_notification('Waitlist expired: ' . $email);
|
send_internal_notification('Waitlist expired: '.$email);
|
||||||
|
|
||||||
return 'Your confirmation code has expired. Please sign up again.';
|
return 'Your confirmation code has expired. Please sign up again.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
send_internal_notification('Waitlist confirmation failed: ' . $e->getMessage());
|
send_internal_notification('Waitlist confirmation failed: '.$e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cancel(Request $request)
|
public function cancel(Request $request)
|
||||||
{
|
{
|
||||||
$email = request()->get('email');
|
$email = request()->get('email');
|
||||||
$confirmation_code = request()->get('confirmation_code');
|
$confirmation_code = request()->get('confirmation_code');
|
||||||
try {
|
try {
|
||||||
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
|
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
|
||||||
if ($found && !$found->verified) {
|
if ($found && ! $found->verified) {
|
||||||
$found->delete();
|
$found->delete();
|
||||||
send_internal_notification('Waitlist cancelled: ' . $email);
|
send_internal_notification('Waitlist cancelled: '.$email);
|
||||||
|
|
||||||
return 'Your email address has been removed from the waitlist.';
|
return 'Your email address has been removed from the waitlist.';
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
send_internal_notification('Waitlist cancellation failed: ' . $e->getMessage());
|
send_internal_notification('Waitlist cancellation failed: '.$e->getMessage());
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class Kernel extends HttpKernel
|
|||||||
|
|
||||||
'api' => [
|
'api' => [
|
||||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||||
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
|
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -20,16 +20,19 @@ class CheckForcePasswordReset
|
|||||||
auth()->logout();
|
auth()->logout();
|
||||||
request()->session()->invalidate();
|
request()->session()->invalidate();
|
||||||
request()->session()->regenerateToken();
|
request()->session()->regenerateToken();
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
$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() === 'force-password-reset' || $request->path() === 'livewire/update' || $request->path() === 'logout') {
|
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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ namespace App\Http\Middleware;
|
|||||||
use App\Providers\RouteServiceProvider;
|
use App\Providers\RouteServiceProvider;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
class DecideWhatToDoWithUser
|
class DecideWhatToDoWithUser
|
||||||
{
|
{
|
||||||
@@ -16,33 +16,37 @@ class DecideWhatToDoWithUser
|
|||||||
$currentTeam = auth()->user()?->recreate_personal_team();
|
$currentTeam = auth()->user()?->recreate_personal_team();
|
||||||
refreshSession($currentTeam);
|
refreshSession($currentTeam);
|
||||||
}
|
}
|
||||||
if(auth()?->user()?->currentTeam()){
|
if (auth()?->user()?->currentTeam()) {
|
||||||
refreshSession(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()->route('onboarding');
|
return redirect()->route('onboarding');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
if (!auth()->user()->hasVerifiedEmail()) {
|
if (! auth()->user()->hasVerifiedEmail()) {
|
||||||
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()->route('verify.email');
|
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()->route('subscription.index');
|
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()->route('onboarding');
|
return redirect()->route('onboarding');
|
||||||
}
|
}
|
||||||
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
|
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
|
||||||
@@ -51,6 +55,7 @@ class DecideWhatToDoWithUser
|
|||||||
if (isSubscriptionActive() && $request->routeIs('subscription.index')) {
|
if (isSubscriptionActive() && $request->routeIs('subscription.index')) {
|
||||||
return redirect(RouteServiceProvider::HOME);
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ class PreventRequestsDuringMaintenance extends Middleware
|
|||||||
*/
|
*/
|
||||||
protected $except = [
|
protected $except = [
|
||||||
'webhooks/*',
|
'webhooks/*',
|
||||||
'/api/health'
|
'/api/health',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class RedirectIfAuthenticated
|
|||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next, string ...$guards): Response
|
public function handle(Request $request, Closure $next, string ...$guards): Response
|
||||||
{
|
{
|
||||||
@@ -24,6 +24,7 @@ class RedirectIfAuthenticated
|
|||||||
return redirect(RouteServiceProvider::HOME);
|
return redirect(RouteServiceProvider::HOME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class TrustProxies extends Middleware
|
|||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
protected $headers =
|
protected $headers =
|
||||||
Request::HEADER_X_FORWARDED_FOR |
|
Request::HEADER_X_FORWARDED_FOR |
|
||||||
Request::HEADER_X_FORWARDED_HOST |
|
Request::HEADER_X_FORWARDED_HOST |
|
||||||
Request::HEADER_X_FORWARDED_PORT |
|
Request::HEADER_X_FORWARDED_PORT |
|
||||||
Request::HEADER_X_FORWARDED_PROTO |
|
Request::HEADER_X_FORWARDED_PROTO |
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user