Compare commits

...

158 Commits

Author SHA1 Message Date
Andras Bacsai
69fc4c7f52 Merge pull request #3031 from coollabsio/next
v4.0.0-beta.323
2024-08-08 14:37:40 +02:00
Andras Bacsai
2e06acf653 servercheckjob should not overlap 2024-08-08 14:02:21 +02:00
Andras Bacsai
d635799b80 chore: Update version to 4.0.0-beta.323 2024-08-08 14:02:07 +02:00
Andras Bacsai
0590456b62 Merge pull request #3029 from coollabsio/next
v4.0.0-beta.322
2024-08-08 13:34:18 +02:00
Andras Bacsai
e2726d9dfb fix: manual update process 2024-08-08 13:32:15 +02:00
Andras Bacsai
2be92a1d29 chore: Update version to 4.0.0-beta.322 2024-08-08 13:20:38 +02:00
Andras Bacsai
d75ed0b208 refactor: Import ProxyTypes enum and use TRAEFIK instead of TRAEFIK_V2 2024-08-08 13:20:24 +02:00
Andras Bacsai
8fa1fcf96e refactor: Update Server model getContainers method to use collect() for containers and containerReplicates 2024-08-08 13:20:10 +02:00
Andras Bacsai
f089185c39 Merge pull request #3027 from coollabsio/next
v4.0.0-beta.321
2024-08-08 12:52:33 +02:00
Andras Bacsai
fa28e952de refactor: Update cron expressions and add helper text for scheduled tasks 2024-08-08 12:50:49 +02:00
Andras Bacsai
c2e431d631 refactor: Update StandalonePostgresql database initialization and backup handling 2024-08-08 12:44:47 +02:00
Andras Bacsai
6fb88ed479 fix: scheduledbackup not found 2024-08-08 12:19:27 +02:00
Andras Bacsai
58c6d066f2 chore: Update version to 4.0.0-beta.321 2024-08-08 12:19:14 +02:00
Andras Bacsai
671b22e964 Merge pull request #2941 from coollabsio/next
v4.0.0-beta.320
2024-08-08 11:27:29 +02:00
Andras Bacsai
777bff6553 refactor: Update Traefik labels on init and cleanup unnecessary dynamic proxy configuration 2024-08-08 11:15:33 +02:00
Andras Bacsai
d3b3c2f7fd fix: Add missing middleware for server check job 2024-08-08 10:28:34 +02:00
Andras Bacsai
0b000919cf feat: able to generate only the required labels for resources 2024-08-07 18:27:50 +02:00
Andras Bacsai
92f90d4e52 Merge pull request #2863 from lorenzomigliorero/feat/labels-by-proxy-type
Generate labels by proxy type
2024-08-07 18:06:51 +02:00
Andras Bacsai
59702c6dbc refactor: Update ProxyTypes enum values to use TRAEFIK instead of TRAEFIK_V2 2024-08-07 17:52:51 +02:00
Andras Bacsai
13e8d3c17c fix: update Traefik labels on init 2024-08-07 17:52:43 +02:00
Andras Bacsai
5d384b1149 feat: Support map fields in Directus 2024-08-07 17:43:41 +02:00
Andras Bacsai
7b31955409 Merge pull request #2906 from Loque-/Loque--patch-1
Support map fields in Directus
2024-08-07 17:43:03 +02:00
Andras Bacsai
efb4049966 feat: Improve homepage service template 2024-08-07 12:58:36 +02:00
Andras Bacsai
6da359bc60 fix: empty string content should be saved as a file 2024-08-07 12:58:32 +02:00
Andras Bacsai
f5f3c77d9e Merge pull request #3010 from Telokis/improvate-homepage-template
feat: Improve homepage service template
2024-08-07 12:17:46 +02:00
Andras Bacsai
dc5c324f9c feat: Update Upgrade component to check for latest version of Coolify 2024-08-07 12:16:06 +02:00
Andras Bacsai
d283be4917 disable php code styling for now 2024-08-07 12:12:09 +02:00
Andras Bacsai
f0278bc33d fix: show latest version on manual update view 2024-08-07 12:10:31 +02:00
Andras Bacsai
052565f4e8 Merge pull request #3011 from stooit/feat/enhance-services-api
Return applications & databases in service API.
2024-08-07 12:02:18 +02:00
Andras Bacsai
64146a46fc chore: Update UpdateCoolifyJob to dispatch CheckForUpdatesJob synchronously 2024-08-07 11:58:53 +02:00
Andras Bacsai
5a82395bb7 feat: Update auto update and update check frequencies in settings 2024-08-07 11:55:16 +02:00
Andras Bacsai
d3085e1ade feat: Add manual update check functionality to settings page 2024-08-07 11:42:55 +02:00
Andras Bacsai
af41ed26ba chore: Update UpdateCoolifyJob timeout to 10 minutes 2024-08-07 11:10:02 +02:00
Andras Bacsai
961ba49d89 fix: Handle null and empty disk usage in DockerCleanupJob 2024-08-07 10:54:22 +02:00
Andras Bacsai
a2150f2f7d refactor: Remove unused code and optimize CheckForUpdatesJob 2024-08-07 10:50:12 +02:00
Andras Bacsai
46b549ab8d chore: Update workflows to include PR build and merge manifest steps 2024-08-07 10:02:01 +02:00
Andras Bacsai
e4c3d61b76 fixes 2024-08-07 09:50:29 +02:00
Andras Bacsai
869f0878c2 fix (cloud): cloud instance should get latest servics on update (all servers) 2024-08-07 09:50:12 +02:00
Stuart Rowlands
dfb6d4da3d Return applications & databases in service API. 2024-08-07 17:20:55 +10:00
Telokis
5d4a379e8d Improve homepage service template 2024-08-07 01:06:54 +02:00
Andras Bacsai
73a265107a Merge branch 'next' of github.com:coollabsio/coolify into next 2024-08-06 14:38:41 +02:00
Andras Bacsai
b77171d2f2 fix: settings view
feat: add separate views for settings
2024-08-06 14:36:56 +02:00
andrasbacsai
8b817dad87 Fix styling 2024-08-06 12:04:23 +00:00
Andras Bacsai
c4436aadfa Merge pull request #3000 from ayntk-ai/update-settings-improvements
Feat: Updated Check Frequency and Auto Update Frequency settings
2024-08-06 14:03:41 +02:00
Andras Bacsai
c9a7af0ffa chore: Update navbar and configuration to improve service status check functionality 2024-08-06 14:02:24 +02:00
Andras Bacsai
c648e0dff9 chore: Update navbar to include service status check 2024-08-06 13:50:32 +02:00
Andras Bacsai
e897eb2999 fix: Stop resources gracefully 2024-08-06 13:27:06 +02:00
Andras Bacsai
35e62a3003 chore: Update server form to use password input for IP Address/Domain field 2024-08-06 13:18:57 +02:00
Andras Bacsai
b66c511160 refactor: Update timeout for graceful_shutdown_container in ApplicationDeploymentJob 2024-08-06 13:07:32 +02:00
Andras Bacsai
a866bf437d Merge branch 'next' of github.com:coollabsio/coolify into next 2024-08-06 13:07:03 +02:00
Andras Bacsai
a4f107e191 fix: graceful shutdown 2024-08-06 13:05:34 +02:00
andrasbacsai
f22d7741a3 Fix styling 2024-08-06 10:13:34 +00:00
Andras Bacsai
f4d64e121c Merge pull request #2969 from alexzvn/feat/graceful-shutdown
feat: add graceful shutdown
2024-08-06 12:13:00 +02:00
Andras Bacsai
818d5e1159 fix: minecraft server template fixed 2024-08-06 12:10:29 +02:00
ayntk-ai
7eacdf23f9 fix database migration 2024-08-06 12:00:34 +02:00
ayntk-ai
0459baa55e Update Kernel.php 2024-08-06 11:48:43 +02:00
ayntk-ai
a426c00a03 should work :) 2024-08-06 11:47:48 +02:00
ayntk-ai
fd36e143e0 final conflict hopfully :) 2024-08-06 11:46:26 +02:00
ayntk-ai
2243a3304b remove duplicates 2024-08-06 11:43:55 +02:00
ayntk-ai
bb23141138 add backup line twice 2024-08-06 11:42:01 +02:00
ayntk-ai
5f1e1c0ac4 fix another conflict 2024-08-06 11:41:24 +02:00
Andras Bacsai
ef8be5f133 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-08-06 11:40:16 +02:00
Andras Bacsai
f205c0ab9e Merge pull request #2981 from kunumigab/main
Added minecraft-server (java) as template
2024-08-06 11:40:30 +02:00
ayntk-ai
c4cf116e6e more fixes 2024-08-06 11:38:30 +02:00
ayntk-ai
f7b1aaca92 fix fix 2024-08-06 11:35:22 +02:00
Andras Bacsai
0ce41d2c1c chore: Update registration view to display a notice for first user that it will be an admin 2024-08-06 11:34:51 +02:00
ayntk-ai
d9edb1c72f fix 2024-08-06 11:32:37 +02:00
ayntk-ai
93322dc3cf updated helper text 2024-08-06 11:29:02 +02:00
Andras Bacsai
22f04e4708 refactor: Remove unnecessary debug statement in ApplicationDeploymentJob 2024-08-06 11:27:10 +02:00
ayntk-ai
d9a079c289 fix conflict in kernel.php 2024-08-06 11:25:57 +02:00
Andras Bacsai
f4690bf15a Merge pull request #2958 from filiabel/formbricks-template-update
chore: update Formbricks template with additional env variables
2024-08-06 11:25:46 +02:00
Andras Bacsai
a81faa68b8 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-08-06 11:16:55 +02:00
Andras Bacsai
4b388b463d Merge pull request #2960 from J-o-n-a-t-h-a-n-M-u-e-l-l-e-r/fix-plausible-totp-key
Fixes Plausible TOTP key by changing it from BASE64 to REALBASE64_32
2024-08-06 11:16:58 +02:00
Andras Bacsai
3c98b558f6 fix: do not use port exposes on docker compose buildpacks 2024-08-06 11:16:49 +02:00
Andras Bacsai
e3c7c615c6 refactor: Cleanup unnecessary dynamic proxy configuration in Init command 2024-08-06 10:53:13 +02:00
Andras Bacsai
74e8a4a703 refactor: Simplify log drain installation and stop log drain if necessary 2024-08-06 10:52:47 +02:00
Andras Bacsai
c5de1a25c3 refactor: Remove unnecessary debug statement in ServerCheckJob 2024-08-06 09:45:48 +02:00
ayntk-ai
d4cb7e25dc renamed database migration file 2024-08-05 21:04:47 +02:00
ayntk-ai
50ede5cab9 remove comments and remove duplicated scheduling 2024-08-05 20:57:27 +02:00
ayntk-ai
b64d4881cb made helper more clear 2024-08-05 20:33:20 +02:00
ayntk-ai
4dfec6771c added defaults, remove duplicated cron validation 2024-08-05 20:31:06 +02:00
Andras Bacsai
92ebc3f0c6 refactor 2024-08-05 20:08:37 +02:00
Andras Bacsai
3e0821e471 refactor 2024-08-05 20:07:08 +02:00
ayntk-ai
38976dac12 fixes and check for valid cron expressions 2024-08-05 20:05:38 +02:00
Andras Bacsai
ea5101c814 fix: file storages (dir/file mount) handled properly 2024-08-05 20:00:57 +02:00
Andras Bacsai
8133a8b770 fix: dir mounts should have proper dirs 2024-08-05 18:38:13 +02:00
Andras Bacsai
3269ca3eb8 chore: Add comment explaining the purpose of disconnecting the network in cleanup_unused_network_from_coolify_proxy() 2024-08-05 18:27:54 +02:00
Andras Bacsai
012d660886 fix: remove network if it is only connected to coolify proxy itself 2024-08-05 18:16:29 +02:00
Andras Bacsai
ddb4b4d215 refactor: Simplify log drain installation in ServerCheckJob 2024-08-05 16:45:30 +02:00
Andras Bacsai
2725a93bfd fix: only run logdrain if necessary 2024-08-05 16:44:50 +02:00
ayntk-ai
27e82f0bde implement logic, jobs and add DB migrate 2024-08-05 16:31:41 +02:00
Andras Bacsai
5755965b4f chore: add pull_request image builds to GH actions 2024-08-05 16:09:49 +02:00
Andras Bacsai
e4b92bb660 feat: new server checking job
feat: show if the server  has problems on ui
2024-08-05 15:48:15 +02:00
Andras Bacsai
bc48b42ff1 refactor: Remove unused code for checking server status in Heading.php 2024-08-05 15:02:33 +02:00
Andras Bacsai
94b2d67a6e refactor: Update livewire polling interval in heading.blade.php 2024-08-05 15:02:23 +02:00
ayntk-ai
7d6a895449 Updated Configuration Settings 2024-08-05 14:44:20 +02:00
Andras Bacsai
39e3ea9f07 fix: async remote command 2024-08-05 13:51:34 +02:00
Andras Bacsai
465b254813 fix: if usagebefore cannot be determined, cleanup docker with force 2024-08-05 13:45:53 +02:00
Andras Bacsai
e2bc3f4841 refactor: Remove commented out code for cleaning up networks in CleanupDocker.php 2024-08-05 13:45:33 +02:00
Andras Bacsai
b38a651a08 feat: coolify init should cleanup stuck networks in proxy 2024-08-05 13:45:24 +02:00
Andras Bacsai
76c39a987c fix: service_url should not have a trailing slash 2024-08-05 13:42:51 +02:00
Andras Bacsai
1d8c496906 feat: delete team in cloud without subscription 2024-08-05 12:03:36 +02:00
Andras Bacsai
820693ac22 fix: plausible template 2024-08-05 11:50:52 +02:00
Andras Bacsai
d7b45a6dd2 fix: remove lazy load from scheduled tasks 2024-08-05 11:30:48 +02:00
Gabriel Peralta
cb2fc68dde Update minecraft.yaml 2024-08-03 21:13:42 -04:00
Andras Bacsai
e6e48c5812 fix: only append docker network if service/app is running 2024-08-01 13:47:58 +02:00
Andras Bacsai
fd855847ff refactor: Adjust keep_days in CleanupDatabase.php based on environment 2024-08-01 09:07:54 +02:00
Andras Bacsai
35dbced3c5 refactor: Update CleanupDatabase.php to adjust keep_days based on environment 2024-08-01 08:52:58 +02:00
Andras Bacsai
e8b2d8bf03 chore: Update version to 4.0.0-beta.320 2024-08-01 08:52:13 +02:00
Gabriel Peralta
9887d5eedd Added minecraft-server (java) as template 2024-07-31 00:50:13 -04:00
Alexzvn
1feb8488a3 cleanup 2024-07-29 08:09:27 +00:00
Alexzvn
342ef4d367 add graceful shutdown 2024-07-29 07:57:13 +00:00
Andras Bacsai
85d080a042 update servicetemplates 2024-07-26 20:32:29 +02:00
Andras Bacsai
69c48a511e Merge branch 'main' into next 2024-07-26 20:31:48 +02:00
Jonathan Müller
a2e439686d Fixes Plausible TOTP key by changing it from BASE64 to REALBASE64_32 2024-07-26 16:30:36 +00:00
Filip Gornitzka Abelson
219b21767b chore: update formbricks template 2024-07-26 17:27:20 +02:00
Andras Bacsai
281c6e39a5 fix: members of root team should not see instance admin stuff 2024-07-26 14:54:24 +02:00
Andras Bacsai
95548b9d9b enhance: Add required CRON_SECRET to Formbricks template 2024-07-26 14:05:48 +02:00
Andras Bacsai
2ed1529b95 Merge pull request #2954 from LaurenceJJones/formbricks_env_var_required
enhance: Formbricks template add required CRON_SECRET
2024-07-26 14:05:34 +02:00
Laurence Jones
85f037702a Update formbricks.yaml 2024-07-26 13:03:28 +01:00
Laurence Jones
2caa91d772 enhance: Formbricks template add required CRON_SECRET
formbricks/formbricks#2874

A new required env var has been added, using special magic to generate a random password as the value to abstract away from user.
2024-07-26 12:25:12 +01:00
Andras Bacsai
d7350fad76 fix: several shared environment variables in one value, parsed correctly 2024-07-26 13:22:44 +02:00
Andras Bacsai
dcfb716711 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-07-26 11:58:43 +02:00
Andras Bacsai
0e0c81c32b fix: update SERVICE_FQDN/URL env variables when you change the domain 2024-07-26 11:58:40 +02:00
Andras Bacsai
802cab4a70 fix: domain update on services on the UI 2024-07-26 11:58:14 +02:00
Andras Bacsai
2c0b40ac8c fix: by default volumes that we cannot determine if they are directories or files are treated as directories 2024-07-26 11:57:49 +02:00
Andras Bacsai
001e8493ae Merge pull request #2946 from vitalybaev/fixes-link-to-feature-requests
Fixes link to feature requests in GitHub's issue template
2024-07-26 10:01:30 +02:00
Vitaly Baev
e9256152e8 Fixes link to feature requests in GitHub's issue template 2024-07-26 00:06:12 +02:00
Andras Bacsai
25410cb31a workaround for null bytes 2024-07-25 21:51:53 +02:00
Andras Bacsai
3dedf5548f feat: new server check job 2024-07-25 20:30:22 +02:00
Andras Bacsai
2d2bb23708 Merge pull request #2879 from lorenzomigliorero/fix/bitbucket-repository-url
fix: bitbucket repos url
2024-07-25 14:23:58 +02:00
Andras Bacsai
6afc0b6303 fix: gitlab logo 2024-07-25 14:17:26 +02:00
Andras Bacsai
f558f01ffd fix: gitlab service 2024-07-25 14:14:00 +02:00
Andras Bacsai
47fa955e71 Merge pull request #2638 from GunniBusch/main
[TEMPLATE] Add GitLab template
2024-07-25 13:49:40 +02:00
andrasbacsai
96c4f5b8da Fix styling 2024-07-25 11:31:59 +00:00
Andras Bacsai
6a4aa492c0 fix: random generated uuid will be full length (not 7 characters) 2024-07-25 13:31:01 +02:00
Andras Bacsai
debd2a3433 fix: link in task failed job notifications 2024-07-25 13:30:38 +02:00
Andras Bacsai
192bc0f13b fix: handle array env vars 2024-07-25 12:38:31 +02:00
Andras Bacsai
8756141f1d fix: remove pull always when uploading backup to s3 2024-07-25 12:11:47 +02:00
Andras Bacsai
b97abc600e chore: remove commented out code for docker image pruning 2024-07-25 12:00:14 +02:00
Andras Bacsai
4b29636b42 fix: api -> application patch endpoint 2024-07-25 11:53:57 +02:00
Andras Bacsai
321c51f8ed feat: check custom internal name through server's applications. 2024-07-25 11:30:31 +02:00
Andras Bacsai
69d8f706cf fix: raw compose deployment .env not found 2024-07-25 11:07:32 +02:00
Andras Bacsai
98f67c5c6c feat: update Caddy button label in proxy.blade.php 2024-07-25 11:07:18 +02:00
Andras Bacsai
1cf8d0b886 chore: update version numbers to 4.0.0-beta.319 2024-07-24 21:12:22 +02:00
andrasbacsai
613830e6a6 Fix styling 2024-07-24 19:11:12 +00:00
Andras Bacsai
2ea146333e fix: uuid in api docs type 2024-07-24 21:10:32 +02:00
Toby Brancher
097ca209bc Support map fields in Directus
To support the map fields in Directus with Postgres you need the Postgis extension installed, with docker we can just change the image to use the equivalent alpine postgis image, source latest stable alpine from here (perhaps there is a better ref to use than one with a specific version number?): https://registry.hub.docker.com/r/postgis/postgis/

If you are already running Directus without postgis, and would like to make use of the mapping fields after updating the docker compose file you will need to go to `execute command`, select the postgres container and run the following command filling in the appropriate env vars;

`psql -U ${SERVICE_USER_POSTGRESQL} -d ${POSTGRESQL_DATABASE} -c "CREATE EXTENSION postgis;"`
2024-07-21 23:39:22 +01:00
Lorenzo Migliorero
2dbba366b7 fix: bitbucket repository url 2024-07-19 13:41:01 +02:00
Lorenzo Migliorero
1b4a8aa58f Merge branch 'feat/labels-by-proxy-type' of github.com:lorenzomigliorero/coolify into feat/labels-by-proxy-type 2024-07-18 11:42:52 +02:00
Lorenzo Migliorero
be6d74a6a3 style: linting 2024-07-18 11:42:41 +02:00
Lorenzo Migliorero
5aae65f62f Merge branch 'next' into feat/labels-by-proxy-type 2024-07-17 21:21:09 +02:00
Lorenzo Migliorero
3be06ced92 feat: labels by proxy type 2024-07-17 21:06:56 +02:00
Leon Adomaitis
53032469e7 added smtp config 2024-06-23 21:43:14 +02:00
Leon Adomaitis
768e27d68c Add metadata 2024-06-23 04:12:03 +02:00
Leon Adomaitis
691ae04ca9 Add gitlab.svg 2024-06-23 04:02:46 +02:00
Leon Adomaitis
6585db1f9c Merge branch 'next' into main 2024-06-23 03:56:50 +02:00
Leon Adomaitis
42aa2d0088 add first outline of the gitlab compose file 2024-06-23 03:52:29 +02:00
144 changed files with 2951 additions and 2613 deletions

View File

@@ -4,5 +4,5 @@ contact_links:
url: https://coollabs.io/discord
about: Reach out to us on Discord.
- name: 🙋‍♂️ Feature Requests
url: https://github.com/coollabsio/coolify/discussions/categories/feature-requests-ideas
url: https://github.com/coollabsio/coolify/discussions/categories/new-features
about: All feature requests will be discussed here.

81
.github/workflows/pr-build.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: PR Build (v4)
on:
pull_request:
types:
- opened
branches-ignore: ["main", "v3"]
paths-ignore:
- .github/workflows/coolify-helper.yml
- docker/coolify-helper/Dockerfile
env:
REGISTRY: ghcr.io
IMAGE_NAME: "coollabsio/coolify"
jobs:
amd64:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image and push to registry
uses: docker/build-push-action@v5
with:
context: .
file: docker/prod/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.number }}
aarch64:
runs-on: [self-hosted, arm64]
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Login to ghcr.io
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image and push to registry
uses: docker/build-push-action@v5
with:
context: .
file: docker/prod/Dockerfile
platforms: linux/aarch64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.number }}-aarch64
merge-manifest:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
needs: [amd64, aarch64]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to ghcr.io
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create & publish manifest
run: |
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.number }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.number }}
- uses: sarisia/actions-status-discord@v1
if: always()
with:
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}

View File

@@ -31,15 +31,13 @@ class StopApplication
} else {
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
}
ray($containers);
if ($containers->count() > 0) {
foreach ($containers as $container) {
$containerName = data_get($container, 'Names');
if ($containerName) {
instant_remote_process(
["docker rm -f {$containerName}"],
$server
);
instant_remote_process(command: ["docker stop --time=30 $containerName"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm $containerName"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm -f {$containerName}"], server: $server, throwError: false);
}
}
}

View File

@@ -22,10 +22,11 @@ class StopDatabase
if (! $server->isFunctional()) {
return 'Server is not functional';
}
instant_remote_process(
["docker rm -f {$database->uuid}"],
$server
);
instant_remote_process(command: ["docker stop --time=30 $database->uuid"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm $database->uuid"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm -f $database->uuid"], server: $server, throwError: false);
if ($database->is_public) {
StopDatabaseProxy::run($database);
}

View File

@@ -12,6 +12,7 @@ use App\Models\ServiceDatabase;
use App\Notifications\Container\ContainerRestarted;
use App\Notifications\Container\ContainerStopped;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Lorisleiva\Actions\Concerns\AsAction;
class GetContainersStatus
@@ -20,13 +21,16 @@ class GetContainersStatus
public $applications;
public ?Collection $containers;
public ?Collection $containerReplicates;
public $server;
public function handle(Server $server)
public function handle(Server $server, ?Collection $containers = null, ?Collection $containerReplicates = null)
{
// if (isDev()) {
// $server = Server::find(0);
// }
$this->containers = $containers;
$this->containerReplicates = $containerReplicates;
$this->server = $server;
if (! $this->server->isFunctional()) {
return 'Server is not ready.';
@@ -66,322 +70,312 @@ class GetContainersStatus
// }
}
private function sentinel()
{
try {
$containers = $this->server->getContainers();
if ($containers->count() === 0) {
return;
}
$databases = $this->server->databases();
$services = $this->server->services()->get();
$previews = $this->server->previews();
$foundApplications = [];
$foundApplicationPreviews = [];
$foundDatabases = [];
$foundServices = [];
// private function sentinel()
// {
// try {
// $this->containers = $this->server->getContainersWithSentinel();
// if ($this->containers->count() === 0) {
// return;
// }
// $databases = $this->server->databases();
// $services = $this->server->services()->get();
// $previews = $this->server->previews();
// $foundApplications = [];
// $foundApplicationPreviews = [];
// $foundDatabases = [];
// $foundServices = [];
foreach ($containers as $container) {
$labels = Arr::undot(data_get($container, 'labels'));
$containerStatus = data_get($container, 'state');
$containerHealth = data_get($container, 'health_status', 'unhealthy');
$containerStatus = "$containerStatus ($containerHealth)";
$applicationId = data_get($labels, 'coolify.applicationId');
if ($applicationId) {
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
if ($pullRequestId) {
if (str($applicationId)->contains('-')) {
$applicationId = str($applicationId)->before('-');
}
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
if ($preview) {
$foundApplicationPreviews[] = $preview->id;
$statusFromDb = $preview->status;
if ($statusFromDb !== $containerStatus) {
$preview->update(['status' => $containerStatus]);
}
} else {
//Notify user that this container should not be there.
}
} else {
$application = $this->applications->where('id', $applicationId)->first();
if ($application) {
$foundApplications[] = $application->id;
$statusFromDb = $application->status;
if ($statusFromDb !== $containerStatus) {
$application->update(['status' => $containerStatus]);
}
} else {
//Notify user that this container should not be there.
}
}
} else {
$uuid = data_get($labels, 'com.docker.compose.service');
$type = data_get($labels, 'coolify.type');
if ($uuid) {
if ($type === 'service') {
$database_id = data_get($labels, 'coolify.service.subId');
if ($database_id) {
$service_db = ServiceDatabase::where('id', $database_id)->first();
if ($service_db) {
$uuid = $service_db->service->uuid;
$isPublic = data_get($service_db, 'is_public');
if ($isPublic) {
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) {
// TODO: fix this with sentinel
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} else {
return data_get($value, 'name') === "$uuid-proxy";
}
})->first();
if (! $foundTcpProxy) {
StartDatabaseProxy::run($service_db);
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
}
}
}
}
} else {
$database = $databases->where('uuid', $uuid)->first();
if ($database) {
$isPublic = data_get($database, 'is_public');
$foundDatabases[] = $database->id;
$statusFromDb = $database->status;
if ($statusFromDb !== $containerStatus) {
$database->update(['status' => $containerStatus]);
}
if ($isPublic) {
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) {
// TODO: fix this with sentinel
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} else {
return data_get($value, 'name') === "$uuid-proxy";
}
})->first();
if (! $foundTcpProxy) {
StartDatabaseProxy::run($database);
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
}
}
} else {
// Notify user that this container should not be there.
}
}
}
if (data_get($container, 'name') === 'coolify-db') {
$foundDatabases[] = 0;
}
}
$serviceLabelId = data_get($labels, 'coolify.serviceId');
if ($serviceLabelId) {
$subType = data_get($labels, 'coolify.service.subType');
$subId = data_get($labels, 'coolify.service.subId');
$service = $services->where('id', $serviceLabelId)->first();
if (! $service) {
continue;
}
if ($subType === 'application') {
$service = $service->applications()->where('id', $subId)->first();
} else {
$service = $service->databases()->where('id', $subId)->first();
}
if ($service) {
$foundServices[] = "$service->id-$service->name";
$statusFromDb = $service->status;
if ($statusFromDb !== $containerStatus) {
// ray('Updating status: ' . $containerStatus);
$service->update(['status' => $containerStatus]);
}
}
}
}
$exitedServices = collect([]);
foreach ($services as $service) {
$apps = $service->applications()->get();
$dbs = $service->databases()->get();
foreach ($apps as $app) {
if (in_array("$app->id-$app->name", $foundServices)) {
continue;
} else {
$exitedServices->push($app);
}
}
foreach ($dbs as $db) {
if (in_array("$db->id-$db->name", $foundServices)) {
continue;
} else {
$exitedServices->push($db);
}
}
}
$exitedServices = $exitedServices->unique('id');
foreach ($exitedServices as $exitedService) {
if (str($exitedService->status)->startsWith('exited')) {
continue;
}
$name = data_get($exitedService, 'name');
$fqdn = data_get($exitedService, 'fqdn');
if ($name) {
if ($fqdn) {
$containerName = "$name, available at $fqdn";
} else {
$containerName = $name;
}
} else {
if ($fqdn) {
$containerName = $fqdn;
} else {
$containerName = null;
}
}
$projectUuid = data_get($service, 'environment.project.uuid');
$serviceUuid = data_get($service, 'uuid');
$environmentName = data_get($service, 'environment.name');
// foreach ($this->containers as $container) {
// $labels = Arr::undot(data_get($container, 'labels'));
// $containerStatus = data_get($container, 'state');
// $containerHealth = data_get($container, 'health_status', 'unhealthy');
// $containerStatus = "$containerStatus ($containerHealth)";
// $applicationId = data_get($labels, 'coolify.applicationId');
// if ($applicationId) {
// $pullRequestId = data_get($labels, 'coolify.pullRequestId');
// if ($pullRequestId) {
// if (str($applicationId)->contains('-')) {
// $applicationId = str($applicationId)->before('-');
// }
// $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
// if ($preview) {
// $foundApplicationPreviews[] = $preview->id;
// $statusFromDb = $preview->status;
// if ($statusFromDb !== $containerStatus) {
// $preview->update(['status' => $containerStatus]);
// }
// } else {
// //Notify user that this container should not be there.
// }
// } else {
// $application = $this->applications->where('id', $applicationId)->first();
// if ($application) {
// $foundApplications[] = $application->id;
// $statusFromDb = $application->status;
// if ($statusFromDb !== $containerStatus) {
// $application->update(['status' => $containerStatus]);
// }
// } else {
// //Notify user that this container should not be there.
// }
// }
// } else {
// $uuid = data_get($labels, 'com.docker.compose.service');
// $type = data_get($labels, 'coolify.type');
// if ($uuid) {
// if ($type === 'service') {
// $database_id = data_get($labels, 'coolify.service.subId');
// if ($database_id) {
// $service_db = ServiceDatabase::where('id', $database_id)->first();
// if ($service_db) {
// $uuid = $service_db->service->uuid;
// $isPublic = data_get($service_db, 'is_public');
// if ($isPublic) {
// $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
// if ($this->server->isSwarm()) {
// // TODO: fix this with sentinel
// return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
// } else {
// return data_get($value, 'name') === "$uuid-proxy";
// }
// })->first();
// if (! $foundTcpProxy) {
// StartDatabaseProxy::run($service_db);
// // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
// }
// }
// }
// }
// } else {
// $database = $databases->where('uuid', $uuid)->first();
// if ($database) {
// $isPublic = data_get($database, 'is_public');
// $foundDatabases[] = $database->id;
// $statusFromDb = $database->status;
// if ($statusFromDb !== $containerStatus) {
// $database->update(['status' => $containerStatus]);
// }
// if ($isPublic) {
// $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
// if ($this->server->isSwarm()) {
// // TODO: fix this with sentinel
// return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
// } else {
// return data_get($value, 'name') === "$uuid-proxy";
// }
// })->first();
// if (! $foundTcpProxy) {
// StartDatabaseProxy::run($database);
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
// }
// }
// } else {
// // Notify user that this container should not be there.
// }
// }
// }
// if (data_get($container, 'name') === 'coolify-db') {
// $foundDatabases[] = 0;
// }
// }
// $serviceLabelId = data_get($labels, 'coolify.serviceId');
// if ($serviceLabelId) {
// $subType = data_get($labels, 'coolify.service.subType');
// $subId = data_get($labels, 'coolify.service.subId');
// $service = $services->where('id', $serviceLabelId)->first();
// if (! $service) {
// continue;
// }
// if ($subType === 'application') {
// $service = $service->applications()->where('id', $subId)->first();
// } else {
// $service = $service->databases()->where('id', $subId)->first();
// }
// if ($service) {
// $foundServices[] = "$service->id-$service->name";
// $statusFromDb = $service->status;
// if ($statusFromDb !== $containerStatus) {
// // ray('Updating status: ' . $containerStatus);
// $service->update(['status' => $containerStatus]);
// }
// }
// }
// }
// $exitedServices = collect([]);
// foreach ($services as $service) {
// $apps = $service->applications()->get();
// $dbs = $service->databases()->get();
// foreach ($apps as $app) {
// if (in_array("$app->id-$app->name", $foundServices)) {
// continue;
// } else {
// $exitedServices->push($app);
// }
// }
// foreach ($dbs as $db) {
// if (in_array("$db->id-$db->name", $foundServices)) {
// continue;
// } else {
// $exitedServices->push($db);
// }
// }
// }
// $exitedServices = $exitedServices->unique('id');
// foreach ($exitedServices as $exitedService) {
// if (str($exitedService->status)->startsWith('exited')) {
// continue;
// }
// $name = data_get($exitedService, 'name');
// $fqdn = data_get($exitedService, 'fqdn');
// if ($name) {
// if ($fqdn) {
// $containerName = "$name, available at $fqdn";
// } else {
// $containerName = $name;
// }
// } else {
// if ($fqdn) {
// $containerName = $fqdn;
// } else {
// $containerName = null;
// }
// }
// $projectUuid = data_get($service, 'environment.project.uuid');
// $serviceUuid = data_get($service, 'uuid');
// $environmentName = data_get($service, 'environment.name');
if ($projectUuid && $serviceUuid && $environmentName) {
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
} else {
$url = null;
}
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
$exitedService->update(['status' => 'exited']);
}
// if ($projectUuid && $serviceUuid && $environmentName) {
// $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
// } else {
// $url = null;
// }
// // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
// $exitedService->update(['status' => 'exited']);
// }
$notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
foreach ($notRunningApplications as $applicationId) {
$application = $this->applications->where('id', $applicationId)->first();
if (str($application->status)->startsWith('exited')) {
continue;
}
$application->update(['status' => 'exited']);
// $notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
// foreach ($notRunningApplications as $applicationId) {
// $application = $this->applications->where('id', $applicationId)->first();
// if (str($application->status)->startsWith('exited')) {
// continue;
// }
// $application->update(['status' => 'exited']);
$name = data_get($application, 'name');
$fqdn = data_get($application, 'fqdn');
// $name = data_get($application, 'name');
// $fqdn = data_get($application, 'fqdn');
$containerName = $name ? "$name ($fqdn)" : $fqdn;
// $containerName = $name ? "$name ($fqdn)" : $fqdn;
$projectUuid = data_get($application, 'environment.project.uuid');
$applicationUuid = data_get($application, 'uuid');
$environment = data_get($application, 'environment.name');
// $projectUuid = data_get($application, 'environment.project.uuid');
// $applicationUuid = data_get($application, 'uuid');
// $environment = data_get($application, 'environment.name');
if ($projectUuid && $applicationUuid && $environment) {
$url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
} else {
$url = null;
}
// if ($projectUuid && $applicationUuid && $environment) {
// $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
// } else {
// $url = null;
// }
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
foreach ($notRunningApplicationPreviews as $previewId) {
$preview = $previews->where('id', $previewId)->first();
if (str($preview->status)->startsWith('exited')) {
continue;
}
$preview->update(['status' => 'exited']);
// // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
// }
// $notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
// foreach ($notRunningApplicationPreviews as $previewId) {
// $preview = $previews->where('id', $previewId)->first();
// if (str($preview->status)->startsWith('exited')) {
// continue;
// }
// $preview->update(['status' => 'exited']);
$name = data_get($preview, 'name');
$fqdn = data_get($preview, 'fqdn');
// $name = data_get($preview, 'name');
// $fqdn = data_get($preview, 'fqdn');
$containerName = $name ? "$name ($fqdn)" : $fqdn;
// $containerName = $name ? "$name ($fqdn)" : $fqdn;
$projectUuid = data_get($preview, 'application.environment.project.uuid');
$environmentName = data_get($preview, 'application.environment.name');
$applicationUuid = data_get($preview, 'application.uuid');
// $projectUuid = data_get($preview, 'application.environment.project.uuid');
// $environmentName = data_get($preview, 'application.environment.name');
// $applicationUuid = data_get($preview, 'application.uuid');
if ($projectUuid && $applicationUuid && $environmentName) {
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
} else {
$url = null;
}
// if ($projectUuid && $applicationUuid && $environmentName) {
// $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
// } else {
// $url = null;
// }
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
foreach ($notRunningDatabases as $database) {
$database = $databases->where('id', $database)->first();
if (str($database->status)->startsWith('exited')) {
continue;
}
$database->update(['status' => 'exited']);
// // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
// }
// $notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
// foreach ($notRunningDatabases as $database) {
// $database = $databases->where('id', $database)->first();
// if (str($database->status)->startsWith('exited')) {
// continue;
// }
// $database->update(['status' => 'exited']);
$name = data_get($database, 'name');
$fqdn = data_get($database, 'fqdn');
// $name = data_get($database, 'name');
// $fqdn = data_get($database, 'fqdn');
$containerName = $name;
// $containerName = $name;
$projectUuid = data_get($database, 'environment.project.uuid');
$environmentName = data_get($database, 'environment.name');
$databaseUuid = data_get($database, 'uuid');
// $projectUuid = data_get($database, 'environment.project.uuid');
// $environmentName = data_get($database, 'environment.name');
// $databaseUuid = data_get($database, 'uuid');
if ($projectUuid && $databaseUuid && $environmentName) {
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
} else {
$url = null;
}
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
// if ($projectUuid && $databaseUuid && $environmentName) {
// $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
// } else {
// $url = null;
// }
// // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
// }
// Check if proxy is running
$this->server->proxyType();
$foundProxyContainer = $containers->filter(function ($value, $key) {
if ($this->server->isSwarm()) {
// TODO: fix this with sentinel
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
} else {
return data_get($value, 'name') === 'coolify-proxy';
}
})->first();
if (! $foundProxyContainer) {
try {
$shouldStart = CheckProxy::run($this->server);
if ($shouldStart) {
StartProxy::run($this->server, false);
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
}
} catch (\Throwable $e) {
ray($e);
}
} else {
$this->server->proxy->status = data_get($foundProxyContainer, 'state');
$this->server->save();
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
}
} catch (\Exception $e) {
// send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
ray($e->getMessage());
// // Check if proxy is running
// $this->server->proxyType();
// $foundProxyContainer = $this->containers->filter(function ($value, $key) {
// if ($this->server->isSwarm()) {
// // TODO: fix this with sentinel
// return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
// } else {
// return data_get($value, 'name') === 'coolify-proxy';
// }
// })->first();
// if (! $foundProxyContainer) {
// try {
// $shouldStart = CheckProxy::run($this->server);
// if ($shouldStart) {
// StartProxy::run($this->server, false);
// $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
// }
// } catch (\Throwable $e) {
// ray($e);
// }
// } else {
// $this->server->proxy->status = data_get($foundProxyContainer, 'state');
// $this->server->save();
// $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
// instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
// }
// } catch (\Exception $e) {
// // send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
// ray($e->getMessage());
return handleError($e);
}
}
// return handleError($e);
// }
// }
private function old_way()
{
if ($this->server->isSwarm()) {
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
} else {
// Precheck for containers
$containers = instant_remote_process(['docker container ls -q'], $this->server, false);
if (! $containers) {
return;
}
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
$containerReplicates = null;
if ($this->containers === null) {
['containers' => $this->containers,'containerReplicates' => $this->containerReplicates] = $this->server->getContainers();
}
if (is_null($containers)) {
if (is_null($this->containers)) {
return;
}
$containers = format_docker_command_output_to_json($containers);
if ($containerReplicates) {
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
foreach ($containerReplicates as $containerReplica) {
if ($this->containerReplicates) {
foreach ($this->containerReplicates as $containerReplica) {
$name = data_get($containerReplica, 'Name');
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
$this->containers = $this->containers->map(function ($container) use ($name, $containerReplica) {
if (data_get($container, 'Spec.Name') === $name) {
$replicas = data_get($containerReplica, 'Replicas');
$running = str($replicas)->explode('/')[0];
@@ -407,7 +401,7 @@ class GetContainersStatus
$foundDatabases = [];
$foundServices = [];
foreach ($containers as $container) {
foreach ($this->containers as $container) {
if ($this->server->isSwarm()) {
$labels = data_get($container, 'Spec.Labels');
$uuid = data_get($labels, 'coolify.name');
@@ -461,7 +455,7 @@ class GetContainersStatus
if ($uuid) {
$isPublic = data_get($service_db, 'is_public');
if ($isPublic) {
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} else {
@@ -486,7 +480,7 @@ class GetContainersStatus
$database->update(['status' => $containerStatus]);
}
if ($isPublic) {
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} else {
@@ -659,7 +653,7 @@ class GetContainersStatus
// Check if proxy is running
$this->server->proxyType();
$foundProxyContainer = $containers->filter(function ($value, $key) {
$foundProxyContainer = $this->containers->filter(function ($value, $key) {
if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
} else {

View File

@@ -11,7 +11,6 @@ class CleanupDocker
public function handle(Server $server, bool $force = true)
{
// cleanup docker images, containers, and builder caches
if ($force) {
instant_remote_process(['docker image prune -af'], $server, false);
@@ -22,15 +21,5 @@ class CleanupDocker
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false);
instant_remote_process(['docker builder prune -f'], $server, false);
}
// cleanup networks
// $networks = collectDockerNetworksByServer($server);
// $proxyNetworks = collectProxyDockerNetworksByServer($server);
// $diff = $proxyNetworks->diff($networks);
// if ($diff->count() > 0) {
// $diff->map(function ($network) use ($server) {
// instant_remote_process(["docker network disconnect $network coolify-proxy"], $server);
// instant_remote_process(["docker network rm $network"], $server);
// });
// }
}
}

View File

@@ -24,12 +24,7 @@ class InstallLogDrain
}
try {
if ($type === 'none') {
$command = [
"echo 'Stopping old Fluent Bit'",
'docker rm -f coolify-log-drain || true',
];
return instant_remote_process($command, $server);
return 'No log drain is enabled.';
} elseif ($type === 'newrelic') {
if (! $server->settings->is_logdrain_newrelic_enabled) {
throw new \Exception('New Relic log drain is not enabled.');

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Actions\Server;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class StopLogDrain
{
use AsAction;
public function handle(Server $server)
{
try {
return instant_remote_process(['docker rm -f coolify-log-drain || true'], $server);
} catch (\Throwable $e) {
return handleError($e);
}
}
}

View File

@@ -4,6 +4,8 @@ namespace App\Actions\Server;
use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Lorisleiva\Actions\Concerns\AsAction;
class UpdateCoolify
@@ -20,12 +22,16 @@ class UpdateCoolify
{
try {
$settings = InstanceSettings::get();
ray('Running InstanceAutoUpdateJob');
$this->server = Server::find(0);
if (! $this->server) {
return;
}
CleanupDocker::dispatch($this->server, false)->onQueue('high');
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
$versions = $response->json();
File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT));
}
$this->latestVersion = get_latest_version_of_coolify();
$this->currentVersion = config('version');
if (! $manual_update) {
@@ -40,6 +46,8 @@ class UpdateCoolify
}
}
$this->update();
$settings->new_version_available = false;
$settings->save();
} catch (\Throwable $e) {
throw $e;
}
@@ -48,7 +56,6 @@ class UpdateCoolify
private function update()
{
if (isDev()) {
ray('Running in dev mode');
remote_process([
'sleep 10',
], $this->server);

View File

@@ -19,18 +19,21 @@ class StopService
ray('Stopping service: '.$service->name);
$applications = $service->applications()->get();
foreach ($applications as $application) {
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server, false);
instant_remote_process(command: ["docker stop --time=30 {$application->name}-{$service->uuid}"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm {$application->name}-{$service->uuid}"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm -f {$application->name}-{$service->uuid}"], server: $server, throwError: false);
$application->update(['status' => 'exited']);
}
$dbs = $service->databases()->get();
foreach ($dbs as $db) {
instant_remote_process(["docker rm -f {$db->name}-{$service->uuid}"], $service->server, false);
instant_remote_process(command: ["docker stop --time=30 {$db->name}-{$service->uuid}"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm {$db->name}-{$service->uuid}"], server: $server, throwError: false);
instant_remote_process(command: ["docker rm -f {$db->name}-{$service->uuid}"], server: $server, throwError: false);
$db->update(['status' => 'exited']);
}
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy"], $service->server);
instant_remote_process(["docker network rm {$service->uuid}"], $service->server);
} catch (\Exception $e) {
echo $e->getMessage();
ray($e->getMessage());
return $e->getMessage();

View File

@@ -18,7 +18,12 @@ class CleanupDatabase extends Command
} else {
echo "Running database cleanup in dry-run mode...\n";
}
$keep_days = 60;
if (isCloud()) {
// Later on we can increase this to 180 days or dynamically set
$keep_days = 60;
} else {
$keep_days = 60;
}
echo "Keep days: $keep_days\n";
// Cleanup failed jobs table
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(1));

View File

@@ -3,6 +3,7 @@
namespace App\Console\Commands;
use App\Actions\Server\StopSentinel;
use App\Enums\ActivityTypes;
use App\Enums\ApplicationDeploymentStatus;
use App\Jobs\CleanupHelperContainersJob;
use App\Models\ApplicationDeploymentQueue;
@@ -12,22 +13,27 @@ use App\Models\ScheduledDatabaseBackup;
use App\Models\Server;
use App\Models\StandalonePostgresql;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
class Init extends Command
{
protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments}';
protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments} {--cleanup-proxy-networks}';
protected $description = 'Cleanup instance related stuffs';
public $servers = null;
public function handle()
{
$this->servers = Server::all();
$this->alive();
get_public_ips();
if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
$servers = Server::all();
foreach ($servers as $server) {
$server->settings->update(['is_metrics_enabled' => false]);
foreach ($this->servers as $server) {
if ($server->settings->is_metrics_enabled === true) {
$server->settings->update(['is_metrics_enabled' => false]);
}
if ($server->isFunctional()) {
StopSentinel::dispatch($server);
}
@@ -36,7 +42,7 @@ class Init extends Command
$full_cleanup = $this->option('full-cleanup');
$cleanup_deployments = $this->option('cleanup-deployments');
$cleanup_proxy_networks = $this->option('cleanup-proxy-networks');
$this->replace_slash_in_environment_name();
if ($cleanup_deployments) {
echo "Running cleanup deployments.\n";
@@ -44,17 +50,26 @@ class Init extends Command
return;
}
if ($cleanup_proxy_networks) {
echo "Running cleanup proxy networks.\n";
$this->cleanup_unused_network_from_coolify_proxy();
return;
}
if ($full_cleanup) {
// Required for falsely deleted coolify db
$this->restore_coolify_db_backup();
$this->update_traefik_labels();
$this->cleanup_unused_network_from_coolify_proxy();
$this->cleanup_unnecessary_dynamic_proxy_configuration();
$this->cleanup_in_progress_application_deployments();
$this->cleanup_stucked_helper_containers();
$this->call('cleanup:queue');
$this->call('cleanup:stucked-resources');
if (! isCloud()) {
try {
$server = Server::find(0)->first();
$server->setupDynamicProxyConfiguration();
$localhost = $this->servers->where('id', 0)->first();
$localhost->setupDynamicProxyConfiguration();
} catch (\Throwable $e) {
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
}
@@ -68,6 +83,13 @@ class Init extends Command
$settings->update(['is_auto_update_enabled' => false]);
}
}
if (isCloud()) {
$response = Http::retry(3, 1000)->get(config('constants.services.official'));
if ($response->successful()) {
$services = $response->json();
File::put(base_path('templates/service-templates.json'), json_encode($services));
}
}
return;
}
@@ -75,6 +97,79 @@ class Init extends Command
$this->call('cleanup:stucked-resources');
}
private function update_traefik_labels()
{
try {
Server::where('proxy->type', 'TRAEFIK_V2')->update(['proxy->type' => 'TRAEFIK']);
} catch (\Throwable $e) {
echo "Error in updating traefik labels: {$e->getMessage()}\n";
}
}
private function cleanup_unnecessary_dynamic_proxy_configuration()
{
if (isCloud()) {
foreach ($this->servers as $server) {
try {
if (! $server->isFunctional()) {
continue;
}
if ($server->id === 0) {
continue;
}
$file = $server->proxyPath().'/dynamic/coolify.yaml';
return instant_remote_process([
"rm -f $file",
], $server, false);
} catch (\Throwable $e) {
echo "Error in cleaning up unnecessary dynamic proxy configuration: {$e->getMessage()}\n";
}
}
}
}
private function cleanup_unused_network_from_coolify_proxy()
{
foreach ($this->servers as $server) {
if (! $server->isFunctional()) {
continue;
}
if (! $server->isProxyShouldRun()) {
continue;
}
try {
['networks' => $networks, 'allNetworks' => $allNetworks] = collectDockerNetworksByServer($server);
$removeNetworks = $allNetworks->diff($networks);
$commands = collect();
foreach ($removeNetworks as $network) {
$out = instant_remote_process(["docker network inspect -f json $network | jq '.[].Containers | if . == {} then null else . end'"], $server, false);
if (empty($out)) {
$commands->push("docker network disconnect $network coolify-proxy >/dev/null 2>&1 || true");
$commands->push("docker network rm $network >/dev/null 2>&1 || true");
} else {
$data = collect(json_decode($out, true));
if ($data->count() === 1) {
// If only coolify-proxy itself is connected to that network (it should not be possible, but who knows)
$isCoolifyProxyItself = data_get($data->first(), 'Name') === 'coolify-proxy';
if ($isCoolifyProxyItself) {
$commands->push("docker network disconnect $network coolify-proxy >/dev/null 2>&1 || true");
$commands->push("docker network rm $network >/dev/null 2>&1 || true");
}
}
}
}
if ($commands->isNotEmpty()) {
echo "Cleaning up unused networks from coolify proxy\n";
remote_process(command: $commands, type: ActivityTypes::INLINE->value, server: $server, ignore_errors: false);
}
} catch (\Throwable $e) {
echo "Error in cleaning up unused networks from coolify proxy: {$e->getMessage()}\n";
}
}
}
private function restore_coolify_db_backup()
{
try {
@@ -102,8 +197,7 @@ class Init extends Command
private function cleanup_stucked_helper_containers()
{
$servers = Server::all();
foreach ($servers as $server) {
foreach ($this->servers as $server) {
if ($server->isFunctional()) {
CleanupHelperContainersJob::dispatch($server);
}
@@ -148,7 +242,6 @@ class Init extends Command
private function cleanup_in_progress_application_deployments()
{
// Cleanup any failed deployments
try {
if (isCloud()) {
return;

View File

@@ -2,9 +2,8 @@
namespace App\Console;
use App\Jobs\CheckLogDrainContainerJob;
use App\Jobs\CheckForUpdatesJob;
use App\Jobs\CleanupInstanceStuffsJob;
use App\Jobs\ContainerStatusJob;
use App\Jobs\DatabaseBackupJob;
use App\Jobs\DockerCleanupJob;
use App\Jobs\PullCoolifyImageJob;
@@ -12,7 +11,9 @@ use App\Jobs\PullHelperImageJob;
use App\Jobs\PullSentinelImageJob;
use App\Jobs\PullTemplatesFromCDN;
use App\Jobs\ScheduledTaskJob;
use App\Jobs\ServerStatusJob;
use App\Jobs\ServerCheckJob;
use App\Jobs\UpdateCoolifyJob;
use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledTask;
use App\Models\Server;
@@ -27,25 +28,26 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule): void
{
$this->all_servers = Server::all();
$settings = InstanceSettings::get();
if (isDev()) {
// Instance Jobs
$schedule->command('horizon:snapshot')->everyMinute();
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
// Server Jobs
$this->check_scheduled_backups($schedule);
$this->check_resources($schedule);
$this->check_scheduled_backups($schedule);
$this->check_scheduled_tasks($schedule);
$schedule->command('uploads:clear')->everyTwoMinutes();
} else {
// Instance Jobs
$schedule->command('horizon:snapshot')->everyFiveMinutes();
$schedule->command('cleanup:unreachable-servers')->daily();
$schedule->job(new PullCoolifyImageJob)->everyTenMinutes()->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->everyThirtyMinutes()->onOneServer();
$schedule->job(new PullCoolifyImageJob)->cron($settings->update_check_frequency)->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->cron($settings->update_check_frequency)->onOneServer();
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
$this->schedule_updates($schedule);
// Server Jobs
$this->check_scheduled_backups($schedule);
@@ -60,12 +62,26 @@ class Kernel extends ConsoleKernel
private function pull_images($schedule)
{
$settings = InstanceSettings::get();
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
foreach ($servers as $server) {
if ($server->isSentinelEnabled()) {
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
$schedule->job(new PullSentinelImageJob($server))->cron($settings->update_check_frequency)->onOneServer();
}
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
$schedule->job(new PullHelperImageJob($server))->cron($settings->update_check_frequency)->onOneServer();
}
}
private function schedule_updates($schedule)
{
$settings = InstanceSettings::get();
$updateCheckFrequency = $settings->update_check_frequency;
$schedule->job(new CheckForUpdatesJob)->cron($updateCheckFrequency)->onOneServer();
if ($settings->is_auto_update_enabled) {
$autoUpdateFrequency = $settings->auto_update_frequency;
$schedule->job(new UpdateCoolifyJob)->cron($autoUpdateFrequency)->onOneServer();
}
}
@@ -75,19 +91,11 @@ class Kernel extends ConsoleKernel
$servers = $this->all_servers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
$own = Team::find(0)->servers;
$servers = $servers->merge($own);
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
} else {
$servers = $this->all_servers->where('ip', '!=', '1.2.3.4');
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
}
foreach ($containerServers as $server) {
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
if ($server->isLogDrainEnabled()) {
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
}
}
foreach ($servers as $server) {
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
}
}

View File

@@ -5,7 +5,7 @@ namespace App\Enums;
enum ProxyTypes: string
{
case NONE = 'NONE';
case TRAEFIK_V2 = 'TRAEFIK_V2';
case TRAEFIK = 'TRAEFIK';
case NGINX = 'NGINX';
case CADDY = 'CADDY';
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class FileStorageChanged implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId;
public function __construct($teamId = null)
{
ray($teamId);
if (is_null($teamId)) {
throw new \Exception('Team id is null');
}
$this->teamId = $teamId;
}
public function broadcastOn(): array
{
return [
new PrivateChannel("team.{$this->teamId}"),
];
}
}

View File

@@ -739,7 +739,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -835,7 +835,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -927,7 +927,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -947,7 +947,7 @@ class ApplicationsController extends Controller
]));
} elseif ($type === 'dockerfile') {
if (! $request->has('name')) {
$request->offsetSet('name', 'dockerfile-'.new Cuid2(7));
$request->offsetSet('name', 'dockerfile-'.new Cuid2);
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
@@ -1009,7 +1009,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -1025,7 +1025,7 @@ class ApplicationsController extends Controller
]));
} elseif ($type === 'dockerimage') {
if (! $request->has('name')) {
$request->offsetSet('name', 'docker-image-'.new Cuid2(7));
$request->offsetSet('name', 'docker-image-'.new Cuid2);
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
@@ -1067,7 +1067,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -1099,7 +1099,7 @@ class ApplicationsController extends Controller
], 422);
}
if (! $request->has('name')) {
$request->offsetSet('name', 'service'.new Cuid2(7));
$request->offsetSet('name', 'service'.new Cuid2);
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
@@ -1320,7 +1320,7 @@ class ApplicationsController extends Controller
#[OA\Patch(
summary: 'Update',
description: 'Update application by UUID.',
path: '/applications',
path: '/applications/{uuid}',
security: [
['bearerAuth' => []],
],
@@ -2322,7 +2322,7 @@ class ApplicationsController extends Controller
return response()->json(['message' => 'Application not found.'], 404);
}
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -2479,7 +2479,7 @@ class ApplicationsController extends Controller
return response()->json(['message' => 'Application not found.'], 404);
}
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,

View File

@@ -84,7 +84,7 @@ class DeployController extends Controller
],
tags: ['Deployments'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Deployment Uuid', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Deployment Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -290,7 +290,7 @@ class DeployController extends Controller
}
switch ($resource?->getMorphClass()) {
case 'App\Models\Application':
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $resource,
deployment_uuid: $deployment_uuid,

View File

@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Api;
use OpenApi\Attributes as OA;
#[OA\Info(title: 'Coolify', version: '0.1')]
#[OA\Server(url: 'https://app.coolify.io/api/v1')]
#[OA\Server(url: 'https://app.coolify.io/api/v1', description: 'Coolify Cloud API. Change the host to your own instance if you are self-hosting.')]
#[OA\SecurityScheme(
type: 'http',
scheme: 'bearer',

View File

@@ -61,7 +61,7 @@ class ProjectController extends Controller
],
tags: ['Projects'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Project UUID', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Project UUID', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -107,7 +107,7 @@ class ProjectController extends Controller
],
tags: ['Projects'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Project UUID', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Project UUID', schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'environment_name', in: 'path', required: true, description: 'Environment name', schema: new OA\Schema(type: 'string')),
],
responses: [

View File

@@ -73,7 +73,7 @@ class SecurityController extends Controller
],
tags: ['Private Keys'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -318,7 +318,7 @@ class SecurityController extends Controller
],
tags: ['Private Keys'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(

View File

@@ -105,7 +105,7 @@ class ServersController extends Controller
],
tags: ['Servers'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -182,7 +182,7 @@ class ServersController extends Controller
],
tags: ['Servers'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -259,7 +259,7 @@ class ServersController extends Controller
],
tags: ['Servers'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -525,7 +525,7 @@ class ServersController extends Controller
'private_key_id' => $privateKey->id,
'team_id' => $teamId,
'proxy' => [
'type' => ProxyTypes::TRAEFIK_V2->value,
'type' => ProxyTypes::TRAEFIK->value,
'status' => ProxyStatus::EXITED->value,
],
]);
@@ -732,7 +732,7 @@ class ServersController extends Controller
],
tags: ['Servers'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server UUID', schema: new OA\Schema(type: 'integer')),
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server UUID', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(

View File

@@ -413,6 +413,8 @@ class ServicesController extends Controller
return response()->json(['message' => 'Service not found.'], 404);
}
$service = $service->load(['applications', 'databases']);
return response()->json($this->removeSensitiveData($service));
}

View File

@@ -103,7 +103,7 @@ class Bitbucket extends Controller
if ($x_bitbucket_event === 'repo:push') {
if ($application->isDeployable()) {
ray('Deploying '.$application->name.' with branch '.$branch);
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -127,7 +127,7 @@ class Bitbucket extends Controller
if ($x_bitbucket_event === 'pullrequest:created') {
if ($application->isPRDeployable()) {
ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id);
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
if ($application->build_pack === 'dockercompose') {

View File

@@ -123,7 +123,7 @@ class Gitea extends Controller
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying '.$application->name.' with branch '.$branch);
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -162,7 +162,7 @@ class Gitea extends Controller
if ($x_gitea_event === 'pull_request') {
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
if ($application->build_pack === 'dockercompose') {

View File

@@ -128,7 +128,7 @@ class Github extends Controller
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying '.$application->name.' with branch '.$branch);
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -167,7 +167,7 @@ class Github extends Controller
if ($x_github_event === 'pull_request') {
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
if ($application->build_pack === 'dockercompose') {
@@ -357,7 +357,7 @@ class Github extends Controller
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying '.$application->name.' with branch '.$branch);
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -396,7 +396,7 @@ class Github extends Controller
if ($x_github_event === 'pull_request') {
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
ApplicationPreview::create([

View File

@@ -137,7 +137,7 @@ class Gitlab extends Controller
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying '.$application->name.' with branch '.$branch);
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -177,7 +177,7 @@ class Gitlab extends Controller
if ($x_gitlab_event === 'merge_request') {
if ($action === 'open' || $action === 'opened' || $action === 'synchronize' || $action === 'reopened' || $action === 'reopen' || $action === 'update') {
if ($application->isPRDeployable()) {
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
if ($application->build_pack === 'dockercompose') {

View File

@@ -307,14 +307,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
]
);
// $this->execute_remote_command(
// [
// "docker image prune -f >/dev/null 2>&1",
// "hidden" => true,
// "ignore_errors" => true,
// ]
// );
ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
}
}
@@ -497,13 +489,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
} else {
$this->write_deployment_configurations();
$server_workdir = $this->application->workdir();
$this->docker_compose_location = '/docker-compose.yaml';
$command = "{$this->coolify_variables} docker compose";
if ($this->env_filename) {
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
$command .= " --env-file {$server_workdir}/{$this->env_filename}";
}
$command .= " --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
$this->execute_remote_command(
['command' => $command, 'hidden' => true],
);
@@ -636,21 +628,26 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->server = $this->original_server;
}
$readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at);
$mainDir = $this->configuration_dir;
if ($this->application->settings->is_raw_compose_deployment_enabled) {
$mainDir = $this->application->workdir();
}
if ($this->pull_request_id === 0) {
$composeFileName = "$this->configuration_dir/docker-compose.yaml";
$composeFileName = "$mainDir/docker-compose.yaml";
} else {
$composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yaml";
$composeFileName = "$mainDir/docker-compose-pr-{$this->pull_request_id}.yaml";
$this->docker_compose_location = "/docker-compose-pr-{$this->pull_request_id}.yaml";
}
$this->execute_remote_command(
[
"mkdir -p $this->configuration_dir",
"mkdir -p $mainDir",
],
[
"echo '{$this->docker_compose_base64}' | base64 -d | tee $composeFileName > /dev/null",
],
[
"echo '{$readme}' > $this->configuration_dir/README.md",
"echo '{$readme}' > $mainDir/README.md",
]
);
if ($this->use_build_server) {
@@ -874,8 +871,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$envs->push($env->key.'='.$real_value);
}
// Add PORT if not exists, use the first port as default
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
$envs->push("PORT={$ports[0]}");
if ($this->build_pack !== 'dockercompose') {
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
$envs->push("PORT={$ports[0]}");
}
}
// Add HOST if not exists
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
@@ -918,15 +917,16 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$envs->push($env->key.'='.$real_value);
}
// Add PORT if not exists, use the first port as default
if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) {
$envs->push("PORT={$ports[0]}");
if ($this->build_pack !== 'dockercompose') {
if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) {
$envs->push("PORT={$ports[0]}");
}
}
// Add HOST if not exists
if ($this->application->environment_variables->where('key', 'HOST')->isEmpty()) {
$envs->push('HOST=0.0.0.0');
}
}
if ($envs->isEmpty()) {
$this->env_filename = null;
if ($this->use_build_server) {
@@ -1025,7 +1025,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->write_deployment_configurations();
$this->server = $this->original_server;
}
if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name) || $this->pull_request_id !== 0 || str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) {
if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty() || $this->pull_request_id !== 0 || str($this->application->custom_docker_run_options)->contains('--ip') || str($this->application->custom_docker_run_options)->contains('--ip6')) {
$this->application_deployment_queue->addLogEntry('----------------------------------------');
if (count($this->application->ports_mappings_array) > 0) {
$this->application_deployment_queue->addLogEntry('Application has ports mapped to the host system, rolling update is not supported.');
@@ -2027,27 +2027,43 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$this->application_deployment_queue->addLogEntry('Building docker image completed.');
}
/**
* @param int $timeout in seconds
*/
private function graceful_shutdown_container(string $containerName, int $timeout = 30)
{
try {
$this->execute_remote_command(
["docker stop --time=$timeout $containerName", 'hidden' => true, 'ignore_errors' => true],
["docker rm $containerName", 'hidden' => true, 'ignore_errors' => true]
);
} catch (\Exception $error) {
// report error if needed
}
$this->execute_remote_command(
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
);
}
private function stop_running_container(bool $force = false)
{
$this->application_deployment_queue->addLogEntry('Removing old containers.');
if ($this->newVersionIsHealthy || $force) {
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
if ($this->pull_request_id === 0) {
$containers = $containers->filter(function ($container) {
return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name.'-pr-'.$this->pull_request_id;
if ($this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty()) {
$this->graceful_shutdown_container($this->container_name);
} else {
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
if ($this->pull_request_id === 0) {
$containers = $containers->filter(function ($container) {
return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name.'-pr-'.$this->pull_request_id;
});
}
$containers->each(function ($container) {
$this->graceful_shutdown_container(data_get($container, 'Names'));
});
}
$containers->each(function ($container) {
$containerName = data_get($container, 'Names');
$this->execute_remote_command(
["docker rm -f $containerName >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true],
);
});
if ($this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name)) {
$this->execute_remote_command(
["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true],
);
}
} else {
if ($this->application->dockerfile || $this->application->build_pack === 'dockerfile' || $this->application->build_pack === 'dockerimage') {
$this->application_deployment_queue->addLogEntry('----------------------------------------');
@@ -2058,9 +2074,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$this->application_deployment_queue->update([
'status' => ApplicationDeploymentStatus::FAILED->value,
]);
$this->execute_remote_command(
["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true],
);
$this->graceful_shutdown_container($this->container_name);
}
}
@@ -2235,7 +2249,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
ray($code);
if ($code !== 69420) {
// 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
if ($this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name)) {
if ($this->application->settings->is_consistent_container_name_enabled || str($this->application->settings->custom_internal_name)->isNotEmpty()) {
// do not remove already running container
} else {
$this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr');

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Jobs;
use App\Models\InstanceSettings;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function handle(): void
{
try {
if (isDev() || isCloud()) {
return;
}
$settings = InstanceSettings::get();
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
$versions = $response->json();
$latest_version = data_get($versions, 'coolify.v4.version');
$current_version = config('version');
if (version_compare($latest_version, $current_version, '>')) {
// New version available
$settings->update(['new_version_available' => true]);
} else {
$settings->update(['new_version_available' => false]);
}
}
} catch (\Throwable $e) {
// Consider implementing a notification to administrators
}
}
}

View File

@@ -90,6 +90,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
try {
BackupCreated::dispatch($this->team->id);
// Check if team is exists
if (is_null($this->team)) {
$this->backup->update(['status' => 'failed']);
@@ -476,7 +477,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
} else {
$network = $this->database->destination->network;
}
$commands[] = "docker run --pull=always -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
instant_remote_process($commands, $this->server);

View File

@@ -26,17 +26,6 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
public function handle(): void
{
try {
// $isInprogress = false;
// $this->server->applications()->each(function ($application) use (&$isInprogress) {
// if ($application->isDeploymentInprogress()) {
// $isInprogress = true;
// return;
// }
// });
// if ($isInprogress) {
// throw new RuntimeException('DockerCleanupJob: ApplicationDeploymentQueue is not empty, skipping...');
// }
if (! $this->server->isFunctional()) {
return;
}
@@ -48,6 +37,12 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
}
$this->usageBefore = $this->server->getDiskUsage();
if (str($this->usageBefore)->isEmpty() || $this->usageBefore === null || $this->usageBefore === 0) {
Log::info('DockerCleanupJob force cleanup on '.$this->server->name);
CleanupDocker::run(server: $this->server, force: true);
return;
}
if ($this->usageBefore >= $this->server->settings->cleanup_after_percentage) {
CleanupDocker::run(server: $this->server, force: false);
$usageAfter = $this->server->getDiskUsage();

View File

@@ -2,6 +2,7 @@
namespace App\Jobs;
use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
@@ -16,16 +17,13 @@ class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 1000;
public function __construct() {}
public function handle(): void
{
try {
if (isDev() || isCloud()) {
return;
}
$settings = InstanceSettings::get();
$server = Server::findOrFail(0);
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
@@ -35,7 +33,6 @@ class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue
$latest_version = get_latest_version_of_coolify();
instant_remote_process(["docker pull -q ghcr.io/coollabsio/coolify:{$latest_version}"], $server, false);
$settings = \App\Models\InstanceSettings::get();
$current_version = config('version');
if (! $settings->is_auto_update_enabled) {
return;
@@ -46,10 +43,6 @@ class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue
if (version_compare($latest_version, $current_version, '<')) {
return;
}
instant_remote_process([
'curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh',
"bash /data/coolify/source/upgrade.sh $latest_version",
], $server);
} catch (\Throwable $e) {
throw $e;
}

View File

@@ -22,15 +22,16 @@ class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue
public function handle(): void
{
try {
if (! isDev()) {
ray('PullTemplatesAndVersions service-templates');
$response = Http::retry(3, 1000)->get(config('constants.services.official'));
if ($response->successful()) {
$services = $response->json();
File::put(base_path('templates/service-templates.json'), json_encode($services));
} else {
send_internal_notification('PullTemplatesAndVersions failed with: '.$response->status().' '.$response->body());
}
if (isDev() || isCloud()) {
return;
}
ray('PullTemplatesAndVersions service-templates');
$response = Http::retry(3, 1000)->get(config('constants.services.official'));
if ($response->successful()) {
$services = $response->json();
File::put(base_path('templates/service-templates.json'), json_encode($services));
} else {
send_internal_notification('PullTemplatesAndVersions failed with: '.$response->status().' '.$response->body());
}
} catch (\Throwable $e) {
send_internal_notification('PullTemplatesAndVersions failed with: '.$e->getMessage());

440
app/Jobs/ServerCheckJob.php Normal file
View File

@@ -0,0 +1,440 @@
<?php
namespace App\Jobs;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Docker\GetContainersStatus;
use App\Actions\Proxy\CheckProxy;
use App\Actions\Proxy\StartProxy;
use App\Actions\Server\InstallLogDrain;
use App\Models\ApplicationPreview;
use App\Models\Server;
use App\Models\ServiceDatabase;
use App\Notifications\Container\ContainerRestarted;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Arr;
class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 3;
public $containers;
public $applications;
public $databases;
public $services;
public $previews;
public function backoff(): int
{
return isDev() ? 1 : 3;
}
public function __construct(public Server $server) {}
// public function middleware(): array
// {
// return [(new WithoutOverlapping($this->server->uuid))];
// }
// public function uniqueId(): int
// {
// return $this->server->uuid;
// }
public function handle()
{
try {
$this->applications = $this->server->applications();
$this->databases = $this->server->databases();
$this->services = $this->server->services()->get();
$this->previews = $this->server->previews();
$up = $this->serverStatus();
if (! $up) {
ray('Server is not reachable.');
return 'Server is not reachable.';
}
if (! $this->server->isFunctional()) {
ray('Server is not ready.');
return 'Server is not ready.';
}
if (! $this->server->isSwarmWorker() && ! $this->server->isBuildServer()) {
['containers' => $this->containers, 'containerReplicates' => $containerReplicates] = $this->server->getContainers();
if (is_null($this->containers)) {
return 'No containers found.';
}
GetContainersStatus::run($this->server, $this->containers, $containerReplicates);
$this->checkLogDrainContainer();
$this->checkSentinel();
}
} catch (\Throwable $e) {
ray($e->getMessage());
return handleError($e);
}
}
private function checkSentinel()
{
if ($this->server->isSentinelEnabled()) {
$sentinelContainerFound = $this->containers->filter(function ($value, $key) {
return data_get($value, 'Name') === '/coolify-sentinel';
})->first();
if ($sentinelContainerFound) {
$status = data_get($sentinelContainerFound, 'State.Status');
if ($status !== 'running') {
PullSentinelImageJob::dispatch($this);
}
}
}
}
private function serverStatus()
{
['uptime' => $uptime] = $this->server->validateConnection();
if ($uptime) {
if ($this->server->unreachable_notification_sent === true) {
$this->server->update(['unreachable_notification_sent' => false]);
}
} else {
// $this->server->team?->notify(new Unreachable($this->server));
foreach ($this->applications as $application) {
$application->update(['status' => 'exited']);
}
foreach ($this->databases as $database) {
$database->update(['status' => 'exited']);
}
foreach ($this->services as $service) {
$apps = $service->applications()->get();
$dbs = $service->databases()->get();
foreach ($apps as $app) {
$app->update(['status' => 'exited']);
}
foreach ($dbs as $db) {
$db->update(['status' => 'exited']);
}
}
return false;
}
return true;
}
private function checkLogDrainContainer()
{
$foundLogDrainContainer = $this->containers->filter(function ($value, $key) {
return data_get($value, 'Name') === '/coolify-log-drain';
})->first();
if ($foundLogDrainContainer) {
$status = data_get($foundLogDrainContainer, 'State.Status');
if ($status !== 'running') {
InstallLogDrain::dispatch($this->server);
}
} else {
InstallLogDrain::dispatch($this->server);
}
}
private function containerStatus()
{
$foundApplications = [];
$foundApplicationPreviews = [];
$foundDatabases = [];
$foundServices = [];
foreach ($this->containers as $container) {
if ($this->server->isSwarm()) {
$labels = data_get($container, 'Spec.Labels');
$uuid = data_get($labels, 'coolify.name');
} else {
$labels = data_get($container, 'Config.Labels');
}
$containerStatus = data_get($container, 'State.Status');
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
$containerStatus = "$containerStatus ($containerHealth)";
$labels = Arr::undot(format_docker_labels_to_json($labels));
$applicationId = data_get($labels, 'coolify.applicationId');
if ($applicationId) {
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
if ($pullRequestId) {
if (str($applicationId)->contains('-')) {
$applicationId = str($applicationId)->before('-');
}
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
if ($preview) {
$foundApplicationPreviews[] = $preview->id;
$statusFromDb = $preview->status;
if ($statusFromDb !== $containerStatus) {
$preview->update(['status' => $containerStatus]);
}
} else {
//Notify user that this container should not be there.
}
} else {
$application = $this->applications->where('id', $applicationId)->first();
if ($application) {
$foundApplications[] = $application->id;
$statusFromDb = $application->status;
if ($statusFromDb !== $containerStatus) {
$application->update(['status' => $containerStatus]);
}
} else {
//Notify user that this container should not be there.
}
}
} else {
$uuid = data_get($labels, 'com.docker.compose.service');
$type = data_get($labels, 'coolify.type');
if ($uuid) {
if ($type === 'service') {
$database_id = data_get($labels, 'coolify.service.subId');
if ($database_id) {
$service_db = ServiceDatabase::where('id', $database_id)->first();
if ($service_db) {
$uuid = data_get($service_db, 'service.uuid');
if ($uuid) {
$isPublic = data_get($service_db, 'is_public');
if ($isPublic) {
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} else {
return data_get($value, 'Name') === "/$uuid-proxy";
}
})->first();
if (! $foundTcpProxy) {
StartDatabaseProxy::run($service_db);
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
}
}
}
}
}
} else {
$database = $this->databases->where('uuid', $uuid)->first();
if ($database) {
$isPublic = data_get($database, 'is_public');
$foundDatabases[] = $database->id;
$statusFromDb = $database->status;
if ($statusFromDb !== $containerStatus) {
$database->update(['status' => $containerStatus]);
}
if ($isPublic) {
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
} else {
return data_get($value, 'Name') === "/$uuid-proxy";
}
})->first();
if (! $foundTcpProxy) {
StartDatabaseProxy::run($database);
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
}
}
} else {
// Notify user that this container should not be there.
}
}
}
if (data_get($container, 'Name') === '/coolify-db') {
$foundDatabases[] = 0;
}
}
$serviceLabelId = data_get($labels, 'coolify.serviceId');
if ($serviceLabelId) {
$subType = data_get($labels, 'coolify.service.subType');
$subId = data_get($labels, 'coolify.service.subId');
$service = $this->services->where('id', $serviceLabelId)->first();
if (! $service) {
continue;
}
if ($subType === 'application') {
$service = $service->applications()->where('id', $subId)->first();
} else {
$service = $service->databases()->where('id', $subId)->first();
}
if ($service) {
$foundServices[] = "$service->id-$service->name";
$statusFromDb = $service->status;
if ($statusFromDb !== $containerStatus) {
// ray('Updating status: ' . $containerStatus);
$service->update(['status' => $containerStatus]);
}
}
}
}
$exitedServices = collect([]);
foreach ($this->services as $service) {
$apps = $service->applications()->get();
$dbs = $service->databases()->get();
foreach ($apps as $app) {
if (in_array("$app->id-$app->name", $foundServices)) {
continue;
} else {
$exitedServices->push($app);
}
}
foreach ($dbs as $db) {
if (in_array("$db->id-$db->name", $foundServices)) {
continue;
} else {
$exitedServices->push($db);
}
}
}
$exitedServices = $exitedServices->unique('id');
foreach ($exitedServices as $exitedService) {
if (str($exitedService->status)->startsWith('exited')) {
continue;
}
$name = data_get($exitedService, 'name');
$fqdn = data_get($exitedService, 'fqdn');
if ($name) {
if ($fqdn) {
$containerName = "$name, available at $fqdn";
} else {
$containerName = $name;
}
} else {
if ($fqdn) {
$containerName = $fqdn;
} else {
$containerName = null;
}
}
$projectUuid = data_get($service, 'environment.project.uuid');
$serviceUuid = data_get($service, 'uuid');
$environmentName = data_get($service, 'environment.name');
if ($projectUuid && $serviceUuid && $environmentName) {
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
} else {
$url = null;
}
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
$exitedService->update(['status' => 'exited']);
}
$notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
foreach ($notRunningApplications as $applicationId) {
$application = $this->applications->where('id', $applicationId)->first();
if (str($application->status)->startsWith('exited')) {
continue;
}
$application->update(['status' => 'exited']);
$name = data_get($application, 'name');
$fqdn = data_get($application, 'fqdn');
$containerName = $name ? "$name ($fqdn)" : $fqdn;
$projectUuid = data_get($application, 'environment.project.uuid');
$applicationUuid = data_get($application, 'uuid');
$environment = data_get($application, 'environment.name');
if ($projectUuid && $applicationUuid && $environment) {
$url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
} else {
$url = null;
}
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningApplicationPreviews = $this->previews->pluck('id')->diff($foundApplicationPreviews);
foreach ($notRunningApplicationPreviews as $previewId) {
$preview = $this->previews->where('id', $previewId)->first();
if (str($preview->status)->startsWith('exited')) {
continue;
}
$preview->update(['status' => 'exited']);
$name = data_get($preview, 'name');
$fqdn = data_get($preview, 'fqdn');
$containerName = $name ? "$name ($fqdn)" : $fqdn;
$projectUuid = data_get($preview, 'application.environment.project.uuid');
$environmentName = data_get($preview, 'application.environment.name');
$applicationUuid = data_get($preview, 'application.uuid');
if ($projectUuid && $applicationUuid && $environmentName) {
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
} else {
$url = null;
}
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningDatabases = $this->databases->pluck('id')->diff($foundDatabases);
foreach ($notRunningDatabases as $database) {
$database = $this->databases->where('id', $database)->first();
if (str($database->status)->startsWith('exited')) {
continue;
}
$database->update(['status' => 'exited']);
$name = data_get($database, 'name');
$fqdn = data_get($database, 'fqdn');
$containerName = $name;
$projectUuid = data_get($database, 'environment.project.uuid');
$environmentName = data_get($database, 'environment.name');
$databaseUuid = data_get($database, 'uuid');
if ($projectUuid && $databaseUuid && $environmentName) {
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
} else {
$url = null;
}
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
// Check if proxy is running
$this->server->proxyType();
$foundProxyContainer = $this->containers->filter(function ($value, $key) {
if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
} else {
return data_get($value, 'Name') === '/coolify-proxy';
}
})->first();
if (! $foundProxyContainer) {
try {
$shouldStart = CheckProxy::run($this->server);
if ($shouldStart) {
StartProxy::run($this->server, false);
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
}
} catch (\Throwable $e) {
ray($e);
}
} else {
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
$this->server->save();
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Jobs;
use App\Actions\Server\UpdateCoolify;
use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class UpdateCoolifyJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 600;
public function handle(): void
{
try {
CheckForUpdatesJob::dispatchSync();
$settings = InstanceSettings::get();
if (! $settings->new_version_available) {
Log::info('No new version available. Skipping update.');
return;
}
$server = Server::findOrFail(0);
if (! $server) {
Log::error('Server not found. Cannot proceed with update.');
return;
}
Log::info('Starting Coolify update process...');
UpdateCoolify::run(false); // false means it's not a manual update
$settings->update(['new_version_available' => false]);
Log::info('Coolify update completed successfully.');
} catch (\Throwable $e) {
Log::error('UpdateCoolifyJob failed: '.$e->getMessage());
// Consider implementing a notification to administrators
}
}
}

View File

@@ -179,7 +179,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
public function getProxyType()
{
// Set Default Proxy Type
$this->selectProxy(ProxyTypes::TRAEFIK_V2->value);
$this->selectProxy(ProxyTypes::TRAEFIK->value);
// $proxyTypeSet = $this->createdServer->proxy->type;
// if (!$proxyTypeSet) {
// $this->currentState = 'select-proxy';

View File

@@ -52,7 +52,7 @@ class Docker extends Component
if (request()->query('network_name')) {
$this->network = request()->query('network_name');
} else {
$this->network = new Cuid2(7);
$this->network = new Cuid2;
}
if ($this->servers->count() > 0) {
$this->name = str("{$this->servers->first()->name}-{$this->network}")->kebab();

View File

@@ -39,7 +39,7 @@ class MonacoEditor extends Component
public function render()
{
if (is_null($this->id)) {
$this->id = new Cuid2(7);
$this->id = new Cuid2;
}
if (is_null($this->name)) {

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Livewire;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
class NavbarDeleteTeam 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 redirect()->route('team.index');
}
public function render()
{
return view('livewire.navbar-delete-team');
}
}

View File

@@ -91,11 +91,25 @@ class Advanced extends Component
public function saveCustomName()
{
if (isset($this->application->settings->custom_internal_name)) {
if (str($this->application->settings->custom_internal_name)->isNotEmpty()) {
$this->application->settings->custom_internal_name = str($this->application->settings->custom_internal_name)->slug()->value();
} else {
$this->application->settings->custom_internal_name = null;
}
$customInternalName = $this->application->settings->custom_internal_name;
$server = $this->application->destination->server;
$allApplications = $server->applications();
$foundSameInternalName = $allApplications->filter(function ($application) {
return $application->id !== $this->application->id && $application->settings->custom_internal_name === $this->application->settings->custom_internal_name;
});
if ($foundSameInternalName->isNotEmpty()) {
$this->dispatch('error', 'This custom container name is already in use by another application on this server.');
$this->application->settings->custom_internal_name = $customInternalName;
$this->application->settings->refresh();
return;
}
$this->application->settings->save();
$this->dispatch('success', 'Custom name saved.');
}

View File

@@ -214,7 +214,7 @@ class General extends Component
}
$this->dispatch('success', 'Docker compose file loaded.');
$this->dispatch('compose_loaded');
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
$this->dispatch('refreshEnvs');
} catch (\Throwable $e) {
$this->application->docker_compose_location = $this->initialDockerComposeLocation;
@@ -228,7 +228,7 @@ class General extends Component
public function generateDomain(string $serviceName)
{
$uuid = new Cuid2(7);
$uuid = new Cuid2;
$domain = generateFqdn($this->application->destination->server, $uuid);
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);

View File

@@ -5,8 +5,6 @@ namespace App\Livewire\Project\Application;
use App\Actions\Application\StopApplication;
use App\Actions\Docker\GetContainersStatus;
use App\Events\ApplicationStatusChanged;
use App\Jobs\ContainerStatusJob;
use App\Jobs\ServerStatusJob;
use App\Models\Application;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
@@ -46,11 +44,7 @@ class Heading extends Component
{
if ($this->application->destination->server->isFunctional()) {
GetContainersStatus::dispatch($this->application->destination->server)->onQueue('high');
// dispatch(new ContainerStatusJob($this->application->destination->server));
} else {
dispatch(new ServerStatusJob($this->application->destination->server));
}
if ($showNotification) {
$this->dispatch('success', 'Success', 'Application status updated.');
}
@@ -102,7 +96,7 @@ class Heading extends Component
protected function setDeploymentUuid()
{
$this->deploymentUuid = new Cuid2(7);
$this->deploymentUuid = new Cuid2;
$this->parameters['deployment_uuid'] = $this->deploymentUuid;
}

View File

@@ -85,7 +85,7 @@ class Previews extends Component
$template = $this->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $preview->pull_request_id, $preview_fqdn);
@@ -170,7 +170,7 @@ class Previews extends Component
protected function setDeploymentUuid()
{
$this->deployment_uuid = new Cuid2(7);
$this->deployment_uuid = new Cuid2;
$this->parameters['deployment_uuid'] = $this->deployment_uuid;
}

View File

@@ -44,7 +44,7 @@ class PreviewsCompose extends Component
$template = $this->preview->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->preview->pull_request_id, $preview_fqdn);

View File

@@ -23,7 +23,7 @@ class Rollback extends Component
public function rollbackImage($commit)
{
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
queue_application_deployment(
application: $this->application,

View File

@@ -47,7 +47,7 @@ class CloneMe extends Component
$this->environment = $this->project->environments->where('name', $this->environment_name)->first();
$this->project_id = $this->project->id;
$this->servers = currentTeam()->servers;
$this->newName = str($this->project->name.'-clone-'.(string) new Cuid2(7))->slug();
$this->newName = str($this->project->name.'-clone-'.(string) new Cuid2)->slug();
}
public function render()
@@ -106,7 +106,7 @@ class CloneMe extends Component
$databases = $this->environment->databases();
$services = $this->environment->services;
foreach ($applications as $application) {
$uuid = (string) new Cuid2(7);
$uuid = (string) new Cuid2;
$newApplication = $application->replicate()->fill([
'uuid' => $uuid,
'fqdn' => generateFqdn($this->server, $uuid),
@@ -133,7 +133,7 @@ class CloneMe extends Component
}
}
foreach ($databases as $database) {
$uuid = (string) new Cuid2(7);
$uuid = (string) new Cuid2;
$newDatabase = $database->replicate()->fill([
'uuid' => $uuid,
'status' => 'exited',
@@ -161,7 +161,7 @@ class CloneMe extends Component
}
}
foreach ($services as $service) {
$uuid = (string) new Cuid2(7);
$uuid = (string) new Cuid2;
$newService = $service->replicate()->fill([
'uuid' => $uuid,
'environment_id' => $environment->id,

View File

@@ -48,7 +48,7 @@ class DockerImage extends Component
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
ray($image, $tag);
$application = Application::create([
'name' => 'docker-image-'.new Cuid2(7),
'name' => 'docker-image-'.new Cuid2,
'repository_project_id' => 0,
'git_repository' => 'coollabsio/coolify',
'git_branch' => 'main',

View File

@@ -53,7 +53,7 @@ CMD ["nginx", "-g", "daemon off;"]
$port = 80;
}
$application = Application::create([
'name' => 'dockerfile-'.new Cuid2(7),
'name' => 'dockerfile-'.new Cuid2,
'repository_project_id' => 0,
'git_repository' => 'coollabsio/coolify',
'git_branch' => 'main',

View File

@@ -25,7 +25,6 @@ class Configuration extends Component
return [
"echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
'check_status',
'refresh' => '$refresh',
];
}
@@ -76,8 +75,7 @@ class Configuration extends Component
{
try {
GetContainersStatus::run($this->service->server);
// dispatch_sync(new ContainerStatusJob($this->service->server));
$this->dispatch('refresh')->self();
$this->dispatch('$refresh');
} catch (\Exception $e) {
return handleError($e, $this);
}

View File

@@ -26,6 +26,8 @@ class FileStorage extends Component
public ?string $workdir = null;
public bool $permanently_delete = true;
protected $rules = [
'fileStorage.is_directory' => 'required',
'fileStorage.fs_path' => 'required',
@@ -56,7 +58,7 @@ class FileStorage extends Component
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
}
}
@@ -71,20 +73,27 @@ class FileStorage extends Component
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
}
}
public function delete()
{
try {
$this->fileStorage->deleteStorageOnServer();
$message = 'File deleted.';
if ($this->fileStorage->is_directory) {
$message = 'Directory deleted.';
}
if ($this->permanently_delete) {
$message = 'Directory deleted from the server.';
$this->fileStorage->deleteStorageOnServer();
}
$this->fileStorage->delete();
$this->dispatch('success', 'File deleted.');
$this->dispatch('success', $message);
} catch (\Throwable $e) {
return handleError($e, $this);
} finally {
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
}
}

View File

@@ -49,6 +49,11 @@ class Navbar extends Component
}
}
public function check_status_without_notification()
{
$this->dispatch('check_status');
}
public function check_status()
{
$this->dispatch('check_status');
@@ -63,6 +68,8 @@ class Navbar extends Component
public function checkDeployments()
{
try {
// TODO: This is a temporary solution. We need to refactor this.
// We need to delete null bytes somehow.
$activity = Activity::where('properties->type_uuid', $this->service->uuid)->latest()->first();
$status = data_get($activity, 'properties.status');
if ($status === 'queued' || $status === 'in_progress') {
@@ -70,7 +77,7 @@ class Navbar extends Component
} else {
$this->isDeploymentProgress = false;
}
} catch (\Exception $e) {
} catch (\Throwable $e) {
$this->isDeploymentProgress = false;
}
}

View File

@@ -9,14 +9,36 @@ class Storage extends Component
{
public $resource;
public $fileStorage;
public function getListeners()
{
$teamId = auth()->user()->currentTeam()->id;
return [
"echo-private:team.{$teamId},FileStorageChanged" => 'refreshStoragesFromEvent',
'refreshStorages',
'addNewVolume',
'refresh_storages' => '$refresh',
];
}
public function mount()
{
$this->refreshStorages();
}
public function refreshStoragesFromEvent()
{
$this->refreshStorages();
$this->dispatch('warning', 'File storage changed. Usually it means that the file / directory is already defined on the server, so Coolify set it up for you properly on the UI.');
}
public function refreshStorages()
{
$this->fileStorage = $this->resource->fileStorages()->get();
$this->dispatch('$refresh');
}
public function addNewVolume($data)
{
try {
@@ -30,7 +52,7 @@ class Storage extends Component
$this->resource->refresh();
$this->dispatch('success', 'Storage added successfully');
$this->dispatch('clearAddStorage');
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -22,7 +22,7 @@ class Danger extends Component
public function mount()
{
$this->modalId = new Cuid2(7);
$this->modalId = new Cuid2;
$parameters = get_route_parameters();
$this->projectUuid = data_get($parameters, 'project_uuid');
$this->environmentName = data_get($parameters, 'environment_name');

View File

@@ -67,7 +67,7 @@ class Destination extends Component
return;
}
$deployment_uuid = new Cuid2(7);
$deployment_uuid = new Cuid2;
$server = Server::find($server_id);
$destination = StandaloneDocker::find($network_id);
queue_application_deployment(

View File

@@ -48,14 +48,14 @@ class Add extends Component
public function submit()
{
$this->validate();
if (str($this->value)->startsWith('{{') && str($this->value)->endsWith('}}')) {
$type = str($this->value)->after('{{')->before('.')->value;
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
$this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
// if (str($this->value)->startsWith('{{') && str($this->value)->endsWith('}}')) {
// $type = str($this->value)->after('{{')->before('.')->value;
// if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
// $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
return;
}
}
// return;
// }
// }
$this->dispatch('saveKey', [
'key' => $this->key,
'value' => $this->value,

View File

@@ -39,7 +39,7 @@ class All extends Component
if (str($this->resourceClass)->contains($resourceWithPreviews) && ! $simpleDockerfile) {
$this->showPreview = true;
}
$this->modalId = new Cuid2(7);
$this->modalId = new Cuid2;
$this->sortMe();
$this->getDevView();
}
@@ -125,14 +125,14 @@ class All extends Component
continue;
}
$found->value = $variable;
if (str($found->value)->startsWith('{{') && str($found->value)->endsWith('}}')) {
$type = str($found->value)->after('{{')->before('.')->value;
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
$this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
// if (str($found->value)->startsWith('{{') && str($found->value)->endsWith('}}')) {
// $type = str($found->value)->after('{{')->before('.')->value;
// if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
// $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
return;
}
}
// return;
// }
// }
$found->save();
continue;
@@ -140,14 +140,14 @@ class All extends Component
$environment = new EnvironmentVariable;
$environment->key = $key;
$environment->value = $variable;
if (str($environment->value)->startsWith('{{') && str($environment->value)->endsWith('}}')) {
$type = str($environment->value)->after('{{')->before('.')->value;
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
$this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
// if (str($environment->value)->startsWith('{{') && str($environment->value)->endsWith('}}')) {
// $type = str($environment->value)->after('{{')->before('.')->value;
// if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
// $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
return;
}
}
// return;
// }
// }
$environment->is_build_time = false;
$environment->is_multiline = false;
$environment->is_preview = $isPreview ? true : false;

View File

@@ -58,7 +58,7 @@ class Show extends Component
if ($this->env->getMorphClass() === 'App\Models\SharedEnvironmentVariable') {
$this->isSharedVariable = true;
}
$this->modalId = new Cuid2(7);
$this->modalId = new Cuid2;
$this->parameters = get_route_parameters();
$this->checkEnvs();
}
@@ -108,14 +108,14 @@ class Show extends Component
} else {
$this->validate();
}
if (str($this->env->value)->startsWith('{{') && str($this->env->value)->endsWith('}}')) {
$type = str($this->env->value)->after('{{')->before('.')->value;
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
$this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
// if (str($this->env->value)->startsWith('{{') && str($this->env->value)->endsWith('}}')) {
// $type = str($this->env->value)->after('{{')->before('.')->value;
// if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
// $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
return;
}
}
// return;
// }
// }
$this->serialize();
$this->env->save();
$this->dispatch('success', 'Environment variable updated.');

View File

@@ -39,7 +39,7 @@ class ResourceOperations extends Component
if (! $new_destination) {
return $this->addError('destination_id', 'Destination not found.');
}
$uuid = (string) new Cuid2(7);
$uuid = (string) new Cuid2;
$server = $new_destination->server;
if ($this->resource->getMorphClass() === 'App\Models\Application') {
$new_resource = $this->resource->replicate()->fill([
@@ -87,7 +87,7 @@ class ResourceOperations extends Component
$this->resource->getMorphClass() === 'App\Models\StandaloneDragonfly' ||
$this->resource->getMorphClass() === 'App\Models\StandaloneClickhouse'
) {
$uuid = (string) new Cuid2(7);
$uuid = (string) new Cuid2;
$new_resource = $this->resource->replicate()->fill([
'uuid' => $uuid,
'name' => $this->resource->name.'-clone-'.$uuid,
@@ -121,7 +121,7 @@ class ResourceOperations extends Component
return redirect()->to($route);
} elseif ($this->resource->type() === 'service') {
$uuid = (string) new Cuid2(7);
$uuid = (string) new Cuid2;
$new_resource = $this->resource->replicate()->fill([
'uuid' => $uuid,
'name' => $this->resource->name.'-clone-'.$uuid,

View File

@@ -47,7 +47,7 @@ class Show extends Component
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
}
$this->modalId = new Cuid2(7);
$this->modalId = new Cuid2;
$this->task = ModelsScheduledTask::where('uuid', request()->route('task_uuid'))->first();
}

View File

@@ -54,7 +54,11 @@ class Add extends Component
public function mount()
{
$this->file_storage_directory_source = application_configuration_dir()."/{$this->resource->uuid}";
if (str($this->resource->getMorphClass())->contains('Standalone')) {
$this->file_storage_directory_source = database_configuration_dir()."/{$this->resource->uuid}";
} else {
$this->file_storage_directory_source = application_configuration_dir()."/{$this->resource->uuid}";
}
$this->uuid = $this->resource->uuid;
$this->parameters = get_route_parameters();
if (data_get($this->parameters, 'application_uuid')) {
@@ -92,7 +96,7 @@ class Add extends Component
'resource_type' => get_class($this->resource),
],
);
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -119,7 +123,7 @@ class Add extends Component
'resource_type' => get_class($this->resource),
],
);
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -8,5 +8,5 @@ class All extends Component
{
public $resource;
protected $listeners = ['refresh_storages' => '$refresh'];
protected $listeners = ['refreshStorages' => '$refresh'];
}

View File

@@ -39,6 +39,6 @@ class Show extends Component
public function delete()
{
$this->storage->delete();
$this->dispatch('refresh_storages');
$this->dispatch('refreshStorages');
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Livewire\Server;
use App\Actions\Server\InstallLogDrain;
use App\Actions\Server\StopLogDrain;
use App\Models\Server;
use Livewire\Component;
@@ -132,6 +133,9 @@ class LogDrains extends Component
'is_logdrain_axiom_enabled' => false,
]);
}
if (! $this->server->isLogDrainEnabled()) {
StopLogDrain::dispatch($this->server);
}
$this->server->settings->save();
$this->dispatch('success', 'Settings saved.');

View File

@@ -104,7 +104,7 @@ class ByIp extends Component
'private_key_id' => $this->private_key_id,
'proxy' => [
// set default proxy type to traefik v2
'type' => ProxyTypes::TRAEFIK_V2->value,
'type' => ProxyTypes::TRAEFIK->value,
'status' => ProxyStatus::EXITED->value,
],
];

View File

@@ -20,6 +20,10 @@ class Proxy extends Component
protected $listeners = ['proxyStatusUpdated', 'saveConfiguration' => 'submit'];
protected $rules = [
'server.settings.generate_exact_labels' => 'required|boolean',
];
public function mount()
{
$this->selectedProxy = $this->server->proxyType();
@@ -31,13 +35,13 @@ class Proxy extends Component
$this->dispatch('refresh')->self();
}
public function change_proxy()
public function changeProxy()
{
$this->server->proxy = null;
$this->server->save();
}
public function select_proxy($proxy_type)
public function selectProxy($proxy_type)
{
$this->server->proxy->set('status', 'exited');
$this->server->proxy->set('type', $proxy_type);
@@ -49,6 +53,17 @@ class Proxy extends Component
$this->dispatch('proxyStatusUpdated');
}
public function instantSave()
{
try {
$this->validate();
$this->server->settings->save();
$this->dispatch('success', 'Settings saved.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function submit()
{
try {

View File

@@ -21,7 +21,6 @@ class DynamicConfigurations extends Component
return [
"echo-private:team.{$teamId},ProxyStatusChanged" => 'loadDynamicConfigurations',
'loadDynamicConfigurations',
'refresh' => '$refresh',
];
}
@@ -42,7 +41,7 @@ class DynamicConfigurations extends Component
$contents[$without_extension] = instant_remote_process(["cat {$proxy_path}/dynamic/{$file}"], $this->server);
}
$this->contents = $contents;
$this->dispatch('refresh');
$this->dispatch('$refresh');
}
public function mount()

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Server\Proxy;
use App\Enums\ProxyTypes;
use App\Models\Server;
use Livewire\Component;
use Symfony\Component\Yaml\Yaml;
@@ -45,7 +46,7 @@ class NewDynamicConfiguration extends Component
return redirect()->route('server.index');
}
$proxy_type = $this->server->proxyType();
if ($proxy_type === 'TRAEFIK_V2') {
if ($proxy_type === ProxyTypes::TRAEFIK->value) {
if (! str($this->fileName)->endsWith('.yaml') && ! str($this->fileName)->endsWith('.yml')) {
$this->fileName = "{$this->fileName}.yaml";
}
@@ -69,7 +70,7 @@ class NewDynamicConfiguration extends Component
return;
}
}
if ($proxy_type === 'TRAEFIK_V2') {
if ($proxy_type === ProxyTypes::TRAEFIK->value) {
$yaml = Yaml::parse($this->value);
$yaml = Yaml::dump($yaml, 10, 2);
$this->value = $yaml;

View File

@@ -1,111 +0,0 @@
<?php
namespace App\Livewire\Settings;
use App\Models\InstanceSettings as ModelsInstanceSettings;
use App\Models\Server;
use Livewire\Component;
class Configuration extends Component
{
public ModelsInstanceSettings $settings;
public bool $do_not_track;
public bool $is_auto_update_enabled;
public bool $is_registration_enabled;
public bool $is_dns_validation_enabled;
public bool $is_api_enabled;
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
protected Server $server;
protected $rules = [
'settings.fqdn' => 'nullable',
'settings.resale_license' => 'nullable',
'settings.public_port_min' => 'required',
'settings.public_port_max' => 'required',
'settings.custom_dns_servers' => 'nullable',
'settings.instance_name' => 'nullable',
'settings.allowed_ips' => 'nullable',
];
protected $validationAttributes = [
'settings.fqdn' => 'FQDN',
'settings.resale_license' => 'Resale License',
'settings.public_port_min' => 'Public port min',
'settings.public_port_max' => 'Public port max',
'settings.custom_dns_servers' => 'Custom DNS servers',
'settings.allowed_ips' => 'Allowed IPs',
];
public function mount()
{
$this->do_not_track = $this->settings->do_not_track;
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
$this->is_registration_enabled = $this->settings->is_registration_enabled;
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
$this->is_api_enabled = $this->settings->is_api_enabled;
}
public function instantSave()
{
$this->settings->do_not_track = $this->do_not_track;
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
$this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
$this->settings->is_api_enabled = $this->is_api_enabled;
$this->settings->save();
$this->dispatch('success', 'Settings updated!');
}
public function submit()
{
try {
$error_show = false;
$this->server = Server::findOrFail(0);
$this->resetErrorBag();
if ($this->settings->public_port_min > $this->settings->public_port_max) {
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
return;
}
$this->validate();
if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) {
if (! validate_dns_entry($this->settings->fqdn, $this->server)) {
$this->dispatch('error', "Validating DNS failed.<br><br>Make sure you have added the DNS records correctly.<br><br>{$this->settings->fqdn}->{$this->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
$error_show = true;
}
}
if ($this->settings->fqdn) {
check_domain_usage(domain: $this->settings->fqdn);
}
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim();
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
return str($dns)->trim()->lower();
});
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique();
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(',');
$this->settings->allowed_ips = str($this->settings->allowed_ips)->replaceEnd(',', '')->trim();
$this->settings->allowed_ips = str($this->settings->allowed_ips)->trim()->explode(',')->map(function ($ip) {
return str($ip)->trim();
});
$this->settings->allowed_ips = $this->settings->allowed_ips->unique();
$this->settings->allowed_ips = $this->settings->allowed_ips->implode(',');
$this->settings->save();
$this->server->setupDynamicProxyConfiguration();
if (! $error_show) {
$this->dispatch('success', 'Instance settings updated successfully!');
}
} catch (\Exception $e) {
return handleError($e, $this);
}
}
}

View File

@@ -2,39 +2,170 @@
namespace App\Livewire\Settings;
use App\Jobs\CheckForUpdatesJob;
use App\Models\InstanceSettings;
use App\Models\S3Storage;
use App\Models\StandalonePostgresql;
use App\Models\Server;
use Livewire\Component;
class Index extends Component
{
public InstanceSettings $settings;
public StandalonePostgresql $database;
public bool $do_not_track;
public $s3s;
public bool $is_auto_update_enabled;
public bool $is_registration_enabled;
public bool $is_dns_validation_enabled;
public bool $is_api_enabled;
public string $auto_update_frequency;
public string $update_check_frequency;
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
protected Server $server;
protected $rules = [
'settings.fqdn' => 'nullable',
'settings.resale_license' => 'nullable',
'settings.public_port_min' => 'required',
'settings.public_port_max' => 'required',
'settings.custom_dns_servers' => 'nullable',
'settings.instance_name' => 'nullable',
'settings.allowed_ips' => 'nullable',
'settings.is_auto_update_enabled' => 'boolean',
'auto_update_frequency' => 'string',
'update_check_frequency' => 'string',
];
protected $validationAttributes = [
'settings.fqdn' => 'FQDN',
'settings.resale_license' => 'Resale License',
'settings.public_port_min' => 'Public port min',
'settings.public_port_max' => 'Public port max',
'settings.custom_dns_servers' => 'Custom DNS servers',
'settings.allowed_ips' => 'Allowed IPs',
'settings.is_auto_update_enabled' => 'Auto Update Enabled',
'auto_update_frequency' => 'Auto Update Frequency',
'update_check_frequency' => 'Update Check Frequency',
];
public function mount()
{
if (isInstanceAdmin()) {
$settings = \App\Models\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;
$this->settings = InstanceSettings::get();
$this->do_not_track = $this->settings->do_not_track;
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
$this->is_registration_enabled = $this->settings->is_registration_enabled;
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
$this->is_api_enabled = $this->settings->is_api_enabled;
$this->auto_update_frequency = $this->settings->auto_update_frequency;
$this->update_check_frequency = $this->settings->update_check_frequency;
} else {
return redirect()->route('dashboard');
}
}
public function instantSave()
{
$this->settings->do_not_track = $this->do_not_track;
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
$this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
$this->settings->is_api_enabled = $this->is_api_enabled;
$this->settings->auto_update_frequency = $this->auto_update_frequency;
$this->settings->update_check_frequency = $this->update_check_frequency;
$this->settings->save();
$this->dispatch('success', 'Settings updated!');
}
public function submit()
{
try {
$error_show = false;
$this->server = Server::findOrFail(0);
$this->resetErrorBag();
if ($this->settings->public_port_min > $this->settings->public_port_max) {
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
return;
}
$this->validate();
if ($this->is_auto_update_enabled && ! validate_cron_expression($this->auto_update_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.');
if (empty($this->auto_update_frequency)) {
$this->auto_update_frequency = '0 0 * * *';
}
return;
}
if (! validate_cron_expression($this->update_check_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.');
if (empty($this->update_check_frequency)) {
$this->update_check_frequency = '0 * * * *';
}
return;
}
if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) {
if (! validate_dns_entry($this->settings->fqdn, $this->server)) {
$this->dispatch('error', "Validating DNS failed.<br><br>Make sure you have added the DNS records correctly.<br><br>{$this->settings->fqdn}->{$this->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
$error_show = true;
}
}
if ($this->settings->fqdn) {
check_domain_usage(domain: $this->settings->fqdn);
}
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim();
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
return str($dns)->trim()->lower();
});
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique();
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(',');
$this->settings->allowed_ips = str($this->settings->allowed_ips)->replaceEnd(',', '')->trim();
$this->settings->allowed_ips = str($this->settings->allowed_ips)->trim()->explode(',')->map(function ($ip) {
return str($ip)->trim();
});
$this->settings->allowed_ips = $this->settings->allowed_ips->unique();
$this->settings->allowed_ips = $this->settings->allowed_ips->implode(',');
$this->settings->do_not_track = $this->do_not_track;
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
$this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
$this->settings->is_api_enabled = $this->is_api_enabled;
$this->settings->auto_update_frequency = $this->auto_update_frequency;
$this->settings->update_check_frequency = $this->update_check_frequency;
$this->settings->save();
$this->server->setupDynamicProxyConfiguration();
if (! $error_show) {
$this->dispatch('success', 'Instance settings updated successfully!');
}
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function checkManually()
{
CheckForUpdatesJob::dispatchSync();
$this->dispatch('updateAvailable');
$settings = InstanceSettings::get();
if ($settings->new_version_available) {
$this->dispatch('success', 'New version available!');
} else {
$this->dispatch('success', 'No new version available.');
}
}
public function render()
{
return view('livewire.settings.index');

View File

@@ -1,6 +1,6 @@
<?php
namespace App\Livewire\Settings;
namespace App\Livewire;
use App\Jobs\DatabaseBackupJob;
use App\Models\InstanceSettings;
@@ -10,13 +10,13 @@ use App\Models\Server;
use App\Models\StandalonePostgresql;
use Livewire\Component;
class Backup extends Component
class SettingsBackup extends Component
{
public InstanceSettings $settings;
public $s3s;
public StandalonePostgresql|null|array $database = [];
public ?StandalonePostgresql $database = null;
public ScheduledDatabaseBackup|null|array $backup = [];
@@ -41,8 +41,24 @@ class Backup extends Component
public function mount()
{
$this->backup = $this->database?->scheduledBackups->first() ?? null;
$this->executions = $this->backup?->executions ?? [];
if (isInstanceAdmin()) {
$settings = InstanceSettings::get();
$this->database = StandalonePostgresql::whereName('coolify-db')->first();
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
if ($this->database) {
if ($this->database->status !== 'running') {
$this->database->status = 'running';
$this->database->save();
}
$this->backup = $this->database->scheduledBackups->first();
$this->executions = $this->backup->executions;
}
$this->settings = $settings;
$this->s3s = $s3s;
} else {
return redirect()->route('dashboard');
}
}
public function add_coolify_database()

View File

@@ -1,12 +1,12 @@
<?php
namespace App\Livewire\Settings;
namespace App\Livewire;
use App\Models\InstanceSettings;
use App\Notifications\TransactionalEmails\Test;
use Livewire\Component;
class Email extends Component
class SettingsEmail extends Component
{
public InstanceSettings $settings;
@@ -42,7 +42,13 @@ class Email extends Component
public function mount()
{
$this->emails = auth()->user()->email;
if (isInstanceAdmin()) {
$this->settings = InstanceSettings::get();
$this->emails = auth()->user()->email;
} else {
return redirect()->route('dashboard');
}
}
public function submitFromFields()

View File

@@ -1,11 +1,11 @@
<?php
namespace App\Livewire\Settings;
namespace App\Livewire;
use App\Models\OauthSetting;
use Livewire\Component;
class Auth extends Component
class SettingsOauth extends Component
{
public $oauth_settings_map;

View File

@@ -3,6 +3,8 @@
namespace App\Livewire;
use App\Actions\Server\UpdateCoolify;
use App\Models\InstanceSettings;
use Illuminate\Support\Facades\Http;
use Livewire\Component;
class Upgrade extends Component
@@ -15,14 +17,23 @@ class Upgrade extends Component
public string $latestVersion = '';
protected $listeners = ['updateAvailable' => 'checkUpdate'];
public function checkUpdate()
{
$this->latestVersion = get_latest_version_of_coolify();
$currentVersion = config('version');
version_compare($currentVersion, $this->latestVersion, '<') ? $this->isUpgradeAvailable = true : $this->isUpgradeAvailable = false;
if (isDev()) {
$this->isUpgradeAvailable = true;
try {
$settings = InstanceSettings::get();
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
$versions = $response->json();
$this->latestVersion = data_get($versions, 'coolify.v4.version');
}
$this->isUpgradeAvailable = $settings->new_version_available;
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function upgrade()

View File

@@ -104,6 +104,8 @@ class Application extends BaseModel
protected $guarded = [];
protected $appends = ['server_status'];
protected static function booted()
{
static::saving(function ($application) {
@@ -232,12 +234,24 @@ class Application extends BaseModel
public function failedTaskLink($task_uuid)
{
if (data_get($this, 'environment.project.uuid')) {
return route('project.application.scheduled-tasks', [
$route = route('project.application.scheduled-tasks', [
'project_uuid' => data_get($this, 'environment.project.uuid'),
'environment_name' => data_get($this, 'environment.name'),
'application_uuid' => data_get($this, 'uuid'),
'task_uuid' => $task_uuid,
]);
$settings = InstanceSettings::get();
if (data_get($settings, 'fqdn')) {
$url = Url::fromString($route);
$url = $url->withPort(null);
$fqdn = data_get($settings, 'fqdn');
$fqdn = str_replace(['http://', 'https://'], '', $fqdn);
$url = $url->withHost($fqdn);
return $url->__toString();
}
return $route;
}
return null;
@@ -275,12 +289,20 @@ class Application extends BaseModel
return Attribute::make(
get: function () {
if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) {
if (str($this->git_repository)->contains('bitbucket')) {
return "{$this->source->html_url}/{$this->git_repository}/src/{$this->git_branch}";
}
return "{$this->source->html_url}/{$this->git_repository}/tree/{$this->git_branch}";
}
// Convert the SSH URL to HTTPS URL
if (strpos($this->git_repository, 'git@') === 0) {
$git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
if (str($this->git_repository)->contains('bitbucket')) {
return "https://{$git_repository}/src/{$this->git_branch}";
}
return "https://{$git_repository}/tree/{$this->git_branch}";
}
@@ -431,6 +453,11 @@ class Application extends BaseModel
);
}
public function isRunning()
{
return (bool) str($this->status)->startsWith('running');
}
public function isExited()
{
return (bool) str($this->status)->startsWith('exited');
@@ -441,6 +468,28 @@ class Application extends BaseModel
return $this->getRawOriginal('status');
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
if ($this->additional_servers->count() === 0) {
return $this->destination->server->isFunctional();
} else {
$additional_servers_status = $this->additional_servers->pluck('pivot.status');
$main_server_status = $this->destination->server->isFunctional();
foreach ($additional_servers_status as $status) {
$server_status = str($status)->before(':')->value();
if ($main_server_status !== $server_status) {
return false;
}
}
return true;
}
}
);
}
public function status(): Attribute
{
return Attribute::make(
@@ -1270,7 +1319,7 @@ class Application extends BaseModel
$template = $this->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pull_request_id, $preview_fqdn);

View File

@@ -35,6 +35,11 @@ class ApplicationPreview extends BaseModel
return self::where('application_id', $application_id)->where('pull_request_id', $pull_request_id)->firstOrFail();
}
public function isRunning()
{
return (bool) str($this->status)->startsWith('running');
}
public function application()
{
return $this->belongsTo(Application::class);
@@ -49,7 +54,7 @@ class ApplicationPreview extends BaseModel
$template = $this->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
$random = new Cuid2(7);
$random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);

View File

@@ -14,7 +14,7 @@ abstract class BaseModel extends Model
static::creating(function (Model $model) {
// Generate a UUID if one isn't set
if (! $model->uuid) {
$model->uuid = (string) new Cuid2(7);
$model->uuid = (string) new Cuid2;
}
});
}

View File

@@ -5,7 +5,6 @@ namespace App\Models;
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use OpenApi\Attributes as OA;
use Symfony\Component\Yaml\Yaml;
use Visus\Cuid2\Cuid2;
@@ -200,28 +199,33 @@ class EnvironmentVariable extends Model
return null;
}
$environment_variable = trim($environment_variable);
$type = str($environment_variable)->after('{{')->before('.')->value;
if (str($environment_variable)->startsWith('{{'.$type) && str($environment_variable)->endsWith('}}')) {
$variable = Str::after($environment_variable, "{$type}.");
$variable = Str::before($variable, '}}');
$variable = str($variable)->trim()->value;
$sharedEnvsFound = str($environment_variable)->matchAll('/{{(.*?)}}/');
if ($sharedEnvsFound->isEmpty()) {
return $environment_variable;
}
foreach ($sharedEnvsFound as $sharedEnv) {
$type = str($sharedEnv)->match('/(.*?)\./');
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
return $variable;
continue;
}
if ($type === 'environment') {
$variable = str($sharedEnv)->match('/\.(.*)/');
if ($type->value() === 'environment') {
$id = $resource->environment->id;
} elseif ($type === 'project') {
} elseif ($type->value() === 'project') {
$id = $resource->environment->project->id;
} else {
} elseif ($type->value() === 'team') {
$id = $resource->team()->id;
}
if (is_null($id)) {
continue;
}
$environment_variable_found = SharedEnvironmentVariable::where('type', $type)->where('key', $variable)->where('team_id', $resource->team()->id)->where("{$type}_id", $id)->first();
if ($environment_variable_found) {
return $environment_variable_found;
$environment_variable = str($environment_variable)->replace("{{{$sharedEnv}}}", $environment_variable_found->value);
}
}
return $environment_variable;
return str($environment_variable)->value();
}
private function get_environment_variables(?string $environment_variable = null): ?string

View File

@@ -18,6 +18,9 @@ class InstanceSettings extends Model implements SendsEmail
'resale_license' => 'encrypted',
'smtp_password' => 'encrypted',
'allowed_ip_ranges' => 'array',
'is_auto_update_enabled' => 'boolean',
'auto_update_frequency' => 'string',
'update_check_frequency' => 'string',
];
public function fqdn(): Attribute

View File

@@ -2,6 +2,7 @@
namespace App\Models;
use App\Events\FileStorageChanged;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class LocalFileVolume extends BaseModel
@@ -33,16 +34,23 @@ class LocalFileVolume extends BaseModel
$workdir = $this->resource->workdir();
$server = $this->resource->destination->server;
}
$commands = collect([
"cd $workdir",
]);
$commands = collect([]);
$fs_path = data_get($this, 'fs_path');
$isFile = instant_remote_process(["test -f $fs_path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $fs_path && echo OK || echo NOK"], $server);
if ($fs_path && $fs_path != '/' && $fs_path != '.' && $fs_path != '..') {
$commands->push("rm -rf $fs_path");
}
ray($commands);
ray($isFile, $isDir);
if ($isFile === 'OK') {
$commands->push("rm -rf $fs_path > /dev/null 2>&1 || true");
return instant_remote_process($commands, $server);
} elseif ($isDir === 'OK') {
$commands->push("rm -rf $fs_path > /dev/null 2>&1 || true");
$commands->push("rmdir $fs_path > /dev/null 2>&1 || true");
}
}
if ($commands->count() > 0) {
return instant_remote_process($commands, $server);
}
}
public function saveStorageOnServer()
@@ -55,13 +63,10 @@ class LocalFileVolume extends BaseModel
$workdir = $this->resource->workdir();
$server = $this->resource->destination->server;
}
$commands = collect([
"mkdir -p $workdir > /dev/null 2>&1 || true",
"cd $workdir",
]);
$is_directory = $this->is_directory;
if ($is_directory) {
$commands = collect([]);
if ($this->is_directory) {
$commands->push("mkdir -p $this->fs_path > /dev/null 2>&1 || true");
$commands->push("cd $workdir");
}
if (str($this->fs_path)->startsWith('.') || str($this->fs_path)->startsWith('/') || str($this->fs_path)->startsWith('~')) {
$parent_dir = str($this->fs_path)->beforeLast('/');
@@ -79,8 +84,11 @@ class LocalFileVolume extends BaseModel
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
if ($isFile == 'OK' && $fileVolume->is_directory) {
$content = instant_remote_process(["cat $path"], $server, false);
$fileVolume->is_directory = false;
$fileVolume->content = $content;
$fileVolume->save();
FileStorageChanged::dispatch(data_get($server, 'team_id'));
throw new \Exception('The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.');
} elseif ($isDir == 'OK' && ! $fileVolume->is_directory) {
$fileVolume->is_directory = true;

View File

@@ -5,6 +5,7 @@ namespace App\Models;
use App\Actions\Server\InstallDocker;
use App\Enums\ProxyTypes;
use App\Jobs\PullSentinelImageJob;
use App\Notifications\Server\Revived;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Collection;
@@ -150,7 +151,7 @@ class Server extends BaseModel
$dynamic_conf_path = $this->proxyPath().'/dynamic';
$proxy_type = $this->proxyType();
$redirect_url = $this->proxy->redirect_url;
if ($proxy_type === 'TRAEFIK_V2') {
if ($proxy_type === ProxyTypes::TRAEFIK->value) {
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.yaml";
} elseif ($proxy_type === 'CADDY') {
$default_redirect_file = "$dynamic_conf_path/default_redirect_404.caddy";
@@ -180,7 +181,7 @@ respond 404
return;
}
if ($proxy_type === 'TRAEFIK_V2') {
if ($proxy_type === ProxyTypes::TRAEFIK->value) {
$dynamic_conf = [
'http' => [
'routers' => [
@@ -254,7 +255,7 @@ respond 404
{
$settings = \App\Models\InstanceSettings::get();
$dynamic_config_path = $this->proxyPath().'/dynamic';
if ($this->proxyType() === 'TRAEFIK_V2') {
if ($this->proxyType() === ProxyTypes::TRAEFIK->value) {
$file = "$dynamic_config_path/coolify.yaml";
if (empty($settings->fqdn) || (isCloud() && $this->id !== 0) || ! $this->isLocalhost()) {
instant_remote_process([
@@ -402,7 +403,7 @@ $schema://$host {
// TODO: should use /traefik for already exisiting configurations?
// Should move everything except /caddy and /nginx to /traefik
// The code needs to be modified as well, so maybe it does not worth it
if ($proxyType === ProxyTypes::TRAEFIK_V2->value) {
if ($proxyType === ProxyTypes::TRAEFIK->value) {
$proxy_path = $proxy_path;
} elseif ($proxyType === ProxyTypes::CADDY->value) {
$proxy_path = $proxy_path.'/caddy';
@@ -420,7 +421,7 @@ $schema://$host {
// return $proxyType;
// }
// if (is_null($proxyType)) {
// $this->proxy->type = ProxyTypes::TRAEFIK_V2->value;
// $this->proxy->type = ProxyTypes::TRAEFIK->value;
// $this->proxy->status = ProxyStatus::EXITED->value;
// $this->save();
// }
@@ -677,7 +678,49 @@ $schema://$host {
return instant_remote_process(["docker start $id"], $this);
}
public function getContainers(): Collection
public function getContainers()
{
$containers = collect([]);
$containerReplicates = collect([]);
if ($this->isSwarm()) {
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this, false);
$containers = format_docker_command_output_to_json($containers);
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this, false);
if ($containerReplicates) {
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
foreach ($containerReplicates as $containerReplica) {
$name = data_get($containerReplica, 'Name');
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
if (data_get($container, 'Spec.Name') === $name) {
$replicas = data_get($containerReplica, 'Replicas');
$running = str($replicas)->explode('/')[0];
$total = str($replicas)->explode('/')[1];
if ($running === $total) {
data_set($container, 'State.Status', 'running');
data_set($container, 'State.Health.Status', 'healthy');
} else {
data_set($container, 'State.Status', 'starting');
data_set($container, 'State.Health.Status', 'unhealthy');
}
}
return $container;
});
}
}
} else {
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this, false);
$containers = format_docker_command_output_to_json($containers);
$containerReplicates = collect([]);
}
return [
'containers' => collect($containers) ?? collect([]),
'containerReplicates' => collect($containerReplicates) ?? collect([]),
];
}
public function getContainersWithSentinel(): Collection
{
$sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false);
$sentinel_found = json_decode($sentinel_found, true);
@@ -690,21 +733,6 @@ $schema://$host {
$containers = data_get(json_decode($containers, true), 'containers', []);
return collect($containers);
} else {
if ($this->isSwarm()) {
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this, false);
} else {
$containers = instant_remote_process(['docker container ls -q'], $this, false);
if (! $containers) {
return collect([]);
}
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this, false);
}
if (is_null($containers)) {
return collect([]);
}
return format_docker_command_output_to_json($containers);
}
}

View File

@@ -2,11 +2,13 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use OpenApi\Attributes as OA;
use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml;
#[OA\Schema(
@@ -38,6 +40,8 @@ class Service extends BaseModel
protected $guarded = [];
protected $appends = ['server_status'];
public function isConfigurationChanged(bool $save = false)
{
$domains = $this->applications()->get()->pluck('fqdn')->sort()->toArray();
@@ -76,6 +80,20 @@ class Service extends BaseModel
}
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->server->isFunctional();
}
);
}
public function isRunning()
{
return (bool) str($this->status())->contains('running');
}
public function isExited()
{
return (bool) str($this->status())->contains('exited');
@@ -575,6 +593,30 @@ class Service extends BaseModel
$fields->put('Vaultwarden', $data);
break;
case str($image)->contains('gitlab/gitlab'):
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GITLAB')->first();
$data = collect([]);
if ($password) {
$data = $data->merge([
'Root Password' => [
'key' => data_get($password, 'key'),
'value' => data_get($password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$data = $data->merge([
'Root User' => [
'key' => 'N/A',
'value' => 'root',
'rules' => 'required',
'isPassword' => true,
],
]);
$fields->put('GitLab', $data->toArray());
break;
}
}
$databases = $this->databases()->get();
@@ -764,12 +806,24 @@ class Service extends BaseModel
public function failedTaskLink($task_uuid)
{
if (data_get($this, 'environment.project.uuid')) {
return route('project.service.scheduled-tasks', [
$route = route('project.service.scheduled-tasks', [
'project_uuid' => data_get($this, 'environment.project.uuid'),
'environment_name' => data_get($this, 'environment.name'),
'service_uuid' => data_get($this, 'uuid'),
'task_uuid' => $task_uuid,
]);
$settings = InstanceSettings::get();
if (data_get($settings, 'fqdn')) {
$url = Url::fromString($route);
$url = $url->withPort(null);
$fqdn = data_get($settings, 'fqdn');
$fqdn = str_replace(['http://', 'https://'], '', $fqdn);
$url = $url->withHost($fqdn);
return $url->__toString();
}
return $route;
}
return null;

View File

@@ -14,7 +14,7 @@ class StandaloneClickhouse extends BaseModel
protected $guarded = [];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
protected $casts = [
'clickhouse_password' => 'encrypted',
@@ -40,6 +40,15 @@ class StandaloneClickhouse extends BaseModel
});
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->destination->server->isFunctional();
}
);
}
public function isConfigurationChanged(bool $save = false)
{
$newConfigHash = $this->image.$this->ports_mappings;

View File

@@ -14,7 +14,7 @@ class StandaloneDragonfly extends BaseModel
protected $guarded = [];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
protected $casts = [
'dragonfly_password' => 'encrypted',
@@ -40,6 +40,15 @@ class StandaloneDragonfly extends BaseModel
});
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->destination->server->isFunctional();
}
);
}
public function isConfigurationChanged(bool $save = false)
{
$newConfigHash = $this->image.$this->ports_mappings;

View File

@@ -14,7 +14,7 @@ class StandaloneKeydb extends BaseModel
protected $guarded = [];
protected $appends = ['internal_db_url', 'external_db_url'];
protected $appends = ['internal_db_url', 'external_db_url', 'server_status'];
protected $casts = [
'keydb_password' => 'encrypted',
@@ -40,6 +40,15 @@ class StandaloneKeydb extends BaseModel
});
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->destination->server->isFunctional();
}
);
}
public function isConfigurationChanged(bool $save = false)
{
$newConfigHash = $this->image.$this->ports_mappings.$this->keydb_conf;

View File

@@ -14,7 +14,7 @@ class StandaloneMariadb extends BaseModel
protected $guarded = [];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
protected $casts = [
'mariadb_password' => 'encrypted',
@@ -40,6 +40,15 @@ class StandaloneMariadb extends BaseModel
});
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->destination->server->isFunctional();
}
);
}
public function isConfigurationChanged(bool $save = false)
{
$newConfigHash = $this->image.$this->ports_mappings.$this->mariadb_conf;

View File

@@ -14,7 +14,7 @@ class StandaloneMongodb extends BaseModel
protected $guarded = [];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
protected static function booted()
{
@@ -44,6 +44,15 @@ class StandaloneMongodb extends BaseModel
});
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->destination->server->isFunctional();
}
);
}
public function isConfigurationChanged(bool $save = false)
{
$newConfigHash = $this->image.$this->ports_mappings.$this->mongo_conf;

View File

@@ -14,7 +14,7 @@ class StandaloneMysql extends BaseModel
protected $guarded = [];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
protected $casts = [
'mysql_password' => 'encrypted',
@@ -41,6 +41,15 @@ class StandaloneMysql extends BaseModel
});
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->destination->server->isFunctional();
}
);
}
public function isConfigurationChanged(bool $save = false)
{
$newConfigHash = $this->image.$this->ports_mappings.$this->mysql_conf;

View File

@@ -14,7 +14,7 @@ class StandalonePostgresql extends BaseModel
protected $guarded = [];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
protected $casts = [
'init_scripts' => 'array',
@@ -46,6 +46,15 @@ class StandalonePostgresql extends BaseModel
return database_configuration_dir()."/{$this->uuid}";
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->destination->server->isFunctional();
}
);
}
public function delete_configurations()
{
$server = data_get($this, 'destination.server');

View File

@@ -14,7 +14,7 @@ class StandaloneRedis extends BaseModel
protected $guarded = [];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
protected static function booted()
{
@@ -36,6 +36,15 @@ class StandaloneRedis extends BaseModel
});
}
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->destination->server->isFunctional();
}
);
}
public function isConfigurationChanged(bool $save = false)
{
$newConfigHash = $this->image.$this->ports_mappings.$this->redis_conf;

View File

@@ -180,6 +180,10 @@ class User extends Authenticatable implements SendsEmail
{
$found_root_team = auth()->user()->teams->filter(function ($team) {
if ($team->id == 0) {
if (! auth()->user()->isAdmin()) {
return false;
}
return true;
}

View File

@@ -12,6 +12,7 @@ use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\RateLimiter;
class Revived extends Notification implements ShouldQueue
{
@@ -44,8 +45,20 @@ class Revived extends Notification implements ShouldQueue
if ($isTelegramEnabled) {
$channels[] = TelegramChannel::class;
}
$executed = RateLimiter::attempt(
'notification-server-revived-'.$this->server->uuid,
1,
function () use ($channels) {
return $channels;
},
7200,
);
return $channels;
if (! $executed) {
return [];
}
return $executed;
}
public function toMail(): MailMessage

View File

@@ -10,6 +10,7 @@ use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\RateLimiter;
class Unreachable extends Notification implements ShouldQueue
{
@@ -35,8 +36,20 @@ class Unreachable extends Notification implements ShouldQueue
if ($isTelegramEnabled) {
$channels[] = TelegramChannel::class;
}
$executed = RateLimiter::attempt(
'notification-server-unreachable-'.$this->server->uuid,
1,
function () use ($channels) {
return $channels;
},
7200,
);
return $channels;
if (! $executed) {
return [];
}
return $executed;
}
public function toMail(): MailMessage

View File

@@ -44,6 +44,8 @@ class FortifyServiceProvider extends ServiceProvider
{
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::registerView(function () {
$isFirstUser = User::count() === 0;
$settings = \App\Models\InstanceSettings::get();
if (! $settings->is_registration_enabled) {
return redirect()->route('login');
@@ -51,7 +53,9 @@ class FortifyServiceProvider extends ServiceProvider
if (config('coolify.waitlist')) {
return redirect()->route('waitlist.index');
} else {
return view('auth.register');
return view('auth.register', [
'isFirstUser' => $isFirstUser,
]);
}
});

View File

@@ -30,7 +30,7 @@ class Datalist extends Component
public function render(): View|Closure|string
{
if (is_null($this->id)) {
$this->id = new Cuid2(7);
$this->id = new Cuid2;
}
if (is_null($this->name)) {
$this->name = $this->id;

View File

@@ -27,7 +27,7 @@ class Input extends Component
public function render(): View|Closure|string
{
if (is_null($this->id)) {
$this->id = new Cuid2(7);
$this->id = new Cuid2;
}
if (is_null($this->name)) {
$this->name = $this->id;

View File

@@ -30,7 +30,7 @@ class Select extends Component
public function render(): View|Closure|string
{
if (is_null($this->id)) {
$this->id = new Cuid2(7);
$this->id = new Cuid2;
}
if (is_null($this->name)) {
$this->name = $this->id;

View File

@@ -41,7 +41,7 @@ class Textarea extends Component
public function render(): View|Closure|string
{
if (is_null($this->id)) {
$this->id = new Cuid2(7);
$this->id = new Cuid2;
}
if (is_null($this->name)) {
$this->name = $this->id;

View File

@@ -9,6 +9,11 @@ const VALID_CRON_STRINGS = [
'weekly' => '0 0 * * 0',
'monthly' => '0 0 1 * *',
'yearly' => '0 0 1 1 *',
'@hourly' => '0 * * * *',
'@daily' => '0 0 * * *',
'@weekly' => '0 0 * * 0',
'@monthly' => '0 0 1 * *',
'@yearly' => '0 0 1 1 *',
];
const RESTART_MODE = 'unless-stopped';

View File

@@ -14,7 +14,7 @@ use Visus\Cuid2\Cuid2;
function generate_database_name(string $type): string
{
$cuid = new Cuid2(7);
$cuid = new Cuid2;
return $type.'-database-'.$cuid;
}

View File

@@ -1,5 +1,6 @@
<?php
use App\Enums\ProxyTypes;
use App\Models\Application;
use App\Models\ApplicationPreview;
use App\Models\Server;
@@ -338,7 +339,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
foreach ($domains as $loop => $domain) {
try {
if ($generate_unique_uuid) {
$uuid = new Cuid2(7);
$uuid = new Cuid2;
}
$url = Url::fromString($domain);
@@ -539,26 +540,55 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
if ($pull_request_id === 0) {
if ($application->fqdn) {
$domains = str(data_get($application, 'fqdn'))->explode(',');
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
));
// Add Caddy labels
$labels = $labels->merge(fqdnLabelsForCaddy(
network: $application->destination->network,
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
));
$shouldGenerateLabelsExactly = $application->destination->server->settings->generate_exact_labels;
if ($shouldGenerateLabelsExactly) {
switch ($application->destination->server->proxyType()) {
case ProxyTypes::TRAEFIK->value:
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
));
break;
case ProxyTypes::CADDY->value:
$labels = $labels->merge(fqdnLabelsForCaddy(
network: $application->destination->network,
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
));
break;
}
} else {
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
));
$labels = $labels->merge(fqdnLabelsForCaddy(
network: $application->destination->network,
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled(),
redirect_direction: $application->redirect
));
}
}
} else {
if (data_get($preview, 'fqdn')) {
@@ -566,24 +596,50 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
} else {
$domains = collect([]);
}
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
// Add Caddy labels
$labels = $labels->merge(fqdnLabelsForCaddy(
network: $application->destination->network,
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
$shouldGenerateLabelsExactly = $application->destination->server->settings->generate_exact_labels;
if ($shouldGenerateLabelsExactly) {
switch ($application->destination->server->proxyType()) {
case ProxyTypes::TRAEFIK->value:
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
break;
case ProxyTypes::CADDY->value:
$labels = $labels->merge(fqdnLabelsForCaddy(
network: $application->destination->network,
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
break;
}
} else {
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
$labels = $labels->merge(fqdnLabelsForCaddy(
network: $application->destination->network,
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
}
}

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