Compare commits

...

90 Commits

Author SHA1 Message Date
Andras Bacsai
0e99f27108 Merge pull request #1621 from coollabsio/next
v4.0.0-beta.185
2024-01-10 11:09:41 +01:00
Andras Bacsai
f445a8c312 fix: update navbar on build_pack change 2024-01-10 11:07:53 +01:00
Andras Bacsai
845fc191d4 Remove ray debug statement in updatedApplicationBuildPack method 2024-01-10 11:00:11 +01:00
Andras Bacsai
95d0d72e0d Fix application build pack logic 2024-01-10 11:00:06 +01:00
Andras Bacsai
76f695036c fix: static buildpack should set port 80 2024-01-10 10:58:31 +01:00
Andras Bacsai
225bf06736 Update version numbers and remove unused files 2024-01-10 10:50:07 +01:00
Andras Bacsai
3a287ae974 Update Dockerfile to use serversideup/php:8.2-fpm-nginx image 2024-01-10 09:25:04 +01:00
Andras Bacsai
e5c61b9f9f Merge pull request #1620 from coollabsio/next
fix: fix php-pgsql version to 8.2
2024-01-09 19:05:46 +01:00
Andras Bacsai
32bc876dfc fix: php pgsql to 8.2 2024-01-09 19:04:12 +01:00
Andras Bacsai
c9b3d2a43d Update PHP version in Dockerfiles 2024-01-09 15:23:37 +01:00
Andras Bacsai
f343210e7c hmm 2024-01-09 15:22:06 +01:00
Andras Bacsai
4a42bff0dc Update version numbers to 4.0.0-beta.185 2024-01-09 15:18:33 +01:00
Andras Bacsai
36931b5b18 Merge pull request #1612 from coollabsio/next
v4.0.0-beta.184
2024-01-09 14:58:01 +01:00
Andras Bacsai
3b080abada remove rays 2024-01-09 14:48:26 +01:00
Andras Bacsai
7feba4bbaa fix: remove traefik debug in dev mode 2024-01-09 14:27:55 +01:00
Andras Bacsai
eef8c756df fix: settings menu 2024-01-09 14:27:42 +01:00
Andras Bacsai
9ed30cb0dc Remove unnecessary proxy configurations 2024-01-09 13:13:55 +01:00
Andras Bacsai
6bc43bd999 fix: sort and rename (unique part) of labels 2024-01-09 13:00:07 +01:00
Andras Bacsai
e7683ee9a5 fix: service labels without ports (unknown ports) 2024-01-09 12:49:03 +01:00
Andras Bacsai
ee71aeaa36 fix: use ip for sslip in dev if remote server is used 2024-01-09 12:48:46 +01:00
Andras Bacsai
404c664500 fix: traefik labels 2024-01-09 12:29:45 +01:00
Andras Bacsai
aa80392b46 small fix 2024-01-09 12:25:50 +01:00
Andras Bacsai
c9509ef658 fix: show framework based notification in build logs 2024-01-09 12:19:39 +01:00
Andras Bacsai
31e08a24c9 fix: healthy status 2024-01-09 08:42:37 +01:00
Andras Bacsai
b6745c691b fix: queue retry
fix: nixpacks builds
2024-01-08 16:33:34 +01:00
Andras Bacsai
b69584fe26 Merge branch 'main' into next 2024-01-07 16:25:13 +01:00
Andras Bacsai
4c3907c296 refactor routes 2024-01-07 16:23:41 +01:00
Andras Bacsai
bf44b4b949 version++ 2024-01-07 13:34:47 +01:00
Andras Bacsai
bee7a2357b Merge pull request #1608 from RayBB/patch-1
fix small typo
2024-01-07 13:34:11 +01:00
Andras Bacsai
98704fc3c2 Merge pull request #1605 from coollabsio/next
v4.0.0-beta.183
2024-01-06 11:39:51 +01:00
Andras Bacsai
e286e78ddc fix: database env variables 2024-01-06 11:38:12 +01:00
Raymond Berger
3c99f24b5a fix small typo 2024-01-05 15:10:30 +01:00
Andras Bacsai
963c519c38 feat: add www-non-www redirects to traefik 2024-01-04 14:14:40 +01:00
Andras Bacsai
d04513d817 Merge pull request #1603 from coollabsio/next
v4.0.0-beta.181
2024-01-04 13:08:18 +01:00
Andras Bacsai
64a7f27e37 fix: file storage save 2024-01-04 13:03:46 +01:00
Andras Bacsai
65652142b2 Merge pull request #1599 from coollabsio/next
v4.0.0-beta.181
2024-01-03 21:26:44 +01:00
Andras Bacsai
7691319c59 Refactor Dockerfile copying logic in ApplicationDeploymentJob.php 2024-01-03 21:15:58 +01:00
Andras Bacsai
206bd50d00 Update nixpacks build command in ApplicationDeploymentJob.php 2024-01-03 21:14:02 +01:00
Andras Bacsai
6159783a73 fix: nixpacks buildpack 2024-01-03 21:03:46 +01:00
Andras Bacsai
ed5f831c86 Merge pull request #1598 from coollabsio/next
v4.0.0-beta.180
2024-01-03 13:42:29 +01:00
Andras Bacsai
65be83e75d fix: only add restart policy if its empty (compose) 2024-01-03 13:37:14 +01:00
Andras Bacsai
25a471b045 Update Dockerfile with new versions of PACK and NIXPACKS 2024-01-03 13:29:23 +01:00
Andras Bacsai
60c7a29aa8 fix: nixpacks cache 2024-01-03 13:20:24 +01:00
Andras Bacsai
11ab6669a0 Merge pull request #1597 from coollabsio/next
v4.0.0-beta.179
2024-01-02 21:13:34 +01:00
Andras Bacsai
53965ab8ed fix: set deployment failed if new container is not healthy 2024-01-02 21:07:16 +01:00
Andras Bacsai
ea271ca079 fix: delete serviceApps & serviceDatabases
fix: cleanup trashed data
2024-01-02 21:03:51 +01:00
Andras Bacsai
f16d0f650f Merge pull request #1596 from coollabsio/next
v4.0.0-beta.178
2024-01-02 17:23:11 +01:00
Andras Bacsai
cb80341a78 Fix customLabels assignment when proxyType is TRAEFIK_V2 2024-01-02 17:22:44 +01:00
Andras Bacsai
83d96c8d11 Refactor custom labels handling in General.php and update Docker Compose Content label in general.blade.php 2024-01-02 17:14:52 +01:00
Andras Bacsai
a8ca57d095 Update link in helper message in general.blade.php 2024-01-02 16:47:05 +01:00
Andras Bacsai
2d936a4b22 add compose link 2024-01-02 16:46:08 +01:00
Andras Bacsai
0653eb8511 set custom labels on every app 2024-01-02 16:44:41 +01:00
Andras Bacsai
cc64132627 Update README.md 2024-01-02 14:06:08 +01:00
Andras Bacsai
e0391e5abd Merge pull request #1594 from coollabsio/next
v4.0.0-beta.177
2024-01-02 13:58:30 +01:00
Andras Bacsai
025135bd2a feat: raw docker compose deployments 2024-01-02 13:55:35 +01:00
Andras Bacsai
5e5873a08d fix: duplicate compose variable 2024-01-02 11:46:02 +01:00
Andras Bacsai
14652c2878 Update sponsor logos and links 2024-01-02 07:31:54 +01:00
Andras Bacsai
a7ef5c456d Merge pull request #1592 from coollabsio/next
v4.0.0-beta.176
2023-12-31 10:52:15 +01:00
Andras Bacsai
117f1d4155 fix: horizon 2023-12-30 15:30:57 +01:00
Andras Bacsai
075f3ce930 remove unnecessary queue 2023-12-30 15:29:26 +01:00
Andras Bacsai
d5b3e88fc4 Merge pull request #1576 from coollabsio/next
v4.0.0-beta.175
2023-12-30 15:00:05 +01:00
Andras Bacsai
ba55e0c1bb feat: add environment description + able to change name 2023-12-30 14:47:26 +01:00
Andras Bacsai
54671354f0 fix: deploy key + docker compose 2023-12-30 14:20:02 +01:00
Andras Bacsai
a2c7e8d455 Merge pull request #1566 from stooit/fix/deploy-key-docker-compose
fix: Resolves deployment of docker compose applications when using a deploy key
2023-12-30 13:27:25 +01:00
Andras Bacsai
32dbdf5204 Fix redirect route in DecideWhatToDoWithUser middleware 2023-12-28 22:26:21 +01:00
Andras Bacsai
019887739c fix: wrong env variable parsing 2023-12-28 17:53:47 +01:00
Andras Bacsai
5596e41f2b fix: sub 2023-12-28 13:43:03 +01:00
Andras Bacsai
6a73a00a1f Merge pull request #1575 from coollabsio/next
v4.0.0-beta.174
2023-12-27 23:09:14 +01:00
Andras Bacsai
4121c9dd8b fix 2023-12-27 23:07:39 +01:00
Andras Bacsai
e4781dc129 fix: restore falsely deleted coolify-db-backup 2023-12-27 23:06:22 +01:00
Andras Bacsai
1c90f46f2a Merge pull request #1572 from coollabsio/next
v4.0.0-beta.173
2023-12-27 17:44:07 +01:00
Andras Bacsai
e78d851c85 fix: button title 2023-12-27 17:14:18 +01:00
Andras Bacsai
52d05005ed fix: deploy instead of restart in case swarm is used 2023-12-27 17:12:09 +01:00
Andras Bacsai
f03aa57758 fix: routing, switch back to old one 2023-12-27 16:45:01 +01:00
Andras Bacsai
8c20c833ba fix: add source commit to final envs 2023-12-27 13:06:59 +01:00
Andras Bacsai
2fe6766b7f fix: cpu limit to float from int 2023-12-27 13:01:57 +01:00
Stuart Rowlands
d9599da4a8 Fix git clone command for deploy key + docker compose. 2023-12-21 11:16:03 -08:00
Andras Bacsai
3f453ba7c0 Merge branch 'main' into next 2023-12-21 14:09:13 +01:00
Andras Bacsai
bd02c3055a update readme 2023-12-21 14:08:39 +01:00
andrasbacsai
7ea7d85d15 Deploying to main from @ coollabsio/coolify@d38350c282 🚀 2023-12-21 12:58:10 +00:00
Andras Bacsai
d38350c282 update 2023-12-21 13:57:52 +01:00
andrasbacsai
a83c70004c Deploying to main from @ coollabsio/coolify@49e1404a2c 🚀 2023-12-21 12:56:00 +00:00
Andras Bacsai
49e1404a2c test gh auto sponsor update 2023-12-21 13:55:40 +01:00
Andras Bacsai
76f23e7dbf do not check server ready on server status job 2023-12-21 10:43:39 +01:00
Andras Bacsai
ad8653f54d Merge pull request #1564 from coollabsio/next
v4.0.0-beta.172
2023-12-21 10:35:40 +01:00
Andras Bacsai
8939d77051 fix 2023-12-21 10:28:02 +01:00
Andras Bacsai
37be4a1796 fix 2023-12-21 10:00:41 +01:00
Andras Bacsai
e4c923e358 fix 2023-12-21 09:57:39 +01:00
Andras Bacsai
62ca3ffaa5 fix 2023-12-21 09:55:16 +01:00
Andras Bacsai
9af3ce4be5 fail job instead of runtime exception 2023-12-21 09:49:18 +01:00
203 changed files with 3256 additions and 1963 deletions

View File

@@ -17,6 +17,40 @@ https://coolify.io/sponsorships
Thank you so much! Thank you so much!
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
## Github Sponsors ($15+)
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
<a href="https://github.com/whitesidest"><img src="https://avatars.githubusercontent.com/u/12365916?s=52&v=4" width="60px" alt="Tyler Whitesides" /></a>
<a href="https://github.com/aniftyco"><img src="https://github.com/aniftyco.png" width="60px" alt="NiftyCo" /></a>
<a href="https://github.com/iujlaki"><img src="https://github.com/iujlaki.png" width="60px" alt="Imre Ujlaki" /></a>
<a href="https://github.com/Illyism"><img src="https://github.com/Illyism.png" width="60px" alt="Ilias Ism" /></a>
<a href="https://github.com/urtho"><img src="https://github.com/urtho.png" width="60px" alt="Paweł Pierścionek" /></a>
<a href="https://github.com/monocursive"><img src="https://github.com/monocursive.png" width="60px" alt="Michael Mazurczak" /></a>
## Organizations
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/1/website"><img src="https://opencollective.com/coollabsio/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/2/website"><img src="https://opencollective.com/coollabsio/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/3/website"><img src="https://opencollective.com/coollabsio/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/4/website"><img src="https://opencollective.com/coollabsio/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/5/website"><img src="https://opencollective.com/coollabsio/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
## Individuals
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
# Cloud # Cloud
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
@@ -59,34 +93,6 @@ Contact us [here](https://coolify.io/docs/contact).
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
# 💰 Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/coollabsio/contribute)]
## Organizations
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="appwrite logo" width="200"/></a>
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
Support this project with your organization. Your logo will show up here with a link to your website.
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/1/website"><img src="https://opencollective.com/coollabsio/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/2/website"><img src="https://opencollective.com/coollabsio/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/3/website"><img src="https://opencollective.com/coollabsio/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/4/website"><img src="https://opencollective.com/coollabsio/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/5/website"><img src="https://opencollective.com/coollabsio/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/6/website"><img src="https://opencollective.com/coollabsio/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/7/website"><img src="https://opencollective.com/coollabsio/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/8/website"><img src="https://opencollective.com/coollabsio/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/coollabsio/organization/9/website"><img src="https://opencollective.com/coollabsio/organization/9/avatar.svg"></a>
## Individuals
<a href="https://opencollective.com/coollabsio"><img src="https://opencollective.com/coollabsio/individuals.svg?width=890"></a>
# Star History # Star History
[![Star History Chart](https://api.star-history.com/svg?repos=coollabsio/coolify&type=Date)](https://star-history.com/#coollabsio/coolify&Date) [![Star History Chart](https://api.star-history.com/svg?repos=coollabsio/coolify&type=Date)](https://star-history.com/#coollabsio/coolify&Date)

View File

@@ -56,7 +56,7 @@ class StartMariadb
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness, 'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (int) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset, 'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ]

View File

@@ -63,7 +63,7 @@ class StartMongodb
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness, 'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (int) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset, 'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ]

View File

@@ -56,7 +56,7 @@ class StartMysql
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness, 'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (int) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset, 'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ]

View File

@@ -66,7 +66,7 @@ class StartPostgresql
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness, 'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (int) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset, 'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ]

View File

@@ -65,7 +65,7 @@ class StartRedis
'memswap_limit' => $this->database->limits_memory_swap, 'memswap_limit' => $this->database->limits_memory_swap,
'mem_swappiness' => $this->database->limits_memory_swappiness, 'mem_swappiness' => $this->database->limits_memory_swappiness,
'mem_reservation' => $this->database->limits_memory_reservation, 'mem_reservation' => $this->database->limits_memory_reservation,
'cpus' => (int) $this->database->limits_cpus, 'cpus' => (float) $this->database->limits_cpus,
'cpuset' => $this->database->limits_cpuset, 'cpuset' => $this->database->limits_cpuset,
'cpu_shares' => $this->database->limits_cpu_shares, 'cpu_shares' => $this->database->limits_cpu_shares,
] ]

View File

@@ -21,14 +21,14 @@ class DeleteService
foreach ($storages as $storage) { foreach ($storages as $storage) {
$storagesToDelete->push($storage); $storagesToDelete->push($storage);
} }
$application->delete(); $application->forceDelete();
} }
foreach ($service->databases()->get() as $database) { foreach ($service->databases()->get() as $database) {
$storages = $database->persistentStorages()->get(); $storages = $database->persistentStorages()->get();
foreach ($storages as $storage) { foreach ($storages as $storage) {
$storagesToDelete->push($storage); $storagesToDelete->push($storage);
} }
$database->delete(); $database->forceDelete();
} }
foreach ($storagesToDelete as $storage) { foreach ($storagesToDelete as $storage) {
$commands[] = "docker volume rm -f $storage->name"; $commands[] = "docker volume rm -f $storage->name";

View File

@@ -7,6 +7,7 @@ use App\Jobs\CleanupHelperContainersJob;
use App\Models\Application; use App\Models\Application;
use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationDeploymentQueue;
use App\Models\InstanceSettings; use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use App\Models\Server; use App\Models\Server;
use App\Models\Service; use App\Models\Service;
use App\Models\ServiceApplication; use App\Models\ServiceApplication;
@@ -31,6 +32,9 @@ class Init extends Command
if ($cleanup) { if ($cleanup) {
echo "Running cleanup\n"; echo "Running cleanup\n";
$this->cleanup_stucked_resources(); $this->cleanup_stucked_resources();
// Required for falsely deleted coolify db
$this->restore_coolify_db_backup();
// $this->cleanup_ssh(); // $this->cleanup_ssh();
} }
$this->cleanup_in_progress_application_deployments(); $this->cleanup_in_progress_application_deployments();
@@ -51,6 +55,30 @@ class Init extends Command
} }
} }
} }
private function restore_coolify_db_backup()
{
try {
$database = StandalonePostgresql::withTrashed()->find(0);
if ($database && $database->trashed()) {
echo "Restoring coolify db backup\n";
$database->restore();
$scheduledBackup = ScheduledDatabaseBackup::find(0);
if (!$scheduledBackup) {
ScheduledDatabaseBackup::create([
'id' => 0,
'enabled' => true,
'save_s3' => false,
'frequency' => '0 0 * * *',
'database_id' => $database->id,
'database_type' => 'App\Models\StandalonePostgresql',
'team_id' => 0,
]);
}
}
} catch (\Throwable $e) {
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
}
}
private function cleanup_stucked_helper_containers() private function cleanup_stucked_helper_containers()
{ {
$servers = Server::all(); $servers = Server::all();
@@ -71,7 +99,7 @@ class Init extends Command
return; return;
} }
try { try {
Http::get("https://get.coollabs.io/coolify/v4/alive?appId=$id&version=$version"); Http::get("https://undead.coollabs.io/coolify/v4/alive?appId=$id&version=$version");
echo "I am alive!\n"; echo "I am alive!\n";
} catch (\Throwable $e) { } catch (\Throwable $e) {
echo "Error in alive: {$e->getMessage()}\n"; echo "Error in alive: {$e->getMessage()}\n";
@@ -110,6 +138,89 @@ class Init extends Command
} }
private function cleanup_stucked_resources() private function cleanup_stucked_resources()
{ {
try {
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($applications as $application) {
echo "Deleting stucked application: {$application->name}\n";
$application->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked application: {$e->getMessage()}\n";
}
try {
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($postgresqls as $postgresql) {
echo "Deleting stucked postgresql: {$postgresql->name}\n";
$postgresql->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked postgresql: {$e->getMessage()}\n";
}
try {
$redis = StandaloneRedis::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($redis as $redis) {
echo "Deleting stucked redis: {$redis->name}\n";
$redis->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked redis: {$e->getMessage()}\n";
}
try {
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($mongodbs as $mongodb) {
echo "Deleting stucked mongodb: {$mongodb->name}\n";
$mongodb->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked mongodb: {$e->getMessage()}\n";
}
try {
$mysqls = StandaloneMysql::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($mysqls as $mysql) {
echo "Deleting stucked mysql: {$mysql->name}\n";
$mysql->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked mysql: {$e->getMessage()}\n";
}
try {
$mariadbs = StandaloneMariadb::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($mariadbs as $mariadb) {
echo "Deleting stucked mariadb: {$mariadb->name}\n";
$mariadb->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked mariadb: {$e->getMessage()}\n";
}
try {
$services = Service::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($services as $service) {
echo "Deleting stucked service: {$service->name}\n";
$service->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked service: {$e->getMessage()}\n";
}
try {
$serviceApps = ServiceApplication::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($serviceApps as $serviceApp) {
echo "Deleting stucked serviceapp: {$serviceApp->name}\n";
$serviceApp->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked serviceapp: {$e->getMessage()}\n";
}
try {
$serviceDbs = ServiceDatabase::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($serviceDbs as $serviceDb) {
echo "Deleting stucked serviceapp: {$serviceDb->name}\n";
$serviceDb->forceDelete();
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked serviceapp: {$e->getMessage()}\n";
}
// Cleanup any resources that are not attached to any environment or destination or server // Cleanup any resources that are not attached to any environment or destination or server
try { try {
$applications = Application::all(); $applications = Application::all();
@@ -134,7 +245,7 @@ class Init extends Command
echo "Error in application: {$e->getMessage()}\n"; echo "Error in application: {$e->getMessage()}\n";
} }
try { try {
$postgresqls = StandalonePostgresql::all(); $postgresqls = StandalonePostgresql::all()->where('id', '!=', 0);
foreach ($postgresqls as $postgresql) { foreach ($postgresqls as $postgresql) {
if (!data_get($postgresql, 'environment')) { if (!data_get($postgresql, 'environment')) {
echo 'Postgresql without environment: ' . $postgresql->name . ' soft deleting\n'; echo 'Postgresql without environment: ' . $postgresql->name . ' soft deleting\n';

View File

@@ -47,7 +47,7 @@ class Handler extends ExceptionHandler
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) { if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
return response()->json(['message' => $exception->getMessage()], 401); return response()->json(['message' => $exception->getMessage()], 401);
} }
return redirect()->guest($exception->redirectTo() ?? route('login')); return redirect()->guest($exception->redirectTo() ?? route('login'));
} }
/** /**
* Register the exception handling callbacks for the application. * Register the exception handling callbacks for the application.

View File

@@ -2,11 +2,13 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Events\TestEvent;
use App\Models\InstanceSettings; use App\Models\InstanceSettings;
use App\Models\S3Storage; use App\Models\S3Storage;
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use App\Models\TeamInvitation; use App\Models\TeamInvitation;
use App\Models\User; use App\Models\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController; use Illuminate\Routing\Controller as BaseController;
@@ -14,11 +16,55 @@ use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\Contracts\FailedPasswordResetLinkRequestResponse;
use Laravel\Fortify\Contracts\SuccessfulPasswordResetLinkRequestResponse;
use Illuminate\Support\Facades\Password;
class Controller extends BaseController class Controller extends BaseController
{ {
use AuthorizesRequests, ValidatesRequests; use AuthorizesRequests, ValidatesRequests;
public function realtime_test() {
if (auth()->user()?->currentTeam()->id !== 0) {
return redirect(RouteServiceProvider::HOME);
}
TestEvent::dispatch();
return 'Look at your other tab.';
}
public function verify() {
return view('auth.verify-email');
}
public function email_verify() {
request()->fulfill();
$name = request()->user()?->name;
send_internal_notification("User {$name} verified their email address.");
return redirect(RouteServiceProvider::HOME);
}
public function forgot_password() {
if (is_transactional_emails_active()) {
$arrayOfRequest = request()->only(Fortify::email());
request()->merge([
'email' => Str::lower($arrayOfRequest['email']),
]);
$type = set_transanctional_email_settings();
if (!$type) {
return response()->json(['message' => 'Transactional emails are not active'], 400);
}
request()->validate([Fortify::email() => 'required|email']);
$status = Password::broker(config('fortify.passwords'))->sendResetLink(
request()->only(Fortify::email())
);
if ($status == Password::RESET_LINK_SENT) {
return app(SuccessfulPasswordResetLinkRequestResponse::class, ['status' => $status]);
}
if ($status == Password::RESET_THROTTLED) {
return response('Already requested a password reset in the past minutes.', 400);
}
return app(FailedPasswordResetLinkRequestResponse::class, ['status' => $status]);
}
return response()->json(['message' => 'Transactional emails are not active'], 400);
}
public function link() public function link()
{ {
$token = request()->get('token'); $token = request()->get('token');
@@ -51,90 +97,7 @@ class Controller extends BaseController
return redirect()->route('login')->with('error', 'Invalid credentials.'); return redirect()->route('login')->with('error', 'Invalid credentials.');
} }
public function license() public function accept_invitation()
{
if (!isCloud()) {
abort(404);
}
return view('settings.license', [
'settings' => InstanceSettings::get(),
]);
}
public function force_passoword_reset()
{
return view('auth.force-password-reset');
}
public function boarding()
{
if (currentTeam()->boarding || isDev()) {
return view('boarding');
} else {
return redirect()->route('dashboard');
}
}
public function settings()
{
if (isInstanceAdmin()) {
$settings = InstanceSettings::get();
$database = StandalonePostgresql::whereName('coolify-db')->first();
if ($database) {
if ($database->status !== 'running') {
$database->status = 'running';
$database->save();
}
$s3s = S3Storage::whereTeamId(0)->get();
}
return view('settings.configuration', [
'settings' => $settings,
'database' => $database,
's3s' => $s3s ?? [],
]);
} else {
return redirect()->route('dashboard');
}
}
public function team()
{
$invitations = [];
if (auth()->user()->isAdminFromSession()) {
$invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
}
return view('team.index', [
'invitations' => $invitations,
]);
}
public function storages()
{
$s3 = S3Storage::ownedByCurrentTeam()->get();
return view('team.storages.all', [
's3' => $s3,
]);
}
public function storages_show()
{
$storage = S3Storage::ownedByCurrentTeam()->whereUuid(request()->storage_uuid)->firstOrFail();
return view('team.storages.show', [
'storage' => $storage,
]);
}
public function members()
{
$invitations = [];
if (auth()->user()->isAdminFromSession()) {
$invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
}
return view('team.members', [
'invitations' => $invitations,
]);
}
public function acceptInvitation()
{ {
try { try {
$resetPassword = request()->query('reset-password'); $resetPassword = request()->query('reset-password');
@@ -169,7 +132,7 @@ class Controller extends BaseController
} }
} }
public function revokeInvitation() public function revoke_invitation()
{ {
try { try {
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail(); $invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();

View File

@@ -1,84 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
class DatabaseController extends Controller
{
use AuthorizesRequests, ValidatesRequests;
public function configuration()
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
return view('project.database.configuration', ['database' => $database]);
}
public function executions()
{
$backup_uuid = request()->route('backup_uuid');
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
$backup = $database->scheduledBackups->where('uuid', $backup_uuid)->first();
if (!$backup) {
return redirect()->route('dashboard');
}
$executions = collect($backup->executions)->sortByDesc('created_at');
return view('project.database.backups.executions', [
'database' => $database,
'backup' => $backup,
'executions' => $executions,
's3s' => currentTeam()->s3s,
]);
}
public function backups()
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
// No backups for redis
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
return redirect()->route('project.database.configuration', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
'database_uuid' => $database->uuid,
]);
}
return view('project.database.backups.all', [
'database' => $database,
's3s' => currentTeam()->s3s,
]);
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure; use Closure;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@@ -16,7 +17,7 @@ class DecideWhatToDoWithUser
} }
if (!auth()->user() || !isCloud() || isInstanceAdmin()) { if (!auth()->user() || !isCloud() || isInstanceAdmin()) {
if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) { if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
return redirect('boarding'); return redirect()->route('boarding');
} }
return $next($request); return $next($request);
} }
@@ -24,27 +25,27 @@ class DecideWhatToDoWithUser
if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) { if ($request->path() === 'verify' || in_array($request->path(), allowedPathsForInvalidAccounts()) || $request->routeIs('verify.verify')) {
return $next($request); return $next($request);
} }
return redirect('/verify'); return redirect()->route('verify.email');
} }
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) { if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) { if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
if (Str::startsWith($request->path(), 'invitations')) { if (Str::startsWith($request->path(), 'invitations')) {
return $next($request); return $next($request);
} }
return redirect('subscription'); return redirect()->route('subscription.index');
} }
} }
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) { if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
if (Str::startsWith($request->path(), 'invitations')) { if (Str::startsWith($request->path(), 'invitations')) {
return $next($request); return $next($request);
} }
return redirect('boarding'); return redirect()->route('boarding');
} }
if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') { if (auth()->user()->hasVerifiedEmail() && $request->path() === 'verify') {
return redirect('/'); return redirect(RouteServiceProvider::HOME);
} }
if (isSubscriptionActive() && $request->path() === 'subscription') { if (isSubscriptionActive() && $request->path() === 'subscription') {
return redirect('/'); return redirect(RouteServiceProvider::HOME);
} }
return $next($request); return $next($request);
} }

View File

@@ -30,6 +30,7 @@ use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Throwable; use Throwable;
use Visus\Cuid2\Cuid2; use Visus\Cuid2\Cuid2;
use Yosymfony\Toml\Toml;
class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
{ {
@@ -73,6 +74,8 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
private $env_args; private $env_args;
private $docker_compose; private $docker_compose;
private $docker_compose_base64; private $docker_compose_base64;
private ?string $nixpacks_plan = null;
private ?string $nixpacks_type = null;
private string $dockerfile_location = '/Dockerfile'; private string $dockerfile_location = '/Dockerfile';
private string $docker_compose_location = '/docker-compose.yml'; private string $docker_compose_location = '/docker-compose.yml';
private ?string $docker_compose_custom_start_command = null; private ?string $docker_compose_custom_start_command = null;
@@ -164,11 +167,15 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$ips = collect([]); $ips = collect([]);
if (count($allContainers) > 0) { if (count($allContainers) > 0) {
$allContainers = $allContainers[0]; $allContainers = $allContainers[0];
$allContainers = collect($allContainers)->sort()->values();
foreach ($allContainers as $container) { foreach ($allContainers as $container) {
$containerName = data_get($container, 'Name'); $containerName = data_get($container, 'Name');
if ($containerName === 'coolify-proxy') { if ($containerName === 'coolify-proxy') {
continue; continue;
} }
if (preg_match('/-(\d{12})/', $containerName)) {
continue;
}
$containerIp = data_get($container, 'IPv4Address'); $containerIp = data_get($container, 'IPv4Address');
if ($containerName && $containerIp) { if ($containerName && $containerIp) {
$containerIp = str($containerIp)->before('/'); $containerIp = str($containerIp)->before('/');
@@ -343,6 +350,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->generate_image_names(); $this->generate_image_names();
$this->check_image_locally_or_remotely(); $this->check_image_locally_or_remotely();
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) { if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
$this->execute_remote_command([
"echo 'Image found ({$this->production_image_name}) with the same Git Commit SHA. Restarting container.'",
]);
$this->create_workdir(); $this->create_workdir();
$this->generate_compose_file(); $this->generate_compose_file();
$this->rolling_update(); $this->rolling_update();
@@ -446,8 +456,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->generate_image_names(); $this->generate_image_names();
$this->cleanup_git(); $this->cleanup_git();
$this->application->loadComposeFile(isInit: false); $this->application->loadComposeFile(isInit: false);
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id); if ($this->application->settings->is_raw_compose_deployment_enabled) {
$yaml = Yaml::dump($composeFile->toArray(), 10); $yaml = $composeFile = $this->application->docker_compose_raw;
} else {
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
$yaml = Yaml::dump($composeFile->toArray(), 10);
}
$this->docker_compose_base64 = base64_encode($yaml); $this->docker_compose_base64 = base64_encode($yaml);
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
@@ -571,7 +585,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->generate_nixpacks_confs(); $this->generate_nixpacks_confs();
$this->generate_compose_file(); $this->generate_compose_file();
$this->generate_build_env_variables(); $this->generate_build_env_variables();
$this->add_build_env_variables_to_dockerfile(); // $this->add_build_env_variables_to_dockerfile();
$this->build_image(); $this->build_image();
$this->rolling_update(); $this->rolling_update();
} }
@@ -593,6 +607,24 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->rolling_update(); $this->rolling_update();
} }
private function framework_based_notification()
{
// Laravel old env variables
if ($this->pull_request_id === 0) {
$nixpacks_php_fallback_path = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
$nixpacks_php_root_dir = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
} else {
$nixpacks_php_fallback_path = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
}
if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) {
$this->execute_remote_command(
[
"echo 'There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/frameworks/laravel#requirements'", 'type' => 'err'
],
);
}
}
private function rolling_update() private function rolling_update()
{ {
if ($this->server->isSwarm()) { if ($this->server->isSwarm()) {
@@ -629,6 +661,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->application_deployment_queue->addLogEntry("Rolling update completed."); $this->application_deployment_queue->addLogEntry("Rolling update completed.");
} }
} }
$this->framework_based_notification();
} }
private function health_check() private function health_check()
{ {
@@ -668,7 +701,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
"echo 'Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}'" "echo 'Attempt {$counter} of {$this->application->health_check_retries} | Healthcheck status: {$this->saved_outputs->get('health_check')}'"
], ],
); );
if (Str::of($this->saved_outputs->get('health_check'))->contains('healthy')) { if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'healthy') {
$this->newVersionIsHealthy = true; $this->newVersionIsHealthy = true;
$this->application->update(['status' => 'running']); $this->application->update(['status' => 'running']);
$this->execute_remote_command( $this->execute_remote_command(
@@ -678,6 +711,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
); );
break; break;
} }
if (Str::of($this->saved_outputs->get('health_check'))->replace('"', '')->value() === 'unhealthy') {
$this->newVersionIsHealthy = false;
break;
}
$counter++; $counter++;
sleep($this->application->health_check_interval); sleep($this->application->health_check_interval);
} }
@@ -862,20 +899,28 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
[ [
"echo -n 'Generating nixpacks configuration with: $nixpacks_command'", "echo -n 'Generating nixpacks configuration with: $nixpacks_command'",
], ],
[executeInDocker($this->deployment_uuid, $nixpacks_command)], [executeInDocker($this->deployment_uuid, $nixpacks_command), "save" => "nixpacks_plan", "hidden" => true],
[executeInDocker($this->deployment_uuid, "cp {$this->workdir}/.nixpacks/Dockerfile {$this->workdir}/Dockerfile")], [executeInDocker($this->deployment_uuid, "nixpacks detect {$this->workdir}"), "save" => "nixpacks_type", "hidden" => true],
[executeInDocker($this->deployment_uuid, "rm -f {$this->workdir}/.nixpacks/Dockerfile")]
); );
if ($this->saved_outputs->get('nixpacks_type')) {
$this->nixpacks_type = $this->saved_outputs->get('nixpacks_type');
}
if ($this->saved_outputs->get('nixpacks_plan')) {
$this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan');
if ($this->nixpacks_plan) {
$parsed = Toml::Parse($this->nixpacks_plan);
// Do any modifications here
$cmds = collect(data_get($parsed, 'phases.setup.cmds', []));
data_set($parsed, 'phases.setup.cmds', $cmds);
$this->nixpacks_plan = json_encode($parsed);
}
}
} }
private function nixpacks_build_cmd() private function nixpacks_build_cmd()
{ {
$this->generate_env_variables(); $this->generate_env_variables();
$cacheKey = $this->application->uuid; $nixpacks_command = "nixpacks plan -f toml {$this->env_args}";
if ($this->pull_request_id !== 0) {
$cacheKey = "{$this->application->uuid}-pr-{$this->pull_request_id}";
}
$nixpacks_command = "nixpacks build --cache-key '{$cacheKey}' -o {$this->workdir} {$this->env_args} --no-error-without-start";
if ($this->application->build_command) { if ($this->application->build_command) {
$nixpacks_command .= " --build-cmd \"{$this->application->build_command}\""; $nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
} }
@@ -896,10 +941,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
foreach ($this->application->nixpacks_environment_variables as $env) { foreach ($this->application->nixpacks_environment_variables as $env) {
$this->env_args->push("--env {$env->key}={$env->value}"); $this->env_args->push("--env {$env->key}={$env->value}");
} }
foreach ($this->application->build_environment_variables as $env) {
$this->env_args->push("--env {$env->key}={$env->value}");
}
} else { } else {
foreach ($this->application->nixpacks_environment_variables_preview as $env) { foreach ($this->application->nixpacks_environment_variables_preview as $env) {
$this->env_args->push("--env {$env->key}={$env->value}"); $this->env_args->push("--env {$env->key}={$env->value}");
} }
foreach ($this->application->build_environment_variables_preview as $env) {
$this->env_args->push("--env {$env->key}={$env->value}");
}
} }
$this->env_args = $this->env_args->implode(' '); $this->env_args = $this->env_args->implode(' ');
@@ -954,7 +1005,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
'memswap_limit' => $this->application->limits_memory_swap, 'memswap_limit' => $this->application->limits_memory_swap,
'mem_swappiness' => $this->application->limits_memory_swappiness, 'mem_swappiness' => $this->application->limits_memory_swappiness,
'mem_reservation' => $this->application->limits_memory_reservation, 'mem_reservation' => $this->application->limits_memory_reservation,
'cpus' => (int) $this->application->limits_cpus, 'cpus' => (float) $this->application->limits_cpus,
'cpuset' => $this->application->limits_cpuset, 'cpuset' => $this->application->limits_cpuset,
'cpu_shares' => $this->application->limits_cpu_shares, 'cpu_shares' => $this->application->limits_cpu_shares,
] ]
@@ -1130,6 +1181,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PORT'))->isEmpty()) { if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PORT'))->isEmpty()) {
$environment_variables->push("PORT={$ports[0]}"); $environment_variables->push("PORT={$ports[0]}");
} }
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('SOURCE_COMMIT'))->isEmpty()) {
if (!is_null($this->commit)) {
$environment_variables->push("SOURCE_COMMIT={$this->commit}");
}
}
return $environment_variables->all(); return $environment_variables->all();
} }
@@ -1188,6 +1244,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ($this->application->settings->is_static || $this->application->build_pack === 'static') { if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
if ($this->application->static_image) { if ($this->application->static_image) {
$this->pull_latest_image($this->application->static_image); $this->pull_latest_image($this->application->static_image);
$this->execute_remote_command(
["echo -n 'Continue with the building process.'"],
);
} }
if ($this->application->build_pack === 'static') { if ($this->application->build_pack === 'static') {
$dockerfile = base64_encode("FROM {$this->application->static_image} $dockerfile = base64_encode("FROM {$this->application->static_image}
@@ -1214,9 +1273,29 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
} }
}"); }");
} else { } else {
$this->execute_remote_command([ if ($this->application->build_pack === 'nixpacks') {
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true $this->nixpacks_plan = base64_encode($this->nixpacks_plan);
]); $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d > {$this->workdir}/thegameplan.json"), "hidden" => true]);
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), "hidden" => true
]);
}
} else {
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --no-cache {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build {$this->buildTarget} --network {$this->destination->network} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
]);
}
}
$dockerfile = base64_encode("FROM {$this->application->static_image} $dockerfile = base64_encode("FROM {$this->application->static_image}
WORKDIR /usr/share/nginx/html/ WORKDIR /usr/share/nginx/html/
@@ -1249,19 +1328,39 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf") executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
], ],
[ [
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true executeInDocker($this->deployment_uuid, "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"), "hidden" => true
] ]
); );
} else { } else {
// Pure Dockerfile based deployment // Pure Dockerfile based deployment
if ($this->application->dockerfile) { if ($this->application->dockerfile) {
$this->execute_remote_command([ $this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --pull $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true executeInDocker($this->deployment_uuid, "docker build --pull {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"), "hidden" => true
]); ]);
} else { } else {
$this->execute_remote_command([ if ($this->application->build_pack === 'nixpacks') {
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true $this->nixpacks_plan = base64_encode($this->nixpacks_plan);
]); $this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d > {$this->workdir}/thegameplan.json"), "hidden" => true]);
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), "hidden" => true
]);
}
} else {
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"), "hidden" => true
]);
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}"), "hidden" => true
]);
}
}
} }
} }
$this->execute_remote_command([ $this->execute_remote_command([
@@ -1287,6 +1386,9 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
}); });
} else { } else {
$this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container."); $this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
$this->application_deployment_queue->update([
'status' => ApplicationDeploymentStatus::FAILED->value,
]);
$this->execute_remote_command( $this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true], [executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
); );

View File

@@ -45,7 +45,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
public function handle() public function handle()
{ {
if (!$this->server->isServerReady($this->tries)) { if (!$this->server->isServerReady($this->tries)) {
throw new \RuntimeException('Server is not reachable.'); return 'Server is not reachable.';
}; };
try { try {
if ($this->server->isSwarm()) { if ($this->server->isSwarm()) {
@@ -162,6 +162,10 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
// Notify user that this container should not be there. // Notify user that this container should not be there.
} }
} }
if (data_get($container,'Name') === '/coolify-db') {
$foundDatabases[] = 0;
}
} }
$serviceLabelId = data_get($labels, 'coolify.serviceId'); $serviceLabelId = data_get($labels, 'coolify.serviceId');
if ($serviceLabelId) { if ($serviceLabelId) {
@@ -212,7 +216,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
} }
$name = data_get($exitedService, 'name'); $name = data_get($exitedService, 'name');
$fqdn = data_get($exitedService, 'fqdn'); $fqdn = data_get($exitedService, 'fqdn');
$containerName = $name ? "$name ($fqdn)" : $fqdn; $containerName = $name ? "$name, available at $fqdn" : $fqdn;
$projectUuid = data_get($service, 'environment.project.uuid'); $projectUuid = data_get($service, 'environment.project.uuid');
$serviceUuid = data_get($service, 'uuid'); $serviceUuid = data_get($service, 'uuid');
$environmentName = data_get($service, 'environment.name'); $environmentName = data_get($service, 'environment.name');

View File

@@ -37,10 +37,6 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
public function handle() public function handle()
{ {
ray("checking server status for {$this->server->id}");
if (!$this->server->isServerReady(4)) {
throw new \RuntimeException('Server is not reachable.');
};
try { try {
if ($this->server->isFunctional()) { if ($this->server->isFunctional()) {
$this->cleanup(notify: false); $this->cleanup(notify: false);

View File

@@ -274,7 +274,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
{ {
$this->skipBoarding(); $this->skipBoarding();
return redirect()->route( return redirect()->route(
'project.resources.new', 'project.resource.create',
[ [
'project_uuid' => $this->createdProject->uuid, 'project_uuid' => $this->createdProject->uuid,
'environment_name' => 'production', 'environment_name' => 'production',

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Livewire\CommandCenter;
use App\Models\Server;
use Livewire\Component;
class Index extends Component
{
public $servers = [];
public function mount() {
$this->servers = Server::isReachable()->get();
}
public function render()
{
return view('livewire.command-center.index');
}
}

View File

@@ -36,7 +36,7 @@ class Form extends Component
instant_remote_process(['docker network rm -f ' . $this->destination->network], $this->destination->server); instant_remote_process(['docker network rm -f ' . $this->destination->network], $this->destination->server);
} }
$this->destination->delete(); $this->destination->delete();
return $this->redirectRoute('dashboard', navigate: true); return redirect()->route('dashboard');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -86,7 +86,7 @@ class Docker extends Component
} }
} }
$this->createNetworkAndAttachToProxy(); $this->createNetworkAndAttachToProxy();
return $this->redirectRoute('destination.show', $docker->uuid, navigate: true); return redirect()->route('destination.show', $docker->uuid);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -22,6 +22,10 @@ class ForcePasswordReset extends Component
{ {
$this->email = auth()->user()->email; $this->email = auth()->user()->email;
} }
public function render()
{
return view('livewire.force-password-reset')->layout('layouts.simple');
}
public function submit() public function submit()
{ {
try { try {
@@ -35,7 +39,7 @@ class ForcePasswordReset extends Component
if ($firstLogin) { if ($firstLogin) {
send_internal_notification('First login for ' . auth()->user()->email); send_internal_notification('First login for ' . auth()->user()->email);
} }
return $this->redirectRoute('dashboard', navigate: true); return redirect()->route('dashboard');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -5,21 +5,19 @@ namespace App\Livewire\Profile;
use Livewire\Attributes\Validate; use Livewire\Attributes\Validate;
use Livewire\Component; use Livewire\Component;
class Form extends Component class Index extends Component
{ {
public int $userId; public int $userId;
public string $email; public string $email;
#[Validate('required')] #[Validate('required')]
public string $name; public string $name;
public function mount() public function mount()
{ {
$this->userId = auth()->user()->id; $this->userId = auth()->user()->id;
$this->name = auth()->user()->name; $this->name = auth()->user()->name;
$this->email = auth()->user()->email; $this->email = auth()->user()->email;
} }
public function submit() public function submit()
{ {
@@ -34,4 +32,8 @@ class Form extends Component
return handleError($e, $this); return handleError($e, $this);
} }
} }
public function render()
{
return view('livewire.profile.index');
}
} }

View File

@@ -27,7 +27,7 @@ class AddEmpty extends Component
'description' => $this->description, 'description' => $this->description,
'team_id' => currentTeam()->id, 'team_id' => currentTeam()->id,
]); ]);
return $this->redirectRoute('project.show', $project->uuid, navigate: true); return redirect()->route('project.show', $project->uuid);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} finally { } finally {

View File

@@ -27,10 +27,10 @@ class AddEnvironment extends Component
'project_id' => $this->project->id, 'project_id' => $this->project->id,
]); ]);
return $this->redirectRoute('project.resources', [ return redirect()->route('project.resource.index', [
'project_uuid' => $this->project->uuid, 'project_uuid' => $this->project->uuid,
'environment_name' => $environment->name, 'environment_name' => $environment->name,
], navigate: true); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
handleError($e, $this); handleError($e, $this);
} finally { } finally {

View File

@@ -4,26 +4,27 @@ namespace App\Livewire\Project\Application;
use App\Models\Application; use App\Models\Application;
use App\Models\Server; use App\Models\Server;
use App\Models\StandaloneDocker;
use Livewire\Component; use Livewire\Component;
class Configuration extends Component class Configuration extends Component
{ {
public Application $application; public Application $application;
public $servers; public $servers;
protected $listeners = ['build_pack_updated' => '$refresh'];
public function mount() public function mount()
{ {
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) { if (!$project) {
return $this->redirectRoute('dashboard', navigate: true); return redirect()->route('dashboard');
} }
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) { if (!$environment) {
return $this->redirectRoute('dashboard', navigate: true); return redirect()->route('dashboard');
} }
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first(); $application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
if (!$application) { if (!$application) {
return $this->redirectRoute('dashboard', navigate: true); return redirect()->route('dashboard');
} }
$this->application = $application; $this->application = $application;
$mainServer = $this->application->destination->server; $mainServer = $this->application->destination->server;

View File

@@ -1,15 +1,15 @@
<?php <?php
namespace App\Livewire\Project\Application; namespace App\Livewire\Project\Application\Deployment;
use App\Models\Application; use App\Models\Application;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Livewire\Component; use Livewire\Component;
class Deployments extends Component class Index extends Component
{ {
public Application $application; public Application $application;
public Array|Collection $deployments = []; public array|Collection $deployments = [];
public int $deployments_count = 0; public int $deployments_count = 0;
public string $current_url; public string $current_url;
public int $skip = 0; public int $skip = 0;
@@ -19,11 +19,28 @@ class Deployments extends Component
protected $queryString = ['pull_request_id']; protected $queryString = ['pull_request_id'];
public function mount() public function mount()
{ {
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
if (!$application) {
return redirect()->route('dashboard');
}
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
$this->application = $application;
$this->deployments = $deployments;
$this->deployments_count = $count;
$this->current_url = url()->current(); $this->current_url = url()->current();
$this->show_pull_request_only(); $this->show_pull_request_only();
$this->show_more(); $this->show_more();
} }
private function show_pull_request_only() { private function show_pull_request_only()
{
if ($this->pull_request_id) { if ($this->pull_request_id) {
$this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id); $this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id);
} }
@@ -57,4 +74,8 @@ class Deployments extends Component
$this->show_pull_request_only(); $this->show_pull_request_only();
$this->show_more(); $this->show_more();
} }
public function render()
{
return view('livewire.project.application.deployment.index');
}
} }

View File

@@ -1,35 +1,20 @@
<?php <?php
namespace App\Http\Controllers; namespace App\Livewire\Project\Application\Deployment;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationDeploymentQueue;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component;
use Illuminate\Foundation\Validation\ValidatesRequests;
class ApplicationController extends Controller class Show extends Component
{ {
use AuthorizesRequests, ValidatesRequests; public Application $application;
public ApplicationDeploymentQueue $application_deployment_queue;
public string $deployment_uuid;
public $isKeepAliveOn = true;
protected $listeners = ['refreshQueue'];
public function deployments() public function mount() {
{
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
if (!$application) {
return redirect()->route('dashboard');
}
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
return view('project.application.deployments', ['application' => $application, 'deployments' => $deployments, 'deployments_count' => $count]);
}
public function deployment()
{
$deploymentUuid = request()->route('deployment_uuid'); $deploymentUuid = request()->route('deployment_uuid');
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
@@ -46,7 +31,7 @@ class ApplicationController extends Controller
} }
// $activity = Activity::where('properties->type_uuid', '=', $deploymentUuid)->first(); // $activity = Activity::where('properties->type_uuid', '=', $deploymentUuid)->first();
// if (!$activity) { // if (!$activity) {
// return redirect()->route('project.application.deployments', [ // return redirect()->route('project.application.deployment.index', [
// 'project_uuid' => $project->uuid, // 'project_uuid' => $project->uuid,
// 'environment_name' => $environment->name, // 'environment_name' => $environment->name,
// 'application_uuid' => $application->uuid, // 'application_uuid' => $application->uuid,
@@ -54,17 +39,32 @@ class ApplicationController extends Controller
// } // }
$application_deployment_queue = ApplicationDeploymentQueue::where('deployment_uuid', $deploymentUuid)->first(); $application_deployment_queue = ApplicationDeploymentQueue::where('deployment_uuid', $deploymentUuid)->first();
if (!$application_deployment_queue) { if (!$application_deployment_queue) {
return redirect()->route('project.application.deployments', [ return redirect()->route('project.application.deployment.index', [
'project_uuid' => $project->uuid, 'project_uuid' => $project->uuid,
'environment_name' => $environment->name, 'environment_name' => $environment->name,
'application_uuid' => $application->uuid, 'application_uuid' => $application->uuid,
]); ]);
} }
return view('project.application.deployment', [ $this->application = $application;
'application' => $application, $this->application_deployment_queue = $application_deployment_queue;
// 'activity' => $activity, $this->deployment_uuid = $deploymentUuid;
'application_deployment_queue' => $application_deployment_queue, }
'deployment_uuid' => $deploymentUuid,
]); public function refreshQueue()
{
$this->application_deployment_queue->refresh();
}
public function polling()
{
$this->dispatch('deploymentFinished');
$this->application_deployment_queue->refresh();
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
$this->isKeepAliveOn = false;
}
}
public function render()
{
return view('livewire.project.application.deployment.show');
} }
} }

View File

@@ -1,27 +0,0 @@
<?php
namespace App\Livewire\Project\Application;
use App\Models\ApplicationDeploymentQueue;
use Livewire\Component;
class DeploymentLogs extends Component
{
public ApplicationDeploymentQueue $application_deployment_queue;
public $isKeepAliveOn = true;
protected $listeners = ['refreshQueue'];
public function refreshQueue()
{
$this->application_deployment_queue->refresh();
}
public function polling()
{
$this->dispatch('deploymentFinished');
$this->application_deployment_queue->refresh();
if (data_get($this->application_deployment_queue, 'status') == 'finished' || data_get($this->application_deployment_queue, 'status') == 'failed') {
$this->isKeepAliveOn = false;
}
}
}

View File

@@ -66,6 +66,7 @@ class General extends Component
'application.settings.is_static' => 'boolean|required', 'application.settings.is_static' => 'boolean|required',
'application.docker_compose_custom_start_command' => 'nullable', 'application.docker_compose_custom_start_command' => 'nullable',
'application.docker_compose_custom_build_command' => 'nullable', 'application.docker_compose_custom_build_command' => 'nullable',
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
'application.name' => 'name', 'application.name' => 'name',
@@ -98,6 +99,7 @@ class General extends Component
'application.settings.is_static' => 'Is static', 'application.settings.is_static' => 'Is static',
'application.docker_compose_custom_start_command' => 'Docker compose custom start command', 'application.docker_compose_custom_start_command' => 'Docker compose custom start command',
'application.docker_compose_custom_build_command' => 'Docker compose custom build command', 'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
'application.settings.is_raw_compose_deployment_enabled' => 'Is raw compose deployment enabled',
]; ];
public function mount() public function mount()
{ {
@@ -114,6 +116,11 @@ class General extends Component
} }
$this->isConfigurationChanged = $this->application->isConfigurationChanged(); $this->isConfigurationChanged = $this->application->isConfigurationChanged();
$this->customLabels = $this->application->parseContainerLabels(); $this->customLabels = $this->application->parseContainerLabels();
if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
}
$this->initialDockerComposeLocation = $this->application->docker_compose_location; $this->initialDockerComposeLocation = $this->application->docker_compose_location;
$this->checkLabelUpdates(); $this->checkLabelUpdates();
} }
@@ -158,12 +165,20 @@ class General extends Component
if ($this->application->build_pack !== 'nixpacks') { if ($this->application->build_pack !== 'nixpacks') {
$this->application->settings->is_static = false; $this->application->settings->is_static = false;
$this->application->settings->save(); $this->application->settings->save();
} else {
$this->application->ports_exposes = $this->ports_exposes = 3000;
$this->resetDefaultLabels(false);
} }
if ($this->application->build_pack === 'dockercompose') { if ($this->application->build_pack === 'dockercompose') {
$this->application->fqdn = null; $this->application->fqdn = null;
$this->application->settings->save(); $this->application->settings->save();
} }
if ($this->application->build_pack === 'static') {
$this->application->ports_exposes = $this->ports_exposes = 80;
$this->resetDefaultLabels(false);
}
$this->submit(); $this->submit();
$this->dispatch('build_pack_updated');
} }
public function checkLabelUpdates() public function checkLabelUpdates()
{ {
@@ -199,7 +214,12 @@ class General extends Component
public function submit($showToaster = true) public function submit($showToaster = true)
{ {
try { try {
ray($this->initialDockerComposeLocation, $this->application->docker_compose_location); if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
}
if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) { if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
$this->loadComposeFile(); $this->loadComposeFile();
} }
@@ -207,6 +227,7 @@ class General extends Component
if ($this->ports_exposes !== $this->application->ports_exposes) { if ($this->ports_exposes !== $this->application->ports_exposes) {
$this->resetDefaultLabels(false); $this->resetDefaultLabels(false);
} }
if (data_get($this->application, 'build_pack') === 'dockerimage') { if (data_get($this->application, 'build_pack') === 'dockerimage') {
$this->validate([ $this->validate([
'application.docker_registry_image_name' => 'required', 'application.docker_registry_image_name' => 'required',

View File

@@ -39,7 +39,7 @@ class Heading extends Component
} else { } else {
dispatch(new ServerStatusJob($this->application->destination->server)); dispatch(new ServerStatusJob($this->application->destination->server));
} }
if ($showNotification) $this->dispatch('success', 'Application status updated.'); if ($showNotification) $this->dispatch('success', "Application ({$this->application->name}) status updated.");
} }
public function force_deploy_without_cache() public function force_deploy_without_cache()
@@ -60,12 +60,12 @@ class Heading extends Component
force_rebuild: false, force_rebuild: false,
is_new_deployment: true, is_new_deployment: true,
); );
return $this->redirectRoute('project.application.deployment', [ return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'], 'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'], 'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid, 'deployment_uuid' => $this->deploymentUuid,
'environment_name' => $this->parameters['environment_name'], 'environment_name' => $this->parameters['environment_name'],
], navigate: true); ]);
} }
public function deploy(bool $force_rebuild = false) public function deploy(bool $force_rebuild = false)
{ {
@@ -83,12 +83,12 @@ class Heading extends Component
deployment_uuid: $this->deploymentUuid, deployment_uuid: $this->deploymentUuid,
force_rebuild: $force_rebuild, force_rebuild: $force_rebuild,
); );
$this->redirectRoute('project.application.deployment', [ return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'], 'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'], 'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid, 'deployment_uuid' => $this->deploymentUuid,
'environment_name' => $this->parameters['environment_name'], 'environment_name' => $this->parameters['environment_name'],
], navigate: true); ]);
} }
protected function setDeploymentUuid() protected function setDeploymentUuid()
@@ -113,12 +113,12 @@ class Heading extends Component
restart_only: true, restart_only: true,
is_new_deployment: true, is_new_deployment: true,
); );
return $this->redirectRoute('project.application.deployment', [ return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'], 'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'], 'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid, 'deployment_uuid' => $this->deploymentUuid,
'environment_name' => $this->parameters['environment_name'], 'environment_name' => $this->parameters['environment_name'],
], navigate: true); ]);
} }
public function restart() public function restart()
{ {
@@ -128,11 +128,11 @@ class Heading extends Component
deployment_uuid: $this->deploymentUuid, deployment_uuid: $this->deploymentUuid,
restart_only: true, restart_only: true,
); );
return $this->redirectRoute('project.application.deployment', [ return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'], 'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'], 'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deploymentUuid, 'deployment_uuid' => $this->deploymentUuid,
'environment_name' => $this->parameters['environment_name'], 'environment_name' => $this->parameters['environment_name'],
], navigate: true); ]);
} }
} }

View File

@@ -52,12 +52,12 @@ class Previews extends Component
force_rebuild: true, force_rebuild: true,
pull_request_id: $pull_request_id, pull_request_id: $pull_request_id,
); );
return $this->redirectRoute('project.application.deployment', [ return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'], 'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'], 'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $this->deployment_uuid, 'deployment_uuid' => $this->deployment_uuid,
'environment_name' => $this->parameters['environment_name'], 'environment_name' => $this->parameters['environment_name'],
], navigate: true); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -29,12 +29,12 @@ class Rollback extends Component
commit: $commit, commit: $commit,
force_rebuild: false, force_rebuild: false,
); );
return $this->redirectRoute('project.application.deployment', [ return redirect()->route('project.application.deployment.show', [
'project_uuid' => $this->parameters['project_uuid'], 'project_uuid' => $this->parameters['project_uuid'],
'application_uuid' => $this->parameters['application_uuid'], 'application_uuid' => $this->parameters['application_uuid'],
'deployment_uuid' => $deployment_uuid, 'deployment_uuid' => $deployment_uuid,
'environment_name' => $this->parameters['environment_name'], 'environment_name' => $this->parameters['environment_name'],
], navigate: true); ]);
} }
public function loadImages($showToast = false) public function loadImages($showToast = false)

View File

@@ -8,7 +8,7 @@ use App\Models\Server;
use Livewire\Component; use Livewire\Component;
use Visus\Cuid2\Cuid2; use Visus\Cuid2\Cuid2;
class CloneProject extends Component class CloneMe extends Component
{ {
public string $project_uuid; public string $project_uuid;
public string $environment_name; public string $environment_name;
@@ -41,7 +41,7 @@ class CloneProject extends Component
public function render() public function render()
{ {
return view('livewire.project.clone-project'); return view('livewire.project.clone-me');
} }
public function selectServer($server_id, $destination_id) public function selectServer($server_id, $destination_id)
@@ -152,10 +152,10 @@ class CloneProject extends Component
} }
$newService->parse(); $newService->parse();
} }
return $this->redirectRoute('project.resources', [ return redirect()->route('project.resource.index', [
'project_uuid' => $newProject->uuid, 'project_uuid' => $newProject->uuid,
'environment_name' => $newEnvironment->name, 'environment_name' => $newEnvironment->name,
], navigate: true); ]);
} catch (\Exception $e) { } catch (\Exception $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Livewire\Project\Database\Backup;
use Livewire\Component;
class Execution extends Component
{
public $database;
public $backup;
public $executions;
public $s3s;
public function mount() {
$backup_uuid = request()->route('backup_uuid');
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
$backup = $database->scheduledBackups->where('uuid', $backup_uuid)->first();
if (!$backup) {
return redirect()->route('dashboard');
}
$executions = collect($backup->executions)->sortByDesc('created_at');
$this->database = $database;
$this->backup = $backup;
$this->executions = $executions;
$this->s3s = currentTeam()->s3s;
}
public function render()
{
return view('livewire.project.database.backup.execution');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Livewire\Project\Database\Backup;
use Livewire\Component;
class Index extends Component
{
public $database;
public $s3s;
public function mount() {
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
// No backups for redis
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
return redirect()->route('project.database.configuration', [
'project_uuid' => $project->uuid,
'environment_name' => $environment->name,
'database_uuid' => $database->uuid,
]);
}
$this->database = $database;
$this->s3s = currentTeam()->s3s;
}
public function render()
{
return view('livewire.project.database.backup.index');
}
}

View File

@@ -50,9 +50,9 @@ class BackupEdit extends Component
$url = $url->withoutQueryParameter('selectedBackupId'); $url = $url->withoutQueryParameter('selectedBackupId');
$url = $url->withFragment('backups'); $url = $url->withFragment('backups');
$url = $url->getPath() . "#{$url->getFragment()}"; $url = $url->getPath() . "#{$url->getFragment()}";
return $this->redirect($url,navigate: true); return redirect($url);
} else { } else {
return $this->redirectRoute('project.database.backups.all', $this->parameters); return redirect()->route('project.database.backup.index', $this->parameters);
} }
} }

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Livewire\Project\Database;
use Livewire\Component;
class Configuration extends Component
{
public $database;
public function mount() {
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
if (!$environment) {
return redirect()->route('dashboard');
}
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
if (!$database) {
return redirect()->route('dashboard');
}
$this->database = $database;
}
public function render()
{
return view('livewire.project.database.configuration');
}
}

View File

@@ -23,7 +23,7 @@ class DeleteEnvironment extends Component
$environment = Environment::findOrFail($this->environment_id); $environment = Environment::findOrFail($this->environment_id);
if ($environment->isEmpty()) { if ($environment->isEmpty()) {
$environment->delete(); $environment->delete();
return $this->redirectRoute('project.show', ['project_uuid' => $this->parameters['project_uuid']], navigate: true); return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
} }
return $this->dispatch('error', 'Environment has defined resources, please delete them first.'); return $this->dispatch('error', 'Environment has defined resources, please delete them first.');
} }

View File

@@ -25,6 +25,6 @@ class DeleteProject extends Component
return $this->dispatch('error', 'Project has resources defined, please delete them first.'); return $this->dispatch('error', 'Project has resources defined, please delete them first.');
} }
$project->delete(); $project->delete();
return $this->redirectRoute('projects', navigate: true); return redirect()->route('project.index');
} }
} }

View File

@@ -12,6 +12,15 @@ class Edit extends Component
'project.name' => 'required|min:3|max:255', 'project.name' => 'required|min:3|max:255',
'project.description' => 'nullable|string|max:255', 'project.description' => 'nullable|string|max:255',
]; ];
public function mount() {
$projectUuid = request()->route('project_uuid');
$teamId = currentTeam()->id;
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
if (!$project) {
return redirect()->route('dashboard');
}
$this->project = $project;
}
public function submit() public function submit()
{ {

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Livewire\Project;
use App\Models\Application;
use App\Models\Project;
use Livewire\Component;
class EnvironmentEdit extends Component
{
public Project $project;
public Application $application;
public $environment;
public array $parameters;
protected $rules = [
'environment.name' => 'required|min:3|max:255',
'environment.description' => 'nullable|min:3|max:255',
];
public function mount() {
$this->parameters = get_route_parameters();
$this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->first();
$this->environment = $this->project->environments()->where('name', request()->route('environment_name'))->first();
}
public function submit()
{
$this->validate();
try {
$this->environment->save();
return redirect()->route('project.environment.edit', ['project_uuid' => $this->project->uuid, 'environment_name' => $this->environment->name]);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function render()
{
return view('livewire.project.environment-edit');
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Project;
use App\Models\Project;
use App\Models\Server;
use Livewire\Component;
class Index extends Component
{
public $projects;
public $servers;
public function mount() {
$this->projects = Project::ownedByCurrentTeam()->get();
$this->servers = Server::ownedByCurrentTeam()->count();
}
public function render()
{
return view('livewire.project.index');
}
}

View File

@@ -69,12 +69,12 @@ class DockerCompose extends Component
$service->parse(isNew: true); $service->parse(isNew: true);
return $this->redirectRoute('project.service.configuration', [ return redirect()->route('project.service.configuration', [
'service_uuid' => $service->uuid, 'service_uuid' => $service->uuid,
'environment_name' => $environment->name, 'environment_name' => $environment->name,
'project_uuid' => $project->uuid, 'project_uuid' => $project->uuid,
]); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -64,11 +64,11 @@ class DockerImage extends Component
'name' => 'docker-image-' . $application->uuid, 'name' => 'docker-image-' . $application->uuid,
'fqdn' => $fqdn 'fqdn' => $fqdn
]); ]);
return $this->redirectRoute('project.application.configuration', [ return redirect()->route('project.application.configuration', [
'application_uuid' => $application->uuid, 'application_uuid' => $application->uuid,
'environment_name' => $environment->name, 'environment_name' => $environment->name,
'project_uuid' => $project->uuid, 'project_uuid' => $project->uuid,
], navigate: false); ]);
} }
public function render() public function render()
{ {

View File

@@ -13,6 +13,6 @@ class EmptyProject extends Component
'name' => generate_random_name(), 'name' => generate_random_name(),
'team_id' => currentTeam()->id, 'team_id' => currentTeam()->id,
]); ]);
return $this->redirectRoute('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production'], navigate: false); return redirect()->route('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production']);
} }
} }

View File

@@ -151,11 +151,11 @@ class GithubPrivateRepository extends Component
$application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid); $application->name = generate_application_name($this->selected_repository_owner . '/' . $this->selected_repository_repo, $this->selected_branch_name, $application->uuid);
$application->save(); $application->save();
return $this->redirectRoute('project.application.configuration', [ return redirect()->route('project.application.configuration', [
'application_uuid' => $application->uuid, 'application_uuid' => $application->uuid,
'environment_name' => $environment->name, 'environment_name' => $environment->name,
'project_uuid' => $project->uuid, 'project_uuid' => $project->uuid,
], navigate: false); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -55,7 +55,11 @@ class GithubPrivateRepositoryDeployKey extends Component
} }
$this->parameters = get_route_parameters(); $this->parameters = get_route_parameters();
$this->query = request()->query(); $this->query = request()->query();
$this->private_keys = PrivateKey::where('team_id', currentTeam()->id)->where('id', '!=', 0)->get(); if (isDev()) {
$this->private_keys = PrivateKey::where('team_id', currentTeam()->id)->get();
} else {
$this->private_keys = PrivateKey::where('team_id', currentTeam()->id)->where('id', '!=', 0)->get();
}
} }
public function instantSave() public function instantSave()
@@ -132,11 +136,11 @@ class GithubPrivateRepositoryDeployKey extends Component
$application->name = generate_random_name($application->uuid); $application->name = generate_random_name($application->uuid);
$application->save(); $application->save();
return $this->redirectRoute('project.application.configuration', [ return redirect()->route('project.application.configuration', [
'application_uuid' => $application->uuid, 'application_uuid' => $application->uuid,
'environment_name' => $environment->name, 'environment_name' => $environment->name,
'project_uuid' => $project->uuid, 'project_uuid' => $project->uuid,
], navigate: false); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -184,11 +184,11 @@ class PublicGitRepository extends Component
$application->fqdn = $fqdn; $application->fqdn = $fqdn;
$application->save(); $application->save();
return $this->redirectRoute('project.application.configuration', [ return redirect()->route('project.application.configuration', [
'application_uuid' => $application->uuid, 'application_uuid' => $application->uuid,
'environment_name' => $environment->name, 'environment_name' => $environment->name,
'project_uuid' => $project->uuid, 'project_uuid' => $project->uuid,
], navigate: false); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -55,10 +55,10 @@ class Select extends Component
public function updatedSelectedEnvironment() public function updatedSelectedEnvironment()
{ {
return $this->redirectRoute('project.resources.new', [ return redirect()->route('project.resource.create', [
'project_uuid' => $this->parameters['project_uuid'], 'project_uuid' => $this->parameters['project_uuid'],
'environment_name' => $this->selectedEnvironment, 'environment_name' => $this->selectedEnvironment,
], navigate: true); ]);
} }
// public function addExistingPostgresql() // public function addExistingPostgresql()
@@ -157,7 +157,7 @@ class Select extends Component
public function setDestination(string $destination_uuid) public function setDestination(string $destination_uuid)
{ {
$this->destination_uuid = $destination_uuid; $this->destination_uuid = $destination_uuid;
return redirect()->route('project.resources.new', [ return redirect()->route('project.resource.create', [
'project_uuid' => $this->parameters['project_uuid'], 'project_uuid' => $this->parameters['project_uuid'],
'environment_name' => $this->parameters['environment_name'], 'environment_name' => $this->parameters['environment_name'],
'type' => $this->type, 'type' => $this->type,

View File

@@ -70,10 +70,10 @@ CMD ["nginx", "-g", "daemon off;"]
'fqdn' => $fqdn 'fqdn' => $fqdn
]); ]);
return $this->redirectRoute('project.application.configuration', [ return redirect()->route('project.application.configuration', [
'application_uuid' => $application->uuid, 'application_uuid' => $application->uuid,
'environment_name' => $environment->name, 'environment_name' => $environment->name,
'project_uuid' => $project->uuid, 'project_uuid' => $project->uuid,
], navigate: false); ]);
} }
} }

View File

@@ -1,52 +1,18 @@
<?php <?php
namespace App\Http\Controllers; namespace App\Livewire\Project\Resource;
use App\Models\EnvironmentVariable; use App\Models\EnvironmentVariable;
use App\Models\Project;
use App\Models\Server;
use App\Models\Service; use App\Models\Service;
use App\Models\StandaloneDocker; use App\Models\StandaloneDocker;
use Illuminate\Support\Str; use Livewire\Component;
class ProjectController extends Controller class Create extends Component
{ {
public function all() public $type;
{ public function mount() {
return view('projects', [
'projects' => Project::ownedByCurrentTeam()->get(),
'servers' => Server::ownedByCurrentTeam()->count(),
]);
}
public function edit()
{
$projectUuid = request()->route('project_uuid');
$teamId = currentTeam()->id;
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
if (!$project) {
return redirect()->route('dashboard');
}
return view('project.edit', ['project' => $project]);
}
public function show()
{
$projectUuid = request()->route('project_uuid');
$teamId = currentTeam()->id;
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
if (!$project) {
return redirect()->route('dashboard');
}
$project->load(['environments']);
return view('project.show', ['project' => $project]);
}
public function new()
{
$services = getServiceTemplates(); $services = getServiceTemplates();
$type = Str::of(request()->query('type')); $type = str(request()->query('type'));
$destination_uuid = request()->query('destination'); $destination_uuid = request()->query('destination');
$server_id = request()->query('server_id'); $server_id = request()->query('server_id');
@@ -81,14 +47,14 @@ class ProjectController extends Controller
$oneClickService = data_get($services, "$oneClickServiceName.compose"); $oneClickService = data_get($services, "$oneClickServiceName.compose");
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null); $oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
if ($oneClickDotEnvs) { if ($oneClickDotEnvs) {
$oneClickDotEnvs = Str::of(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) { $oneClickDotEnvs = str(base64_decode($oneClickDotEnvs))->split('/\r\n|\r|\n/')->filter(function ($value) {
return !empty($value); return !empty($value);
}); });
} }
if ($oneClickService) { if ($oneClickService) {
$destination = StandaloneDocker::whereUuid($destination_uuid)->first(); $destination = StandaloneDocker::whereUuid($destination_uuid)->first();
$service = Service::create([ $service = Service::create([
'name' => "$oneClickServiceName-" . Str::random(10), 'name' => "$oneClickServiceName-" . str()->random(10),
'docker_compose_raw' => base64_decode($oneClickService), 'docker_compose_raw' => base64_decode($oneClickService),
'environment_id' => $environment->id, 'environment_id' => $environment->id,
'server_id' => (int) $server_id, 'server_id' => (int) $server_id,
@@ -99,8 +65,8 @@ class ProjectController extends Controller
$service->save(); $service->save();
if ($oneClickDotEnvs?->count() > 0) { if ($oneClickDotEnvs?->count() > 0) {
$oneClickDotEnvs->each(function ($value) use ($service) { $oneClickDotEnvs->each(function ($value) use ($service) {
$key = Str::before($value, '='); $key = str()->before($value, '=');
$value = Str::of(Str::after($value, '=')); $value = str(str()->after($value, '='));
$generatedValue = $value; $generatedValue = $value;
if ($value->contains('SERVICE_')) { if ($value->contains('SERVICE_')) {
$command = $value->after('SERVICE_')->beforeLast('_'); $command = $value->after('SERVICE_')->beforeLast('_');
@@ -123,24 +89,10 @@ class ProjectController extends Controller
]); ]);
} }
} }
return view('project.new', [ $this->type = $type->value();
'type' => $type->value()
]);
} }
public function render()
public function resources()
{ {
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); return view('livewire.project.resource.create');
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first();
if (!$environment) {
return redirect()->route('dashboard');
}
return view('project.resources', [
'project' => $project,
'environment' => $environment
]);
} }
} }

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Livewire\Project\Resource;
use App\Models\Environment;
use App\Models\Project;
use Livewire\Component;
class Index extends Component
{
public Project $project;
public Environment $environment;
public function mount () {
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
if (!$project) {
return redirect()->route('dashboard');
}
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first();
if (!$environment) {
return redirect()->route('dashboard');
}
$this->project = $project;
$this->environment = $environment;
}
public function render()
{
return view('livewire.project.resource.index');
}
}

View File

@@ -41,7 +41,7 @@ class Application extends Component
try { try {
$this->application->delete(); $this->application->delete();
$this->dispatch('success', 'Application deleted successfully.'); $this->dispatch('success', 'Application deleted successfully.');
return $this->redirectRoute('project.service.configuration', $this->parameters, navigate: true); return redirect()->route('project.service.configuration', $this->parameters);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Livewire\Project\Service;
use App\Jobs\ContainerStatusJob;
use App\Models\Service;
use Livewire\Component;
class Configuration extends Component
{
public Service $service;
public $applications;
public $databases;
public array $parameters;
public array $query;
public function getListeners()
{
$userId = auth()->user()->id;
return [
"echo-private:user.{$userId},ServiceStatusChanged" => 'checkStatus',
"refreshStacks",
"checkStatus",
];
}
public function render()
{
return view('livewire.project.service.configuration');
}
public function mount()
{
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$this->applications = $this->service->applications->sort();
$this->databases = $this->service->databases->sort();
}
public function checkStatus()
{
dispatch_sync(new ContainerStatusJob($this->service->server));
$this->refreshStacks();
$this->dispatch('serviceStatusChanged');
}
public function refreshStacks()
{
$this->applications = $this->service->applications->sort();
$this->applications->each(function ($application) {
$application->refresh();
});
$this->databases = $this->service->databases->sort();
$this->databases->each(function ($database) {
$database->refresh();
});
}
}

View File

@@ -2,53 +2,51 @@
namespace App\Livewire\Project\Service; namespace App\Livewire\Project\Service;
use App\Jobs\ContainerStatusJob;
use App\Models\Service; use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
use Illuminate\Support\Collection;
use Livewire\Component; use Livewire\Component;
class Index extends Component class Index extends Component
{ {
public Service $service; public Service $service;
public $applications; public ?ServiceApplication $serviceApplication = null;
public $databases; public ?ServiceDatabase $serviceDatabase = null;
public array $parameters; public array $parameters;
public array $query; public array $query;
public function getListeners() public Collection $services;
public $s3s;
protected $listeners = ['generateDockerCompose'];
public function mount()
{ {
$userId = auth()->user()->id; try {
return [ $this->services = collect([]);
"echo-private:user.{$userId},ServiceStatusChanged" => 'checkStatus', $this->parameters = get_route_parameters();
"refreshStacks", $this->query = request()->query();
"checkStatus", $this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
]; $service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
if ($service) {
$this->serviceApplication = $service;
$this->serviceApplication->getFilesFromServer();
} else {
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
$this->serviceDatabase->getFilesFromServer();
}
$this->s3s = currentTeam()->s3s;
} catch(\Throwable $e) {
return handleError($e, $this);
}
}
public function generateDockerCompose()
{
$this->service->parse();
} }
public function render() public function render()
{ {
return view('livewire.project.service.index'); return view('livewire.project.service.index');
} }
public function mount()
{
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$this->applications = $this->service->applications->sort();
$this->databases = $this->service->databases->sort();
}
public function checkStatus()
{
dispatch_sync(new ContainerStatusJob($this->service->server));
$this->refreshStacks();
$this->dispatch('serviceStatusChanged');
}
public function refreshStacks()
{
$this->applications = $this->service->applications->sort();
$this->applications->each(function ($application) {
$application->refresh();
});
$this->databases = $this->service->databases->sort();
$this->databases->each(function ($database) {
$database->refresh();
});
}
} }

View File

@@ -1,52 +0,0 @@
<?php
namespace App\Livewire\Project\Service;
use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
use Illuminate\Support\Collection;
use Livewire\Component;
class Show extends Component
{
public Service $service;
public ?ServiceApplication $serviceApplication = null;
public ?ServiceDatabase $serviceDatabase = null;
public array $parameters;
public array $query;
public Collection $services;
public $s3s;
protected $listeners = ['generateDockerCompose'];
public function mount()
{
try {
$this->services = collect([]);
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
if ($service) {
$this->serviceApplication = $service;
$this->serviceApplication->getFilesFromServer();
} else {
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
$this->serviceDatabase->getFilesFromServer();
}
$this->s3s = currentTeam()->s3s;
} catch(\Throwable $e) {
return handleError($e, $this);
}
}
public function generateDockerCompose()
{
$this->service->parse();
}
public function render()
{
return view('livewire.project.service.show');
}
}

View File

@@ -25,10 +25,10 @@ class Danger extends Component
{ {
try { try {
DeleteResourceJob::dispatchSync($this->resource); DeleteResourceJob::dispatchSync($this->resource);
return $this->redirectRoute('project.resources', [ return redirect()->route('project.resource.index', [
'project_uuid' => $this->projectUuid, 'project_uuid' => $this->projectUuid,
'environment_name' => $this->environmentName 'environment_name' => $this->environmentName
], navigate: true); ]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -139,6 +139,18 @@ class All extends Component
case 'standalone-postgresql': case 'standalone-postgresql':
$environment->standalone_postgresql_id = $this->resource->id; $environment->standalone_postgresql_id = $this->resource->id;
break; 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': case 'service':
$environment->service_id = $this->resource->id; $environment->service_id = $this->resource->id;
break; break;

View File

@@ -10,7 +10,6 @@ use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql; use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis; use App\Models\StandaloneRedis;
use Illuminate\Support\Sleep;
use Livewire\Component; use Livewire\Component;
class ExecuteContainerCommand extends Component class ExecuteContainerCommand extends Component

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Livewire\Project;
use App\Models\Project;
use Livewire\Component;
class Show extends Component
{
public Project $project;
public function mount() {
$projectUuid = request()->route('project_uuid');
$teamId = currentTeam()->id;
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
if (!$project) {
return redirect()->route('dashboard');
}
$project->load(['environments']);
$this->project = $project;
}
public function render()
{
return view('livewire.project.show');
}
}

View File

@@ -1,6 +1,6 @@
<?php <?php
namespace App\Livewire\PrivateKey; namespace App\Livewire\Security\PrivateKey;
use App\Models\PrivateKey; use App\Models\PrivateKey;
use DanHarrin\LivewireRateLimiting\WithRateLimiting; use DanHarrin\LivewireRateLimiting\WithRateLimiting;
@@ -67,9 +67,9 @@ class Create extends Component
'team_id' => currentTeam()->id 'team_id' => currentTeam()->id
]); ]);
if ($this->from === 'server') { if ($this->from === 'server') {
return $this->redirectRoute('server.create', navigate: true); return redirect()->route('server.create');
} }
return $this->redirectRoute('security.private-key.show', ['private_key_uuid' => $private_key->uuid], navigate: true); return redirect()->route('security.private-key.show', ['private_key_uuid' => $private_key->uuid]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -1,11 +1,11 @@
<?php <?php
namespace App\Livewire\PrivateKey; namespace App\Livewire\Security\PrivateKey;
use App\Models\PrivateKey; use App\Models\PrivateKey;
use Livewire\Component; use Livewire\Component;
class Change extends Component class Show extends Component
{ {
public PrivateKey $private_key; public PrivateKey $private_key;
public $public_key; public $public_key;
@@ -24,6 +24,7 @@ class Change extends Component
public function mount() public function mount()
{ {
try { try {
$this->private_key = PrivateKey::ownedByCurrentTeam(['name', 'description', 'private_key', 'is_git_related'])->whereUuid(request()->private_key_uuid)->firstOrFail();
$this->public_key = $this->private_key->publicKey(); $this->public_key = $this->private_key->publicKey();
}catch(\Throwable $e) { }catch(\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
@@ -35,7 +36,7 @@ class Change extends Component
if ($this->private_key->isEmpty()) { if ($this->private_key->isEmpty()) {
$this->private_key->delete(); $this->private_key->delete();
currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get(); currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get();
return $this->redirectRoute('security.private-key.index', navigate: true); return redirect()->route('security.private-key.index');
} }
$this->dispatch('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.'); $this->dispatch('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.');
} catch (\Throwable $e) { } catch (\Throwable $e) {

View File

@@ -19,7 +19,7 @@ class Delete extends Component
return; return;
} }
$this->server->delete(); $this->server->delete();
return $this->redirectRoute('server.all', navigate: true); return redirect()->route('server.index');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -15,7 +15,7 @@ class Show extends Component
try { try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first(); $this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) { if (is_null($this->server)) {
return $this->redirectRoute('server.all', navigate: true); return redirect()->route('server.index');
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);

View File

@@ -6,7 +6,7 @@ use App\Models\Server;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Livewire\Component; use Livewire\Component;
class All extends Component class Index extends Component
{ {
public ?Collection $servers = null; public ?Collection $servers = null;
@@ -15,6 +15,6 @@ class All extends Component
} }
public function render() public function render()
{ {
return view('livewire.server.all'); return view('livewire.server.index');
} }
} }

View File

@@ -43,7 +43,7 @@ class LogDrains extends Component
try { try {
$server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first(); $server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($server)) { if (is_null($server)) {
return $this->redirectRoute('server.all', navigate: true); return redirect()->route('server.index');
} }
$this->server = $server; $this->server = $server;
} catch (\Throwable $e) { } catch (\Throwable $e) {

View File

@@ -93,7 +93,7 @@ class ByIp extends Component
$server->settings->is_swarm_worker = $this->is_swarm_worker; $server->settings->is_swarm_worker = $this->is_swarm_worker;
$server->settings->save(); $server->settings->save();
$server->addInitialNetwork(); $server->addInitialNetwork();
return $this->redirectRoute('server.show', $server->uuid); return redirect()->route('server.show', $server->uuid);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -17,7 +17,7 @@ class Show extends Component
try { try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first(); $this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) { if (is_null($this->server)) {
return $this->redirectRoute('server.all', navigate: true); return redirect()->route('server.index');
} }
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false); $this->privateKeys = PrivateKey::ownedByCurrentTeam()->get()->where('is_git_related', false);
} catch (\Throwable $e) { } catch (\Throwable $e) {

View File

@@ -15,7 +15,7 @@ class Logs extends Component
try { try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first(); $this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) { if (is_null($this->server)) {
return $this->redirectRoute('server.all', navigate: true); return redirect()->route('server.index');
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);

View File

@@ -20,7 +20,7 @@ class Show extends Component
try { try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first(); $this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) { if (is_null($this->server)) {
return $this->redirectRoute('server.all', navigate: true); return redirect()->route('server.index');
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);

View File

@@ -17,7 +17,7 @@ class Show extends Component
try { try {
$this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first(); $this->server = Server::ownedByCurrentTeam()->whereUuid(request()->server_uuid)->first();
if (is_null($this->server)) { if (is_null($this->server)) {
return $this->redirectRoute('server.all', navigate: true); return redirect()->route('server.index');
} }
} catch (\Throwable $e) { } catch (\Throwable $e) {

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Livewire\Settings;
use App\Models\InstanceSettings;
use App\Models\S3Storage;
use App\Models\StandalonePostgresql;
use Livewire\Component;
class Index extends Component
{
public InstanceSettings $settings;
public StandalonePostgresql $database;
public $s3s;
public function mount()
{
if (isInstanceAdmin()) {
$settings = InstanceSettings::get();
$database = StandalonePostgresql::whereName('coolify-db')->first();
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
if ($database) {
if ($database->status !== 'running') {
$database->status = 'running';
$database->save();
}
$this->database = $database;
}
$this->settings = $settings;
$this->s3s = $s3s;
} else {
return redirect()->route('dashboard');
}
}
public function render()
{
return view('livewire.settings.index');
}
}

View File

@@ -1,15 +1,16 @@
<?php <?php
namespace App\Livewire; namespace App\Livewire\Settings;
use App\Actions\License\CheckResaleLicense; use App\Actions\License\CheckResaleLicense;
use App\Models\InstanceSettings; use App\Models\InstanceSettings;
use Livewire\Component; use Livewire\Component;
class CheckLicense extends Component class License extends Component
{ {
public InstanceSettings|null $settings = null; public InstanceSettings $settings;
public string|null $instance_id = null; public string|null $instance_id = null;
protected $rules = [ protected $rules = [
'settings.resale_license' => 'nullable', 'settings.resale_license' => 'nullable',
'settings.is_resale_license_active' => 'nullable', 'settings.is_resale_license_active' => 'nullable',
@@ -20,12 +21,17 @@ class CheckLicense extends Component
'settings.is_resale_license_active' => 'Is License Active', 'settings.is_resale_license_active' => 'Is License Active',
]; ];
public function mount() public function mount () {
{ if (!isCloud()) {
abort(404);
}
$this->instance_id = config('app.id'); $this->instance_id = config('app.id');
$this->settings = InstanceSettings::get(); $this->settings = InstanceSettings::get();
} }
public function render()
{
return view('livewire.settings.license')->layout('layouts.subscription');
}
public function submit() public function submit()
{ {
$this->validate(); $this->validate();
@@ -37,7 +43,7 @@ class CheckLicense extends Component
} catch (\Throwable $e) { } catch (\Throwable $e) {
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage()); session()->flash('error', 'Something went wrong. Please contact support. <br>Error: ' . $e->getMessage());
ray($e->getMessage()); ray($e->getMessage());
return $this->redirect('/settings/license', navigate: true); return redirect()->route('settings.license');
} }
} }
} }

View File

@@ -41,7 +41,7 @@ class Change extends Component
$github_app_uuid = request()->github_app_uuid; $github_app_uuid = request()->github_app_uuid;
$this->github_app = GithubApp::where('uuid', $github_app_uuid)->first(); $this->github_app = GithubApp::where('uuid', $github_app_uuid)->first();
if (!$this->github_app) { if (!$this->github_app) {
return $this->redirectRoute('source.all', navigate: true); return redirect()->route('source.all');
} }
$settings = InstanceSettings::get(); $settings = InstanceSettings::get();
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
@@ -67,7 +67,7 @@ class Change extends Component
$type = data_get($parameters, 'type'); $type = data_get($parameters, 'type');
$destination = data_get($parameters, 'destination'); $destination = data_get($parameters, 'destination');
session()->forget('from'); session()->forget('from');
return $this->redirectRoute($back, [ return redirect()->route($back, [
'environment_name' => $environment_name, 'environment_name' => $environment_name,
'project_uuid' => $project_uuid, 'project_uuid' => $project_uuid,
'type' => $type, 'type' => $type,
@@ -117,7 +117,7 @@ class Change extends Component
{ {
try { try {
$this->github_app->delete(); $this->github_app->delete();
return $this->redirectRoute('source.all', navigate: true); return redirect()->route('source.all');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -48,7 +48,7 @@ class Create extends Component
if (session('from')) { if (session('from')) {
session(['from' => session('from') + ['source_id' => $github_app->id]]); session(['from' => session('from') + ['source_id' => $github_app->id]]);
} }
return $this->redirectRoute('source.github.show', ['github_app_uuid' => $github_app->uuid], navigate: true); return redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -3,15 +3,16 @@
namespace App\Livewire\Subscription; namespace App\Livewire\Subscription;
use App\Models\InstanceSettings; use App\Models\InstanceSettings;
use App\Providers\RouteServiceProvider;
use Livewire\Component; use Livewire\Component;
class Show extends Component class Index extends Component
{ {
public InstanceSettings $settings; public InstanceSettings $settings;
public bool $alreadySubscribed = false; public bool $alreadySubscribed = false;
public function mount() { public function mount() {
if (!isCloud()) { if (!isCloud()) {
return $this->redirect('/', navigate: true); return redirect(RouteServiceProvider::HOME);
} }
$this->settings = InstanceSettings::get(); $this->settings = InstanceSettings::get();
$this->alreadySubscribed = currentTeam()->subscription()->exists(); $this->alreadySubscribed = currentTeam()->subscription()->exists();
@@ -25,6 +26,6 @@ class Show extends Component
} }
public function render() public function render()
{ {
return view('livewire.subscription.show')->layout('layouts.subscription'); return view('livewire.subscription.index')->layout('layouts.subscription');
} }
} }

View File

@@ -30,7 +30,7 @@ class Create extends Component
]); ]);
auth()->user()->teams()->attach($team, ['role' => 'admin']); auth()->user()->teams()->attach($team, ['role' => 'admin']);
refreshSession(); refreshSession();
return $this->redirectRoute('team.index', navigate: true); return redirect()->route('team.index');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Livewire\Team;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
class Delete extends Component
{
public function delete()
{
$currentTeam = currentTeam();
$currentTeam->delete();
$currentTeam->members->each(function ($user) use ($currentTeam) {
if ($user->id === auth()->user()->id) {
return;
}
$user->teams()->detach($currentTeam);
$session = DB::table('sessions')->where('user_id', $user->id)->first();
if ($session) {
DB::table('sessions')->where('id', $session->id)->delete();
}
});
refreshSession();
return $this->redirectRoute('team.index', navigate: true);
}
}

View File

@@ -1,35 +0,0 @@
<?php
namespace App\Livewire\Team;
use App\Models\Team;
use Livewire\Component;
class Form extends Component
{
public Team $team;
protected $rules = [
'team.name' => 'required|min:3|max:255',
'team.description' => 'nullable|min:3|max:255',
];
protected $validationAttributes = [
'team.name' => 'name',
'team.description' => 'description',
];
public function mount()
{
$this->team = currentTeam();
}
public function submit()
{
$this->validate();
try {
$this->team->save();
refreshSession();
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Livewire\Team;
use App\Models\Team;
use App\Models\TeamInvitation;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
class Index extends Component
{
public $invitations = [];
public Team $team;
protected $rules = [
'team.name' => 'required|min:3|max:255',
'team.description' => 'nullable|min:3|max:255',
];
protected $validationAttributes = [
'team.name' => 'name',
'team.description' => 'description',
];
public function mount() {
$this->team = currentTeam();
if (auth()->user()->isAdminFromSession()) {
$this->invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
}
}
public function render()
{
return view('livewire.team.index');
}
public function submit()
{
$this->validate();
try {
$this->team->save();
refreshSession();
$this->dispatch('success', 'Team updated successfully.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function delete()
{
$currentTeam = currentTeam();
$currentTeam->delete();
$currentTeam->members->each(function ($user) use ($currentTeam) {
if ($user->id === auth()->user()->id) {
return;
}
$user->teams()->detach($currentTeam);
$session = DB::table('sessions')->where('user_id', $user->id)->first();
if ($session) {
DB::table('sessions')->where('id', $session->id)->delete();
}
});
refreshSession();
return redirect()->route('team.index');
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Livewire\Team\Member;
use App\Models\TeamInvitation;
use Livewire\Component;
class Index extends Component
{
public $invitations = [];
public function mount() {
if (auth()->user()->isAdminFromSession()) {
$this->invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
}
}
public function render()
{
return view('livewire.team.member.index');
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace App\Livewire\Team\Notification;
use Livewire\Component;
class Index extends Component
{
public function render()
{
return view('livewire.team.notification.index');
}
}

View File

@@ -65,7 +65,7 @@ class Create extends Component
$this->storage->team_id = currentTeam()->id; $this->storage->team_id = currentTeam()->id;
$this->storage->testConnection(); $this->storage->testConnection();
$this->storage->save(); $this->storage->save();
return $this->redirectRoute('team.storages.show', $this->storage->uuid, navigate: true); return redirect()->route('team.storage.show', $this->storage->uuid);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -43,7 +43,7 @@ class Form extends Component
{ {
try { try {
$this->storage->delete(); $this->storage->delete();
return $this->redirectRoute('team.storages.all', navigate: true); return redirect()->route('team.storage.index');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Livewire\Team\Storage;
use App\Models\S3Storage;
use Livewire\Component;
class Index extends Component
{
public $s3;
public function mount() {
$this->s3 = S3Storage::ownedByCurrentTeam()->get();
}
public function render()
{
return view('livewire.team.storage.index');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Livewire\Team\Storage;
use App\Models\S3Storage;
use Livewire\Component;
class Show extends Component
{
public $storage = null;
public function mount()
{
$this->storage = S3Storage::ownedByCurrentTeam()->whereUuid(request()->storage_uuid)->first();
if (!$this->storage) {
abort(404);
}
}
public function render()
{
return view('livewire.team.storage.show');
}
}

View File

@@ -24,7 +24,7 @@ class Index extends Component
public function mount() public function mount()
{ {
if (config('coolify.waitlist') == false) { if (config('coolify.waitlist') == false) {
return $this->redirectRoute('register', navigate: true); return redirect()->route('register');
} }
$this->waitingInLine = Waitlist::whereVerified(true)->count(); $this->waitingInLine = Waitlist::whereVerified(true)->count();
$this->users = User::count(); $this->users = User::count();

View File

@@ -382,6 +382,9 @@ class Application extends BaseModel
public function deploymentType() public function deploymentType()
{ {
if (isDev() && data_get($this, 'private_key_id') === 0) {
return 'deploy_key';
}
if (data_get($this, 'private_key_id')) { if (data_get($this, 'private_key_id')) {
return 'deploy_key'; return 'deploy_key';
} else if (data_get($this, 'source')) { } else if (data_get($this, 'source')) {
@@ -422,7 +425,7 @@ class Application extends BaseModel
public function isConfigurationChanged($save = false) public function isConfigurationChanged($save = false)
{ {
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->health_check_path . $this->health_check_port . $this->health_check_host . $this->health_check_method . $this->health_check_return_code . $this->health_check_scheme . $this->health_check_response_text . $this->health_check_interval . $this->health_check_timeout . $this->health_check_retries . $this->health_check_start_period . $this->health_check_enabled . $this->limits_memory . $this->limits_swap . $this->limits_swappiness . $this->limits_reservation . $this->limits_cpus . $this->limits_cpuset . $this->limits_cpu_shares . $this->dockerfile . $this->dockerfile_location . $this->custom_labels; $newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->health_check_path . $this->health_check_port . $this->health_check_host . $this->health_check_method . $this->health_check_return_code . $this->health_check_scheme . $this->health_check_response_text . $this->health_check_interval . $this->health_check_timeout . $this->health_check_retries . $this->health_check_start_period . $this->health_check_enabled . $this->limits_memory . $this->limits_swap . $this->limits_swappiness . $this->limits_reservation . $this->limits_cpus . $this->limits_cpuset . $this->limits_cpu_shares . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
if ($this->pull_request_id === 0) { if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
$newConfigHash .= json_encode($this->environment_variables->all()); $newConfigHash .= json_encode($this->environment_variables->all());
} else { } else {
$newConfigHash .= json_encode($this->environment_variables_preview->all()); $newConfigHash .= json_encode($this->environment_variables_preview->all());
@@ -633,7 +636,7 @@ class Application extends BaseModel
'memswap_limit' => $this->limits_memory_swap, 'memswap_limit' => $this->limits_memory_swap,
'mem_swappiness' => $this->limits_memory_swappiness, 'mem_swappiness' => $this->limits_memory_swappiness,
'mem_reservation' => $this->limits_memory_reservation, 'mem_reservation' => $this->limits_memory_reservation,
'cpus' => (int) $this->limits_cpus, 'cpus' => (float) $this->limits_cpus,
'cpuset' => $this->limits_cpuset, 'cpuset' => $this->limits_cpuset,
'cpu_shares' => $this->limits_cpu_shares, 'cpu_shares' => $this->limits_cpu_shares,
] ]
@@ -752,7 +755,6 @@ class Application extends BaseModel
// if (count($this->ports_mappings_array) > 0) { // if (count($this->ports_mappings_array) > 0) {
// $deployment->addLogEntry('Application has ports mapped to the host system, rolling update is not supported.'); // $deployment->addLogEntry('Application has ports mapped to the host system, rolling update is not supported.');
$containers = getCurrentApplicationContainerStatus($server, $this->id, $pullRequestId); $containers = getCurrentApplicationContainerStatus($server, $this->id, $pullRequestId);
ray($containers);
// if ($pullRequestId === 0) { // if ($pullRequestId === 0) {
// $containers = $containers->filter(function ($container) use ($containerName) { // $containers = $containers->filter(function ($container) use ($containerName) {
// return data_get($container, 'Names') !== $containerName; // return data_get($container, 'Names') !== $containerName;
@@ -859,7 +861,9 @@ class Application extends BaseModel
} }
$private_key = base64_encode($private_key); $private_key = base64_encode($private_key);
$git_clone_command_base = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$customRepository} {$baseDir}"; $git_clone_command_base = "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$customPort} -o Port={$customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" {$git_clone_command} {$customRepository} {$baseDir}";
if (!$only_checkout) { if ($only_checkout) {
$git_clone_command = $git_clone_command_base;
} else {
$git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command_base); $git_clone_command = $this->setGitImportSettings($deployment_uuid, $git_clone_command_base);
} }
if ($exec_in_docker) { if ($exec_in_docker) {
@@ -979,6 +983,7 @@ class Application extends BaseModel
// $fileList->push(".$prComposeFile"); // $fileList->push(".$prComposeFile");
// } // }
$commands = collect([ $commands = collect([
"rm -rf /tmp/{$uuid}",
"mkdir -p /tmp/{$uuid} && cd /tmp/{$uuid}", "mkdir -p /tmp/{$uuid} && cd /tmp/{$uuid}",
$cloneCommand, $cloneCommand,
"git sparse-checkout init --cone", "git sparse-checkout init --cone",

View File

@@ -48,7 +48,7 @@ class S3Storage extends BaseModel
if ($this->unusable_email_sent === false && is_transactional_emails_active()) { if ($this->unusable_email_sent === false && is_transactional_emails_active()) {
$mail = new MailMessage(); $mail = new MailMessage();
$mail->subject('Coolify: S3 Storage Connection Error'); $mail->subject('Coolify: S3 Storage Connection Error');
$mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('team.storages.show', ['storage_uuid' => $this->uuid])]); $mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('team.storage.show', ['storage_uuid' => $this->uuid])]);
$users = collect([]); $users = collect([]);
$members = $this->team->members()->get(); $members = $this->team->members()->get();
foreach ($members as $user) { foreach ($members as $user) {

View File

@@ -154,17 +154,21 @@ class Server extends BaseModel
if ($this->skipServer()) { if ($this->skipServer()) {
return false; return false;
} }
$serverUptimeCheckNumber = $this->unreachable_count;
if ($this->unreachable_count < $tries) { if ($this->unreachable_count < $tries) {
$serverUptimeCheckNumber = $this->unreachable_count + 1; $serverUptimeCheckNumber = $this->unreachable_count + 1;
} }
if ($this->unreachable_count > $tries) {
$serverUptimeCheckNumber = $tries;
}
$serverUptimeCheckNumberMax = $tries; $serverUptimeCheckNumberMax = $tries;
ray('server: ' . $this->name); // ray('server: ' . $this->name);
ray('serverUptimeCheckNumber: ' . $serverUptimeCheckNumber); // ray('serverUptimeCheckNumber: ' . $serverUptimeCheckNumber);
ray('serverUptimeCheckNumberMax: ' . $serverUptimeCheckNumberMax); // ray('serverUptimeCheckNumberMax: ' . $serverUptimeCheckNumberMax);
$result = $this->validateConnection(); $result = $this->validateConnection();
ray($result);
if ($result) { if ($result) {
if ($this->unreachable_notification_sent === true) { if ($this->unreachable_notification_sent === true) {
$this->update(['unreachable_notification_sent' => false]); $this->update(['unreachable_notification_sent' => false]);

View File

@@ -27,7 +27,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
public function toMail(): MailMessage public function toMail(): MailMessage
{ {
$mail = new MailMessage(); $mail = new MailMessage();
$mail->subject("Coolify: A service ({$this->name}) has been restarted automatically on {$this->server->name}"); $mail->subject("Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}");
$mail->view('emails.container-restarted', [ $mail->view('emails.container-restarted', [
'containerName' => $this->name, 'containerName' => $this->name,
'serverName' => $this->server->name, 'serverName' => $this->server->name,
@@ -38,12 +38,12 @@ class ContainerRestarted extends Notification implements ShouldQueue
public function toDiscord(): string public function toDiscord(): string
{ {
$message = "Coolify: A service ({$this->name}) has been restarted automatically on {$this->server->name}"; $message = "Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}";
return $message; return $message;
} }
public function toTelegram(): array public function toTelegram(): array
{ {
$message = "Coolify: A service ({$this->name}) has been restarted automatically on {$this->server->name}"; $message = "Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}";
$payload = [ $payload = [
"message" => $message, "message" => $message,
]; ];

View File

@@ -26,7 +26,7 @@ class ContainerStopped extends Notification implements ShouldQueue
public function toMail(): MailMessage public function toMail(): MailMessage
{ {
$mail = new MailMessage(); $mail = new MailMessage();
$mail->subject("Coolify: A service ({$this->name}) has been stopped on {$this->server->name}"); $mail->subject("Coolify: A resource has been stopped unexpectedly on {$this->server->name}");
$mail->view('emails.container-stopped', [ $mail->view('emails.container-stopped', [
'containerName' => $this->name, 'containerName' => $this->name,
'serverName' => $this->server->name, 'serverName' => $this->server->name,
@@ -37,12 +37,12 @@ class ContainerStopped extends Notification implements ShouldQueue
public function toDiscord(): string public function toDiscord(): string
{ {
$message = "Coolify: A service ({$this->name}) has been stopped on {$this->server->name}"; $message = "Coolify: A resource has been stopped unexpectedly on {$this->server->name}";
return $message; return $message;
} }
public function toTelegram(): array public function toTelegram(): array
{ {
$message = "Coolify: A service ($this->name} has been stopped on {$this->server->name}"; $message = "Coolify: A resource has been stopped unexpectedly on {$this->server->name}";
$payload = [ $payload = [
"message" => $message, "message" => $message,
]; ];

View File

@@ -31,9 +31,9 @@ class FortifyServiceProvider extends ServiceProvider
{ {
// First user (root) will be redirected to /settings instead of / on registration. // First user (root) will be redirected to /settings instead of / on registration.
if ($request->user()->currentTeam->id === 0) { if ($request->user()->currentTeam->id === 0) {
return redirect('/settings'); return redirect()->route('settings.index');
} }
return redirect('/'); return redirect(RouteServiceProvider::HOME);
} }
}); });
} }

View File

@@ -41,11 +41,11 @@ function queue_application_deployment(int $application_id, string $deployment_uu
dispatch(new ApplicationDeploymentNewJob( dispatch(new ApplicationDeploymentNewJob(
deployment: $deployment, deployment: $deployment,
application: Application::find($application_id) application: Application::find($application_id)
))->onConnection('long-running')->onQueue('long-running'); ));
} else { } else {
dispatch(new ApplicationDeploymentJob( dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $deployment->id, application_deployment_queue_id: $deployment->id,
))->onConnection('long-running')->onQueue('long-running'); ));
} }
} }
@@ -57,11 +57,11 @@ function queue_next_deployment(Application $application, bool $isNew = false)
dispatch(new ApplicationDeploymentNewJob( dispatch(new ApplicationDeploymentNewJob(
deployment: $next_found, deployment: $next_found,
application: $application application: $application
))->onConnection('long-running')->onQueue('long-running'); ));
} else { } else {
dispatch(new ApplicationDeploymentJob( dispatch(new ApplicationDeploymentJob(
application_deployment_queue_id: $next_found->id, application_deployment_queue_id: $next_found->id,
))->onConnection('long-running')->onQueue('long-running'); ));
} }
} }
} }

View File

@@ -216,64 +216,72 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels = collect([]); $labels = collect([]);
$labels->push('traefik.enable=true'); $labels->push('traefik.enable=true');
foreach ($domains as $loop => $domain) { foreach ($domains as $loop => $domain) {
$uuid = new Cuid2(7); try {
$url = Url::fromString($domain); $uuid = new Cuid2(7);
$host = $url->getHost(); $url = Url::fromString($domain);
$path = $url->getPath(); $host = $url->getHost();
$schema = $url->getScheme(); $path = $url->getPath();
$port = $url->getPort(); $schema = $url->getScheme();
if (is_null($port) && !is_null($onlyPort)) { $port = $url->getPort();
$port = $onlyPort; if (is_null($port) && !is_null($onlyPort)) {
} $port = $onlyPort;
$http_label = "{$uuid}-{$loop}-http"; }
$https_label = "{$uuid}-{$loop}-https"; $http_label = "http-{$loop}-{$uuid}";
$https_label = "https-{$loop}-{$uuid}";
if ($schema === 'https') { if ($schema === 'https') {
// Set labels for https // Set labels for https
$labels->push("traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)"); $labels->push("traefik.http.routers.{$https_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
$labels->push("traefik.http.routers.{$https_label}.entryPoints=https"); $labels->push("traefik.http.routers.{$https_label}.entryPoints=https");
$labels->push("traefik.http.routers.{$https_label}.middlewares=gzip"); $labels->push("traefik.http.routers.{$https_label}.middlewares=gzip");
if ($port) { if ($port) {
$labels->push("traefik.http.routers.{$https_label}.service={$https_label}"); $labels->push("traefik.http.routers.{$https_label}.service={$https_label}");
$labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port"); $labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port");
} }
if ($path !== '/') { if ($path !== '/') {
$labels->push("traefik.http.routers.{$https_label}.middlewares={$https_label}-stripprefix"); $labels->push("traefik.http.routers.{$https_label}.middlewares={$https_label}-stripprefix");
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}"); $labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
} }
$labels->push("traefik.http.routers.{$https_label}.tls=true"); $labels->push("traefik.http.routers.{$https_label}.tls=true");
$labels->push("traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt"); $labels->push("traefik.http.routers.{$https_label}.tls.certresolver=letsencrypt");
// Set labels for http (redirect to https) // Set labels for http (redirect to https)
$labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)"); $labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
$labels->push("traefik.http.routers.{$http_label}.entryPoints=http"); $labels->push("traefik.http.routers.{$http_label}.entryPoints=http");
if ($is_force_https_enabled) { if ($port) {
$labels->push("traefik.http.routers.{$http_label}.middlewares=redirect-to-https"); $labels->push("traefik.http.services.{$http_label}.loadbalancer.server.port=$port");
} $labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
} else { }
// Set labels for http if ($is_force_https_enabled) {
$labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)"); $labels->push("traefik.http.routers.{$http_label}.middlewares=redirect-to-https");
$labels->push("traefik.http.routers.{$http_label}.entryPoints=http"); }
$labels->push("traefik.http.routers.{$http_label}.middlewares=gzip"); } else {
if ($port) { // Set labels for http
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}"); $labels->push("traefik.http.routers.{$http_label}.rule=Host(`{$host}`) && PathPrefix(`{$path}`)");
$labels->push("traefik.http.services.{$http_label}.loadbalancer.server.port=$port"); $labels->push("traefik.http.routers.{$http_label}.entryPoints=http");
} $labels->push("traefik.http.routers.{$http_label}.middlewares=gzip");
if ($path !== '/') { if ($port) {
$labels->push("traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix"); $labels->push("traefik.http.services.{$http_label}.loadbalancer.server.port=$port");
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}"); $labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
}
if ($path !== '/') {
$labels->push("traefik.http.routers.{$http_label}.middlewares={$http_label}-stripprefix");
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
}
} }
} catch (\Throwable $e) {
continue;
} }
} }
return $labels; return $labels->sort();
} }
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
{ {
$ports = $application->settings->is_static ? [80] : $application->ports_exposes_array; $ports = $application->settings->is_static ? [80] : $application->ports_exposes_array;
$onlyPort = null; $onlyPort = null;
if (count($ports) === 1) { if (count($ports) > 0) {
$onlyPort = $ports[0]; $onlyPort = $ports[0];
} }
$pull_request_id = data_get($preview, 'pull_request_id', 0); $pull_request_id = data_get($preview, 'pull_request_id', 0);

View File

@@ -101,7 +101,6 @@ function generate_default_proxy_configuration(Server $server)
$labels = [ $labels = [
"traefik.enable=true", "traefik.enable=true",
"traefik.http.routers.traefik.entrypoints=http", "traefik.http.routers.traefik.entrypoints=http",
"traefik.http.routers.traefik.middlewares=traefik-basic-auth@file",
"traefik.http.routers.traefik.service=api@internal", "traefik.http.routers.traefik.service=api@internal",
"traefik.http.services.traefik.loadbalancer.server.port=8080", "traefik.http.services.traefik.loadbalancer.server.port=8080",
// Global Middlewares // Global Middlewares
@@ -156,7 +155,7 @@ function generate_default_proxy_configuration(Server $server)
], ],
]; ];
if (isDev()) { if (isDev()) {
$config['services']['traefik']['command'][] = "--log.level=debug"; // $config['services']['traefik']['command'][] = "--log.level=debug";
$config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log"; $config['services']['traefik']['command'][] = "--accesslog.filepath=/traefik/access.log";
$config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100"; $config['services']['traefik']['command'][] = "--accesslog.bufferingsize=100";
} }

View File

@@ -102,6 +102,7 @@ function refreshSession(?Team $team = null): void
} }
function handleError(?Throwable $error = null, ?Livewire\Component $livewire = null, ?string $customErrorMessage = null) function handleError(?Throwable $error = null, ?Livewire\Component $livewire = null, ?string $customErrorMessage = null)
{ {
ray($error);
if ($error instanceof TooManyRequestsException) { if ($error instanceof TooManyRequestsException) {
if (isset($livewire)) { if (isset($livewire)) {
return $livewire->dispatch('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds."); return $livewire->dispatch('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.");
@@ -227,11 +228,15 @@ function base_ip(): string
} }
function getFqdnWithoutPort(String $fqdn) function getFqdnWithoutPort(String $fqdn)
{ {
$url = Url::fromString($fqdn); try {
$host = $url->getHost(); $url = Url::fromString($fqdn);
$scheme = $url->getScheme(); $host = $url->getHost();
$path = $url->getPath(); $scheme = $url->getScheme();
return "$scheme://$host$path"; $path = $url->getPath();
return "$scheme://$host$path";
} catch (\Throwable $e) {
return $fqdn;
}
} }
/** /**
* If fqdn is set, return it, otherwise return public ip. * If fqdn is set, return it, otherwise return public ip.
@@ -403,7 +408,7 @@ function generateFqdn(Server $server, string $random)
} }
function sslip(Server $server) function sslip(Server $server)
{ {
if (isDev()) { if (isDev() && $server->id === 0) {
return "http://127.0.0.1.sslip.io"; return "http://127.0.0.1.sslip.io";
} }
if ($server->ip === 'host.docker.internal') { if ($server->ip === 'host.docker.internal') {
@@ -1030,7 +1035,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} }
data_set($service, 'labels', $serviceLabels->toArray()); data_set($service, 'labels', $serviceLabels->toArray());
data_forget($service, 'is_database'); data_forget($service, 'is_database');
data_set($service, 'restart', RESTART_MODE); if (!data_get($service, 'restart')) {
data_set($service, 'restart', RESTART_MODE);
}
data_set($service, 'container_name', $containerName); data_set($service, 'container_name', $containerName);
data_forget($service, 'volumes.*.content'); data_forget($service, 'volumes.*.content');
data_forget($service, 'volumes.*.isDirectory'); data_forget($service, 'volumes.*.isDirectory');
@@ -1394,6 +1401,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$key = $value; $key = $value;
$defaultValue = null; $defaultValue = null;
} }
$foundEnv = EnvironmentVariable::where([
'key' => $key,
'application_id' => $resource->id,
'is_preview' => false,
])->first();
if ($foundEnv) { if ($foundEnv) {
$defaultValue = data_get($foundEnv, 'value'); $defaultValue = data_get($foundEnv, 'value');
} }
@@ -1463,7 +1475,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
} }
data_set($service, 'labels', $serviceLabels->toArray()); data_set($service, 'labels', $serviceLabels->toArray());
data_forget($service, 'is_database'); data_forget($service, 'is_database');
data_set($service, 'restart', RESTART_MODE); if (!data_get($service, 'restart')) {
data_set($service, 'restart', RESTART_MODE);
}
data_set($service, 'container_name', $containerName); data_set($service, 'container_name', $containerName);
data_forget($service, 'volumes.*.content'); data_forget($service, 'volumes.*.content');
data_forget($service, 'volumes.*.isDirectory'); data_forget($service, 'volumes.*.isDirectory');
@@ -1520,6 +1534,9 @@ function parseEnvVariable(Str|string $value)
$command = $value->after('SERVICE_')->before('_'); $command = $value->after('SERVICE_')->before('_');
$forService = $value->after('SERVICE_')->after('_')->before('_'); $forService = $value->after('SERVICE_')->after('_')->before('_');
$port = $value->afterLast('_'); $port = $value->afterLast('_');
if (filter_var($port, FILTER_VALIDATE_INT) === false) {
$port = null;
}
} else { } else {
// SERVICE_BASE64_64_UMAMI // SERVICE_BASE64_64_UMAMI
$command = $value->after('SERVICE_')->beforeLast('_'); $command = $value->after('SERVICE_')->beforeLast('_');

991
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -184,19 +184,8 @@ return [
'connection' => 'redis', 'connection' => 'redis',
'queue' => ['default'], 'queue' => ['default'],
'balance' => 'auto', 'balance' => 'auto',
'autoScalingStrategy' => 'time', // 'autoScalingStrategy' => 'time',
'maxProcesses' => 1, // 'maxProcesses' => 1,
'maxTime' => 0,
'maxJobs' => 0,
'memory' => 128,
'tries' => 1,
'timeout' => 300,
'nice' => 0,
],
'long-running' => [
'connection' => 'redis',
'queue' => ['long-running'],
'balance' => 'auto',
'maxTime' => 0, 'maxTime' => 0,
'maxJobs' => 0, 'maxJobs' => 0,
'memory' => 128, 'memory' => 128,
@@ -209,27 +198,15 @@ return [
'environments' => [ 'environments' => [
'production' => [ 'production' => [
's6' => [ 's6' => [
'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 2),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
],
'long-running' => [
'autoScalingStrategy' => 'size', 'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6), 'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1), 'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1), 'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
], ],
],
],
'local' => [ 'local' => [
's6' => [ 's6' => [
'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 2),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
],
'long-running' => [
'autoScalingStrategy' => 'size', 'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6), 'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1), 'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),

View File

@@ -33,14 +33,6 @@ return [
'sync' => [ 'sync' => [
'driver' => 'sync', 'driver' => 'sync',
], ],
'long-running' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'long-running',
'retry_after' => 3600,
'block_for' => null,
'after_commit' => true,
],
'database' => [ 'database' => [
'driver' => 'database', 'driver' => 'database',
'table' => 'jobs', 'table' => 'jobs',
@@ -73,7 +65,7 @@ return [
'driver' => 'redis', 'driver' => 'redis',
'connection' => 'default', 'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'), 'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 300, 'retry_after' => 3600,
'block_for' => null, 'block_for' => null,
'after_commit' => true, 'after_commit' => true,
], ],

View File

@@ -7,7 +7,7 @@ return [
// The release version of your application // The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.171', 'release' => '4.0.0-beta.185',
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'), 'environment' => config('app.env'),

Some files were not shown because too many files have changed in this diff Show More