mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
1195 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d921456036 | ||
|
|
6cc93250b8 | ||
|
|
39082541ff | ||
|
|
8cff40fdd4 | ||
|
|
a777db1234 | ||
|
|
3fca169096 | ||
|
|
58b451f616 | ||
|
|
f9b82f711f | ||
|
|
d92b5db320 | ||
|
|
a164e4bf3a | ||
|
|
be3cbd9e21 | ||
|
|
43fed96af1 | ||
|
|
3d3d31ef29 | ||
|
|
d51e70bcaa | ||
|
|
32f4c6c982 | ||
|
|
8f47761200 | ||
|
|
342ebecef2 | ||
|
|
220a8fe2cc | ||
|
|
480cb00098 | ||
|
|
07ed550dc2 | ||
|
|
6045870398 | ||
|
|
2bf102cdf1 | ||
|
|
889a5b2bce | ||
|
|
df964a094b | ||
|
|
e395d4ecee | ||
|
|
d077e0c83c | ||
|
|
d931241edc | ||
|
|
64a9a72457 | ||
|
|
a8417aca16 | ||
|
|
b7b2ecad59 | ||
|
|
dcaa2f4168 | ||
|
|
7c7f54d224 | ||
|
|
b942f8c726 | ||
|
|
f661f23ee5 | ||
|
|
5a631df2a2 | ||
|
|
fc9bb7dac6 | ||
|
|
0a82dc2e8e | ||
|
|
c5eff85c28 | ||
|
|
d1627276a6 | ||
|
|
2be2a2621e | ||
|
|
995c303f27 | ||
|
|
9c03525369 | ||
|
|
afe0673fd1 | ||
|
|
37333f7fbe | ||
|
|
c9160cabc5 | ||
|
|
9e289d5e97 | ||
|
|
901a580e11 | ||
|
|
d196292551 | ||
|
|
8d856b0ec6 | ||
|
|
90fad52760 | ||
|
|
2817875461 | ||
|
|
bcbdee1dcc | ||
|
|
3de4f2805a | ||
|
|
995197cad9 | ||
|
|
89cc4d1df4 | ||
|
|
61f3b3592f | ||
|
|
8cb6f67a60 | ||
|
|
7c580f898c | ||
|
|
9ad6ce5851 | ||
|
|
a66090b594 | ||
|
|
d992a3f7d7 | ||
|
|
b418a78e2e | ||
|
|
cae9ae51ad | ||
|
|
04c92ec4bd | ||
|
|
54834891fb | ||
|
|
c9e2f4244d | ||
|
|
503b86ac13 | ||
|
|
ec051eba38 | ||
|
|
2f50f64ecf | ||
|
|
66fe124dd1 | ||
|
|
87e56c2f66 | ||
|
|
f044b0292c | ||
|
|
ee1d4cd45d | ||
|
|
b25f83e096 | ||
|
|
8b7e1e4169 | ||
|
|
ca9a2cb13a | ||
|
|
93af92743c | ||
|
|
0ebef3792d | ||
|
|
7a3bb14653 | ||
|
|
fbc0a39a1c | ||
|
|
473bad24b7 | ||
|
|
313d968985 | ||
|
|
b5775ff9d2 | ||
|
|
8f66a41c09 | ||
|
|
6721471c63 | ||
|
|
638421de40 | ||
|
|
7bc5338cb3 | ||
|
|
07c9db9b54 | ||
|
|
fafc4fb71e | ||
|
|
6b49d32102 | ||
|
|
3a391aa3cb | ||
|
|
42019321e3 | ||
|
|
b61860b3ab | ||
|
|
91950e1891 | ||
|
|
42715bba50 | ||
|
|
0aacad655d | ||
|
|
289480c954 | ||
|
|
3da7746629 | ||
|
|
8d48051a8d | ||
|
|
ec16c0f0f4 | ||
|
|
19d19112d9 | ||
|
|
d9f1c2a406 | ||
|
|
ef2be40478 | ||
|
|
8e2ee5e5e4 | ||
|
|
c3da2bfade | ||
|
|
bbd6780971 | ||
|
|
0a6dab1f24 | ||
|
|
faa9a982a9 | ||
|
|
de8bb8a951 | ||
|
|
b766eef5ef | ||
|
|
a185787044 | ||
|
|
76f7cd08ee | ||
|
|
460451bcce | ||
|
|
71edb68995 | ||
|
|
e188482247 | ||
|
|
047d320665 | ||
|
|
5c4d9a85be | ||
|
|
56c08056d2 | ||
|
|
b39ac73cd8 | ||
|
|
c9054e7d8c | ||
|
|
adf5c9bd46 | ||
|
|
3ea3674407 | ||
|
|
657c7d8cff | ||
|
|
350e32326f | ||
|
|
1894573c2f | ||
|
|
73f889ac9f | ||
|
|
a336dae84c | ||
|
|
467c826c04 | ||
|
|
a4c164a57e | ||
|
|
8c0c22a925 | ||
|
|
c8d528ffc4 | ||
|
|
bb7b1f9e0c | ||
|
|
f994f83ce1 | ||
|
|
2af083b2e5 | ||
|
|
de4d0961da | ||
|
|
c8d48ccbff | ||
|
|
695d3b82b5 | ||
|
|
965625ad01 | ||
|
|
379733938c | ||
|
|
3198999746 | ||
|
|
1f03499fc5 | ||
|
|
a53d888747 | ||
|
|
62905f084f | ||
|
|
ba7ee4fba7 | ||
|
|
6cb3df9350 | ||
|
|
5c1c71c625 | ||
|
|
7fd0cfc85f | ||
|
|
efad3b1284 | ||
|
|
a06de9682c | ||
|
|
0d4ad05c1c | ||
|
|
aef088a9d2 | ||
|
|
e8f3aa681e | ||
|
|
42293fb11a | ||
|
|
65e0eb5205 | ||
|
|
2f1a7f8f40 | ||
|
|
73e9410264 | ||
|
|
c835c02bf2 | ||
|
|
336d44a5cc | ||
|
|
7027931095 | ||
|
|
87b56d538d | ||
|
|
0519ce2001 | ||
|
|
d4d0330f70 | ||
|
|
25ae54cab7 | ||
|
|
a67576b447 | ||
|
|
4d181eef8e | ||
|
|
1835a91467 | ||
|
|
bcc61b0d8b | ||
|
|
f8055e7976 | ||
|
|
2509406d1c | ||
|
|
b3d15f91e4 | ||
|
|
85c36df2a3 | ||
|
|
6ef79f5213 | ||
|
|
b576014d07 | ||
|
|
1f37318f79 | ||
|
|
6950966b06 | ||
|
|
8eacf67725 | ||
|
|
52120e7a38 | ||
|
|
1490828069 | ||
|
|
9bdad6bb67 | ||
|
|
f24063cfea | ||
|
|
1defed27a0 | ||
|
|
34d6a12d95 | ||
|
|
5d3de967f0 | ||
|
|
8b73f9da17 | ||
|
|
820099622e | ||
|
|
366d39a7a3 | ||
|
|
853a14c6b8 | ||
|
|
15ca68f7e1 | ||
|
|
8b9548a463 | ||
|
|
6688120aee | ||
|
|
93e4e723fa | ||
|
|
8b74e50c50 | ||
|
|
129a644781 | ||
|
|
bbfbd4a105 | ||
|
|
9d31d990fc | ||
|
|
c7f15c42fa | ||
|
|
515d401746 | ||
|
|
2a03b452d3 | ||
|
|
7aa8c765f6 | ||
|
|
038f65aae6 | ||
|
|
db24828a5a | ||
|
|
c7693d0ec3 | ||
|
|
5e2afd4b4d | ||
|
|
7a21312daf | ||
|
|
051a1405e7 | ||
|
|
a6669ed876 | ||
|
|
f1b00436aa | ||
|
|
e699103d3e | ||
|
|
ba9cb88ca3 | ||
|
|
365850d922 | ||
|
|
46ed17c99e | ||
|
|
fadff798a7 | ||
|
|
81512bb3b7 | ||
|
|
3dd00dd91a | ||
|
|
fd97c5085b | ||
|
|
fa6a249fb4 | ||
|
|
0aa9b1735b | ||
|
|
6027bee3b8 | ||
|
|
4d72787c83 | ||
|
|
c6740cfea0 | ||
|
|
9c1d585c43 | ||
|
|
863acf988e | ||
|
|
a6b3beafbb | ||
|
|
2ffc3f497b | ||
|
|
f3a279be26 | ||
|
|
9ad6631747 | ||
|
|
0131f5e341 | ||
|
|
b5ab9a8da6 | ||
|
|
57fa2709da | ||
|
|
96c6a198d7 | ||
|
|
d106d4bd4e | ||
|
|
53cd3091f7 | ||
|
|
f1e7b870aa | ||
|
|
99fe076b5a | ||
|
|
65fcaa17d9 | ||
|
|
89c6563e00 | ||
|
|
76b0bef32e | ||
|
|
c20aa0b256 | ||
|
|
b4908cfcb4 | ||
|
|
4fb5b04d27 | ||
|
|
8385bbb0a0 | ||
|
|
cee6b54033 | ||
|
|
0dd591a5ff | ||
|
|
62278126e4 | ||
|
|
0aa85a3701 | ||
|
|
0e1ba64836 | ||
|
|
f7e1ce8656 | ||
|
|
5030c14dc2 | ||
|
|
1333cd1d84 | ||
|
|
112c259d27 | ||
|
|
130d1e1756 | ||
|
|
a7df9fa625 | ||
|
|
9064aedc89 | ||
|
|
fda5d23d32 | ||
|
|
60be51dbe0 | ||
|
|
e9f451339f | ||
|
|
4d8ffd05a9 | ||
|
|
b630105572 | ||
|
|
9fa71f847f | ||
|
|
f70a9c6974 | ||
|
|
a4d173c733 | ||
|
|
2eb7712e09 | ||
|
|
54923b7640 | ||
|
|
bb927505fe | ||
|
|
5e66e314d2 | ||
|
|
6fe791c1f1 | ||
|
|
860c537f81 | ||
|
|
a352e4cbf7 | ||
|
|
5322d446bd | ||
|
|
604ab0afd8 | ||
|
|
3d87a88d3d | ||
|
|
10f9e22a8e | ||
|
|
8edda0cdda | ||
|
|
21047afc02 | ||
|
|
2e9793ffb2 | ||
|
|
fcd100df39 | ||
|
|
dfd564a3a4 | ||
|
|
a43c916009 | ||
|
|
c8332ca9bf | ||
|
|
e98170f921 | ||
|
|
b8f25406cd | ||
|
|
76dcc12b13 | ||
|
|
baa2228c9b | ||
|
|
5275ae8e9c | ||
|
|
c71e1e107e | ||
|
|
8ab72c7e10 | ||
|
|
a8970df91b | ||
|
|
2468251f56 | ||
|
|
6e74f3e40e | ||
|
|
407f84a4bb | ||
|
|
91632f0adb | ||
|
|
af3c575d84 | ||
|
|
bf1475441d | ||
|
|
9268f9db1d | ||
|
|
600c43827a | ||
|
|
74092ea95b | ||
|
|
b67abe58e8 | ||
|
|
678647f39a | ||
|
|
453956172b | ||
|
|
b550c32f9b | ||
|
|
f6b886adbc | ||
|
|
9642453052 | ||
|
|
64fca99c26 | ||
|
|
c7da43f50d | ||
|
|
6efa2dd9ba | ||
|
|
5e980c5fe0 | ||
|
|
c8c7a415ea | ||
|
|
c3cfb8d23b | ||
|
|
1b055f0316 | ||
|
|
1fcbf0b363 | ||
|
|
61dbc81765 | ||
|
|
b8b76dfa40 | ||
|
|
297b314904 | ||
|
|
55dd1ab0a1 | ||
|
|
8c803f1c4b | ||
|
|
3b942049a2 | ||
|
|
f78fd212bb | ||
|
|
f931ebece8 | ||
|
|
ea0a9763bf | ||
|
|
b59e47dcf9 | ||
|
|
ce09ef8848 | ||
|
|
188727daba | ||
|
|
1150633fef | ||
|
|
62ae845f4b | ||
|
|
0757fd741e | ||
|
|
a07fa8ccd2 | ||
|
|
c77f32e696 | ||
|
|
4391771416 | ||
|
|
01ab820459 | ||
|
|
c7218f2856 | ||
|
|
592221b4bf | ||
|
|
836458ad85 | ||
|
|
7233c86f3d | ||
|
|
154b1b05e4 | ||
|
|
4d88638d4d | ||
|
|
9403986643 | ||
|
|
ad48610b2f | ||
|
|
63487cf3ec | ||
|
|
4ae2087c2e | ||
|
|
5179129a6b | ||
|
|
6a00d8c88c | ||
|
|
50f43f9396 | ||
|
|
6f205f8931 | ||
|
|
3776ffa49b | ||
|
|
318b5beac1 | ||
|
|
344c5d6d12 | ||
|
|
4d319a8caa | ||
|
|
74b24a0690 | ||
|
|
1ca0464957 | ||
|
|
f7ebc8a88c | ||
|
|
a102099ac1 | ||
|
|
59ac22aa05 | ||
|
|
cd7244b3d7 | ||
|
|
f81b676abe | ||
|
|
234b154053 | ||
|
|
a1d09ad574 | ||
|
|
b983b23e7e | ||
|
|
0b81e77a94 | ||
|
|
b8cf314bfe | ||
|
|
4a3338e59c | ||
|
|
5b8538c0f4 | ||
|
|
88d6320d08 | ||
|
|
651c9c2c9b | ||
|
|
8dd45cd388 | ||
|
|
126ac354d5 | ||
|
|
024769c402 | ||
|
|
5acf141669 | ||
|
|
92e3e8ab7b | ||
|
|
187a29c666 | ||
|
|
4c24631795 | ||
|
|
7d6bd10cca | ||
|
|
f8c86769a7 | ||
|
|
e0b0dda382 | ||
|
|
b8708f086e | ||
|
|
3539e4dce9 | ||
|
|
5fdadcf557 | ||
|
|
acb3f01f79 | ||
|
|
83becdb19d | ||
|
|
e3e8fe7895 | ||
|
|
6ddff8fae1 | ||
|
|
f5cb2dbdcf | ||
|
|
fe19769d82 | ||
|
|
45e404b15b | ||
|
|
5bdaa68368 | ||
|
|
d903a377bf | ||
|
|
8e7745f4c1 | ||
|
|
a9ea6330d9 | ||
|
|
bfb0260550 | ||
|
|
bba1cb3832 | ||
|
|
29ad2144b7 | ||
|
|
38d367e709 | ||
|
|
0e81ff970f | ||
|
|
00feef40a3 | ||
|
|
dfba593072 | ||
|
|
c770c8d988 | ||
|
|
0f071031a9 | ||
|
|
99efa857f4 | ||
|
|
80035395ff | ||
|
|
d3490e1c95 | ||
|
|
dab13c92eb | ||
|
|
1f18542960 | ||
|
|
8f21ea9367 | ||
|
|
73e64d9052 | ||
|
|
6cdd87da41 | ||
|
|
2a5d49f9b3 | ||
|
|
cc7ba9eb9f | ||
|
|
c4cc42c8d5 | ||
|
|
2d0838b112 | ||
|
|
07b94a8e48 | ||
|
|
4b08abc144 | ||
|
|
93e4fc2f32 | ||
|
|
6dd86eec30 | ||
|
|
a7ab5d55d3 | ||
|
|
689547463c | ||
|
|
8a0046c571 | ||
|
|
fb3991321a | ||
|
|
ca6543a919 | ||
|
|
364a6aa3a2 | ||
|
|
82b0667277 | ||
|
|
74c126c731 | ||
|
|
d87a0fe74f | ||
|
|
9a858f628d | ||
|
|
fed01fa9d2 | ||
|
|
e1468da36a | ||
|
|
ddfc1440cd | ||
|
|
5fc46384e6 | ||
|
|
48d9df1e43 | ||
|
|
6b62d91f82 | ||
|
|
e6ca8cd167 | ||
|
|
059748ad3b | ||
|
|
a334f998a2 | ||
|
|
53a5ccef31 | ||
|
|
9eea73cefb | ||
|
|
b210e1f243 | ||
|
|
ef3202101c | ||
|
|
22d5159d16 | ||
|
|
1cbd30bd9e | ||
|
|
ad54358de7 | ||
|
|
3689b58b92 | ||
|
|
5a4180a750 | ||
|
|
047922b13a | ||
|
|
798d747164 | ||
|
|
29676ffb22 | ||
|
|
8a50b063d4 | ||
|
|
3d2444ab2e | ||
|
|
7c395edab4 | ||
|
|
7e7f322e21 | ||
|
|
9350fb4b97 | ||
|
|
59c3cc6ce1 | ||
|
|
d7001937ac | ||
|
|
3fe58ec66b | ||
|
|
ed12f73483 | ||
|
|
6914280fb1 | ||
|
|
3dd5546369 | ||
|
|
576bff1af9 | ||
|
|
3c4243d854 | ||
|
|
23d121d67a | ||
|
|
548304765c | ||
|
|
037ba3ff79 | ||
|
|
48b4c17391 | ||
|
|
6acc0e6025 | ||
|
|
43d7f746e4 | ||
|
|
146fee14e5 | ||
|
|
bde7fb2acb | ||
|
|
08a729dc7b | ||
|
|
7554de5993 | ||
|
|
0538c2f478 | ||
|
|
77a0179822 | ||
|
|
3d7295fec3 | ||
|
|
fd814abd8a | ||
|
|
4c38a59995 | ||
|
|
642a6e3203 | ||
|
|
9edbc15828 | ||
|
|
43eb2fb00b | ||
|
|
9a899deeb8 | ||
|
|
9e1a7d5d9a | ||
|
|
5bdbab7276 | ||
|
|
13bceb934f | ||
|
|
78b194cb16 | ||
|
|
3616fc8ca9 | ||
|
|
10e307f92b | ||
|
|
01f027ac1b | ||
|
|
dadc7aaf08 | ||
|
|
b96807d34c | ||
|
|
45b736bb01 | ||
|
|
3d873a79a0 | ||
|
|
6869c582ff | ||
|
|
9b9e5e939c | ||
|
|
f626c15ecc | ||
|
|
8df1fe2e60 | ||
|
|
fd2a533057 | ||
|
|
0a6401f990 | ||
|
|
1326fcb345 | ||
|
|
8b8e534598 | ||
|
|
0b518a3b76 | ||
|
|
f357f40fc7 | ||
|
|
93fb14884e | ||
|
|
26ccc4afb4 | ||
|
|
5fda1bb932 | ||
|
|
409ba8a1bb | ||
|
|
49f5240ff8 | ||
|
|
0c3ed3d393 | ||
|
|
6e3dc474f2 | ||
|
|
d3eb87561e | ||
|
|
8b58c8f856 | ||
|
|
8c60ef5bd6 | ||
|
|
1d59383c78 | ||
|
|
60f590454d | ||
|
|
dcb61a553e | ||
|
|
e06e31642f | ||
|
|
9dfce48380 | ||
|
|
8eed87e2f7 | ||
|
|
d56d4eb8fc | ||
|
|
fd32cd04ab | ||
|
|
1d3b7ffd3b | ||
|
|
0b5baf60a5 | ||
|
|
bc31df6fb2 | ||
|
|
818399bc23 | ||
|
|
e7fdff0f69 | ||
|
|
6312c0ba84 | ||
|
|
44efe0b5e1 | ||
|
|
de7d584648 | ||
|
|
b9f12d2586 | ||
|
|
c76e8bb0de | ||
|
|
3b655f8e3f | ||
|
|
2b9df41444 | ||
|
|
628fec6904 | ||
|
|
f36135cbfc | ||
|
|
75fe005055 | ||
|
|
8ff7aeb78b | ||
|
|
f1a9e28d5a | ||
|
|
843cd90ee5 | ||
|
|
1cbfd03912 | ||
|
|
ce60a39dc5 | ||
|
|
f1e4395a83 | ||
|
|
52fd7ad571 | ||
|
|
5f797ec0ae | ||
|
|
21e77bf0c1 | ||
|
|
0686e48e89 | ||
|
|
1cef233db2 | ||
|
|
907e52572c | ||
|
|
795c8abf64 | ||
|
|
cc641d8cba | ||
|
|
d4668ef44a | ||
|
|
e8b539c3bd | ||
|
|
6555f0b50c | ||
|
|
bb05058dda | ||
|
|
9667cd4a7a | ||
|
|
3ae9501814 | ||
|
|
73d0948734 | ||
|
|
c3e2a741ea | ||
|
|
4792146f1d | ||
|
|
09b9305aa3 | ||
|
|
ff7d0d442d | ||
|
|
9a127bdc80 | ||
|
|
919e88afb4 | ||
|
|
1d1ec20cb7 | ||
|
|
5c29ecdf10 | ||
|
|
9e09c449cf | ||
|
|
09bcd693f5 | ||
|
|
0c15e45419 | ||
|
|
31cbf552a2 | ||
|
|
f7853ee174 | ||
|
|
de3a7b6eca | ||
|
|
b56c7c34cb | ||
|
|
49845f3da7 | ||
|
|
987409bae4 | ||
|
|
07d8461f96 | ||
|
|
f255a71434 | ||
|
|
2a2818ac0d | ||
|
|
fd3cdc2c7d | ||
|
|
84c3f832ae | ||
|
|
70c28fceeb | ||
|
|
f2c4f83f5a | ||
|
|
c8dd6f07ac | ||
|
|
561e424a7d | ||
|
|
c46d38907e | ||
|
|
5c334bbac6 | ||
|
|
9628072b0c | ||
|
|
39ecff9f90 | ||
|
|
d1daec060a | ||
|
|
fb5bea7f91 | ||
|
|
efc3ea6e40 | ||
|
|
ecefb9e1f5 | ||
|
|
a4dea2009a | ||
|
|
829e41f93f | ||
|
|
e7e3adc7fb | ||
|
|
a993fef235 | ||
|
|
81df71416c | ||
|
|
40af7aa025 | ||
|
|
050155328b | ||
|
|
376c081bed | ||
|
|
9f5e1fa9e3 | ||
|
|
7d139fd33b | ||
|
|
b75a2857a0 | ||
|
|
ab6c1ddc20 | ||
|
|
dc0b0980a9 | ||
|
|
788d1711db | ||
|
|
d92dc4c5e6 | ||
|
|
4bf34aea62 | ||
|
|
7e9a54ce67 | ||
|
|
f8c19e1fb3 | ||
|
|
27c1bda09b | ||
|
|
1af7ffcdc4 | ||
|
|
39647367a5 | ||
|
|
ae4b263810 | ||
|
|
8901bb5df8 | ||
|
|
053aa25d2c | ||
|
|
7a7157c155 | ||
|
|
0c5e8600bd | ||
|
|
048e153025 | ||
|
|
e7cafe6850 | ||
|
|
1385a86084 | ||
|
|
348923ae02 | ||
|
|
7d754558b0 | ||
|
|
744609e7e9 | ||
|
|
9bd05b65a3 | ||
|
|
d42934f258 | ||
|
|
ff752e2411 | ||
|
|
4120fba9a8 | ||
|
|
6ecb9c21ce | ||
|
|
01f7b07fa3 | ||
|
|
54d8cb9027 | ||
|
|
238337fecb | ||
|
|
7a51acbf8d | ||
|
|
fb478c79b3 | ||
|
|
abcc004953 | ||
|
|
2edf71a0dd | ||
|
|
cbec39099a | ||
|
|
dba5499182 | ||
|
|
2fdf52929c | ||
|
|
8128dfc061 | ||
|
|
2b394d6fea | ||
|
|
964ded1d0b | ||
|
|
838c3830d6 | ||
|
|
2db93bd9b9 | ||
|
|
2f82dedd4f | ||
|
|
8106602d15 | ||
|
|
3d0bf6b472 | ||
|
|
ba7a7e9695 | ||
|
|
dd0ad04384 | ||
|
|
910a1f43a9 | ||
|
|
e2f959ce4c | ||
|
|
68fe886fb0 | ||
|
|
4631c73809 | ||
|
|
77558b37da | ||
|
|
e060409a76 | ||
|
|
af01bc3e77 | ||
|
|
1e158badfc | ||
|
|
c620bb58ed | ||
|
|
8a91395472 | ||
|
|
c5f3398b73 | ||
|
|
3878527de8 | ||
|
|
4abcb2d5b9 | ||
|
|
a635e51486 | ||
|
|
b6ce2e9122 | ||
|
|
8c60dd5523 | ||
|
|
94e2d951c4 | ||
|
|
381e24bea5 | ||
|
|
2b1e35980f | ||
|
|
a42c8da344 | ||
|
|
7a0e415ecf | ||
|
|
d721f4809a | ||
|
|
22431eee9a | ||
|
|
c058c0a766 | ||
|
|
1724c0d3ff | ||
|
|
0b8f48230f | ||
|
|
e8d84b7067 | ||
|
|
5236bbc757 | ||
|
|
094e1d1bba | ||
|
|
68b25523d6 | ||
|
|
bdc478d5f5 | ||
|
|
002472d7c6 | ||
|
|
0d65bf62b9 | ||
|
|
01c7e76071 | ||
|
|
884ae0efb0 | ||
|
|
8e7040bf7c | ||
|
|
059e6a88eb | ||
|
|
9947158f7e | ||
|
|
61aa9e8766 | ||
|
|
75813a289c | ||
|
|
af11d8cf3d | ||
|
|
48990db699 | ||
|
|
da71353bfa | ||
|
|
0f5559bc61 | ||
|
|
1afb509c33 | ||
|
|
bccca6e874 | ||
|
|
083dc15053 | ||
|
|
1b6d376472 | ||
|
|
891deee05a | ||
|
|
5ffbba908b | ||
|
|
f762959c9f | ||
|
|
90a5a23fd9 | ||
|
|
94e87141ff | ||
|
|
fceaf3e94b | ||
|
|
3be554cb55 | ||
|
|
27b18fbedf | ||
|
|
5e7c6906b3 | ||
|
|
d05ffe32a3 | ||
|
|
f1298d1db4 | ||
|
|
408738e08d | ||
|
|
8d04fbdb74 | ||
|
|
dccb31d17e | ||
|
|
f61210287e | ||
|
|
18ad7220f0 | ||
|
|
79e0df1d43 | ||
|
|
a2f53085e5 | ||
|
|
c5782252ea | ||
|
|
bf3d88facd | ||
|
|
e45b0bf715 | ||
|
|
9db6c12eea | ||
|
|
3a391b69e8 | ||
|
|
cc1fb83c79 | ||
|
|
efa5dd28f1 | ||
|
|
f1eddae379 | ||
|
|
34febe670d | ||
|
|
3137131a1a | ||
|
|
1b6546d26c | ||
|
|
b9f820cef4 | ||
|
|
d8639f58d7 | ||
|
|
cf9be9355f | ||
|
|
e36bb11ba8 | ||
|
|
190beb3d3f | ||
|
|
890a6925d1 | ||
|
|
d03b8420f8 | ||
|
|
cbd3c880c3 | ||
|
|
6b24001876 | ||
|
|
6bb45430c9 | ||
|
|
bc6b4ed850 | ||
|
|
8a63ef5da9 | ||
|
|
e324866a27 | ||
|
|
0e5f733657 | ||
|
|
c5932ed337 | ||
|
|
ef428f844f | ||
|
|
eb8b752a6e | ||
|
|
ce0b38035c | ||
|
|
562a8f1fac | ||
|
|
68f1621757 | ||
|
|
e3087573bb | ||
|
|
7869f223a3 | ||
|
|
0e99f27108 | ||
|
|
f445a8c312 | ||
|
|
845fc191d4 | ||
|
|
95d0d72e0d | ||
|
|
76f695036c | ||
|
|
225bf06736 | ||
|
|
3a287ae974 | ||
|
|
e5c61b9f9f | ||
|
|
32bc876dfc | ||
|
|
c9b3d2a43d | ||
|
|
f343210e7c | ||
|
|
4a42bff0dc | ||
|
|
36931b5b18 | ||
|
|
3b080abada | ||
|
|
7feba4bbaa | ||
|
|
eef8c756df | ||
|
|
9ed30cb0dc | ||
|
|
6bc43bd999 | ||
|
|
e7683ee9a5 | ||
|
|
ee71aeaa36 | ||
|
|
404c664500 | ||
|
|
aa80392b46 | ||
|
|
c9509ef658 | ||
|
|
31e08a24c9 | ||
|
|
14b32e30cd | ||
|
|
5aaad66fe5 | ||
|
|
b6745c691b | ||
|
|
5ee29c6072 | ||
|
|
b69584fe26 | ||
|
|
4c3907c296 | ||
|
|
bf44b4b949 | ||
|
|
bee7a2357b | ||
|
|
98704fc3c2 | ||
|
|
e286e78ddc | ||
|
|
557e1407d0 | ||
|
|
3c99f24b5a | ||
|
|
512197021b | ||
|
|
e233ec05b5 | ||
|
|
d0e3a20a65 | ||
|
|
e2e6813632 | ||
|
|
963c519c38 | ||
|
|
d04513d817 | ||
|
|
64a7f27e37 | ||
|
|
65652142b2 | ||
|
|
7691319c59 | ||
|
|
206bd50d00 | ||
|
|
6159783a73 | ||
|
|
ed5f831c86 | ||
|
|
65be83e75d | ||
|
|
25a471b045 | ||
|
|
60c7a29aa8 | ||
|
|
11ab6669a0 | ||
|
|
53965ab8ed | ||
|
|
ea271ca079 | ||
|
|
f16d0f650f | ||
|
|
cb80341a78 | ||
|
|
83d96c8d11 | ||
|
|
a8ca57d095 | ||
|
|
2d936a4b22 | ||
|
|
0653eb8511 | ||
|
|
cc64132627 | ||
|
|
e0391e5abd | ||
|
|
025135bd2a | ||
|
|
5e5873a08d | ||
|
|
14652c2878 | ||
|
|
9bbe9567c7 | ||
|
|
7913a639b5 | ||
|
|
adecf328fc | ||
|
|
a7ef5c456d | ||
|
|
117f1d4155 | ||
|
|
075f3ce930 | ||
|
|
d5b3e88fc4 | ||
|
|
ba55e0c1bb | ||
|
|
54671354f0 | ||
|
|
a2c7e8d455 | ||
|
|
32dbdf5204 | ||
|
|
019887739c | ||
|
|
5596e41f2b | ||
|
|
6a73a00a1f | ||
|
|
4121c9dd8b | ||
|
|
e4781dc129 | ||
|
|
1c90f46f2a | ||
|
|
e78d851c85 | ||
|
|
52d05005ed | ||
|
|
f03aa57758 | ||
|
|
8c20c833ba | ||
|
|
2fe6766b7f | ||
|
|
d9599da4a8 | ||
|
|
3f453ba7c0 | ||
|
|
bd02c3055a | ||
|
|
7ea7d85d15 | ||
|
|
d38350c282 | ||
|
|
a83c70004c | ||
|
|
49e1404a2c | ||
|
|
76f23e7dbf | ||
|
|
ad8653f54d | ||
|
|
8939d77051 | ||
|
|
37be4a1796 | ||
|
|
e4c923e358 | ||
|
|
62ca3ffaa5 | ||
|
|
9af3ce4be5 | ||
|
|
fe143ef8a5 | ||
|
|
5fb5845e90 | ||
|
|
794cfbd8eb | ||
|
|
29ee9915f3 | ||
|
|
331d485213 | ||
|
|
665e3761c4 | ||
|
|
ac19f0e34f | ||
|
|
d7cfa0578f | ||
|
|
694169bb84 | ||
|
|
ab853cac87 | ||
|
|
51db2f797d | ||
|
|
0c90d3d0a1 | ||
|
|
51efe23690 | ||
|
|
e3ee84105c | ||
|
|
6cbd61ac6c | ||
|
|
638d0c8c99 | ||
|
|
aecc81fe9d | ||
|
|
c9a1437870 | ||
|
|
66b41b3d4c | ||
|
|
c41cfe2a2f | ||
|
|
5f2ad56529 | ||
|
|
cd842bc1b2 | ||
|
|
27b6aad53a | ||
|
|
64b58b7661 | ||
|
|
94960d96a9 | ||
|
|
2549244f97 | ||
|
|
5bfffce33b | ||
|
|
3a4f19f368 | ||
|
|
50e17ed932 | ||
|
|
a8fcd7aee4 | ||
|
|
87036cc49b | ||
|
|
e48842c6ec | ||
|
|
b9e405c497 | ||
|
|
f75effe022 | ||
|
|
a745f568f3 | ||
|
|
e2e3ad0358 | ||
|
|
ba769f5fb7 | ||
|
|
0126286731 | ||
|
|
7952202435 | ||
|
|
798acb8ee5 | ||
|
|
ef595dd4c2 | ||
|
|
70c662daf8 | ||
|
|
8ae385b9f9 | ||
|
|
802a0f7684 | ||
|
|
62c38c9859 | ||
|
|
27c36bec83 | ||
|
|
c6b8eabe10 | ||
|
|
967fca9eca | ||
|
|
40a239ddda | ||
|
|
99d07981cf | ||
|
|
b3ee6b7144 | ||
|
|
468ad7d904 | ||
|
|
f1aa97e374 | ||
|
|
3b6d3343c7 | ||
|
|
ab2f9f073f | ||
|
|
67131152cc | ||
|
|
3bda289428 | ||
|
|
fadfa0ad8e | ||
|
|
11a957c6c9 | ||
|
|
b46de99af9 | ||
|
|
03420076c9 | ||
|
|
549446abdf | ||
|
|
06ab2145ca | ||
|
|
5d088e530e | ||
|
|
123e6eddd7 | ||
|
|
2c17e431ac | ||
|
|
fe6073ba7d | ||
|
|
8a9ad04744 | ||
|
|
75d1ec4f42 | ||
|
|
a0abde8652 | ||
|
|
db13dd9304 | ||
|
|
638bcf9732 | ||
|
|
b06b465ffa | ||
|
|
02c8b9f471 | ||
|
|
1ff1664b6c | ||
|
|
52d84c5e9e | ||
|
|
e0289e2949 | ||
|
|
ff8d8371ad | ||
|
|
69343f974a | ||
|
|
2dc175be63 | ||
|
|
d93bf97919 | ||
|
|
4ea8916d53 | ||
|
|
f7fca69a23 | ||
|
|
f954ee15c3 | ||
|
|
3c54e01d87 | ||
|
|
00d708610d | ||
|
|
f3b04c1ef9 | ||
|
|
6b751f965b | ||
|
|
d3c9894479 | ||
|
|
f68aace445 | ||
|
|
f042c70b3c | ||
|
|
2116d79aad | ||
|
|
4bc63e283c | ||
|
|
d5804f99c2 | ||
|
|
dfc353ce54 | ||
|
|
8f65ddb754 | ||
|
|
29e750f0b2 | ||
|
|
b24661b876 | ||
|
|
bbbd605f32 | ||
|
|
ff13cb4e26 | ||
|
|
5cb572b6a5 | ||
|
|
d8b97e06cf | ||
|
|
becb4df950 | ||
|
|
20dc2b47fe | ||
|
|
67df166c20 | ||
|
|
87c3d0048c | ||
|
|
1c71ac78e2 | ||
|
|
41181cac12 | ||
|
|
41b6df0e6e | ||
|
|
4f3f98be0a | ||
|
|
7a97a4b69c | ||
|
|
6ae87466ca | ||
|
|
5159d47159 | ||
|
|
0138d04080 | ||
|
|
c803768e5f | ||
|
|
60c8e0d625 | ||
|
|
dd99ad0af8 | ||
|
|
24a1f02af5 | ||
|
|
601a1e128e | ||
|
|
ccb9769e67 | ||
|
|
d79da996d3 | ||
|
|
4f800f5331 | ||
|
|
a19a58338c | ||
|
|
8a80dbd5d8 | ||
|
|
ec5cca7b3e | ||
|
|
ce721c1764 | ||
|
|
f4d7c4f942 | ||
|
|
40716550ec | ||
|
|
f0ee26cd86 | ||
|
|
423dfc6280 | ||
|
|
6d9a66ff1b | ||
|
|
17c8872130 | ||
|
|
3ffa2b6b8d | ||
|
|
35134f2327 | ||
|
|
8ed4b540e1 | ||
|
|
47202a7951 | ||
|
|
fe6e76ad0d | ||
|
|
e9920f05f5 | ||
|
|
57a39f12bb | ||
|
|
ba85e3bc8b | ||
|
|
1fd12832ca | ||
|
|
a8807b8d09 | ||
|
|
dbc55233cb | ||
|
|
af6d94c0d8 | ||
|
|
e022492770 | ||
|
|
5e03979f9c | ||
|
|
956416b522 | ||
|
|
2846e049fa | ||
|
|
3ffd3fc819 | ||
|
|
63dff5961e | ||
|
|
4b024017ad | ||
|
|
720bb8c478 | ||
|
|
771dc30b81 | ||
|
|
6f97af096d | ||
|
|
3d28669cad | ||
|
|
efe043ec9d | ||
|
|
6bb79e10bc | ||
|
|
ba2e4c06f1 | ||
|
|
1bfb9637ba | ||
|
|
d7e9821582 | ||
|
|
08585f7e9a | ||
|
|
3eedb43b66 | ||
|
|
26aca6a4c0 | ||
|
|
3cbf8c281d | ||
|
|
d931d57373 | ||
|
|
4e680deb93 | ||
|
|
6a6275d4fa | ||
|
|
efd9087b74 | ||
|
|
6882ce8d0f | ||
|
|
eccd41217f | ||
|
|
538de9bd81 | ||
|
|
a249ee1b1f | ||
|
|
828fec9448 | ||
|
|
205995cabe | ||
|
|
4071e096bc | ||
|
|
14bac0f0d7 | ||
|
|
69c124032c | ||
|
|
b55bd298f2 | ||
|
|
86c2415210 | ||
|
|
82f3d54bc3 | ||
|
|
a6e76dfabc | ||
|
|
c1f6bf41f5 | ||
|
|
f934dfef33 | ||
|
|
19c66c6628 | ||
|
|
d7d948caf6 | ||
|
|
1b34337fe8 | ||
|
|
718603e37e | ||
|
|
9df0a2e545 | ||
|
|
7ffebd71f3 | ||
|
|
2f286a6595 | ||
|
|
13701f6030 | ||
|
|
91f224ddea | ||
|
|
8cffae10b2 | ||
|
|
1158b2f4db | ||
|
|
f542bcf428 | ||
|
|
22178df8ae | ||
|
|
acfe1daf9b | ||
|
|
fb3f71881f | ||
|
|
0f7546a4dc | ||
|
|
6ccafacc87 | ||
|
|
852c2df12e | ||
|
|
4c752951ab | ||
|
|
f32191b889 | ||
|
|
31251ef6cb | ||
|
|
e9365aa09b | ||
|
|
e5c860319f | ||
|
|
ceedd5225f | ||
|
|
61efdfb7c1 | ||
|
|
fcef5d902e | ||
|
|
2073b8949b | ||
|
|
5c59a752e3 | ||
|
|
caf6e3a23e | ||
|
|
8eb1c4da46 | ||
|
|
bab2f391ed | ||
|
|
131c6df82a | ||
|
|
9f44d0c47a | ||
|
|
dff7ed5b7b | ||
|
|
54b6472f3b | ||
|
|
04a36e5c90 | ||
|
|
b2f851272b | ||
|
|
778915599f | ||
|
|
a6f9527157 | ||
|
|
db976709c7 | ||
|
|
7e4947ba07 | ||
|
|
e2578a7dd0 | ||
|
|
7d028b15f5 | ||
|
|
d240bfda8b | ||
|
|
d59ec2548a | ||
|
|
e0cefc787a | ||
|
|
0496198d0f | ||
|
|
cbf7f1fa41 | ||
|
|
66587d864a | ||
|
|
b106705c8d | ||
|
|
5cc22a8271 | ||
|
|
cd22863a8c | ||
|
|
118a02f70d | ||
|
|
862177d61a | ||
|
|
41280aa780 | ||
|
|
3a987c8a6e | ||
|
|
29ca461a9a | ||
|
|
d48b72c160 | ||
|
|
ce49f26c53 | ||
|
|
02989678be | ||
|
|
810b55163c | ||
|
|
ac13ba0957 | ||
|
|
a3e088199f | ||
|
|
42ee4ca032 | ||
|
|
17deff4d86 | ||
|
|
8b6323b906 | ||
|
|
a696b5271a | ||
|
|
31959f26d4 | ||
|
|
45d2f80f69 | ||
|
|
53975fcf61 | ||
|
|
76296c1f19 | ||
|
|
c25baf69e1 | ||
|
|
f952512615 | ||
|
|
c6557eada8 | ||
|
|
2c2d74c0d6 | ||
|
|
028a2eb275 | ||
|
|
ce7fad5bef | ||
|
|
cd7852e4f9 | ||
|
|
7b022a2482 | ||
|
|
12d9b6538b | ||
|
|
335788c2d6 | ||
|
|
2352e4a71d | ||
|
|
dc03179bd1 | ||
|
|
cc72f416e8 | ||
|
|
3b67d0a8de | ||
|
|
0135ba7e89 | ||
|
|
a28a28cd23 | ||
|
|
b52680a2d8 | ||
|
|
0670e6c1d6 | ||
|
|
c3882b75c1 | ||
|
|
64b6f86a36 | ||
|
|
b9efc22253 | ||
|
|
e3d9eb0154 | ||
|
|
66f3967479 | ||
|
|
c54439e84c | ||
|
|
db4a4c74fc | ||
|
|
5c7ef80219 | ||
|
|
243d1c06fc | ||
|
|
ef25f7d800 | ||
|
|
45640ffdb1 | ||
|
|
378291b209 | ||
|
|
3583e552f1 | ||
|
|
d7dfeaf988 | ||
|
|
7fe5eca661 | ||
|
|
0dff57e69f | ||
|
|
f4803ad58b | ||
|
|
2d7bbbe300 | ||
|
|
928b68043b | ||
|
|
b21add0210 | ||
|
|
c41ffd6bfb | ||
|
|
b4874c7df3 | ||
|
|
c505a6ce9c | ||
|
|
706e4b13ee | ||
|
|
4af471ee31 | ||
|
|
87062e4e22 | ||
|
|
500ba0fab8 | ||
|
|
1c72c127d5 | ||
|
|
69bb4ae5ee | ||
|
|
5f8b8bd730 | ||
|
|
44f6d93639 | ||
|
|
e35b8a0f96 | ||
|
|
b26e23e7c3 | ||
|
|
e6f7e32037 | ||
|
|
1c386db41d | ||
|
|
085b655d9f | ||
|
|
2788fcf4e1 | ||
|
|
d058e04213 | ||
|
|
066f171163 | ||
|
|
2001be07d0 | ||
|
|
39552cc42f | ||
|
|
7f5d7e0eb0 | ||
|
|
0eda49b104 | ||
|
|
636995d0e4 | ||
|
|
4c0623f022 | ||
|
|
3e2e1080f5 | ||
|
|
3f866a07d8 | ||
|
|
23571ae104 | ||
|
|
c1710c8f7b | ||
|
|
d4d2cc71a0 | ||
|
|
8d86d53292 | ||
|
|
fae97e4dee | ||
|
|
8d0c3abf2e | ||
|
|
d396f649df | ||
|
|
ec21155c9e | ||
|
|
58111f53b9 | ||
|
|
2cbe1e8489 | ||
|
|
10e5a58b9e | ||
|
|
6f886e8b6f | ||
|
|
f96a91eb31 | ||
|
|
65a1961722 | ||
|
|
c5a932ab88 | ||
|
|
d1e10dacc0 | ||
|
|
96327af838 | ||
|
|
1cb6d594d0 | ||
|
|
16261fc36e | ||
|
|
cff694b0c4 | ||
|
|
97fd56b9e4 | ||
|
|
72cfa3e7b0 | ||
|
|
3cf41e1e23 | ||
|
|
2a7a63a672 | ||
|
|
7fb9e672cf | ||
|
|
9012f6b953 | ||
|
|
407eba8b76 | ||
|
|
68f6ab5796 | ||
|
|
3dd36a2271 | ||
|
|
7f69eb3c2e | ||
|
|
6ccbf911b2 | ||
|
|
5c77cec68f | ||
|
|
25a0489f7f | ||
|
|
5e27b88bef | ||
|
|
ccb972dcb9 |
@@ -4,3 +4,7 @@ APP_KEY=
|
|||||||
|
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
REDIS_PASSWORD=
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
|
PUSHER_APP_ID=
|
||||||
|
PUSHER_APP_KEY=
|
||||||
|
PUSHER_APP_SECRET=
|
||||||
|
|||||||
12
.env.windows-docker-desktop.example
Normal file
12
.env.windows-docker-desktop.example
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
IS_WINDOWS_DOCKER_DESKTOP=true
|
||||||
|
|
||||||
|
APP_ID=coolify-windows-docker-desktop
|
||||||
|
APP_NAME=Coolify
|
||||||
|
APP_KEY=base64:ssTlCmrIE/q7whnKMvT6DwURikg69COzGsAwFVROm80=
|
||||||
|
|
||||||
|
DB_PASSWORD=coolify
|
||||||
|
REDIS_PASSWORD=coolify
|
||||||
|
|
||||||
|
PUSHER_APP_ID=coolify
|
||||||
|
PUSHER_APP_KEY=coolify
|
||||||
|
PUSHER_APP_SECRET=coolify
|
||||||
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
2
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
@@ -21,6 +21,6 @@ body:
|
|||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: Coolify's version (see bottom left corner).
|
description: Coolify's version (see top of your screen).
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
20
.github/workflows/coolify-helper-next.yml
vendored
20
.github/workflows/coolify-helper-next.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -40,15 +40,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -64,13 +64,13 @@ jobs:
|
|||||||
needs: [ amd64, aarch64 ]
|
needs: [ amd64, aarch64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
|||||||
20
.github/workflows/coolify-helper.yml
vendored
20
.github/workflows/coolify-helper.yml
vendored
@@ -18,15 +18,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -40,15 +40,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@@ -64,13 +64,13 @@ jobs:
|
|||||||
needs: [ amd64, aarch64 ]
|
needs: [ amd64, aarch64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
|||||||
84
.github/workflows/coolify-testing-host.yml
vendored
Normal file
84
.github/workflows/coolify-testing-host.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
name: Coolify Testing Host (v4-non-prod)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main", "next" ]
|
||||||
|
paths:
|
||||||
|
- .github/workflows/coolify-testing-host.yml
|
||||||
|
- docker/testing-host/Dockerfile
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME: "coollabsio/coolify-testing-host"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
amd64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Login to ghcr.io
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build image and push to registry
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
no-cache: true
|
||||||
|
context: .
|
||||||
|
file: docker/testing-host/Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
aarch64:
|
||||||
|
runs-on: [ self-hosted, arm64 ]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Login to ghcr.io
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build image and push to registry
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
no-cache: true
|
||||||
|
context: .
|
||||||
|
file: docker/testing-host/Dockerfile
|
||||||
|
platforms: linux/aarch64
|
||||||
|
push: true
|
||||||
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64
|
||||||
|
merge-manifest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
needs: [ amd64, aarch64 ]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Login to ghcr.io
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Create & publish manifest
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
- uses: sarisia/actions-status-discord@v1
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
|
||||||
28
.github/workflows/development-build.yml
vendored
28
.github/workflows/development-build.yml
vendored
@@ -2,7 +2,7 @@ name: Development Build (v4)
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["next"]
|
branches-ignore: ["main", "v3"]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- .github/workflows/coolify-helper.yml
|
- .github/workflows/coolify-helper.yml
|
||||||
- docker/coolify-helper/Dockerfile
|
- docker/coolify-helper/Dockerfile
|
||||||
@@ -15,42 +15,42 @@ jobs:
|
|||||||
amd64:
|
amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
aarch64:
|
aarch64:
|
||||||
runs-on: [self-hosted, arm64]
|
runs-on: [self-hosted, arm64]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/aarch64
|
platforms: linux/aarch64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
|
||||||
merge-manifest:
|
merge-manifest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
@@ -59,20 +59,20 @@ jobs:
|
|||||||
needs: [amd64, aarch64]
|
needs: [amd64, aarch64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Create & publish manifest
|
- name: Create & publish manifest
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
|
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/docker-image.yml
vendored
2
.github/workflows/docker-image.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
22
.github/workflows/production-build.yml
vendored
22
.github/workflows/production-build.yml
vendored
@@ -12,9 +12,9 @@ jobs:
|
|||||||
amd64:
|
amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
@@ -34,9 +34,9 @@ jobs:
|
|||||||
aarch64:
|
aarch64:
|
||||||
runs-on: [self-hosted, arm64]
|
runs-on: [self-hosted, arm64]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod-ssu/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
@@ -61,13 +61,13 @@ jobs:
|
|||||||
needs: [amd64, aarch64]
|
needs: [amd64, aarch64]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -78,7 +78,7 @@ jobs:
|
|||||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
||||||
- name: Create & publish manifest
|
- name: Create & publish manifest
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
|
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
- uses: sarisia/actions-status-discord@v1
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
|
|
||||||
$email = 'test@example.com';
|
|
||||||
$user = User::whereEmail($email)->first();
|
|
||||||
$teams = $user->teams;
|
|
||||||
foreach ($teams as $team) {
|
|
||||||
$servers = $team->servers;
|
|
||||||
if ($servers->count() > 0) {
|
|
||||||
foreach ($servers as $server) {
|
|
||||||
dump($server);
|
|
||||||
$server->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dump($team);
|
|
||||||
$team->delete();
|
|
||||||
}
|
|
||||||
if ($user) {
|
|
||||||
dump($user);
|
|
||||||
$user->delete();
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @label Send Email
|
|
||||||
* @description Send email to all users
|
|
||||||
*/
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
|
|
||||||
set_transanctional_email_settings();
|
|
||||||
|
|
||||||
$users = User::whereEmail('test@example.com');
|
|
||||||
foreach ($users as $user) {
|
|
||||||
Mail::send([], [], function ($message) use ($user) {
|
|
||||||
$message
|
|
||||||
->to($user->email)
|
|
||||||
->subject("Testing")
|
|
||||||
->text(
|
|
||||||
<<<EOF
|
|
||||||
Hello,
|
|
||||||
|
|
||||||
Welcome to Coolify Cloud.
|
|
||||||
Here is your user id: $user->id
|
|
||||||
|
|
||||||
EOF
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -22,8 +22,6 @@ You can ask for guidance anytime on our
|
|||||||
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
||||||
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
||||||
|
|
||||||
- Run `./scripts/run setup:dev` - This will generate a secret key for you, delete any existing database layouts, migrate database to the new layout, and seed your database.
|
|
||||||
|
|
||||||
### 4) Start development
|
### 4) Start development
|
||||||
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||||
|
|
||||||
@@ -31,7 +29,6 @@ Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if y
|
|||||||
|
|
||||||
Mails are caught by Mailpit: `localhost:8025`
|
Mails are caught by Mailpit: `localhost:8025`
|
||||||
|
|
||||||
|
|
||||||
## New Service Contribution
|
## New Service Contribution
|
||||||
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
|
Check out the docs [here](https://coolify.io/docs/resources/services/add-service).
|
||||||
|
|
||||||
|
|||||||
86
README.md
86
README.md
@@ -10,6 +10,17 @@ No vendor lock-in, which means that all the configuration for your applications/
|
|||||||
|
|
||||||
For more information, take a look at our landing page [here](https://coolify.io).
|
For more information, take a look at our landing page [here](https://coolify.io).
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
||||||
|
```
|
||||||
|
You can find the installation script source [here](./scripts/install.sh).
|
||||||
|
|
||||||
|
# Support
|
||||||
|
|
||||||
|
Contact us [here](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, 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.
|
||||||
|
|
||||||
@@ -17,6 +28,43 @@ https://coolify.io/sponsorships
|
|||||||
|
|
||||||
Thank you so much!
|
Thank you so much!
|
||||||
|
|
||||||
|
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
||||||
|
|
||||||
|
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
||||||
|
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
||||||
|
|
||||||
|
## Github Sponsors ($40+)
|
||||||
|
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
|
||||||
|
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
|
||||||
|
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
|
||||||
|
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
|
||||||
|
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
|
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
|
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
|
||||||
|
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
|
||||||
|
<a href="https://github.com/whitesidest"><img src="https://avatars.githubusercontent.com/u/12365916?s=52&v=4" width="60px" alt="Tyler Whitesides" /></a>
|
||||||
|
<a href="https://github.com/aniftyco"><img src="https://github.com/aniftyco.png" width="60px" alt="NiftyCo" /></a>
|
||||||
|
<a href="https://github.com/iujlaki"><img src="https://github.com/iujlaki.png" width="60px" alt="Imre Ujlaki" /></a>
|
||||||
|
<a href="https://il.ly"><img src="https://github.com/Illyism.png" width="60px" alt="Ilias Ism" /></a>
|
||||||
|
<a href="https://github.com/urtho"><img src="https://github.com/urtho.png" width="60px" alt="Paweł Pierścionek" /></a>
|
||||||
|
<a href="https://github.com/monocursive"><img src="https://github.com/monocursive.png" width="60px" alt="Michael Mazurczak" /></a>
|
||||||
|
|
||||||
|
## Organizations
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/1/website"><img src="https://opencollective.com/coollabsio/organization/1/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/2/website"><img src="https://opencollective.com/coollabsio/organization/2/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/3/website"><img src="https://opencollective.com/coollabsio/organization/3/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/4/website"><img src="https://opencollective.com/coollabsio/organization/4/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/5/website"><img src="https://opencollective.com/coollabsio/organization/5/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
||||||
|
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
||||||
|
|
||||||
|
## Individuals
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
||||||
|
|
||||||
# Cloud
|
# Cloud
|
||||||
|
|
||||||
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
|
||||||
@@ -32,16 +80,6 @@ By subscribing to the cloud version, you get the Coolify server for the same pri
|
|||||||
- Better support
|
- Better support
|
||||||
- Less maintenance for you
|
- Less maintenance for you
|
||||||
|
|
||||||
# Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
|
|
||||||
```
|
|
||||||
You can find the installation script source [here](./scripts/install.sh).
|
|
||||||
|
|
||||||
# Support
|
|
||||||
|
|
||||||
Contact us [here](https://coolify.io/docs/contact).
|
|
||||||
|
|
||||||
# Recognitions
|
# Recognitions
|
||||||
|
|
||||||
@@ -59,33 +97,9 @@ Contact us [here](https://coolify.io/docs/contact).
|
|||||||
|
|
||||||
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
# 💰 Financial Contributors
|
# Repo Activity
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|

|
||||||
|
|
||||||
## Organizations
|
|
||||||
|
|
||||||
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
|
||||||
|
|
||||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="appwrite logo" width="200"/></a>
|
|
||||||
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
|
||||||
|
|
||||||
Support this project with your organization. Your logo will show up here with a link to your website.
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/1/website"><img src="https://opencollective.com/coollabsio/organization/1/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/2/website"><img src="https://opencollective.com/coollabsio/organization/2/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/3/website"><img src="https://opencollective.com/coollabsio/organization/3/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/4/website"><img src="https://opencollective.com/coollabsio/organization/4/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/5/website"><img src="https://opencollective.com/coollabsio/organization/5/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
|
|
||||||
|
|
||||||
## Individuals
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
|
|
||||||
|
|
||||||
# Star History
|
# Star History
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
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
|
||||||
@@ -10,7 +12,20 @@ class StopApplication
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Application $application)
|
public function handle(Application $application)
|
||||||
{
|
{
|
||||||
$server = $application->destination->server;
|
if ($application->destination->server->isSwarm()) {
|
||||||
|
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$servers = collect([]);
|
||||||
|
$servers->push($application->destination->server);
|
||||||
|
$application->additional_servers->map(function ($server) use ($servers) {
|
||||||
|
$servers->push($server);
|
||||||
|
});
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
if (!$server->isFunctional()) {
|
||||||
|
return 'Server is not functional';
|
||||||
|
}
|
||||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
||||||
if ($containers->count() > 0) {
|
if ($containers->count() > 0) {
|
||||||
foreach ($containers as $container) {
|
foreach ($containers as $container) {
|
||||||
@@ -22,8 +37,7 @@ class StopApplication
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: make notification for application
|
}
|
||||||
// $application->environment->project->team->notify(new StatusChanged($application));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
app/Actions/Application/StopApplicationOneServer.php
Normal file
38
app/Actions/Application/StopApplicationOneServer.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Application;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Server;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StopApplicationOneServer
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Application $application, Server $server)
|
||||||
|
{
|
||||||
|
if ($application->destination->server->isSwarm()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!$server->isFunctional()) {
|
||||||
|
return 'Server is not functional';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
||||||
|
if ($containers->count() > 0) {
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$containerName = data_get($container, 'Names');
|
||||||
|
if ($containerName) {
|
||||||
|
instant_remote_process(
|
||||||
|
["docker rm -f {$containerName}"],
|
||||||
|
$server
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ class PrepareCoolifyTask
|
|||||||
|
|
||||||
public function __invoke(): Activity
|
public function __invoke(): Activity
|
||||||
{
|
{
|
||||||
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors);
|
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish, call_event_data: $this->remoteProcessArgs->call_event_data);
|
||||||
dispatch($job);
|
dispatch($job);
|
||||||
$this->activity->refresh();
|
$this->activity->refresh();
|
||||||
return $this->activity;
|
return $this->activity;
|
||||||
|
|||||||
@@ -17,24 +17,26 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
public bool $hide_from_output;
|
public bool $hide_from_output;
|
||||||
|
|
||||||
public bool $is_finished;
|
|
||||||
|
|
||||||
public bool $ignore_errors;
|
public bool $ignore_errors;
|
||||||
|
|
||||||
|
public $call_event_on_finish = null;
|
||||||
|
|
||||||
|
public $call_event_data = null;
|
||||||
|
|
||||||
protected $time_start;
|
protected $time_start;
|
||||||
|
|
||||||
protected $current_time;
|
protected $current_time;
|
||||||
|
|
||||||
protected $last_write_at = 0;
|
protected $last_write_at = 0;
|
||||||
|
|
||||||
protected $throttle_interval_ms = 500;
|
protected $throttle_interval_ms = 200;
|
||||||
|
|
||||||
protected int $counter = 1;
|
protected int $counter = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*/
|
*/
|
||||||
public function __construct(Activity $activity, bool $hide_from_output = false, bool $is_finished = false, bool $ignore_errors = false)
|
public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null, $call_event_data = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) {
|
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) {
|
||||||
@@ -43,8 +45,9 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
$this->activity = $activity;
|
$this->activity = $activity;
|
||||||
$this->hide_from_output = $hide_from_output;
|
$this->hide_from_output = $hide_from_output;
|
||||||
$this->is_finished = $is_finished;
|
|
||||||
$this->ignore_errors = $ignore_errors;
|
$this->ignore_errors = $ignore_errors;
|
||||||
|
$this->call_event_on_finish = $call_event_on_finish;
|
||||||
|
$this->call_event_data = $call_event_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function decodeOutput(?Activity $activity = null): string
|
public static function decodeOutput(?Activity $activity = null): string
|
||||||
@@ -74,17 +77,29 @@ class RunRemoteProcess
|
|||||||
$this->time_start = hrtime(true);
|
$this->time_start = hrtime(true);
|
||||||
|
|
||||||
$status = ProcessStatus::IN_PROGRESS;
|
$status = ProcessStatus::IN_PROGRESS;
|
||||||
$processResult = Process::forever()->run($this->getCommand(), $this->handleOutput(...));
|
$timeout = config('constants.ssh.command_timeout');
|
||||||
|
$process = Process::timeout($timeout)->start($this->getCommand(), $this->handleOutput(...));
|
||||||
|
$this->activity->properties = $this->activity->properties->merge([
|
||||||
|
'process_id' => $process->id(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$processResult = $process->wait();
|
||||||
|
// $processResult = Process::timeout($timeout)->run($this->getCommand(), $this->handleOutput(...));
|
||||||
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
|
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
|
||||||
$status = ProcessStatus::ERROR;
|
$status = ProcessStatus::ERROR;
|
||||||
} else {
|
} else {
|
||||||
if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
|
if ($processResult->exitCode() == 0) {
|
||||||
$status = ProcessStatus::FINISHED;
|
$status = ProcessStatus::FINISHED;
|
||||||
}
|
}
|
||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
$status = ProcessStatus::ERROR;
|
$status = ProcessStatus::ERROR;
|
||||||
}
|
}
|
||||||
|
// if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
|
||||||
|
// $status = ProcessStatus::FINISHED;
|
||||||
|
// }
|
||||||
|
// if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
|
// $status = ProcessStatus::ERROR;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->activity->properties = $this->activity->properties->merge([
|
$this->activity->properties = $this->activity->properties->merge([
|
||||||
@@ -97,7 +112,21 @@ class RunRemoteProcess
|
|||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
||||||
}
|
}
|
||||||
|
if ($this->call_event_on_finish) {
|
||||||
|
try {
|
||||||
|
if ($this->call_event_data) {
|
||||||
|
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
||||||
|
"data" => $this->call_event_data,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
||||||
|
'userId' => $this->activity->causer_id,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
return $processResult;
|
return $processResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +146,6 @@ class RunRemoteProcess
|
|||||||
}
|
}
|
||||||
$this->current_time = $this->elapsedTime();
|
$this->current_time = $this->elapsedTime();
|
||||||
$this->activity->description = $this->encodeOutput($type, $output);
|
$this->activity->description = $this->encodeOutput($type, $output);
|
||||||
|
|
||||||
if ($this->isAfterLastThrottle()) {
|
if ($this->isAfterLastThrottle()) {
|
||||||
// Let's write to database.
|
// Let's write to database.
|
||||||
DB::transaction(function () {
|
DB::transaction(function () {
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class StartDatabaseProxy
|
|||||||
$proxyContainerName = "{$database->uuid}-proxy";
|
$proxyContainerName = "{$database->uuid}-proxy";
|
||||||
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
$databaseType = $database->databaseType();
|
$databaseType = $database->databaseType();
|
||||||
$network = data_get($database, 'service.destination.network');
|
// $connectPredefined = data_get($database, 'service.connect_to_docker_network');
|
||||||
|
$network = $database->service->uuid;
|
||||||
$server = data_get($database, 'service.destination.server');
|
$server = data_get($database, 'service.destination.server');
|
||||||
$proxyContainerName = "{$database->service->uuid}-proxy";
|
$proxyContainerName = "{$database->service->uuid}-proxy";
|
||||||
switch ($databaseType) {
|
switch ($databaseType) {
|
||||||
@@ -124,6 +125,7 @@ class StartDatabaseProxy
|
|||||||
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
||||||
$nginxconf_base64 = base64_encode($nginxconf);
|
$nginxconf_base64 = base64_encode($nginxconf);
|
||||||
$dockerfile_base64 = base64_encode($dockerfile);
|
$dockerfile_base64 = base64_encode($dockerfile);
|
||||||
|
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
|
||||||
instant_remote_process([
|
instant_remote_process([
|
||||||
"mkdir -p $configuration_dir",
|
"mkdir -p $configuration_dir",
|
||||||
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ class StartMariadb
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -69,6 +68,9 @@ class StartMariadb
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
@@ -104,8 +106,8 @@ class StartMariadb
|
|||||||
$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->name} started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -138,7 +140,7 @@ class StartMariadb
|
|||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ class StartMongodb
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -76,6 +75,9 @@ class StartMongodb
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
@@ -120,8 +122,8 @@ class StartMongodb
|
|||||||
$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->name} started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -154,7 +156,7 @@ class StartMongodb
|
|||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ class StartMysql
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -69,6 +68,9 @@ class StartMysql
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
@@ -104,8 +106,8 @@ class StartMysql
|
|||||||
$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->name} started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server,callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -138,7 +140,7 @@ class StartMysql
|
|||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
||||||
|
|||||||
@@ -50,12 +50,8 @@ class StartPostgresql
|
|||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => [
|
'test' => [
|
||||||
'CMD-SHELL',
|
"CMD-SHELL",
|
||||||
'pg_isready',
|
"psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1"
|
||||||
'-d',
|
|
||||||
$this->database->postgres_db,
|
|
||||||
'-U',
|
|
||||||
$this->database->postgres_user,
|
|
||||||
],
|
],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
@@ -66,8 +62,7 @@ class StartPostgresql
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -79,6 +74,9 @@ class StartPostgresql
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
ray('Log Drain Enabled');
|
ray('Log Drain Enabled');
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
@@ -130,8 +128,8 @@ class StartPostgresql
|
|||||||
$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->name} started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -166,7 +164,7 @@ class StartPostgresql
|
|||||||
ray('Generate Environment Variables')->green();
|
ray('Generate Environment Variables')->green();
|
||||||
ray($this->database->runtime_environment_variables)->green();
|
ray($this->database->runtime_environment_variables)->green();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_USER'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_USER'))->isEmpty()) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
@@ -65,8 +66,7 @@ class StartRedis
|
|||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (int) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpuset' => $this->database->limits_cpuset,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
@@ -78,6 +78,9 @@ class StartRedis
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
@@ -104,7 +107,7 @@ class StartRedis
|
|||||||
'target' => '/usr/local/etc/redis/redis.conf',
|
'target' => '/usr/local/etc/redis/redis.conf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
$docker_compose['services'][$container_name]['command'] = $startCommand . ' /usr/local/etc/redis/redis.conf';
|
$docker_compose['services'][$container_name]['command'] = "redis-server /usr/local/etc/redis/redis.conf --requirepass {$this->database->redis_password} --appendonly yes";
|
||||||
}
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
@@ -114,8 +117,8 @@ class StartRedis
|
|||||||
$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->name} started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
return remote_process($this->commands, $database->destination->server);
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -148,7 +151,7 @@ class StartRedis
|
|||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||||
@@ -163,8 +166,9 @@ class StartRedis
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'redis.conf';
|
$filename = 'redis.conf';
|
||||||
$content = $this->database->redis_conf;
|
Storage::disk('local')->put("tmp/redis.conf_{$this->database->uuid}", $this->database->redis_conf);
|
||||||
$content_base64 = base64_encode($content);
|
$path = Storage::path("tmp/redis.conf_{$this->database->uuid}");
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
instant_scp($path, "{$this->configuration_dir}/{$filename}", $this->database->destination->server);
|
||||||
|
Storage::disk('local')->delete("tmp/redis.conf_{$this->database->uuid}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Events\DatabaseStatusChanged;
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@@ -16,6 +17,9 @@ class StopDatabase
|
|||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
||||||
{
|
{
|
||||||
$server = $database->destination->server;
|
$server = $database->destination->server;
|
||||||
|
if (!$server->isFunctional()) {
|
||||||
|
return 'Server is not functional';
|
||||||
|
}
|
||||||
instant_remote_process(
|
instant_remote_process(
|
||||||
["docker rm -f {$database->uuid}"],
|
["docker rm -f {$database->uuid}"],
|
||||||
$server
|
$server
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ class StopDatabaseProxy
|
|||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
||||||
{
|
{
|
||||||
$server = data_get($database, 'destination.server');
|
$server = data_get($database, 'destination.server');
|
||||||
|
$uuid = $database->uuid;
|
||||||
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
|
$uuid = $database->service->uuid;
|
||||||
$server = data_get($database, 'service.server');
|
$server = data_get($database, 'service.server');
|
||||||
}
|
}
|
||||||
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $server);
|
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
|
||||||
$database->is_public = false;
|
$database->is_public = false;
|
||||||
$database->save();
|
$database->save();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ class CheckConfiguration
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Server $server, bool $reset = false)
|
public function handle(Server $server, bool $reset = false)
|
||||||
{
|
{
|
||||||
$proxy_path = get_proxy_path();
|
$proxyType = $server->proxyType();
|
||||||
|
if ($proxyType === 'NONE') {
|
||||||
|
return 'OK';
|
||||||
|
}
|
||||||
|
$proxy_path = $server->proxyPath();
|
||||||
|
|
||||||
$proxy_configuration = instant_remote_process([
|
$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",
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ class CheckProxy
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Server $server, $fromUI = false)
|
public function handle(Server $server, $fromUI = false)
|
||||||
{
|
{
|
||||||
|
if ($server->proxyType() === 'NONE') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!$server->isProxyShouldRun()) {
|
if (!$server->isProxyShouldRun()) {
|
||||||
if ($fromUI) {
|
if ($fromUI) {
|
||||||
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
||||||
@@ -17,6 +20,15 @@ class CheckProxy
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($server->isSwarm()) {
|
||||||
|
$status = getContainerStatus($server, 'coolify-proxy_traefik');
|
||||||
|
$server->proxy->set('status', $status);
|
||||||
|
$server->save();
|
||||||
|
if ($status === 'running') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} 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');
|
||||||
@@ -34,18 +46,19 @@ class CheckProxy
|
|||||||
$port443 = is_resource($connection443) && fclose($connection443);
|
$port443 = is_resource($connection443) && fclose($connection443);
|
||||||
if ($port80) {
|
if ($port80) {
|
||||||
if ($fromUI) {
|
if ($fromUI) {
|
||||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($port443) {
|
if ($port443) {
|
||||||
if ($fromUI) {
|
if ($fromUI) {
|
||||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class SaveConfiguration
|
|||||||
if (is_null($proxy_settings)) {
|
if (is_null($proxy_settings)) {
|
||||||
$proxy_settings = CheckConfiguration::run($server, true);
|
$proxy_settings = CheckConfiguration::run($server, true);
|
||||||
}
|
}
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = $server->proxyPath();
|
||||||
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
||||||
|
|
||||||
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
|
use App\Events\ProxyStarted;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
@@ -14,8 +15,11 @@ class StartProxy
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$proxyType = $server->proxyType();
|
$proxyType = $server->proxyType();
|
||||||
|
if (is_null($proxyType) || $proxyType === 'NONE') {
|
||||||
|
return 'OK';
|
||||||
|
}
|
||||||
$commands = collect([]);
|
$commands = collect([]);
|
||||||
$proxy_path = get_proxy_path();
|
$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");
|
||||||
@@ -24,8 +28,19 @@ class StartProxy
|
|||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
$server->save();
|
$server->save();
|
||||||
|
if ($server->isSwarm()) {
|
||||||
$commands = $commands->merge([
|
$commands = $commands->merge([
|
||||||
"mkdir -p $proxy_path && cd $proxy_path",
|
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
||||||
|
"echo 'Creating required Docker Compose file.'",
|
||||||
|
"echo 'Starting coolify-proxy.'",
|
||||||
|
"cd $proxy_path && docker stack deploy -c docker-compose.yml coolify-proxy",
|
||||||
|
"echo 'Proxy started successfully.'"
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$caddfile = "import /dynamic/*.caddy";
|
||||||
|
$commands = $commands->merge([
|
||||||
|
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
||||||
|
"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',
|
||||||
@@ -36,21 +51,22 @@ class StartProxy
|
|||||||
"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);
|
$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);
|
||||||
$server->proxy->set('status', 'running');
|
$server->proxy->set('status', 'running');
|
||||||
$server->proxy->set('type', $proxyType);
|
$server->proxy->set('type', $proxyType);
|
||||||
$server->save();
|
$server->save();
|
||||||
|
ProxyStarted::dispatch($server);
|
||||||
return 'OK';
|
return 'OK';
|
||||||
}
|
}
|
||||||
} catch(\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
app/Actions/Server/CleanupDocker.php
Normal file
23
app/Actions/Server/CleanupDocker.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Server;
|
||||||
|
|
||||||
|
class CleanupDocker
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Server $server, bool $force = true)
|
||||||
|
{
|
||||||
|
if ($force) {
|
||||||
|
instant_remote_process(['docker image prune -af'], $server, false);
|
||||||
|
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false);
|
||||||
|
instant_remote_process(['docker builder prune -af'], $server, false);
|
||||||
|
} else {
|
||||||
|
instant_remote_process(['docker image prune -f'], $server, false);
|
||||||
|
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false);
|
||||||
|
instant_remote_process(['docker builder prune -f'], $server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,9 +13,9 @@ class InstallDocker
|
|||||||
{
|
{
|
||||||
$supported_os_type = $server->validateOS();
|
$supported_os_type = $server->validateOS();
|
||||||
if (!$supported_os_type) {
|
if (!$supported_os_type) {
|
||||||
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/servers#install-docker-engine-manually">documentation</a>.');
|
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
|
||||||
}
|
}
|
||||||
ray('Installing Docker on server: ' . $server->name . ' (' . $server->ip . ')' . ' with OS: ' . $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",
|
||||||
@@ -43,25 +43,32 @@ class InstallDocker
|
|||||||
"echo 'Restarting Docker Engine...'",
|
"echo 'Restarting Docker Engine...'",
|
||||||
"ls -l /tmp"
|
"ls -l /tmp"
|
||||||
]);
|
]);
|
||||||
|
return remote_process($command, $server);
|
||||||
} else {
|
} else {
|
||||||
if ($supported_os_type === '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",
|
"command -v jq >/dev/null || apt-get update -y",
|
||||||
"command -v jq >/dev/null || apt install -y jq",
|
"command -v jq >/dev/null || apt install -y curl wget git jq",
|
||||||
|
|
||||||
]);
|
]);
|
||||||
} else if ($supported_os_type === 'rhel') {
|
} else if ($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 jq",
|
"command -v jq >/dev/null || dnf install -y curl wget git jq",
|
||||||
|
]);
|
||||||
|
} else if ($supported_os_type->contains('sles')) {
|
||||||
|
$command = $command->merge([
|
||||||
|
"echo 'Installing Prerequisites...'",
|
||||||
|
"command -v jq >/dev/null || zypper update -y",
|
||||||
|
"command -v jq >/dev/null || zypper install -y curl wget git jq",
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
throw new \Exception('Unsupported OS');
|
throw new \Exception('Unsupported OS');
|
||||||
}
|
}
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Docker Engine...'",
|
"echo 'Installing Docker Engine...'",
|
||||||
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh",
|
"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",
|
||||||
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
||||||
@@ -70,10 +77,19 @@ class InstallDocker
|
|||||||
"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",
|
||||||
"echo 'Creating default Docker network (coolify)...'",
|
|
||||||
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
|
||||||
"echo 'Done!'"
|
|
||||||
]);
|
]);
|
||||||
|
if ($server->isSwarm()) {
|
||||||
|
$command = $command->merge([
|
||||||
|
"docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true",
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$command = $command->merge([
|
||||||
|
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
||||||
|
]);
|
||||||
|
$command = $command->merge([
|
||||||
|
"echo 'Done!'",
|
||||||
|
]);
|
||||||
|
}
|
||||||
return remote_process($command, $server);
|
return remote_process($command, $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class InstallLogDrain
|
|||||||
$type = 'highlight';
|
$type = 'highlight';
|
||||||
} else if ($server->settings->is_logdrain_axiom_enabled) {
|
} else if ($server->settings->is_logdrain_axiom_enabled) {
|
||||||
$type = 'axiom';
|
$type = 'axiom';
|
||||||
|
} else if ($server->settings->is_logdrain_custom_enabled) {
|
||||||
|
$type = 'custom';
|
||||||
} else {
|
} else {
|
||||||
$type = 'none';
|
$type = 'none';
|
||||||
}
|
}
|
||||||
@@ -114,15 +116,23 @@ class InstallLogDrain
|
|||||||
json_date_format iso8601
|
json_date_format iso8601
|
||||||
tls On
|
tls On
|
||||||
");
|
");
|
||||||
|
} else if ($type === 'custom') {
|
||||||
|
if (!$server->settings->is_logdrain_custom_enabled) {
|
||||||
|
throw new \Exception('Custom log drain is not enabled.');
|
||||||
|
}
|
||||||
|
$config = base64_encode($server->settings->logdrain_custom_config);
|
||||||
|
$parsers = base64_encode($server->settings->logdrain_custom_config_parser);
|
||||||
} else {
|
} else {
|
||||||
throw new \Exception('Unknown log drain type.');
|
throw new \Exception('Unknown log drain type.');
|
||||||
}
|
}
|
||||||
|
if ($type !== 'custom') {
|
||||||
$parsers = base64_encode("
|
$parsers = base64_encode("
|
||||||
[PARSER]
|
[PARSER]
|
||||||
Name empty_line_skipper
|
Name empty_line_skipper
|
||||||
Format regex
|
Format regex
|
||||||
Regex /^(?!\s*$).+/
|
Regex /^(?!\s*$).+/
|
||||||
");
|
");
|
||||||
|
}
|
||||||
$compose = base64_encode("
|
$compose = base64_encode("
|
||||||
services:
|
services:
|
||||||
coolify-log-drain:
|
coolify-log-drain:
|
||||||
@@ -179,10 +189,16 @@ Files:
|
|||||||
"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') {
|
||||||
|
$add_envs_command = [
|
||||||
|
"touch $config_path/.env"
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
throw new \Exception('Unknown log drain type.');
|
||||||
}
|
}
|
||||||
$restart_command = [
|
$restart_command = [
|
||||||
"echo 'Stopping old Fluent Bit'",
|
"echo 'Stopping old Fluent Bit'",
|
||||||
"cd $config_path && docker rm -f coolify-log-drain || true",
|
"cd $config_path && docker compose down --remove-orphans || true",
|
||||||
"echo 'Starting Fluent Bit'",
|
"echo 'Starting Fluent Bit'",
|
||||||
"cd $config_path && docker compose up -d --remove-orphans",
|
"cd $config_path && docker compose up -d --remove-orphans",
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,19 +12,21 @@ class UpdateCoolify
|
|||||||
public ?Server $server = null;
|
public ?Server $server = null;
|
||||||
public ?string $latestVersion = null;
|
public ?string $latestVersion = null;
|
||||||
public ?string $currentVersion = null;
|
public ?string $currentVersion = null;
|
||||||
|
public bool $async = false;
|
||||||
|
|
||||||
public function handle(bool $force)
|
public function handle(bool $force = false, bool $async = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$this->async = $async;
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
ray('Running InstanceAutoUpdateJob');
|
ray('Running InstanceAutoUpdateJob');
|
||||||
$this->server = Server::find(0)->first();
|
$this->server = Server::find(0);
|
||||||
if (!$this->server) {
|
if (!$this->server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
CleanupDocker::run($this->server, false);
|
||||||
$this->latestVersion = get_latest_version_of_coolify();
|
$this->latestVersion = get_latest_version_of_coolify();
|
||||||
$this->currentVersion = config('version');
|
$this->currentVersion = config('version');
|
||||||
ray('latest version:' . $this->latestVersion . " current version: " . $this->currentVersion . ' force: ' . $force);
|
|
||||||
if ($settings->next_channel) {
|
if ($settings->next_channel) {
|
||||||
ray('next channel enabled');
|
ray('next channel enabled');
|
||||||
$this->latestVersion = 'next';
|
$this->latestVersion = 'next';
|
||||||
@@ -43,7 +45,7 @@ class UpdateCoolify
|
|||||||
}
|
}
|
||||||
$this->update();
|
$this->update();
|
||||||
}
|
}
|
||||||
send_internal_notification('InstanceAutoUpdateJob done to version: ' . $this->latestVersion . ' from version: ' . $this->currentVersion);
|
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray('InstanceAutoUpdateJob failed');
|
ray('InstanceAutoUpdateJob failed');
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
@@ -56,17 +58,31 @@ class UpdateCoolify
|
|||||||
{
|
{
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
ray("Running update on local docker container. Updating to $this->latestVersion");
|
ray("Running update on local docker container. Updating to $this->latestVersion");
|
||||||
|
if ($this->async) {
|
||||||
|
ray('Running async update');
|
||||||
remote_process([
|
remote_process([
|
||||||
"sleep 10"
|
"sleep 10"
|
||||||
], $this->server);
|
], $this->server);
|
||||||
|
} else {
|
||||||
|
instant_remote_process([
|
||||||
|
"sleep 10"
|
||||||
|
], $this->server);
|
||||||
|
}
|
||||||
ray('Update done');
|
ray('Update done');
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
ray('Running update on production server');
|
ray('Running update on production server');
|
||||||
|
if ($this->async) {
|
||||||
remote_process([
|
remote_process([
|
||||||
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
"curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||||
], $this->server);
|
], $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);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
app/Actions/Service/DeleteService.php
Normal file
54
app/Actions/Service/DeleteService.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Service;
|
||||||
|
|
||||||
|
class DeleteService
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Service $service)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$server = data_get($service, 'server');
|
||||||
|
if ($server->isFunctional()) {
|
||||||
|
$storagesToDelete = collect([]);
|
||||||
|
|
||||||
|
$service->environment_variables()->delete();
|
||||||
|
$commands = [];
|
||||||
|
foreach ($service->applications()->get() as $application) {
|
||||||
|
$storages = $application->persistentStorages()->get();
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
$storagesToDelete->push($storage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($service->databases()->get() as $database) {
|
||||||
|
$storages = $database->persistentStorages()->get();
|
||||||
|
foreach ($storages as $storage) {
|
||||||
|
$storagesToDelete->push($storage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($storagesToDelete as $storage) {
|
||||||
|
$commands[] = "docker volume rm -f $storage->name";
|
||||||
|
}
|
||||||
|
$commands[] = "docker rm -f $service->uuid";
|
||||||
|
|
||||||
|
instant_remote_process($commands, $server, false);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new \Exception($e->getMessage());
|
||||||
|
} finally {
|
||||||
|
foreach ($service->applications()->get() as $application) {
|
||||||
|
$application->forceDelete();
|
||||||
|
}
|
||||||
|
foreach ($service->databases()->get() as $database) {
|
||||||
|
$database->forceDelete();
|
||||||
|
}
|
||||||
|
foreach ($service->scheduled_tasks as $task) {
|
||||||
|
$task->delete();
|
||||||
|
}
|
||||||
|
$service->tags()->detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,24 +11,27 @@ class StartService
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
$network = $service->destination->network;
|
ray('Starting service: ' . $service->name);
|
||||||
$service->saveComposeConfigs();
|
$service->saveComposeConfigs();
|
||||||
$commands[] = "cd " . $service->workdir();
|
$commands[] = "cd " . $service->workdir();
|
||||||
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
||||||
$commands[] = "echo 'Creating Docker network.'";
|
$commands[] = "echo 'Creating Docker network.'";
|
||||||
$commands[] = "docker network create --attachable '{$service->uuid}' >/dev/null || true";
|
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid";
|
||||||
$commands[] = "echo 'Starting service {$service->name} on {$service->server->name}.'";
|
$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";
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate --build";
|
||||||
$commands[] = "docker network connect $service->uuid coolify-proxy || true";
|
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
|
||||||
$compose = data_get($service,'docker_compose',[]);
|
if (data_get($service, 'connect_to_docker_network')) {
|
||||||
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
|
$compose = data_get($service, 'docker_compose', []);
|
||||||
foreach($serviceNames as $serviceName => $serviceConfig){
|
$network = $service->destination->network;
|
||||||
|
$serviceNames = data_get(Yaml::parse($compose), 'services', []);
|
||||||
|
foreach ($serviceNames as $serviceName => $serviceConfig) {
|
||||||
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} || true";
|
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} || true";
|
||||||
}
|
}
|
||||||
$activity = remote_process($commands, $service->server);
|
}
|
||||||
|
$activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
|
||||||
return $activity;
|
return $activity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ class StopService
|
|||||||
use AsAction;
|
use AsAction;
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
|
$server = $service->destination->server;
|
||||||
|
if (!$server->isFunctional()) {
|
||||||
|
return 'Server is not functional';
|
||||||
|
}
|
||||||
|
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);
|
||||||
@@ -24,5 +30,11 @@ class StopService
|
|||||||
instant_remote_process(["docker network rm {$service->uuid} 2>/dev/null"], $service->server, false);
|
instant_remote_process(["docker network rm {$service->uuid} 2>/dev/null"], $service->server, false);
|
||||||
// TODO: make notification for databases
|
// TODO: make notification for databases
|
||||||
// $service->environment->project->team->notify(new StatusChanged($service));
|
// $service->environment->project->team->notify(new StatusChanged($service));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
echo $e->getMessage();
|
||||||
|
ray($e->getMessage());
|
||||||
|
return $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
55
app/Actions/Shared/ComplexStatusCheck.php
Normal file
55
app/Actions/Shared/ComplexStatusCheck.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Shared;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class ComplexStatusCheck
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Application $application)
|
||||||
|
{
|
||||||
|
$servers = $application->additional_servers;
|
||||||
|
$servers->push($application->destination->server);
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$is_main_server = $application->destination->server->id === $server->id;
|
||||||
|
if (!$server->isFunctional()) {
|
||||||
|
if ($is_main_server) {
|
||||||
|
$application->update(['status' => 'exited:unhealthy']);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$container = instant_remote_process(["docker container inspect $(docker container ls -q --filter 'label=coolify.applicationId={$application->id}' --filter 'label=coolify.pullRequestId=0') --format '{{json .}}'"], $server, false);
|
||||||
|
$container = format_docker_command_output_to_json($container);
|
||||||
|
if ($container->count() === 1) {
|
||||||
|
$container = $container->first();
|
||||||
|
$containerStatus = data_get($container, 'State.Status');
|
||||||
|
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||||
|
if ($is_main_server) {
|
||||||
|
$statusFromDb = $application->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$application->update(['status' => "$containerStatus:$containerHealth"]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$additional_server = $application->additional_servers()->wherePivot('server_id', $server->id);
|
||||||
|
$statusFromDb = $additional_server->first()->pivot->status;
|
||||||
|
if ($statusFromDb !== $containerStatus) {
|
||||||
|
$additional_server->updateExistingPivot($server->id, ['status' => "$containerStatus:$containerHealth"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($is_main_server) {
|
||||||
|
$application->update(['status' => 'exited:unhealthy']);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/Actions/Shared/PullImage.php
Normal file
25
app/Actions/Shared/PullImage.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Shared;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class PullImage
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Service $resource)
|
||||||
|
{
|
||||||
|
$resource->saveComposeConfigs();
|
||||||
|
|
||||||
|
$commands[] = "cd " . $resource->workdir();
|
||||||
|
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
|
||||||
|
$commands[] = "docker compose pull";
|
||||||
|
|
||||||
|
$server = data_get($resource, 'server');
|
||||||
|
|
||||||
|
if (!$server) return;
|
||||||
|
|
||||||
|
instant_remote_process($commands, $resource->server);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/Console/Commands/CleanupApplicationDeploymentQueue.php
Normal file
25
app/Console/Commands/CleanupApplicationDeploymentQueue.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class CleanupApplicationDeploymentQueue extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
|
||||||
|
protected $description = 'CleanupApplicationDeploymentQueue';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$team_id = $this->option('team-id');
|
||||||
|
$servers = \App\Models\Server::where('team_id', $team_id)->get();
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$deployments = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->where("server_id", $server->id)->get();
|
||||||
|
foreach ($deployments as $deployment) {
|
||||||
|
$deployment->update(['status' => 'failed']);
|
||||||
|
instant_remote_process(['docker rm -f ' . $deployment->deployment_uuid], $server, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
app/Console/Commands/CleanupDatabase.php
Normal file
63
app/Console/Commands/CleanupDatabase.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class CleanupDatabase extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:database {--yes}';
|
||||||
|
protected $description = 'Cleanup database';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
echo "Running database cleanup...\n";
|
||||||
|
} else {
|
||||||
|
echo "Running database cleanup in dry-run mode...\n";
|
||||||
|
}
|
||||||
|
$keep_days = 60;
|
||||||
|
echo "Keep days: $keep_days\n";
|
||||||
|
// Cleanup failed jobs table
|
||||||
|
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(1));
|
||||||
|
$count = $failed_jobs->count();
|
||||||
|
echo "Delete $count entries from failed_jobs.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$failed_jobs->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup sessions table
|
||||||
|
$sessions = DB::table('sessions')->where('last_activity', '<', now()->subDays($keep_days)->timestamp);
|
||||||
|
$count = $sessions->count();
|
||||||
|
echo "Delete $count entries from sessions.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$sessions->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup activity_log table
|
||||||
|
$activity_log = DB::table('activity_log')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10);
|
||||||
|
$count = $activity_log->count();
|
||||||
|
echo "Delete $count entries from activity_log.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$activity_log->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup application_deployment_queues table
|
||||||
|
$application_deployment_queues = DB::table('application_deployment_queues')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10);
|
||||||
|
$count = $application_deployment_queues->count();
|
||||||
|
echo "Delete $count entries from application_deployment_queues.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$application_deployment_queues->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup webhooks table
|
||||||
|
$webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days));
|
||||||
|
$count = $webhooks->count();
|
||||||
|
echo "Delete $count entries from webhooks.\n";
|
||||||
|
if ($this->option('yes')) {
|
||||||
|
$webhooks->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
23
app/Console/Commands/CleanupQueue.php
Normal file
23
app/Console/Commands/CleanupQueue.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
|
|
||||||
|
class CleanupQueue extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:queue';
|
||||||
|
protected $description = 'Cleanup Queue';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
echo "Running queue cleanup...\n";
|
||||||
|
$prefix = config('database.redis.options.prefix');
|
||||||
|
$keys = Redis::connection()->keys('*:laravel*');
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$keyWithoutPrefix = str_replace($prefix, '', $key);
|
||||||
|
Redis::connection()->del($keyWithoutPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
308
app/Console/Commands/CleanupStuckedResources.php
Normal file
308
app/Console/Commands/CleanupStuckedResources.php
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ScheduledTask;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use App\Models\StandaloneMariadb;
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use App\Models\StandaloneMysql;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class CleanupStuckedResources extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:stucked-resources';
|
||||||
|
protected $description = 'Cleanup Stucked Resources';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
ray('Running cleanup stucked resources.');
|
||||||
|
echo "Running cleanup stucked resources.\n";
|
||||||
|
$this->cleanup_stucked_resources();
|
||||||
|
}
|
||||||
|
private function cleanup_stucked_resources()
|
||||||
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
echo "Deleting stuck application: {$application->name}\n";
|
||||||
|
$application->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($postgresqls as $postgresql) {
|
||||||
|
echo "Deleting stuck postgresql: {$postgresql->name}\n";
|
||||||
|
$postgresql->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck postgresql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$redis = StandaloneRedis::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($redis as $redis) {
|
||||||
|
echo "Deleting stuck redis: {$redis->name}\n";
|
||||||
|
$redis->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($mongodbs as $mongodb) {
|
||||||
|
echo "Deleting stuck mongodb: {$mongodb->name}\n";
|
||||||
|
$mongodb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck mongodb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$mysqls = StandaloneMysql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($mysqls as $mysql) {
|
||||||
|
echo "Deleting stuck mysql: {$mysql->name}\n";
|
||||||
|
$mysql->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck mysql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$mariadbs = StandaloneMariadb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($mariadbs as $mariadb) {
|
||||||
|
echo "Deleting stuck mariadb: {$mariadb->name}\n";
|
||||||
|
$mariadb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck mariadb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$services = Service::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($services as $service) {
|
||||||
|
echo "Deleting stuck service: {$service->name}\n";
|
||||||
|
$service->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck service: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceApps = ServiceApplication::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($serviceApps as $serviceApp) {
|
||||||
|
echo "Deleting stuck serviceapp: {$serviceApp->name}\n";
|
||||||
|
$serviceApp->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck serviceapp: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceDbs = ServiceDatabase::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($serviceDbs as $serviceDb) {
|
||||||
|
echo "Deleting stuck serviceapp: {$serviceDb->name}\n";
|
||||||
|
$serviceDb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck serviceapp: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$scheduled_tasks = ScheduledTask::all();
|
||||||
|
foreach ($scheduled_tasks as $scheduled_task) {
|
||||||
|
if (!$scheduled_task->service && !$scheduled_task->application) {
|
||||||
|
echo "Deleting stuck scheduledtask: {$scheduled_task->name}\n";
|
||||||
|
$scheduled_task->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck scheduledtasks: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup any resources that are not attached to any environment or destination or server
|
||||||
|
try {
|
||||||
|
$applications = Application::all();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
if (!data_get($application, 'environment')) {
|
||||||
|
echo 'Application without environment: ' . $application->name . '\n';
|
||||||
|
$application->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$application->destination()) {
|
||||||
|
echo 'Application without destination: ' . $application->name . '\n';
|
||||||
|
$application->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($application, 'destination.server')) {
|
||||||
|
echo 'Application without server: ' . $application->name . '\n';
|
||||||
|
$application->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in application: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$postgresqls = StandalonePostgresql::all()->where('id', '!=', 0);
|
||||||
|
foreach ($postgresqls as $postgresql) {
|
||||||
|
if (!data_get($postgresql, 'environment')) {
|
||||||
|
echo 'Postgresql without environment: ' . $postgresql->name . '\n';
|
||||||
|
$postgresql->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$postgresql->destination()) {
|
||||||
|
echo 'Postgresql without destination: ' . $postgresql->name . '\n';
|
||||||
|
$postgresql->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($postgresql, 'destination.server')) {
|
||||||
|
echo 'Postgresql without server: ' . $postgresql->name . '\n';
|
||||||
|
$postgresql->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in postgresql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$redis = StandaloneRedis::all();
|
||||||
|
foreach ($redis as $redis) {
|
||||||
|
if (!data_get($redis, 'environment')) {
|
||||||
|
echo 'Redis without environment: ' . $redis->name . '\n';
|
||||||
|
$redis->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$redis->destination()) {
|
||||||
|
echo 'Redis without destination: ' . $redis->name . '\n';
|
||||||
|
$redis->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($redis, 'destination.server')) {
|
||||||
|
echo 'Redis without server: ' . $redis->name . '\n';
|
||||||
|
$redis->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in redis: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$mongodbs = StandaloneMongodb::all();
|
||||||
|
foreach ($mongodbs as $mongodb) {
|
||||||
|
if (!data_get($mongodb, 'environment')) {
|
||||||
|
echo 'Mongodb without environment: ' . $mongodb->name . '\n';
|
||||||
|
$mongodb->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$mongodb->destination()) {
|
||||||
|
echo 'Mongodb without destination: ' . $mongodb->name . '\n';
|
||||||
|
$mongodb->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($mongodb, 'destination.server')) {
|
||||||
|
echo 'Mongodb without server: ' . $mongodb->name . '\n';
|
||||||
|
$mongodb->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in mongodb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$mysqls = StandaloneMysql::all();
|
||||||
|
foreach ($mysqls as $mysql) {
|
||||||
|
if (!data_get($mysql, 'environment')) {
|
||||||
|
echo 'Mysql without environment: ' . $mysql->name . '\n';
|
||||||
|
$mysql->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$mysql->destination()) {
|
||||||
|
echo 'Mysql without destination: ' . $mysql->name . '\n';
|
||||||
|
$mysql->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($mysql, 'destination.server')) {
|
||||||
|
echo 'Mysql without server: ' . $mysql->name . '\n';
|
||||||
|
$mysql->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in mysql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$mariadbs = StandaloneMariadb::all();
|
||||||
|
foreach ($mariadbs as $mariadb) {
|
||||||
|
if (!data_get($mariadb, 'environment')) {
|
||||||
|
echo 'Mariadb without environment: ' . $mariadb->name . '\n';
|
||||||
|
$mariadb->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$mariadb->destination()) {
|
||||||
|
echo 'Mariadb without destination: ' . $mariadb->name . '\n';
|
||||||
|
$mariadb->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($mariadb, 'destination.server')) {
|
||||||
|
echo 'Mariadb without server: ' . $mariadb->name . '\n';
|
||||||
|
$mariadb->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in mariadb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$services = Service::all();
|
||||||
|
foreach ($services as $service) {
|
||||||
|
if (!data_get($service, 'environment')) {
|
||||||
|
echo 'Service without environment: ' . $service->name . '\n';
|
||||||
|
$service->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!$service->destination()) {
|
||||||
|
echo 'Service without destination: ' . $service->name . '\n';
|
||||||
|
$service->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!data_get($service, 'server')) {
|
||||||
|
echo 'Service without server: ' . $service->name . '\n';
|
||||||
|
$service->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in service: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceApplications = ServiceApplication::all();
|
||||||
|
foreach ($serviceApplications as $service) {
|
||||||
|
if (!data_get($service, 'service')) {
|
||||||
|
echo 'ServiceApplication without service: ' . $service->name . '\n';
|
||||||
|
$service->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in serviceApplications: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceDatabases = ServiceDatabase::all();
|
||||||
|
foreach ($serviceDatabases as $service) {
|
||||||
|
if (!data_get($service, 'service')) {
|
||||||
|
echo 'ServiceDatabase without service: ' . $service->name . '\n';
|
||||||
|
$service->forceDelete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in ServiceDatabases: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/Console/Commands/CleanupUnreachableServers.php
Normal file
27
app/Console/Commands/CleanupUnreachableServers.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class CleanupUnreachableServers extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'cleanup:unreachable-servers';
|
||||||
|
protected $description = 'Cleanup Unreachable Servers (7 days)';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
echo "Running unreachable server cleanup...\n";
|
||||||
|
$servers = Server::where('unreachable_count', 3)->where('unreachable_notification_sent', true)->where('updated_at', '<', now()->subDays(7))->get();
|
||||||
|
if ($servers->count() > 0) {
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
echo "Cleanup unreachable server ($server->id) with name $server->name";
|
||||||
|
send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up...");
|
||||||
|
$server->update([
|
||||||
|
'ip' => '1.2.3.4'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Console/Commands/Dev.php
Normal file
33
app/Console/Commands/Dev.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
|
||||||
|
class Dev extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'dev:init';
|
||||||
|
protected $description = 'Init the app in dev mode';
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
// Generate APP_KEY if not exists
|
||||||
|
if (empty(env('APP_KEY'))) {
|
||||||
|
echo "Generating APP_KEY.\n";
|
||||||
|
Artisan::call('key:generate');
|
||||||
|
}
|
||||||
|
// Seed database if it's empty
|
||||||
|
$settings = InstanceSettings::find(0);
|
||||||
|
if (!$settings) {
|
||||||
|
echo "Initializing instance, seeding database.\n";
|
||||||
|
Artisan::call('migrate --seed');
|
||||||
|
} else {
|
||||||
|
echo "Instance already initialized.\n";
|
||||||
|
}
|
||||||
|
// Set permissions
|
||||||
|
Process::run(['chmod', '-R', 'o+rwx', '.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,29 +2,28 @@
|
|||||||
|
|
||||||
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;
|
||||||
use App\Models\TeamInvitation;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Models\Waitlist;
|
use App\Models\Waitlist;
|
||||||
use App\Notifications\Application\DeploymentFailed;
|
use App\Notifications\Application\DeploymentFailed;
|
||||||
use App\Notifications\Application\DeploymentSuccess;
|
use App\Notifications\Application\DeploymentSuccess;
|
||||||
use App\Notifications\Application\StatusChanged;
|
use App\Notifications\Application\StatusChanged;
|
||||||
use App\Notifications\Database\BackupFailed;
|
use App\Notifications\Database\BackupFailed;
|
||||||
use App\Notifications\Database\BackupSuccess;
|
use App\Notifications\Database\BackupSuccess;
|
||||||
|
use App\Notifications\Database\DailyBackup;
|
||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
use App\Notifications\TransactionalEmails\InvitationLink;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Mail\Message;
|
use Illuminate\Mail\Message;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Mail;
|
use Mail;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
use function Laravel\Prompts\confirm;
|
use function Laravel\Prompts\confirm;
|
||||||
use function Laravel\Prompts\select;
|
use function Laravel\Prompts\select;
|
||||||
@@ -58,6 +57,8 @@ class Emails extends Command
|
|||||||
options: [
|
options: [
|
||||||
'updates' => 'Send Update Email to all users',
|
'updates' => 'Send Update Email to all users',
|
||||||
'emails-test' => 'Test',
|
'emails-test' => 'Test',
|
||||||
|
'database-backup-statuses-daily' => 'Database - Backup Statuses (Daily)',
|
||||||
|
'application-deployment-success-daily' => 'Application - Deployment Success (Daily)',
|
||||||
'application-deployment-success' => 'Application - Deployment Success',
|
'application-deployment-success' => 'Application - Deployment Success',
|
||||||
'application-deployment-failed' => 'Application - Deployment Failed',
|
'application-deployment-failed' => 'Application - Deployment Failed',
|
||||||
'application-status-changed' => 'Application - Status Changed',
|
'application-status-changed' => 'Application - Status Changed',
|
||||||
@@ -71,8 +72,12 @@ class Emails extends Command
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
||||||
|
if (isDev()) {
|
||||||
|
$this->email = "test@example.com";
|
||||||
|
} 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();
|
||||||
|
|
||||||
@@ -106,7 +111,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,6 +120,35 @@ class Emails extends Command
|
|||||||
$this->mail = (new Test())->toMail();
|
$this->mail = (new Test())->toMail();
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
break;
|
break;
|
||||||
|
case 'database-backup-statuses-daily':
|
||||||
|
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||||
|
$databases = collect();
|
||||||
|
foreach ($scheduled_backups as $scheduled_backup) {
|
||||||
|
$last_days_backups = $scheduled_backup->get_last_days_backup_status();
|
||||||
|
if ($last_days_backups->isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$failed = $last_days_backups->where('status', 'failed');
|
||||||
|
$database = $scheduled_backup->database;
|
||||||
|
$databases->put($database->name, [
|
||||||
|
'failed_count' => $failed->count(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->mail = (new DailyBackup($databases))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
break;
|
||||||
|
case 'application-deployment-success-daily':
|
||||||
|
$applications = Application::all();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$deployments = $application->get_last_days_deployments();
|
||||||
|
ray($deployments);
|
||||||
|
if ($deployments->isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'application-deployment-success':
|
case 'application-deployment-success':
|
||||||
$application = Application::all()->first();
|
$application = Application::all()->first();
|
||||||
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
||||||
|
|||||||
21
app/Console/Commands/Horizon.php
Normal file
21
app/Console/Commands/Horizon.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class Horizon extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'start:horizon';
|
||||||
|
protected $description = 'Start Horizon';
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
if (config('coolify.is_horizon_enabled')) {
|
||||||
|
$this->info('Horizon is enabled. Starting.');
|
||||||
|
$this->call('horizon');
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,46 +4,88 @@ namespace App\Console\Commands;
|
|||||||
|
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Jobs\CleanupHelperContainersJob;
|
use App\Jobs\CleanupHelperContainersJob;
|
||||||
use App\Models\Application;
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
|
||||||
use App\Models\ServiceApplication;
|
|
||||||
use App\Models\ServiceDatabase;
|
|
||||||
use App\Models\StandaloneMariadb;
|
|
||||||
use App\Models\StandaloneMongodb;
|
|
||||||
use App\Models\StandaloneMysql;
|
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\StandaloneRedis;
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
|
|
||||||
class Init extends Command
|
class Init extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'app:init {--cleanup}';
|
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();
|
||||||
$cleanup = $this->option('cleanup');
|
$full_cleanup = $this->option('full-cleanup');
|
||||||
if ($cleanup) {
|
$cleanup_deployments = $this->option('cleanup-deployments');
|
||||||
$this->cleanup_stucked_resources();
|
if ($cleanup_deployments) {
|
||||||
$this->cleanup_ssh();
|
echo "Running cleanup deployments.\n";
|
||||||
|
$this->cleanup_in_progress_application_deployments();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if ($full_cleanup) {
|
||||||
|
// Required for falsely deleted coolify db
|
||||||
|
$this->restore_coolify_db_backup();
|
||||||
$this->cleanup_in_progress_application_deployments();
|
$this->cleanup_in_progress_application_deployments();
|
||||||
$this->cleanup_stucked_helper_containers();
|
$this->cleanup_stucked_helper_containers();
|
||||||
|
$this->call('cleanup:queue');
|
||||||
|
$this->call('cleanup:stucked-resources');
|
||||||
|
try {
|
||||||
|
$server = Server::find(0)->first();
|
||||||
|
$server->setupDynamicProxyConfiguration();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
private function cleanup_stucked_helper_containers() {
|
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if (!is_null(env('AUTOUPDATE', null))) {
|
||||||
|
if (env('AUTOUPDATE') == true) {
|
||||||
|
$settings->update(['is_auto_update_enabled' => true]);
|
||||||
|
} else {
|
||||||
|
$settings->update(['is_auto_update_enabled' => false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->cleanup_stucked_helper_containers();
|
||||||
|
$this->call('cleanup:stucked-resources');
|
||||||
|
}
|
||||||
|
private function restore_coolify_db_backup()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$database = StandalonePostgresql::withTrashed()->find(0);
|
||||||
|
if ($database && $database->trashed()) {
|
||||||
|
echo "Restoring coolify db backup\n";
|
||||||
|
$database->restore();
|
||||||
|
$scheduledBackup = ScheduledDatabaseBackup::find(0);
|
||||||
|
if (!$scheduledBackup) {
|
||||||
|
ScheduledDatabaseBackup::create([
|
||||||
|
'id' => 0,
|
||||||
|
'enabled' => true,
|
||||||
|
'save_s3' => false,
|
||||||
|
'frequency' => '0 0 * * *',
|
||||||
|
'database_id' => $database->id,
|
||||||
|
'database_type' => 'App\Models\StandalonePostgresql',
|
||||||
|
'team_id' => 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function cleanup_stucked_helper_containers()
|
||||||
|
{
|
||||||
$servers = Server::all();
|
$servers = Server::all();
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
if ($server->isFunctional()) {
|
if ($server->isFunctional()) {
|
||||||
CleanupHelperContainersJob::dispatch($server);
|
CleanupHelperContainersJob::dispatch($server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
private function alive()
|
private function alive()
|
||||||
{
|
{
|
||||||
@@ -56,34 +98,41 @@ class Init extends Command
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Http::get("https://get.coollabs.io/coolify/v4/alive?appId=$id&version=$version");
|
Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version");
|
||||||
echo "I am alive!\n";
|
echo "I am alive!\n";
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in alive: {$e->getMessage()}\n";
|
echo "Error in alive: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function cleanup_ssh()
|
// private function cleanup_ssh()
|
||||||
{
|
// {
|
||||||
try {
|
|
||||||
$files = Storage::allFiles('ssh/keys');
|
// TODO: it will cleanup id.root@host.docker.internal
|
||||||
foreach ($files as $file) {
|
// try {
|
||||||
Storage::delete($file);
|
// $files = Storage::allFiles('ssh/keys');
|
||||||
}
|
// foreach ($files as $file) {
|
||||||
$files = Storage::allFiles('ssh/mux');
|
// Storage::delete($file);
|
||||||
foreach ($files as $file) {
|
// }
|
||||||
Storage::delete($file);
|
// $files = Storage::allFiles('ssh/mux');
|
||||||
}
|
// foreach ($files as $file) {
|
||||||
} catch (\Throwable $e) {
|
// Storage::delete($file);
|
||||||
echo "Error in cleaning ssh: {$e->getMessage()}\n";
|
// }
|
||||||
}
|
// } catch (\Throwable $e) {
|
||||||
}
|
// echo "Error in cleaning ssh: {$e->getMessage()}\n";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
private function cleanup_in_progress_application_deployments()
|
private function cleanup_in_progress_application_deployments()
|
||||||
{
|
{
|
||||||
// Cleanup any failed deployments
|
// Cleanup any failed deployments
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$halted_deployments = ApplicationDeploymentQueue::where('status', '==', 'in_progress')->get();
|
if (isCloud()) {
|
||||||
foreach ($halted_deployments as $deployment) {
|
return;
|
||||||
|
}
|
||||||
|
$queued_inprogress_deployments = ApplicationDeploymentQueue::whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])->get();
|
||||||
|
foreach ($queued_inprogress_deployments as $deployment) {
|
||||||
|
ray($deployment->id, $deployment->status);
|
||||||
|
echo "Cleaning up deployment: {$deployment->id}\n";
|
||||||
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
||||||
$deployment->save();
|
$deployment->save();
|
||||||
}
|
}
|
||||||
@@ -91,167 +140,4 @@ class Init extends Command
|
|||||||
echo "Error: {$e->getMessage()}\n";
|
echo "Error: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function cleanup_stucked_resources()
|
|
||||||
{
|
|
||||||
// Cleanup any resources that are not attached to any environment or destination or server
|
|
||||||
try {
|
|
||||||
$applications = Application::all();
|
|
||||||
foreach ($applications as $application) {
|
|
||||||
if (!data_get($application, 'environment')) {
|
|
||||||
ray('Application without environment', $application->name);
|
|
||||||
$application->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($application, 'destination.server')) {
|
|
||||||
ray('Application without server', $application->name);
|
|
||||||
$application->delete();
|
|
||||||
}
|
|
||||||
if (!$application->destination()) {
|
|
||||||
ray('Application without destination', $application->name);
|
|
||||||
$application->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in application: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$postgresqls = StandalonePostgresql::all();
|
|
||||||
foreach ($postgresqls as $postgresql) {
|
|
||||||
if (!data_get($postgresql, 'environment')) {
|
|
||||||
ray('Postgresql without environment', $postgresql->name);
|
|
||||||
$postgresql->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($postgresql, 'destination.server')) {
|
|
||||||
ray('Postgresql without server', $postgresql->name);
|
|
||||||
$postgresql->delete();
|
|
||||||
}
|
|
||||||
if (!$postgresql->destination()) {
|
|
||||||
ray('Postgresql without destination', $postgresql->name);
|
|
||||||
$postgresql->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in postgresql: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$redis = StandaloneRedis::all();
|
|
||||||
foreach ($redis as $redis) {
|
|
||||||
if (!data_get($redis, 'environment')) {
|
|
||||||
ray('Redis without environment', $redis->name);
|
|
||||||
$redis->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($redis, 'destination.server')) {
|
|
||||||
ray('Redis without server', $redis->name);
|
|
||||||
$redis->delete();
|
|
||||||
}
|
|
||||||
if (!$redis->destination()) {
|
|
||||||
ray('Redis without destination', $redis->name);
|
|
||||||
$redis->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in redis: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$mongodbs = StandaloneMongodb::all();
|
|
||||||
foreach ($mongodbs as $mongodb) {
|
|
||||||
if (!data_get($mongodb, 'environment')) {
|
|
||||||
ray('Mongodb without environment', $mongodb->name);
|
|
||||||
$mongodb->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($mongodb, 'destination.server')) {
|
|
||||||
ray('Mongodb without server', $mongodb->name);
|
|
||||||
$mongodb->delete();
|
|
||||||
}
|
|
||||||
if (!$mongodb->destination()) {
|
|
||||||
ray('Mongodb without destination', $mongodb->name);
|
|
||||||
$mongodb->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in mongodb: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$mysqls = StandaloneMysql::all();
|
|
||||||
foreach ($mysqls as $mysql) {
|
|
||||||
if (!data_get($mysql, 'environment')) {
|
|
||||||
ray('Mysql without environment', $mysql->name);
|
|
||||||
$mysql->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($mysql, 'destination.server')) {
|
|
||||||
ray('Mysql without server', $mysql->name);
|
|
||||||
$mysql->delete();
|
|
||||||
}
|
|
||||||
if (!$mysql->destination()) {
|
|
||||||
ray('Mysql without destination', $mysql->name);
|
|
||||||
$mysql->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in mysql: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$mariadbs = StandaloneMariadb::all();
|
|
||||||
foreach ($mariadbs as $mariadb) {
|
|
||||||
if (!data_get($mariadb, 'environment')) {
|
|
||||||
ray('Mariadb without environment', $mariadb->name);
|
|
||||||
$mariadb->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($mariadb, 'destination.server')) {
|
|
||||||
ray('Mariadb without server', $mariadb->name);
|
|
||||||
$mariadb->delete();
|
|
||||||
}
|
|
||||||
if (!$mariadb->destination()) {
|
|
||||||
ray('Mariadb without destination', $mariadb->name);
|
|
||||||
$mariadb->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in mariadb: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$services = Service::all();
|
|
||||||
foreach ($services as $service) {
|
|
||||||
if (!data_get($service, 'environment')) {
|
|
||||||
ray('Service without environment', $service->name);
|
|
||||||
$service->delete();
|
|
||||||
}
|
|
||||||
if (!data_get($service, 'server')) {
|
|
||||||
ray('Service without server', $service->name);
|
|
||||||
$service->delete();
|
|
||||||
}
|
|
||||||
if (!$service->destination()) {
|
|
||||||
ray('Service without destination', $service->name);
|
|
||||||
$service->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in service: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$serviceApplications = ServiceApplication::all();
|
|
||||||
foreach ($serviceApplications as $service) {
|
|
||||||
if (!data_get($service, 'service')) {
|
|
||||||
ray('ServiceApplication without service', $service->name);
|
|
||||||
$service->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in serviceApplications: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$serviceDatabases = ServiceDatabase::all();
|
|
||||||
foreach ($serviceDatabases as $service) {
|
|
||||||
if (!data_get($service, 'service')) {
|
|
||||||
ray('ServiceDatabase without service', $service->name);
|
|
||||||
$service->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in ServiceDatabases: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
41
app/Console/Commands/RootChangeEmail.php
Normal file
41
app/Console/Commands/RootChangeEmail.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class RootChangeEmail extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'root:change-email';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Change Root Email';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
$this->info('You are about to change the root user\'s email.');
|
||||||
|
$email = $this->ask('Give me a new email for root user');
|
||||||
|
$this->info('Updating root email...');
|
||||||
|
try {
|
||||||
|
User::find(0)->update(['email' => $email]);
|
||||||
|
$this->info('Root user\'s email updated successfully.');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('Failed to update root user\'s email.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,14 +8,14 @@ use Illuminate\Support\Facades\Hash;
|
|||||||
|
|
||||||
use function Laravel\Prompts\password;
|
use function Laravel\Prompts\password;
|
||||||
|
|
||||||
class UsersResetRoot extends Command
|
class RootResetPassword extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'users:reset-root';
|
protected $signature = 'root:reset-password';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
21
app/Console/Commands/Scheduler.php
Normal file
21
app/Console/Commands/Scheduler.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class Scheduler extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'start:scheduler';
|
||||||
|
protected $description = 'Start Scheduler';
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
if (config('coolify.is_scheduler_enabled')) {
|
||||||
|
$this->info('Scheduler is enabled. Starting.');
|
||||||
|
$this->call('schedule:work');
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\DeleteResourceJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
@@ -12,21 +13,21 @@ use function Laravel\Prompts\confirm;
|
|||||||
use function Laravel\Prompts\multiselect;
|
use function Laravel\Prompts\multiselect;
|
||||||
use function Laravel\Prompts\select;
|
use function Laravel\Prompts\select;
|
||||||
|
|
||||||
class ResourcesDelete extends Command
|
class ServicesDelete extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'resources:delete';
|
protected $signature = 'services:delete';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $description = 'Delete a resource from the database';
|
protected $description = 'Delete a service from the database';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
@@ -34,7 +35,7 @@ class ResourcesDelete extends Command
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$resource = select(
|
$resource = select(
|
||||||
'What resource do you want to delete?',
|
'What service do you want to delete?',
|
||||||
['Application', 'Database', 'Service', 'Server'],
|
['Application', 'Database', 'Service', 'Server'],
|
||||||
);
|
);
|
||||||
if ($resource === 'Application') {
|
if ($resource === 'Application') {
|
||||||
@@ -91,7 +92,7 @@ class ResourcesDelete extends Command
|
|||||||
if (!$confirmed) {
|
if (!$confirmed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +116,7 @@ class ResourcesDelete extends Command
|
|||||||
if (!$confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +140,7 @@ class ResourcesDelete extends Command
|
|||||||
if (!$confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ class ServicesGenerate extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
ray()->clearAll();
|
// ray()->clearAll();
|
||||||
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
||||||
$files = array_filter($files, function ($file) {
|
$files = array_filter($files, function ($file) {
|
||||||
return strpos($file, '.yaml') !== false;
|
return strpos($file, '.yaml') !== false;
|
||||||
@@ -73,6 +73,18 @@ class ServicesGenerate extends Command
|
|||||||
} else {
|
} else {
|
||||||
$slogan = str($file)->headline()->value();
|
$slogan = str($file)->headline()->value();
|
||||||
}
|
}
|
||||||
|
$logo = collect(preg_grep('/^# logo:/', explode("\n", $content)))->values();
|
||||||
|
if ($logo->count() > 0) {
|
||||||
|
$logo = str($logo[0])->after('# logo:')->trim()->value();
|
||||||
|
} else {
|
||||||
|
$logo = 'svgs/unknown.svg';
|
||||||
|
}
|
||||||
|
$minversion = collect(preg_grep('/^# minversion:/', explode("\n", $content)))->values();
|
||||||
|
if ($minversion->count() > 0) {
|
||||||
|
$minversion = str($minversion[0])->after('# minversion:')->trim()->value();
|
||||||
|
} else {
|
||||||
|
$minversion = '0.0.0';
|
||||||
|
}
|
||||||
$env_file = collect(preg_grep('/^# env_file:/', explode("\n", $content)))->values();
|
$env_file = collect(preg_grep('/^# env_file:/', explode("\n", $content)))->values();
|
||||||
if ($env_file->count() > 0) {
|
if ($env_file->count() > 0) {
|
||||||
$env_file = str($env_file[0])->after('# env_file:')->trim()->value();
|
$env_file = str($env_file[0])->after('# env_file:')->trim()->value();
|
||||||
@@ -88,6 +100,12 @@ class ServicesGenerate extends Command
|
|||||||
} else {
|
} else {
|
||||||
$tags = null;
|
$tags = null;
|
||||||
}
|
}
|
||||||
|
$port = collect(preg_grep('/^# port:/', explode("\n", $content)))->values();
|
||||||
|
if ($port->count() > 0) {
|
||||||
|
$port = str($port[0])->after('# port:')->trim()->value();
|
||||||
|
} else {
|
||||||
|
$port = null;
|
||||||
|
}
|
||||||
$json = Yaml::parse($content);
|
$json = Yaml::parse($content);
|
||||||
$yaml = base64_encode(Yaml::dump($json, 10, 2));
|
$yaml = base64_encode(Yaml::dump($json, 10, 2));
|
||||||
$payload = [
|
$payload = [
|
||||||
@@ -96,7 +114,12 @@ class ServicesGenerate extends Command
|
|||||||
'slogan' => $slogan,
|
'slogan' => $slogan,
|
||||||
'compose' => $yaml,
|
'compose' => $yaml,
|
||||||
'tags' => $tags,
|
'tags' => $tags,
|
||||||
|
'logo' => $logo,
|
||||||
|
'minversion' => $minversion,
|
||||||
];
|
];
|
||||||
|
if ($port) {
|
||||||
|
$payload['port'] = $port;
|
||||||
|
}
|
||||||
if ($env_file) {
|
if ($env_file) {
|
||||||
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
|
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
|
||||||
$env_file_base64 = base64_encode($env_file_content);
|
$env_file_base64 = base64_encode($env_file_content);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class SyncBunny extends Command
|
|||||||
|
|
||||||
$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',
|
||||||
@@ -71,19 +71,31 @@ class SyncBunny extends Command
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
$confirmed = confirm('Are you sure you want to sync?');
|
if (!$only_template && !$only_version) {
|
||||||
|
$this->info('About to sync files (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.');
|
||||||
|
}
|
||||||
|
if ($only_template) {
|
||||||
|
$this->info('About to sync service-templates.json to BunnyCDN.');
|
||||||
|
$confirmed = confirm("Are you sure you want to sync?");
|
||||||
if (!$confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($only_template) {
|
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->storage(fileName: "$parent_dir/templates/$service_template")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$service_template"),
|
$pool->storage(fileName: "$parent_dir/templates/$service_template")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$service_template"),
|
||||||
$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) {
|
||||||
|
$this->info('About to sync versions.json to BunnyCDN.');
|
||||||
|
$file = file_get_contents("$parent_dir/$versions");
|
||||||
|
$json = json_decode($file, true);
|
||||||
|
$actual_version = data_get($json, 'coolify.v4.version');
|
||||||
|
|
||||||
|
$confirmed = confirm("Are you sure you want to sync to {$actual_version}?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if ($only_version) {
|
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->storage(fileName: "$parent_dir/$versions")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"),
|
$pool->storage(fileName: "$parent_dir/$versions")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
||||||
@@ -92,6 +104,7 @@ class SyncBunny extends Command
|
|||||||
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"),
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ namespace App\Console;
|
|||||||
use App\Jobs\CheckLogDrainContainerJob;
|
use App\Jobs\CheckLogDrainContainerJob;
|
||||||
use App\Jobs\CleanupInstanceStuffsJob;
|
use App\Jobs\CleanupInstanceStuffsJob;
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
|
use App\Jobs\ScheduledTaskJob;
|
||||||
use App\Jobs\InstanceAutoUpdateJob;
|
use App\Jobs\InstanceAutoUpdateJob;
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Jobs\PullHelperImageJob;
|
use App\Jobs\PullHelperImageJob;
|
||||||
use App\Jobs\ServerStatusJob;
|
use App\Jobs\ServerStatusJob;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
|
use App\Models\ScheduledTask;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
@@ -30,9 +32,12 @@ class Kernel extends ConsoleKernel
|
|||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->pull_helper_image($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
|
$this->check_scheduled_tasks($schedule);
|
||||||
} else {
|
} else {
|
||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
|
$schedule->command('cleanup:unreachable-servers')->daily();
|
||||||
|
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||||
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
|
|
||||||
@@ -41,6 +46,9 @@ class Kernel extends ConsoleKernel
|
|||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->pull_helper_image($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
|
$this->check_scheduled_tasks($schedule);
|
||||||
|
|
||||||
|
$schedule->command('cleanup:database --yes')->daily();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function pull_helper_image($schedule)
|
private function pull_helper_image($schedule)
|
||||||
@@ -56,15 +64,19 @@ class Kernel extends ConsoleKernel
|
|||||||
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
||||||
$own = Team::find(0)->servers;
|
$own = Team::find(0)->servers;
|
||||||
$servers = $servers->merge($own);
|
$servers = $servers->merge($own);
|
||||||
|
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||||
} else {
|
} else {
|
||||||
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
||||||
|
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||||
|
}
|
||||||
|
foreach ($containerServers as $server) {
|
||||||
|
$schedule->job(new ContainerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
||||||
|
if ($server->isLogDrainEnabled()) {
|
||||||
|
$schedule->job(new CheckLogDrainContainerJob($server))->everyTwoMinutes()->onOneServer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ServerStatusJob($server))->everyTenMinutes()->onOneServer();
|
$schedule->job(new ServerStatusJob($server))->everyTwoMinutes()->onOneServer();
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
|
||||||
if ($server->isLogDrainEnabled()) {
|
|
||||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function instance_auto_update($schedule)
|
private function instance_auto_update($schedule)
|
||||||
@@ -81,7 +93,6 @@ class Kernel extends ConsoleKernel
|
|||||||
{
|
{
|
||||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||||
if ($scheduled_backups->isEmpty()) {
|
if ($scheduled_backups->isEmpty()) {
|
||||||
ray('no scheduled backups');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach ($scheduled_backups as $scheduled_backup) {
|
foreach ($scheduled_backups as $scheduled_backup) {
|
||||||
@@ -103,6 +114,31 @@ class Kernel extends ConsoleKernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function check_scheduled_tasks($schedule)
|
||||||
|
{
|
||||||
|
$scheduled_tasks = ScheduledTask::all();
|
||||||
|
if ($scheduled_tasks->isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($scheduled_tasks as $scheduled_task) {
|
||||||
|
$service = $scheduled_task->service;
|
||||||
|
$application = $scheduled_task->application;
|
||||||
|
|
||||||
|
if (!$application && !$service) {
|
||||||
|
ray('application/service attached to scheduled task does not exist');
|
||||||
|
$scheduled_task->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
|
||||||
|
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
|
||||||
|
}
|
||||||
|
$schedule->job(new ScheduledTaskJob(
|
||||||
|
task: $scheduled_task
|
||||||
|
))->cron($scheduled_task->frequency)->onOneServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function commands(): void
|
protected function commands(): void
|
||||||
{
|
{
|
||||||
$this->load(__DIR__ . '/Commands');
|
$this->load(__DIR__ . '/Commands');
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ class CoolifyTaskArgs extends Data
|
|||||||
public string $command,
|
public string $command,
|
||||||
public string $type,
|
public string $type,
|
||||||
public ?string $type_uuid = null,
|
public ?string $type_uuid = null,
|
||||||
|
public ?int $process_id = null,
|
||||||
public ?Model $model = null,
|
public ?Model $model = null,
|
||||||
public ?string $status = null ,
|
public ?string $status = null ,
|
||||||
public bool $ignore_errors = false,
|
public bool $ignore_errors = false,
|
||||||
|
public $call_event_on_finish = null,
|
||||||
|
public $call_event_data = null
|
||||||
) {
|
) {
|
||||||
if(is_null($status)){
|
if(is_null($status)){
|
||||||
$this->status = ProcessStatus::QUEUED->value;
|
$this->status = ProcessStatus::QUEUED->value;
|
||||||
|
|||||||
@@ -8,5 +8,7 @@ enum ProcessStatus: string
|
|||||||
case IN_PROGRESS = 'in_progress';
|
case IN_PROGRESS = 'in_progress';
|
||||||
case FINISHED = 'finished';
|
case FINISHED = 'finished';
|
||||||
case ERROR = 'error';
|
case ERROR = 'error';
|
||||||
|
case KILLED = 'killed';
|
||||||
case CANCELLED = 'cancelled';
|
case CANCELLED = 'cancelled';
|
||||||
|
case CLOSED = 'closed';
|
||||||
}
|
}
|
||||||
|
|||||||
34
app/Events/ApplicationStatusChanged.php
Normal file
34
app/Events/ApplicationStatusChanged.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ApplicationStatusChanged implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $teamId;
|
||||||
|
public function __construct($teamId = null)
|
||||||
|
{
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
throw new \Exception("Team id is null");
|
||||||
|
}
|
||||||
|
$this->teamId = $teamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/BackupCreated.php
Normal file
34
app/Events/BackupCreated.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class BackupCreated implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $teamId;
|
||||||
|
public function __construct($teamId = null)
|
||||||
|
{
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
throw new \Exception("Team id is null");
|
||||||
|
}
|
||||||
|
$this->teamId = $teamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/DatabaseStatusChanged.php
Normal file
34
app/Events/DatabaseStatusChanged.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class DatabaseStatusChanged implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $userId;
|
||||||
|
public function __construct($userId = null)
|
||||||
|
{
|
||||||
|
if (is_null($userId)) {
|
||||||
|
$userId = auth()->user()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($userId)) {
|
||||||
|
throw new \Exception("User id is null");
|
||||||
|
}
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("user.{$this->userId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Events/ProxyStarted.php
Normal file
16
app/Events/ProxyStarted.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ProxyStarted
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public function __construct(public $data)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/ProxyStatusChanged.php
Normal file
34
app/Events/ProxyStatusChanged.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ProxyStatusChanged implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $teamId;
|
||||||
|
public function __construct($teamId = null)
|
||||||
|
{
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
throw new \Exception("Team id is null");
|
||||||
|
}
|
||||||
|
$this->teamId = $teamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Events/ServiceStatusChanged.php
Normal file
34
app/Events/ServiceStatusChanged.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class ServiceStatusChanged implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $userId;
|
||||||
|
public function __construct($userId = null)
|
||||||
|
{
|
||||||
|
if (is_null($userId)) {
|
||||||
|
$userId = auth()->user()->id ?? null;
|
||||||
|
}
|
||||||
|
if (is_null($userId)) {
|
||||||
|
throw new \Exception("User id is null");
|
||||||
|
}
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("user.{$this->userId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/Events/TestEvent.php
Normal file
28
app/Events/TestEvent.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class TestEvent implements ShouldBroadcast
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $teamId;
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->teamId = auth()->user()->currentTeam()->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,9 @@ class Handler extends ExceptionHandler
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
if (str($e->getMessage())->contains('No space left on device')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ray('reporting to sentry');
|
ray('reporting to sentry');
|
||||||
Integration::captureUnhandledException($e);
|
Integration::captureUnhandledException($e);
|
||||||
});
|
});
|
||||||
|
|||||||
184
app/Http/Controllers/Api/Deploy.php
Normal file
184
app/Http/Controllers/Api/Deploy.php
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartMariadb;
|
||||||
|
use App\Actions\Database\StartMongodb;
|
||||||
|
use App\Actions\Database\StartMysql;
|
||||||
|
use App\Actions\Database\StartPostgresql;
|
||||||
|
use App\Actions\Database\StartRedis;
|
||||||
|
use App\Actions\Service\StartService;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Tag;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Deploy extends Controller
|
||||||
|
{
|
||||||
|
public function deployments(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$servers = Server::whereTeamId($teamId)->get();
|
||||||
|
$deployments_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn("server_id", $servers->pluck("id"))->get([
|
||||||
|
"id",
|
||||||
|
"application_id",
|
||||||
|
"application_name",
|
||||||
|
"deployment_url",
|
||||||
|
"pull_request_id",
|
||||||
|
"server_name",
|
||||||
|
"server_id",
|
||||||
|
"status"
|
||||||
|
])->sortBy('id')->toArray();
|
||||||
|
return response()->json($deployments_per_server, 200);
|
||||||
|
}
|
||||||
|
public function deploy(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
$uuids = $request->query->get('uuid');
|
||||||
|
$tags = $request->query->get('tag');
|
||||||
|
$force = $request->query->get('force') ?? false;
|
||||||
|
|
||||||
|
if ($uuids && $tags) {
|
||||||
|
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||||
|
}
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
if ($tags) {
|
||||||
|
return $this->by_tags($tags, $teamId, $force);
|
||||||
|
} else if ($uuids) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
private function by_uuids(string $uuid, int $teamId, bool $force = false)
|
||||||
|
{
|
||||||
|
$uuids = explode(',', $uuid);
|
||||||
|
$uuids = collect(array_filter($uuids));
|
||||||
|
|
||||||
|
if (count($uuids) === 0) {
|
||||||
|
return response()->json(['error' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||||
|
}
|
||||||
|
$deployments = collect();
|
||||||
|
$payload = collect();
|
||||||
|
foreach ($uuids as $uuid) {
|
||||||
|
$resource = getResourceByUuid($uuid, $teamId);
|
||||||
|
if ($resource) {
|
||||||
|
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
|
||||||
|
if ($deployment_uuid) {
|
||||||
|
$deployments->push(['message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
|
||||||
|
} else {
|
||||||
|
$deployments->push(['message' => $return_message, 'resource_uuid' => $uuid]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($deployments->count() > 0) {
|
||||||
|
$payload->put('deployments', $deployments->toArray());
|
||||||
|
return response()->json($payload->toArray(), 200);
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
$tags = explode(',', $tags);
|
||||||
|
$tags = collect(array_filter($tags));
|
||||||
|
|
||||||
|
if (count($tags) === 0) {
|
||||||
|
return response()->json(['error' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
|
||||||
|
}
|
||||||
|
$message = collect([]);
|
||||||
|
$deployments = collect();
|
||||||
|
$payload = collect();
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
|
||||||
|
if (!$found_tag) {
|
||||||
|
// $message->push("Tag {$tag} not found.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$applications = $found_tag->applications()->get();
|
||||||
|
$services = $found_tag->services()->get();
|
||||||
|
if ($applications->count() === 0 && $services->count() === 0) {
|
||||||
|
$message->push("No resources found for tag {$tag}.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($applications as $resource) {
|
||||||
|
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
|
||||||
|
if ($deployment_uuid) {
|
||||||
|
$deployments->push(['resource_uuid' => $resource->uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
|
||||||
|
}
|
||||||
|
$message = $message->merge($return_message);
|
||||||
|
}
|
||||||
|
foreach ($services as $resource) {
|
||||||
|
['message' => $return_message] = $this->deploy_resource($resource, $force);
|
||||||
|
$message = $message->merge($return_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ray($message);
|
||||||
|
if ($message->count() > 0) {
|
||||||
|
$payload->put('message', $message->toArray());
|
||||||
|
if ($deployments->count() > 0) {
|
||||||
|
$payload->put('details', $deployments->toArray());
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
public function deploy_resource($resource, bool $force = false): array
|
||||||
|
{
|
||||||
|
$message = null;
|
||||||
|
$deployment_uuid = null;
|
||||||
|
if (gettype($resource) !== 'object') {
|
||||||
|
return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
|
||||||
|
}
|
||||||
|
$type = $resource?->getMorphClass();
|
||||||
|
if ($type === 'App\Models\Application') {
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $resource,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: $force,
|
||||||
|
);
|
||||||
|
$message = "Application {$resource->name} deployment queued.";
|
||||||
|
} else if ($type === 'App\Models\StandalonePostgresql') {
|
||||||
|
StartPostgresql::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} else if ($type === 'App\Models\StandaloneRedis') {
|
||||||
|
StartRedis::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} else if ($type === 'App\Models\StandaloneMongodb') {
|
||||||
|
StartMongodb::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} else if ($type === 'App\Models\StandaloneMysql') {
|
||||||
|
StartMysql::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} else if ($type === 'App\Models\StandaloneMariadb') {
|
||||||
|
StartMariadb::run($resource);
|
||||||
|
$resource->update([
|
||||||
|
'started_at' => now(),
|
||||||
|
]);
|
||||||
|
$message = "Database {$resource->name} started.";
|
||||||
|
} else if ($type === 'App\Models\Service') {
|
||||||
|
StartService::run($resource);
|
||||||
|
$message = "Service {$resource->name} started. It could take a while, be patient.";
|
||||||
|
}
|
||||||
|
return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
|
||||||
|
}
|
||||||
|
}
|
||||||
104
app/Http/Controllers/Api/Domains.php
Normal file
104
app/Http/Controllers/Api/Domains.php
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\Project as ModelsProject;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Domains extends Controller
|
||||||
|
{
|
||||||
|
public function domains(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$projects = ModelsProject::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);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Http/Controllers/Api/Project.php
Normal file
39
app/Http/Controllers/Api/Project.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Project as ModelsProject;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Project extends Controller
|
||||||
|
{
|
||||||
|
public function projects(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$projects = ModelsProject::whereTeamId($teamId)->select('id', 'name', 'uuid')->get();
|
||||||
|
return response()->json($projects);
|
||||||
|
}
|
||||||
|
public function project_by_uuid(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']);
|
||||||
|
return response()->json($project);
|
||||||
|
}
|
||||||
|
public function environment_details(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
|
||||||
|
$environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']);
|
||||||
|
return response()->json($environment);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Http/Controllers/Api/Resources.php
Normal file
38
app/Http/Controllers/Api/Resources.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Project;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Resources extends Controller
|
||||||
|
{
|
||||||
|
public function resources(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$projects = Project::where('team_id', $teamId)->get();
|
||||||
|
$resources = collect();
|
||||||
|
$resources->push($projects->pluck('applications')->flatten());
|
||||||
|
$resources->push($projects->pluck('services')->flatten());
|
||||||
|
foreach (collect(DATABASE_TYPES) as $db) {
|
||||||
|
$resources->push($projects->pluck(str($db)->plural(2))->flatten());
|
||||||
|
}
|
||||||
|
$resources = $resources->flatten();
|
||||||
|
$resources = $resources->map(function ($resource) {
|
||||||
|
$payload = $resource->toArray();
|
||||||
|
if ($resource->getMorphClass() === 'App\Models\Service') {
|
||||||
|
$payload['status'] = $resource->status();
|
||||||
|
} else {
|
||||||
|
$payload['status'] = $resource->status;
|
||||||
|
}
|
||||||
|
$payload['type'] = $resource->type();
|
||||||
|
return $payload;
|
||||||
|
});
|
||||||
|
return response()->json($resources);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
app/Http/Controllers/Api/Server.php
Normal file
57
app/Http/Controllers/Api/Server.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Server as ModelsServer;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Server extends Controller
|
||||||
|
{
|
||||||
|
public function servers(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$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_usable'] = $server->settings->is_usable;
|
||||||
|
return $server;
|
||||||
|
});
|
||||||
|
return response()->json($servers);
|
||||||
|
}
|
||||||
|
public function server_by_uuid(Request $request)
|
||||||
|
{
|
||||||
|
$with_resources = $request->query('resources');
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$server = ModelsServer::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
|
||||||
|
if (is_null($server)) {
|
||||||
|
return response()->json(['error' => 'Server not found.'], 404);
|
||||||
|
}
|
||||||
|
if ($with_resources) {
|
||||||
|
$server['resources'] = $server->definedResources()->map(function ($resource) {
|
||||||
|
$payload = [
|
||||||
|
'id' => $resource->id,
|
||||||
|
'uuid' => $resource->uuid,
|
||||||
|
'name' => $resource->name,
|
||||||
|
'type' => $resource->type(),
|
||||||
|
'created_at' => $resource->created_at,
|
||||||
|
'updated_at' => $resource->updated_at,
|
||||||
|
];
|
||||||
|
if ($resource->type() === 'service') {
|
||||||
|
$payload['status'] = $resource->status();
|
||||||
|
} else {
|
||||||
|
$payload['status'] = $resource->status;
|
||||||
|
}
|
||||||
|
return $payload;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$server->load(['settings']);
|
||||||
|
}
|
||||||
|
return response()->json($server);
|
||||||
|
}
|
||||||
|
}
|
||||||
65
app/Http/Controllers/Api/Team.php
Normal file
65
app/Http/Controllers/Api/Team.php
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Team extends Controller
|
||||||
|
{
|
||||||
|
public function teams(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$teams = auth()->user()->teams;
|
||||||
|
return response()->json($teams);
|
||||||
|
}
|
||||||
|
public function team_by_id(Request $request)
|
||||||
|
{
|
||||||
|
$id = $request->id;
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$teams = auth()->user()->teams;
|
||||||
|
$team = $teams->where('id', $id)->first();
|
||||||
|
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($team);
|
||||||
|
}
|
||||||
|
public function members_by_id(Request $request)
|
||||||
|
{
|
||||||
|
$id = $request->id;
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$teams = auth()->user()->teams;
|
||||||
|
$team = $teams->where('id', $id)->first();
|
||||||
|
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($team->members);
|
||||||
|
}
|
||||||
|
public function current_team(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$team = auth()->user()->currentTeam();
|
||||||
|
return response()->json($team);
|
||||||
|
}
|
||||||
|
public function current_team_members(Request $request)
|
||||||
|
{
|
||||||
|
$teamId = get_team_id_from_token();
|
||||||
|
if (is_null($teamId)) {
|
||||||
|
return invalid_token();
|
||||||
|
}
|
||||||
|
$team = auth()->user()->currentTeam();
|
||||||
|
return response()->json($team->members);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,23 +2,68 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Events\TestEvent;
|
||||||
use App\Models\S3Storage;
|
|
||||||
use App\Models\StandalonePostgresql;
|
|
||||||
use App\Models\TeamInvitation;
|
use App\Models\TeamInvitation;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Laravel\Fortify\Fortify;
|
||||||
|
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse;
|
||||||
|
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse;
|
||||||
|
use Illuminate\Support\Facades\Password;
|
||||||
|
|
||||||
class Controller extends BaseController
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
use AuthorizesRequests, ValidatesRequests;
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
|
|
||||||
|
public function realtime_test() {
|
||||||
|
if (auth()->user()?->currentTeam()->id !== 0) {
|
||||||
|
return redirect(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
TestEvent::dispatch();
|
||||||
|
return 'Look at your other tab.';
|
||||||
|
}
|
||||||
|
public function verify() {
|
||||||
|
return view('auth.verify-email');
|
||||||
|
}
|
||||||
|
public function email_verify(EmailVerificationRequest $request) {
|
||||||
|
$request->fulfill();
|
||||||
|
$name = request()->user()?->name;
|
||||||
|
send_internal_notification("User {$name} verified their email address.");
|
||||||
|
return redirect(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
public function forgot_password(Request $request) {
|
||||||
|
if (is_transactional_emails_active()) {
|
||||||
|
$arrayOfRequest = $request->only(Fortify::email());
|
||||||
|
$request->merge([
|
||||||
|
'email' => Str::lower($arrayOfRequest['email']),
|
||||||
|
]);
|
||||||
|
$type = set_transanctional_email_settings();
|
||||||
|
if (!$type) {
|
||||||
|
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
||||||
|
}
|
||||||
|
$request->validate([Fortify::email() => 'required|email']);
|
||||||
|
$status = Password::broker(config('fortify.passwords'))->sendResetLink(
|
||||||
|
$request->only(Fortify::email())
|
||||||
|
);
|
||||||
|
if ($status == Password::RESET_LINK_SENT) {
|
||||||
|
return app(SuccessfulPasswordResetLinkRequestResponse::class, ['status' => $status]);
|
||||||
|
}
|
||||||
|
if ($status == Password::RESET_THROTTLED) {
|
||||||
|
return response('Already requested a password reset in the past minutes.', 400);
|
||||||
|
}
|
||||||
|
return app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]);
|
||||||
|
}
|
||||||
|
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
||||||
|
}
|
||||||
public function link()
|
public function link()
|
||||||
{
|
{
|
||||||
$token = request()->get('token');
|
$token = request()->get('token');
|
||||||
@@ -39,7 +84,7 @@ class Controller extends BaseController
|
|||||||
} else {
|
} else {
|
||||||
$team = $user->teams()->first();
|
$team = $user->teams()->first();
|
||||||
}
|
}
|
||||||
if (is_null(data_get($user, 'email_verified_at'))){
|
if (is_null(data_get($user, 'email_verified_at'))) {
|
||||||
$user->email_verified_at = now();
|
$user->email_verified_at = now();
|
||||||
$user->save();
|
$user->save();
|
||||||
}
|
}
|
||||||
@@ -51,99 +96,32 @@ class Controller extends BaseController
|
|||||||
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function license()
|
public function accept_invitation()
|
||||||
{
|
|
||||||
if (!isCloud()) {
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
return view('settings.license', [
|
|
||||||
'settings' => InstanceSettings::get(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function force_passoword_reset()
|
|
||||||
{
|
|
||||||
return view('auth.force-password-reset');
|
|
||||||
}
|
|
||||||
public function boarding()
|
|
||||||
{
|
|
||||||
if (currentTeam()->boarding || isDev()) {
|
|
||||||
return view('boarding');
|
|
||||||
} else {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function settings()
|
|
||||||
{
|
|
||||||
if (isInstanceAdmin()) {
|
|
||||||
$settings = InstanceSettings::get();
|
|
||||||
$database = StandalonePostgresql::whereName('coolify-db')->first();
|
|
||||||
if ($database) {
|
|
||||||
$s3s = S3Storage::whereTeamId(0)->get();
|
|
||||||
}
|
|
||||||
return view('settings.configuration', [
|
|
||||||
'settings' => $settings,
|
|
||||||
'database' => $database,
|
|
||||||
's3s' => $s3s ?? [],
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function team()
|
|
||||||
{
|
|
||||||
$invitations = [];
|
|
||||||
if (auth()->user()->isAdminFromSession()) {
|
|
||||||
$invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
|
|
||||||
}
|
|
||||||
return view('team.index', [
|
|
||||||
'invitations' => $invitations,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function storages()
|
|
||||||
{
|
|
||||||
$s3 = S3Storage::ownedByCurrentTeam()->get();
|
|
||||||
return view('team.storages.all', [
|
|
||||||
's3' => $s3,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function storages_show()
|
|
||||||
{
|
|
||||||
$storage = S3Storage::ownedByCurrentTeam()->whereUuid(request()->storage_uuid)->firstOrFail();
|
|
||||||
return view('team.storages.show', [
|
|
||||||
'storage' => $storage,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function members()
|
|
||||||
{
|
|
||||||
$invitations = [];
|
|
||||||
if (auth()->user()->isAdminFromSession()) {
|
|
||||||
$invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
|
|
||||||
}
|
|
||||||
return view('team.members', [
|
|
||||||
'invitations' => $invitations,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function acceptInvitation()
|
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
$resetPassword = request()->query('reset-password');
|
||||||
|
$invitationUuid = request()->route('uuid');
|
||||||
|
$invitation = TeamInvitation::whereUuid($invitationUuid)->firstOrFail();
|
||||||
$user = User::whereEmail($invitation->email)->firstOrFail();
|
$user = User::whereEmail($invitation->email)->firstOrFail();
|
||||||
if (auth()->user()->id !== $user->id) {
|
|
||||||
abort(401);
|
|
||||||
}
|
|
||||||
$invitationValid = $invitation->isValid();
|
$invitationValid = $invitation->isValid();
|
||||||
if ($invitationValid) {
|
if ($invitationValid) {
|
||||||
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
if ($resetPassword) {
|
||||||
refreshSession($invitation->team);
|
$user->update([
|
||||||
|
'password' => Hash::make($invitationUuid),
|
||||||
|
'force_password_reset' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($user->teams()->where('team_id', $invitation->team->id)->exists()) {
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
|
}
|
||||||
|
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
||||||
|
$invitation->delete();
|
||||||
|
if (auth()->user()?->id !== $user->id) {
|
||||||
|
return redirect()->route('login');
|
||||||
|
}
|
||||||
|
refreshSession($invitation->team);
|
||||||
|
return redirect()->route('team.index');
|
||||||
} else {
|
} else {
|
||||||
abort(401);
|
abort(401);
|
||||||
}
|
}
|
||||||
@@ -153,7 +131,7 @@ class Controller extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function revokeInvitation()
|
public function revoke_invitation()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
|
||||||
|
|
||||||
class DatabaseController extends Controller
|
|
||||||
{
|
|
||||||
use AuthorizesRequests, ValidatesRequests;
|
|
||||||
|
|
||||||
public function configuration()
|
|
||||||
{
|
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
|
||||||
if (!$project) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
|
|
||||||
if (!$environment) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
|
||||||
if (!$database) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
return view('project.database.configuration', ['database' => $database]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function executions()
|
|
||||||
{
|
|
||||||
$backup_uuid = request()->route('backup_uuid');
|
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
|
||||||
if (!$project) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
|
|
||||||
if (!$environment) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
|
||||||
if (!$database) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$backup = $database->scheduledBackups->where('uuid', $backup_uuid)->first();
|
|
||||||
if (!$backup) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$executions = collect($backup->executions)->sortByDesc('created_at');
|
|
||||||
return view('project.database.backups.executions', [
|
|
||||||
'database' => $database,
|
|
||||||
'backup' => $backup,
|
|
||||||
'executions' => $executions,
|
|
||||||
's3s' => currentTeam()->s3s,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function backups()
|
|
||||||
{
|
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
|
||||||
if (!$project) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
|
|
||||||
if (!$environment) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
|
||||||
if (!$database) {
|
|
||||||
return redirect()->route('dashboard');
|
|
||||||
}
|
|
||||||
// No backups for redis
|
|
||||||
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
|
|
||||||
return redirect()->route('project.database.configuration', [
|
|
||||||
'project_uuid' => $project->uuid,
|
|
||||||
'environment_name' => $environment->name,
|
|
||||||
'database_uuid' => $database->uuid,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return view('project.database.backups.all', [
|
|
||||||
'database' => $database,
|
|
||||||
's3s' => currentTeam()->s3s,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
35
app/Http/Controllers/OauthController.php
Normal file
35
app/Http/Controllers/OauthController.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class OauthController extends Controller {
|
||||||
|
public function redirect(string $provider)
|
||||||
|
{
|
||||||
|
$socialite_provider = get_socialite_provider($provider);
|
||||||
|
return $socialite_provider->redirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function callback(string $provider)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$oauthUser = get_socialite_provider($provider)->user();
|
||||||
|
$user = User::whereEmail($oauthUser->email)->first();
|
||||||
|
if (!$user) {
|
||||||
|
$user = User::create([
|
||||||
|
'name' => $oauthUser->name,
|
||||||
|
'email' => $oauthUser->email,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
Auth::login($user);
|
||||||
|
return redirect('/');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
|
return redirect()->route('login')->withErrors([__('auth.failed.callback')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
186
app/Http/Controllers/Webhook/Bitbucket.php
Normal file
186
app/Http/Controllers/Webhook/Bitbucket.php
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Livewire\Project\Service\Storage;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Bitbucket extends Controller
|
||||||
|
{
|
||||||
|
public function manual(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$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}_Bitbicket::manual_bitbucket", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$return_payloads = collect([]);
|
||||||
|
$payload = $request->collect();
|
||||||
|
$headers = $request->headers->all();
|
||||||
|
$x_bitbucket_token = data_get($headers, 'x-hub-signature.0', "");
|
||||||
|
$x_bitbucket_event = data_get($headers, 'x-event-key.0', "");
|
||||||
|
$handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']);
|
||||||
|
if (!$handled_events->contains($x_bitbucket_event)) {
|
||||||
|
return response([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Nothing to do. Event not handled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ($x_bitbucket_event === 'repo:push') {
|
||||||
|
$branch = data_get($payload, 'push.changes.0.new.name');
|
||||||
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
|
|
||||||
|
if (!$branch) {
|
||||||
|
return response([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
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') {
|
||||||
|
$branch = data_get($payload, 'pullrequest.destination.branch.name');
|
||||||
|
$base_branch = data_get($payload, 'pullrequest.source.branch.name');
|
||||||
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
|
$pull_request_id = data_get($payload, 'pullrequest.id');
|
||||||
|
$pull_request_html_url = data_get($payload, 'pullrequest.links.html.href');
|
||||||
|
$commit = data_get($payload, 'pullrequest.source.commit.hash');
|
||||||
|
}
|
||||||
|
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
||||||
|
$applications = $applications->where('git_branch', $branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
return response([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => "Nothing to do. No applications found with deploy key set, branch is '$branch' and Git Repository name has $full_name.",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$webhook_secret = data_get($application, 'manual_webhook_secret_bitbucket');
|
||||||
|
$payload = $request->getContent();
|
||||||
|
|
||||||
|
list($algo, $hash) = explode('=', $x_bitbucket_token, 2);
|
||||||
|
$payloadHash = hash_hmac($algo, $payload, $webhook_secret);
|
||||||
|
if (!hash_equals($hash, $payloadHash) && !isDev()) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Invalid token.',
|
||||||
|
]);
|
||||||
|
ray('Invalid signature');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
|
if (!$isFunctional) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Server is not functional.',
|
||||||
|
]);
|
||||||
|
ray('Server is not functional: ' . $application->destination->server->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($x_bitbucket_event === 'repo:push') {
|
||||||
|
if ($application->isDeployable()) {
|
||||||
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Preview deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Auto deployment disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_bitbucket_event === 'pullrequest:created') {
|
||||||
|
if ($application->isPRDeployable()) {
|
||||||
|
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);
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if (!$found) {
|
||||||
|
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(
|
||||||
|
application: $application,
|
||||||
|
pull_request_id: $pull_request_id,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
commit: $commit,
|
||||||
|
is_webhook: true,
|
||||||
|
git_type: 'bitbucket'
|
||||||
|
);
|
||||||
|
$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 ($x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
|
||||||
|
ray('Pull request rejected');
|
||||||
|
$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);
|
||||||
|
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);
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
459
app/Http/Controllers/Webhook/Github.php
Normal file
459
app/Http/Controllers/Webhook/Github.php
Normal file
@@ -0,0 +1,459 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Enums\ProcessStatus;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Jobs\ApplicationPullRequestUpdateJob;
|
||||||
|
use App\Jobs\GithubAppPermissionJob;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
|
use App\Models\GithubApp;
|
||||||
|
use App\Models\PrivateKey;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class Github extends Controller
|
||||||
|
{
|
||||||
|
public function manual(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
ray($request);
|
||||||
|
$return_payloads = collect([]);
|
||||||
|
$x_github_delivery = request()->header('X-GitHub-Delivery');
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$files = Storage::disk('webhooks-during-maintenance')->files();
|
||||||
|
$github_delivery_found = collect($files)->filter(function ($file) use ($x_github_delivery) {
|
||||||
|
return Str::contains($file, $x_github_delivery);
|
||||||
|
})->first();
|
||||||
|
if ($github_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}_Github::manual_{$x_github_delivery}", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$x_github_event = Str::lower($request->header('X-GitHub-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_github_event === 'ping') {
|
||||||
|
// Just pong
|
||||||
|
return response('pong');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($content_type !== 'application/json') {
|
||||||
|
$payload = json_decode(data_get($payload, 'payload'), true);
|
||||||
|
}
|
||||||
|
if ($x_github_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/');
|
||||||
|
}
|
||||||
|
ray('Manual Webhook GitHub Push Event with branch: ' . $branch);
|
||||||
|
}
|
||||||
|
if ($x_github_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 GitHub 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_github_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_github_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_github');
|
||||||
|
$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 token.',
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
|
if (!$isFunctional) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Server is not functional.',
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
if ($application->isDeployable()) {
|
||||||
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true,
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Deployments disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'pull_request') {
|
||||||
|
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
||||||
|
if ($application->isPRDeployable()) {
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if (!$found) {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'github',
|
||||||
|
'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,
|
||||||
|
is_webhook: true,
|
||||||
|
git_type: 'github'
|
||||||
|
);
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function normal(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$return_payloads = collect([]);
|
||||||
|
$id = null;
|
||||||
|
$x_github_delivery = $request->header('X-GitHub-Delivery');
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$files = Storage::disk('webhooks-during-maintenance')->files();
|
||||||
|
$github_delivery_found = collect($files)->filter(function ($file) use ($x_github_delivery) {
|
||||||
|
return Str::contains($file, $x_github_delivery);
|
||||||
|
})->first();
|
||||||
|
if ($github_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}_Github::normal_{$x_github_delivery}", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$x_github_event = Str::lower($request->header('X-GitHub-Event'));
|
||||||
|
$x_github_hook_installation_target_id = $request->header('X-GitHub-Hook-Installation-Target-Id');
|
||||||
|
$x_hub_signature_256 = Str::after($request->header('X-Hub-Signature-256'), 'sha256=');
|
||||||
|
$payload = $request->collect();
|
||||||
|
if ($x_github_event === 'ping') {
|
||||||
|
// Just pong
|
||||||
|
return response('pong');
|
||||||
|
}
|
||||||
|
$github_app = GithubApp::where('app_id', $x_github_hook_installation_target_id)->first();
|
||||||
|
if (is_null($github_app)) {
|
||||||
|
return response('Nothing to do. No GitHub App found.');
|
||||||
|
}
|
||||||
|
$webhook_secret = data_get($github_app, 'webhook_secret');
|
||||||
|
$hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret);
|
||||||
|
if (config('app.env') !== 'local') {
|
||||||
|
if (!hash_equals($x_hub_signature_256, $hmac)) {
|
||||||
|
return response('Invalid signature.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'installation' || $x_github_event === 'installation_repositories') {
|
||||||
|
// Installation handled by setup redirect url. Repositories queried on-demand.
|
||||||
|
$action = data_get($payload, 'action');
|
||||||
|
if ($action === 'new_permissions_accepted') {
|
||||||
|
GithubAppPermissionJob::dispatch($github_app);
|
||||||
|
}
|
||||||
|
return response('cool');
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
$id = data_get($payload, 'repository.id');
|
||||||
|
$branch = data_get($payload, 'ref');
|
||||||
|
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||||
|
$branch = Str::after($branch, 'refs/heads/');
|
||||||
|
}
|
||||||
|
ray('Webhook GitHub Push Event: ' . $id . ' with branch: ' . $branch);
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'pull_request') {
|
||||||
|
$action = data_get($payload, 'action');
|
||||||
|
$id = data_get($payload, 'repository.id');
|
||||||
|
$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 GitHub Pull Request Event: ' . $id . ' with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id);
|
||||||
|
}
|
||||||
|
if (!$id || !$branch) {
|
||||||
|
return response('Nothing to do. No id or branch found.');
|
||||||
|
}
|
||||||
|
$applications = Application::where('repository_project_id', $id)->whereRelation('source', 'is_public', false);
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
$applications = $applications->where('git_branch', $branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
return response("Nothing to do. No applications found with branch '$branch'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_github_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) {
|
||||||
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
|
if (!$isFunctional) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Server is not functional.',
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'push') {
|
||||||
|
if ($application->isDeployable()) {
|
||||||
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true
|
||||||
|
);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Deployment queued.',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Deployments disabled.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_github_event === 'pull_request') {
|
||||||
|
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
|
||||||
|
if ($application->isPRDeployable()) {
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if (!$found) {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'github',
|
||||||
|
'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,
|
||||||
|
is_webhook: true,
|
||||||
|
git_type: 'github'
|
||||||
|
);
|
||||||
|
$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' || $action === 'close') {
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if ($found) {
|
||||||
|
ApplicationPullRequestUpdateJob::dispatchSync(application: $application, preview: $found, status: ProcessStatus::CLOSED);
|
||||||
|
$found->delete();
|
||||||
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
|
// ray('Stopping container: ' . $container_name);
|
||||||
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'success',
|
||||||
|
'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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function redirect(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$code = $request->get('code');
|
||||||
|
$state = $request->get('state');
|
||||||
|
$github_app = GithubApp::where('uuid', $state)->firstOrFail();
|
||||||
|
$api_url = data_get($github_app, 'api_url');
|
||||||
|
$data = Http::withBody(null)->accept('application/vnd.github+json')->post("$api_url/app-manifests/$code/conversions")->throw()->json();
|
||||||
|
$id = data_get($data, 'id');
|
||||||
|
$slug = data_get($data, 'slug');
|
||||||
|
$client_id = data_get($data, 'client_id');
|
||||||
|
$client_secret = data_get($data, 'client_secret');
|
||||||
|
$private_key = data_get($data, 'pem');
|
||||||
|
$webhook_secret = data_get($data, 'webhook_secret');
|
||||||
|
$private_key = PrivateKey::create([
|
||||||
|
'name' => $slug,
|
||||||
|
'private_key' => $private_key,
|
||||||
|
'team_id' => $github_app->team_id,
|
||||||
|
'is_git_related' => true,
|
||||||
|
]);
|
||||||
|
$github_app->name = $slug;
|
||||||
|
$github_app->app_id = $id;
|
||||||
|
$github_app->client_id = $client_id;
|
||||||
|
$github_app->client_secret = $client_secret;
|
||||||
|
$github_app->webhook_secret = $webhook_secret;
|
||||||
|
$github_app->private_key_id = $private_key->id;
|
||||||
|
$github_app->save();
|
||||||
|
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function install(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$installation_id = $request->get('installation_id');
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$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}_Github::install_{$installation_id}", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$source = $request->get('source');
|
||||||
|
$setup_action = $request->get('setup_action');
|
||||||
|
$github_app = GithubApp::where('uuid', $source)->firstOrFail();
|
||||||
|
if ($setup_action === 'install') {
|
||||||
|
$github_app->installation_id = $installation_id;
|
||||||
|
$github_app->save();
|
||||||
|
}
|
||||||
|
return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
202
app/Http/Controllers/Webhook/Gitlab.php
Normal file
202
app/Http/Controllers/Webhook/Gitlab.php
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
<?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 Gitlab extends Controller
|
||||||
|
{
|
||||||
|
public function manual(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$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}_Gitlab::manual_gitlab", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$return_payloads = collect([]);
|
||||||
|
$payload = $request->collect();
|
||||||
|
$headers = $request->headers->all();
|
||||||
|
$x_gitlab_token = data_get($headers, 'x-gitlab-token.0');
|
||||||
|
$x_gitlab_event = data_get($payload, 'object_kind');
|
||||||
|
if ($x_gitlab_event === 'push') {
|
||||||
|
$branch = data_get($payload, 'ref');
|
||||||
|
$full_name = data_get($payload, 'project.path_with_namespace');
|
||||||
|
if (Str::isMatch('/refs\/heads\/*/', $branch)) {
|
||||||
|
$branch = Str::after($branch, 'refs/heads/');
|
||||||
|
}
|
||||||
|
if (!$branch) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
|
]);
|
||||||
|
return response($return_payloads);
|
||||||
|
}
|
||||||
|
ray('Manual Webhook GitLab Push Event with branch: ' . $branch);
|
||||||
|
}
|
||||||
|
if ($x_gitlab_event === 'merge_request') {
|
||||||
|
$action = data_get($payload, 'object_attributes.action');
|
||||||
|
$branch = data_get($payload, 'object_attributes.source_branch');
|
||||||
|
$base_branch = data_get($payload, 'object_attributes.target_branch');
|
||||||
|
$full_name = data_get($payload, 'project.path_with_namespace');
|
||||||
|
$pull_request_id = data_get($payload, 'object_attributes.iid');
|
||||||
|
$pull_request_html_url = data_get($payload, 'object_attributes.url');
|
||||||
|
if (!$branch) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
|
]);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
$applications = Application::where('git_repository', 'like', "%$full_name%");
|
||||||
|
if ($x_gitlab_event === 'push') {
|
||||||
|
$applications = $applications->where('git_branch', $branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_gitlab_event === 'merge_request') {
|
||||||
|
$applications = $applications->where('git_branch', $base_branch)->get();
|
||||||
|
if ($applications->isEmpty()) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => "Nothing to do. No applications found with branch '$base_branch'.",
|
||||||
|
]);
|
||||||
|
return response($return_payloads);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$webhook_secret = data_get($application, 'manual_webhook_secret_gitlab');
|
||||||
|
if ($webhook_secret !== $x_gitlab_token) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Invalid token.',
|
||||||
|
]);
|
||||||
|
ray('Invalid signature');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$isFunctional = $application->destination->server->isFunctional();
|
||||||
|
if (!$isFunctional) {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Server is not functional',
|
||||||
|
]);
|
||||||
|
ray('Server is not functional: ' . $application->destination->server->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($x_gitlab_event === 'push') {
|
||||||
|
if ($application->isDeployable()) {
|
||||||
|
ray('Deploying ' . $application->name . ' with branch ' . $branch);
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
queue_application_deployment(
|
||||||
|
application: $application,
|
||||||
|
deployment_uuid: $deployment_uuid,
|
||||||
|
force_rebuild: false,
|
||||||
|
is_webhook: true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'Deployments disabled',
|
||||||
|
]);
|
||||||
|
ray('Deployments disabled for ' . $application->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($x_gitlab_event === 'merge_request') {
|
||||||
|
if ($action === 'open' || $action === 'opened' || $action === 'synchronize' || $action === 'reopened' || $action === 'reopen' || $action === 'update') {
|
||||||
|
if ($application->isPRDeployable()) {
|
||||||
|
$deployment_uuid = new Cuid2(7);
|
||||||
|
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||||
|
if (!$found) {
|
||||||
|
ApplicationPreview::create([
|
||||||
|
'git_type' => 'gitlab',
|
||||||
|
'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,
|
||||||
|
is_webhook: true,
|
||||||
|
git_type: 'gitlab'
|
||||||
|
);
|
||||||
|
ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id);
|
||||||
|
$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',
|
||||||
|
]);
|
||||||
|
ray('Preview deployments disabled for ' . $application->name);
|
||||||
|
}
|
||||||
|
} else if ($action === 'closed' || $action === 'close') {
|
||||||
|
$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',
|
||||||
|
]);
|
||||||
|
return response($return_payloads);
|
||||||
|
}
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'No Preview Deployment found',
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$return_payloads->push([
|
||||||
|
'application' => $application->name,
|
||||||
|
'status' => 'failed',
|
||||||
|
'message' => 'No action found. Contact us for debugging.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response($return_payloads);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
|
return handleError($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
258
app/Http/Controllers/Webhook/Stripe.php
Normal file
258
app/Http/Controllers/Webhook/Stripe.php
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Jobs\ServerLimitCheckJob;
|
||||||
|
use App\Jobs\SubscriptionInvoiceFailedJob;
|
||||||
|
use App\Jobs\SubscriptionTrialEndedJob;
|
||||||
|
use App\Jobs\SubscriptionTrialEndsSoonJob;
|
||||||
|
use App\Models\Subscription;
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Models\Webhook;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Sleep;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class Stripe extends Controller
|
||||||
|
{
|
||||||
|
public function events(Request $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (app()->isDownForMaintenance()) {
|
||||||
|
ray('Maintenance mode is on');
|
||||||
|
$epoch = now()->valueOf();
|
||||||
|
$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}_Stripe::events_stripe", $json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$webhookSecret = config('subscription.stripe_webhook_secret');
|
||||||
|
$signature = $request->header('Stripe-Signature');
|
||||||
|
$excludedPlans = config('subscription.stripe_excluded_plans');
|
||||||
|
$event = \Stripe\Webhook::constructEvent(
|
||||||
|
$request->getContent(),
|
||||||
|
$signature,
|
||||||
|
$webhookSecret
|
||||||
|
);
|
||||||
|
$webhook = Webhook::create([
|
||||||
|
'type' => 'stripe',
|
||||||
|
'payload' => $request->getContent()
|
||||||
|
]);
|
||||||
|
$type = data_get($event, 'type');
|
||||||
|
$data = data_get($event, 'data.object');
|
||||||
|
switch ($type) {
|
||||||
|
case 'checkout.session.completed':
|
||||||
|
$clientReferenceId = data_get($data, 'client_reference_id');
|
||||||
|
if (is_null($clientReferenceId)) {
|
||||||
|
send_internal_notification('Checkout session completed without client reference id.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$userId = Str::before($clientReferenceId, ':');
|
||||||
|
$teamId = Str::after($clientReferenceId, ':');
|
||||||
|
$subscriptionId = data_get($data, 'subscription');
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$team = Team::find($teamId);
|
||||||
|
$found = $team->members->where('id', $userId)->first();
|
||||||
|
if (!$found->isAdmin()) {
|
||||||
|
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}.");
|
||||||
|
}
|
||||||
|
$subscription = Subscription::where('team_id', $teamId)->first();
|
||||||
|
if ($subscription) {
|
||||||
|
send_internal_notification('Old subscription activated for team: ' . $teamId);
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_subscription_id' => $subscriptionId,
|
||||||
|
'stripe_customer_id' => $customerId,
|
||||||
|
'stripe_invoice_paid' => true,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
send_internal_notification('New subscription for team: ' . $teamId);
|
||||||
|
Subscription::create([
|
||||||
|
'team_id' => $teamId,
|
||||||
|
'stripe_subscription_id' => $subscriptionId,
|
||||||
|
'stripe_customer_id' => $customerId,
|
||||||
|
'stripe_invoice_paid' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'invoice.paid':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$planId = data_get($data, 'lines.data.0.plan.id');
|
||||||
|
if (Str::contains($excludedPlans, $planId)) {
|
||||||
|
send_internal_notification('Subscription excluded.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
if (!$subscription) {
|
||||||
|
Sleep::for(5)->seconds();
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
|
}
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_invoice_paid' => true,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case 'invoice.payment_failed':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
if (!$subscription) {
|
||||||
|
send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: ' . $customerId);
|
||||||
|
return response('No subscription found in Coolify.');
|
||||||
|
}
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
if (!$team) {
|
||||||
|
send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: ' . $customerId);
|
||||||
|
return response('No team found in Coolify.');
|
||||||
|
}
|
||||||
|
if (!$subscription->stripe_invoice_paid) {
|
||||||
|
SubscriptionInvoiceFailedJob::dispatch($team);
|
||||||
|
send_internal_notification('Invoice payment failed: ' . $customerId);
|
||||||
|
} else {
|
||||||
|
send_internal_notification('Invoice payment failed but already paid: ' . $customerId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'payment_intent.payment_failed':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
if (!$subscription) {
|
||||||
|
send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: ' . $customerId);
|
||||||
|
return response('No subscription found in Coolify.');
|
||||||
|
}
|
||||||
|
if ($subscription->stripe_invoice_paid) {
|
||||||
|
send_internal_notification('payment_intent.payment_failed but invoice is active for customer: ' . $customerId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send_internal_notification('Subscription payment failed for customer: ' . $customerId);
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.updated':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$status = data_get($data, 'status');
|
||||||
|
$subscriptionId = data_get($data, 'items.data.0.subscription');
|
||||||
|
$planId = data_get($data, 'items.data.0.plan.id');
|
||||||
|
if (Str::contains($excludedPlans, $planId)) {
|
||||||
|
send_internal_notification('Subscription excluded.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
if (!$subscription) {
|
||||||
|
Sleep::for(5)->seconds();
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
|
}
|
||||||
|
if (!$subscription) {
|
||||||
|
send_internal_notification('No subscription found for: ' . $customerId);
|
||||||
|
return response("No subscription found", 400);
|
||||||
|
}
|
||||||
|
$trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended');
|
||||||
|
$cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end');
|
||||||
|
$alreadyCancelAtPeriodEnd = data_get($subscription, 'stripe_cancel_at_period_end');
|
||||||
|
$feedback = data_get($data, 'cancellation_details.feedback');
|
||||||
|
$comment = data_get($data, 'cancellation_details.comment');
|
||||||
|
$lookup_key = data_get($data, 'items.data.0.price.lookup_key');
|
||||||
|
if (str($lookup_key)->contains('ultimate')) {
|
||||||
|
$quantity = data_get($data, 'items.data.0.quantity', 10);
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
$team->update([
|
||||||
|
'custom_server_limit' => $quantity,
|
||||||
|
]);
|
||||||
|
ServerLimitCheckJob::dispatch($team);
|
||||||
|
}
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_feedback' => $feedback,
|
||||||
|
'stripe_comment' => $comment,
|
||||||
|
'stripe_plan_id' => $planId,
|
||||||
|
'stripe_cancel_at_period_end' => $cancelAtPeriodEnd,
|
||||||
|
]);
|
||||||
|
if ($status === 'paused' || $status === 'incomplete_expired') {
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_invoice_paid' => false,
|
||||||
|
]);
|
||||||
|
send_internal_notification('Subscription paused or incomplete for customer: ' . $customerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trial ended but subscribed, reactive servers
|
||||||
|
if ($trialEndedAlready && $status === 'active') {
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
$team->trialEndedButSubscribed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($feedback) {
|
||||||
|
$reason = "Cancellation feedback for {$customerId}: '" . $feedback . "'";
|
||||||
|
if ($comment) {
|
||||||
|
$reason .= ' with comment: \'' . $comment . "'";
|
||||||
|
}
|
||||||
|
send_internal_notification($reason);
|
||||||
|
}
|
||||||
|
if ($alreadyCancelAtPeriodEnd !== $cancelAtPeriodEnd) {
|
||||||
|
if ($cancelAtPeriodEnd) {
|
||||||
|
// send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id);
|
||||||
|
} else {
|
||||||
|
send_internal_notification('customer.subscription.updated for customer: ' . $customerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.deleted':
|
||||||
|
// End subscription
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
$team->trialEnded();
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_subscription_id' => null,
|
||||||
|
'stripe_plan_id' => null,
|
||||||
|
'stripe_cancel_at_period_end' => false,
|
||||||
|
'stripe_invoice_paid' => false,
|
||||||
|
'stripe_trial_already_ended' => true,
|
||||||
|
]);
|
||||||
|
send_internal_notification('customer.subscription.deleted for customer: ' . $customerId);
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.trial_will_end':
|
||||||
|
// Not used for now
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
if (!$team) {
|
||||||
|
throw new Exception('No team found for subscription: ' . $subscription->id);
|
||||||
|
}
|
||||||
|
SubscriptionTrialEndsSoonJob::dispatch($team);
|
||||||
|
break;
|
||||||
|
case 'customer.subscription.paused':
|
||||||
|
$customerId = data_get($data, 'customer');
|
||||||
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
if (!$team) {
|
||||||
|
throw new Exception('No team found for subscription: ' . $subscription->id);
|
||||||
|
}
|
||||||
|
$team->trialEnded();
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_trial_already_ended' => true,
|
||||||
|
'stripe_invoice_paid' => false,
|
||||||
|
]);
|
||||||
|
SubscriptionTrialEndedJob::dispatch($team);
|
||||||
|
send_internal_notification('Subscription paused for customer: ' . $customerId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unhandled event type
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if ($type !== 'payment_intent.payment_failed') {
|
||||||
|
send_internal_notification("Subscription webhook ($type) failed: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
$webhook->update([
|
||||||
|
'status' => 'failed',
|
||||||
|
'failure_reason' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
return response($e->getMessage(), 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/Http/Controllers/Webhook/Waitlist.php
Normal file
58
app/Http/Controllers/Webhook/Waitlist.php
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Webhook;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Waitlist as ModelsWaitlist;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Waitlist extends Controller
|
||||||
|
{
|
||||||
|
public function confirm(Request $request)
|
||||||
|
{
|
||||||
|
$email = request()->get('email');
|
||||||
|
$confirmation_code = request()->get('confirmation_code');
|
||||||
|
ray($email, $confirmation_code);
|
||||||
|
try {
|
||||||
|
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
|
||||||
|
if ($found) {
|
||||||
|
if (!$found->verified) {
|
||||||
|
if ($found->created_at > now()->subMinutes(config('constants.waitlist.expiration'))) {
|
||||||
|
$found->verified = true;
|
||||||
|
$found->save();
|
||||||
|
send_internal_notification('Waitlist confirmed: ' . $email);
|
||||||
|
return 'Thank you for confirming your email address. We will notify you when you are next in line.';
|
||||||
|
} else {
|
||||||
|
$found->delete();
|
||||||
|
send_internal_notification('Waitlist expired: ' . $email);
|
||||||
|
return 'Your confirmation code has expired. Please sign up again.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
send_internal_notification('Waitlist confirmation failed: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function cancel(Request $request)
|
||||||
|
{
|
||||||
|
$email = request()->get('email');
|
||||||
|
$confirmation_code = request()->get('confirmation_code');
|
||||||
|
try {
|
||||||
|
$found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
|
||||||
|
if ($found && !$found->verified) {
|
||||||
|
$found->delete();
|
||||||
|
send_internal_notification('Waitlist cancelled: ' . $email);
|
||||||
|
return 'Your email address has been removed from the waitlist.';
|
||||||
|
}
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
send_internal_notification('Waitlist cancellation failed: ' . $e->getMessage());
|
||||||
|
ray($e->getMessage());
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
|
||||||
|
|
||||||
use App\Enums\ProcessStatus;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Spatie\Activitylog\Models\Activity;
|
|
||||||
|
|
||||||
class ActivityMonitor extends Component
|
|
||||||
{
|
|
||||||
public string|null $header = null;
|
|
||||||
public $activityId;
|
|
||||||
public $isPollingActive = false;
|
|
||||||
|
|
||||||
protected $activity;
|
|
||||||
protected $listeners = ['newMonitorActivity'];
|
|
||||||
|
|
||||||
public function newMonitorActivity($activityId)
|
|
||||||
{
|
|
||||||
$this->activityId = $activityId;
|
|
||||||
|
|
||||||
$this->hydrateActivity();
|
|
||||||
|
|
||||||
$this->isPollingActive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hydrateActivity()
|
|
||||||
{
|
|
||||||
$this->activity = Activity::query()
|
|
||||||
->find($this->activityId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function polling()
|
|
||||||
{
|
|
||||||
$this->hydrateActivity();
|
|
||||||
$this->setStatus(ProcessStatus::IN_PROGRESS);
|
|
||||||
$exit_code = data_get($this->activity, 'properties.exitCode');
|
|
||||||
if ($exit_code !== null) {
|
|
||||||
if ($exit_code === 0) {
|
|
||||||
$this->setStatus(ProcessStatus::FINISHED);
|
|
||||||
} else {
|
|
||||||
$this->setStatus(ProcessStatus::ERROR);
|
|
||||||
}
|
|
||||||
$this->isPollingActive = false;
|
|
||||||
$this->emit('activityFinished');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function setStatus($status)
|
|
||||||
{
|
|
||||||
$this->activity->properties = $this->activity->properties->merge([
|
|
||||||
'status' => $status,
|
|
||||||
]);
|
|
||||||
$this->activity->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire;
|
|
||||||
|
|
||||||
use App\Models\Project;
|
|
||||||
use App\Models\Server;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Dashboard extends Component
|
|
||||||
{
|
|
||||||
public $projects = [];
|
|
||||||
public $servers = [];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->servers = Server::ownedByCurrentTeam()->get();
|
|
||||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
|
||||||
}
|
|
||||||
// public function getIptables()
|
|
||||||
// {
|
|
||||||
// $servers = Server::ownedByCurrentTeam()->get();
|
|
||||||
// foreach ($servers as $server) {
|
|
||||||
// checkRequiredCommands($server);
|
|
||||||
// $iptables = instant_remote_process(['docker run --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c "iptables -L -n | jc --iptables"'], $server);
|
|
||||||
// ray($iptables);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.dashboard');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Destination\New;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class StandaloneDocker extends Component
|
|
||||||
{
|
|
||||||
public string $name;
|
|
||||||
public string $network;
|
|
||||||
|
|
||||||
public Collection $servers;
|
|
||||||
public Server $server;
|
|
||||||
public int|null $server_id = null;
|
|
||||||
|
|
||||||
protected $rules = [
|
|
||||||
'name' => 'required|string',
|
|
||||||
'network' => 'required|string',
|
|
||||||
'server_id' => 'required|integer'
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'name' => 'name',
|
|
||||||
'network' => 'network',
|
|
||||||
'server_id' => 'server'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
if (request()->query('server_id')) {
|
|
||||||
$this->server_id = request()->query('server_id');
|
|
||||||
} else {
|
|
||||||
if ($this->servers->count() > 0) {
|
|
||||||
$this->server_id = $this->servers->first()->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (request()->query('network_name')) {
|
|
||||||
$this->network = request()->query('network_name');
|
|
||||||
} else {
|
|
||||||
$this->network = new Cuid2(7);
|
|
||||||
}
|
|
||||||
$this->name = Str::kebab("{$this->servers->first()->name}-{$this->network}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generate_name()
|
|
||||||
{
|
|
||||||
$this->server = Server::find($this->server_id);
|
|
||||||
$this->name = Str::kebab("{$this->server->name}-{$this->network}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->validate();
|
|
||||||
try {
|
|
||||||
$this->server = Server::find($this->server_id);
|
|
||||||
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
|
|
||||||
if ($found) {
|
|
||||||
$this->createNetworkAndAttachToProxy();
|
|
||||||
$this->emit('error', 'Network already added to this server.');
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
$docker = ModelsStandaloneDocker::create([
|
|
||||||
'name' => $this->name,
|
|
||||||
'network' => $this->network,
|
|
||||||
'server_id' => $this->server_id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$this->createNetworkAndAttachToProxy();
|
|
||||||
return redirect()->route('destination.show', $docker->uuid);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createNetworkAndAttachToProxy()
|
|
||||||
{
|
|
||||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
|
||||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Destination;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Show extends Component
|
|
||||||
{
|
|
||||||
public Server $server;
|
|
||||||
public Collection|array $networks = [];
|
|
||||||
|
|
||||||
public function scan()
|
|
||||||
{
|
|
||||||
$alreadyAddedNetworks = $this->server->standaloneDockers;
|
|
||||||
$networks = instant_remote_process(['docker network ls --format "{{json .}}"'], $this->server, false);
|
|
||||||
$this->networks = format_docker_command_output_to_json($networks)->filter(function ($network) {
|
|
||||||
return $network['Name'] !== 'bridge' && $network['Name'] !== 'host' && $network['Name'] !== 'none';
|
|
||||||
})->filter(function ($network) use ($alreadyAddedNetworks) {
|
|
||||||
return !$alreadyAddedNetworks->contains('network', $network['Name']);
|
|
||||||
});
|
|
||||||
if ($this->networks->count() === 0) {
|
|
||||||
$this->emit('success', 'No new networks found.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Profile;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Form extends Component
|
|
||||||
{
|
|
||||||
public int $userId;
|
|
||||||
public string $name;
|
|
||||||
public string $email;
|
|
||||||
|
|
||||||
protected $rules = [
|
|
||||||
'name' => 'required',
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'name' => 'name',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->userId = auth()->user()->id;
|
|
||||||
$this->name = auth()->user()->name;
|
|
||||||
$this->email = auth()->user()->email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->validate();
|
|
||||||
User::where('id', $this->userId)->update([
|
|
||||||
'name' => $this->name,
|
|
||||||
]);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Advanced extends Component
|
|
||||||
{
|
|
||||||
public Application $application;
|
|
||||||
protected $rules = [
|
|
||||||
'application.settings.is_git_submodules_enabled' => 'boolean|required',
|
|
||||||
'application.settings.is_git_lfs_enabled' => 'boolean|required',
|
|
||||||
'application.settings.is_preview_deployments_enabled' => 'boolean|required',
|
|
||||||
'application.settings.is_auto_deploy_enabled' => 'boolean|required',
|
|
||||||
'application.settings.is_force_https_enabled' => 'boolean|required',
|
|
||||||
'application.settings.is_log_drain_enabled' => 'boolean|required',
|
|
||||||
'application.settings.is_gpu_enabled' => 'boolean|required',
|
|
||||||
'application.settings.gpu_driver' => 'string|required',
|
|
||||||
'application.settings.gpu_count' => 'string|required',
|
|
||||||
'application.settings.gpu_device_ids' => 'string|required',
|
|
||||||
'application.settings.gpu_options' => 'string|required',
|
|
||||||
];
|
|
||||||
public function instantSave()
|
|
||||||
{
|
|
||||||
if ($this->application->settings->is_log_drain_enabled) {
|
|
||||||
if (!$this->application->destination->server->isLogDrainEnabled()) {
|
|
||||||
$this->application->settings->is_log_drain_enabled = false;
|
|
||||||
$this->emit('error', 'Log drain is not enabled on this server.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->application->settings->is_force_https_enabled) {
|
|
||||||
$this->emit('resetDefaultLabels', false);
|
|
||||||
}
|
|
||||||
$this->application->settings->save();
|
|
||||||
$this->emit('success', 'Settings saved.');
|
|
||||||
}
|
|
||||||
public function submit() {
|
|
||||||
if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) {
|
|
||||||
$this->emit('error', 'You cannot set both GPU count and GPU device IDs.');
|
|
||||||
$this->application->settings->gpu_count = null;
|
|
||||||
$this->application->settings->gpu_device_ids = null;
|
|
||||||
$this->application->settings->save();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->application->settings->save();
|
|
||||||
$this->emit('success', 'Settings saved.');
|
|
||||||
}
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.application.advanced');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
|
||||||
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class DeploymentLogs extends Component
|
|
||||||
{
|
|
||||||
public ApplicationDeploymentQueue $application_deployment_queue;
|
|
||||||
public $isKeepAliveOn = true;
|
|
||||||
protected $listeners = ['refreshQueue'];
|
|
||||||
|
|
||||||
public function refreshQueue()
|
|
||||||
{
|
|
||||||
$this->application_deployment_queue->refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function polling()
|
|
||||||
{
|
|
||||||
$this->emit('deploymentFinished');
|
|
||||||
$this->application_deployment_queue->refresh();
|
|
||||||
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
|
|
||||||
$this->isKeepAliveOn = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Deployments extends Component
|
|
||||||
{
|
|
||||||
public Application $application;
|
|
||||||
public Array|Collection $deployments = [];
|
|
||||||
public int $deployments_count = 0;
|
|
||||||
public string $current_url;
|
|
||||||
public int $skip = 0;
|
|
||||||
public int $default_take = 40;
|
|
||||||
public bool $show_next = false;
|
|
||||||
public ?string $pull_request_id = null;
|
|
||||||
protected $queryString = ['pull_request_id'];
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->current_url = url()->current();
|
|
||||||
$this->show_pull_request_only();
|
|
||||||
$this->show_more();
|
|
||||||
}
|
|
||||||
private function show_pull_request_only() {
|
|
||||||
if ($this->pull_request_id) {
|
|
||||||
$this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private function show_more()
|
|
||||||
{
|
|
||||||
if (count($this->deployments) !== 0) {
|
|
||||||
$this->show_next = true;
|
|
||||||
if (count($this->deployments) < $this->default_take) {
|
|
||||||
$this->show_next = false;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reload_deployments()
|
|
||||||
{
|
|
||||||
$this->load_deployments();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function load_deployments(int|null $take = null)
|
|
||||||
{
|
|
||||||
if ($take) {
|
|
||||||
$this->skip = $this->skip + $take;
|
|
||||||
}
|
|
||||||
$take = $this->default_take;
|
|
||||||
|
|
||||||
['deployments' => $deployments, 'count' => $count] = $this->application->deployments($this->skip, $take);
|
|
||||||
$this->deployments = $deployments;
|
|
||||||
$this->deployments_count = $count;
|
|
||||||
$this->show_pull_request_only();
|
|
||||||
$this->show_more();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class General extends Component
|
|
||||||
{
|
|
||||||
public string $applicationId;
|
|
||||||
|
|
||||||
public Application $application;
|
|
||||||
public Collection $services;
|
|
||||||
public string $name;
|
|
||||||
public ?string $fqdn = null;
|
|
||||||
public string $git_repository;
|
|
||||||
public string $git_branch;
|
|
||||||
public ?string $git_commit_sha = null;
|
|
||||||
public string $build_pack;
|
|
||||||
public ?string $ports_exposes = null;
|
|
||||||
|
|
||||||
public $customLabels;
|
|
||||||
public bool $labelsChanged = false;
|
|
||||||
public bool $isConfigurationChanged = false;
|
|
||||||
|
|
||||||
public bool $is_static;
|
|
||||||
|
|
||||||
protected $listeners = [
|
|
||||||
'resetDefaultLabels'
|
|
||||||
];
|
|
||||||
protected $rules = [
|
|
||||||
'application.name' => 'required',
|
|
||||||
'application.description' => 'nullable',
|
|
||||||
'application.fqdn' => 'nullable',
|
|
||||||
'application.git_repository' => 'required',
|
|
||||||
'application.git_branch' => 'required',
|
|
||||||
'application.git_commit_sha' => 'nullable',
|
|
||||||
'application.install_command' => 'nullable',
|
|
||||||
'application.build_command' => 'nullable',
|
|
||||||
'application.start_command' => 'nullable',
|
|
||||||
'application.build_pack' => 'required',
|
|
||||||
'application.static_image' => 'required',
|
|
||||||
'application.base_directory' => 'required',
|
|
||||||
'application.publish_directory' => 'nullable',
|
|
||||||
'application.ports_exposes' => 'required',
|
|
||||||
'application.ports_mappings' => 'nullable',
|
|
||||||
'application.dockerfile' => 'nullable',
|
|
||||||
'application.docker_registry_image_name' => 'nullable',
|
|
||||||
'application.docker_registry_image_tag' => 'nullable',
|
|
||||||
'application.dockerfile_location' => 'nullable',
|
|
||||||
'application.custom_labels' => 'nullable',
|
|
||||||
'application.dockerfile_target_build' => 'nullable',
|
|
||||||
'application.settings.is_static' => 'boolean|required',
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'application.name' => 'name',
|
|
||||||
'application.description' => 'description',
|
|
||||||
'application.fqdn' => 'FQDN',
|
|
||||||
'application.git_repository' => 'Git repository',
|
|
||||||
'application.git_branch' => 'Git branch',
|
|
||||||
'application.git_commit_sha' => 'Git commit SHA',
|
|
||||||
'application.install_command' => 'Install command',
|
|
||||||
'application.build_command' => 'Build command',
|
|
||||||
'application.start_command' => 'Start command',
|
|
||||||
'application.build_pack' => 'Build pack',
|
|
||||||
'application.static_image' => 'Static image',
|
|
||||||
'application.base_directory' => 'Base directory',
|
|
||||||
'application.publish_directory' => 'Publish directory',
|
|
||||||
'application.ports_exposes' => 'Ports exposes',
|
|
||||||
'application.ports_mappings' => 'Ports mappings',
|
|
||||||
'application.dockerfile' => 'Dockerfile',
|
|
||||||
'application.docker_registry_image_name' => 'Docker registry image name',
|
|
||||||
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
|
||||||
'application.dockerfile_location' => 'Dockerfile location',
|
|
||||||
'application.custom_labels' => 'Custom labels',
|
|
||||||
'application.dockerfile_target_build' => 'Dockerfile target build',
|
|
||||||
'application.settings.is_static' => 'Is static',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->ports_exposes = $this->application->ports_exposes;
|
|
||||||
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
|
||||||
$this->application->isConfigurationChanged(true);
|
|
||||||
}
|
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
|
||||||
if (is_null(data_get($this->application, 'custom_labels'))) {
|
|
||||||
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
|
||||||
} else {
|
|
||||||
$this->customLabels = str($this->application->custom_labels)->replace(',', "\n");
|
|
||||||
}
|
|
||||||
$this->checkLabelUpdates();
|
|
||||||
}
|
|
||||||
public function instantSave()
|
|
||||||
{
|
|
||||||
$this->application->settings->save();
|
|
||||||
$this->emit('success', 'Settings saved.');
|
|
||||||
}
|
|
||||||
public function updatedApplicationBuildPack()
|
|
||||||
{
|
|
||||||
if ($this->application->build_pack !== 'nixpacks') {
|
|
||||||
$this->application->settings->is_static = $this->is_static = false;
|
|
||||||
$this->application->settings->save();
|
|
||||||
}
|
|
||||||
$this->submit();
|
|
||||||
}
|
|
||||||
public function checkLabelUpdates()
|
|
||||||
{
|
|
||||||
if (md5($this->application->custom_labels) !== md5(implode(",", generateLabelsApplication($this->application)))) {
|
|
||||||
$this->labelsChanged = true;
|
|
||||||
} else {
|
|
||||||
$this->labelsChanged = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getWildcardDomain()
|
|
||||||
{
|
|
||||||
$server = data_get($this->application, 'destination.server');
|
|
||||||
if ($server) {
|
|
||||||
$fqdn = generateFqdn($server, $this->application->uuid);
|
|
||||||
$this->application->fqdn = $fqdn;
|
|
||||||
$this->application->save();
|
|
||||||
$this->updatedApplicationFqdn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function resetDefaultLabels($showToaster = true)
|
|
||||||
{
|
|
||||||
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
|
||||||
$this->ports_exposes = $this->application->ports_exposes;
|
|
||||||
$this->submit($showToaster);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updatedApplicationFqdn()
|
|
||||||
{
|
|
||||||
$this->resetDefaultLabels(false);
|
|
||||||
$this->emit('success', 'Labels reseted to default!');
|
|
||||||
}
|
|
||||||
public function submit($showToaster = true)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->validate();
|
|
||||||
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
|
||||||
$this->resetDefaultLabels(false);
|
|
||||||
}
|
|
||||||
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
|
||||||
$this->validate([
|
|
||||||
'application.docker_registry_image_name' => 'required',
|
|
||||||
'application.docker_registry_image_tag' => 'required',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (data_get($this->application, 'fqdn')) {
|
|
||||||
$domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
|
||||||
return Str::of($domain)->trim()->lower();
|
|
||||||
});
|
|
||||||
$this->application->fqdn = $domains->implode(',');
|
|
||||||
}
|
|
||||||
if (data_get($this->application, 'dockerfile')) {
|
|
||||||
$port = get_port_from_dockerfile($this->application->dockerfile);
|
|
||||||
if ($port && !$this->application->ports_exposes) {
|
|
||||||
$this->application->ports_exposes = $port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->application->base_directory && $this->application->base_directory !== '/') {
|
|
||||||
$this->application->base_directory = rtrim($this->application->base_directory, '/');
|
|
||||||
}
|
|
||||||
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
|
||||||
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
|
||||||
}
|
|
||||||
if (gettype($this->customLabels) === 'string') {
|
|
||||||
$this->customLabels = str($this->customLabels)->replace(',', "\n");
|
|
||||||
}
|
|
||||||
$this->application->custom_labels = $this->customLabels->explode("\n")->implode(',');
|
|
||||||
$this->application->save();
|
|
||||||
$showToaster && $this->emit('success', 'Application settings updated!');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
} finally {
|
|
||||||
$this->checkLabelUpdates();
|
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
|
||||||
|
|
||||||
use App\Actions\Application\StopApplication;
|
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use App\Jobs\ServerStatusJob;
|
|
||||||
use App\Models\Application;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class Heading extends Component
|
|
||||||
{
|
|
||||||
public Application $application;
|
|
||||||
public array $parameters;
|
|
||||||
|
|
||||||
protected string $deploymentUuid;
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function check_status()
|
|
||||||
{
|
|
||||||
if ($this->application->destination->server->isFunctional()) {
|
|
||||||
dispatch(new ContainerStatusJob($this->application->destination->server));
|
|
||||||
$this->application->refresh();
|
|
||||||
$this->application->previews->each(function ($preview) {
|
|
||||||
$preview->refresh();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
dispatch(new ServerStatusJob($this->application->destination->server));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function force_deploy_without_cache()
|
|
||||||
{
|
|
||||||
$this->deploy(force_rebuild: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deploy(bool $force_rebuild = false)
|
|
||||||
{
|
|
||||||
$this->setDeploymentUuid();
|
|
||||||
queue_application_deployment(
|
|
||||||
application_id: $this->application->id,
|
|
||||||
deployment_uuid: $this->deploymentUuid,
|
|
||||||
force_rebuild: $force_rebuild,
|
|
||||||
);
|
|
||||||
return redirect()->route('project.application.deployment', [
|
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function setDeploymentUuid()
|
|
||||||
{
|
|
||||||
$this->deploymentUuid = new Cuid2(7);
|
|
||||||
$this->parameters['deployment_uuid'] = $this->deploymentUuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function stop()
|
|
||||||
{
|
|
||||||
StopApplication::run($this->application);
|
|
||||||
$this->application->status = 'exited';
|
|
||||||
$this->application->save();
|
|
||||||
$this->application->refresh();
|
|
||||||
}
|
|
||||||
public function restart() {
|
|
||||||
$this->setDeploymentUuid();
|
|
||||||
queue_application_deployment(
|
|
||||||
application_id: $this->application->id,
|
|
||||||
deployment_uuid: $this->deploymentUuid,
|
|
||||||
restart_only: true,
|
|
||||||
);
|
|
||||||
return redirect()->route('project.application.deployment', [
|
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
|
||||||
'application_uuid' => $this->parameters['application_uuid'],
|
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
|
||||||
'environment_name' => $this->parameters['environment_name'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project;
|
|
||||||
|
|
||||||
use App\Models\Project;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Edit extends Component
|
|
||||||
{
|
|
||||||
public Project $project;
|
|
||||||
protected $rules = [
|
|
||||||
'project.name' => 'required|min:3|max:255',
|
|
||||||
'project.description' => 'nullable|string|max:255',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->validate();
|
|
||||||
try {
|
|
||||||
$this->project->save();
|
|
||||||
$this->emit('saved');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\New;
|
|
||||||
|
|
||||||
use App\Models\EnvironmentVariable;
|
|
||||||
use App\Models\Project;
|
|
||||||
use App\Models\Service;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class DockerCompose extends Component
|
|
||||||
{
|
|
||||||
public string $dockerComposeRaw = '';
|
|
||||||
public string $envFile = '';
|
|
||||||
public array $parameters;
|
|
||||||
public array $query;
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
$this->query = request()->query();
|
|
||||||
if (isDev()) {
|
|
||||||
$this->dockerComposeRaw = 'services:
|
|
||||||
ghost:
|
|
||||||
image: ghost:5
|
|
||||||
volumes:
|
|
||||||
- ~/configs:/etc/configs/:ro
|
|
||||||
- ./var/lib/ghost/content:/tmp/ghost2/content:ro
|
|
||||||
- /var/lib/ghost/content:/tmp/ghost/content:rw
|
|
||||||
- ghost-content-data:/var/lib/ghost/content
|
|
||||||
- type: volume
|
|
||||||
source: mydata
|
|
||||||
target: /data
|
|
||||||
- type: bind
|
|
||||||
source: ./var/lib/ghost/data
|
|
||||||
target: /data
|
|
||||||
- type: bind
|
|
||||||
source: /tmp
|
|
||||||
target: /tmp
|
|
||||||
labels:
|
|
||||||
- "test.label=true"
|
|
||||||
ports:
|
|
||||||
- "3000"
|
|
||||||
- "3000-3005"
|
|
||||||
- "8000:8000"
|
|
||||||
- "9090-9091:8080-8081"
|
|
||||||
- "49100:22"
|
|
||||||
- "127.0.0.1:8001:8001"
|
|
||||||
- "127.0.0.1:5000-5010:5000-5010"
|
|
||||||
- "127.0.0.1::5000"
|
|
||||||
- "6060:6060/udp"
|
|
||||||
- "12400-12500:1240"
|
|
||||||
- target: 80
|
|
||||||
published: 8080
|
|
||||||
protocol: tcp
|
|
||||||
mode: host
|
|
||||||
networks:
|
|
||||||
- some-network
|
|
||||||
- other-network
|
|
||||||
environment:
|
|
||||||
- database__client=${DATABASE_CLIENT:-mysql}
|
|
||||||
- database__connection__database=${MYSQL_DATABASE:-ghost}
|
|
||||||
- database__connection__host=${DATABASE_CONNECTION_HOST:-mysql}
|
|
||||||
- test=${TEST:?true}
|
|
||||||
- url=$SERVICE_FQDN_GHOST
|
|
||||||
- database__connection__user=$SERVICE_USER_MYSQL
|
|
||||||
- database__connection__password=$SERVICE_PASSWORD_MYSQL
|
|
||||||
depends_on:
|
|
||||||
- mysql
|
|
||||||
mysql:
|
|
||||||
image: mysql:8.0
|
|
||||||
volumes:
|
|
||||||
- ghost-mysql-data:/var/lib/mysql
|
|
||||||
environment:
|
|
||||||
- MYSQL_USER=${SERVICE_USER_MYSQL}
|
|
||||||
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MYSQL}
|
|
||||||
- MYSQL_DATABASE=$MYSQL_DATABASE
|
|
||||||
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MYSQLROOT}
|
|
||||||
- SESSION_SECRET
|
|
||||||
minio:
|
|
||||||
image: minio/minio
|
|
||||||
environment:
|
|
||||||
RACK_ENV: development
|
|
||||||
A: $A
|
|
||||||
SHOW: ${SHOW}
|
|
||||||
SHOW1: ${SHOW2-show1}
|
|
||||||
SHOW2: ${SHOW3:-show2}
|
|
||||||
SHOW3: ${SHOW4?show3}
|
|
||||||
SHOW4: ${SHOW5:?show4}
|
|
||||||
SHOW5: ${SERVICE_USER_MINIO}
|
|
||||||
SHOW6: ${SERVICE_PASSWORD_MINIO}
|
|
||||||
SHOW7: ${SERVICE_PASSWORD_64_MINIO}
|
|
||||||
SHOW8: ${SERVICE_BASE64_64_MINIO}
|
|
||||||
SHOW9: ${SERVICE_BASE64_128_MINIO}
|
|
||||||
SHOW10: ${SERVICE_BASE64_MINIO}
|
|
||||||
SHOW11:
|
|
||||||
';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->validate([
|
|
||||||
'dockerComposeRaw' => 'required'
|
|
||||||
]);
|
|
||||||
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
|
||||||
$server_id = $this->query['server_id'];
|
|
||||||
|
|
||||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
|
||||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
|
||||||
$service = Service::create([
|
|
||||||
'name' => 'service' . Str::random(10),
|
|
||||||
'docker_compose_raw' => $this->dockerComposeRaw,
|
|
||||||
'environment_id' => $environment->id,
|
|
||||||
'server_id' => (int) $server_id,
|
|
||||||
]);
|
|
||||||
$variables = parseEnvFormatToArray($this->envFile);
|
|
||||||
foreach ($variables as $key => $variable) {
|
|
||||||
EnvironmentVariable::create([
|
|
||||||
'key' => $key,
|
|
||||||
'value' => $variable,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
'service_id' => $service->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$service->name = "service-$service->uuid";
|
|
||||||
|
|
||||||
$service->parse(isNew: true);
|
|
||||||
|
|
||||||
return redirect()->route('project.service', [
|
|
||||||
'service_uuid' => $service->uuid,
|
|
||||||
'environment_name' => $environment->name,
|
|
||||||
'project_uuid' => $project->uuid,
|
|
||||||
]);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class ComposeModal extends Component
|
|
||||||
{
|
|
||||||
public ?string $raw = null;
|
|
||||||
public ?string $actual = null;
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.compose-modal');
|
|
||||||
}
|
|
||||||
public function submit() {
|
|
||||||
$this->emit('warning', "Saving new docker compose...");
|
|
||||||
$this->emit('saveCompose', $this->raw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
|
||||||
|
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use App\Models\Service;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Index extends Component
|
|
||||||
{
|
|
||||||
public Service $service;
|
|
||||||
public $applications;
|
|
||||||
public $databases;
|
|
||||||
public array $parameters;
|
|
||||||
public array $query;
|
|
||||||
protected $listeners = ["refreshStacks", "checkStatus"];
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.index');
|
|
||||||
}
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
$this->query = request()->query();
|
|
||||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
|
||||||
$this->applications = $this->service->applications->sort();
|
|
||||||
$this->databases = $this->service->databases->sort();
|
|
||||||
}
|
|
||||||
public function checkStatus()
|
|
||||||
{
|
|
||||||
dispatch_sync(new ContainerStatusJob($this->service->server));
|
|
||||||
$this->refreshStacks();
|
|
||||||
}
|
|
||||||
public function refreshStacks()
|
|
||||||
{
|
|
||||||
$this->applications = $this->service->applications->sort();
|
|
||||||
$this->applications->each(function ($application) {
|
|
||||||
$application->refresh();
|
|
||||||
});
|
|
||||||
$this->databases = $this->service->databases->sort();
|
|
||||||
$this->databases->each(function ($database) {
|
|
||||||
$database->refresh();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Modal extends Component
|
|
||||||
{
|
|
||||||
public function checkStatus() {
|
|
||||||
$this->emit('checkStatus');
|
|
||||||
}
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.modal');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Service;
|
|
||||||
|
|
||||||
use App\Actions\Service\StartService;
|
|
||||||
use App\Actions\Service\StopService;
|
|
||||||
use App\Models\Service;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Navbar extends Component
|
|
||||||
{
|
|
||||||
public Service $service;
|
|
||||||
public array $parameters;
|
|
||||||
public array $query;
|
|
||||||
protected $listeners = ["checkStatus"];
|
|
||||||
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.service.navbar');
|
|
||||||
}
|
|
||||||
public function checkStatus() {
|
|
||||||
$this->service->refresh();
|
|
||||||
}
|
|
||||||
public function deploy()
|
|
||||||
{
|
|
||||||
$this->service->parse();
|
|
||||||
$activity = StartService::run($this->service);
|
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
|
||||||
}
|
|
||||||
public function stop(bool $forceCleanup = false)
|
|
||||||
{
|
|
||||||
StopService::run($this->service);
|
|
||||||
$this->service->refresh();
|
|
||||||
if ($forceCleanup) {
|
|
||||||
$this->emit('success', 'Force cleanup service successfully.');
|
|
||||||
} else {
|
|
||||||
$this->emit('success', 'Service stopped successfully.');
|
|
||||||
}
|
|
||||||
$this->emit('checkStatus');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
|
||||||
|
|
||||||
use App\Jobs\DeleteResourceJob;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class Danger extends Component
|
|
||||||
{
|
|
||||||
public $resource;
|
|
||||||
public array $parameters;
|
|
||||||
public ?string $modalId = null;
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->modalId = new Cuid2(7);
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
DeleteResourceJob::dispatchSync($this->resource);
|
|
||||||
return redirect()->route('project.resources', [
|
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
|
||||||
'environment_name' => $this->parameters['environment_name']
|
|
||||||
]);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Destination extends Component
|
|
||||||
{
|
|
||||||
public $resource;
|
|
||||||
public $servers = [];
|
|
||||||
public $additionalServers = [];
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
|
|
||||||
|
|
||||||
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class Show extends Component
|
|
||||||
{
|
|
||||||
public $parameters;
|
|
||||||
public ModelsEnvironmentVariable $env;
|
|
||||||
public ?string $modalId = null;
|
|
||||||
public bool $isDisabled = false;
|
|
||||||
public bool $isLocked = false;
|
|
||||||
public string $type;
|
|
||||||
|
|
||||||
protected $rules = [
|
|
||||||
'env.key' => 'required|string',
|
|
||||||
'env.value' => 'nullable',
|
|
||||||
'env.is_build_time' => 'required|boolean',
|
|
||||||
'env.is_shown_once' => 'required|boolean',
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'key' => 'Key',
|
|
||||||
'value' => 'Value',
|
|
||||||
'is_build_time' => 'Build Time',
|
|
||||||
'is_shown_once' => 'Shown Once',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->modalId = new Cuid2(7);
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
$this->checkEnvs();
|
|
||||||
}
|
|
||||||
public function checkEnvs()
|
|
||||||
{
|
|
||||||
$this->isDisabled = false;
|
|
||||||
if (str($this->env->key)->startsWith('SERVICE_FQDN') || str($this->env->key)->startsWith('SERVICE_URL')) {
|
|
||||||
$this->isDisabled = true;
|
|
||||||
}
|
|
||||||
if ($this->env->is_shown_once) {
|
|
||||||
$this->isLocked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function lock()
|
|
||||||
{
|
|
||||||
$this->env->is_shown_once = true;
|
|
||||||
$this->env->save();
|
|
||||||
$this->checkEnvs();
|
|
||||||
$this->emit('refreshEnvs');
|
|
||||||
}
|
|
||||||
public function instantSave()
|
|
||||||
{
|
|
||||||
$this->submit();
|
|
||||||
}
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->validate();
|
|
||||||
$this->env->save();
|
|
||||||
$this->emit('success', 'Environment variable updated successfully.');
|
|
||||||
$this->emit('refreshEnvs');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete()
|
|
||||||
{
|
|
||||||
$this->env->delete();
|
|
||||||
$this->emit('refreshEnvs');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class GetLogs extends Component
|
|
||||||
{
|
|
||||||
public string $outputs = '';
|
|
||||||
public string $errors = '';
|
|
||||||
public Server $server;
|
|
||||||
public ?string $container = null;
|
|
||||||
public ?bool $streamLogs = false;
|
|
||||||
public ?bool $showTimeStamps = true;
|
|
||||||
public int $numberOfLines = 100;
|
|
||||||
public function doSomethingWithThisChunkOfOutput($output)
|
|
||||||
{
|
|
||||||
$this->outputs .= removeAnsiColors($output);
|
|
||||||
}
|
|
||||||
public function instantSave()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public function getLogs($refresh = false)
|
|
||||||
{
|
|
||||||
if ($this->container) {
|
|
||||||
if ($this->showTimeStamps) {
|
|
||||||
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} -t {$this->container}");
|
|
||||||
} else {
|
|
||||||
$sshCommand = generateSshCommand($this->server, "docker logs -n {$this->numberOfLines} {$this->container}");
|
|
||||||
}
|
|
||||||
if ($refresh) {
|
|
||||||
$this->outputs = '';
|
|
||||||
}
|
|
||||||
Process::run($sshCommand, function (string $type, string $output) {
|
|
||||||
$this->doSomethingWithThisChunkOfOutput($output);
|
|
||||||
});
|
|
||||||
if ($this->showTimeStamps) {
|
|
||||||
$this->outputs = str($this->outputs)->split('/\n/')->sort(function ($a, $b) {
|
|
||||||
$a = explode(' ', $a);
|
|
||||||
$b = explode(' ', $b);
|
|
||||||
return $a[0] <=> $b[0];
|
|
||||||
})->join("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.shared.get-logs');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\Service;
|
|
||||||
use App\Models\StandaloneMariadb;
|
|
||||||
use App\Models\StandaloneMongodb;
|
|
||||||
use App\Models\StandaloneMysql;
|
|
||||||
use App\Models\StandalonePostgresql;
|
|
||||||
use App\Models\StandaloneRedis;
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Logs extends Component
|
|
||||||
{
|
|
||||||
public ?string $type = null;
|
|
||||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
|
|
||||||
public Server $server;
|
|
||||||
public ?string $container = null;
|
|
||||||
public $parameters;
|
|
||||||
public $query;
|
|
||||||
public $status;
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
$this->query = request()->query();
|
|
||||||
if (data_get($this->parameters, 'application_uuid')) {
|
|
||||||
$this->type = 'application';
|
|
||||||
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
|
||||||
$this->status = $this->resource->status;
|
|
||||||
$this->server = $this->resource->destination->server;
|
|
||||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
|
|
||||||
if ($containers->count() > 0) {
|
|
||||||
$this->container = data_get($containers[0], 'Names');
|
|
||||||
}
|
|
||||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
|
||||||
$this->type = 'database';
|
|
||||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
|
||||||
if (is_null($resource)) {
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->resource = $resource;
|
|
||||||
$this->status = $this->resource->status;
|
|
||||||
$this->server = $this->resource->destination->server;
|
|
||||||
$this->container = $this->resource->uuid;
|
|
||||||
} else if (data_get($this->parameters, 'service_uuid')) {
|
|
||||||
$this->type = 'service';
|
|
||||||
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
|
||||||
$this->status = $this->resource->status;
|
|
||||||
$this->server = $this->resource->server;
|
|
||||||
$this->container = data_get($this->parameters, 'service_name') . '-' . $this->resource->uuid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render()
|
|
||||||
{
|
|
||||||
return view('livewire.project.shared.logs');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared\Storages;
|
|
||||||
|
|
||||||
use Livewire\Component;
|
|
||||||
|
|
||||||
class Add extends Component
|
|
||||||
{
|
|
||||||
public $uuid;
|
|
||||||
public $parameters;
|
|
||||||
public string $name;
|
|
||||||
public string $mount_path;
|
|
||||||
public string|null $host_path = null;
|
|
||||||
|
|
||||||
protected $listeners = ['clearAddStorage' => 'clear'];
|
|
||||||
protected $rules = [
|
|
||||||
'name' => 'required|string',
|
|
||||||
'mount_path' => 'required|string',
|
|
||||||
'host_path' => 'string|nullable',
|
|
||||||
];
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'name' => 'name',
|
|
||||||
'mount_path' => 'mount',
|
|
||||||
'host_path' => 'host',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->parameters = get_route_parameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
$this->validate();
|
|
||||||
$name = $this->uuid . '-' . $this->name;
|
|
||||||
$this->emit('addNewVolume', [
|
|
||||||
'name' => $name,
|
|
||||||
'mount_path' => $this->mount_path,
|
|
||||||
'host_path' => $this->host_path,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clear()
|
|
||||||
{
|
|
||||||
$this->name = '';
|
|
||||||
$this->mount_path = '';
|
|
||||||
$this->host_path = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user