mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
581 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69ebff1a7a | ||
|
|
5d9cfc393e | ||
|
|
e2a256b31c | ||
|
|
4855af7e57 | ||
|
|
a664174c02 | ||
|
|
c19c13b4e2 | ||
|
|
266b99bc25 | ||
|
|
51ef24e1fb | ||
|
|
33d38ccf40 | ||
|
|
f470ebbbe0 | ||
|
|
11bd46b200 | ||
|
|
53f5674771 | ||
|
|
c53d88902c | ||
|
|
e342c4fd65 | ||
|
|
aab7bd5e28 | ||
|
|
3adefb9e49 | ||
|
|
c904441787 | ||
|
|
b7f79ae034 | ||
|
|
2d63fcdc7f | ||
|
|
c1d0cabcfb | ||
|
|
166419b13a | ||
|
|
cfc4d3acc7 | ||
|
|
13a0c2cf43 | ||
|
|
6ef6975432 | ||
|
|
2c40e93d3b | ||
|
|
a30ae4fb38 | ||
|
|
5b8785d1a9 | ||
|
|
f6f3364269 | ||
|
|
2f93f4450f | ||
|
|
2ad7c2b1ce | ||
|
|
6c848199ed | ||
|
|
76aab722b8 | ||
|
|
12290304c4 | ||
|
|
3a27d13c3e | ||
|
|
4f588ced96 | ||
|
|
e266c7cdec | ||
|
|
eedc3faba3 | ||
|
|
2e2c932f07 | ||
|
|
e4aed185a2 | ||
|
|
dddbe40bbe | ||
|
|
59d6818f70 | ||
|
|
7678cd47df | ||
|
|
b101fbacd4 | ||
|
|
a61a86dc3b | ||
|
|
0b3cde44c3 | ||
|
|
618d5d837c | ||
|
|
d234e8969d | ||
|
|
1be77b3fea | ||
|
|
6b302ab786 | ||
|
|
5831dd6196 | ||
|
|
3c623f13e2 | ||
|
|
da54c24e8d | ||
|
|
1e39c3d5ab | ||
|
|
6071412986 | ||
|
|
ba7148206a | ||
|
|
59c5b22e6c | ||
|
|
be7f2ad9c4 | ||
|
|
62295ef573 | ||
|
|
ceb9fcf3b6 | ||
|
|
60282f7b6c | ||
|
|
f14b0a3411 | ||
|
|
30af317bd9 | ||
|
|
95faa1c3ad | ||
|
|
423d31f227 | ||
|
|
fbb063030d | ||
|
|
1968726cfe | ||
|
|
fb280afe41 | ||
|
|
fd488a561a | ||
|
|
ab57a5d8ef | ||
|
|
f5ae222a6e | ||
|
|
5d95d8b79a | ||
|
|
fbb5f2ca2e | ||
|
|
16cbca36c1 | ||
|
|
24a578bedb | ||
|
|
36dc479772 | ||
|
|
83d6e488e4 | ||
|
|
a4d358d512 | ||
|
|
c0c197101d | ||
|
|
62e39ccc7f | ||
|
|
a88a016137 | ||
|
|
f4c8986ab3 | ||
|
|
e286eae53b | ||
|
|
bc3e59e4ef | ||
|
|
17ebc650c9 | ||
|
|
0ef386b4a8 | ||
|
|
26fce85bb0 | ||
|
|
2a079e3365 | ||
|
|
5fb5ed75c4 | ||
|
|
a2008fe9d1 | ||
|
|
3c96485e3d | ||
|
|
1c97d47ea0 | ||
|
|
8f8f5878dd | ||
|
|
6e6f39dc1f | ||
|
|
d2d1f984e1 | ||
|
|
d635e5dbae | ||
|
|
49c56524e1 | ||
|
|
6ced607f2a | ||
|
|
aaa2febef4 | ||
|
|
10e6eddcfe | ||
|
|
2639bf92ad | ||
|
|
59eae3a44e | ||
|
|
5aa8ccfcf4 | ||
|
|
aea7cc9638 | ||
|
|
c970907c73 | ||
|
|
38c6c1ee40 | ||
|
|
a6118f5daf | ||
|
|
b196c138d9 | ||
|
|
8f9949160c | ||
|
|
beae0b545f | ||
|
|
b8dd7704b3 | ||
|
|
d913be66e6 | ||
|
|
63de538879 | ||
|
|
1d733b2282 | ||
|
|
1d0ad51fdf | ||
|
|
83d00bbe3c | ||
|
|
c422b4dbcf | ||
|
|
767fd334dd | ||
|
|
972223f01b | ||
|
|
9318cac189 | ||
|
|
7aa991fd7c | ||
|
|
5c27f43b3d | ||
|
|
a2f4d4ed6d | ||
|
|
6aca2740fb | ||
|
|
cd13b5b83e | ||
|
|
758dbafbf1 | ||
|
|
f6663661df | ||
|
|
9666099408 | ||
|
|
d382af6860 | ||
|
|
4905454269 | ||
|
|
ed8bd37230 | ||
|
|
ec1a7aa893 | ||
|
|
62adf2c5dc | ||
|
|
3e4538de98 | ||
|
|
5a7b16ea5f | ||
|
|
aa7bc40f85 | ||
|
|
4fd83dc727 | ||
|
|
0e451f87a9 | ||
|
|
0b0ae55f0b | ||
|
|
40ec3d9753 | ||
|
|
9535c8df29 | ||
|
|
6ca1d36d5d | ||
|
|
f5d16c46cb | ||
|
|
725c3fd547 | ||
|
|
dcfcee1db6 | ||
|
|
9f8c44d96b | ||
|
|
5b74fd34f5 | ||
|
|
5a4c9422b2 | ||
|
|
81b916724e | ||
|
|
9540f60fa2 | ||
|
|
8eb1686125 | ||
|
|
daf3710a5e | ||
|
|
1067f37e4d | ||
|
|
3b3c0b94e5 | ||
|
|
8b0a0d67da | ||
|
|
5541c135df | ||
|
|
2552cb2208 | ||
|
|
f001e9bc34 | ||
|
|
f943fdc5be | ||
|
|
a4f1fcba58 | ||
|
|
68091b44fc | ||
|
|
9f8caac91c | ||
|
|
8082dc1a01 | ||
|
|
a71cf5bc66 | ||
|
|
0775074509 | ||
|
|
242d2fb283 | ||
|
|
5646818965 | ||
|
|
ffc5320940 | ||
|
|
7c10c55b1c | ||
|
|
7c96b6207a | ||
|
|
0be8ffbdc9 | ||
|
|
24fa56762e | ||
|
|
84d8e35411 | ||
|
|
3d3ccc435c | ||
|
|
be3b01472e | ||
|
|
de6f5b1105 | ||
|
|
14d9c06dcd | ||
|
|
8abfaa1967 | ||
|
|
46f7ae9588 | ||
|
|
f2c32b9aeb | ||
|
|
3dab1eb92e | ||
|
|
9c22e01716 | ||
|
|
d1c47a4062 | ||
|
|
6c3f97d9ae | ||
|
|
ebd8e2ce40 | ||
|
|
b650f3f754 | ||
|
|
f33ba40478 | ||
|
|
5cea9c4603 | ||
|
|
d32832fabc | ||
|
|
165f0a3d4a | ||
|
|
f14995200b | ||
|
|
12bb2ecc4a | ||
|
|
933ec5741d | ||
|
|
8004a40139 | ||
|
|
a6209fbe5c | ||
|
|
b47c327b55 | ||
|
|
25434a7acd | ||
|
|
0de042dbac | ||
|
|
eb9e2203b0 | ||
|
|
dcaa7a6ad7 | ||
|
|
5b584a6c6d | ||
|
|
c40ea6f1da | ||
|
|
61a7b9ac94 | ||
|
|
9e81416fef | ||
|
|
c58706e3e4 | ||
|
|
45bca8649b | ||
|
|
b095b88281 | ||
|
|
cb41584137 | ||
|
|
6659153804 | ||
|
|
44c429a224 | ||
|
|
fe9c501c1d | ||
|
|
4e94b4a0c1 | ||
|
|
2f4d7c0e43 | ||
|
|
e443fc394a | ||
|
|
5b56c50f03 | ||
|
|
3adeb2f73f | ||
|
|
9eaa13a08a | ||
|
|
df5a4a9667 | ||
|
|
26048339d6 | ||
|
|
d85af3fefc | ||
|
|
575338609b | ||
|
|
d32e43ef37 | ||
|
|
a96ef1bfab | ||
|
|
1bfedf69f2 | ||
|
|
208fe7d87b | ||
|
|
a6d58b5d72 | ||
|
|
277b4276e6 | ||
|
|
f6adc9285a | ||
|
|
f03bbe0e95 | ||
|
|
535375193c | ||
|
|
d79c063fd6 | ||
|
|
35f45492e3 | ||
|
|
ab8a7893d9 | ||
|
|
76b8d048d4 | ||
|
|
0e583334e7 | ||
|
|
0ad8ca224f | ||
|
|
050e56f69a | ||
|
|
6099ac11d9 | ||
|
|
adac728a60 | ||
|
|
e2e64e36a0 | ||
|
|
762af66cbf | ||
|
|
b08f525bd4 | ||
|
|
5ae16b195c | ||
|
|
91db1953ff | ||
|
|
1c8f92d3b7 | ||
|
|
4075572dbc | ||
|
|
2971e360d7 | ||
|
|
32bb2780f2 | ||
|
|
af69575b29 | ||
|
|
d4a7d0d25f | ||
|
|
45f9def0f6 | ||
|
|
5a90eed7ef | ||
|
|
38e1f17edf | ||
|
|
1651845e20 | ||
|
|
38e96548b5 | ||
|
|
47e4126dca | ||
|
|
e0b175ab07 | ||
|
|
93ec785f4f | ||
|
|
e849addab8 | ||
|
|
a5e6975dac | ||
|
|
4ac8e1cc67 | ||
|
|
1a5e3a7836 | ||
|
|
4498d1ed4b | ||
|
|
c8b974820b | ||
|
|
527373e297 | ||
|
|
a84be8dc33 | ||
|
|
bd856f7f67 | ||
|
|
8ff216e5fb | ||
|
|
5255311a2e | ||
|
|
774a245e84 | ||
|
|
194675c838 | ||
|
|
75862ca8de | ||
|
|
5580a4e704 | ||
|
|
51e601a303 | ||
|
|
734e9fd68d | ||
|
|
09fc950ae8 | ||
|
|
cf6caa279d | ||
|
|
68c976ab70 | ||
|
|
1560ab2a50 | ||
|
|
e3a6458506 | ||
|
|
1768b9374f | ||
|
|
51d0a30a6c | ||
|
|
9701c65297 | ||
|
|
58e3bb2571 | ||
|
|
31cbd1602d | ||
|
|
dd5723d596 | ||
|
|
620f26a6f1 | ||
|
|
540717e809 | ||
|
|
d446cd4103 | ||
|
|
7d1a76570c | ||
|
|
e18766ec21 | ||
|
|
3d0354cf7e | ||
|
|
af5b9fced1 | ||
|
|
ab5202515e | ||
|
|
f863db7ea5 | ||
|
|
3adc0bdd6e | ||
|
|
97027875bf | ||
|
|
fd9c13009f | ||
|
|
46a72fac47 | ||
|
|
5d6ee04991 | ||
|
|
d523becb29 | ||
|
|
41672f75d0 | ||
|
|
acd8541e68 | ||
|
|
ed6af777a4 | ||
|
|
05f162f4e8 | ||
|
|
5e0adc3777 | ||
|
|
7d06fc4403 | ||
|
|
0e1bcceb8e | ||
|
|
a922f2fedf | ||
|
|
1e0226c8ed | ||
|
|
5c45908087 | ||
|
|
aefdc76805 | ||
|
|
b3c8c881b7 | ||
|
|
9ab5a1f7bd | ||
|
|
23968e7886 | ||
|
|
390d24b6d7 | ||
|
|
e4296345b3 | ||
|
|
bcffbe418b | ||
|
|
bfbee4e78f | ||
|
|
2bdb44dac3 | ||
|
|
4daa1b8c16 | ||
|
|
febc399568 | ||
|
|
9b9d4f9941 | ||
|
|
f49b87870c | ||
|
|
ed49d4e3a0 | ||
|
|
7811c75139 | ||
|
|
068a1b4bc4 | ||
|
|
c618d912db | ||
|
|
3d43f2127a | ||
|
|
79fde593a9 | ||
|
|
64ce41df0f | ||
|
|
23e205b6cd | ||
|
|
4161ea7eb6 | ||
|
|
77037f8933 | ||
|
|
bdcc0c8de5 | ||
|
|
ac133875fa | ||
|
|
f4819a849b | ||
|
|
2cd1785a92 | ||
|
|
797352a5db | ||
|
|
03a3405524 | ||
|
|
925326b885 | ||
|
|
ffdf51a490 | ||
|
|
272d5547c8 | ||
|
|
fbaec769f0 | ||
|
|
d93640a8d9 | ||
|
|
3749970831 | ||
|
|
7d45bb1335 | ||
|
|
da04e0c027 | ||
|
|
c637d2d90d | ||
|
|
1bb8860f37 | ||
|
|
38a22dcf4d | ||
|
|
91e1eb7664 | ||
|
|
c7946e7551 | ||
|
|
958645e37f | ||
|
|
acdfa89ec1 | ||
|
|
62ec85ee2e | ||
|
|
8f24a66456 | ||
|
|
0dd0888775 | ||
|
|
5040ddea28 | ||
|
|
1a04a57c01 | ||
|
|
9b6c162224 | ||
|
|
e22c5d22f5 | ||
|
|
84e5b39830 | ||
|
|
cdb6964b0b | ||
|
|
df4ecd47a7 | ||
|
|
4a84c7238a | ||
|
|
2b3057e1b4 | ||
|
|
ae65172946 | ||
|
|
3a2d17bc05 | ||
|
|
4c1067cf36 | ||
|
|
b046a3e9f7 | ||
|
|
199881c596 | ||
|
|
99c8607ff4 | ||
|
|
e61fcc77f9 | ||
|
|
20dca179fb | ||
|
|
0f542c65ae | ||
|
|
d609fcaee1 | ||
|
|
fe8a7fc54f | ||
|
|
398f122593 | ||
|
|
f0abdcc2da | ||
|
|
c9a278b750 | ||
|
|
6990c593a4 | ||
|
|
8f54b51ecd | ||
|
|
3eb628b773 | ||
|
|
fabb97330a | ||
|
|
03c9793d11 | ||
|
|
a4320b7cee | ||
|
|
cbf9bc99ea | ||
|
|
0fbc382467 | ||
|
|
0f8ccac775 | ||
|
|
ee20c3339e | ||
|
|
0b11093d18 | ||
|
|
de8118b59d | ||
|
|
58522b59b7 | ||
|
|
356394c03d | ||
|
|
3e4db2f5b2 | ||
|
|
872981b8b4 | ||
|
|
51c468ae0b | ||
|
|
80a797aec8 | ||
|
|
cfdab13d77 | ||
|
|
4fc8988ff4 | ||
|
|
ab5619292e | ||
|
|
b02e5d3f27 | ||
|
|
5f0c9c3a31 | ||
|
|
ea6ec07a45 | ||
|
|
36be325d0d | ||
|
|
6138ddcac6 | ||
|
|
0509da6730 | ||
|
|
5b877b84c2 | ||
|
|
0c35726a8d | ||
|
|
e74899611b | ||
|
|
e9149e534d | ||
|
|
7cded7a36d | ||
|
|
ba74d55b4c | ||
|
|
a1d13fc14e | ||
|
|
cdb2a3a8e5 | ||
|
|
074c56edaa | ||
|
|
66bc03b4cd | ||
|
|
2dcb7fec05 | ||
|
|
249bccb49e | ||
|
|
a55eaa10ac | ||
|
|
ca2c75ce19 | ||
|
|
250d7cbc53 | ||
|
|
92a53a151e | ||
|
|
db3148c080 | ||
|
|
c46eeac4b5 | ||
|
|
19111ba059 | ||
|
|
9bc61a0a17 | ||
|
|
78b9166bdb | ||
|
|
1752448050 | ||
|
|
3fc78bc760 | ||
|
|
0c30b6222d | ||
|
|
3f74609c7f | ||
|
|
31b0ccba99 | ||
|
|
3fc544e0b9 | ||
|
|
67078fdc71 | ||
|
|
c91f426af3 | ||
|
|
9c2fea4b2e | ||
|
|
53d1fa0331 | ||
|
|
4ae7e46e81 | ||
|
|
ebfc0bd1e1 | ||
|
|
e1a1490911 | ||
|
|
6b75ff7de4 | ||
|
|
301469de6b | ||
|
|
b4d69a22df | ||
|
|
a86e971020 | ||
|
|
145af41c82 | ||
|
|
69c0b7240a | ||
|
|
6543132bcb | ||
|
|
9134437218 | ||
|
|
b7acf99cde | ||
|
|
21d52d7846 | ||
|
|
ab5929cc69 | ||
|
|
1452cdf5ad | ||
|
|
3eb1a1f48c | ||
|
|
5f7a97c31f | ||
|
|
9cba0a6df3 | ||
|
|
af57b2aa73 | ||
|
|
b9b9582601 | ||
|
|
c8ba98b93d | ||
|
|
a50e1e2f0c | ||
|
|
1093294f06 | ||
|
|
c023be2348 | ||
|
|
deece51e83 | ||
|
|
e2ab569244 | ||
|
|
93b202bde4 | ||
|
|
6bb6de188c | ||
|
|
61f58fa30f | ||
|
|
026f3fd72d | ||
|
|
9a706d55b4 | ||
|
|
9646969107 | ||
|
|
df433efe62 | ||
|
|
efa7cab3e2 | ||
|
|
a386a1bde5 | ||
|
|
cc1bf023be | ||
|
|
edb06fa233 | ||
|
|
abdb8074b1 | ||
|
|
157da798dd | ||
|
|
9c5501326e | ||
|
|
3ea462efc9 | ||
|
|
f77df5b732 | ||
|
|
b77074fe4e | ||
|
|
a7b7d3fa32 | ||
|
|
f4f3034389 | ||
|
|
4b2ffb456f | ||
|
|
e17ff99c5b | ||
|
|
1cf036bbc6 | ||
|
|
da4c2ee60f | ||
|
|
fcf7c5ddd5 | ||
|
|
27926322e1 | ||
|
|
c735ff545e | ||
|
|
b4f048b028 | ||
|
|
54a57d217f | ||
|
|
cf28490acc | ||
|
|
019670d5d1 | ||
|
|
b07cc500e7 | ||
|
|
82c235d5af | ||
|
|
307e4a6990 | ||
|
|
ddb7af63a6 | ||
|
|
7a429ee5bb | ||
|
|
bb591604ac | ||
|
|
81f7a65dd5 | ||
|
|
4b313bb1c6 | ||
|
|
aba0b2f13c | ||
|
|
9a284e47da | ||
|
|
9f2fbc661a | ||
|
|
949407368e | ||
|
|
93c65f6a79 | ||
|
|
d9fe16a3ee | ||
|
|
adaca4d4e3 | ||
|
|
a6d5f3038c | ||
|
|
4d49132821 | ||
|
|
c287276d0e | ||
|
|
e89868c692 | ||
|
|
f93317cd2e | ||
|
|
8412802f4d | ||
|
|
53c20e1e99 | ||
|
|
3c8c8e20b1 | ||
|
|
49f8abcd79 | ||
|
|
4a4d73b87b | ||
|
|
046eab3776 | ||
|
|
17c0e91a0d | ||
|
|
fe4a0ae166 | ||
|
|
52c84f8d22 | ||
|
|
b22fecb615 | ||
|
|
23b7fc3c54 | ||
|
|
1efb1235b4 | ||
|
|
0924070a13 | ||
|
|
12fc5a8f91 | ||
|
|
9eba058cf7 | ||
|
|
fa4f5fea8c | ||
|
|
898563fe7c | ||
|
|
c418a17161 | ||
|
|
cd0da04ea2 | ||
|
|
01e942c6a0 | ||
|
|
bb9abafa82 | ||
|
|
d0cd926517 | ||
|
|
9baf0161c7 | ||
|
|
8ba18b2ce1 | ||
|
|
ab021ee535 | ||
|
|
c76a1b1ba5 | ||
|
|
6266a5e500 | ||
|
|
5d27e89bfa | ||
|
|
6da4e78374 | ||
|
|
be30651172 | ||
|
|
95764c2b76 | ||
|
|
92a75685b5 | ||
|
|
a1592373aa | ||
|
|
5747a87f66 | ||
|
|
9cda671aef | ||
|
|
2c9983046c | ||
|
|
11d33f328e | ||
|
|
f79c741d95 | ||
|
|
8a39a4469a | ||
|
|
42daae10c6 | ||
|
|
64a65e2018 | ||
|
|
16c71f3647 | ||
|
|
363f525ad1 | ||
|
|
f4fb519d55 | ||
|
|
b7786504b8 | ||
|
|
f0adf10e6a | ||
|
|
cc8c6c5d16 | ||
|
|
4782446f42 | ||
|
|
230155312f | ||
|
|
3ab4365fca | ||
|
|
7349068b95 | ||
|
|
da6cc151d1 | ||
|
|
50527cf0a3 | ||
|
|
26f490bb00 | ||
|
|
dc4f412227 | ||
|
|
ec4234e243 | ||
|
|
f47fcb01ce | ||
|
|
02f6673345 | ||
|
|
e6cd8702b5 | ||
|
|
fda4ea8cca | ||
|
|
655d004ce7 | ||
|
|
56981d134c | ||
|
|
b9d49d2951 | ||
|
|
0c1e7c499e | ||
|
|
32fead5753 | ||
|
|
e5e9faba35 | ||
|
|
2852630d6c |
@@ -1,11 +1,3 @@
|
|||||||
############################################################################################################
|
|
||||||
# Development Environment
|
|
||||||
|
|
||||||
# User and group id for the user that will run the application inside the container
|
|
||||||
# Run in your terminal: `id -u` and `id -g` and that's the results
|
|
||||||
USERID=
|
|
||||||
GROUPID=
|
|
||||||
############################################################################################################
|
|
||||||
APP_NAME=Coolify-localhost
|
APP_NAME=Coolify-localhost
|
||||||
APP_ID=development
|
APP_ID=development
|
||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
@@ -13,6 +5,7 @@ APP_KEY=
|
|||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
APP_PORT=8000
|
APP_PORT=8000
|
||||||
|
MUX_ENABLED=false
|
||||||
|
|
||||||
DUSK_DRIVER_URL=http://selenium:4444
|
DUSK_DRIVER_URL=http://selenium:4444
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/coolify-helper-next.yml
vendored
2
.github/workflows/coolify-helper-next.yml
vendored
@@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ "next" ]
|
branches: [ "next" ]
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/coolify-helper.yml
|
- .github/workflows/coolify-helper-next.yml
|
||||||
- docker/coolify-helper/Dockerfile
|
- docker/coolify-helper/Dockerfile
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|||||||
2
.github/workflows/coolify-helper.yml
vendored
2
.github/workflows/coolify-helper.yml
vendored
@@ -77,7 +77,7 @@ jobs:
|
|||||||
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 }}:aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
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
|
- uses: sarisia/actions-status-discord@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/development-build.yml
vendored
2
.github/workflows/development-build.yml
vendored
@@ -13,7 +13,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
amd64:
|
amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, x64]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
|
|||||||
2
.github/workflows/production-build.yml
vendored
2
.github/workflows/production-build.yml
vendored
@@ -10,7 +10,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
amd64:
|
amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: [self-hosted, x64]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
|
|||||||
37
CONTRIBUTION.md
Normal file
37
CONTRIBUTION.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
> "First, thanks for considering to contribute to my project.
|
||||||
|
It really means a lot!" - [@andrasbacsai](https://github.com/andrasbacsai)
|
||||||
|
|
||||||
|
You can ask for guidance anytime on our
|
||||||
|
[Discord server](https://coollabs.io/discord) in the `#contribution` channel.
|
||||||
|
|
||||||
|
## Code Contribution
|
||||||
|
|
||||||
|
### 1) Setup your development environment
|
||||||
|
|
||||||
|
- You need to have Docker Engine (or equivalent) [installed](https://docs.docker.com/engine/install/) on your system.
|
||||||
|
- For better DX, install [Spin](https://serversideup.net/open-source/spin/).
|
||||||
|
|
||||||
|
### 2) Set your environment variables
|
||||||
|
|
||||||
|
- Copy [.env.development.example](./.env.development.example) to .env.
|
||||||
|
|
||||||
|
## 3) Start & setup Coolify
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
- 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
|
||||||
|
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||||
|
|
||||||
|
Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if you logged in with root user.
|
||||||
|
|
||||||
|
Mails are caught by Mailpit: `localhost:8025`
|
||||||
|
|
||||||
|
|
||||||
|
## New Service Contribution
|
||||||
|
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Verc
|
|||||||
|
|
||||||
It helps you to manage your servers, applications, databases on your own hardware, all you need is SSH connection. You can manage VPS, Bare Metal, Raspberry PI's anything.
|
It helps you to manage your servers, applications, databases on your own hardware, all you need is SSH connection. You can manage VPS, Bare Metal, Raspberry PI's anything.
|
||||||
|
|
||||||
Image if you could have the ease of a cloud but with your own servers. That is **Coolify**.
|
Imagine if you could have the ease of a cloud but with your own servers. That is **Coolify**.
|
||||||
|
|
||||||
No vendor lock-in, which means that all the configuration for your applications/databases/etc are saved to your server. So if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You just lose the automations and all the magic. 🪄️
|
No vendor lock-in, which means that all the configuration for your applications/databases/etc are saved to your server. So if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You just lose the automations and all the magic. 🪄️
|
||||||
|
|
||||||
@@ -36,10 +36,11 @@ You can find the installation script [here](./scripts/install.sh).
|
|||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Contact us [here](https://docs.coollabs.io/contact).
|
Contact us [here](https://coolify.io/docs/contact).
|
||||||
|
|
||||||
## Recognitions
|
## Recognitions
|
||||||
|
|
||||||
|
<p>
|
||||||
<a href="https://news.ycombinator.com/item?id=26624341">
|
<a href="https://news.ycombinator.com/item?id=26624341">
|
||||||
<img
|
<img
|
||||||
style="width: 250px; height: 54px;" width="250" height="54"
|
style="width: 250px; height: 54px;" width="250" height="54"
|
||||||
@@ -47,9 +48,12 @@ Contact us [here](https://docs.coollabs.io/contact).
|
|||||||
src="https://hackernews-badge.vercel.app/api?id=26624341"
|
src="https://hackernews-badge.vercel.app/api?id=26624341"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<a href="https://www.producthunt.com/posts/coolify?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An open-source & self-hostable Heroku, Netlify alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://www.producthunt.com/posts/coolify?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An open-source & self-hostable Heroku, Netlify alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
|
|
||||||
|
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
## 💰 Financial Contributors
|
## 💰 Financial Contributors
|
||||||
|
|
||||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
|
||||||
|
|||||||
30
app/Actions/Application/StopApplication.php
Normal file
30
app/Actions/Application/StopApplication.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Application;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Notifications\Application\StatusChanged;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StopApplication
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Application $application)
|
||||||
|
{
|
||||||
|
$server = $application->destination->server;
|
||||||
|
$containers = getCurrentApplicationContainerStatus($server, $application->id);
|
||||||
|
if ($containers->count() > 0) {
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$containerName = data_get($container, 'Names');
|
||||||
|
if ($containerName) {
|
||||||
|
instant_remote_process(
|
||||||
|
["docker rm -f {$containerName}"],
|
||||||
|
$server
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: make notification for application
|
||||||
|
// $application->environment->project->team->notify(new StatusChanged($application));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,14 +5,12 @@ namespace App\Actions\CoolifyTask;
|
|||||||
use App\Enums\ActivityTypes;
|
use App\Enums\ActivityTypes;
|
||||||
use App\Enums\ProcessStatus;
|
use App\Enums\ProcessStatus;
|
||||||
use App\Jobs\ApplicationDeploymentJob;
|
use App\Jobs\ApplicationDeploymentJob;
|
||||||
|
use App\Models\Server;
|
||||||
use Illuminate\Process\ProcessResult;
|
use Illuminate\Process\ProcessResult;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Process;
|
use Illuminate\Support\Facades\Process;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
|
||||||
const TIMEOUT = 3600;
|
|
||||||
const IDLE_TIMEOUT = 3600;
|
|
||||||
|
|
||||||
class RunRemoteProcess
|
class RunRemoteProcess
|
||||||
{
|
{
|
||||||
public Activity $activity;
|
public Activity $activity;
|
||||||
@@ -76,8 +74,7 @@ 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(...));
|
||||||
$processResult = Process::timeout(TIMEOUT)->idleTimeout(IDLE_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;
|
||||||
@@ -97,9 +94,8 @@ class RunRemoteProcess
|
|||||||
'status' => $status->value,
|
'status' => $status->value,
|
||||||
]);
|
]);
|
||||||
$this->activity->save();
|
$this->activity->save();
|
||||||
|
|
||||||
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
throw new \RuntimeException($processResult->errorOutput());
|
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $processResult;
|
return $processResult;
|
||||||
@@ -107,13 +103,11 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
protected function getCommand(): string
|
protected function getCommand(): string
|
||||||
{
|
{
|
||||||
$user = $this->activity->getExtraProperty('user');
|
$server_uuid = $this->activity->getExtraProperty('server_uuid');
|
||||||
$server_ip = $this->activity->getExtraProperty('server_ip');
|
|
||||||
$private_key_location = $this->activity->getExtraProperty('private_key_location');
|
|
||||||
$port = $this->activity->getExtraProperty('port');
|
|
||||||
$command = $this->activity->getExtraProperty('command');
|
$command = $this->activity->getExtraProperty('command');
|
||||||
|
$server = Server::whereUuid($server_uuid)->firstOrFail();
|
||||||
|
|
||||||
return generate_ssh_command($private_key_location, $server_ip, $user, $port, $command);
|
return generateSshCommand($server, $command);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function handleOutput(string $type, string $output)
|
protected function handleOutput(string $type, string $output)
|
||||||
|
|||||||
96
app/Actions/Database/StartDatabaseProxy.php
Normal file
96
app/Actions/Database/StartDatabaseProxy.php
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class StartDatabaseProxy
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $database)
|
||||||
|
{
|
||||||
|
$internalPort = null;
|
||||||
|
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
|
||||||
|
$internalPort = 6379;
|
||||||
|
} else if ($database->getMorphClass() === 'App\Models\StandalonePostgresql') {
|
||||||
|
$internalPort = 5432;
|
||||||
|
} else if ($database->getMorphClass() === 'App\Models\StandaloneMongodb') {
|
||||||
|
$internalPort = 27017;
|
||||||
|
}
|
||||||
|
$containerName = "{$database->uuid}-proxy";
|
||||||
|
$configuration_dir = database_proxy_dir($database->uuid);
|
||||||
|
$nginxconf = <<<EOF
|
||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
|
||||||
|
error_log /var/log/nginx/error.log;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
stream {
|
||||||
|
server {
|
||||||
|
listen $database->public_port;
|
||||||
|
proxy_pass $database->uuid:$internalPort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF;
|
||||||
|
$dockerfile = <<< EOF
|
||||||
|
FROM nginx:stable-alpine
|
||||||
|
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
EOF;
|
||||||
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
|
'services' => [
|
||||||
|
$containerName => [
|
||||||
|
'build' => [
|
||||||
|
'context' => $configuration_dir,
|
||||||
|
'dockerfile' => 'Dockerfile',
|
||||||
|
],
|
||||||
|
'image' => "nginx:stable-alpine",
|
||||||
|
'container_name' => $containerName,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'ports' => [
|
||||||
|
"$database->public_port:$database->public_port",
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$database->destination->network,
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => [
|
||||||
|
'CMD-SHELL',
|
||||||
|
'stat /etc/nginx/nginx.conf || exit 1',
|
||||||
|
],
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 3,
|
||||||
|
'start_period' => '1s'
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
||||||
|
$nginxconf_base64 = base64_encode($nginxconf);
|
||||||
|
$dockerfile_base64 = base64_encode($dockerfile);
|
||||||
|
instant_remote_process([
|
||||||
|
"mkdir -p $configuration_dir",
|
||||||
|
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||||
|
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
||||||
|
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
||||||
|
"docker compose --project-directory {$configuration_dir} up --build -d",
|
||||||
|
], $database->destination->server);
|
||||||
|
}
|
||||||
|
}
|
||||||
163
app/Actions/Database/StartMongodb.php
Normal file
163
app/Actions/Database/StartMongodb.php
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StartMongodb
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public StandaloneMongodb $database;
|
||||||
|
public array $commands = [];
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
public function handle(StandaloneMongodb $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
$startCommand = "mongod";
|
||||||
|
|
||||||
|
$container_name = $this->database->uuid;
|
||||||
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
|
$this->commands = [
|
||||||
|
"echo '####### Starting {$database->name}.'",
|
||||||
|
"mkdir -p $this->configuration_dir",
|
||||||
|
];
|
||||||
|
|
||||||
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
|
$environment_variables = $this->generate_environment_variables();
|
||||||
|
$this->add_custom_mongo_conf();
|
||||||
|
|
||||||
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
|
'services' => [
|
||||||
|
$container_name => [
|
||||||
|
'image' => $this->database->image,
|
||||||
|
'command' => $startCommand,
|
||||||
|
'container_name' => $container_name,
|
||||||
|
'environment' => $environment_variables,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network,
|
||||||
|
],
|
||||||
|
'labels' => [
|
||||||
|
'coolify.managed' => 'true',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => [
|
||||||
|
'CMD-SHELL',
|
||||||
|
'mongo --eval "printjson(db.serverStatus())" | grep uptime | grep -v grep'
|
||||||
|
],
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 10,
|
||||||
|
'start_period' => '5s'
|
||||||
|
],
|
||||||
|
'mem_limit' => $this->database->limits_memory,
|
||||||
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
|
'cpus' => $this->database->limits_cpus,
|
||||||
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $this->database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
|
}
|
||||||
|
if (count($persistent_storages) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
|
}
|
||||||
|
if (count($volume_names) > 0) {
|
||||||
|
$docker_compose['volumes'] = $volume_names;
|
||||||
|
}
|
||||||
|
if (!is_null($this->database->mongo_conf)) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
|
'type' => 'bind',
|
||||||
|
'source' => $this->configuration_dir . '/mongod.conf',
|
||||||
|
'target' => '/etc/mongo/mongod.conf',
|
||||||
|
'read_only' => true,
|
||||||
|
];
|
||||||
|
$docker_compose['services'][$container_name]['command'] = $startCommand . ' --config /etc/mongo/mongod.conf';
|
||||||
|
}
|
||||||
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo '####### {$database->name} started.'";
|
||||||
|
return remote_process($this->commands, $database->destination->server);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes_only_volume_names()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes_names = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes_names[$name] = [
|
||||||
|
'name' => $name,
|
||||||
|
'external' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_environment_variables()
|
||||||
|
{
|
||||||
|
$environment_variables = collect();
|
||||||
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
|
$environment_variables->push("$env->key=$env->value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
||||||
|
$environment_variables->push("MONGO_INITDB_ROOT_USERNAME={$this->database->mongo_initdb_root_username}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_PASSWORD'))->isEmpty()) {
|
||||||
|
$environment_variables->push("MONGO_INITDB_ROOT_PASSWORD={$this->database->mongo_initdb_root_password}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
|
||||||
|
$environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}");
|
||||||
|
}
|
||||||
|
return $environment_variables->all();
|
||||||
|
}
|
||||||
|
private function add_custom_mongo_conf()
|
||||||
|
{
|
||||||
|
if (is_null($this->database->mongo_conf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$filename = 'mongod.conf';
|
||||||
|
$content = $this->database->mongo_conf;
|
||||||
|
$content_base64 = base64_encode($content);
|
||||||
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,25 +2,28 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StartPostgresql
|
class StartPostgresql
|
||||||
{
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
public StandalonePostgresql $database;
|
public StandalonePostgresql $database;
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
public array $init_scripts = [];
|
public array $init_scripts = [];
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function __invoke(Server $server, StandalonePostgresql $database)
|
public function handle(StandalonePostgresql $database)
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
|
"echo '####### Starting {$database->name}.'",
|
||||||
"mkdir -p $this->configuration_dir",
|
"mkdir -p $this->configuration_dir",
|
||||||
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/"
|
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/"
|
||||||
];
|
];
|
||||||
@@ -40,6 +43,9 @@ class StartPostgresql
|
|||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network,
|
$this->database->destination->network,
|
||||||
],
|
],
|
||||||
|
'labels' => [
|
||||||
|
'coolify.managed' => 'true',
|
||||||
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => [
|
'test' => [
|
||||||
'CMD-SHELL',
|
'CMD-SHELL',
|
||||||
@@ -96,7 +102,8 @@ class StartPostgresql
|
|||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
return remote_process($this->commands, $server);
|
$this->commands[] = "echo '####### {$database->name} started.'";
|
||||||
|
return remote_process($this->commands, $database->destination->server);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
@@ -137,6 +144,9 @@ class StartPostgresql
|
|||||||
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()) {
|
||||||
$environment_variables->push("POSTGRES_USER={$this->database->postgres_user}");
|
$environment_variables->push("POSTGRES_USER={$this->database->postgres_user}");
|
||||||
}
|
}
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PGUSER'))->isEmpty()) {
|
||||||
|
$environment_variables->push("PGUSER={$this->database->postgres_user}");
|
||||||
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("POSTGRES_PASSWORD={$this->database->postgres_password}");
|
$environment_variables->push("POSTGRES_PASSWORD={$this->database->postgres_password}");
|
||||||
|
|||||||
159
app/Actions/Database/StartRedis.php
Normal file
159
app/Actions/Database/StartRedis.php
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StartRedis
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public StandaloneRedis $database;
|
||||||
|
public array $commands = [];
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
|
||||||
|
public function handle(StandaloneRedis $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
|
||||||
|
$startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
|
||||||
|
|
||||||
|
$container_name = $this->database->uuid;
|
||||||
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
|
$this->commands = [
|
||||||
|
"echo '####### Starting {$database->name}.'",
|
||||||
|
"mkdir -p $this->configuration_dir",
|
||||||
|
];
|
||||||
|
|
||||||
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
|
$environment_variables = $this->generate_environment_variables();
|
||||||
|
$this->add_custom_redis();
|
||||||
|
|
||||||
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
|
'services' => [
|
||||||
|
$container_name => [
|
||||||
|
'image' => $this->database->image,
|
||||||
|
'command' => $startCommand,
|
||||||
|
'container_name' => $container_name,
|
||||||
|
'environment' => $environment_variables,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network,
|
||||||
|
],
|
||||||
|
'labels' => [
|
||||||
|
'coolify.managed' => 'true',
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => [
|
||||||
|
'CMD-SHELL',
|
||||||
|
'redis-cli',
|
||||||
|
'ping'
|
||||||
|
],
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 10,
|
||||||
|
'start_period' => '5s'
|
||||||
|
],
|
||||||
|
'mem_limit' => $this->database->limits_memory,
|
||||||
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
|
'cpus' => $this->database->limits_cpus,
|
||||||
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $this->database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
|
}
|
||||||
|
if (count($persistent_storages) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
|
}
|
||||||
|
if (count($volume_names) > 0) {
|
||||||
|
$docker_compose['volumes'] = $volume_names;
|
||||||
|
}
|
||||||
|
if (!is_null($this->database->redis_conf)) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
|
'type' => 'bind',
|
||||||
|
'source' => $this->configuration_dir . '/redis.conf',
|
||||||
|
'target' => '/usr/local/etc/redis/redis.conf',
|
||||||
|
'read_only' => true,
|
||||||
|
];
|
||||||
|
$docker_compose['services'][$container_name]['command'] = $startCommand . ' /usr/local/etc/redis/redis.conf';
|
||||||
|
}
|
||||||
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo '####### {$database->name} started.'";
|
||||||
|
return remote_process($this->commands, $database->destination->server);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes_only_volume_names()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes_names = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes_names[$name] = [
|
||||||
|
'name' => $name,
|
||||||
|
'external' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_environment_variables()
|
||||||
|
{
|
||||||
|
$environment_variables = collect();
|
||||||
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
|
$environment_variables->push("$env->key=$env->value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||||
|
$environment_variables->push("REDIS_PASSWORD={$this->database->redis_password}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $environment_variables->all();
|
||||||
|
}
|
||||||
|
private function add_custom_redis()
|
||||||
|
{
|
||||||
|
if (is_null($this->database->redis_conf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$filename = 'redis.conf';
|
||||||
|
$content = $this->database->redis_conf;
|
||||||
|
$content_base64 = base64_encode($content);
|
||||||
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/Actions/Database/StopDatabase.php
Normal file
27
app/Actions/Database/StopDatabase.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StopDatabase
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $database)
|
||||||
|
{
|
||||||
|
$server = $database->destination->server;
|
||||||
|
instant_remote_process(
|
||||||
|
["docker rm -f {$database->uuid}"],
|
||||||
|
$server
|
||||||
|
);
|
||||||
|
if ($database->is_public) {
|
||||||
|
StopDatabaseProxy::run($database);
|
||||||
|
}
|
||||||
|
// TODO: make notification for services
|
||||||
|
// $database->environment->project->team->notify(new StatusChanged($database));
|
||||||
|
}
|
||||||
|
}
|
||||||
20
app/Actions/Database/StopDatabaseProxy.php
Normal file
20
app/Actions/Database/StopDatabaseProxy.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StopDatabaseProxy
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $database)
|
||||||
|
{
|
||||||
|
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server);
|
||||||
|
$database->is_public = false;
|
||||||
|
$database->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,6 +58,11 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
'password' => Hash::make($input['password']),
|
'password' => Hash::make($input['password']),
|
||||||
]);
|
]);
|
||||||
$team = $user->teams()->first();
|
$team = $user->teams()->first();
|
||||||
|
if (isCloud()) {
|
||||||
|
$user->sendVerificationEmail();
|
||||||
|
} else {
|
||||||
|
$user->markEmailAsVerified();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Set session variable
|
// Set session variable
|
||||||
session(['currentTeam' => $user->currentTeam = $team]);
|
session(['currentTeam' => $user->currentTeam = $team]);
|
||||||
|
|||||||
@@ -57,13 +57,13 @@ class CheckResaleLicense
|
|||||||
throw new \Exception('Invalid license key.');
|
throw new \Exception('Invalid license key.');
|
||||||
}
|
}
|
||||||
throw new \Exception('Cannot activate license key.');
|
throw new \Exception('Cannot activate license key.');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
ray($th);
|
ray($e);
|
||||||
$settings->update([
|
$settings->update([
|
||||||
'resale_license' => null,
|
'resale_license' => null,
|
||||||
'is_resale_license_active' => false,
|
'is_resale_license_active' => false,
|
||||||
]);
|
]);
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,24 +2,27 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class CheckConfigurationSync
|
class CheckConfiguration
|
||||||
{
|
{
|
||||||
public function __invoke(Server $server, bool $reset = false)
|
use AsAction;
|
||||||
|
public function handle(Server $server, bool $reset = false)
|
||||||
{
|
{
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
$proxy_configuration = instant_remote_process([
|
$proxy_configuration = instant_remote_process([
|
||||||
|
"mkdir -p $proxy_path",
|
||||||
"cat $proxy_path/docker-compose.yml",
|
"cat $proxy_path/docker-compose.yml",
|
||||||
], $server, false);
|
], $server, false);
|
||||||
|
|
||||||
if ($reset || is_null($proxy_configuration)) {
|
if ($reset || !$proxy_configuration || is_null($proxy_configuration)) {
|
||||||
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
||||||
resolve(SaveConfigurationSync::class)($server, $proxy_configuration);
|
|
||||||
return $proxy_configuration;
|
|
||||||
}
|
}
|
||||||
|
if (!$proxy_configuration || is_null($proxy_configuration)) {
|
||||||
|
throw new \Exception("Could not generate proxy configuration");
|
||||||
|
}
|
||||||
return $proxy_configuration;
|
return $proxy_configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
51
app/Actions/Proxy/CheckProxy.php
Normal file
51
app/Actions/Proxy/CheckProxy.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class CheckProxy
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Server $server, $fromUI = false)
|
||||||
|
{
|
||||||
|
if (!$server->isProxyShouldRun()) {
|
||||||
|
if ($fromUI) {
|
||||||
|
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$status = getContainerStatus($server, 'coolify-proxy');
|
||||||
|
if ($status === 'running') {
|
||||||
|
$server->proxy->set('status', 'running');
|
||||||
|
$server->save();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$ip = $server->ip;
|
||||||
|
if ($server->id === 0) {
|
||||||
|
$ip = 'host.docker.internal';
|
||||||
|
}
|
||||||
|
|
||||||
|
$connection80 = @fsockopen($ip, '80');
|
||||||
|
$connection443 = @fsockopen($ip, '443');
|
||||||
|
$port80 = is_resource($connection80) && fclose($connection80);
|
||||||
|
$port443 = is_resource($connection443) && fclose($connection443);
|
||||||
|
if ($port80) {
|
||||||
|
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>");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($port443) {
|
||||||
|
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>");
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/Actions/Proxy/SaveConfiguration.php
Normal file
29
app/Actions/Proxy/SaveConfiguration.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class SaveConfiguration
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public function handle(Server $server, ?string $proxy_settings = null)
|
||||||
|
{
|
||||||
|
if (is_null($proxy_settings)) {
|
||||||
|
$proxy_settings = CheckConfiguration::run($server, true);
|
||||||
|
}
|
||||||
|
$proxy_path = get_proxy_path();
|
||||||
|
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
||||||
|
|
||||||
|
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
|
$server->save();
|
||||||
|
|
||||||
|
return instant_remote_process([
|
||||||
|
"mkdir -p $proxy_path",
|
||||||
|
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
||||||
|
], $server);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Proxy;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class SaveConfigurationSync
|
|
||||||
{
|
|
||||||
public function __invoke(Server $server, string $configuration)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$proxy_path = get_proxy_path();
|
|
||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
|
||||||
|
|
||||||
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
|
||||||
$server->save();
|
|
||||||
|
|
||||||
instant_remote_process([
|
|
||||||
"mkdir -p $proxy_path",
|
|
||||||
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
|
||||||
], $server);
|
|
||||||
} catch (\Throwable $th) {
|
|
||||||
ray($th);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,49 +2,55 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
use App\Enums\ProxyStatus;
|
|
||||||
use App\Enums\ProxyTypes;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
|
||||||
class StartProxy
|
class StartProxy
|
||||||
{
|
{
|
||||||
public function __invoke(Server $server): Activity
|
use AsAction;
|
||||||
|
public function handle(Server $server, bool $async = true): string|Activity
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
|
$proxyType = $server->proxyType();
|
||||||
|
$commands = collect([]);
|
||||||
$proxy_path = get_proxy_path();
|
$proxy_path = get_proxy_path();
|
||||||
$networks = collect($server->standaloneDockers)->map(function ($docker) {
|
$configuration = CheckConfiguration::run($server);
|
||||||
return $docker['network'];
|
if (!$configuration) {
|
||||||
})->unique();
|
throw new \Exception("Configuration is not synced");
|
||||||
if ($networks->count() === 0) {
|
|
||||||
$networks = collect(['coolify']);
|
|
||||||
}
|
}
|
||||||
$create_networks_command = $networks->map(function ($network) {
|
SaveConfiguration::run($server, $configuration);
|
||||||
return "docker network ls --format '{{.Name}}' | grep '^$network$' >/dev/null 2>&1 || docker network create --attachable $network > /dev/null 2>&1";
|
|
||||||
});
|
|
||||||
|
|
||||||
$configuration = resolve(CheckConfigurationSync::class)($server);
|
|
||||||
|
|
||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
$server->save();
|
$server->save();
|
||||||
|
$commands = $commands->merge([
|
||||||
$activity = remote_process([
|
"mkdir -p $proxy_path && cd $proxy_path",
|
||||||
"echo 'Creating required Docker networks...'",
|
"echo 'Creating required Docker Compose file.'",
|
||||||
...$create_networks_command,
|
"echo 'Pulling docker image.'",
|
||||||
"cd $proxy_path",
|
'docker compose pull',
|
||||||
"echo 'Creating Docker Compose file...'",
|
"echo 'Stopping existing coolify-proxy.'",
|
||||||
"echo 'Pulling docker image...'",
|
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||||
'docker compose pull -q',
|
"echo 'Starting coolify-proxy.'",
|
||||||
"echo 'Stopping existing proxy...'",
|
|
||||||
'docker compose down -v --remove-orphans',
|
|
||||||
"lsof -nt -i:80 | xargs -r kill -9",
|
|
||||||
"lsof -nt -i:443 | xargs -r kill -9",
|
|
||||||
"echo 'Starting proxy...'",
|
|
||||||
'docker compose up -d --remove-orphans',
|
'docker compose up -d --remove-orphans',
|
||||||
"echo 'Proxy installed successfully...'"
|
"echo 'Proxy started successfully.'"
|
||||||
], $server);
|
]);
|
||||||
|
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||||
|
if ($async) {
|
||||||
|
$activity = remote_process($commands, $server);
|
||||||
return $activity;
|
return $activity;
|
||||||
|
} else {
|
||||||
|
instant_remote_process($commands, $server);
|
||||||
|
$server->proxy->set('status', 'running');
|
||||||
|
$server->proxy->set('type', $proxyType);
|
||||||
|
$server->save();
|
||||||
|
return 'OK';
|
||||||
|
}
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use App\Models\Team;
|
|
||||||
|
|
||||||
class InstallDocker
|
class InstallDocker
|
||||||
{
|
{
|
||||||
public function __invoke(Server $server, Team $team)
|
use AsAction;
|
||||||
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
$dockerVersion = '24.0';
|
$dockerVersion = '24.0';
|
||||||
$config = base64_encode('{
|
$config = base64_encode('{
|
||||||
@@ -18,42 +19,44 @@ class InstallDocker
|
|||||||
"max-file": "3"
|
"max-file": "3"
|
||||||
}
|
}
|
||||||
}');
|
}');
|
||||||
if (isDev()) {
|
|
||||||
$activity = remote_process([
|
|
||||||
"echo ####### Installing Prerequisites...",
|
|
||||||
"echo ####### Installing/updating Docker Engine...",
|
|
||||||
"echo ####### Configuring Docker Engine (merging existing configuration with the required)...",
|
|
||||||
"echo ####### Restarting Docker Engine...",
|
|
||||||
], $server);
|
|
||||||
} else {
|
|
||||||
$activity = remote_process([
|
|
||||||
"echo ####### Installing Prerequisites...",
|
|
||||||
"command -v jq >/dev/null || apt-get update",
|
|
||||||
"command -v jq >/dev/null || apt install -y jq",
|
|
||||||
"echo ####### Installing/updating Docker Engine...",
|
|
||||||
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh",
|
|
||||||
"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",
|
|
||||||
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
|
||||||
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
|
|
||||||
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
|
|
||||||
"echo ####### Restarting Docker Engine...",
|
|
||||||
"systemctl restart docker",
|
|
||||||
"echo ####### Creating default network...",
|
|
||||||
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
|
||||||
"echo ####### Done!"
|
|
||||||
], $server);
|
|
||||||
$found = StandaloneDocker::where('server_id', $server->id);
|
$found = StandaloneDocker::where('server_id', $server->id);
|
||||||
if ($found->count() == 0) {
|
if ($found->count() == 0 && $server->id) {
|
||||||
StandaloneDocker::create([
|
StandaloneDocker::create([
|
||||||
'name' => 'coolify',
|
'name' => 'coolify',
|
||||||
'network' => 'coolify',
|
'network' => 'coolify',
|
||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDev() && $server->id === 0) {
|
||||||
|
$command = [
|
||||||
|
"echo '####### Installing Prerequisites...'",
|
||||||
|
"sleep 1",
|
||||||
|
"echo '####### Installing/updating Docker Engine...'",
|
||||||
|
"echo '####### Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||||
|
"sleep 4",
|
||||||
|
"echo '####### Restarting Docker Engine...'",
|
||||||
|
"ls -l /tmp"
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$command = [
|
||||||
|
"echo '####### Installing Prerequisites...'",
|
||||||
|
"command -v jq >/dev/null || apt-get update",
|
||||||
|
"command -v jq >/dev/null || apt install -y jq",
|
||||||
|
"echo '####### Installing/updating Docker Engine...'",
|
||||||
|
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh",
|
||||||
|
"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",
|
||||||
|
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
||||||
|
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
|
||||||
|
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
|
||||||
|
"echo '####### Restarting Docker Engine...'",
|
||||||
|
"systemctl restart docker",
|
||||||
|
"echo '####### Creating default Docker network (coolify)...'",
|
||||||
|
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
||||||
|
"echo '####### Done!'"
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
return remote_process($command, $server);
|
||||||
|
|
||||||
return $activity;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,23 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
|
||||||
class UpdateCoolify
|
class UpdateCoolify
|
||||||
{
|
{
|
||||||
|
use AsAction;
|
||||||
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 function __invoke(bool $force)
|
public function handle(bool $force)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
ray('Running InstanceAutoUpdateJob');
|
ray('Running InstanceAutoUpdateJob');
|
||||||
$localhost_name = 'localhost';
|
$this->server = Server::find(0)->first();
|
||||||
$this->server = Server::where('name', $localhost_name)->first();
|
|
||||||
if (!$this->server) {
|
if (!$this->server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -43,11 +44,11 @@ class UpdateCoolify
|
|||||||
$this->update();
|
$this->update();
|
||||||
}
|
}
|
||||||
send_internal_notification('InstanceAutoUpdateJob done to version: ' . $this->latestVersion . ' from version: ' . $this->currentVersion);
|
send_internal_notification('InstanceAutoUpdateJob done to version: ' . $this->latestVersion . ' from version: ' . $this->currentVersion);
|
||||||
} catch (\Exception $th) {
|
} catch (\Throwable $e) {
|
||||||
ray('InstanceAutoUpdateJob failed');
|
ray('InstanceAutoUpdateJob failed');
|
||||||
ray($th->getMessage());
|
ray($e->getMessage());
|
||||||
send_internal_notification('InstanceAutoUpdateJob failed: ' . $th->getMessage());
|
send_internal_notification('InstanceAutoUpdateJob failed: ' . $e->getMessage());
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
34
app/Actions/Service/StartService.php
Normal file
34
app/Actions/Service/StartService.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class StartService
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Service $service)
|
||||||
|
{
|
||||||
|
$network = $service->destination->network;
|
||||||
|
$service->saveComposeConfigs();
|
||||||
|
$commands[] = "cd " . $service->workdir();
|
||||||
|
$commands[] = "echo '####### Saved configuration files to {$service->workdir()}.'";
|
||||||
|
$commands[] = "echo '####### Creating Docker network.'";
|
||||||
|
$commands[] = "docker network create --attachable {$service->uuid} >/dev/null 2>/dev/null || true";
|
||||||
|
$commands[] = "echo '####### Starting service {$service->name} on {$service->server->name}.'";
|
||||||
|
$commands[] = "echo '####### Pulling images.'";
|
||||||
|
$commands[] = "docker compose pull";
|
||||||
|
$commands[] = "echo '####### Starting containers.'";
|
||||||
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate";
|
||||||
|
$commands[] = "docker network connect $service->uuid coolify-proxy 2>/dev/null || true";
|
||||||
|
$compose = data_get($service,'docker_compose',[]);
|
||||||
|
$serviceNames = data_get(Yaml::parse($compose),'services',[]);
|
||||||
|
foreach($serviceNames as $serviceName => $serviceConfig){
|
||||||
|
$commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} $network {$serviceName}-{$service->uuid} 2>/dev/null || true";
|
||||||
|
}
|
||||||
|
$activity = remote_process($commands, $service->server);
|
||||||
|
return $activity;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/Actions/Service/StopService.php
Normal file
29
app/Actions/Service/StopService.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Notifications\Application\StatusChanged;
|
||||||
|
|
||||||
|
class StopService
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
public function handle(Service $service)
|
||||||
|
{
|
||||||
|
$applications = $service->applications()->get();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server);
|
||||||
|
$application->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
$dbs = $service->databases()->get();
|
||||||
|
foreach ($dbs as $db) {
|
||||||
|
instant_remote_process(["docker rm -f {$db->name}-{$service->uuid}"], $service->server);
|
||||||
|
$db->update(['status' => 'exited']);
|
||||||
|
}
|
||||||
|
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy 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
|
||||||
|
// $service->environment->project->team->notify(new StatusChanged($service));
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Console/Commands/Cloud.php
Normal file
33
app/Console/Commands/Cloud.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class Cloud extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'cloud:unused-servers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Get Unused Servers from Cloud';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended',true)->each(function($server){
|
||||||
|
$this->info($server->name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
280
app/Console/Commands/Emails.php
Normal file
280
app/Console/Commands/Emails.php
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\SendConfirmationForWaitlistJob;
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Models\TeamInvitation;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Waitlist;
|
||||||
|
use App\Notifications\Application\DeploymentFailed;
|
||||||
|
use App\Notifications\Application\DeploymentSuccess;
|
||||||
|
use App\Notifications\Application\StatusChanged;
|
||||||
|
use App\Notifications\Database\BackupFailed;
|
||||||
|
use App\Notifications\Database\BackupSuccess;
|
||||||
|
use App\Notifications\Test;
|
||||||
|
use App\Notifications\TransactionalEmails\InvitationLink;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Mail\Message;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Mail;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\confirm;
|
||||||
|
use function Laravel\Prompts\select;
|
||||||
|
use function Laravel\Prompts\text;
|
||||||
|
|
||||||
|
class Emails extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'emails';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Send out test / prod emails';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
private ?MailMessage $mail = null;
|
||||||
|
private ?string $email = null;
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$type = select(
|
||||||
|
'Which Email should be sent?',
|
||||||
|
options: [
|
||||||
|
'updates' => 'Send Update Email to all users',
|
||||||
|
'emails-test' => 'Test',
|
||||||
|
'application-deployment-success' => 'Application - Deployment Success',
|
||||||
|
'application-deployment-failed' => 'Application - Deployment Failed',
|
||||||
|
'application-status-changed' => 'Application - Status Changed',
|
||||||
|
'backup-success' => 'Database - Backup Success',
|
||||||
|
'backup-failed' => 'Database - Backup Failed',
|
||||||
|
// 'invitation-link' => 'Invitation Link',
|
||||||
|
'waitlist-invitation-link' => 'Waitlist Invitation Link',
|
||||||
|
'waitlist-confirmation' => 'Waitlist Confirmation',
|
||||||
|
'realusers-before-trial' => 'REAL - Registered Users Before Trial without Subscription',
|
||||||
|
'realusers-server-lost-connection' => 'REAL - Server Lost Connection',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
||||||
|
if (!in_array($type, $emailsGathered)) {
|
||||||
|
$this->email = text('Email Address to send to');
|
||||||
|
}
|
||||||
|
set_transanctional_email_settings();
|
||||||
|
|
||||||
|
$this->mail = new MailMessage();
|
||||||
|
$this->mail->subject("Test Email");
|
||||||
|
switch ($type) {
|
||||||
|
case 'updates':
|
||||||
|
$teams = Team::all();
|
||||||
|
if (!$teams || $teams->isEmpty()) {
|
||||||
|
echo 'No teams found.' . PHP_EOL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$emails = [];
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
foreach ($team->members as $member) {
|
||||||
|
if ($member->email && $member->marketing_emails) {
|
||||||
|
$emails[] = $member->email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$emails = array_unique($emails);
|
||||||
|
$this->info("Sending to " . count($emails) . " emails.");
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$this->info($email);
|
||||||
|
}
|
||||||
|
$confirmed = confirm('Are you sure?');
|
||||||
|
if ($confirmed) {
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$this->mail = new MailMessage();
|
||||||
|
$this->mail->subject('One-click Services, Docker Compose support');
|
||||||
|
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
||||||
|
'token' => encrypt($email),
|
||||||
|
]);
|
||||||
|
$this->mail->view('emails.updates',["unsubscribeUrl" => $unsubscribeUrl]);
|
||||||
|
$this->sendEmail($email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'emails-test':
|
||||||
|
$this->mail = (new Test())->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
break;
|
||||||
|
case 'application-deployment-success':
|
||||||
|
$application = Application::all()->first();
|
||||||
|
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
break;
|
||||||
|
case 'application-deployment-failed':
|
||||||
|
$application = Application::all()->first();
|
||||||
|
$preview = ApplicationPreview::all()->first();
|
||||||
|
if (!$preview) {
|
||||||
|
$preview = ApplicationPreview::create([
|
||||||
|
'application_id' => $application->id,
|
||||||
|
'pull_request_id' => 1,
|
||||||
|
'pull_request_html_url' => 'http://example.com',
|
||||||
|
'fqdn' => $application->fqdn,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->mail = (new DeploymentFailed($application, 'test'))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
$this->mail = (new DeploymentFailed($application, 'test', $preview))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
break;
|
||||||
|
case 'application-status-changed':
|
||||||
|
$application = Application::all()->first();
|
||||||
|
$this->mail = (new StatusChanged($application))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
break;
|
||||||
|
case 'backup-failed':
|
||||||
|
$backup = ScheduledDatabaseBackup::all()->first();
|
||||||
|
$db = StandalonePostgresql::all()->first();
|
||||||
|
if (!$backup) {
|
||||||
|
$backup = ScheduledDatabaseBackup::create([
|
||||||
|
'enabled' => true,
|
||||||
|
'frequency' => 'daily',
|
||||||
|
'save_s3' => false,
|
||||||
|
'database_id' => $db->id,
|
||||||
|
'database_type' => $db->getMorphClass(),
|
||||||
|
'team_id' => 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$output = 'Because of an error, the backup of the database ' . $db->name . ' failed.';
|
||||||
|
$this->mail = (new BackupFailed($backup, $db, $output))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
break;
|
||||||
|
case 'backup-success':
|
||||||
|
$backup = ScheduledDatabaseBackup::all()->first();
|
||||||
|
$db = StandalonePostgresql::all()->first();
|
||||||
|
if (!$backup) {
|
||||||
|
$backup = ScheduledDatabaseBackup::create([
|
||||||
|
'enabled' => true,
|
||||||
|
'frequency' => 'daily',
|
||||||
|
'save_s3' => false,
|
||||||
|
'database_id' => $db->id,
|
||||||
|
'database_type' => $db->getMorphClass(),
|
||||||
|
'team_id' => 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->mail = (new BackupSuccess($backup, $db))->toMail();
|
||||||
|
$this->sendEmail();
|
||||||
|
break;
|
||||||
|
// case 'invitation-link':
|
||||||
|
// $user = User::all()->first();
|
||||||
|
// $invitation = TeamInvitation::whereEmail($user->email)->first();
|
||||||
|
// if (!$invitation) {
|
||||||
|
// $invitation = TeamInvitation::create([
|
||||||
|
// 'uuid' => Str::uuid(),
|
||||||
|
// 'email' => $user->email,
|
||||||
|
// 'team_id' => 1,
|
||||||
|
// 'link' => 'http://example.com',
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
// $this->mail = (new InvitationLink($user))->toMail();
|
||||||
|
// $this->sendEmail();
|
||||||
|
// break;
|
||||||
|
case 'waitlist-invitation-link':
|
||||||
|
$this->mail = new MailMessage();
|
||||||
|
$this->mail->view('emails.waitlist-invitation', [
|
||||||
|
'loginLink' => 'https://coolify.io',
|
||||||
|
]);
|
||||||
|
$this->mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
||||||
|
$this->sendEmail();
|
||||||
|
break;
|
||||||
|
case 'waitlist-confirmation':
|
||||||
|
$found = Waitlist::where('email', $this->email)->first();
|
||||||
|
if ($found) {
|
||||||
|
SendConfirmationForWaitlistJob::dispatch($this->email, $found->uuid);
|
||||||
|
} else {
|
||||||
|
throw new Exception('Waitlist not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'realusers-before-trial':
|
||||||
|
$this->mail = new MailMessage();
|
||||||
|
$this->mail->view('emails.before-trial-conversion');
|
||||||
|
$this->mail->subject('Trial period has been added for all subscription plans.');
|
||||||
|
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
|
||||||
|
if (!$teams || $teams->isEmpty()) {
|
||||||
|
echo 'No teams found.' . PHP_EOL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$emails = [];
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
foreach ($team->members as $member) {
|
||||||
|
if ($member->email) {
|
||||||
|
$emails[] = $member->email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$emails = array_unique($emails);
|
||||||
|
$this->info("Sending to " . count($emails) . " emails.");
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$this->info($email);
|
||||||
|
}
|
||||||
|
$confirmed = confirm('Are you sure?');
|
||||||
|
if ($confirmed) {
|
||||||
|
foreach ($emails as $email) {
|
||||||
|
$this->sendEmail($email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'realusers-server-lost-connection':
|
||||||
|
$serverId = text('Server Id');
|
||||||
|
$server = Server::find($serverId);
|
||||||
|
if (!$server) {
|
||||||
|
throw new Exception('Server not found');
|
||||||
|
}
|
||||||
|
$admins = [];
|
||||||
|
$members = $server->team->members;
|
||||||
|
foreach ($members as $member) {
|
||||||
|
if ($member->isAdmin()) {
|
||||||
|
$admins[] = $member->email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->info('Sending to ' . count($admins) . ' admins.');
|
||||||
|
foreach ($admins as $admin) {
|
||||||
|
$this->info($admin);
|
||||||
|
}
|
||||||
|
$this->mail = new MailMessage();
|
||||||
|
$this->mail->view('emails.server-lost-connection', [
|
||||||
|
'name' => $server->name,
|
||||||
|
]);
|
||||||
|
$this->mail->subject('Action required: Server ' . $server->name . ' lost connection.');
|
||||||
|
foreach ($admins as $email) {
|
||||||
|
$this->sendEmail($email);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function sendEmail(string $email = null)
|
||||||
|
{
|
||||||
|
if ($email) {
|
||||||
|
$this->email = $email;
|
||||||
|
}
|
||||||
|
Mail::send(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
fn (Message $message) => $message
|
||||||
|
->to($this->email)
|
||||||
|
->subject($this->mail->subject)
|
||||||
|
->html((string)$this->mail->render())
|
||||||
|
);
|
||||||
|
$this->info("Email sent to $this->email successfully. 📧");
|
||||||
|
}
|
||||||
|
}
|
||||||
96
app/Console/Commands/GenerateServiceTemplates.php
Normal file
96
app/Console/Commands/GenerateServiceTemplates.php
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class GenerateServiceTemplates extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'services:generate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Generate service-templates.yaml based on /templates/compose directory';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
ray()->clearAll();
|
||||||
|
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
||||||
|
$files = array_filter($files, function ($file) {
|
||||||
|
return strpos($file, '.yaml') !== false;
|
||||||
|
});
|
||||||
|
$serviceTemplatesJson = [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$parsed = $this->process_file($file);
|
||||||
|
if ($parsed) {
|
||||||
|
$name = data_get($parsed, 'name');
|
||||||
|
$parsed = data_forget($parsed, 'name');
|
||||||
|
$serviceTemplatesJson[$name] = $parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$serviceTemplatesJson = json_encode($serviceTemplatesJson, JSON_PRETTY_PRINT);
|
||||||
|
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function process_file($file)
|
||||||
|
{
|
||||||
|
$serviceName = str($file)->before('.yaml')->value();
|
||||||
|
$content = file_get_contents(base_path("templates/compose/$file"));
|
||||||
|
// $this->info($content);
|
||||||
|
$ignore = collect(preg_grep('/^# ignore:/', explode("\n", $content)))->values();
|
||||||
|
if ($ignore->count() > 0) {
|
||||||
|
$ignore = (bool)str($ignore[0])->after('# ignore:')->trim()->value();
|
||||||
|
} else {
|
||||||
|
$ignore = false;
|
||||||
|
}
|
||||||
|
if ($ignore) {
|
||||||
|
$this->info("Ignoring $file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->info("Processing $file");
|
||||||
|
$documentation = collect(preg_grep('/^# documentation:/', explode("\n", $content)))->values();
|
||||||
|
if ($documentation->count() > 0) {
|
||||||
|
$documentation = str($documentation[0])->after('# documentation:')->trim()->value();
|
||||||
|
} else {
|
||||||
|
$documentation = 'https://coolify.io/docs';
|
||||||
|
}
|
||||||
|
|
||||||
|
$slogan = collect(preg_grep('/^# slogan:/', explode("\n", $content)))->values();
|
||||||
|
if ($slogan->count() > 0) {
|
||||||
|
$slogan = str($slogan[0])->after('# slogan:')->trim()->value();
|
||||||
|
} else {
|
||||||
|
$slogan = str($file)->headline()->value();
|
||||||
|
}
|
||||||
|
$env_file = collect(preg_grep('/^# env_file:/', explode("\n", $content)))->values();
|
||||||
|
if ($env_file->count() > 0) {
|
||||||
|
$env_file = str($env_file[0])->after('# env_file:')->trim()->value();
|
||||||
|
} else {
|
||||||
|
$env_file = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = Yaml::parse($content);
|
||||||
|
$yaml = base64_encode(Yaml::dump($json, 10, 2));
|
||||||
|
$payload = [
|
||||||
|
'name' => $serviceName,
|
||||||
|
'documentation' => $documentation,
|
||||||
|
'slogan' => $slogan,
|
||||||
|
'compose' => $yaml,
|
||||||
|
];
|
||||||
|
if ($env_file) {
|
||||||
|
$payload['envs'] = $env_file;
|
||||||
|
}
|
||||||
|
return $payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ class Init extends Command
|
|||||||
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
||||||
$deployment->save();
|
$deployment->save();
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error: {$e->getMessage()}\n";
|
echo "Error: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
139
app/Console/Commands/ResourcesDelete.php
Normal file
139
app/Console/Commands/ResourcesDelete.php
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\confirm;
|
||||||
|
use function Laravel\Prompts\multiselect;
|
||||||
|
use function Laravel\Prompts\select;
|
||||||
|
|
||||||
|
class ResourcesDelete extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'resources:delete';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Delete a resource from the database';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$resource = select(
|
||||||
|
'What resource do you want to delete?',
|
||||||
|
['Application', 'Database', 'Service', 'Server'],
|
||||||
|
);
|
||||||
|
if ($resource === 'Application') {
|
||||||
|
$this->deleteApplication();
|
||||||
|
} elseif ($resource === 'Database') {
|
||||||
|
$this->deleteDatabase();
|
||||||
|
} elseif ($resource === 'Service') {
|
||||||
|
$this->deleteService();
|
||||||
|
} elseif ($resource === 'Server') {
|
||||||
|
$this->deleteServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function deleteServer()
|
||||||
|
{
|
||||||
|
$servers = Server::all();
|
||||||
|
if ($servers->count() === 0) {
|
||||||
|
$this->error('There are no applications to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$serversToDelete = multiselect(
|
||||||
|
label: 'What server do you want to delete?',
|
||||||
|
options: $servers->pluck('name', 'id')->sortKeys(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($serversToDelete as $server) {
|
||||||
|
$toDelete = $servers->where('id', $server)->first();
|
||||||
|
$this->info($toDelete);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function deleteApplication()
|
||||||
|
{
|
||||||
|
$applications = Application::all();
|
||||||
|
if ($applications->count() === 0) {
|
||||||
|
$this->error('There are no applications to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$applicationsToDelete = multiselect(
|
||||||
|
'What application do you want to delete?',
|
||||||
|
$applications->pluck('name', 'id')->sortKeys(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($applicationsToDelete as $application) {
|
||||||
|
ray($application);
|
||||||
|
$toDelete = $applications->where('id', $application)->first();
|
||||||
|
$this->info($toDelete);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources? ");
|
||||||
|
if (!$confirmed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function deleteDatabase()
|
||||||
|
{
|
||||||
|
$databases = StandalonePostgresql::all();
|
||||||
|
if ($databases->count() === 0) {
|
||||||
|
$this->error('There are no databases to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$databasesToDelete = multiselect(
|
||||||
|
'What database do you want to delete?',
|
||||||
|
$databases->pluck('name', 'id')->sortKeys(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($databasesToDelete as $database) {
|
||||||
|
$toDelete = $databases->where('id', $database)->first();
|
||||||
|
$this->info($toDelete);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function deleteService()
|
||||||
|
{
|
||||||
|
$services = Service::all();
|
||||||
|
if ($services->count() === 0) {
|
||||||
|
$this->error('There are no services to delete.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$servicesToDelete = multiselect(
|
||||||
|
'What service do you want to delete?',
|
||||||
|
$services->pluck('name', 'id')->sortKeys(),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($servicesToDelete as $service) {
|
||||||
|
$toDelete = $services->where('id', $service)->first();
|
||||||
|
$this->info($toDelete);
|
||||||
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$toDelete->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ use Illuminate\Http\Client\PendingRequest;
|
|||||||
use Illuminate\Http\Client\Pool;
|
use Illuminate\Http\Client\Pool;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\confirm;
|
||||||
|
|
||||||
class SyncBunny extends Command
|
class SyncBunny extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -14,7 +16,7 @@ class SyncBunny extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'sync:bunny';
|
protected $signature = 'sync:bunny {--only-template} {--only-version}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -28,6 +30,9 @@ class SyncBunny extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
$that = $this;
|
||||||
|
$only_template = $this->option('only-template');
|
||||||
|
$only_version = $this->option('only-version');
|
||||||
$bunny_cdn = "https://cdn.coollabs.io";
|
$bunny_cdn = "https://cdn.coollabs.io";
|
||||||
$bunny_cdn_path = "coolify";
|
$bunny_cdn_path = "coolify";
|
||||||
$bunny_cdn_storage_name = "coolcdn";
|
$bunny_cdn_storage_name = "coolcdn";
|
||||||
@@ -39,51 +44,71 @@ class SyncBunny extends Command
|
|||||||
$install_script = "install.sh";
|
$install_script = "install.sh";
|
||||||
$upgrade_script = "upgrade.sh";
|
$upgrade_script = "upgrade.sh";
|
||||||
$production_env = ".env.production";
|
$production_env = ".env.production";
|
||||||
|
$service_template = "service-templates.json";
|
||||||
|
|
||||||
$versions = "versions.json";
|
$versions = "versions.json";
|
||||||
|
|
||||||
PendingRequest::macro('storage', function ($file) {
|
PendingRequest::macro('storage', function ($fileName) use($that) {
|
||||||
$headers = [
|
$headers = [
|
||||||
'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
|
'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
'Content-Type' => 'application/octet-stream'
|
'Content-Type' => 'application/octet-stream'
|
||||||
];
|
];
|
||||||
$fileStream = fopen($file, "r");
|
$fileStream = fopen($fileName, "r");
|
||||||
$file = fread($fileStream, filesize($file));
|
$file = fread($fileStream, filesize($fileName));
|
||||||
|
$that->info('Uploading: ' . $fileName);
|
||||||
return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
|
return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
|
||||||
});
|
});
|
||||||
PendingRequest::macro('purge', function ($url) {
|
PendingRequest::macro('purge', function ($url) use ($that) {
|
||||||
$headers = [
|
$headers = [
|
||||||
'AccessKey' => env('BUNNY_API_KEY'),
|
'AccessKey' => env('BUNNY_API_KEY'),
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
];
|
];
|
||||||
ray('Purging: ' . $url);
|
$that->info('Purging: ' . $url);
|
||||||
return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [
|
return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [
|
||||||
"url" => $url,
|
"url" => $url,
|
||||||
"async" => false
|
"async" => false
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
|
$confirmed = confirm('Are you sure you want to sync?');
|
||||||
|
if (!$confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($only_template) {
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->storage(file: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
|
$pool->storage(fileName: "$parent_dir/templates/$service_template")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$service_template"),
|
||||||
$pool->storage(file: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"),
|
||||||
$pool->storage(file: "$parent_dir/$production_env")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$production_env"),
|
]);
|
||||||
$pool->storage(file: "$parent_dir/scripts/$upgrade_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$upgrade_script"),
|
$this->info('Service template uploaded & purged...');
|
||||||
$pool->storage(file: "$parent_dir/scripts/$install_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$install_script"),
|
return;
|
||||||
$pool->storage(file: "$parent_dir/$versions")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"),
|
}
|
||||||
|
if ($only_version) {
|
||||||
|
Http::pool(fn (Pool $pool) => [
|
||||||
|
$pool->storage(fileName: "$parent_dir/$versions")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$versions"),
|
||||||
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
||||||
|
]);
|
||||||
|
$this->info('versions.json uploaded & purged...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
||||||
|
$pool->storage(fileName: "$parent_dir/$production_env")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$production_env"),
|
||||||
|
$pool->storage(fileName: "$parent_dir/scripts/$upgrade_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$upgrade_script"),
|
||||||
|
$pool->storage(fileName: "$parent_dir/scripts/$install_script")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$install_script"),
|
||||||
]);
|
]);
|
||||||
ray("{$bunny_cdn}/{$bunny_cdn_path}");
|
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file_prod"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$compose_file_prod"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$production_env"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$production_env"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
|
||||||
]);
|
]);
|
||||||
echo "All files uploaded & purged...\n";
|
$this->info("All files uploaded & purged...");
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
echo $e->getMessage();
|
$this->error("Error: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,182 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use App\Models\ApplicationPreview;
|
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
|
||||||
use App\Models\StandalonePostgresql;
|
|
||||||
use App\Models\TeamInvitation;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Notifications\Application\DeploymentFailed;
|
|
||||||
use App\Notifications\Application\DeploymentSuccess;
|
|
||||||
use App\Notifications\Application\StatusChanged;
|
|
||||||
use App\Notifications\Database\BackupFailed;
|
|
||||||
use App\Notifications\Database\BackupSuccess;
|
|
||||||
use App\Notifications\Test;
|
|
||||||
use App\Notifications\TransactionalEmails\InvitationLink;
|
|
||||||
use Exception;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Mail\Message;
|
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
|
||||||
use Mail;
|
|
||||||
use Str;
|
|
||||||
|
|
||||||
use function Laravel\Prompts\select;
|
|
||||||
use function Laravel\Prompts\text;
|
|
||||||
|
|
||||||
class TestEmail extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'email:test';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Send a test email to the admin';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*/
|
|
||||||
private ?MailMessage $mail = null;
|
|
||||||
private string $email = 'andras.bacsai@protonmail.com';
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$type = select(
|
|
||||||
'Which Email should be sent?',
|
|
||||||
options: [
|
|
||||||
'emails-test' => 'Test',
|
|
||||||
'application-deployment-success' => 'Application - Deployment Success',
|
|
||||||
'application-deployment-failed' => 'Application - Deployment Failed',
|
|
||||||
'application-status-changed' => 'Application - Status Changed',
|
|
||||||
'backup-success' => 'Database - Backup Success',
|
|
||||||
'backup-failed' => 'Database - Backup Failed',
|
|
||||||
'invitation-link' => 'Invitation Link',
|
|
||||||
'waitlist-invitation-link' => 'Waitlist Invitation Link',
|
|
||||||
'waitlist-confirmation' => 'Waitlist Confirmation',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
$this->email = text('Email Address to send to');
|
|
||||||
set_transanctional_email_settings();
|
|
||||||
|
|
||||||
$this->mail = new MailMessage();
|
|
||||||
$this->mail->subject("Test Email");
|
|
||||||
switch ($type) {
|
|
||||||
case 'emails-test':
|
|
||||||
$this->mail = (new Test())->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
case 'application-deployment-success':
|
|
||||||
$application = Application::all()->first();
|
|
||||||
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
case 'application-deployment-failed':
|
|
||||||
$application = Application::all()->first();
|
|
||||||
$preview = ApplicationPreview::all()->first();
|
|
||||||
if (!$preview) {
|
|
||||||
$preview = ApplicationPreview::create([
|
|
||||||
'application_id' => $application->id,
|
|
||||||
'pull_request_id' => 1,
|
|
||||||
'pull_request_html_url' => 'http://example.com',
|
|
||||||
'fqdn' => $application->fqdn,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$this->mail = (new DeploymentFailed($application, 'test'))->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
$this->mail = (new DeploymentFailed($application, 'test', $preview))->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
case 'application-status-changed':
|
|
||||||
$application = Application::all()->first();
|
|
||||||
$this->mail = (new StatusChanged($application))->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
case 'backup-failed':
|
|
||||||
$backup = ScheduledDatabaseBackup::all()->first();
|
|
||||||
$db = StandalonePostgresql::all()->first();
|
|
||||||
if (!$backup) {
|
|
||||||
$backup = ScheduledDatabaseBackup::create([
|
|
||||||
'enabled' => true,
|
|
||||||
'frequency' => 'daily',
|
|
||||||
'save_s3' => false,
|
|
||||||
'database_id' => $db->id,
|
|
||||||
'database_type' => $db->getMorphClass(),
|
|
||||||
'team_id' => 0,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$output = 'Because of an error, the backup of the database ' . $db->name . ' failed.';
|
|
||||||
$this->mail = (new BackupFailed($backup, $db, $output))->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
case 'backup-success':
|
|
||||||
$backup = ScheduledDatabaseBackup::all()->first();
|
|
||||||
$db = StandalonePostgresql::all()->first();
|
|
||||||
if (!$backup) {
|
|
||||||
$backup = ScheduledDatabaseBackup::create([
|
|
||||||
'enabled' => true,
|
|
||||||
'frequency' => 'daily',
|
|
||||||
'save_s3' => false,
|
|
||||||
'database_id' => $db->id,
|
|
||||||
'database_type' => $db->getMorphClass(),
|
|
||||||
'team_id' => 0,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$this->mail = (new BackupSuccess($backup, $db))->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
case 'invitation-link':
|
|
||||||
$user = User::all()->first();
|
|
||||||
$invitation = TeamInvitation::whereEmail($user->email)->first();
|
|
||||||
if (!$invitation) {
|
|
||||||
$invitation = TeamInvitation::create([
|
|
||||||
'uuid' => Str::uuid(),
|
|
||||||
'email' => $user->email,
|
|
||||||
'team_id' => 1,
|
|
||||||
'link' => 'http://example.com',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$this->mail = (new InvitationLink($user))->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
case 'waitlist-invitation-link':
|
|
||||||
$this->mail = new MailMessage();
|
|
||||||
$this->mail->view('emails.waitlist-invitation', [
|
|
||||||
'email' => 'test2@example.com',
|
|
||||||
'password' => "supersecretpassword",
|
|
||||||
]);
|
|
||||||
$this->mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
case 'waitlist-confirmation':
|
|
||||||
$this->mail = new MailMessage();
|
|
||||||
$this->mail->view(
|
|
||||||
'emails.waitlist-confirmation',
|
|
||||||
[
|
|
||||||
'confirmation_url' => 'http://example.com',
|
|
||||||
'cancel_url' => 'http://example.com',
|
|
||||||
]
|
|
||||||
);
|
|
||||||
$this->mail->subject('You are on the waitlist!');
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private function sendEmail()
|
|
||||||
{
|
|
||||||
Mail::send(
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
fn (Message $message) => $message
|
|
||||||
->to($this->email)
|
|
||||||
->subject($this->mail->subject)
|
|
||||||
->html((string)$this->mail->render())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
49
app/Console/Commands/UsersResetRoot.php
Normal file
49
app/Console/Commands/UsersResetRoot.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
use function Laravel\Prompts\password;
|
||||||
|
|
||||||
|
class UsersResetRoot extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'users:reset-root';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Reset Root Password';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
$this->info('You are about to reset the root password.');
|
||||||
|
$password = password('Give me a new password for root user: ');
|
||||||
|
$passwordAgain = password('Again');
|
||||||
|
if ($password != $passwordAgain) {
|
||||||
|
$this->error('Passwords do not match.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->info('Updating root password...');
|
||||||
|
try {
|
||||||
|
User::find(0)->update(['password' => Hash::make($password)]);
|
||||||
|
$this->info('Root password updated successfully.');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('Failed to update root password.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@ class WaitlistInvite extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'waitlist:invite {email?} {--only-email}';
|
protected $signature = 'waitlist:invite {--people=1} {--only-email} {email?}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -33,6 +33,12 @@ class WaitlistInvite extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
$people = $this->option('people');
|
||||||
|
for ($i = 0; $i < $people; $i++) {
|
||||||
|
$this->main();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function main() {
|
||||||
if ($this->argument('email')) {
|
if ($this->argument('email')) {
|
||||||
if ($this->option('only-email')) {
|
if ($this->option('only-email')) {
|
||||||
$this->next_patient = User::whereEmail($this->argument('email'))->first();
|
$this->next_patient = User::whereEmail($this->argument('email'))->first();
|
||||||
@@ -86,13 +92,10 @@ class WaitlistInvite extends Command
|
|||||||
}
|
}
|
||||||
private function send_email()
|
private function send_email()
|
||||||
{
|
{
|
||||||
ray($this->next_patient->email, $this->password);
|
|
||||||
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
||||||
$loginLink = route('auth.link', ['token' => $token]);
|
$loginLink = route('auth.link', ['token' => $token]);
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->view('emails.waitlist-invitation', [
|
$mail->view('emails.waitlist-invitation', [
|
||||||
'email' => $this->next_patient->email,
|
|
||||||
'password' => $this->password,
|
|
||||||
'loginLink' => $loginLink,
|
'loginLink' => $loginLink,
|
||||||
]);
|
]);
|
||||||
$mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
$mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
||||||
|
|||||||
@@ -2,19 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Console;
|
namespace App\Console;
|
||||||
|
|
||||||
use App\Jobs\ApplicationContainerStatusJob;
|
|
||||||
use App\Jobs\CheckResaleLicenseJob;
|
use App\Jobs\CheckResaleLicenseJob;
|
||||||
use App\Jobs\CleanupInstanceStuffsJob;
|
use App\Jobs\CleanupInstanceStuffsJob;
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
use App\Jobs\DatabaseContainerStatusJob;
|
|
||||||
use App\Jobs\DockerCleanupJob;
|
use App\Jobs\DockerCleanupJob;
|
||||||
use App\Jobs\InstanceAutoUpdateJob;
|
use App\Jobs\InstanceAutoUpdateJob;
|
||||||
use App\Jobs\ProxyCheckJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Jobs\ResourceStatusJob;
|
|
||||||
use App\Models\Application;
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\Server;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
@@ -24,36 +20,41 @@ class Kernel extends ConsoleKernel
|
|||||||
{
|
{
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$schedule->command('horizon:snapshot')->everyMinute();
|
$schedule->command('horizon:snapshot')->everyMinute();
|
||||||
// $schedule->job(new ResourceStatusJob)->everyMinute();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
||||||
$schedule->job(new ProxyCheckJob)->everyFiveMinutes();
|
$this->check_scheduled_backups($schedule);
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute();
|
$this->check_resources($schedule);
|
||||||
// $schedule->job(new CheckResaleLicenseJob)->hourly();
|
$this->cleanup_servers($schedule);
|
||||||
$schedule->job(new DockerCleanupJob)->everyOddHour();
|
$this->check_scheduled_backups($schedule);
|
||||||
} else {
|
} else {
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTenMinutes()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||||
// $schedule->job(new ResourceStatusJob)->everyMinute()->onOneServer();
|
|
||||||
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
$schedule->job(new ProxyCheckJob)->everyFiveMinutes()->onOneServer();
|
|
||||||
$schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
|
|
||||||
}
|
|
||||||
$this->instance_auto_update($schedule);
|
$this->instance_auto_update($schedule);
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
|
$this->cleanup_servers($schedule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function cleanup_servers($schedule)
|
||||||
|
{
|
||||||
|
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private function check_resources($schedule)
|
private function check_resources($schedule)
|
||||||
{
|
{
|
||||||
$applications = Application::all();
|
if (isCloud()) {
|
||||||
foreach ($applications as $application) {
|
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false);
|
||||||
$schedule->job(new ApplicationContainerStatusJob($application))->everyMinute()->onOneServer();
|
} else {
|
||||||
|
$servers = Server::all();
|
||||||
}
|
}
|
||||||
|
foreach ($servers as $server) {
|
||||||
$postgresqls = StandalonePostgresql::all();
|
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
foreach ($postgresqls as $postgresql) {
|
|
||||||
$schedule->job(new DatabaseContainerStatusJob($postgresql))->everyMinute()->onOneServer();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function instance_auto_update($schedule){
|
private function instance_auto_update($schedule)
|
||||||
|
{
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -64,7 +65,6 @@ class Kernel extends ConsoleKernel
|
|||||||
}
|
}
|
||||||
private function check_scheduled_backups($schedule)
|
private function check_scheduled_backups($schedule)
|
||||||
{
|
{
|
||||||
ray('check_scheduled_backups');
|
|
||||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||||
if ($scheduled_backups->isEmpty()) {
|
if ($scheduled_backups->isEmpty()) {
|
||||||
ray('no scheduled backups');
|
ray('no scheduled backups');
|
||||||
@@ -74,6 +74,11 @@ class Kernel extends ConsoleKernel
|
|||||||
if (!$scheduled_backup->enabled) {
|
if (!$scheduled_backup->enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (is_null(data_get($scheduled_backup, 'database'))) {
|
||||||
|
ray('database not found');
|
||||||
|
$scheduled_backup->delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) {
|
if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) {
|
||||||
$scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency];
|
$scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency];
|
||||||
|
|||||||
@@ -12,16 +12,16 @@ use Spatie\LaravelData\Data;
|
|||||||
class CoolifyTaskArgs extends Data
|
class CoolifyTaskArgs extends Data
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $server_ip,
|
public string $server_uuid,
|
||||||
public string $private_key_location,
|
|
||||||
public string $command,
|
public string $command,
|
||||||
public int $port,
|
|
||||||
public string $user,
|
|
||||||
public string $type,
|
public string $type,
|
||||||
public ?string $type_uuid = null,
|
public ?string $type_uuid = null,
|
||||||
public ?Model $model = null,
|
public ?Model $model = null,
|
||||||
public string $status = ProcessStatus::QUEUED->value,
|
public ?string $status = null ,
|
||||||
public bool $ignore_errors = false,
|
public bool $ignore_errors = false,
|
||||||
) {
|
) {
|
||||||
|
if(is_null($status)){
|
||||||
|
$this->status = ProcessStatus::QUEUED->value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Exceptions;
|
namespace App\Exceptions;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
use Sentry\Laravel\Integration;
|
use Sentry\Laravel\Integration;
|
||||||
use Sentry\State\Scope;
|
use Sentry\State\Scope;
|
||||||
@@ -25,7 +26,7 @@ class Handler extends ExceptionHandler
|
|||||||
* @var array<int, class-string<\Throwable>>
|
* @var array<int, class-string<\Throwable>>
|
||||||
*/
|
*/
|
||||||
protected $dontReport = [
|
protected $dontReport = [
|
||||||
//
|
ProcessException::class
|
||||||
];
|
];
|
||||||
/**
|
/**
|
||||||
* A list of the inputs that are never flashed to the session on validation exceptions.
|
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||||
@@ -45,13 +46,23 @@ class Handler extends ExceptionHandler
|
|||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$this->reportable(function (Throwable $e) {
|
$this->reportable(function (Throwable $e) {
|
||||||
|
if (isDev()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
$this->settings = InstanceSettings::get();
|
$this->settings = InstanceSettings::get();
|
||||||
if ($this->settings->do_not_track || isDev()) {
|
if ($this->settings->do_not_track) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app('sentry')->configureScope(
|
app('sentry')->configureScope(
|
||||||
function (Scope $scope){
|
function (Scope $scope) {
|
||||||
$scope->setUser(['id'=> config('sentry.server_name')]);
|
$email = auth()?->user() ? auth()->user()->email : 'guest';
|
||||||
|
$instanceAdmin = User::find(0)->email ?? 'admin@localhost';
|
||||||
|
$scope->setUser(
|
||||||
|
[
|
||||||
|
'email' => $email,
|
||||||
|
'instanceAdmin' => $instanceAdmin
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
Integration::captureUnhandledException($e);
|
Integration::captureUnhandledException($e);
|
||||||
|
|||||||
10
app/Exceptions/ProcessException.php
Normal file
10
app/Exceptions/ProcessException.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class ProcessException extends Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,21 +3,17 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Project;
|
|
||||||
use App\Models\S3Storage;
|
use App\Models\S3Storage;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\TeamInvitation;
|
use App\Models\TeamInvitation;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Auth;
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Crypt;
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Str;
|
||||||
use Throwable;
|
|
||||||
use Str;
|
|
||||||
|
|
||||||
|
|
||||||
class Controller extends BaseController
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
@@ -35,23 +31,21 @@ class Controller extends BaseController
|
|||||||
return redirect()->route('login');
|
return redirect()->route('login');
|
||||||
}
|
}
|
||||||
if (Hash::check($password, $user->password)) {
|
if (Hash::check($password, $user->password)) {
|
||||||
Auth::login($user);
|
$invitation = TeamInvitation::whereEmail($email);
|
||||||
|
if ($invitation->exists()) {
|
||||||
|
$team = $invitation->first()->team;
|
||||||
|
$user->teams()->attach($team->id, ['role' => $invitation->first()->role]);
|
||||||
|
$invitation->delete();
|
||||||
|
} else {
|
||||||
$team = $user->teams()->first();
|
$team = $user->teams()->first();
|
||||||
|
}
|
||||||
|
Auth::login($user);
|
||||||
session(['currentTeam' => $team]);
|
session(['currentTeam' => $team]);
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
||||||
}
|
}
|
||||||
public function subscription()
|
|
||||||
{
|
|
||||||
if (!isCloud()) {
|
|
||||||
abort(404);
|
|
||||||
}
|
|
||||||
return view('subscription.index', [
|
|
||||||
'settings' => InstanceSettings::get(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function license()
|
public function license()
|
||||||
{
|
{
|
||||||
@@ -137,25 +131,21 @@ class Controller extends BaseController
|
|||||||
try {
|
try {
|
||||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
||||||
$user = User::whereEmail($invitation->email)->firstOrFail();
|
$user = User::whereEmail($invitation->email)->firstOrFail();
|
||||||
if (is_null(auth()->user())) {
|
|
||||||
return redirect()->route('login');
|
|
||||||
}
|
|
||||||
if (auth()->user()->id !== $user->id) {
|
if (auth()->user()->id !== $user->id) {
|
||||||
abort(401);
|
abort(401);
|
||||||
}
|
}
|
||||||
|
$invitationValid = $invitation->isValid();
|
||||||
$createdAt = $invitation->created_at;
|
if ($invitationValid) {
|
||||||
$diff = $createdAt->diffInMinutes(now());
|
|
||||||
if ($diff <= config('constants.invitation.link.expiration')) {
|
|
||||||
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
||||||
|
refreshSession($invitation->team);
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
} else {
|
} else {
|
||||||
$invitation->delete();
|
|
||||||
abort(401);
|
abort(401);
|
||||||
}
|
}
|
||||||
} catch (Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
throw $th;
|
ray($e->getMessage());
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,8 +162,8 @@ class Controller extends BaseController
|
|||||||
}
|
}
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
return redirect()->route('team.index');
|
return redirect()->route('team.index');
|
||||||
} catch (Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
throw $th;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class DatabaseController extends Controller
|
|||||||
if (!$environment) {
|
if (!$environment) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$database = $environment->databases->where('uuid', request()->route('database_uuid'))->first();
|
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
||||||
if (!$database) {
|
if (!$database) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ class DatabaseController extends Controller
|
|||||||
if (!$environment) {
|
if (!$environment) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$database = $environment->databases->where('uuid', request()->route('database_uuid'))->first();
|
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
||||||
if (!$database) {
|
if (!$database) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
@@ -64,10 +64,18 @@ class DatabaseController extends Controller
|
|||||||
if (!$environment) {
|
if (!$environment) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$database = $environment->databases->where('uuid', request()->route('database_uuid'))->first();
|
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
||||||
if (!$database) {
|
if (!$database) {
|
||||||
return redirect()->route('dashboard');
|
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', [
|
return view('project.database.backups.all', [
|
||||||
'database' => $database,
|
'database' => $database,
|
||||||
's3s' => currentTeam()->s3s,
|
's3s' => currentTeam()->s3s,
|
||||||
|
|||||||
@@ -2,8 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\EnvironmentVariable;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandaloneDocker;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class ProjectController extends Controller
|
class ProjectController extends Controller
|
||||||
{
|
{
|
||||||
@@ -41,9 +45,10 @@ class ProjectController extends Controller
|
|||||||
|
|
||||||
public function new()
|
public function new()
|
||||||
{
|
{
|
||||||
$type = request()->query('type');
|
$services = getServiceTemplates();
|
||||||
|
$type = Str::of(request()->query('type'));
|
||||||
$destination_uuid = request()->query('destination');
|
$destination_uuid = request()->query('destination');
|
||||||
$server = requesT()->query('server');
|
$server_id = request()->query('server_id');
|
||||||
|
|
||||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
@@ -54,15 +59,87 @@ class ProjectController extends Controller
|
|||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
if (in_array($type, DATABASE_TYPES)) {
|
if (in_array($type, DATABASE_TYPES)) {
|
||||||
$standalone_postgresql = create_standalone_postgresql($environment->id, $destination_uuid);
|
if ($type->value() === "postgresql") {
|
||||||
|
$database = create_standalone_postgresql($environment->id, $destination_uuid);
|
||||||
|
} else if ($type->value() === 'redis') {
|
||||||
|
$database = create_standalone_redis($environment->id, $destination_uuid);
|
||||||
|
} else if ($type->value() === 'mongodb') {
|
||||||
|
$database = create_standalone_mongodb($environment->id, $destination_uuid);
|
||||||
|
}
|
||||||
return redirect()->route('project.database.configuration', [
|
return redirect()->route('project.database.configuration', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'database_uuid' => $standalone_postgresql->uuid,
|
'database_uuid' => $database->uuid,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
if ($type->startsWith('one-click-service-') && !is_null( (int)$server_id)) {
|
||||||
|
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||||
|
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||||
|
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||||
|
if ($oneClickDotEnvs) {
|
||||||
|
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
||||||
|
}
|
||||||
|
if ($oneClickService) {
|
||||||
|
$destination = StandaloneDocker::whereUuid($destination_uuid)->first();
|
||||||
|
$service = Service::create([
|
||||||
|
'name' => "$oneClickServiceName-" . Str::random(10),
|
||||||
|
'docker_compose_raw' => base64_decode($oneClickService),
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'server_id' => (int) $server_id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination->getMorphClass(),
|
||||||
|
]);
|
||||||
|
$service->name = "$oneClickServiceName-" . $service->uuid;
|
||||||
|
$service->save();
|
||||||
|
if ($oneClickDotEnvs?->count() > 0) {
|
||||||
|
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||||
|
$key = Str::before($value, '=');
|
||||||
|
$value = Str::of(Str::after($value, '='));
|
||||||
|
$generatedValue = $value;
|
||||||
|
if ($value->contains('SERVICE_')) {
|
||||||
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
|
// TODO: make it shared with Service.php
|
||||||
|
switch ($command->value()) {
|
||||||
|
case 'PASSWORD':
|
||||||
|
$generatedValue = Str::password(symbols: false);
|
||||||
|
break;
|
||||||
|
case 'PASSWORD_64':
|
||||||
|
$generatedValue = Str::password(length: 64, symbols: false);
|
||||||
|
break;
|
||||||
|
case 'BASE64_64':
|
||||||
|
$generatedValue = Str::random(64);
|
||||||
|
break;
|
||||||
|
case 'BASE64_128':
|
||||||
|
$generatedValue = Str::random(128);
|
||||||
|
break;
|
||||||
|
case 'BASE64':
|
||||||
|
$generatedValue = Str::random(32);
|
||||||
|
break;
|
||||||
|
case 'USER':
|
||||||
|
$generatedValue = Str::random(16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EnvironmentVariable::create([
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $generatedValue,
|
||||||
|
'service_id' => $service->id,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$service->parse(isNew: true);
|
||||||
|
|
||||||
|
return redirect()->route('project.service', [
|
||||||
|
'service_uuid' => $service->uuid,
|
||||||
|
'environment_name' => $environment->name,
|
||||||
|
'project_uuid' => $project->uuid,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
return view('project.new', [
|
return view('project.new', [
|
||||||
'type' => $type
|
'type' => $type->value()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
|
||||||
|
|
||||||
class ServerController extends Controller
|
|
||||||
{
|
|
||||||
use AuthorizesRequests, ValidatesRequests;
|
|
||||||
|
|
||||||
public function new_server()
|
|
||||||
{
|
|
||||||
$privateKeys = PrivateKey::ownedByCurrentTeam()->get();
|
|
||||||
if (!isCloud()) {
|
|
||||||
return view('server.create', [
|
|
||||||
'limit_reached' => false,
|
|
||||||
'private_keys' => $privateKeys,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$team = currentTeam();
|
|
||||||
$servers = $team->servers->count();
|
|
||||||
['serverLimit' => $serverLimit] = $team->limits;
|
|
||||||
$limit_reached = $servers >= $serverLimit;
|
|
||||||
|
|
||||||
return view('server.create', [
|
|
||||||
'limit_reached' => $limit_reached,
|
|
||||||
'private_keys' => $privateKeys,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,8 +38,7 @@ class Kernel extends HttpKernel
|
|||||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
\App\Http\Middleware\CheckForcePasswordReset::class,
|
\App\Http\Middleware\CheckForcePasswordReset::class,
|
||||||
\App\Http\Middleware\IsSubscriptionValid::class,
|
\App\Http\Middleware\DecideWhatToDoWithUser::class,
|
||||||
\App\Http\Middleware\IsBoardingFlow::class,
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class Index extends Component
|
|||||||
{
|
{
|
||||||
public string $currentState = 'welcome';
|
public string $currentState = 'welcome';
|
||||||
|
|
||||||
|
public ?string $selectedServerType = null;
|
||||||
public ?Collection $privateKeys = null;
|
public ?Collection $privateKeys = null;
|
||||||
public ?int $selectedExistingPrivateKey = null;
|
public ?int $selectedExistingPrivateKey = null;
|
||||||
public ?string $privateKeyType = null;
|
public ?string $privateKeyType = null;
|
||||||
@@ -36,6 +37,11 @@ class Index extends Component
|
|||||||
public ?int $selectedExistingProject = null;
|
public ?int $selectedExistingProject = null;
|
||||||
public ?Project $createdProject = null;
|
public ?Project $createdProject = null;
|
||||||
|
|
||||||
|
public bool $dockerInstallationStarted = false;
|
||||||
|
|
||||||
|
public string $serverPublicKey;
|
||||||
|
public bool $serverReachable = true;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->privateKeyName = generate_random_name();
|
$this->privateKeyName = generate_random_name();
|
||||||
@@ -53,20 +59,16 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->remoteServerHost = 'coolify-testing-host';
|
$this->remoteServerHost = 'coolify-testing-host';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function welcome() {
|
public function explanation()
|
||||||
|
{
|
||||||
if (isCloud()) {
|
if (isCloud()) {
|
||||||
return $this->setServerType('remote');
|
return $this->setServerType('remote');
|
||||||
}
|
}
|
||||||
$this->currentState = 'select-server-type';
|
$this->currentState = 'select-server-type';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function restartBoarding()
|
public function restartBoarding()
|
||||||
{
|
{
|
||||||
if ($this->createdServer) {
|
|
||||||
$this->createdServer->delete();
|
|
||||||
}
|
|
||||||
if ($this->createdPrivateKey) {
|
|
||||||
$this->createdPrivateKey->delete();
|
|
||||||
}
|
|
||||||
return redirect()->route('boarding');
|
return redirect()->route('boarding');
|
||||||
}
|
}
|
||||||
public function skipBoarding()
|
public function skipBoarding()
|
||||||
@@ -74,20 +76,21 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
Team::find(currentTeam()->id)->update([
|
Team::find(currentTeam()->id)->update([
|
||||||
'show_boarding' => false
|
'show_boarding' => false
|
||||||
]);
|
]);
|
||||||
ray(currentTeam());
|
|
||||||
refreshSession();
|
refreshSession();
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setServerType(string $type)
|
public function setServerType(string $type)
|
||||||
{
|
{
|
||||||
if ($type === 'localhost') {
|
$this->selectedServerType = $type;
|
||||||
|
if ($this->selectedServerType === 'localhost') {
|
||||||
$this->createdServer = Server::find(0);
|
$this->createdServer = Server::find(0);
|
||||||
if (!$this->createdServer) {
|
if (!$this->createdServer) {
|
||||||
return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
|
return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
|
||||||
}
|
}
|
||||||
return $this->validateServer();
|
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
||||||
} elseif ($type === 'remote') {
|
return $this->validateServer('localhost');
|
||||||
|
} elseif ($this->selectedServerType === 'remote') {
|
||||||
$this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get();
|
$this->privateKeys = PrivateKey::ownedByCurrentTeam(['name'])->where('id', '!=', 0)->get();
|
||||||
if ($this->privateKeys->count() > 0) {
|
if ($this->privateKeys->count() > 0) {
|
||||||
$this->selectedExistingPrivateKey = $this->privateKeys->first()->id;
|
$this->selectedExistingPrivateKey = $this->privateKeys->first()->id;
|
||||||
@@ -110,11 +113,11 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
|
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
|
||||||
|
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
|
||||||
$this->validateServer();
|
$this->validateServer();
|
||||||
$this->getProxyType();
|
|
||||||
$this->getProjects();
|
|
||||||
}
|
}
|
||||||
public function getProxyType() {
|
public function getProxyType()
|
||||||
|
{
|
||||||
$proxyTypeSet = $this->createdServer->proxy->type;
|
$proxyTypeSet = $this->createdServer->proxy->type;
|
||||||
if (!$proxyTypeSet) {
|
if (!$proxyTypeSet) {
|
||||||
$this->currentState = 'select-proxy';
|
$this->currentState = 'select-proxy';
|
||||||
@@ -124,6 +127,8 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
}
|
}
|
||||||
public function selectExistingPrivateKey()
|
public function selectExistingPrivateKey()
|
||||||
{
|
{
|
||||||
|
$this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey);
|
||||||
|
$this->privateKey = $this->createdPrivateKey->private_key;
|
||||||
$this->currentState = 'create-server';
|
$this->currentState = 'create-server';
|
||||||
}
|
}
|
||||||
public function createNewServer()
|
public function createNewServer()
|
||||||
@@ -135,7 +140,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
{
|
{
|
||||||
$this->selectedExistingPrivateKey = null;
|
$this->selectedExistingPrivateKey = null;
|
||||||
$this->privateKeyType = $type;
|
$this->privateKeyType = $type;
|
||||||
if ($type === 'create' && !isDev()) {
|
if ($type === 'create') {
|
||||||
$this->createNewPrivateKey();
|
$this->createNewPrivateKey();
|
||||||
}
|
}
|
||||||
$this->currentState = 'create-private-key';
|
$this->currentState = 'create-private-key';
|
||||||
@@ -146,23 +151,28 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
'privateKeyName' => 'required',
|
'privateKeyName' => 'required',
|
||||||
'privateKey' => 'required',
|
'privateKey' => 'required',
|
||||||
]);
|
]);
|
||||||
$this->currentState = 'create-server';
|
|
||||||
}
|
|
||||||
public function saveServer()
|
|
||||||
{
|
|
||||||
$this->validate([
|
|
||||||
'remoteServerName' => 'required',
|
|
||||||
'remoteServerHost' => 'required',
|
|
||||||
'remoteServerPort' => 'required',
|
|
||||||
'remoteServerUser' => 'required',
|
|
||||||
]);
|
|
||||||
$this->privateKey = formatPrivateKey($this->privateKey);
|
|
||||||
$this->createdPrivateKey = PrivateKey::create([
|
$this->createdPrivateKey = PrivateKey::create([
|
||||||
'name' => $this->privateKeyName,
|
'name' => $this->privateKeyName,
|
||||||
'description' => $this->privateKeyDescription,
|
'description' => $this->privateKeyDescription,
|
||||||
'private_key' => $this->privateKey,
|
'private_key' => $this->privateKey,
|
||||||
'team_id' => currentTeam()->id
|
'team_id' => currentTeam()->id
|
||||||
]);
|
]);
|
||||||
|
$this->createdPrivateKey->save();
|
||||||
|
$this->currentState = 'create-server';
|
||||||
|
}
|
||||||
|
public function saveServer()
|
||||||
|
{
|
||||||
|
$this->validate([
|
||||||
|
'remoteServerName' => 'required',
|
||||||
|
'remoteServerHost' => 'required|ip',
|
||||||
|
'remoteServerPort' => 'required|integer',
|
||||||
|
'remoteServerUser' => 'required',
|
||||||
|
]);
|
||||||
|
$this->privateKey = formatPrivateKey($this->privateKey);
|
||||||
|
$foundServer = Server::whereIp($this->remoteServerHost)->first();
|
||||||
|
if ($foundServer) {
|
||||||
|
return $this->emit('error', 'IP address is already in use by another team.');
|
||||||
|
}
|
||||||
$this->createdServer = Server::create([
|
$this->createdServer = Server::create([
|
||||||
'name' => $this->remoteServerName,
|
'name' => $this->remoteServerName,
|
||||||
'ip' => $this->remoteServerHost,
|
'ip' => $this->remoteServerHost,
|
||||||
@@ -170,38 +180,52 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
'user' => $this->remoteServerUser,
|
'user' => $this->remoteServerUser,
|
||||||
'description' => $this->remoteServerDescription,
|
'description' => $this->remoteServerDescription,
|
||||||
'private_key_id' => $this->createdPrivateKey->id,
|
'private_key_id' => $this->createdPrivateKey->id,
|
||||||
'team_id' => currentTeam()->id
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
|
$this->createdServer->save();
|
||||||
$this->validateServer();
|
$this->validateServer();
|
||||||
}
|
}
|
||||||
public function validateServer() {
|
public function validateServer()
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->createdServer);
|
$customErrorMessage = "Server is not reachable:";
|
||||||
if (!$uptime) {
|
config()->set('coolify.mux_enabled', false);
|
||||||
throw new \Exception('Server is not reachable.');
|
|
||||||
} else {
|
instant_remote_process(['uptime'], $this->createdServer, true);
|
||||||
$this->createdServer->settings->update([
|
|
||||||
|
$this->createdServer->settings()->update([
|
||||||
'is_reachable' => true,
|
'is_reachable' => true,
|
||||||
]);
|
]);
|
||||||
$this->emit('success', 'Server is reachable.');
|
} catch (\Throwable $e) {
|
||||||
|
$this->serverReachable = false;
|
||||||
|
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
|
||||||
}
|
}
|
||||||
ray($dockerVersion, $uptime);
|
|
||||||
if (!$dockerVersion) {
|
|
||||||
$this->emit('error', 'Docker is not installed on the server.');
|
|
||||||
$this->currentState = 'install-docker';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->getProxyType();
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
try {
|
||||||
return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this);
|
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true);
|
||||||
|
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||||
|
if (is_null($dockerVersion)) {
|
||||||
|
$this->currentState = 'install-docker';
|
||||||
|
throw new \Exception('Docker version is not supported or not installed.');
|
||||||
|
}
|
||||||
|
$this->createdServer->settings()->update([
|
||||||
|
'is_usable' => true,
|
||||||
|
]);
|
||||||
|
$this->getProxyType();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->dockerInstallationStarted = false;
|
||||||
|
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function installDocker()
|
public function installDocker()
|
||||||
{
|
{
|
||||||
$activity = resolve(InstallDocker::class)($this->createdServer, currentTeam());
|
$this->dockerInstallationStarted = true;
|
||||||
|
$activity = InstallDocker::run($this->createdServer);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
$this->currentState = 'select-proxy';
|
}
|
||||||
|
public function dockerInstalledOrSkipped()
|
||||||
|
{
|
||||||
|
$this->validateServer();
|
||||||
}
|
}
|
||||||
public function selectProxy(string|null $proxyType = null)
|
public function selectProxy(string|null $proxyType = null)
|
||||||
{
|
{
|
||||||
@@ -214,14 +238,16 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
$this->getProjects();
|
$this->getProjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getProjects() {
|
public function getProjects()
|
||||||
|
{
|
||||||
$this->projects = Project::ownedByCurrentTeam(['name'])->get();
|
$this->projects = Project::ownedByCurrentTeam(['name'])->get();
|
||||||
if ($this->projects->count() > 0) {
|
if ($this->projects->count() > 0) {
|
||||||
$this->selectedExistingProject = $this->projects->first()->id;
|
$this->selectedExistingProject = $this->projects->first()->id;
|
||||||
}
|
}
|
||||||
$this->currentState = 'create-project';
|
$this->currentState = 'create-project';
|
||||||
}
|
}
|
||||||
public function selectExistingProject() {
|
public function selectExistingProject()
|
||||||
|
{
|
||||||
$this->createdProject = Project::find($this->selectedExistingProject);
|
$this->createdProject = Project::find($this->selectedExistingProject);
|
||||||
$this->currentState = 'create-resource';
|
$this->currentState = 'create-resource';
|
||||||
}
|
}
|
||||||
@@ -241,7 +267,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
|||||||
[
|
[
|
||||||
'project_uuid' => $this->createdProject->uuid,
|
'project_uuid' => $this->createdProject->uuid,
|
||||||
'environment_name' => 'production',
|
'environment_name' => 'production',
|
||||||
'server'=> $this->createdServer->id,
|
'server' => $this->createdServer->id,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ class CheckLicense extends Component
|
|||||||
try {
|
try {
|
||||||
resolve(CheckResaleLicense::class)();
|
resolve(CheckResaleLicense::class)();
|
||||||
$this->emit('reloadWindow');
|
$this->emit('reloadWindow');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $e) {
|
||||||
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $th->getMessage());
|
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
||||||
ray($th->getMessage());
|
ray($e->getMessage());
|
||||||
return redirect()->to('/settings/license');
|
return redirect()->to('/settings/license');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,21 +9,13 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class Dashboard extends Component
|
class Dashboard extends Component
|
||||||
{
|
{
|
||||||
public int $projects = 0;
|
public $projects = [];
|
||||||
public int $servers = 0;
|
public $servers = [];
|
||||||
public int $s3s = 0;
|
|
||||||
public int $resources = 0;
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->servers = Server::ownedByCurrentTeam()->get()->count();
|
$this->servers = Server::ownedByCurrentTeam()->get();
|
||||||
$this->s3s = S3Storage::ownedByCurrentTeam()->get()->count();
|
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||||
$projects = Project::ownedByCurrentTeam()->get();
|
|
||||||
foreach ($projects as $project) {
|
|
||||||
$this->resources += $project->applications->count();
|
|
||||||
$this->resources += $project->postgresqls->count();
|
|
||||||
}
|
|
||||||
$this->projects = $projects->count();
|
|
||||||
}
|
}
|
||||||
// public function getIptables()
|
// public function getIptables()
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ class Form extends Component
|
|||||||
}
|
}
|
||||||
$this->destination->delete();
|
$this->destination->delete();
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,14 +71,14 @@ class StandaloneDocker extends Component
|
|||||||
}
|
}
|
||||||
$this->createNetworkAndAttachToProxy();
|
$this->createNetworkAndAttachToProxy();
|
||||||
return redirect()->route('destination.show', $docker->uuid);
|
return redirect()->route('destination.show', $docker->uuid);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createNetworkAndAttachToProxy()
|
private function createNetworkAndAttachToProxy()
|
||||||
{
|
{
|
||||||
instant_remote_process(['docker network create --attachable ' . $this->network], $this->server, throwError: false);
|
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||||
instant_remote_process(["docker network connect $this->network coolify-proxy"], $this->server, throwError: false);
|
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
app/Http/Livewire/Dev/Compose.php
Normal file
28
app/Http/Livewire/Dev/Compose.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Dev;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Compose extends Component
|
||||||
|
{
|
||||||
|
public string $compose = '';
|
||||||
|
public string $base64 = '';
|
||||||
|
public $services;
|
||||||
|
public function mount() {
|
||||||
|
$this->services = getServiceTemplates();
|
||||||
|
}
|
||||||
|
public function setService(string $selected) {
|
||||||
|
$this->base64 = data_get($this->services, $selected . '.compose');
|
||||||
|
if ($this->base64) {
|
||||||
|
$this->compose = base64_decode($this->base64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function updatedCompose($value) {
|
||||||
|
$this->base64 = base64_encode($value);
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.dev.compose');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,8 +36,8 @@ class ForcePasswordReset extends Component
|
|||||||
send_internal_notification('First login for ' . auth()->user()->email);
|
send_internal_notification('First login for ' . auth()->user()->email);
|
||||||
}
|
}
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ namespace App\Http\Livewire;
|
|||||||
|
|
||||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Route;
|
|
||||||
|
|
||||||
class Help extends Component
|
class Help extends Component
|
||||||
{
|
{
|
||||||
@@ -19,7 +19,7 @@ class Help extends Component
|
|||||||
];
|
];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->path = Route::current()->uri();
|
$this->path = Route::current()?->uri() ?? null;
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$this->description = "I'm having trouble with {$this->path}";
|
$this->description = "I'm having trouble with {$this->path}";
|
||||||
$this->subject = "Help with {$this->path}";
|
$this->subject = "Help with {$this->path}";
|
||||||
@@ -28,9 +28,9 @@ class Help extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->rateLimit(1, 60);
|
$this->rateLimit(3, 60);
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$subscriptionType = auth()->user()?->subscription?->type() ?? 'unknown';
|
$subscriptionType = auth()->user()?->subscription?->type() ?? 'Free';
|
||||||
$debug = "Route: {$this->path}";
|
$debug = "Route: {$this->path}";
|
||||||
$mail = new MailMessage();
|
$mail = new MailMessage();
|
||||||
$mail->view(
|
$mail->view(
|
||||||
@@ -41,10 +41,10 @@ class Help extends Component
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}");
|
||||||
send_user_an_email($mail, 'hi@coollabs.io');
|
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
||||||
$this->emit('success', 'Your message has been sent successfully. We will get in touch with you as soon as possible.');
|
$this->emit('success', 'Your message has been sent successfully. <br>We will get in touch with you as soon as possible.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class DiscordSettings extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->submit();
|
$this->submit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
$this->team->discord_enabled = false;
|
$this->team->discord_enabled = false;
|
||||||
$this->validate();
|
$this->validate();
|
||||||
@@ -46,9 +46,7 @@ class DiscordSettings extends Component
|
|||||||
public function saveModel()
|
public function saveModel()
|
||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
if (is_a($this->team, Team::class)) {
|
|
||||||
refreshSession();
|
refreshSession();
|
||||||
}
|
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->emit('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,9 +62,10 @@ class EmailSettings extends Component
|
|||||||
'team.smtp_from_name' => 'required',
|
'team.smtp_from_name' => 'required',
|
||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
@@ -81,9 +82,10 @@ class EmailSettings extends Component
|
|||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,9 +94,9 @@ class EmailSettings extends Component
|
|||||||
try {
|
try {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
$this->submitResend();
|
$this->submitResend();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
@@ -102,17 +104,15 @@ class EmailSettings extends Component
|
|||||||
try {
|
try {
|
||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
$this->submit();
|
$this->submit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function saveModel()
|
public function saveModel()
|
||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
if (is_a($this->team, Team::class)) {
|
|
||||||
refreshSession();
|
refreshSession();
|
||||||
}
|
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->emit('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
public function submit()
|
public function submit()
|
||||||
@@ -130,10 +130,11 @@ class EmailSettings extends Component
|
|||||||
'team.smtp_timeout' => 'nullable',
|
'team.smtp_timeout' => 'nullable',
|
||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->smtp_enabled = false;
|
$this->team->smtp_enabled = false;
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function submitResend()
|
public function submitResend()
|
||||||
@@ -141,14 +142,16 @@ class EmailSettings extends Component
|
|||||||
try {
|
try {
|
||||||
$this->resetErrorBag();
|
$this->resetErrorBag();
|
||||||
$this->validate([
|
$this->validate([
|
||||||
|
'team.smtp_from_address' => 'required|email',
|
||||||
|
'team.smtp_from_name' => 'required',
|
||||||
'team.resend_api_key' => 'required'
|
'team.resend_api_key' => 'required'
|
||||||
]);
|
]);
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
refreshSession();
|
refreshSession();
|
||||||
$this->emit('success', 'Settings saved successfully.');
|
$this->emit('success', 'Settings saved successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->team->resend_enabled = false;
|
$this->team->resend_enabled = false;
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function copyFromInstanceSettings()
|
public function copyFromInstanceSettings()
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class TelegramSettings extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->submit();
|
$this->submit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
$this->team->telegram_enabled = false;
|
$this->team->telegram_enabled = false;
|
||||||
$this->validate();
|
$this->validate();
|
||||||
@@ -52,9 +52,7 @@ class TelegramSettings extends Component
|
|||||||
public function saveModel()
|
public function saveModel()
|
||||||
{
|
{
|
||||||
$this->team->save();
|
$this->team->save();
|
||||||
if (is_a($this->team, Team::class)) {
|
|
||||||
refreshSession();
|
refreshSession();
|
||||||
}
|
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->emit('success', 'Settings saved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use Livewire\Component;
|
|||||||
class Change extends Component
|
class Change extends Component
|
||||||
{
|
{
|
||||||
public PrivateKey $private_key;
|
public PrivateKey $private_key;
|
||||||
|
public $public_key;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'private_key.name' => 'required|string',
|
'private_key.name' => 'required|string',
|
||||||
'private_key.description' => 'nullable|string',
|
'private_key.description' => 'nullable|string',
|
||||||
@@ -21,6 +21,14 @@ class Change extends Component
|
|||||||
'private_key.private_key' => 'private key'
|
'private_key.private_key' => 'private key'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->public_key = $this->private_key->publicKey();
|
||||||
|
}catch(\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -30,8 +38,8 @@ class Change extends Component
|
|||||||
return redirect()->route('security.private-key.index');
|
return redirect()->route('security.private-key.index');
|
||||||
}
|
}
|
||||||
$this->emit('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.');
|
$this->emit('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,8 +49,8 @@ class Change extends Component
|
|||||||
$this->private_key->private_key = formatPrivateKey($this->private_key->private_key);
|
$this->private_key->private_key = formatPrivateKey($this->private_key->private_key);
|
||||||
$this->private_key->save();
|
$this->private_key->save();
|
||||||
refresh_server_connection($this->private_key);
|
refresh_server_connection($this->private_key);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,20 @@
|
|||||||
namespace App\Http\Livewire\PrivateKey;
|
namespace App\Http\Livewire\PrivateKey;
|
||||||
|
|
||||||
use App\Models\PrivateKey;
|
use App\Models\PrivateKey;
|
||||||
|
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use phpseclib3\Crypt\PublicKeyLoader;
|
||||||
|
|
||||||
class Create extends Component
|
class Create extends Component
|
||||||
{
|
{
|
||||||
public string|null $from = null;
|
use WithRateLimiting;
|
||||||
public string $name;
|
public string $name;
|
||||||
public string|null $description = null;
|
|
||||||
public string $value;
|
public string $value;
|
||||||
|
|
||||||
|
public ?string $from = null;
|
||||||
|
public ?string $description = null;
|
||||||
|
public ?string $publicKey = null;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'value' => 'required|string',
|
'value' => 'required|string',
|
||||||
@@ -20,6 +26,32 @@ class Create extends Component
|
|||||||
'value' => 'private Key',
|
'value' => 'private Key',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function generateNewKey()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->rateLimit(10);
|
||||||
|
$this->name = generate_random_name();
|
||||||
|
$this->description = 'Created by Coolify';
|
||||||
|
['private' => $this->value, 'public' => $this->publicKey] = generateSSHKey();
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function updated($updateProperty)
|
||||||
|
{
|
||||||
|
if ($updateProperty === 'value') {
|
||||||
|
try {
|
||||||
|
$this->publicKey = PublicKeyLoader::load($this->$updateProperty)->getPublicKey()->toString('OpenSSH',['comment' => '']);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
if ($this->$updateProperty === "") {
|
||||||
|
$this->publicKey = "";
|
||||||
|
} else {
|
||||||
|
$this->publicKey = "Invalid private key";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->validateOnly($updateProperty);
|
||||||
|
}
|
||||||
public function createPrivateKey()
|
public function createPrivateKey()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$this->validate();
|
||||||
@@ -37,9 +69,9 @@ class Create extends Component
|
|||||||
if ($this->from === 'server') {
|
if ($this->from === 'server') {
|
||||||
return redirect()->route('server.create');
|
return redirect()->route('server.create');
|
||||||
}
|
}
|
||||||
return redirect()->route('private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class Form extends Component
|
|||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
]);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ class AddEmpty extends Component
|
|||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
return redirect()->route('project.show', $project->uuid);
|
return redirect()->route('project.show', $project->uuid);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->name = '';
|
$this->name = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class AddEnvironment extends Component
|
|||||||
'project_uuid' => $this->project->uuid,
|
'project_uuid' => $this->project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
general_error_handler($e, $this);
|
handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->name = '';
|
$this->name = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,12 +60,16 @@ class DeploymentNavbar extends Component
|
|||||||
$previous_logs[] = $new_log_entry;
|
$previous_logs[] = $new_log_entry;
|
||||||
$this->application_deployment_queue->update([
|
$this->application_deployment_queue->update([
|
||||||
'logs' => json_encode($previous_logs, flags: JSON_THROW_ON_ERROR),
|
'logs' => json_encode($previous_logs, flags: JSON_THROW_ON_ERROR),
|
||||||
'current_process_id' => null,
|
|
||||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->application_deployment_queue->update([
|
||||||
|
'current_process_id' => null,
|
||||||
|
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||||
|
]);
|
||||||
|
queue_next_deployment($this->application);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,25 +3,27 @@
|
|||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Http\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\InstanceSettings;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
|
||||||
|
|
||||||
class General extends Component
|
class General extends Component
|
||||||
{
|
{
|
||||||
public string $applicationId;
|
public string $applicationId;
|
||||||
|
|
||||||
public Application $application;
|
public Application $application;
|
||||||
|
public Collection $services;
|
||||||
public string $name;
|
public string $name;
|
||||||
public string|null $fqdn;
|
public ?string $fqdn = null;
|
||||||
public string $git_repository;
|
public string $git_repository;
|
||||||
public string $git_branch;
|
public string $git_branch;
|
||||||
public string|null $git_commit_sha;
|
public ?string $git_commit_sha = null;
|
||||||
public string $build_pack;
|
public string $build_pack;
|
||||||
public string|null $wildcard_domain = null;
|
public ?string $ports_exposes = null;
|
||||||
public string|null $server_wildcard_domain = null;
|
|
||||||
public string|null $global_wildcard_domain = null;
|
public $customLabels;
|
||||||
|
public bool $labelsChanged = false;
|
||||||
|
public bool $isConfigurationChanged = false;
|
||||||
|
|
||||||
public bool $is_static;
|
public bool $is_static;
|
||||||
public bool $is_git_submodules_enabled;
|
public bool $is_git_submodules_enabled;
|
||||||
@@ -31,6 +33,7 @@ class General extends Component
|
|||||||
public bool $is_auto_deploy_enabled;
|
public bool $is_auto_deploy_enabled;
|
||||||
public bool $is_force_https_enabled;
|
public bool $is_force_https_enabled;
|
||||||
|
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'application.name' => 'required',
|
'application.name' => 'required',
|
||||||
'application.description' => 'nullable',
|
'application.description' => 'nullable',
|
||||||
@@ -48,6 +51,10 @@ class General extends Component
|
|||||||
'application.ports_exposes' => 'required',
|
'application.ports_exposes' => 'required',
|
||||||
'application.ports_mappings' => 'nullable',
|
'application.ports_mappings' => 'nullable',
|
||||||
'application.dockerfile' => 'nullable',
|
'application.dockerfile' => 'nullable',
|
||||||
|
'application.docker_registry_image_name' => 'nullable',
|
||||||
|
'application.docker_registry_image_tag' => 'nullable',
|
||||||
|
'application.dockerfile_location' => 'nullable',
|
||||||
|
'application.custom_labels' => 'nullable',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@@ -66,11 +73,55 @@ class General extends Component
|
|||||||
'application.ports_exposes' => 'Ports exposes',
|
'application.ports_exposes' => 'Ports exposes',
|
||||||
'application.ports_mappings' => 'Ports mappings',
|
'application.ports_mappings' => 'Ports mappings',
|
||||||
'application.dockerfile' => 'Dockerfile',
|
'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',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
if (data_get($this->application, 'settings')) {
|
||||||
|
$this->is_static = $this->application->settings->is_static;
|
||||||
|
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
||||||
|
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
||||||
|
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||||
|
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
||||||
|
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
||||||
|
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||||
|
}
|
||||||
|
$this->checkLabelUpdates();
|
||||||
|
}
|
||||||
|
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 instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
// @TODO: find another way - if possible
|
// @TODO: find another way - if possible
|
||||||
|
$force_https = $this->application->settings->is_force_https_enabled;
|
||||||
$this->application->settings->is_static = $this->is_static;
|
$this->application->settings->is_static = $this->is_static;
|
||||||
if ($this->is_static) {
|
if ($this->is_static) {
|
||||||
$this->application->ports_exposes = 80;
|
$this->application->ports_exposes = 80;
|
||||||
@@ -87,67 +138,57 @@ class General extends Component
|
|||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
$this->checkWildCardDomain();
|
$this->checkLabelUpdates();
|
||||||
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
|
if ($force_https !== $this->is_force_https_enabled) {
|
||||||
|
$this->resetDefaultLabels(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkWildCardDomain()
|
public function getWildcardDomain()
|
||||||
{
|
{
|
||||||
$coolify_instance_settings = InstanceSettings::get();
|
$server = data_get($this->application, 'destination.server');
|
||||||
$this->server_wildcard_domain = data_get($this->application, 'destination.server.settings.wildcard_domain');
|
if ($server) {
|
||||||
$this->global_wildcard_domain = data_get($coolify_instance_settings, 'wildcard_domain');
|
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||||
$this->wildcard_domain = $this->server_wildcard_domain ?? $this->global_wildcard_domain ?? null;
|
$this->application->fqdn = $fqdn;
|
||||||
}
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$this->is_static = $this->application->settings->is_static;
|
|
||||||
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
|
||||||
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
|
||||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
|
||||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
|
||||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
|
||||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
|
||||||
$this->checkWildCardDomain();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateGlobalRandomDomain()
|
|
||||||
{
|
|
||||||
// Set wildcard domain based on Global wildcard domain
|
|
||||||
$url = Url::fromString($this->global_wildcard_domain);
|
|
||||||
$host = $url->getHost();
|
|
||||||
$path = $url->getPath() === '/' ? '' : $url->getPath();
|
|
||||||
$scheme = $url->getScheme();
|
|
||||||
$this->application->fqdn = $scheme . '://' . $this->application->uuid . '.' . $host . $path;
|
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public function generateServerRandomDomain()
|
public function resetDefaultLabels($showToaster = true)
|
||||||
{
|
{
|
||||||
// Set wildcard domain based on Server wildcard domain
|
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||||
$url = Url::fromString($this->server_wildcard_domain);
|
$this->ports_exposes = $this->application->ports_exposes;
|
||||||
$host = $url->getHost();
|
$this->submit($showToaster);
|
||||||
$path = $url->getPath() === '/' ? '' : $url->getPath();
|
|
||||||
$scheme = $url->getScheme();
|
|
||||||
$this->application->fqdn = $scheme . '://' . $this->application->uuid . '.' . $host . $path;
|
|
||||||
$this->application->save();
|
|
||||||
$this->emit('success', 'Application settings updated!');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit()
|
public function updatedApplicationFqdn()
|
||||||
|
{
|
||||||
|
$this->resetDefaultLabels(false);
|
||||||
|
$this->emit('success', 'Labels reseted to default!');
|
||||||
|
}
|
||||||
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
ray($this->application);
|
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
if (data_get($this->application,'fqdn')) {
|
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) {
|
$domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||||
return Str::of($domain)->trim()->lower();
|
return Str::of($domain)->trim()->lower();
|
||||||
});
|
});
|
||||||
$this->application->fqdn = $domains->implode(',');
|
$this->application->fqdn = $domains->implode(',');
|
||||||
}
|
}
|
||||||
if ($this->application->dockerfile) {
|
if (data_get($this->application, 'dockerfile')) {
|
||||||
$port = get_port_from_dockerfile($this->application->dockerfile);
|
$port = get_port_from_dockerfile($this->application->dockerfile);
|
||||||
if ($port) {
|
if ($port && !$this->application->ports_exposes) {
|
||||||
$this->application->ports_exposes = $port;
|
$this->application->ports_exposes = $port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,10 +198,17 @@ class General extends Component
|
|||||||
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
||||||
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
||||||
}
|
}
|
||||||
|
if (gettype($this->customLabels) === 'string') {
|
||||||
|
$this->customLabels = str($this->customLabels)->replace(',', "\n");
|
||||||
|
}
|
||||||
|
$this->application->custom_labels = $this->customLabels->explode("\n")->implode(',');
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
$this->emit('success', 'Application settings updated!');
|
$showToaster && $this->emit('success', 'Application settings updated!');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->checkLabelUpdates();
|
||||||
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Http\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Jobs\ApplicationContainerStatusJob;
|
use App\Actions\Application\StopApplication;
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Notifications\Application\StatusChanged;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -22,10 +22,13 @@ class Heading extends Component
|
|||||||
|
|
||||||
public function check_status()
|
public function check_status()
|
||||||
{
|
{
|
||||||
dispatch_sync(new ApplicationContainerStatusJob(
|
if ($this->application->destination->server->isFunctional()) {
|
||||||
application: $this->application,
|
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||||
));
|
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
|
$this->application->previews->each(function ($preview) {
|
||||||
|
$preview->refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function force_deploy_without_cache()
|
public function force_deploy_without_cache()
|
||||||
@@ -57,21 +60,9 @@ class Heading extends Component
|
|||||||
|
|
||||||
public function stop()
|
public function stop()
|
||||||
{
|
{
|
||||||
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id);
|
StopApplication::run($this->application);
|
||||||
if ($containers->count() === 0) {
|
$this->application->status = 'exited';
|
||||||
return;
|
|
||||||
}
|
|
||||||
foreach ($containers as $container) {
|
|
||||||
$containerName = data_get($container, 'Names');
|
|
||||||
if ($containerName) {
|
|
||||||
remote_process(
|
|
||||||
["docker rm -f {$containerName}"],
|
|
||||||
$this->application->destination->server
|
|
||||||
);
|
|
||||||
$this->application->status = 'stopped';
|
|
||||||
$this->application->save();
|
$this->application->save();
|
||||||
// $this->application->environment->project->team->notify(new StatusChanged($this->application));
|
$this->application->refresh();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ class Form extends Component
|
|||||||
public function generate_real_url()
|
public function generate_real_url()
|
||||||
{
|
{
|
||||||
if (data_get($this->application, 'fqdn')) {
|
if (data_get($this->application, 'fqdn')) {
|
||||||
$url = Url::fromString($this->application->fqdn);
|
$firstFqdn = Str::of($this->application->fqdn)->before(',');
|
||||||
|
$url = Url::fromString($firstFqdn);
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
$this->preview_url_template = Str::of($this->application->preview_url_template)->replace('{{domain}}', $host);
|
$this->preview_url_template = Str::of($this->application->preview_url_template)->replace('{{domain}}', $host);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Http\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Jobs\ApplicationContainerStatusJob;
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -23,23 +22,15 @@ class Previews extends Component
|
|||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadStatus($pull_request_id)
|
|
||||||
{
|
|
||||||
dispatch(new ApplicationContainerStatusJob(
|
|
||||||
application: $this->application,
|
|
||||||
pullRequestId: $pull_request_id
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function load_prs()
|
public function load_prs()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
['rate_limit_remaining' => $rate_limit_remaining, 'data' => $data] = git_api(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/pulls");
|
['rate_limit_remaining' => $rate_limit_remaining, 'data' => $data] = githubApi(source: $this->application->source, endpoint: "/repos/{$this->application->git_repository}/pulls");
|
||||||
$this->rate_limit_remaining = $rate_limit_remaining;
|
$this->rate_limit_remaining = $rate_limit_remaining;
|
||||||
$this->pull_requests = $data->sortBy('number')->values();
|
$this->pull_requests = $data->sortBy('number')->values();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->rate_limit_remaining = 0;
|
$this->rate_limit_remaining = 0;
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +59,7 @@ class Previews extends Component
|
|||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
]);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,14 +72,13 @@ class Previews extends Component
|
|||||||
public function stop(int $pull_request_id)
|
public function stop(int $pull_request_id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$container_name = generateApplicationContainerName($this->application->uuid, $pull_request_id);
|
$container_name = generateApplicationContainerName($this->application, $pull_request_id);
|
||||||
ray('Stopping container: ' . $container_name);
|
|
||||||
|
|
||||||
instant_remote_process(["docker rm -f $container_name"], $this->application->destination->server, throwError: false);
|
instant_remote_process(["docker rm -f $container_name"], $this->application->destination->server, throwError: false);
|
||||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->delete();
|
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->delete();
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class Rollback extends Component
|
|||||||
];
|
];
|
||||||
})->toArray();
|
})->toArray();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
143
app/Http/Livewire/Project/CloneProject.php
Normal file
143
app/Http/Livewire/Project/CloneProject.php
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project;
|
||||||
|
|
||||||
|
use App\Models\Environment;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\Server;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
class CloneProject extends Component
|
||||||
|
{
|
||||||
|
public string $project_uuid;
|
||||||
|
public string $environment_name;
|
||||||
|
public int $project_id;
|
||||||
|
|
||||||
|
public Project $project;
|
||||||
|
public $environments;
|
||||||
|
public $servers;
|
||||||
|
public ?Environment $environment = null;
|
||||||
|
public ?int $selectedServer = null;
|
||||||
|
public ?Server $server = null;
|
||||||
|
public $resources = [];
|
||||||
|
public string $newProjectName = '';
|
||||||
|
|
||||||
|
protected $messages = [
|
||||||
|
'selectedServer' => 'Please select a server.',
|
||||||
|
'newProjectName' => 'Please enter a name for the new project.',
|
||||||
|
];
|
||||||
|
public function mount($project_uuid)
|
||||||
|
{
|
||||||
|
$this->project_uuid = $project_uuid;
|
||||||
|
$this->project = Project::where('uuid', $project_uuid)->firstOrFail();
|
||||||
|
$this->environment = $this->project->environments->where('name', $this->environment_name)->first();
|
||||||
|
$this->project_id = $this->project->id;
|
||||||
|
$this->servers = currentTeam()->servers;
|
||||||
|
$this->newProjectName = $this->project->name . ' (clone)';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.clone-project');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectServer($server_id)
|
||||||
|
{
|
||||||
|
$this->selectedServer = $server_id;
|
||||||
|
$this->server = $this->servers->where('id', $server_id)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clone()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate([
|
||||||
|
'selectedServer' => 'required',
|
||||||
|
'newProjectName' => 'required',
|
||||||
|
]);
|
||||||
|
$newProject = Project::create([
|
||||||
|
'name' => $this->newProjectName,
|
||||||
|
'team_id' => currentTeam()->id,
|
||||||
|
'description' => $this->project->description . ' (clone)',
|
||||||
|
]);
|
||||||
|
if ($this->environment->id !== 1) {
|
||||||
|
$newProject->environments()->create([
|
||||||
|
'name' => $this->environment->name,
|
||||||
|
]);
|
||||||
|
$newProject->environments()->find(1)->delete();
|
||||||
|
}
|
||||||
|
$newEnvironment = $newProject->environments->first();
|
||||||
|
// Clone Applications
|
||||||
|
$applications = $this->environment->applications;
|
||||||
|
$databases = $this->environment->databases();
|
||||||
|
$services = $this->environment->services;
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
$uuid = (string)new Cuid2(7);
|
||||||
|
$newApplication = $application->replicate()->fill([
|
||||||
|
'uuid' => $uuid,
|
||||||
|
'fqdn' => generateFqdn($this->server, $uuid),
|
||||||
|
'status' => 'exited',
|
||||||
|
'environment_id' => $newEnvironment->id,
|
||||||
|
'destination_id' => $this->selectedServer,
|
||||||
|
]);
|
||||||
|
$newApplication->environment_id = $newProject->environments->first()->id;
|
||||||
|
$newApplication->save();
|
||||||
|
$environmentVaribles = $application->environment_variables()->get();
|
||||||
|
foreach ($environmentVaribles as $environmentVarible) {
|
||||||
|
$newEnvironmentVariable = $environmentVarible->replicate()->fill([
|
||||||
|
'application_id' => $newApplication->id,
|
||||||
|
]);
|
||||||
|
$newEnvironmentVariable->save();
|
||||||
|
}
|
||||||
|
$persistentVolumes = $application->persistentStorages()->get();
|
||||||
|
foreach ($persistentVolumes as $volume) {
|
||||||
|
$newPersistentVolume = $volume->replicate()->fill([
|
||||||
|
'name' => $newApplication->uuid . '-' . str($volume->name)->afterLast('-'),
|
||||||
|
'resource_id' => $newApplication->id,
|
||||||
|
]);
|
||||||
|
$newPersistentVolume->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($databases as $database) {
|
||||||
|
$uuid = (string)new Cuid2(7);
|
||||||
|
$newDatabase = $database->replicate()->fill([
|
||||||
|
'uuid' => $uuid,
|
||||||
|
'environment_id' => $newEnvironment->id,
|
||||||
|
'destination_id' => $this->selectedServer,
|
||||||
|
]);
|
||||||
|
$newDatabase->environment_id = $newProject->environments->first()->id;
|
||||||
|
$newDatabase->save();
|
||||||
|
$environmentVaribles = $database->environment_variables()->get();
|
||||||
|
foreach ($environmentVaribles as $environmentVarible) {
|
||||||
|
$payload = [];
|
||||||
|
if ($database->type() === 'standalone-postgres') {
|
||||||
|
$payload['standalone_postgresql_id'] = $newDatabase->id;
|
||||||
|
} else if ($database->type() === 'standalone_redis') {
|
||||||
|
$payload['standalone_redis_id'] = $newDatabase->id;
|
||||||
|
} else if ($database->type() === 'standalone_mongodb') {
|
||||||
|
$payload['standalone_mongodb_id'] = $newDatabase->id;
|
||||||
|
}
|
||||||
|
$newEnvironmentVariable = $environmentVarible->replicate()->fill($payload);
|
||||||
|
$newEnvironmentVariable->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$uuid = (string)new Cuid2(7);
|
||||||
|
$newService = $service->replicate()->fill([
|
||||||
|
'uuid' => $uuid,
|
||||||
|
'environment_id' => $newEnvironment->id,
|
||||||
|
'destination_id' => $this->selectedServer,
|
||||||
|
]);
|
||||||
|
$newService->environment_id = $newProject->environments->first()->id;
|
||||||
|
$newService->save();
|
||||||
|
$newService->parse();
|
||||||
|
}
|
||||||
|
return redirect()->route('project.resources', [
|
||||||
|
'project_uuid' => $newProject->uuid,
|
||||||
|
'environment_name' => $newEnvironment->name,
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ class BackupEdit extends Component
|
|||||||
{
|
{
|
||||||
public $backup;
|
public $backup;
|
||||||
public $s3s;
|
public $s3s;
|
||||||
|
public ?string $status = null;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
@@ -16,6 +17,7 @@ class BackupEdit extends Component
|
|||||||
'backup.number_of_backups_locally' => 'required|integer|min:1',
|
'backup.number_of_backups_locally' => 'required|integer|min:1',
|
||||||
'backup.save_s3' => 'required|boolean',
|
'backup.save_s3' => 'required|boolean',
|
||||||
'backup.s3_storage_id' => 'nullable|integer',
|
'backup.s3_storage_id' => 'nullable|integer',
|
||||||
|
'backup.databases_to_backup' => 'nullable',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'backup.enabled' => 'Enabled',
|
'backup.enabled' => 'Enabled',
|
||||||
@@ -23,6 +25,7 @@ class BackupEdit extends Component
|
|||||||
'backup.number_of_backups_locally' => 'Number of Backups Locally',
|
'backup.number_of_backups_locally' => 'Number of Backups Locally',
|
||||||
'backup.save_s3' => 'Save to S3',
|
'backup.save_s3' => 'Save to S3',
|
||||||
'backup.s3_storage_id' => 'S3 Storage',
|
'backup.s3_storage_id' => 'S3 Storage',
|
||||||
|
'backup.databases_to_backup' => 'Databases to Backup',
|
||||||
];
|
];
|
||||||
protected $messages = [
|
protected $messages = [
|
||||||
'backup.s3_storage_id' => 'Select a S3 Storage',
|
'backup.s3_storage_id' => 'Select a S3 Storage',
|
||||||
@@ -36,7 +39,6 @@ class BackupEdit extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
// TODO: Delete backup from server and add a confirmation modal
|
// TODO: Delete backup from server and add a confirmation modal
|
||||||
@@ -48,10 +50,11 @@ class BackupEdit extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->custom_validate();
|
$this->custom_validate();
|
||||||
|
|
||||||
$this->backup->save();
|
$this->backup->save();
|
||||||
$this->backup->refresh();
|
$this->backup->refresh();
|
||||||
$this->emit('success', 'Backup updated successfully');
|
$this->emit('success', 'Backup updated successfully');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->emit('error', $e->getMessage());
|
$this->emit('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,13 +73,15 @@ class BackupEdit extends Component
|
|||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
ray($this->backup->s3_storage_id);
|
|
||||||
try {
|
try {
|
||||||
$this->custom_validate();
|
$this->custom_validate();
|
||||||
|
if ($this->backup->databases_to_backup == '' || $this->backup->databases_to_backup === null) {
|
||||||
|
$this->backup->databases_to_backup = null;
|
||||||
|
}
|
||||||
$this->backup->save();
|
$this->backup->save();
|
||||||
$this->backup->refresh();
|
$this->backup->refresh();
|
||||||
$this->emit('success', 'Backup updated successfully');
|
$this->emit('success', 'Backup updated successfully');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->emit('error', $e->getMessage());
|
$this->emit('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ class BackupNow extends Component
|
|||||||
dispatch(new DatabaseBackupJob(
|
dispatch(new DatabaseBackupJob(
|
||||||
backup: $this->backup
|
backup: $this->backup
|
||||||
));
|
));
|
||||||
$this->emit('success', 'Backup queued. It will be available in a few minutes');
|
$this->emit('success', 'Backup queued. It will be available in a few minutes.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class CreateScheduledBackup extends Component
|
|||||||
public $database;
|
public $database;
|
||||||
public $frequency;
|
public $frequency;
|
||||||
public bool $enabled = true;
|
public bool $enabled = true;
|
||||||
public bool $save_s3 = true;
|
public bool $save_s3 = false;
|
||||||
public $s3_storage_id;
|
public $s3_storage_id;
|
||||||
public $s3s;
|
public $s3s;
|
||||||
|
|
||||||
@@ -22,6 +22,11 @@ class CreateScheduledBackup extends Component
|
|||||||
'frequency' => 'Backup Frequency',
|
'frequency' => 'Backup Frequency',
|
||||||
'save_s3' => 'Save to S3',
|
'save_s3' => 'Save to S3',
|
||||||
];
|
];
|
||||||
|
public function mount() {
|
||||||
|
if ($this->s3s->count() > 0) {
|
||||||
|
$this->s3_storage_id = $this->s3s->first()->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function submit(): void
|
public function submit(): void
|
||||||
{
|
{
|
||||||
@@ -32,7 +37,7 @@ class CreateScheduledBackup extends Component
|
|||||||
$this->emit('error', 'Invalid Cron / Human expression.');
|
$this->emit('error', 'Invalid Cron / Human expression.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ScheduledDatabaseBackup::create([
|
$payload = [
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'frequency' => $this->frequency,
|
'frequency' => $this->frequency,
|
||||||
'save_s3' => $this->save_s3,
|
'save_s3' => $this->save_s3,
|
||||||
@@ -40,10 +45,14 @@ class CreateScheduledBackup extends Component
|
|||||||
'database_id' => $this->database->id,
|
'database_id' => $this->database->id,
|
||||||
'database_type' => $this->database->getMorphClass(),
|
'database_type' => $this->database->getMorphClass(),
|
||||||
'team_id' => currentTeam()->id,
|
'team_id' => currentTeam()->id,
|
||||||
]);
|
];
|
||||||
|
if ($this->database->type() === 'standalone-postgresql') {
|
||||||
|
$payload['databases_to_backup'] = $this->database->postgres_db;
|
||||||
|
}
|
||||||
|
ScheduledDatabaseBackup::create($payload);
|
||||||
$this->emit('refreshScheduledBackups');
|
$this->emit('refreshScheduledBackups');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
general_error_handler($e, $this);
|
handleError($e, $this);
|
||||||
} finally {
|
} finally {
|
||||||
$this->frequency = '';
|
$this->frequency = '';
|
||||||
$this->save_s3 = true;
|
$this->save_s3 = true;
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Http\Livewire\Project\Database;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartMongodb;
|
||||||
use App\Actions\Database\StartPostgresql;
|
use App\Actions\Database\StartPostgresql;
|
||||||
use App\Jobs\DatabaseContainerStatusJob;
|
use App\Actions\Database\StartRedis;
|
||||||
use App\Notifications\Application\StatusChanged;
|
use App\Actions\Database\StopDatabase;
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Heading extends Component
|
class Heading extends Component
|
||||||
@@ -25,9 +27,7 @@ class Heading extends Component
|
|||||||
|
|
||||||
public function check_status()
|
public function check_status()
|
||||||
{
|
{
|
||||||
dispatch_sync(new DatabaseContainerStatusJob(
|
dispatch_sync(new ContainerStatusJob($this->database->destination->server));
|
||||||
database: $this->database,
|
|
||||||
));
|
|
||||||
$this->database->refresh();
|
$this->database->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,24 +38,24 @@ class Heading extends Component
|
|||||||
|
|
||||||
public function stop()
|
public function stop()
|
||||||
{
|
{
|
||||||
remote_process(
|
StopDatabase::run($this->database);
|
||||||
["docker rm -f {$this->database->uuid}"],
|
$this->database->status = 'exited';
|
||||||
$this->database->destination->server
|
|
||||||
);
|
|
||||||
if ($this->database->is_public) {
|
|
||||||
stopPostgresProxy($this->database);
|
|
||||||
$this->database->is_public = false;
|
|
||||||
}
|
|
||||||
$this->database->status = 'stopped';
|
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('refresh');
|
$this->check_status();
|
||||||
// $this->database->environment->project->team->notify(new StatusChanged($this->database));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function start()
|
public function start()
|
||||||
{
|
{
|
||||||
if ($this->database->type() === 'standalone-postgresql') {
|
if ($this->database->type() === 'standalone-postgresql') {
|
||||||
$activity = resolve(StartPostgresql::class)($this->database->destination->server, $this->database);
|
$activity = StartPostgresql::run($this->database);
|
||||||
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
|
}
|
||||||
|
if ($this->database->type() === 'standalone-redis') {
|
||||||
|
$activity = StartRedis::run($this->database);
|
||||||
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
|
}
|
||||||
|
if ($this->database->type() === 'standalone-mongodb') {
|
||||||
|
$activity = StartMongodb::run($this->database);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class InitScript extends Component
|
|||||||
$this->script['filename'] = $this->filename;
|
$this->script['filename'] = $this->filename;
|
||||||
$this->emitUp('save_init_script', $this->script);
|
$this->emitUp('save_init_script', $this->script);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
91
app/Http/Livewire/Project/Database/Mongodb/General.php
Normal file
91
app/Http/Livewire/Project/Database/Mongodb/General.php
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Database\Mongodb;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
use Exception;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class General extends Component
|
||||||
|
{
|
||||||
|
protected $listeners = ['refresh'];
|
||||||
|
|
||||||
|
public StandaloneMongodb $database;
|
||||||
|
public string $db_url;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'database.name' => 'required',
|
||||||
|
'database.description' => 'nullable',
|
||||||
|
'database.mongo_conf' => 'nullable',
|
||||||
|
'database.mongo_initdb_root_username' => 'required',
|
||||||
|
'database.mongo_initdb_root_password' => 'required',
|
||||||
|
'database.mongo_initdb_database' => 'required',
|
||||||
|
'database.image' => 'required',
|
||||||
|
'database.ports_mappings' => 'nullable',
|
||||||
|
'database.is_public' => 'nullable|boolean',
|
||||||
|
'database.public_port' => 'nullable|integer',
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'database.name' => 'Name',
|
||||||
|
'database.description' => 'Description',
|
||||||
|
'database.mongo_conf' => 'Mongo Configuration',
|
||||||
|
'database.mongo_initdb_root_username' => 'Root Username',
|
||||||
|
'database.mongo_initdb_root_password' => 'Root Password',
|
||||||
|
'database.mongo_initdb_database' => 'Database',
|
||||||
|
'database.image' => 'Image',
|
||||||
|
'database.ports_mappings' => 'Port Mapping',
|
||||||
|
'database.is_public' => 'Is Public',
|
||||||
|
'database.public_port' => 'Public Port',
|
||||||
|
];
|
||||||
|
public function submit() {
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
if ($this->database->mongo_conf === "") {
|
||||||
|
$this->database->mongo_conf = null;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->emit('success', 'Database updated successfully.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->database->is_public && !$this->database->public_port) {
|
||||||
|
$this->emit('error', 'Public port is required.');
|
||||||
|
$this->database->is_public = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
$this->emit('success', 'Starting TCP proxy...');
|
||||||
|
StartDatabaseProxy::run($this->database);
|
||||||
|
$this->emit('success', 'Database is now publicly accessible.');
|
||||||
|
} else {
|
||||||
|
StopDatabaseProxy::run($this->database);
|
||||||
|
$this->emit('success', 'Database is no longer publicly accessible.');
|
||||||
|
}
|
||||||
|
$this->db_url = $this->database->getDbUrl();
|
||||||
|
$this->database->save();
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
$this->database->is_public = !$this->database->is_public;
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function refresh(): void
|
||||||
|
{
|
||||||
|
$this->database->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->db_url = $this->database->getDbUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.database.mongodb.general');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Database\Postgresql;
|
namespace App\Http\Livewire\Project\Database\Postgresql;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
use function Aws\filter;
|
use function Aws\filter;
|
||||||
|
|
||||||
class General extends Component
|
class General extends Component
|
||||||
@@ -46,14 +49,7 @@ class General extends Component
|
|||||||
];
|
];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->getDbUrl();
|
$this->db_url = $this->database->getDbUrl();
|
||||||
}
|
|
||||||
public function getDbUrl() {
|
|
||||||
if ($this->database->is_public) {
|
|
||||||
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->destination->server->ip}:{$this->database->public_port}/{$this->database->postgres_db}";
|
|
||||||
} else {
|
|
||||||
$this->db_url = "postgres://{$this->database->postgres_user}:{$this->database->postgres_password}@{$this->database->uuid}:5432/{$this->database->postgres_db}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
@@ -65,17 +61,17 @@ class General extends Component
|
|||||||
}
|
}
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->emit('success', 'Starting TCP proxy...');
|
$this->emit('success', 'Starting TCP proxy...');
|
||||||
startPostgresProxy($this->database);
|
StartDatabaseProxy::run($this->database);
|
||||||
$this->emit('success', 'Database is now publicly accessible.');
|
$this->emit('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
stopPostgresProxy($this->database);
|
StopDatabaseProxy::run($this->database);
|
||||||
$this->emit('success', 'Database is no longer publicly accessible.');
|
$this->emit('success', 'Database is no longer publicly accessible.');
|
||||||
}
|
}
|
||||||
$this->getDbUrl();
|
$this->db_url = $this->database->getDbUrl();
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
} catch(Exception $e) {
|
} catch(\Throwable $e) {
|
||||||
$this->database->is_public = !$this->database->is_public;
|
$this->database->is_public = !$this->database->is_public;
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -140,7 +136,7 @@ class General extends Component
|
|||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->emit('success', 'Database updated successfully.');
|
$this->emit('success', 'Database updated successfully.');
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
86
app/Http/Livewire/Project/Database/Redis/General.php
Normal file
86
app/Http/Livewire/Project/Database/Redis/General.php
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Database\Redis;
|
||||||
|
|
||||||
|
use App\Actions\Database\StartDatabaseProxy;
|
||||||
|
use App\Actions\Database\StopDatabaseProxy;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Exception;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class General extends Component
|
||||||
|
{
|
||||||
|
protected $listeners = ['refresh'];
|
||||||
|
|
||||||
|
public StandaloneRedis $database;
|
||||||
|
public string $db_url;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'database.name' => 'required',
|
||||||
|
'database.description' => 'nullable',
|
||||||
|
'database.redis_conf' => 'nullable',
|
||||||
|
'database.redis_password' => 'required',
|
||||||
|
'database.image' => 'required',
|
||||||
|
'database.ports_mappings' => 'nullable',
|
||||||
|
'database.is_public' => 'nullable|boolean',
|
||||||
|
'database.public_port' => 'nullable|integer',
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'database.name' => 'Name',
|
||||||
|
'database.description' => 'Description',
|
||||||
|
'database.redis_conf' => 'Redis Configuration',
|
||||||
|
'database.redis_password' => 'Redis Password',
|
||||||
|
'database.image' => 'Image',
|
||||||
|
'database.ports_mappings' => 'Port Mapping',
|
||||||
|
'database.is_public' => 'Is Public',
|
||||||
|
'database.public_port' => 'Public Port',
|
||||||
|
];
|
||||||
|
public function submit() {
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
if ($this->database->redis_conf === "") {
|
||||||
|
$this->database->redis_conf = null;
|
||||||
|
}
|
||||||
|
$this->database->save();
|
||||||
|
$this->emit('success', 'Database updated successfully.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->database->is_public && !$this->database->public_port) {
|
||||||
|
$this->emit('error', 'Public port is required.');
|
||||||
|
$this->database->is_public = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
$this->emit('success', 'Starting TCP proxy...');
|
||||||
|
StartDatabaseProxy::run($this->database);
|
||||||
|
$this->emit('success', 'Database is now publicly accessible.');
|
||||||
|
} else {
|
||||||
|
StopDatabaseProxy::run($this->database);
|
||||||
|
$this->emit('success', 'Database is no longer publicly accessible.');
|
||||||
|
}
|
||||||
|
$this->db_url = $this->database->getDbUrl();
|
||||||
|
$this->database->save();
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
$this->database->is_public = !$this->database->is_public;
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function refresh(): void
|
||||||
|
{
|
||||||
|
$this->database->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->db_url = $this->database->getDbUrl();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.database.redis.general');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,8 +19,8 @@ class Edit extends Component
|
|||||||
try {
|
try {
|
||||||
$this->project->save();
|
$this->project->save();
|
||||||
$this->emit('saved');
|
$this->emit('saved');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
141
app/Http/Livewire/Project/New/DockerCompose.php
Normal file
141
app/Http/Livewire/Project/New/DockerCompose.php
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
app/Http/Livewire/Project/New/DockerImage.php
Normal file
78
app/Http/Livewire/Project/New/DockerImage.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\New;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\StandaloneDocker;
|
||||||
|
use App\Models\SwarmDocker;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class DockerImage extends Component
|
||||||
|
{
|
||||||
|
public string $dockerImage = '';
|
||||||
|
public array $parameters;
|
||||||
|
public array $query;
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$this->validate([
|
||||||
|
'dockerImage' => 'required'
|
||||||
|
]);
|
||||||
|
$image = Str::of($this->dockerImage)->before(':');
|
||||||
|
if (Str::of($this->dockerImage)->contains(':')) {
|
||||||
|
$tag = Str::of($this->dockerImage)->after(':');
|
||||||
|
} else {
|
||||||
|
$tag = 'latest';
|
||||||
|
}
|
||||||
|
$destination_uuid = $this->query['destination'];
|
||||||
|
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||||
|
if (!$destination) {
|
||||||
|
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
||||||
|
}
|
||||||
|
if (!$destination) {
|
||||||
|
throw new \Exception('Destination not found. What?!');
|
||||||
|
}
|
||||||
|
$destination_class = $destination->getMorphClass();
|
||||||
|
|
||||||
|
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||||
|
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||||
|
ray($image,$tag);
|
||||||
|
$application = Application::create([
|
||||||
|
'name' => 'docker-image-' . new Cuid2(7),
|
||||||
|
'repository_project_id' => 0,
|
||||||
|
'git_repository' => "coollabsio/coolify",
|
||||||
|
'git_branch' => 'main',
|
||||||
|
'build_pack' => 'dockerimage',
|
||||||
|
'ports_exposes' => 80,
|
||||||
|
'docker_registry_image_name' => $image,
|
||||||
|
'docker_registry_image_tag' => $tag,
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination_class,
|
||||||
|
'health_check_enabled' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
|
$application->update([
|
||||||
|
'name' => 'docker-image-' . $application->uuid,
|
||||||
|
'fqdn' => $fqdn
|
||||||
|
]);
|
||||||
|
|
||||||
|
redirect()->route('project.application.configuration', [
|
||||||
|
'application_uuid' => $application->uuid,
|
||||||
|
'environment_name' => $environment->name,
|
||||||
|
'project_uuid' => $project->uuid,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.new.docker-image');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,8 +9,9 @@ use App\Models\StandaloneDocker;
|
|||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use App\Traits\SaveFromRedirect;
|
use App\Traits\SaveFromRedirect;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Route;
|
use Spatie\Url\Url;
|
||||||
|
|
||||||
class GithubPrivateRepository extends Component
|
class GithubPrivateRepository extends Component
|
||||||
{
|
{
|
||||||
@@ -40,21 +41,6 @@ class GithubPrivateRepository extends Component
|
|||||||
public string|null $publish_directory = null;
|
public string|null $publish_directory = null;
|
||||||
protected int $page = 1;
|
protected int $page = 1;
|
||||||
|
|
||||||
// public function saveFromRedirect(string $route, ?Collection $parameters = null){
|
|
||||||
// session()->forget('from');
|
|
||||||
// if (!$parameters || $parameters->count() === 0) {
|
|
||||||
// $parameters = $this->parameters;
|
|
||||||
// }
|
|
||||||
// $parameters = collect($parameters) ?? collect([]);
|
|
||||||
// $queries = collect($this->query) ?? collect([]);
|
|
||||||
// $parameters = $parameters->merge($queries);
|
|
||||||
// session(['from'=> [
|
|
||||||
// 'back'=> $this->currentRoute,
|
|
||||||
// 'route' => $route,
|
|
||||||
// 'parameters' => $parameters
|
|
||||||
// ]]);
|
|
||||||
// return redirect()->route($route);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -110,6 +96,7 @@ class GithubPrivateRepository extends Component
|
|||||||
$this->loadBranchByPage();
|
$this->loadBranchByPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->selected_branch_name = data_get($this->branches,'0.name');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function loadBranchByPage()
|
protected function loadBranchByPage()
|
||||||
@@ -159,13 +146,19 @@ class GithubPrivateRepository extends Component
|
|||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
|
$application->fqdn = $fqdn;
|
||||||
|
|
||||||
|
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
|
||||||
|
$application->save();
|
||||||
|
|
||||||
redirect()->route('project.application.configuration', [
|
redirect()->route('project.application.configuration', [
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use App\Models\StandaloneDocker;
|
|||||||
use App\Models\SwarmDocker;
|
use App\Models\SwarmDocker;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class GithubPrivateRepositoryDeployKey extends Component
|
class GithubPrivateRepositoryDeployKey extends Component
|
||||||
{
|
{
|
||||||
@@ -29,7 +30,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
public string $repository_url;
|
public string $repository_url;
|
||||||
public string $branch;
|
public string $branch;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'repository_url' => 'required|url',
|
'repository_url' => 'required',
|
||||||
'branch' => 'required|string',
|
'branch' => 'required|string',
|
||||||
'port' => 'required|numeric',
|
'port' => 'required|numeric',
|
||||||
'is_static' => 'required|boolean',
|
'is_static' => 'required|boolean',
|
||||||
@@ -43,10 +44,9 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
'publish_directory' => 'Publish directory',
|
'publish_directory' => 'Publish directory',
|
||||||
];
|
];
|
||||||
private object $repository_url_parsed;
|
private object $repository_url_parsed;
|
||||||
private GithubApp|GitlabApp|null $git_source = null;
|
private GithubApp|GitlabApp|string $git_source = 'other';
|
||||||
private string $git_host;
|
private ?string $git_host = null;
|
||||||
private string $git_repository;
|
private string $git_repository;
|
||||||
private string $git_branch;
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -93,11 +93,24 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
|
|
||||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||||
|
if ($this->git_source === 'other') {
|
||||||
$application_init = [
|
$application_init = [
|
||||||
'name' => generate_random_name(),
|
'name' => generate_random_name(),
|
||||||
'git_repository' => $this->git_repository,
|
'git_repository' => $this->git_repository,
|
||||||
'git_branch' => $this->git_branch,
|
'git_branch' => $this->branch,
|
||||||
'git_full_url' => "git@$this->git_host:$this->git_repository.git",
|
'build_pack' => 'nixpacks',
|
||||||
|
'ports_exposes' => $this->port,
|
||||||
|
'publish_directory' => $this->publish_directory,
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination_class,
|
||||||
|
'private_key_id' => $this->private_key_id,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$application_init = [
|
||||||
|
'name' => generate_random_name(),
|
||||||
|
'git_repository' => $this->git_repository,
|
||||||
|
'git_branch' => $this->branch,
|
||||||
'build_pack' => 'nixpacks',
|
'build_pack' => 'nixpacks',
|
||||||
'ports_exposes' => $this->port,
|
'ports_exposes' => $this->port,
|
||||||
'publish_directory' => $this->publish_directory,
|
'publish_directory' => $this->publish_directory,
|
||||||
@@ -108,17 +121,24 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
'source_id' => $this->git_source->id,
|
'source_id' => $this->git_source->id,
|
||||||
'source_type' => $this->git_source->getMorphClass()
|
'source_type' => $this->git_source->getMorphClass()
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$application = Application::create($application_init);
|
$application = Application::create($application_init);
|
||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
|
$application->fqdn = $fqdn;
|
||||||
|
$application->name = generate_random_name($application->uuid);
|
||||||
|
$application->save();
|
||||||
|
|
||||||
return redirect()->route('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,18 +147,18 @@ class GithubPrivateRepositoryDeployKey extends Component
|
|||||||
$this->repository_url_parsed = Url::fromString($this->repository_url);
|
$this->repository_url_parsed = Url::fromString($this->repository_url);
|
||||||
$this->git_host = $this->repository_url_parsed->getHost();
|
$this->git_host = $this->repository_url_parsed->getHost();
|
||||||
$this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2);
|
$this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2);
|
||||||
if ($this->branch) {
|
|
||||||
$this->git_branch = $this->branch;
|
|
||||||
} else {
|
|
||||||
$this->git_branch = $this->repository_url_parsed->getSegment(4) ?? 'main';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->git_host == 'github.com') {
|
if ($this->git_host == 'github.com') {
|
||||||
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
||||||
} elseif ($this->git_host == 'gitlab.com') {
|
return;
|
||||||
$this->git_source = GitlabApp::where('name', 'Public GitLab')->first();
|
|
||||||
} elseif ($this->git_host == 'bitbucket.org') {
|
|
||||||
// Not supported yet
|
|
||||||
}
|
}
|
||||||
|
if (Str::of($this->repository_url)->startsWith('http')) {
|
||||||
|
$this->git_host = $this->repository_url_parsed->getHost();
|
||||||
|
$this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2);
|
||||||
|
$this->git_repository = Str::finish("git@$this->git_host:$this->git_repository", '.git');
|
||||||
|
} else {
|
||||||
|
$this->git_repository = $this->repository_url;
|
||||||
|
}
|
||||||
|
$this->git_source = 'other';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ class PublicGitRepository extends Component
|
|||||||
public string $git_branch = 'main';
|
public string $git_branch = 'main';
|
||||||
public int $rate_limit_remaining = 0;
|
public int $rate_limit_remaining = 0;
|
||||||
public $rate_limit_reset = 0;
|
public $rate_limit_reset = 0;
|
||||||
|
private object $repository_url_parsed;
|
||||||
|
public GithubApp|GitlabApp|string $git_source = 'other';
|
||||||
|
public string $git_host;
|
||||||
|
public string $git_repository;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'repository_url' => 'required|url',
|
'repository_url' => 'required|url',
|
||||||
'port' => 'required|numeric',
|
'port' => 'required|numeric',
|
||||||
@@ -38,10 +43,6 @@ class PublicGitRepository extends Component
|
|||||||
'is_static' => 'static',
|
'is_static' => 'static',
|
||||||
'publish_directory' => 'publish directory',
|
'publish_directory' => 'publish directory',
|
||||||
];
|
];
|
||||||
private object $repository_url_parsed;
|
|
||||||
private GithubApp|GitlabApp|null $git_source = null;
|
|
||||||
private string $git_host;
|
|
||||||
private string $git_repository;
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -64,26 +65,35 @@ class PublicGitRepository extends Component
|
|||||||
}
|
}
|
||||||
$this->emit('success', 'Application settings updated!');
|
$this->emit('success', 'Application settings updated!');
|
||||||
}
|
}
|
||||||
|
public function load_any_git()
|
||||||
|
{
|
||||||
|
$this->branch_found = true;
|
||||||
|
}
|
||||||
public function load_branch()
|
public function load_branch()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->branch_found = false;
|
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'repository_url' => 'required|url'
|
'repository_url' => 'required|url'
|
||||||
]);
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$this->branch_found = false;
|
||||||
$this->get_git_source();
|
$this->get_git_source();
|
||||||
$this->get_branch();
|
$this->get_branch();
|
||||||
$this->selected_branch = $this->git_branch;
|
$this->selected_branch = $this->git_branch;
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
ray($e->getMessage());
|
||||||
}
|
|
||||||
if (!$this->branch_found && $this->git_branch == 'main') {
|
if (!$this->branch_found && $this->git_branch == 'main') {
|
||||||
try {
|
try {
|
||||||
$this->git_branch = 'master';
|
$this->git_branch = 'master';
|
||||||
$this->get_branch();
|
$this->get_branch();
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,22 +107,24 @@ class PublicGitRepository extends Component
|
|||||||
|
|
||||||
if ($this->git_host == 'github.com') {
|
if ($this->git_host == 'github.com') {
|
||||||
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
||||||
} elseif ($this->git_host == 'gitlab.com') {
|
return;
|
||||||
$this->git_source = GitlabApp::where('name', 'Public GitLab')->first();
|
|
||||||
} elseif ($this->git_host == 'bitbucket.org') {
|
|
||||||
// Not supported yet
|
|
||||||
}
|
|
||||||
if (is_null($this->git_source)) {
|
|
||||||
throw new \Exception('Git source not found. What?!');
|
|
||||||
}
|
}
|
||||||
|
$this->git_repository = $this->repository_url;
|
||||||
|
$this->git_source = 'other';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function get_branch()
|
private function get_branch()
|
||||||
{
|
{
|
||||||
['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = git_api(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}");
|
if ($this->git_source === 'other') {
|
||||||
|
$this->branch_found = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($this->git_source->getMorphClass() === 'App\Models\GithubApp') {
|
||||||
|
['rate_limit_remaining' => $this->rate_limit_remaining, 'rate_limit_reset' => $this->rate_limit_reset] = githubApi(source: $this->git_source, endpoint: "/repos/{$this->git_repository}/branches/{$this->git_branch}");
|
||||||
$this->rate_limit_reset = Carbon::parse((int)$this->rate_limit_reset)->format('Y-M-d H:i:s');
|
$this->rate_limit_reset = Carbon::parse((int)$this->rate_limit_reset)->format('Y-M-d H:i:s');
|
||||||
$this->branch_found = true;
|
$this->branch_found = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
@@ -122,9 +134,6 @@ class PublicGitRepository extends Component
|
|||||||
$project_uuid = $this->parameters['project_uuid'];
|
$project_uuid = $this->parameters['project_uuid'];
|
||||||
$environment_name = $this->parameters['environment_name'];
|
$environment_name = $this->parameters['environment_name'];
|
||||||
|
|
||||||
$this->get_git_source();
|
|
||||||
$this->git_branch = $this->selected_branch ?? $this->git_branch;
|
|
||||||
|
|
||||||
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||||
if (!$destination) {
|
if (!$destination) {
|
||||||
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
||||||
@@ -137,7 +146,19 @@ class PublicGitRepository extends Component
|
|||||||
$project = Project::where('uuid', $project_uuid)->first();
|
$project = Project::where('uuid', $project_uuid)->first();
|
||||||
$environment = $project->load(['environments'])->environments->where('name', $environment_name)->first();
|
$environment = $project->load(['environments'])->environments->where('name', $environment_name)->first();
|
||||||
|
|
||||||
|
if ($this->git_source === 'other') {
|
||||||
|
$application_init = [
|
||||||
|
'name' => generate_random_name(),
|
||||||
|
'git_repository' => $this->git_repository,
|
||||||
|
'git_branch' => $this->git_branch,
|
||||||
|
'build_pack' => 'nixpacks',
|
||||||
|
'ports_exposes' => $this->port,
|
||||||
|
'publish_directory' => $this->publish_directory,
|
||||||
|
'environment_id' => $environment->id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination_class,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
$application_init = [
|
$application_init = [
|
||||||
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
||||||
'git_repository' => $this->git_repository,
|
'git_repository' => $this->git_repository,
|
||||||
@@ -151,18 +172,25 @@ class PublicGitRepository extends Component
|
|||||||
'source_id' => $this->git_source->id,
|
'source_id' => $this->git_source->id,
|
||||||
'source_type' => $this->git_source->getMorphClass()
|
'source_type' => $this->git_source->getMorphClass()
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$application = Application::create($application_init);
|
$application = Application::create($application_init);
|
||||||
|
|
||||||
$application->settings->is_static = $this->is_static;
|
$application->settings->is_static = $this->is_static;
|
||||||
$application->settings->save();
|
$application->settings->save();
|
||||||
|
|
||||||
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
|
$application->fqdn = $fqdn;
|
||||||
|
$application->save();
|
||||||
|
|
||||||
return redirect()->route('project.application.configuration', [
|
return redirect()->route('project.application.configuration', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'application_uuid' => $application->uuid,
|
'application_uuid' => $application->uuid,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\New;
|
namespace App\Http\Livewire\Project\New;
|
||||||
|
|
||||||
|
use App\Models\Project;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
|
||||||
use App\Models\SwarmDocker;
|
|
||||||
use Countable;
|
use Countable;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Route;
|
|
||||||
|
|
||||||
class Select extends Component
|
class Select extends Component
|
||||||
{
|
{
|
||||||
@@ -17,36 +16,73 @@ class Select extends Component
|
|||||||
public string $type;
|
public string $type;
|
||||||
public string $server_id;
|
public string $server_id;
|
||||||
public string $destination_uuid;
|
public string $destination_uuid;
|
||||||
public Countable|array|Server $servers;
|
public Countable|array|Server $servers = [];
|
||||||
public Collection|array $standaloneDockers = [];
|
public Collection|array $standaloneDockers = [];
|
||||||
public Collection|array $swarmDockers = [];
|
public Collection|array $swarmDockers = [];
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
|
public Collection|array $services = [];
|
||||||
|
public bool $loadingServices = true;
|
||||||
|
public bool $loading = false;
|
||||||
|
public $environments = [];
|
||||||
|
public ?string $selectedEnvironment = null;
|
||||||
public ?string $existingPostgresqlUrl = null;
|
public ?string $existingPostgresqlUrl = null;
|
||||||
|
|
||||||
protected $queryString = [
|
protected $queryString = [
|
||||||
'server',
|
'server',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$this->existingPostgresqlUrl = 'postgres://coolify:password@coolify-db:5432';
|
$this->existingPostgresqlUrl = 'postgres://coolify:password@coolify-db:5432';
|
||||||
}
|
}
|
||||||
|
$projectUuid = data_get($this->parameters, 'project_uuid');
|
||||||
|
$this->environments = Project::whereUuid($projectUuid)->first()->environments;
|
||||||
|
$this->selectedEnvironment = data_get($this->parameters, 'environment_name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function updatedSelectedEnvironment()
|
||||||
|
{
|
||||||
|
return redirect()->route('project.resources.new', [
|
||||||
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
|
'environment_name' => $this->selectedEnvironment,
|
||||||
|
]);
|
||||||
|
}
|
||||||
// public function addExistingPostgresql()
|
// public function addExistingPostgresql()
|
||||||
// {
|
// {
|
||||||
// try {
|
// try {
|
||||||
// instantCommand("psql {$this->existingPostgresqlUrl} -c 'SELECT 1'");
|
// instantCommand("psql {$this->existingPostgresqlUrl} -c 'SELECT 1'");
|
||||||
// $this->emit('success', 'Successfully connected to the database.');
|
// $this->emit('success', 'Successfully connected to the database.');
|
||||||
// } catch (\Exception $e) {
|
// } catch (\Throwable $e) {
|
||||||
// return general_error_handler($e, $this);
|
// return handleError($e, $this);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
public function loadThings()
|
||||||
|
{
|
||||||
|
$this->loadServices();
|
||||||
|
$this->loadServers();
|
||||||
|
}
|
||||||
|
public function loadServices(bool $forceReload = false)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($forceReload) {
|
||||||
|
Cache::forget('services');
|
||||||
|
}
|
||||||
|
$this->services = getServiceTemplates();
|
||||||
|
$this->emit('success', 'Successfully loaded services.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->loadingServices = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public function setType(string $type)
|
public function setType(string $type)
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
|
if ($this->loading) return;
|
||||||
|
$this->loading = true;
|
||||||
if ($type === "existing-postgresql") {
|
if ($type === "existing-postgresql") {
|
||||||
$this->current_step = $type;
|
$this->current_step = $type;
|
||||||
return;
|
return;
|
||||||
@@ -83,10 +119,11 @@ class Select extends Component
|
|||||||
'environment_name' => $this->parameters['environment_name'],
|
'environment_name' => $this->parameters['environment_name'],
|
||||||
'type' => $this->type,
|
'type' => $this->type,
|
||||||
'destination' => $this->destination_uuid,
|
'destination' => $this->destination_uuid,
|
||||||
|
'server_id' => $this->server_id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load_servers()
|
public function loadServers()
|
||||||
{
|
{
|
||||||
$this->servers = Server::isUsable()->get();
|
$this->servers = Server::isUsable()->get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||||
|
|
||||||
$port = get_port_from_dockerfile($this->dockerfile);
|
$port = get_port_from_dockerfile($this->dockerfile);
|
||||||
|
if (!$port) {
|
||||||
|
$port = 80;
|
||||||
|
}
|
||||||
$application = Application::create([
|
$application = Application::create([
|
||||||
'name' => 'dockerfile-' . new Cuid2(7),
|
'name' => 'dockerfile-' . new Cuid2(7),
|
||||||
'repository_project_id' => 0,
|
'repository_project_id' => 0,
|
||||||
@@ -56,11 +59,15 @@ CMD ["nginx", "-g", "daemon off;"]
|
|||||||
'environment_id' => $environment->id,
|
'environment_id' => $environment->id,
|
||||||
'destination_id' => $destination->id,
|
'destination_id' => $destination->id,
|
||||||
'destination_type' => $destination_class,
|
'destination_type' => $destination_class,
|
||||||
|
'health_check_enabled' => false,
|
||||||
'source_id' => 0,
|
'source_id' => 0,
|
||||||
'source_type' => GithubApp::class
|
'source_type' => GithubApp::class
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||||
$application->update([
|
$application->update([
|
||||||
'name' => 'dockerfile-' . $application->id
|
'name' => 'dockerfile-' . $application->uuid,
|
||||||
|
'fqdn' => $fqdn
|
||||||
]);
|
]);
|
||||||
|
|
||||||
redirect()->route('project.application.configuration', [
|
redirect()->route('project.application.configuration', [
|
||||||
|
|||||||
56
app/Http/Livewire/Project/Service/Application.php
Normal file
56
app/Http/Livewire/Project/Service/Application.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Application extends Component
|
||||||
|
{
|
||||||
|
public ServiceApplication $application;
|
||||||
|
public $parameters;
|
||||||
|
protected $rules = [
|
||||||
|
'application.human_name' => 'nullable',
|
||||||
|
'application.description' => 'nullable',
|
||||||
|
'application.fqdn' => 'nullable',
|
||||||
|
'application.image' => 'required',
|
||||||
|
'application.exclude_from_status' => 'required|boolean',
|
||||||
|
'application.required_fqdn' => 'required|boolean',
|
||||||
|
];
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.application');
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->application->delete();
|
||||||
|
$this->emit('success', 'Application deleted successfully.');
|
||||||
|
return redirect()->route('project.service', $this->parameters);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->application->save();
|
||||||
|
updateCompose($this->application);
|
||||||
|
$this->emit('success', 'Application saved successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->emit('generateDockerCompose');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Http/Livewire/Project/Service/ComposeModal.php
Normal file
19
app/Http/Livewire/Project/Service/ComposeModal.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class ComposeModal extends Component
|
||||||
|
{
|
||||||
|
public string $raw;
|
||||||
|
public string $actual;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/Http/Livewire/Project/Service/Database.php
Normal file
46
app/Http/Livewire/Project/Service/Database.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Database extends Component
|
||||||
|
{
|
||||||
|
public ServiceDatabase $database;
|
||||||
|
public $fileStorages;
|
||||||
|
protected $listeners = ["refreshFileStorages"];
|
||||||
|
protected $rules = [
|
||||||
|
'database.human_name' => 'nullable',
|
||||||
|
'database.description' => 'nullable',
|
||||||
|
'database.image' => 'required',
|
||||||
|
'database.exclude_from_status' => 'required|boolean',
|
||||||
|
];
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.database');
|
||||||
|
}
|
||||||
|
public function mount() {
|
||||||
|
$this->refreshFileStorages();
|
||||||
|
}
|
||||||
|
public function instantSave() {
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
|
public function refreshFileStorages()
|
||||||
|
{
|
||||||
|
$this->fileStorages = $this->database->fileStorages()->get();
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->database->save();
|
||||||
|
updateCompose($this->database);
|
||||||
|
$this->emit('success', 'Database saved successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
ray($e);
|
||||||
|
} finally {
|
||||||
|
$this->emit('generateDockerCompose');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
app/Http/Livewire/Project/Service/FileStorage.php
Normal file
60
app/Http/Livewire/Project/Service/FileStorage.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\LocalFileVolume;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class FileStorage extends Component
|
||||||
|
{
|
||||||
|
public LocalFileVolume $fileStorage;
|
||||||
|
public ServiceApplication|ServiceDatabase $service;
|
||||||
|
public string $fs_path;
|
||||||
|
public ?string $workdir = null;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'fileStorage.is_directory' => 'required',
|
||||||
|
'fileStorage.fs_path' => 'required',
|
||||||
|
'fileStorage.mount_path' => 'required',
|
||||||
|
'fileStorage.content' => 'nullable',
|
||||||
|
];
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->service = $this->fileStorage->service;
|
||||||
|
if (Str::of($this->fileStorage->fs_path)->startsWith('.')) {
|
||||||
|
$this->workdir = $this->service->service->workdir();
|
||||||
|
$this->fs_path = Str::of($this->fileStorage->fs_path)->after('.');
|
||||||
|
} else {
|
||||||
|
$this->workdir = null;
|
||||||
|
$this->fs_path = $this->fileStorage->fs_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$original = $this->fileStorage->getOriginal();
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
if ($this->fileStorage->is_directory) {
|
||||||
|
$this->fileStorage->content = null;
|
||||||
|
}
|
||||||
|
$this->fileStorage->save();
|
||||||
|
$this->fileStorage->saveStorageOnServer($this->service);
|
||||||
|
$this->emit('success', 'File updated successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->fileStorage->setRawAttributes($original);
|
||||||
|
$this->fileStorage->save();
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.file-storage');
|
||||||
|
}
|
||||||
|
}
|
||||||
45
app/Http/Livewire/Project/Service/Index.php
Normal file
45
app/Http/Livewire/Project/Service/Index.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Http/Livewire/Project/Service/Modal.php
Normal file
16
app/Http/Livewire/Project/Service/Modal.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?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');
|
||||||
|
}
|
||||||
|
}
|
||||||
39
app/Http/Livewire/Project/Service/Navbar.php
Normal file
39
app/Http/Livewire/Project/Service/Navbar.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Actions\Service\StartService;
|
||||||
|
use App\Actions\Service\StopService;
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
|
use App\Models\Service;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Navbar extends Component
|
||||||
|
{
|
||||||
|
public Service $service;
|
||||||
|
public array $parameters;
|
||||||
|
public array $query;
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.navbar');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkStatus()
|
||||||
|
{
|
||||||
|
$this->emit('checkStatus');
|
||||||
|
}
|
||||||
|
public function deploy()
|
||||||
|
{
|
||||||
|
$this->service->parse();
|
||||||
|
$activity = StartService::run($this->service);
|
||||||
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
|
}
|
||||||
|
public function stop()
|
||||||
|
{
|
||||||
|
StopService::run($this->service);
|
||||||
|
$this->service->refresh();
|
||||||
|
$this->emit('success', 'Service stopped successfully.');
|
||||||
|
$this->checkStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
49
app/Http/Livewire/Project/Service/Show.php
Normal file
49
app/Http/Livewire/Project/Service/Show.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\ServiceApplication;
|
||||||
|
use App\Models\ServiceDatabase;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Show extends Component
|
||||||
|
{
|
||||||
|
public Service $service;
|
||||||
|
public ?ServiceApplication $serviceApplication = null;
|
||||||
|
public ?ServiceDatabase $serviceDatabase = null;
|
||||||
|
public array $parameters;
|
||||||
|
public array $query;
|
||||||
|
public Collection $services;
|
||||||
|
protected $listeners = ['generateDockerCompose'];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->services = collect([]);
|
||||||
|
$this->parameters = get_route_parameters();
|
||||||
|
$this->query = request()->query();
|
||||||
|
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||||
|
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
|
||||||
|
if ($service) {
|
||||||
|
$this->serviceApplication = $service;
|
||||||
|
$this->serviceApplication->getFilesFromServer();
|
||||||
|
} else {
|
||||||
|
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
||||||
|
$this->serviceDatabase->getFilesFromServer();
|
||||||
|
}
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public function generateDockerCompose()
|
||||||
|
{
|
||||||
|
$this->service->parse();
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.show');
|
||||||
|
}
|
||||||
|
}
|
||||||
42
app/Http/Livewire/Project/Service/StackForm.php
Normal file
42
app/Http/Livewire/Project/Service/StackForm.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class StackForm extends Component
|
||||||
|
{
|
||||||
|
public $service;
|
||||||
|
protected $listeners = ["saveCompose"];
|
||||||
|
protected $rules = [
|
||||||
|
'service.docker_compose_raw' => 'required',
|
||||||
|
'service.docker_compose' => 'required',
|
||||||
|
'service.name' => 'required',
|
||||||
|
'service.description' => 'nullable',
|
||||||
|
];
|
||||||
|
public function saveCompose($raw)
|
||||||
|
{
|
||||||
|
$this->service->docker_compose_raw = $raw;
|
||||||
|
$this->submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->service->save();
|
||||||
|
$this->service->parse();
|
||||||
|
$this->service->refresh();
|
||||||
|
$this->service->saveComposeConfigs();
|
||||||
|
$this->emit('refreshStacks');
|
||||||
|
$this->emit('refreshEnvs');
|
||||||
|
$this->emit('success', 'Service saved successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.stack-form');
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/Http/Livewire/Project/Service/Storage.php
Normal file
35
app/Http/Livewire/Project/Service/Storage.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Service;
|
||||||
|
|
||||||
|
use App\Models\LocalPersistentVolume;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Storage extends Component
|
||||||
|
{
|
||||||
|
protected $listeners = ['addNewVolume'];
|
||||||
|
public $resource;
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.storage');
|
||||||
|
}
|
||||||
|
public function addNewVolume($data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
LocalPersistentVolume::create([
|
||||||
|
'name' => $data['name'],
|
||||||
|
'mount_path' => $data['mount_path'],
|
||||||
|
'host_path' => $data['host_path'],
|
||||||
|
'resource_id' => $this->resource->id,
|
||||||
|
'resource_type' => $this->resource->getMorphClass(),
|
||||||
|
]);
|
||||||
|
$this->resource->refresh();
|
||||||
|
$this->emit('success', 'Storage added successfully');
|
||||||
|
$this->emit('clearAddStorage');
|
||||||
|
$this->emit('refreshStorages');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Project\Shared;
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Jobs\StopResourceJob;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ class Danger extends Component
|
|||||||
{
|
{
|
||||||
public $resource;
|
public $resource;
|
||||||
public array $parameters;
|
public array $parameters;
|
||||||
public string|null $modalId = null;
|
public ?string $modalId = null;
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@@ -19,13 +20,14 @@ class Danger extends Component
|
|||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
{
|
{
|
||||||
$destination = $this->resource->destination->getMorphClass()::where('id', $this->resource->destination->id)->first();
|
try {
|
||||||
|
StopResourceJob::dispatchSync($this->resource);
|
||||||
instant_remote_process(["docker rm -f {$this->resource->uuid}"], $destination->server);
|
|
||||||
$this->resource->delete();
|
|
||||||
return redirect()->route('project.resources', [
|
return redirect()->route('project.resources', [
|
||||||
'project_uuid' => $this->parameters['project_uuid'],
|
'project_uuid' => $this->parameters['project_uuid'],
|
||||||
'environment_name' => $this->parameters['environment_name']
|
'environment_name' => $this->parameters['environment_name']
|
||||||
]);
|
]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ class Add extends Component
|
|||||||
public $parameters;
|
public $parameters;
|
||||||
public bool $is_preview = false;
|
public bool $is_preview = false;
|
||||||
public string $key;
|
public string $key;
|
||||||
public string $value;
|
public ?string $value = null;
|
||||||
public bool $is_build_time = false;
|
public bool $is_build_time = false;
|
||||||
|
|
||||||
protected $listeners = ['clearAddEnv' => 'clear'];
|
protected $listeners = ['clearAddEnv' => 'clear'];
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'key' => 'required|string',
|
'key' => 'required|string',
|
||||||
'value' => 'required|string',
|
'value' => 'nullable',
|
||||||
'is_build_time' => 'required|boolean',
|
'is_build_time' => 'required|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@@ -31,8 +31,8 @@ class Add extends Component
|
|||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
ray('submitting');
|
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
ray($this->key, $this->value, $this->is_build_time);
|
||||||
$this->emitUp('submit', [
|
$this->emitUp('submit', [
|
||||||
'key' => $this->key,
|
'key' => $this->key,
|
||||||
'value' => $this->value,
|
'value' => $this->value,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
|
|||||||
use App\Models\EnvironmentVariable;
|
use App\Models\EnvironmentVariable;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
use Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class All extends Component
|
class All extends Component
|
||||||
{
|
{
|
||||||
@@ -68,15 +68,31 @@ class All extends Component
|
|||||||
$environment->value = $variable;
|
$environment->value = $variable;
|
||||||
$environment->is_build_time = false;
|
$environment->is_build_time = false;
|
||||||
$environment->is_preview = $isPreview ? true : false;
|
$environment->is_preview = $isPreview ? true : false;
|
||||||
if ($this->resource->type() === 'application') {
|
switch ($this->resource->type()) {
|
||||||
|
case 'application':
|
||||||
$environment->application_id = $this->resource->id;
|
$environment->application_id = $this->resource->id;
|
||||||
}
|
break;
|
||||||
if ($this->resource->type() === 'standalone-postgresql') {
|
case 'standalone-postgresql':
|
||||||
$environment->standalone_postgresql_id = $this->resource->id;
|
$environment->standalone_postgresql_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'standalone-redis':
|
||||||
|
$environment->standalone_redis_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'standalone-mongodb':
|
||||||
|
$environment->standalone_mongodb_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'service':
|
||||||
|
$environment->service_id = $this->resource->id;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$environment->save();
|
$environment->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($isPreview) {
|
||||||
|
$this->emit('success', 'Preview environment variables updated successfully.');
|
||||||
|
} else {
|
||||||
|
$this->emit('success', 'Environment variables updated successfully.');
|
||||||
|
}
|
||||||
$this->refreshEnvs();
|
$this->refreshEnvs();
|
||||||
}
|
}
|
||||||
public function refreshEnvs()
|
public function refreshEnvs()
|
||||||
@@ -99,17 +115,22 @@ class All extends Component
|
|||||||
$environment->is_build_time = $data['is_build_time'];
|
$environment->is_build_time = $data['is_build_time'];
|
||||||
$environment->is_preview = $data['is_preview'];
|
$environment->is_preview = $data['is_preview'];
|
||||||
|
|
||||||
if ($this->resource->type() === 'application') {
|
switch ($this->resource->type()) {
|
||||||
|
case 'application':
|
||||||
$environment->application_id = $this->resource->id;
|
$environment->application_id = $this->resource->id;
|
||||||
}
|
break;
|
||||||
if ($this->resource->type() === 'standalone-postgresql') {
|
case 'standalone-postgresql':
|
||||||
$environment->standalone_postgresql_id = $this->resource->id;
|
$environment->standalone_postgresql_id = $this->resource->id;
|
||||||
|
break;
|
||||||
|
case 'service':
|
||||||
|
$environment->service_id = $this->resource->id;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$environment->save();
|
$environment->save();
|
||||||
$this->refreshEnvs();
|
$this->refreshEnvs();
|
||||||
$this->emit('success', 'Environment variable added successfully.');
|
$this->emit('success', 'Environment variable added successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Throwable $e) {
|
||||||
return general_error_handler(err: $e, that: $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,19 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
|
|||||||
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
public $parameters;
|
public $parameters;
|
||||||
public ModelsEnvironmentVariable $env;
|
public ModelsEnvironmentVariable $env;
|
||||||
public string|null $modalId = null;
|
public ?string $modalId = null;
|
||||||
|
public bool $isDisabled = false;
|
||||||
|
public string $type;
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'env.key' => 'required|string',
|
'env.key' => 'required|string',
|
||||||
'env.value' => 'required|string',
|
'env.value' => 'nullable',
|
||||||
'env.is_build_time' => 'required|boolean',
|
'env.is_build_time' => 'required|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@@ -24,6 +28,10 @@ class Show extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
$this->isDisabled = false;
|
||||||
|
if (Str::of($this->env->key)->startsWith('SERVICE_FQDN') || Str::of($this->env->key)->startsWith('SERVICE_URL')) {
|
||||||
|
$this->isDisabled = true;
|
||||||
|
}
|
||||||
$this->modalId = new Cuid2(7);
|
$this->modalId = new Cuid2(7);
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
}
|
}
|
||||||
@@ -37,6 +45,7 @@ class Show extends Component
|
|||||||
$this->validate();
|
$this->validate();
|
||||||
$this->env->save();
|
$this->env->save();
|
||||||
$this->emit('success', 'Environment variable updated successfully.');
|
$this->emit('success', 'Environment variable updated successfully.');
|
||||||
|
$this->emit('refreshEnvs');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
public function delete()
|
||||||
|
|||||||
45
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
45
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?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 .= $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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.get-logs');
|
||||||
|
}
|
||||||
|
}
|
||||||
47
app/Http/Livewire/Project/Shared/HealthChecks.php
Normal file
47
app/Http/Livewire/Project/Shared/HealthChecks.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class HealthChecks extends Component
|
||||||
|
{
|
||||||
|
|
||||||
|
public $resource;
|
||||||
|
protected $rules = [
|
||||||
|
'resource.health_check_enabled' => 'boolean',
|
||||||
|
'resource.health_check_path' => 'string',
|
||||||
|
'resource.health_check_port' => 'nullable|string',
|
||||||
|
'resource.health_check_host' => 'string',
|
||||||
|
'resource.health_check_method' => 'string',
|
||||||
|
'resource.health_check_return_code' => 'integer',
|
||||||
|
'resource.health_check_scheme' => 'string',
|
||||||
|
'resource.health_check_response_text' => 'nullable|string',
|
||||||
|
'resource.health_check_interval' => 'integer',
|
||||||
|
'resource.health_check_timeout' => 'integer',
|
||||||
|
'resource.health_check_retries' => 'integer',
|
||||||
|
'resource.health_check_start_period' => 'integer',
|
||||||
|
|
||||||
|
];
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$this->resource->save();
|
||||||
|
$this->emit('success', 'Health check updated.');
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->resource->save();
|
||||||
|
$this->emit('success', 'Health check updated.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.shared.health-checks');
|
||||||
|
}
|
||||||
|
}
|
||||||
66
app/Http/Livewire/Project/Shared/Logs.php
Normal file
66
app/Http/Livewire/Project/Shared/Logs.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Shared;
|
||||||
|
|
||||||
|
use App\Models\Application;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\StandaloneMongodb;
|
||||||
|
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 $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);
|
||||||
|
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)) {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user