mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 20:59:24 +00:00
Compare commits
502 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
581cc73cd4 | ||
|
|
358fbf6b3d | ||
|
|
ca0535c285 | ||
|
|
9007a645a6 | ||
|
|
68b1b9774d | ||
|
|
b9b4c23d5b | ||
|
|
149fee2452 | ||
|
|
87af9e46a6 | ||
|
|
d6f87d3fb6 | ||
|
|
493af61233 | ||
|
|
ab03908f1d | ||
|
|
9ef7cf3c12 | ||
|
|
eab7fd44d4 | ||
|
|
0d1d25a945 | ||
|
|
534372c29c | ||
|
|
1ccb239797 | ||
|
|
66287b43d0 | ||
|
|
143e4e0d23 | ||
|
|
73f3a09157 | ||
|
|
5ce449aa08 | ||
|
|
6203804713 | ||
|
|
0858faf628 | ||
|
|
a84f3e0577 | ||
|
|
8d571a5eab | ||
|
|
7a117c61c4 | ||
|
|
8b034f15fc | ||
|
|
b4a6499c83 | ||
|
|
c083acaeef | ||
|
|
9c6d8320d8 | ||
|
|
0e7a304610 | ||
|
|
83993cbbb2 | ||
|
|
6840ddd3e6 | ||
|
|
2c6ece62bb | ||
|
|
3f8514050e | ||
|
|
a4a653603e | ||
|
|
b6d8851c99 | ||
|
|
bcd7697f50 | ||
|
|
f1da735c40 | ||
|
|
01331c287b | ||
|
|
3320de787a | ||
|
|
2bddb09384 | ||
|
|
6f673d7a07 | ||
|
|
0a5a101ef4 | ||
|
|
88590fbf0f | ||
|
|
90291b2edf | ||
|
|
070573f0df | ||
|
|
e583beb753 | ||
|
|
d31683df61 | ||
|
|
0a83ed82fa | ||
|
|
4031e477ee | ||
|
|
0c1991d1de | ||
|
|
05b697b18c | ||
|
|
061aeba605 | ||
|
|
f446e784cc | ||
|
|
72fe24d98e | ||
|
|
0cd3a3d848 | ||
|
|
126b2dc65b | ||
|
|
a0031efce0 | ||
|
|
3bffe3f010 | ||
|
|
b9a37233a2 | ||
|
|
aab122d97e | ||
|
|
658d608f55 | ||
|
|
0838343841 | ||
|
|
b557ea1e1d | ||
|
|
4520070df3 | ||
|
|
be8ea78b1b | ||
|
|
1175d68ab5 | ||
|
|
f56d373ed2 | ||
|
|
c6253658ca | ||
|
|
4249aec936 | ||
|
|
25f80aba5f | ||
|
|
4550983761 | ||
|
|
b0238372a2 | ||
|
|
a021b71496 | ||
|
|
e3958d9626 | ||
|
|
55891d7001 | ||
|
|
b12ac8bb29 | ||
|
|
728a9f88eb | ||
|
|
57267c3ee0 | ||
|
|
d3d133ed1f | ||
|
|
f5240abbe5 | ||
|
|
abf5840f97 | ||
|
|
dc6d5af4aa | ||
|
|
0b88cd69f2 | ||
|
|
ce165719d6 | ||
|
|
4f543ce20f | ||
|
|
55f957df21 | ||
|
|
38f59b9410 | ||
|
|
ebe6655349 | ||
|
|
038ea08ca7 | ||
|
|
ba424efd39 | ||
|
|
75aef0e60b | ||
|
|
eda8b34297 | ||
|
|
d8151ddb2e | ||
|
|
7925228f97 | ||
|
|
a7dc62aaa0 | ||
|
|
632dbd155b | ||
|
|
fe092bb7a5 | ||
|
|
928345c8ea | ||
|
|
52d6fb51d5 | ||
|
|
06d7c69487 | ||
|
|
756c7f81ca | ||
|
|
d7af57a95e | ||
|
|
722ff15fbd | ||
|
|
f9c469497e | ||
|
|
7ecbedb48a | ||
|
|
76878f66b9 | ||
|
|
b9afef50c4 | ||
|
|
83ebd1e649 | ||
|
|
76431c3fd5 | ||
|
|
96a4d0bbb0 | ||
|
|
4cfc739730 | ||
|
|
fcd0d8d359 | ||
|
|
3fcac0ac35 | ||
|
|
fcc8a7f0ed | ||
|
|
6950ead041 | ||
|
|
f78c49fc82 | ||
|
|
5f2581020b | ||
|
|
2fb674ae85 | ||
|
|
a95bd906bc | ||
|
|
21795cf788 | ||
|
|
6e98fd9403 | ||
|
|
ead1edc2b9 | ||
|
|
db822cb876 | ||
|
|
65bfce43c0 | ||
|
|
50fc05ab52 | ||
|
|
c9cf5c486f | ||
|
|
379f4b9dff | ||
|
|
aa02b8d433 | ||
|
|
70ecb92e82 | ||
|
|
d5cc2a2eed | ||
|
|
2b91bd24c5 | ||
|
|
5e8ac1b48e | ||
|
|
dc86170ef5 | ||
|
|
0232cf5b4c | ||
|
|
6e73f7f2e4 | ||
|
|
61c43804e3 | ||
|
|
72421d692b | ||
|
|
f801bb98cd | ||
|
|
b2d111e49a | ||
|
|
c82e02218f | ||
|
|
29f64076de | ||
|
|
393c334b12 | ||
|
|
678b264688 | ||
|
|
2620bfbf08 | ||
|
|
18c32decad | ||
|
|
a6f9e5f0af | ||
|
|
f187040b7e | ||
|
|
5510321776 | ||
|
|
69691b2ca7 | ||
|
|
8bfc1a7c06 | ||
|
|
554222abc7 | ||
|
|
b1a1aeeb75 | ||
|
|
91acd4cb6a | ||
|
|
6c5a1c317a | ||
|
|
b09a9f871e | ||
|
|
b5506f006b | ||
|
|
a6c3594448 | ||
|
|
5dd3952230 | ||
|
|
22ec0f8826 | ||
|
|
da6e04bb1a | ||
|
|
aaeacad781 | ||
|
|
b539f40fa5 | ||
|
|
fae340afcb | ||
|
|
69ebff1a7a | ||
|
|
5d9cfc393e | ||
|
|
e2a256b31c | ||
|
|
4855af7e57 | ||
|
|
a664174c02 | ||
|
|
c19c13b4e2 | ||
|
|
266b99bc25 | ||
|
|
51ef24e1fb | ||
|
|
33d38ccf40 | ||
|
|
f470ebbbe0 | ||
|
|
11bd46b200 | ||
|
|
53f5674771 | ||
|
|
c53d88902c | ||
|
|
e342c4fd65 | ||
|
|
aab7bd5e28 | ||
|
|
3adefb9e49 | ||
|
|
1bfce6716c | ||
|
|
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 |
@@ -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_ID=development
|
||||
APP_ENV=local
|
||||
@@ -13,6 +5,7 @@ APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
APP_PORT=8000
|
||||
MUX_ENABLED=false
|
||||
|
||||
DUSK_DRIVER_URL=http://selenium:4444
|
||||
|
||||
|
||||
2
.github/workflows/development-build.yml
vendored
2
.github/workflows/development-build.yml
vendored
@@ -13,7 +13,7 @@ env:
|
||||
|
||||
jobs:
|
||||
amd64:
|
||||
runs-on: [self-hosted, x64]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- 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:
|
||||
amd64:
|
||||
runs-on: [self-hosted, x64]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Login to ghcr.io
|
||||
|
||||
28
.tinkerwell/snippets/SendEmail.php
Normal file
28
.tinkerwell/snippets/SendEmail.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* @label Send Email
|
||||
* @description Send email to all users
|
||||
*/
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
set_transanctional_email_settings();
|
||||
|
||||
$users = User::whereEmail('andras.bacsai@gmail.com');
|
||||
foreach ($users as $user) {
|
||||
Mail::send([], [], function ($message) use ($user) {
|
||||
$message
|
||||
->to($user->email)
|
||||
->subject("Testing")
|
||||
->text(
|
||||
<<<EOF
|
||||
Hello,
|
||||
|
||||
Welcome to Coolify Cloud.
|
||||
Here is your user id: $user->id
|
||||
|
||||
EOF
|
||||
);
|
||||
});
|
||||
}
|
||||
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.
|
||||
|
||||
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. 🪄️
|
||||
|
||||
@@ -36,10 +36,11 @@ You can find the installation script [here](./scripts/install.sh).
|
||||
|
||||
## Support
|
||||
|
||||
Contact us [here](https://docs.coollabs.io/contact).
|
||||
Contact us [here](https://coolify.io/docs/contact).
|
||||
|
||||
## Recognitions
|
||||
|
||||
<p>
|
||||
<a href="https://news.ycombinator.com/item?id=26624341">
|
||||
<img
|
||||
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"
|
||||
/>
|
||||
</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://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
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
102
app/Actions/Database/StartDatabaseProxy.php
Normal file
102
app/Actions/Database/StartDatabaseProxy.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
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|StandaloneMysql|StandaloneMariadb $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;
|
||||
} else if ($database->getMorphClass() === 'App\Models\StandaloneMysql') {
|
||||
$internalPort = 3306;
|
||||
} else if ($database->getMorphClass() === 'App\Models\StandaloneMariadb') {
|
||||
$internalPort = 3306;
|
||||
}
|
||||
$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);
|
||||
}
|
||||
}
|
||||
158
app/Actions/Database/StartMariadb.php
Normal file
158
app/Actions/Database/StartMariadb.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMariadb;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StartMariadb
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public StandaloneMariadb $database;
|
||||
public array $commands = [];
|
||||
public string $configuration_dir;
|
||||
|
||||
public function handle(StandaloneMariadb $database)
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo '####### Starting {$database->name}.'",
|
||||
"mkdir -p $this->configuration_dir",
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_mysql();
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
'container_name' => $container_name,
|
||||
'environment' => $environment_variables,
|
||||
'restart' => RESTART_MODE,
|
||||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"],
|
||||
'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->mariadb_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||
'read_only' => true,
|
||||
];
|
||||
}
|
||||
$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('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_ROOT_PASSWORD={$this->database->mariadb_root_password}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_DATABASE'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_DATABASE={$this->database->mariadb_database}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_USER'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_USER={$this->database->mariadb_user}");
|
||||
}
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
|
||||
}
|
||||
return $environment_variables->all();
|
||||
}
|
||||
private function add_custom_mysql()
|
||||
{
|
||||
if (is_null($this->database->mariadb_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'custom-config.cnf';
|
||||
$content = $this->database->mariadb_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
}
|
||||
}
|
||||
178
app/Actions/Database/StartMongodb.php
Normal file
178
app/Actions/Database/StartMongodb.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?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',
|
||||
'mongosh --eval "printjson(db.runCommand(\"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->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';
|
||||
}
|
||||
$this->add_default_database();
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d',
|
||||
'target' => '/docker-entrypoint-initdb.d',
|
||||
'read_only' => true,
|
||||
];
|
||||
|
||||
$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}";
|
||||
}
|
||||
private function add_default_database()
|
||||
{
|
||||
$content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js";
|
||||
}
|
||||
}
|
||||
158
app/Actions/Database/StartMysql.php
Normal file
158
app/Actions/Database/StartMysql.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMysql;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StartMysql
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public StandaloneMysql $database;
|
||||
public array $commands = [];
|
||||
public string $configuration_dir;
|
||||
|
||||
public function handle(StandaloneMysql $database)
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo '####### Starting {$database->name}.'",
|
||||
"mkdir -p $this->configuration_dir",
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_mysql();
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
'container_name' => $container_name,
|
||||
'environment' => $environment_variables,
|
||||
'restart' => RESTART_MODE,
|
||||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p{$this->database->mysql_root_password}"],
|
||||
'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->mysql_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||
'read_only' => true,
|
||||
];
|
||||
}
|
||||
$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('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_ROOT_PASSWORD={$this->database->mysql_root_password}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_DATABASE'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_DATABASE={$this->database->mysql_database}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_USER'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_USER={$this->database->mysql_user}");
|
||||
}
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
|
||||
}
|
||||
return $environment_variables->all();
|
||||
}
|
||||
private function add_custom_mysql()
|
||||
{
|
||||
if (is_null($this->database->mysql_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'custom-config.cnf';
|
||||
$content = $this->database->mysql_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,21 @@
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StartPostgresql
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public StandalonePostgresql $database;
|
||||
public array $commands = [];
|
||||
public array $init_scripts = [];
|
||||
public string $configuration_dir;
|
||||
|
||||
public function __invoke(Server $server, StandalonePostgresql $database)
|
||||
public function handle(StandalonePostgresql $database)
|
||||
{
|
||||
$this->database = $database;
|
||||
$container_name = $this->database->uuid;
|
||||
@@ -41,6 +43,9 @@ class StartPostgresql
|
||||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => [
|
||||
'CMD-SHELL',
|
||||
@@ -98,7 +103,7 @@ class StartPostgresql
|
||||
$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, $server);
|
||||
return remote_process($this->commands, $database->destination->server);
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes()
|
||||
@@ -139,6 +144,9 @@ class StartPostgresql
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_USER'))->isEmpty()) {
|
||||
$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()) {
|
||||
$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}";
|
||||
|
||||
}
|
||||
}
|
||||
29
app/Actions/Database/StopDatabase.php
Normal file
29
app/Actions/Database/StopDatabase.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StopDatabase
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $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));
|
||||
}
|
||||
}
|
||||
22
app/Actions/Database/StopDatabaseProxy.php
Normal file
22
app/Actions/Database/StopDatabaseProxy.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StopDatabaseProxy
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $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']),
|
||||
]);
|
||||
$team = $user->teams()->first();
|
||||
if (isCloud()) {
|
||||
$user->sendVerificationEmail();
|
||||
} else {
|
||||
$user->markEmailAsVerified();
|
||||
}
|
||||
}
|
||||
// Set session variable
|
||||
session(['currentTeam' => $user->currentTeam = $team]);
|
||||
|
||||
@@ -4,26 +4,26 @@ namespace App\Actions\License;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
|
||||
class CheckResaleLicense
|
||||
{
|
||||
public function __invoke()
|
||||
use AsAction;
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings->update([
|
||||
'is_resale_license_active' => false,
|
||||
]);
|
||||
if (isDev()) {
|
||||
$settings->update([
|
||||
'is_resale_license_active' => true,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
if (!$settings->resale_license) {
|
||||
return;
|
||||
}
|
||||
// if (!$settings->resale_license) {
|
||||
// return;
|
||||
// }
|
||||
$base_url = config('coolify.license_url');
|
||||
if (isDev()) {
|
||||
$base_url = 'http://host.docker.internal:8787';
|
||||
}
|
||||
$instance_id = config('app.id');
|
||||
|
||||
ray("Checking license key against $base_url/lemon/validate");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -10,56 +10,47 @@ use Spatie\Activitylog\Models\Activity;
|
||||
class StartProxy
|
||||
{
|
||||
use AsAction;
|
||||
public function handle(Server $server, bool $async = true): Activity|string
|
||||
public function handle(Server $server, bool $async = true): string|Activity
|
||||
{
|
||||
$commands = collect([]);
|
||||
$proxyType = $server->proxyType();
|
||||
if ($proxyType === 'none') {
|
||||
return 'OK';
|
||||
}
|
||||
$proxy_path = get_proxy_path();
|
||||
$configuration = CheckConfiguration::run($server);
|
||||
if (!$configuration) {
|
||||
throw new \Exception("Configuration is not synced");
|
||||
}
|
||||
SaveConfiguration::run($server, $configuration);
|
||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||
$server->save();
|
||||
|
||||
$commands = $commands->merge([
|
||||
"apt-get update > /dev/null 2>&1 || true",
|
||||
"command -v lsof >/dev/null || echo '####### Installing lsof.'",
|
||||
"command -v lsof >/dev/null || apt install -y lsof",
|
||||
"command -v lsof >/dev/null || command -v fuser >/dev/null || apt install -y psmisc",
|
||||
"mkdir -p $proxy_path && cd $proxy_path",
|
||||
"echo '####### Creating Docker Compose file.'",
|
||||
"echo '####### Pulling docker image.'",
|
||||
'docker compose pull',
|
||||
"echo '####### Stopping existing coolify-proxy.'",
|
||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||
"command -v fuser >/dev/null || command -v lsof >/dev/null || echo '####### Could not kill existing processes listening on port 80 & 443. Please stop the process holding these ports...'",
|
||||
"command -v lsof >/dev/null && lsof -nt -i:80 | xargs -r kill -9 || true",
|
||||
"command -v lsof >/dev/null && lsof -nt -i:443 | xargs -r kill -9 || true",
|
||||
"command -v fuser >/dev/null && fuser -k 80/tcp || true",
|
||||
"command -v fuser >/dev/null && fuser -k 443/tcp || true",
|
||||
"systemctl disable nginx > /dev/null 2>&1 || true",
|
||||
"systemctl disable apache2 > /dev/null 2>&1 || true",
|
||||
"systemctl disable apache > /dev/null 2>&1 || true",
|
||||
"echo '####### Starting coolify-proxy.'",
|
||||
'docker compose up -d --remove-orphans',
|
||||
"echo '####### Proxy installed successfully.'"
|
||||
]);
|
||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||
if ($async) {
|
||||
$activity = remote_process($commands, $server);
|
||||
return $activity;
|
||||
} else {
|
||||
instant_remote_process($commands, $server);
|
||||
$server->proxy->set('status', 'running');
|
||||
$server->proxy->set('type', $proxyType);
|
||||
try {
|
||||
$proxyType = $server->proxyType();
|
||||
$commands = collect([]);
|
||||
$proxy_path = get_proxy_path();
|
||||
$configuration = CheckConfiguration::run($server);
|
||||
if (!$configuration) {
|
||||
throw new \Exception("Configuration is not synced");
|
||||
}
|
||||
SaveConfiguration::run($server, $configuration);
|
||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||
$server->save();
|
||||
return 'OK';
|
||||
$commands = $commands->merge([
|
||||
"mkdir -p $proxy_path && cd $proxy_path",
|
||||
"echo 'Creating required Docker Compose file.'",
|
||||
"echo 'Pulling docker image.'",
|
||||
'docker compose pull',
|
||||
"echo 'Stopping existing coolify-proxy.'",
|
||||
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||
"echo 'Starting coolify-proxy.'",
|
||||
'docker compose up -d --remove-orphans',
|
||||
"echo 'Proxy started successfully.'"
|
||||
]);
|
||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||
if ($async) {
|
||||
$activity = remote_process($commands, $server);
|
||||
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,12 +2,14 @@
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
|
||||
class InstallDocker
|
||||
{
|
||||
public function __invoke(Server $server)
|
||||
use AsAction;
|
||||
public function handle(Server $server)
|
||||
{
|
||||
$dockerVersion = '24.0';
|
||||
$config = base64_encode('{
|
||||
|
||||
@@ -2,16 +2,18 @@
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
|
||||
class UpdateCoolify
|
||||
{
|
||||
use AsAction;
|
||||
public ?Server $server = null;
|
||||
public ?string $latestVersion = null;
|
||||
public ?string $currentVersion = null;
|
||||
|
||||
public function __invoke(bool $force)
|
||||
public function handle(bool $force)
|
||||
{
|
||||
try {
|
||||
$settings = InstanceSettings::get();
|
||||
|
||||
@@ -4,12 +4,14 @@ 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()}.'";
|
||||
@@ -21,6 +23,11 @@ class StartService
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -22,5 +22,7 @@ class StopService
|
||||
}
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,50 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class Init extends Command
|
||||
{
|
||||
protected $signature = 'app:init';
|
||||
protected $signature = 'app:init {--cleanup}';
|
||||
protected $description = 'Cleanup instance related stuffs';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
ray()->clearAll();
|
||||
$cleanup = $this->option('cleanup');
|
||||
if ($cleanup) {
|
||||
$this->cleanup_stucked_resources();
|
||||
$this->cleanup_ssh();
|
||||
}
|
||||
$this->cleanup_in_progress_application_deployments();
|
||||
}
|
||||
|
||||
private function cleanup_ssh()
|
||||
{
|
||||
try {
|
||||
$files = Storage::allFiles('ssh/keys');
|
||||
foreach ($files as $file) {
|
||||
Storage::delete($file);
|
||||
}
|
||||
$files = Storage::allFiles('ssh/mux');
|
||||
foreach ($files as $file) {
|
||||
Storage::delete($file);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning ssh: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
private function cleanup_in_progress_application_deployments()
|
||||
{
|
||||
// Cleanup any failed deployments
|
||||
@@ -30,4 +61,167 @@ class Init extends Command
|
||||
echo "Error: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
private function cleanup_stucked_resources()
|
||||
{
|
||||
// Cleanup any resources that are not attached to any environment or destination or server
|
||||
try {
|
||||
$applications = Application::all();
|
||||
foreach ($applications as $application) {
|
||||
if (!data_get($application, 'environment')) {
|
||||
ray('Application without environment', $application->name);
|
||||
$application->delete();
|
||||
}
|
||||
if (!data_get($application, 'destination.server')) {
|
||||
ray('Application without server', $application->name);
|
||||
$application->delete();
|
||||
}
|
||||
if (!$application->destination()) {
|
||||
ray('Application without destination', $application->name);
|
||||
$application->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in application: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$postgresqls = StandalonePostgresql::all();
|
||||
foreach ($postgresqls as $postgresql) {
|
||||
if (!data_get($postgresql, 'environment')) {
|
||||
ray('Postgresql without environment', $postgresql->name);
|
||||
$postgresql->delete();
|
||||
}
|
||||
if (!data_get($postgresql, 'destination.server')) {
|
||||
ray('Postgresql without server', $postgresql->name);
|
||||
$postgresql->delete();
|
||||
}
|
||||
if (!$postgresql->destination()) {
|
||||
ray('Postgresql without destination', $postgresql->name);
|
||||
$postgresql->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in postgresql: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$redis = StandaloneRedis::all();
|
||||
foreach ($redis as $redis) {
|
||||
if (!data_get($redis, 'environment')) {
|
||||
ray('Redis without environment', $redis->name);
|
||||
$redis->delete();
|
||||
}
|
||||
if (!data_get($redis, 'destination.server')) {
|
||||
ray('Redis without server', $redis->name);
|
||||
$redis->delete();
|
||||
}
|
||||
if (!$redis->destination()) {
|
||||
ray('Redis without destination', $redis->name);
|
||||
$redis->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in redis: {$e->getMessage()}\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$mongodbs = StandaloneMongodb::all();
|
||||
foreach ($mongodbs as $mongodb) {
|
||||
if (!data_get($mongodb, 'environment')) {
|
||||
ray('Mongodb without environment', $mongodb->name);
|
||||
$mongodb->delete();
|
||||
}
|
||||
if (!data_get($mongodb, 'destination.server')) {
|
||||
ray('Mongodb without server', $mongodb->name);
|
||||
$mongodb->delete();
|
||||
}
|
||||
if (!$mongodb->destination()) {
|
||||
ray('Mongodb without destination', $mongodb->name);
|
||||
$mongodb->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in mongodb: {$e->getMessage()}\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$mysqls = StandaloneMysql::all();
|
||||
foreach ($mysqls as $mysql) {
|
||||
if (!data_get($mysql, 'environment')) {
|
||||
ray('Mysql without environment', $mysql->name);
|
||||
$mysql->delete();
|
||||
}
|
||||
if (!data_get($mysql, 'destination.server')) {
|
||||
ray('Mysql without server', $mysql->name);
|
||||
$mysql->delete();
|
||||
}
|
||||
if (!$mysql->destination()) {
|
||||
ray('Mysql without destination', $mysql->name);
|
||||
$mysql->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in mysql: {$e->getMessage()}\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$mariadbs = StandaloneMariadb::all();
|
||||
foreach ($mariadbs as $mariadb) {
|
||||
if (!data_get($mariadb, 'environment')) {
|
||||
ray('Mariadb without environment', $mariadb->name);
|
||||
$mariadb->delete();
|
||||
}
|
||||
if (!data_get($mariadb, 'destination.server')) {
|
||||
ray('Mariadb without server', $mariadb->name);
|
||||
$mariadb->delete();
|
||||
}
|
||||
if (!$mariadb->destination()) {
|
||||
ray('Mariadb without destination', $mariadb->name);
|
||||
$mariadb->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in mariadb: {$e->getMessage()}\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$services = Service::all();
|
||||
foreach ($services as $service) {
|
||||
if (!data_get($service, 'environment')) {
|
||||
ray('Service without environment', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
if (!data_get($service, 'server')) {
|
||||
ray('Service without server', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
if (!$service->destination()) {
|
||||
ray('Service without destination', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in service: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$serviceApplications = ServiceApplication::all();
|
||||
foreach ($serviceApplications as $service) {
|
||||
if (!data_get($service, 'service')) {
|
||||
ray('ServiceApplication without service', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in serviceApplications: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$serviceDatabases = ServiceDatabase::all();
|
||||
foreach ($serviceDatabases as $service) {
|
||||
if (!data_get($service, 'service')) {
|
||||
ray('ServiceDatabase without service', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in ServiceDatabases: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
146
app/Console/Commands/ResourcesDelete.php
Normal file
146
app/Console/Commands/ResourcesDelete.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?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();
|
||||
if ($toDelete) {
|
||||
$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) {
|
||||
$toDelete = $applications->where('id', $application)->first();
|
||||
if ($toDelete) {
|
||||
$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();
|
||||
if ($toDelete) {
|
||||
$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();
|
||||
if ($toDelete) {
|
||||
$this->info($toDelete);
|
||||
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||
if (!$confirmed) {
|
||||
return;
|
||||
}
|
||||
$toDelete->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
app/Console/Commands/ServicesGenerate.php
Normal file
107
app/Console/Commands/ServicesGenerate.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ServicesGenerate 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;
|
||||
}
|
||||
|
||||
$tags = collect(preg_grep('/^# tags:/', explode("\n", $content)))->values();
|
||||
if ($tags->count() > 0) {
|
||||
$tags = str($tags[0])->after('# tags:')->trim()->explode(',')->map(function ($tag) {
|
||||
return str($tag)->trim()->lower()->value();
|
||||
})->values();
|
||||
} else {
|
||||
$tags = null;
|
||||
}
|
||||
$json = Yaml::parse($content);
|
||||
$yaml = base64_encode(Yaml::dump($json, 10, 2));
|
||||
$payload = [
|
||||
'name' => $serviceName,
|
||||
'documentation' => $documentation,
|
||||
'slogan' => $slogan,
|
||||
'compose' => $yaml,
|
||||
'tags' => $tags,
|
||||
];
|
||||
if ($env_file) {
|
||||
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
|
||||
$env_file_base64 = base64_encode($env_file_content);
|
||||
$payload['envs'] = $env_file_base64;
|
||||
}
|
||||
return $payload;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class SyncBunny extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'sync:bunny {--only-template} {--only-version}';
|
||||
protected $signature = 'sync:bunny {--templates} {--release}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -31,8 +31,8 @@ class SyncBunny extends Command
|
||||
public function handle()
|
||||
{
|
||||
$that = $this;
|
||||
$only_template = $this->option('only-template');
|
||||
$only_version = $this->option('only-version');
|
||||
$only_template = $this->option('templates');
|
||||
$only_version = $this->option('release');
|
||||
$bunny_cdn = "https://cdn.coollabs.io";
|
||||
$bunny_cdn_path = "coolify";
|
||||
$bunny_cdn_storage_name = "coolcdn";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use App\Jobs\DatabaseBackupJob;
|
||||
use App\Jobs\DockerCleanupJob;
|
||||
use App\Jobs\InstanceAutoUpdateJob;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Jobs\PullHelperImageJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Server;
|
||||
@@ -19,27 +20,51 @@ class Kernel extends ConsoleKernel
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
if (isDev()) {
|
||||
// $schedule->job(new ContainerStatusJob(Server::find(0)))->everyTenMinutes()->onOneServer();
|
||||
// $schedule->command('horizon:snapshot')->everyMinute();
|
||||
// Instance Jobs
|
||||
$schedule->command('horizon:snapshot')->everyMinute();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
||||
// $schedule->job(new CheckResaleLicenseJob)->hourly();
|
||||
// $schedule->job(new DockerCleanupJob)->everyOddHour();
|
||||
// $this->instance_auto_update($schedule);
|
||||
// $this->check_scheduled_backups($schedule);
|
||||
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||
// Server Jobs
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
$this->cleanup_servers($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->pull_helper_image($schedule);
|
||||
} else {
|
||||
// Instance Jobs
|
||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||
$schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
|
||||
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||
|
||||
// Server Jobs
|
||||
$this->instance_auto_update($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
$this->cleanup_servers($schedule);
|
||||
$this->pull_helper_image($schedule);
|
||||
}
|
||||
}
|
||||
private function pull_helper_image($schedule)
|
||||
{
|
||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new PullHelperImageJob($server))->everyTenMinutes()->onOneServer();
|
||||
}
|
||||
}
|
||||
private function cleanup_servers($schedule)
|
||||
{
|
||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
|
||||
}
|
||||
}
|
||||
private function check_resources($schedule)
|
||||
{
|
||||
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true);
|
||||
if (isCloud()) {
|
||||
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false);
|
||||
} else {
|
||||
$servers = Server::all();
|
||||
}
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
||||
}
|
||||
@@ -56,7 +81,6 @@ class Kernel extends ConsoleKernel
|
||||
}
|
||||
private function check_scheduled_backups($schedule)
|
||||
{
|
||||
ray('check_scheduled_backups');
|
||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||
if ($scheduled_backups->isEmpty()) {
|
||||
ray('no scheduled backups');
|
||||
|
||||
@@ -41,7 +41,7 @@ class ApplicationController extends Controller
|
||||
if (!$application) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 8);
|
||||
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
|
||||
return view('project.application.deployments', ['application' => $application, 'deployments' => $deployments, 'deployments_count' => $count]);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,10 @@ class Controller extends BaseController
|
||||
} else {
|
||||
$team = $user->teams()->first();
|
||||
}
|
||||
if (is_null(data_get($user, 'email_verified_at'))){
|
||||
$user->email_verified_at = now();
|
||||
$user->save();
|
||||
}
|
||||
Auth::login($user);
|
||||
session(['currentTeam' => $team]);
|
||||
return redirect()->route('dashboard');
|
||||
@@ -46,15 +50,6 @@ class Controller extends BaseController
|
||||
}
|
||||
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()
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ class DatabaseController extends Controller
|
||||
if (!$environment) {
|
||||
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) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
@@ -37,7 +37,7 @@ class DatabaseController extends Controller
|
||||
if (!$environment) {
|
||||
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) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
@@ -64,10 +64,18 @@ class DatabaseController extends Controller
|
||||
if (!$environment) {
|
||||
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) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
// No backups for redis
|
||||
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
|
||||
return redirect()->route('project.database.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'database_uuid' => $database->uuid,
|
||||
]);
|
||||
}
|
||||
return view('project.database.backups.all', [
|
||||
'database' => $database,
|
||||
's3s' => currentTeam()->s3s,
|
||||
|
||||
@@ -32,8 +32,14 @@ class MagicController extends Controller
|
||||
|
||||
public function environments()
|
||||
{
|
||||
$project = Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first();
|
||||
if (!$project) {
|
||||
return response()->json([
|
||||
'environments' => []
|
||||
]);
|
||||
}
|
||||
return response()->json([
|
||||
'environments' => Project::ownedByCurrentTeam()->whereUuid(request()->query('project_uuid'))->first()->environments
|
||||
'environments' => $project->environments
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use App\Models\EnvironmentVariable;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Models\StandaloneDocker;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ProjectController extends Controller
|
||||
@@ -59,27 +59,41 @@ class ProjectController extends Controller
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
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);
|
||||
} else if ($type->value() === 'mysql') {
|
||||
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
||||
}else if ($type->value() === 'mariadb') {
|
||||
$database = create_standalone_mariadb($environment->id, $destination_uuid);
|
||||
}
|
||||
return redirect()->route('project.database.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
'database_uuid' => $standalone_postgresql->uuid,
|
||||
'database_uuid' => $database->uuid,
|
||||
]);
|
||||
}
|
||||
if ($type->startsWith('one-click-service-') && !is_null( (int)$server_id)) {
|
||||
if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) {
|
||||
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||
ray($oneClickServiceName);
|
||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||
if ($oneClickDotEnvs) {
|
||||
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/');
|
||||
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
|
||||
return !empty($value);
|
||||
});
|
||||
}
|
||||
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();
|
||||
@@ -90,6 +104,7 @@ class ProjectController extends Controller
|
||||
$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);
|
||||
|
||||
@@ -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,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\CheckForcePasswordReset::class,
|
||||
\App\Http\Middleware\IsSubscriptionValid::class,
|
||||
\App\Http\Middleware\IsBoardingFlow::class,
|
||||
\App\Http\Middleware\DecideWhatToDoWithUser::class,
|
||||
|
||||
],
|
||||
|
||||
|
||||
@@ -76,7 +76,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
Team::find(currentTeam()->id)->update([
|
||||
'show_boarding' => false
|
||||
]);
|
||||
ray(currentTeam());
|
||||
refreshSession();
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
@@ -214,14 +213,14 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
]);
|
||||
$this->getProxyType();
|
||||
} catch (\Throwable $e) {
|
||||
$this->dockerInstallationStarted = false;
|
||||
// $this->dockerInstallationStarted = false;
|
||||
return handleError(error: $e, customErrorMessage: $customErrorMessage, livewire: $this);
|
||||
}
|
||||
}
|
||||
public function installDocker()
|
||||
{
|
||||
$this->dockerInstallationStarted = true;
|
||||
$activity = resolve(InstallDocker::class)($this->createdServer);
|
||||
$activity = InstallDocker::run($this->createdServer);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
}
|
||||
public function dockerInstalledOrSkipped()
|
||||
|
||||
@@ -32,7 +32,7 @@ class CheckLicense extends Component
|
||||
$this->settings->save();
|
||||
if ($this->settings->resale_license) {
|
||||
try {
|
||||
resolve(CheckResaleLicense::class)();
|
||||
CheckResaleLicense::run();
|
||||
$this->emit('reloadWindow');
|
||||
} catch (\Throwable $e) {
|
||||
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
|
||||
|
||||
@@ -9,21 +9,13 @@ use Livewire\Component;
|
||||
|
||||
class Dashboard extends Component
|
||||
{
|
||||
public int $projects = 0;
|
||||
public int $servers = 0;
|
||||
public int $s3s = 0;
|
||||
public int $resources = 0;
|
||||
public $projects = [];
|
||||
public $servers = [];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->servers = Server::ownedByCurrentTeam()->get()->count();
|
||||
$this->s3s = S3Storage::ownedByCurrentTeam()->get()->count();
|
||||
$projects = Project::ownedByCurrentTeam()->get();
|
||||
foreach ($projects as $project) {
|
||||
$this->resources += $project->applications->count();
|
||||
$this->resources += $project->postgresqls->count();
|
||||
}
|
||||
$this->projects = $projects->count();
|
||||
$this->servers = Server::ownedByCurrentTeam()->get();
|
||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||
}
|
||||
// public function getIptables()
|
||||
// {
|
||||
|
||||
@@ -16,7 +16,7 @@ class Form extends Component
|
||||
protected $validationAttributes = [
|
||||
'destination.name' => 'name',
|
||||
'destination.network' => 'network',
|
||||
'destination.server.ip' => 'IP Address',
|
||||
'destination.server.ip' => 'IP Address/Domain',
|
||||
];
|
||||
|
||||
public function submit()
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
@@ -60,12 +60,16 @@ class DeploymentNavbar extends Component
|
||||
$previous_logs[] = $new_log_entry;
|
||||
$this->application_deployment_queue->update([
|
||||
'logs' => json_encode($previous_logs, flags: JSON_THROW_ON_ERROR),
|
||||
'current_process_id' => null,
|
||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
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,24 +3,31 @@
|
||||
namespace App\Http\Livewire\Project\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class Deployments extends Component
|
||||
{
|
||||
public Application $application;
|
||||
public $deployments = [];
|
||||
public Array|Collection $deployments = [];
|
||||
public int $deployments_count = 0;
|
||||
public string $current_url;
|
||||
public int $skip = 0;
|
||||
public int $default_take = 8;
|
||||
public int $default_take = 40;
|
||||
public bool $show_next = false;
|
||||
|
||||
public ?string $pull_request_id = null;
|
||||
protected $queryString = ['pull_request_id'];
|
||||
public function mount()
|
||||
{
|
||||
$this->current_url = url()->current();
|
||||
$this->show_pull_request_only();
|
||||
$this->show_more();
|
||||
}
|
||||
|
||||
private function show_pull_request_only() {
|
||||
if ($this->pull_request_id) {
|
||||
$this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id);
|
||||
}
|
||||
}
|
||||
private function show_more()
|
||||
{
|
||||
if (count($this->deployments) !== 0) {
|
||||
@@ -47,6 +54,7 @@ class Deployments extends Component
|
||||
['deployments' => $deployments, 'count' => $count] = $this->application->deployments($this->skip, $take);
|
||||
$this->deployments = $deployments;
|
||||
$this->deployments_count = $count;
|
||||
$this->show_pull_request_only();
|
||||
$this->show_more();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace App\Http\Livewire\Project\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
@@ -17,14 +14,16 @@ class General extends Component
|
||||
public Application $application;
|
||||
public Collection $services;
|
||||
public string $name;
|
||||
public string|null $fqdn;
|
||||
public ?string $fqdn = null;
|
||||
public string $git_repository;
|
||||
public string $git_branch;
|
||||
public string|null $git_commit_sha;
|
||||
public ?string $git_commit_sha = null;
|
||||
public string $build_pack;
|
||||
public string|null $wildcard_domain = null;
|
||||
public string|null $server_wildcard_domain = null;
|
||||
public string|null $global_wildcard_domain = null;
|
||||
public ?string $ports_exposes = null;
|
||||
|
||||
public $customLabels;
|
||||
public bool $labelsChanged = false;
|
||||
public bool $isConfigurationChanged = false;
|
||||
|
||||
public bool $is_static;
|
||||
public bool $is_git_submodules_enabled;
|
||||
@@ -52,6 +51,10 @@ class General extends Component
|
||||
'application.ports_exposes' => 'required',
|
||||
'application.ports_mappings' => 'nullable',
|
||||
'application.dockerfile' => 'nullable',
|
||||
'application.docker_registry_image_name' => 'nullable',
|
||||
'application.docker_registry_image_tag' => 'nullable',
|
||||
'application.dockerfile_location' => 'nullable',
|
||||
'application.custom_labels' => 'nullable',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'application.name' => 'name',
|
||||
@@ -70,12 +73,55 @@ class General extends Component
|
||||
'application.ports_exposes' => 'Ports exposes',
|
||||
'application.ports_mappings' => 'Ports mappings',
|
||||
'application.dockerfile' => 'Dockerfile',
|
||||
|
||||
'application.docker_registry_image_name' => 'Docker registry image name',
|
||||
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
||||
'application.dockerfile_location' => 'Dockerfile location',
|
||||
'application.custom_labels' => 'Custom labels',
|
||||
];
|
||||
|
||||
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()
|
||||
{
|
||||
// @TODO: find another way - if possible
|
||||
$force_https = $this->application->settings->is_force_https_enabled;
|
||||
$this->application->settings->is_static = $this->is_static;
|
||||
if ($this->is_static) {
|
||||
$this->application->ports_exposes = 80;
|
||||
@@ -91,58 +137,49 @@ class General extends Component
|
||||
$this->application->settings->save();
|
||||
$this->application->save();
|
||||
$this->application->refresh();
|
||||
$this->checkWildCardDomain();
|
||||
$this->emit('success', 'Application settings updated!');
|
||||
$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();
|
||||
$this->server_wildcard_domain = data_get($this->application, 'destination.server.settings.wildcard_domain');
|
||||
$this->global_wildcard_domain = data_get($coolify_instance_settings, 'wildcard_domain');
|
||||
$this->wildcard_domain = $this->server_wildcard_domain ?? $this->global_wildcard_domain ?? null;
|
||||
$server = data_get($this->application, 'destination.server');
|
||||
if ($server) {
|
||||
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||
$this->application->fqdn = $fqdn;
|
||||
$this->application->save();
|
||||
$this->updatedApplicationFqdn();
|
||||
}
|
||||
}
|
||||
|
||||
public function mount()
|
||||
public function resetDefaultLabels($showToaster = true)
|
||||
{
|
||||
$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();
|
||||
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||
$this->ports_exposes = $this->application->ports_exposes;
|
||||
$this->submit($showToaster);
|
||||
}
|
||||
|
||||
public function generateGlobalRandomDomain()
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
// 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->emit('success', 'Application settings updated!');
|
||||
$this->resetDefaultLabels(false);
|
||||
$this->emit('success', 'Labels reseted to default!');
|
||||
}
|
||||
|
||||
public function generateServerRandomDomain()
|
||||
{
|
||||
// Set wildcard domain based on Server wildcard domain
|
||||
$url = Url::fromString($this->server_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->emit('success', 'Application settings updated!');
|
||||
}
|
||||
|
||||
public function submit()
|
||||
public function submit($showToaster = true)
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
||||
$this->resetDefaultLabels(false);
|
||||
}
|
||||
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||
$this->validate([
|
||||
'application.docker_registry_image_name' => 'required',
|
||||
'application.docker_registry_image_tag' => 'required',
|
||||
]);
|
||||
}
|
||||
if (data_get($this->application, 'fqdn')) {
|
||||
$domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
|
||||
return Str::of($domain)->trim()->lower();
|
||||
@@ -151,7 +188,7 @@ class General extends Component
|
||||
}
|
||||
if (data_get($this->application, 'dockerfile')) {
|
||||
$port = get_port_from_dockerfile($this->application->dockerfile);
|
||||
if ($port) {
|
||||
if ($port && !$this->application->ports_exposes) {
|
||||
$this->application->ports_exposes = $port;
|
||||
}
|
||||
}
|
||||
@@ -161,10 +198,17 @@ class General extends Component
|
||||
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
||||
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
||||
}
|
||||
if (gettype($this->customLabels) === 'string') {
|
||||
$this->customLabels = str($this->customLabels)->replace(',', "\n");
|
||||
}
|
||||
$this->application->custom_labels = $this->customLabels->explode("\n")->implode(',');
|
||||
$this->application->save();
|
||||
$this->emit('success', 'Application settings updated!');
|
||||
$showToaster && $this->emit('success', 'Application settings updated!');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->checkLabelUpdates();
|
||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Livewire\Project\Application;
|
||||
|
||||
use App\Actions\Application\StopApplication;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Models\Application;
|
||||
use Livewire\Component;
|
||||
@@ -21,11 +22,13 @@ class Heading extends Component
|
||||
|
||||
public function check_status()
|
||||
{
|
||||
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||
$this->application->refresh();
|
||||
$this->application->previews->each(function ($preview) {
|
||||
$preview->refresh();
|
||||
});
|
||||
if ($this->application->destination->server->isFunctional()) {
|
||||
dispatch(new ContainerStatusJob($this->application->destination->server));
|
||||
$this->application->refresh();
|
||||
$this->application->previews->each(function ($preview) {
|
||||
$preview->refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function force_deploy_without_cache()
|
||||
@@ -57,22 +60,23 @@ class Heading extends Component
|
||||
|
||||
public function stop()
|
||||
{
|
||||
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id);
|
||||
if ($containers->count() === 0) {
|
||||
return;
|
||||
}
|
||||
foreach ($containers as $container) {
|
||||
$containerName = data_get($container, 'Names');
|
||||
if ($containerName) {
|
||||
instant_remote_process(
|
||||
["docker rm -f {$containerName}"],
|
||||
$this->application->destination->server
|
||||
);
|
||||
$this->application->status = 'exited';
|
||||
$this->application->save();
|
||||
// $this->application->environment->project->team->notify(new StatusChanged($this->application));
|
||||
}
|
||||
}
|
||||
StopApplication::run($this->application);
|
||||
$this->application->status = 'exited';
|
||||
$this->application->save();
|
||||
$this->application->refresh();
|
||||
}
|
||||
public function restart() {
|
||||
$this->setDeploymentUuid();
|
||||
queue_application_deployment(
|
||||
application_id: $this->application->id,
|
||||
deployment_uuid: $this->deploymentUuid,
|
||||
restart_only: true,
|
||||
);
|
||||
return redirect()->route('project.application.deployment', [
|
||||
'project_uuid' => $this->parameters['project_uuid'],
|
||||
'application_uuid' => $this->parameters['application_uuid'],
|
||||
'deployment_uuid' => $this->deploymentUuid,
|
||||
'environment_name' => $this->parameters['environment_name'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ class Form extends Component
|
||||
public function generate_real_url()
|
||||
{
|
||||
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();
|
||||
$this->preview_url_template = Str::of($this->application->preview_url_template)->replace('{{domain}}', $host);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class Previews extends Component
|
||||
public function load_prs()
|
||||
{
|
||||
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->pull_requests = $data->sortBy('number')->values();
|
||||
} catch (\Throwable $e) {
|
||||
@@ -72,8 +72,7 @@ class Previews extends Component
|
||||
public function stop(int $pull_request_id)
|
||||
{
|
||||
try {
|
||||
$container_name = generateApplicationContainerName($this->application);
|
||||
ray('Stopping container: ' . $container_name);
|
||||
$container_name = generateApplicationContainerName($this->application, $pull_request_id);
|
||||
|
||||
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();
|
||||
|
||||
147
app/Http/Livewire/Project/CloneProject.php
Normal file
147
app/Http/Livewire/Project/CloneProject.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?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',
|
||||
]);
|
||||
$foundProject = Project::where('name', $this->newProjectName)->first();
|
||||
if ($foundProject) {
|
||||
throw new \Exception('Project with the same name already exists.');
|
||||
}
|
||||
$newProject = Project::create([
|
||||
'name' => $this->newProjectName,
|
||||
'team_id' => currentTeam()->id,
|
||||
'description' => $this->project->description . ' (clone)',
|
||||
]);
|
||||
if ($this->environment->name !== 'production') {
|
||||
$newProject->environments()->create([
|
||||
'name' => $this->environment->name,
|
||||
]);
|
||||
}
|
||||
$newEnvironment = $newProject->environments->where('name', $this->environment->name)->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->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->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;
|
||||
} else if ($database->type() === 'standalone_mysql') {
|
||||
$payload['standalone_mysql_id'] = $newDatabase->id;
|
||||
}else if ($database->type() === 'standalone_mariadb') {
|
||||
$payload['standalone_mariadb_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->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 $s3s;
|
||||
public ?string $status = null;
|
||||
public array $parameters;
|
||||
|
||||
protected $rules = [
|
||||
@@ -16,6 +17,7 @@ class BackupEdit extends Component
|
||||
'backup.number_of_backups_locally' => 'required|integer|min:1',
|
||||
'backup.save_s3' => 'required|boolean',
|
||||
'backup.s3_storage_id' => 'nullable|integer',
|
||||
'backup.databases_to_backup' => 'nullable',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'backup.enabled' => 'Enabled',
|
||||
@@ -23,6 +25,7 @@ class BackupEdit extends Component
|
||||
'backup.number_of_backups_locally' => 'Number of Backups Locally',
|
||||
'backup.save_s3' => 'Save to S3',
|
||||
'backup.s3_storage_id' => 'S3 Storage',
|
||||
'backup.databases_to_backup' => 'Databases to Backup',
|
||||
];
|
||||
protected $messages = [
|
||||
'backup.s3_storage_id' => 'Select a S3 Storage',
|
||||
@@ -36,7 +39,6 @@ class BackupEdit extends Component
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function delete()
|
||||
{
|
||||
// TODO: Delete backup from server and add a confirmation modal
|
||||
@@ -48,6 +50,7 @@ class BackupEdit extends Component
|
||||
{
|
||||
try {
|
||||
$this->custom_validate();
|
||||
|
||||
$this->backup->save();
|
||||
$this->backup->refresh();
|
||||
$this->emit('success', 'Backup updated successfully');
|
||||
@@ -70,9 +73,11 @@ class BackupEdit extends Component
|
||||
|
||||
public function submit()
|
||||
{
|
||||
ray($this->backup->s3_storage_id);
|
||||
try {
|
||||
$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->refresh();
|
||||
$this->emit('success', 'Backup updated successfully');
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackupExecution;
|
||||
use Livewire\Component;
|
||||
|
||||
class BackupExecution extends Component
|
||||
{
|
||||
public ScheduledDatabaseBackupExecution $execution;
|
||||
|
||||
public function download()
|
||||
{
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
delete_backup_locally($this->execution->filename, $this->execution->scheduledDatabaseBackup->database->destination->server);
|
||||
$this->execution->delete();
|
||||
$this->emit('success', 'Backup deleted successfully.');
|
||||
$this->emit('refreshBackupExecutions');
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,51 @@
|
||||
|
||||
namespace App\Http\Livewire\Project\Database;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Component;
|
||||
|
||||
class BackupExecutions extends Component
|
||||
{
|
||||
public $backup;
|
||||
public $executions;
|
||||
protected $listeners = ['refreshBackupExecutions'];
|
||||
public $setDeletableBackup;
|
||||
protected $listeners = ['refreshBackupExecutions', 'deleteBackup'];
|
||||
|
||||
public function deleteBackup($exeuctionId)
|
||||
{
|
||||
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
||||
if (is_null($execution)) {
|
||||
$this->emit('error', 'Backup execution not found.');
|
||||
return;
|
||||
}
|
||||
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
|
||||
$execution->delete();
|
||||
$this->emit('success', 'Backup deleted successfully.');
|
||||
$this->emit('refreshBackupExecutions');
|
||||
}
|
||||
public function download($exeuctionId)
|
||||
{
|
||||
try {
|
||||
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
||||
if (is_null($execution)) {
|
||||
$this->emit('error', 'Backup execution not found.');
|
||||
return;
|
||||
}
|
||||
$filename = data_get($execution, 'filename');
|
||||
$server = $execution->scheduledDatabaseBackup->database->destination->server;
|
||||
$privateKeyLocation = savePrivateKeyToFs($server);
|
||||
$disk = Storage::build([
|
||||
'driver' => 'sftp',
|
||||
'host' => $server->ip,
|
||||
'port' => $server->port,
|
||||
'username' => $server->user,
|
||||
'privateKey' => $privateKeyLocation,
|
||||
]);
|
||||
return $disk->download($filename);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function refreshBackupExecutions(): void
|
||||
{
|
||||
$this->executions = $this->backup->executions;
|
||||
|
||||
@@ -13,6 +13,6 @@ class BackupNow extends Component
|
||||
dispatch(new DatabaseBackupJob(
|
||||
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 $frequency;
|
||||
public bool $enabled = true;
|
||||
public bool $save_s3 = true;
|
||||
public bool $save_s3 = false;
|
||||
public $s3_storage_id;
|
||||
public $s3s;
|
||||
|
||||
@@ -22,6 +22,11 @@ class CreateScheduledBackup extends Component
|
||||
'frequency' => 'Backup Frequency',
|
||||
'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
|
||||
{
|
||||
@@ -32,7 +37,7 @@ class CreateScheduledBackup extends Component
|
||||
$this->emit('error', 'Invalid Cron / Human expression.');
|
||||
return;
|
||||
}
|
||||
ScheduledDatabaseBackup::create([
|
||||
$payload = [
|
||||
'enabled' => true,
|
||||
'frequency' => $this->frequency,
|
||||
'save_s3' => $this->save_s3,
|
||||
@@ -40,7 +45,15 @@ class CreateScheduledBackup extends Component
|
||||
'database_id' => $this->database->id,
|
||||
'database_type' => $this->database->getMorphClass(),
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
];
|
||||
if ($this->database->type() === 'standalone-postgresql') {
|
||||
$payload['databases_to_backup'] = $this->database->postgres_db;
|
||||
} else if ($this->database->type() === 'standalone-mysql') {
|
||||
$payload['databases_to_backup'] = $this->database->mysql_database;
|
||||
}else if ($this->database->type() === 'standalone-mariadb') {
|
||||
$payload['databases_to_backup'] = $this->database->mariadb_database;
|
||||
}
|
||||
ScheduledDatabaseBackup::create($payload);
|
||||
$this->emit('refreshScheduledBackups');
|
||||
} catch (\Throwable $e) {
|
||||
handleError($e, $this);
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
|
||||
namespace App\Http\Livewire\Project\Database;
|
||||
|
||||
use App\Actions\Database\StartMariadb;
|
||||
use App\Actions\Database\StartMongodb;
|
||||
use App\Actions\Database\StartMysql;
|
||||
use App\Actions\Database\StartPostgresql;
|
||||
use App\Actions\Database\StartRedis;
|
||||
use App\Actions\Database\StopDatabase;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -35,24 +40,28 @@ class Heading extends Component
|
||||
|
||||
public function stop()
|
||||
{
|
||||
instant_remote_process(
|
||||
["docker rm -f {$this->database->uuid}"],
|
||||
$this->database->destination->server
|
||||
);
|
||||
if ($this->database->is_public) {
|
||||
stopPostgresProxy($this->database);
|
||||
$this->database->is_public = false;
|
||||
}
|
||||
StopDatabase::run($this->database);
|
||||
$this->database->status = 'exited';
|
||||
$this->database->save();
|
||||
$this->check_status();
|
||||
// $this->database->environment->project->team->notify(new StatusChanged($this->database));
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
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);
|
||||
} else if ($this->database->type() === 'standalone-redis') {
|
||||
$activity = StartRedis::run($this->database);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
} else if ($this->database->type() === 'standalone-mongodb') {
|
||||
$activity = StartMongodb::run($this->database);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
} else if ($this->database->type() === 'standalone-mysql') {
|
||||
$activity = StartMysql::run($this->database);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
} else if ($this->database->type() === 'standalone-mariadb') {
|
||||
$activity = StartMariadb::run($this->database);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
}
|
||||
}
|
||||
|
||||
95
app/Http/Livewire/Project/Database/Mariadb/General.php
Normal file
95
app/Http/Livewire/Project/Database/Mariadb/General.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\Database\Mariadb;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public StandaloneMariadb $database;
|
||||
public string $db_url;
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.mariadb_root_password' => 'required',
|
||||
'database.mariadb_user' => 'required',
|
||||
'database.mariadb_password' => 'required',
|
||||
'database.mariadb_database' => 'required',
|
||||
'database.mariadb_conf' => 'nullable',
|
||||
'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.mariadb_root_password' => 'Root Password',
|
||||
'database.mariadb_user' => 'User',
|
||||
'database.mariadb_password' => 'Password',
|
||||
'database.mariadb_database' => 'Database',
|
||||
'database.mariadb_conf' => 'MariaDB Configuration',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$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) {
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
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.mariadb.general');
|
||||
}
|
||||
}
|
||||
96
app/Http/Livewire/Project/Database/Mongodb/General.php
Normal file
96
app/Http/Livewire/Project/Database/Mongodb/General.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?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) {
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
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');
|
||||
}
|
||||
}
|
||||
95
app/Http/Livewire/Project/Database/Mysql/General.php
Normal file
95
app/Http/Livewire/Project/Database/Mysql/General.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\Database\Mysql;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\StandaloneMysql;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public StandaloneMysql $database;
|
||||
public string $db_url;
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.mysql_root_password' => 'required',
|
||||
'database.mysql_user' => 'required',
|
||||
'database.mysql_password' => 'required',
|
||||
'database.mysql_database' => 'required',
|
||||
'database.mysql_conf' => 'nullable',
|
||||
'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.mysql_root_password' => 'Root Password',
|
||||
'database.mysql_user' => 'User',
|
||||
'database.mysql_password' => 'Password',
|
||||
'database.mysql_database' => 'Database',
|
||||
'database.mysql_conf' => 'MySQL Configuration',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$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) {
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
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.mysql.general');
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Http\Livewire\Project\Database\Postgresql;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
@@ -47,14 +49,7 @@ class General extends Component
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
$this->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}";
|
||||
}
|
||||
$this->db_url = $this->database->getDbUrl();
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
@@ -65,20 +60,23 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
$this->emit('success', 'Starting TCP proxy...');
|
||||
startPostgresProxy($this->database);
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->emit('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
stopPostgresProxy($this->database);
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->emit('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->getDbUrl();
|
||||
$this->db_url = $this->database->getDbUrl();
|
||||
$this->database->save();
|
||||
} catch(\Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
}
|
||||
public function save_init_script($script)
|
||||
{
|
||||
|
||||
91
app/Http/Livewire/Project/Database/Redis/General.php
Normal file
91
app/Http/Livewire/Project/Database/Redis/General.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?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) {
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
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');
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,10 @@ class DeleteEnvironment extends Component
|
||||
'environment_id' => 'required|int',
|
||||
]);
|
||||
$environment = Environment::findOrFail($this->environment_id);
|
||||
if ($environment->applications->count() > 0) {
|
||||
return $this->emit('error', 'Environment has resources defined, please delete them first.');
|
||||
if ($environment->isEmpty()) {
|
||||
$environment->delete();
|
||||
return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
|
||||
}
|
||||
$environment->delete();
|
||||
return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
|
||||
return $this->emit('error', 'Environment has defined resources, please delete them first.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,6 @@ class DockerCompose extends Component
|
||||
- type: volume
|
||||
source: mydata
|
||||
target: /data
|
||||
volume:
|
||||
nocopy: true
|
||||
- type: bind
|
||||
source: ./var/lib/ghost/data
|
||||
target: /data
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,7 @@ class GithubPrivateRepository extends Component
|
||||
$this->loadBranchByPage();
|
||||
}
|
||||
}
|
||||
$this->selected_branch_name = data_get($this->branches,'0.name');
|
||||
}
|
||||
|
||||
protected function loadBranchByPage()
|
||||
@@ -144,8 +145,9 @@ class GithubPrivateRepository extends Component
|
||||
$application->settings->is_static = $this->is_static;
|
||||
$application->settings->save();
|
||||
|
||||
$sslip = sslip($destination->server);
|
||||
$application->fqdn = "http://{$application->uuid}.$sslip";
|
||||
$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();
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class GithubPrivateRepositoryDeployKey extends Component
|
||||
{
|
||||
@@ -29,7 +30,7 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
public string $repository_url;
|
||||
public string $branch;
|
||||
protected $rules = [
|
||||
'repository_url' => 'required|url',
|
||||
'repository_url' => 'required',
|
||||
'branch' => 'required|string',
|
||||
'port' => 'required|numeric',
|
||||
'is_static' => 'required|boolean',
|
||||
@@ -43,10 +44,9 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
'publish_directory' => 'Publish directory',
|
||||
];
|
||||
private object $repository_url_parsed;
|
||||
private GithubApp|GitlabApp|null $git_source = null;
|
||||
private string $git_host;
|
||||
private GithubApp|GitlabApp|string $git_source = 'other';
|
||||
private ?string $git_host = null;
|
||||
private string $git_repository;
|
||||
private string $git_branch;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
@@ -93,27 +93,42 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
|
||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||
$application_init = [
|
||||
'name' => generate_random_name(),
|
||||
'git_repository' => $this->git_repository,
|
||||
'git_branch' => $this->git_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,
|
||||
'source_id' => $this->git_source->id,
|
||||
'source_type' => $this->git_source->getMorphClass()
|
||||
];
|
||||
if ($this->git_source === 'other') {
|
||||
$application_init = [
|
||||
'name' => generate_random_name(),
|
||||
'git_repository' => $this->git_repository,
|
||||
'git_branch' => $this->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,
|
||||
'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',
|
||||
'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,
|
||||
'source_id' => $this->git_source->id,
|
||||
'source_type' => $this->git_source->getMorphClass()
|
||||
];
|
||||
}
|
||||
|
||||
$application = Application::create($application_init);
|
||||
$application->settings->is_static = $this->is_static;
|
||||
$application->settings->save();
|
||||
|
||||
$sslip = sslip($destination->server);
|
||||
$application->fqdn = "http://{$application->uuid}.$sslip";
|
||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||
$application->fqdn = $fqdn;
|
||||
$application->name = generate_random_name($application->uuid);
|
||||
$application->save();
|
||||
|
||||
@@ -132,18 +147,18 @@ class GithubPrivateRepositoryDeployKey extends Component
|
||||
$this->repository_url_parsed = Url::fromString($this->repository_url);
|
||||
$this->git_host = $this->repository_url_parsed->getHost();
|
||||
$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') {
|
||||
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
||||
} elseif ($this->git_host == 'gitlab.com') {
|
||||
$this->git_source = GitlabApp::where('name', 'Public GitLab')->first();
|
||||
} elseif ($this->git_host == 'bitbucket.org') {
|
||||
// Not supported yet
|
||||
return;
|
||||
}
|
||||
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 int $rate_limit_remaining = 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 = [
|
||||
'repository_url' => 'required|url',
|
||||
'port' => 'required|numeric',
|
||||
@@ -38,10 +43,6 @@ class PublicGitRepository extends Component
|
||||
'is_static' => 'static',
|
||||
'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()
|
||||
{
|
||||
@@ -64,25 +65,34 @@ class PublicGitRepository extends Component
|
||||
}
|
||||
$this->emit('success', 'Application settings updated!');
|
||||
}
|
||||
|
||||
public function load_any_git()
|
||||
{
|
||||
$this->branch_found = true;
|
||||
}
|
||||
public function load_branch()
|
||||
{
|
||||
try {
|
||||
$this->branch_found = false;
|
||||
$this->validate([
|
||||
'repository_url' => 'required|url'
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
try {
|
||||
$this->branch_found = false;
|
||||
$this->get_git_source();
|
||||
$this->get_branch();
|
||||
$this->selected_branch = $this->git_branch;
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
if (!$this->branch_found && $this->git_branch == 'main') {
|
||||
try {
|
||||
$this->git_branch = 'master';
|
||||
$this->get_branch();
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
if (!$this->branch_found && $this->git_branch == 'main') {
|
||||
try {
|
||||
$this->git_branch = 'master';
|
||||
$this->get_branch();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
} else {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
@@ -97,21 +107,23 @@ class PublicGitRepository extends Component
|
||||
|
||||
if ($this->git_host == 'github.com') {
|
||||
$this->git_source = GithubApp::where('name', 'Public GitHub')->first();
|
||||
} elseif ($this->git_host == 'gitlab.com') {
|
||||
$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?!');
|
||||
return;
|
||||
}
|
||||
$this->git_repository = $this->repository_url;
|
||||
$this->git_source = 'other';
|
||||
}
|
||||
|
||||
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}");
|
||||
$this->rate_limit_reset = Carbon::parse((int)$this->rate_limit_reset)->format('Y-M-d H:i:s');
|
||||
$this->branch_found = true;
|
||||
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->branch_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
@@ -122,9 +134,6 @@ class PublicGitRepository extends Component
|
||||
$project_uuid = $this->parameters['project_uuid'];
|
||||
$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();
|
||||
if (!$destination) {
|
||||
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
|
||||
@@ -137,28 +146,42 @@ class PublicGitRepository extends Component
|
||||
$project = Project::where('uuid', $project_uuid)->first();
|
||||
$environment = $project->load(['environments'])->environments->where('name', $environment_name)->first();
|
||||
|
||||
$application_init = [
|
||||
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
||||
'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,
|
||||
'source_id' => $this->git_source->id,
|
||||
'source_type' => $this->git_source->getMorphClass()
|
||||
];
|
||||
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 = [
|
||||
'name' => generate_application_name($this->git_repository, $this->git_branch),
|
||||
'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,
|
||||
'source_id' => $this->git_source->id,
|
||||
'source_type' => $this->git_source->getMorphClass()
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$application = Application::create($application_init);
|
||||
|
||||
$application->settings->is_static = $this->is_static;
|
||||
$application->settings->save();
|
||||
|
||||
$sslip = sslip($destination->server);
|
||||
$application->fqdn = "http://{$application->uuid}.$sslip";
|
||||
$application->name = generate_application_name($this->git_repository, $this->git_branch, $application->uuid);
|
||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||
$application->fqdn = $fqdn;
|
||||
$application->save();
|
||||
|
||||
return redirect()->route('project.application.configuration', [
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
namespace App\Http\Livewire\Project\New;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use Countable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Component;
|
||||
|
||||
class Select extends Component
|
||||
@@ -22,13 +21,18 @@ class Select extends Component
|
||||
public Collection|array $swarmDockers = [];
|
||||
public array $parameters;
|
||||
public Collection|array $services = [];
|
||||
public Collection|array $allServices = [];
|
||||
|
||||
public bool $loadingServices = true;
|
||||
public bool $loading = false;
|
||||
|
||||
public $environments = [];
|
||||
public ?string $selectedEnvironment = null;
|
||||
public ?string $existingPostgresqlUrl = null;
|
||||
|
||||
public ?string $search = null;
|
||||
protected $queryString = [
|
||||
'server',
|
||||
'search'
|
||||
];
|
||||
|
||||
public function mount()
|
||||
@@ -37,6 +41,22 @@ class Select extends Component
|
||||
if (isDev()) {
|
||||
$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 render()
|
||||
{
|
||||
$this->loadServices();
|
||||
return view('livewire.project.new.select');
|
||||
}
|
||||
|
||||
public function updatedSelectedEnvironment()
|
||||
{
|
||||
return redirect()->route('project.resources.new', [
|
||||
'project_uuid' => $this->parameters['project_uuid'],
|
||||
'environment_name' => $this->selectedEnvironment,
|
||||
]);
|
||||
}
|
||||
|
||||
// public function addExistingPostgresql()
|
||||
@@ -49,19 +69,28 @@ class Select extends Component
|
||||
// }
|
||||
// }
|
||||
|
||||
public function loadThings()
|
||||
{
|
||||
$this->loadServices();
|
||||
$this->loadServers();
|
||||
}
|
||||
public function loadServices(bool $forceReload = false)
|
||||
public function loadServices(bool $force = false)
|
||||
{
|
||||
try {
|
||||
if ($forceReload) {
|
||||
Cache::forget('services');
|
||||
if (count($this->allServices) > 0 && !$force) {
|
||||
if (!$this->search) {
|
||||
$this->services = $this->allServices;
|
||||
return;
|
||||
}
|
||||
$this->services = $this->allServices->filter(function ($service, $key) {
|
||||
$tags = collect(data_get($service, 'tags', []));
|
||||
return str_contains(strtolower($key), strtolower($this->search)) || $tags->contains(function ($tag) {
|
||||
return str_contains(strtolower($tag), strtolower($this->search));
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$this->search = null;
|
||||
$this->allServices = getServiceTemplates();
|
||||
$this->services = $this->allServices->filter(function ($service, $key) {
|
||||
return str_contains(strtolower($key), strtolower($this->search));
|
||||
});;
|
||||
$this->emit('success', 'Successfully loaded services.');
|
||||
}
|
||||
$this->services = getServiceTemplates();
|
||||
$this->emit('success', 'Successfully loaded services.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
|
||||
@@ -45,6 +45,9 @@ CMD ["nginx", "-g", "daemon off;"]
|
||||
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
|
||||
|
||||
$port = get_port_from_dockerfile($this->dockerfile);
|
||||
if (!$port) {
|
||||
$port = 80;
|
||||
}
|
||||
$application = Application::create([
|
||||
'name' => 'dockerfile-' . new Cuid2(7),
|
||||
'repository_project_id' => 0,
|
||||
@@ -56,14 +59,15 @@ CMD ["nginx", "-g", "daemon off;"]
|
||||
'environment_id' => $environment->id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination_class,
|
||||
'health_check_enabled' => false,
|
||||
'source_id' => 0,
|
||||
'source_type' => GithubApp::class
|
||||
]);
|
||||
|
||||
$sslip = sslip($destination->server);
|
||||
$fqdn = generateFqdn($destination->server, $application->uuid);
|
||||
$application->update([
|
||||
'name' => 'dockerfile-' . $application->uuid,
|
||||
'fqdn' => "http://{$application->uuid}.$sslip"
|
||||
'fqdn' => $fqdn
|
||||
]);
|
||||
|
||||
redirect()->route('project.application.configuration', [
|
||||
|
||||
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 = null;
|
||||
public ?string $actual = null;
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.compose-modal');
|
||||
}
|
||||
public function submit() {
|
||||
$this->emit('warning', "Saving new docker compose...");
|
||||
$this->emit('saveCompose', $this->raw);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ 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',
|
||||
@@ -23,22 +24,28 @@ class FileStorage extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->service = $this->fileStorage->service;
|
||||
$this->fs_path = Str::of($this->fileStorage->fs_path)->beforeLast('/');
|
||||
$file = Str::of($this->fileStorage->fs_path)->afterLast('/');
|
||||
if (Str::of($this->fs_path)->startsWith('.')) {
|
||||
$this->fs_path = Str::of($this->fs_path)->after('.');
|
||||
$this->fs_path = $this->service->service->workdir() . $this->fs_path . "/" . $file;
|
||||
}
|
||||
|
||||
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->service->saveFileVolumes();
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Http\Livewire\Project\Service;
|
||||
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Models\Service;
|
||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
@@ -14,17 +13,25 @@ class Index extends Component
|
||||
public $databases;
|
||||
public array $parameters;
|
||||
public array $query;
|
||||
protected $rules = [
|
||||
'service.docker_compose_raw' => 'required',
|
||||
'service.docker_compose' => 'required',
|
||||
'service.name' => 'required',
|
||||
'service.description' => 'nullable',
|
||||
];
|
||||
public function checkStatus() {
|
||||
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||
$this->refreshStack();
|
||||
protected $listeners = ["refreshStacks", "checkStatus"];
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.index');
|
||||
}
|
||||
public function refreshStack()
|
||||
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) {
|
||||
@@ -35,30 +42,4 @@ class Index extends Component
|
||||
$database->refresh();
|
||||
});
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->query = request()->query();
|
||||
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
|
||||
$this->refreshStack();
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.index');
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->service->save();
|
||||
$this->service->parse();
|
||||
$this->service->refresh();
|
||||
$this->service->saveComposeConfigs();
|
||||
$this->refreshStack();
|
||||
$this->emit('refreshEnvs');
|
||||
$this->emit('success', 'Service saved successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ use Livewire\Component;
|
||||
|
||||
class Modal extends Component
|
||||
{
|
||||
public function serviceStatusUpdated() {
|
||||
$this->emit('serviceStatusUpdated');
|
||||
public function checkStatus() {
|
||||
$this->emit('checkStatus');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ 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;
|
||||
|
||||
@@ -13,20 +12,13 @@ class Navbar extends Component
|
||||
public Service $service;
|
||||
public array $parameters;
|
||||
public array $query;
|
||||
protected $listeners = ['serviceStatusUpdated'];
|
||||
protected $listeners = ["checkStatus"];
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.navbar');
|
||||
}
|
||||
public function serviceStatusUpdated()
|
||||
{
|
||||
ray('serviceStatusUpdated');
|
||||
$this->check_status();
|
||||
}
|
||||
public function check_status()
|
||||
{
|
||||
dispatch_sync(new ContainerStatusJob($this->service->server));
|
||||
public function checkStatus() {
|
||||
$this->service->refresh();
|
||||
}
|
||||
public function deploy()
|
||||
@@ -40,5 +32,6 @@ class Navbar extends Component
|
||||
StopService::run($this->service);
|
||||
$this->service->refresh();
|
||||
$this->emit('success', 'Service stopped successfully.');
|
||||
$this->emit('checkStatus');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,16 +20,23 @@ class Show extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$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;
|
||||
} else {
|
||||
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
|
||||
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()
|
||||
{
|
||||
|
||||
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,7 +2,7 @@
|
||||
|
||||
namespace App\Http\Livewire\Project\Shared;
|
||||
|
||||
use App\Actions\Service\StopService;
|
||||
use App\Jobs\DeleteResourceJob;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
@@ -10,7 +10,7 @@ class Danger extends Component
|
||||
{
|
||||
public $resource;
|
||||
public array $parameters;
|
||||
public string|null $modalId = null;
|
||||
public ?string $modalId = null;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
@@ -20,20 +20,8 @@ class Danger extends Component
|
||||
|
||||
public function delete()
|
||||
{
|
||||
// Should be queued
|
||||
try {
|
||||
if ($this->resource->type() === 'service') {
|
||||
$server = $this->resource->server;
|
||||
StopService::run($this->resource);
|
||||
} else {
|
||||
$destination = data_get($this->resource, 'destination');
|
||||
if ($destination) {
|
||||
$destination = $this->resource->destination->getMorphClass()::where('id', $this->resource->destination->id)->first();
|
||||
$server = $destination->server;
|
||||
}
|
||||
instant_remote_process(["docker rm -f {$this->resource->uuid}"], $server);
|
||||
}
|
||||
$this->resource->delete();
|
||||
DeleteResourceJob::dispatchSync($this->resource);
|
||||
return redirect()->route('project.resources', [
|
||||
'project_uuid' => $this->parameters['project_uuid'],
|
||||
'environment_name' => $this->parameters['environment_name']
|
||||
|
||||
@@ -31,11 +31,17 @@ class All extends Component
|
||||
public function getDevView()
|
||||
{
|
||||
$this->variables = $this->resource->environment_variables->map(function ($item) {
|
||||
if ($item->is_shown_once) {
|
||||
return "$item->key=(locked secret)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
');
|
||||
if ($this->showPreview) {
|
||||
$this->variablesPreview = $this->resource->environment_variables_preview->map(function ($item) {
|
||||
if ($item->is_shown_once) {
|
||||
return "$item->key=(locked secret)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
');
|
||||
@@ -49,19 +55,27 @@ class All extends Component
|
||||
{
|
||||
if ($isPreview) {
|
||||
$variables = parseEnvFormatToArray($this->variablesPreview);
|
||||
$existingVariables = $this->resource->environment_variables_preview();
|
||||
$this->resource->environment_variables_preview()->delete();
|
||||
} else {
|
||||
$variables = parseEnvFormatToArray($this->variables);
|
||||
$existingVariables = $this->resource->environment_variables();
|
||||
$this->resource->environment_variables()->delete();
|
||||
}
|
||||
foreach ($variables as $key => $variable) {
|
||||
$found = $existingVariables->where('key', $key)->first();
|
||||
$found = $this->resource->environment_variables()->where('key', $key)->first();
|
||||
$foundPreview = $this->resource->environment_variables_preview()->where('key', $key)->first();
|
||||
if ($found) {
|
||||
if ($found->is_shown_once) {
|
||||
continue;
|
||||
}
|
||||
$found->value = $variable;
|
||||
$found->save();
|
||||
continue;
|
||||
}
|
||||
if ($foundPreview) {
|
||||
if ($foundPreview->is_shown_once) {
|
||||
continue;
|
||||
}
|
||||
$foundPreview->value = $variable;
|
||||
$foundPreview->save();
|
||||
continue;
|
||||
} else {
|
||||
$environment = new EnvironmentVariable();
|
||||
$environment->key = $key;
|
||||
@@ -75,6 +89,18 @@ class All extends Component
|
||||
case 'standalone-postgresql':
|
||||
$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 'standalone-mysql':
|
||||
$environment->standalone_mysql_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-mariadb':
|
||||
$environment->standalone_mariadb_id = $this->resource->id;
|
||||
break;
|
||||
case 'service':
|
||||
$environment->service_id = $this->resource->id;
|
||||
break;
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
|
||||
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
@@ -13,29 +12,45 @@ class Show extends Component
|
||||
public ModelsEnvironmentVariable $env;
|
||||
public ?string $modalId = null;
|
||||
public bool $isDisabled = false;
|
||||
public bool $isLocked = false;
|
||||
public string $type;
|
||||
|
||||
protected $rules = [
|
||||
'env.key' => 'required|string',
|
||||
'env.value' => 'nullable',
|
||||
'env.is_build_time' => 'required|boolean',
|
||||
'env.is_shown_once' => 'required|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'key' => 'key',
|
||||
'value' => 'value',
|
||||
'is_build_time' => 'build',
|
||||
'key' => 'Key',
|
||||
'value' => 'Value',
|
||||
'is_build_time' => 'Build Time',
|
||||
'is_shown_once' => 'Shown Once',
|
||||
];
|
||||
|
||||
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->parameters = get_route_parameters();
|
||||
$this->checkEnvs();
|
||||
}
|
||||
public function checkEnvs()
|
||||
{
|
||||
$this->isDisabled = false;
|
||||
if (str($this->env->key)->startsWith('SERVICE_FQDN') || str($this->env->key)->startsWith('SERVICE_URL')) {
|
||||
$this->isDisabled = true;
|
||||
}
|
||||
if ($this->env->is_shown_once) {
|
||||
$this->isLocked = true;
|
||||
}
|
||||
}
|
||||
public function lock()
|
||||
{
|
||||
$this->env->is_shown_once = true;
|
||||
$this->env->save();
|
||||
$this->checkEnvs();
|
||||
$this->emit('refreshEnvs');
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
$this->submit();
|
||||
|
||||
52
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
52
app/Http/Livewire/Project/Shared/GetLogs.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?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);
|
||||
});
|
||||
if ($this->showTimeStamps) {
|
||||
$this->outputs = str($this->outputs)->split('/\n/')->sort(function ($a, $b) {
|
||||
$a = explode(' ', $a);
|
||||
$b = explode(' ', $b);
|
||||
return $a[0] <=> $b[0];
|
||||
})->join("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.shared.get-logs');
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ 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',
|
||||
@@ -22,12 +23,19 @@ class HealthChecks extends Component
|
||||
'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('saved');
|
||||
$this->emit('success', 'Health check updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
73
app/Http/Livewire/Project/Shared/Logs.php
Normal file
73
app/Http/Livewire/Project/Shared/Logs.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\Shared;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Livewire\Component;
|
||||
|
||||
class Logs extends Component
|
||||
{
|
||||
public ?string $type = null;
|
||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
|
||||
public Server $server;
|
||||
public ?string $container = null;
|
||||
public $parameters;
|
||||
public $query;
|
||||
public $status;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->query = request()->query();
|
||||
if (data_get($this->parameters, 'application_uuid')) {
|
||||
$this->type = 'application';
|
||||
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
||||
$this->status = $this->resource->status;
|
||||
$this->server = $this->resource->destination->server;
|
||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id);
|
||||
if ($containers->count() > 0) {
|
||||
$this->container = data_get($containers[0], 'Names');
|
||||
}
|
||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||
$this->type = 'database';
|
||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
abort(404);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->resource = $resource;
|
||||
$this->status = $this->resource->status;
|
||||
$this->server = $this->resource->destination->server;
|
||||
$this->container = $this->resource->uuid;
|
||||
} else if (data_get($this->parameters, 'service_uuid')) {
|
||||
$this->type = 'service';
|
||||
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
||||
$this->status = $this->resource->status;
|
||||
$this->server = $this->resource->server;
|
||||
$this->container = data_get($this->parameters, 'service_name') . '-' . $this->resource->uuid;
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.shared.logs');
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use Livewire\Component;
|
||||
|
||||
class Add extends Component
|
||||
{
|
||||
public $uuid;
|
||||
public $parameters;
|
||||
public string $name;
|
||||
public string $mount_path;
|
||||
@@ -31,8 +32,9 @@ class Add extends Component
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$this->emitUp('submit', [
|
||||
'name' => $this->name,
|
||||
$name = $this->uuid . '-' . $this->name;
|
||||
$this->emit('addNewVolume', [
|
||||
'name' => $name,
|
||||
'mount_path' => $this->mount_path,
|
||||
'host_path' => $this->host_path,
|
||||
]);
|
||||
|
||||
@@ -8,28 +8,10 @@ use Livewire\Component;
|
||||
class All extends Component
|
||||
{
|
||||
public $resource;
|
||||
protected $listeners = ['refreshStorages', 'submit'];
|
||||
protected $listeners = ['refreshStorages'];
|
||||
|
||||
public function refreshStorages()
|
||||
{
|
||||
$this->resource->refresh();
|
||||
}
|
||||
|
||||
public function submit($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');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class Show extends Component
|
||||
public LocalPersistentVolume $storage;
|
||||
public bool $isReadOnly = false;
|
||||
public ?string $modalId = null;
|
||||
public ?string $realName = null;
|
||||
public bool $isFirst = true;
|
||||
|
||||
protected $rules = [
|
||||
'storage.name' => 'required|string',
|
||||
@@ -26,11 +26,6 @@ class Show extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if ($this->storage->resource_type === 'App\Models\ServiceApplication' || $this->storage->resource_type === 'App\Models\ServiceDatabase') {
|
||||
$this->realName = "{$this->storage->service->service->uuid}_{$this->storage->name}";
|
||||
} else {
|
||||
$this->realName = $this->storage->name;
|
||||
}
|
||||
$this->modalId = new Cuid2(7);
|
||||
}
|
||||
|
||||
|
||||
19
app/Http/Livewire/Project/Shared/Webhooks.php
Normal file
19
app/Http/Livewire/Project/Shared/Webhooks.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\Shared;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Webhooks extends Component
|
||||
{
|
||||
public $resource;
|
||||
public ?string $deploywebhook = null;
|
||||
public function mount()
|
||||
{
|
||||
$this->deploywebhook = generateDeployWebhook($this->resource);
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.shared.webhooks');
|
||||
}
|
||||
}
|
||||
38
app/Http/Livewire/Security/ApiTokens.php
Normal file
38
app/Http/Livewire/Security/ApiTokens.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Security;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class ApiTokens extends Component
|
||||
{
|
||||
public ?string $description = null;
|
||||
public $tokens = [];
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.security.api-tokens');
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$this->tokens = auth()->user()->tokens;
|
||||
}
|
||||
public function addNewToken()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'description' => 'required|min:3|max:255',
|
||||
]);
|
||||
$token = auth()->user()->createToken($this->description);
|
||||
$this->tokens = auth()->user()->tokens;
|
||||
session()->flash('token', $token->plainTextToken);
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function revoke(int $id)
|
||||
{
|
||||
$token = auth()->user()->tokens()->where('id', $id)->first();
|
||||
$token->delete();
|
||||
$this->tokens = auth()->user()->tokens;
|
||||
}
|
||||
}
|
||||
29
app/Http/Livewire/Server/Create.php
Normal file
29
app/Http/Livewire/Server/Create.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Server;
|
||||
|
||||
use App\Models\PrivateKey;
|
||||
use Livewire\Component;
|
||||
|
||||
class Create extends Component
|
||||
{
|
||||
public $private_keys = [];
|
||||
public bool $limit_reached = false;
|
||||
public function mount()
|
||||
{
|
||||
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
|
||||
if (!isCloud()) {
|
||||
$this->limit_reached = false;
|
||||
return;
|
||||
}
|
||||
$team = currentTeam();
|
||||
$servers = $team->servers->count();
|
||||
['serverLimit' => $serverLimit] = $team->limits;
|
||||
|
||||
$this->limit_reached = $servers >= $serverLimit;
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.create');
|
||||
}
|
||||
}
|
||||
31
app/Http/Livewire/Server/Delete.php
Normal file
31
app/Http/Livewire/Server/Delete.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Server;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
||||
class Delete extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public $server;
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
$this->authorize('delete', $this->server);
|
||||
if ($this->server->hasDefinedResources()) {
|
||||
$this->emit('error', 'Server has defined resources. Please delete them first.');
|
||||
return;
|
||||
}
|
||||
$this->server->delete();
|
||||
return redirect()->route('server.all');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.delete');
|
||||
}
|
||||
}
|
||||
28
app/Http/Livewire/Server/Destination/Show.php
Normal file
28
app/Http/Livewire/Server/Destination/Show.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Server\Destination;
|
||||
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public ?Server $server = null;
|
||||
public $parameters = [];
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
try {
|
||||
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||
if (is_null($this->server)) {
|
||||
return redirect()->route('server.all');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.destination.show');
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,17 @@ namespace App\Http\Livewire\Server;
|
||||
|
||||
use App\Actions\Server\InstallDocker;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
||||
class Form extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
public Server $server;
|
||||
public $uptime;
|
||||
public $dockerVersion;
|
||||
public string|null $wildcard_domain = null;
|
||||
public bool $isValidConnection = false;
|
||||
public bool $isValidDocker = false;
|
||||
public ?string $wildcard_domain = null;
|
||||
public int $cleanup_after_percentage;
|
||||
public bool $dockerInstallationStarted = false;
|
||||
protected $listeners = ['serverRefresh'];
|
||||
|
||||
protected $rules = [
|
||||
'server.name' => 'required|min:6',
|
||||
@@ -29,11 +28,11 @@ class Form extends Component
|
||||
'wildcard_domain' => 'nullable|url',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'server.name' => 'name',
|
||||
'server.description' => 'description',
|
||||
'server.ip' => 'ip',
|
||||
'server.user' => 'user',
|
||||
'server.port' => 'port',
|
||||
'server.name' => 'Name',
|
||||
'server.description' => 'Description',
|
||||
'server.ip' => 'IP address/Domain',
|
||||
'server.user' => 'User',
|
||||
'server.port' => 'Port',
|
||||
'server.settings.is_cloudflare_tunnel' => 'Cloudflare Tunnel',
|
||||
'server.settings.is_reachable' => 'is reachable',
|
||||
'server.settings.is_part_of_swarm' => 'is part of swarm'
|
||||
@@ -44,59 +43,77 @@ class Form extends Component
|
||||
$this->wildcard_domain = $this->server->settings->wildcard_domain;
|
||||
$this->cleanup_after_percentage = $this->server->settings->cleanup_after_percentage;
|
||||
}
|
||||
public function instantSave() {
|
||||
public function serverRefresh()
|
||||
{
|
||||
$this->validateServer();
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->validateServer();
|
||||
$this->server->settings->save();
|
||||
}
|
||||
public function installDocker()
|
||||
{
|
||||
$this->emit('installDocker');
|
||||
$this->dockerInstallationStarted = true;
|
||||
$activity = resolve(InstallDocker::class)($this->server);
|
||||
$activity = InstallDocker::run($this->server);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
}
|
||||
|
||||
public function validateServer()
|
||||
public function checkLocalhostConnection()
|
||||
{
|
||||
$uptime = $this->server->validateConnection();
|
||||
if ($uptime) {
|
||||
$this->emit('success', 'Server is reachable.');
|
||||
$this->server->settings->is_reachable = true;
|
||||
$this->server->settings->is_usable = true;
|
||||
$this->server->settings->save();
|
||||
} else {
|
||||
$this->emit('error', 'Server is not reachable. Please check your connection and configuration.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
public function validateServer($install = true)
|
||||
{
|
||||
try {
|
||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true);
|
||||
$uptime = $this->server->validateConnection();
|
||||
if ($uptime) {
|
||||
$this->uptime = $uptime;
|
||||
$this->emit('success', 'Server is reachable.');
|
||||
$install && $this->emit('success', 'Server is reachable.');
|
||||
} else {
|
||||
$this->emit('error', 'Server is not reachable.');
|
||||
$install && $this->emit('error', 'Server is not reachable. Please check your connection and configuration.');
|
||||
return;
|
||||
}
|
||||
if ($dockerVersion) {
|
||||
$this->dockerVersion = $dockerVersion;
|
||||
$this->emit('success', 'Docker Engine 23+ is installed!');
|
||||
$dockerInstalled = $this->server->validateDockerEngine();
|
||||
if ($dockerInstalled) {
|
||||
$install && $this->emit('success', 'Docker Engine is installed.<br> Checking version.');
|
||||
} else {
|
||||
$this->emit('error', 'No Docker Engine or older than 23 version installed.');
|
||||
$install && $this->installDocker();
|
||||
return;
|
||||
}
|
||||
$dockerVersion = $this->server->validateDockerEngineVersion();
|
||||
if ($dockerVersion) {
|
||||
$install && $this->emit('success', 'Docker Engine version is 23+.');
|
||||
} else {
|
||||
$install && $this->installDocker();
|
||||
return;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this, customErrorMessage: "Server is not reachable: ");
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->emit('proxyStatusUpdated');
|
||||
}
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
$this->authorize('delete', $this->server);
|
||||
if (!$this->server->isEmpty()) {
|
||||
$this->emit('error', 'Server has defined resources. Please delete them first.');
|
||||
return;
|
||||
}
|
||||
$this->server->delete();
|
||||
return redirect()->route('server.all');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
if (isCloud() && !isDev()) {
|
||||
$this->validate();
|
||||
$this->validate([
|
||||
'server.ip' => 'required',
|
||||
]);
|
||||
} else {
|
||||
$this->validate();
|
||||
}
|
||||
$uniqueIPs = Server::all()->reject(function (Server $server) {
|
||||
return $server->id === $this->server->id;
|
||||
})->pluck('ip')->toArray();
|
||||
@@ -104,6 +121,7 @@ class Form extends Component
|
||||
$this->emit('error', 'IP address is already in use by another team.');
|
||||
return;
|
||||
}
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->server->settings->wildcard_domain = $this->wildcard_domain;
|
||||
$this->server->settings->cleanup_after_percentage = $this->cleanup_after_percentage;
|
||||
$this->server->settings->save();
|
||||
|
||||
@@ -11,13 +11,13 @@ class ByIp extends Component
|
||||
{
|
||||
public $private_keys;
|
||||
public $limit_reached;
|
||||
public int|null $private_key_id = null;
|
||||
public ?int $private_key_id = null;
|
||||
public $new_private_key_name;
|
||||
public $new_private_key_description;
|
||||
public $new_private_key_value;
|
||||
|
||||
public string $name;
|
||||
public string|null $description = null;
|
||||
public ?string $description = null;
|
||||
public string $ip;
|
||||
public string $user = 'root';
|
||||
public int $port = 22;
|
||||
@@ -31,11 +31,11 @@ class ByIp extends Component
|
||||
'port' => 'required|integer',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'name' => 'name',
|
||||
'description' => 'description',
|
||||
'ip' => 'ip',
|
||||
'user' => 'user',
|
||||
'port' => 'port',
|
||||
'name' => 'Name',
|
||||
'description' => 'Description',
|
||||
'ip' => 'IP Address/Domain',
|
||||
'user' => 'User',
|
||||
'port' => 'Port',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
|
||||
31
app/Http/Livewire/Server/PrivateKey/Show.php
Normal file
31
app/Http/Livewire/Server/PrivateKey/Show.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Server\PrivateKey;
|
||||
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public ?Server $server = null;
|
||||
public $privateKeys = [];
|
||||
public $parameters = [];
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
try {
|
||||
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
|
||||
if (is_null($this->server)) {
|
||||
return redirect()->route('server.all');
|
||||
}
|
||||
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.private-key.show');
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Livewire\Server\Proxy;
|
||||
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
@@ -11,18 +12,40 @@ class Deploy extends Component
|
||||
public Server $server;
|
||||
public bool $traefikDashboardAvailable = false;
|
||||
public ?string $currentRoute = null;
|
||||
protected $listeners = ['proxyStatusUpdated', 'traefikDashboardAvailable'];
|
||||
public ?string $serverIp = null;
|
||||
|
||||
public function mount() {
|
||||
protected $listeners = ['proxyStatusUpdated', 'traefikDashboardAvailable', 'serverRefresh' => 'proxyStatusUpdated', "checkProxy", "startProxy"];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if ($this->server->id === 0) {
|
||||
$this->serverIp = base_ip();
|
||||
} else {
|
||||
$this->serverIp = $this->server->ip;
|
||||
}
|
||||
$this->currentRoute = request()->route()->getName();
|
||||
}
|
||||
public function traefikDashboardAvailable(bool $data) {
|
||||
public function traefikDashboardAvailable(bool $data)
|
||||
{
|
||||
$this->traefikDashboardAvailable = $data;
|
||||
}
|
||||
public function proxyStatusUpdated()
|
||||
{
|
||||
$this->server->refresh();
|
||||
}
|
||||
public function ip()
|
||||
{
|
||||
}
|
||||
public function checkProxy()
|
||||
{
|
||||
try {
|
||||
CheckProxy::run($this->server, true);
|
||||
$this->emit('startProxyPolling');
|
||||
$this->emit('proxyChecked');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function startProxy()
|
||||
{
|
||||
try {
|
||||
|
||||
28
app/Http/Livewire/Server/Proxy/Logs.php
Normal file
28
app/Http/Livewire/Server/Proxy/Logs.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Server\Proxy;
|
||||
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
class Logs extends Component
|
||||
{
|
||||
public ?Server $server = null;
|
||||
public $parameters = [];
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
try {
|
||||
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||
if (is_null($this->server)) {
|
||||
return redirect()->route('server.all');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.proxy.logs');
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,6 @@ class Modal extends Component
|
||||
|
||||
public function proxyStatusUpdated()
|
||||
{
|
||||
$this->server->proxy->set('status', 'running');
|
||||
$this->server->save();
|
||||
$this->emit('proxyStatusUpdated');
|
||||
}
|
||||
}
|
||||
|
||||
33
app/Http/Livewire/Server/Proxy/Show.php
Normal file
33
app/Http/Livewire/Server/Proxy/Show.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Server\Proxy;
|
||||
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public ?Server $server = null;
|
||||
public $parameters = [];
|
||||
protected $listeners = ['proxyStatusUpdated'];
|
||||
public function proxyStatusUpdated()
|
||||
{
|
||||
$this->server->refresh();
|
||||
}
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
try {
|
||||
$this->server = Server::ownedByCurrentTeam(['name', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||
if (is_null($this->server)) {
|
||||
return redirect()->route('server.all');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.proxy.show');
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Livewire\Server\Proxy;
|
||||
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
@@ -9,12 +10,42 @@ use Livewire\Component;
|
||||
class Status extends Component
|
||||
{
|
||||
public Server $server;
|
||||
public bool $polling = false;
|
||||
public int $numberOfPolls = 0;
|
||||
|
||||
protected $listeners = ['proxyStatusUpdated'];
|
||||
protected $listeners = ['proxyStatusUpdated', 'startProxyPolling'];
|
||||
public function startProxyPolling()
|
||||
{
|
||||
$this->polling = true;
|
||||
}
|
||||
public function proxyStatusUpdated()
|
||||
{
|
||||
$this->server->refresh();
|
||||
}
|
||||
public function checkProxy(bool $notification = false)
|
||||
{
|
||||
try {
|
||||
if ($this->polling) {
|
||||
if ($this->numberOfPolls >= 10) {
|
||||
$this->polling = false;
|
||||
$this->numberOfPolls = 0;
|
||||
$notification && $this->emit('error', 'Proxy is not running.');
|
||||
return;
|
||||
}
|
||||
$this->numberOfPolls++;
|
||||
}
|
||||
CheckProxy::run($this->server, true);
|
||||
$this->emit('proxyStatusUpdated');
|
||||
if ($this->server->proxy->status === 'running') {
|
||||
$this->polling = false;
|
||||
$notification && $this->emit('success', 'Proxy is running.');
|
||||
} else {
|
||||
$notification && $this->emit('error', 'Proxy is not running.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function getProxyStatus()
|
||||
{
|
||||
try {
|
||||
@@ -24,9 +55,4 @@ class Status extends Component
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function getProxyStatusWithNoti()
|
||||
{
|
||||
$this->emit('success', 'Refreshed proxy status.');
|
||||
$this->getProxyStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,10 @@ class Show extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
public ?Server $server = null;
|
||||
public $parameters = [];
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
try {
|
||||
$this->server = Server::ownedByCurrentTeam(['name', 'description', 'ip', 'port', 'user', 'proxy'])->whereUuid(request()->server_uuid)->first();
|
||||
if (is_null($this->server)) {
|
||||
@@ -21,6 +23,10 @@ class Show extends Component
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
$this->emit('serverRefresh');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.show');
|
||||
|
||||
@@ -35,31 +35,13 @@ class ShowPrivateKey extends Component
|
||||
public function checkConnection()
|
||||
{
|
||||
try {
|
||||
['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->server, true);
|
||||
$uptime = $this->server->validateConnection();
|
||||
if ($uptime) {
|
||||
$this->server->settings->update([
|
||||
'is_reachable' => true
|
||||
]);
|
||||
$this->emit('success', 'Server is reachable with this private key.');
|
||||
$this->emit('success', 'Server is reachable.');
|
||||
} else {
|
||||
$this->server->settings->update([
|
||||
'is_reachable' => false,
|
||||
'is_usable' => false
|
||||
]);
|
||||
$this->emit('error', 'Server is not reachable with this private key.');
|
||||
$this->emit('error', 'Server is not reachable. Please check your connection and private key configuration.');
|
||||
return;
|
||||
}
|
||||
if ($dockerVersion) {
|
||||
$this->server->settings->update([
|
||||
'is_usable' => true
|
||||
]);
|
||||
$this->emit('success', 'Server is usable for Coolify.');
|
||||
} else {
|
||||
$this->server->settings->update([
|
||||
'is_usable' => false
|
||||
]);
|
||||
$this->emit('error', 'Old (lower than 23) or no Docker version detected. Install Docker Engine on the General tab.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user