Compare commits

...

760 Commits

Author SHA1 Message Date
9e5551c4dd fix: use correct env variable for invoice ninja password 2024-10-10 13:13:44 +02:00
Andras Bacsai
789fc1348d Merge branch 'main' into next 2024-10-10 12:44:25 +02:00
Andras Bacsai
fd15c7b49e fix: scheduled database server 2024-10-10 12:42:34 +02:00
Andras Bacsai
7872818f9e fix: mautic 5
remove mautic 4 because if it out of support
2024-10-10 12:26:44 +02:00
Andras Bacsai
1b79f78fda fix: Update password variables in Service model 2024-10-10 12:06:17 +02:00
Andras Bacsai
e6ff801559 feat: Improve search functionality in project selection 2024-10-10 11:52:19 +02:00
Andras Bacsai
8b7c34d5e6 fix: Improve service template readability 2024-10-10 11:47:06 +02:00
Andras Bacsai
e6566d8be3 fix: new parser with SERVICE_URL_ envs 2024-10-10 11:46:41 +02:00
Andras Bacsai
290c287f7e fix: azimutt template - still not working haha 2024-10-10 11:46:27 +02:00
Andras Bacsai
1de50227c3 Merge pull request #3826 from lucasmichot/feat/template-readability
Improve `service-templates.json` file readability
2024-10-10 11:46:17 +02:00
Andras Bacsai
d69164bc43 Merge pull request #3785 from coollabsio/new-services-2
Feat: New services and service fixes part 2
2024-10-10 10:29:51 +02:00
Andras Bacsai
0e2889b857 improvement: Add link to duplicate domain
fix: duplicate domain error
fix: remove fqdn constraint from db, because it is checked on app level
2024-10-10 10:24:11 +02:00
Andras Bacsai
df9b2ebb1f chore: Ignore .ignition.json files in Docker and Git 2024-10-10 10:23:17 +02:00
Lucas Michot
91a1fdcb9a Improve service-templates.json file readability 2024-10-10 10:05:35 +02:00
Andras Bacsai
10ca408b37 fix: is_static settings through API
fix: validation rules
2024-10-10 09:33:29 +02:00
peaklabs-dev
1d4dd405b8 fix joplin healthcheck 2024-10-09 15:24:48 +02:00
peaklabs-dev
482016be1d Update service-templates.json 2024-10-09 14:56:16 +02:00
peaklabs-dev
c5e6d04a95 Update forgejo-with-runner.yaml 2024-10-09 14:56:05 +02:00
peaklabs-dev
ce352eb590 fix audiobookshelf 2024-10-08 22:49:09 +02:00
🏔️ Peak
3e5b0d920c Merge pull request #3234 from Amitind/add-service-audiobookshelf
audiobookshelf added
2024-10-08 22:45:01 +02:00
peaklabs-dev
dc75e5dc6e fix onedev 2024-10-08 22:40:43 +02:00
🏔️ Peak
34e7805ef4 Merge pull request #3111 from christiankolbow/template-onedev
feat: add onedev template
2024-10-08 22:36:11 +02:00
peaklabs-dev
dee9123694 Update service-templates.json 2024-10-08 22:12:33 +02:00
peaklabs-dev
b02720aac8 fix moutic 4 and 5 2024-10-08 22:12:24 +02:00
peaklabs-dev
3000579bcd fix joplin 2024-10-08 22:11:37 +02:00
🏔️ Peak
be03e0c587 Merge pull request #3033 from nekomi2/main
feat: add joplin server
2024-10-08 21:48:12 +02:00
peaklabs-dev
f0e6892d77 Fix: Keycloak 2024-10-08 21:33:23 +02:00
🏔️ Peak
44f2d521a2 Merge pull request #3109 from christiankolbow/template-keycloak
feat: add keycloak template
2024-10-08 21:29:13 +02:00
peaklabs-dev
ef821b2c2d add healthcheck to azimutt 2024-10-08 21:19:46 +02:00
Andras Bacsai
d031911ada chore: Update version to 4.0.0-beta.358 2024-10-08 17:16:23 +02:00
Andras Bacsai
ab8bb8dc4e fix: select server view 2024-10-08 17:16:23 +02:00
Andras Bacsai
1c6d47f2fc Merge pull request #3796 from coollabsio/fix-server-selection
fix: server selection view if 3 or more servers are defined
2024-10-08 17:09:24 +02:00
Andras Bacsai
742df4f5df fix: select server view 2024-10-08 17:08:35 +02:00
Andras Bacsai
2522ecf832 chore: Update version to 4.0.0-beta.357 2024-10-08 17:08:26 +02:00
peaklabs-dev
8d2fef558f fix mattermost 2024-10-08 16:24:16 +02:00
🏔️ Peak
a979beb3a6 Merge pull request #3103 from sroepges/main
fix: update mattermost image tag and add default port
2024-10-08 16:17:46 +02:00
peaklabs-dev
0143b9bb26 fix bookstack 2024-10-08 16:06:57 +02:00
🏔️ Peak
ce9d4308d4 Merge pull request #2978 from JuanxCursed/main
feat: Add Mautic 4 and 5 to service templates
2024-10-08 15:57:07 +02:00
🏔️ Peak
57a96c2579 Merge pull request #2934 from cr4zyiw4n/feat--add-bookstack-template
added bookstack template
2024-10-08 15:43:11 +02:00
peaklabs-dev
4e9bafd6df Update azimutt.yaml 2024-10-08 15:39:58 +02:00
peaklabs-dev
15b5cbf68f ignore azimutt for now (probably needs magic env update) 2024-10-08 15:39:50 +02:00
peaklabs-dev
bfe9a17f4b Merge branch 'new-services-2' of https://github.com/coollabsio/coolify into new-services-2 2024-10-08 15:15:49 +02:00
peaklabs-dev
ec3916bb7e fix forjo 2024-10-08 15:15:47 +02:00
Andras Bacsai
974b4b92c1 wip: coolify.json 2024-10-08 15:11:19 +02:00
peaklabs-dev
e16ccb2bcd fix forjo with mariadb 2024-10-08 15:01:23 +02:00
🏔️ Peak
a361a864dd Merge pull request #3787 from clho40/hotfix/outline
Add FORCE_HTTPS Env Variable for External HTTPS Handling Support in getoutline
2024-10-08 14:38:28 +02:00
🏔️ Peak
1bed6bf696 Merge pull request #3784 from djsisson/fix-posthog-template
fix posthog template
2024-10-08 14:37:35 +02:00
Ho Chia Leung
5c65eb281d Update getoutline.yaml
Added FORCE_HTTPS environment variable to control HTTPS redirection. Set this to false when using external HTTPS handling, such as with Cloudflare Tunnel and its SSL service.
2024-10-08 14:32:25 +02:00
peaklabs-dev
0cd2ca6e79 fix forgejo 2024-10-08 14:32:21 +02:00
🏔️ Peak
7cb9cb0a44 Merge pull request #2722 from DarkGhostHunter/feat-forgejo
Feat: Forgejo templates
2024-10-08 14:11:19 +02:00
peaklabs-dev
2b9b2af8fe fix paperless 2024-10-08 14:05:20 +02:00
🏔️ Peak
b8f211eced Merge pull request #2719 from martonsz/service-paperless
Adding service for paperless
2024-10-08 13:41:43 +02:00
peaklabs-dev
1034d9c315 fix azimutt 2024-10-08 12:40:56 +02:00
🏔️ Peak
37bc2643a5 Merge pull request #2695 from tonylyjones/feature/add-azimutt-service
Add service template for azimutt
2024-10-08 12:10:29 +02:00
Andras Bacsai
052fcd2520 chore: Update service names and volumes in windmill.yaml 2024-10-08 11:59:28 +02:00
Darren Sisson
df152e9fbd fix posthog template 2024-10-08 10:57:40 +01:00
Andras Bacsai
8af41b533c fix: soketi 2024-10-08 11:42:51 +02:00
Andras Bacsai
17b852ce1d fix: update services 2024-10-08 11:29:31 +02:00
Andras Bacsai
f4c88d3600 Merge pull request #3763 from coollabsio/new-services
Feat: Service fixes and new services
2024-10-08 11:29:19 +02:00
Andras Bacsai
9c2b24b9d0 Merge pull request #3758 from MarioZet23/custom-traefik-middleware
feat: Custom traefik middlewares v2
2024-10-08 11:23:04 +02:00
Andras Bacsai
4e6bf6bb8d Merge pull request #3762 from djsisson/fix-reactive-resume-template
fix reactive resume template
2024-10-08 10:56:13 +02:00
Andras Bacsai
74571d99a1 Merge pull request #3781 from lucasmichot/fix/langfr
Fix : spelling mistake
2024-10-08 10:54:10 +02:00
Andras Bacsai
4bcfe1c0b8 fix: database descriptions 2024-10-08 10:53:03 +02:00
Andras Bacsai
b25ef93a8e chore: Update version to 4.0.0-beta.357 2024-10-08 09:20:03 +02:00
Lucas Michot
e6db0b9798 Fix a spelling mistake in French 2024-10-08 09:13:49 +02:00
peaklabs-dev
2805681a30 Update service-templates.json 2024-10-07 19:36:27 +02:00
peaklabs-dev
22dc06f1f2 fix dozzel 2024-10-07 19:36:22 +02:00
peaklabs-dev
30b1d076d2 Update organizr.yaml 2024-10-07 19:31:12 +02:00
🏔️ Peak
c486281816 Merge pull request #3008 from Telokis/add-organizr-template-service
feat: Add organizr
2024-10-07 19:27:45 +02:00
peaklabs-dev
71d28492b8 fix libreoffice 2024-10-07 19:14:13 +02:00
peaklabs-dev
1914b505ed Delete libreoffice.svg 2024-10-07 19:07:03 +02:00
🏔️ Peak
8c78e9adf0 Merge pull request #3249 from Amitind/add-service
added libreoffice
2024-10-07 19:06:18 +02:00
peaklabs-dev
e1168bebe0 fix windmill health checks 2024-10-07 18:55:16 +02:00
peaklabs-dev
bb8a276aa9 Fix: Windmill 2024-10-07 18:36:25 +02:00
peaklabs-dev
8d543d40cc Merge branch 'new-services' of https://github.com/coollabsio/coolify into new-services 2024-10-07 18:29:43 +02:00
🏔️ Peak
f5ca820be8 Merge pull request #3766 from djsisson/fix-windmill-template
fix windmill template
2024-10-07 18:29:30 +02:00
peaklabs-dev
09d1ca79b9 Fix: dozzle 2024-10-07 18:10:16 +02:00
Darren Sisson
5fcd432c76 fix windmill template 2024-10-07 17:08:28 +01:00
peaklabs-dev
837796debf Fix: Soketi 2024-10-07 18:08:15 +02:00
🏔️ Peak
16817993fa Merge pull request #3720 from Skeyelab/dozzle-auth
feat: add dozzle-with-auth template
2024-10-07 17:52:26 +02:00
🏔️ Peak
2b818285b8 Merge pull request #3694 from danielalves96/add-dozzle-template
feat: add dozzle template
2024-10-07 17:52:06 +02:00
🏔️ Peak
5c83b573b6 Merge pull request #3757 from Nathanjms/next
Add Soketi Service
2024-10-07 17:26:56 +02:00
🏔️ Peak
46f226c3f7 Merge pull request #3198 from FranckKe/upgrade_authentik
feat: Update Authentik
2024-10-07 15:34:21 +02:00
🏔️ Peak
7aa60adc98 Merge branch 'new-services' into upgrade_authentik 2024-10-07 15:34:13 +02:00
peaklabs-dev
a2cfa1423d Fix: Easyappointments 2024-10-07 15:11:40 +02:00
🏔️ Peak
cdedf3b25c Merge pull request #2885 from leocabeza/next
feat: add easyappointments service template
2024-10-07 14:43:51 +02:00
peaklabs-dev
63f329d123 Fix: Rabbitmq 2024-10-07 14:40:49 +02:00
peaklabs-dev
0b084060ee Merge branch 'new-services' of https://github.com/coollabsio/coolify into new-services 2024-10-07 14:36:41 +02:00
peaklabs-dev
2c895927f6 Add missing comments 2024-10-07 14:36:23 +02:00
🏔️ Peak
db8c59eaaf Merge pull request #3330 from djsisson/fix-rabbitmq
fix rabbit-mq
2024-10-07 14:36:06 +02:00
peaklabs-dev
b31cfd1604 Feat: Supertokens json 2024-10-07 14:32:40 +02:00
peaklabs-dev
8c503bc7fc Feat: Supertokens 2024-10-07 14:32:04 +02:00
🏔️ Peak
c4ee91930d Merge pull request #2383 from marcomaiermm/feat-add-supertokens-template
feat: Add Supertokens template
2024-10-07 14:15:36 +02:00
Darren Sisson
8fd103cc77 fix reactive resume template 2024-10-07 13:08:33 +01:00
Andras Bacsai
40f2c7a1f9 Merge pull request #3714 from coollabsio/next
v4.0.0-beta.356
2024-10-07 14:08:08 +02:00
Andras Bacsai
3f8324d09e chore: Refactor loadServices2 method and remove unused code 2024-10-07 14:03:04 +02:00
Andras Bacsai
eee292b02f fix: directus 2024-10-07 13:45:02 +02:00
Andras Bacsai
2b4dc1f9c8 Merge pull request #3752 from peaklabs-dev/fix-directus
Fix: Directus template
2024-10-07 13:44:00 +02:00
Andras Bacsai
dd29827a14 Merge pull request #3749 from mkilinskidev/mki
Fix subtitle typo in server navbar
2024-10-07 13:23:40 +02:00
Andras Bacsai
dfe290b546 Merge pull request #3741 from coollabsio/dependabot/npm_and_yarn/cookie-0.7.0
chore(deps): bump cookie from 0.6.0 to 0.7.0
2024-10-07 13:15:16 +02:00
Andras Bacsai
4eeb7ee7c6 Merge pull request #3570 from katywings/nitropage
chore: add Nitropage service template and logo
2024-10-07 12:43:25 +02:00
Andras Bacsai
51813463b8 fix: chatwoot service 2024-10-07 12:36:33 +02:00
Andras Bacsai
da8a45391c Merge branch 'next' of github.com:coollabsio/coolify into next 2024-10-07 12:22:09 +02:00
Andras Bacsai
673081ffb3 chore: Update select.blade.php with improved search functionality 2024-10-07 12:22:00 +02:00
peaklabs-dev
8f6c01ba49 Update service-templates.json 2024-10-07 12:14:07 +02:00
peaklabs-dev
40852995b4 Merge branch 'next' of https://github.com/coollabsio/coolify into next 2024-10-07 12:13:53 +02:00
peaklabs-dev
e6bb1deaa9 Update getoutline.yaml 2024-10-07 12:13:52 +02:00
Andras Bacsai
2dc3177a7a feat: Refactor setType method to use slug value for type 2024-10-07 12:12:32 +02:00
Andras Bacsai
facbd9a50d Merge branch 'next' of github.com:coollabsio/coolify into next 2024-10-07 12:12:22 +02:00
Andras Bacsai
a0532afb24 feat: Refactor setType method to use slug value for type 2024-10-07 12:12:05 +02:00
Andras Bacsai
a725a6eaf2 feat: update setType method to use slug value for type 2024-10-07 12:11:56 +02:00
peaklabs-dev
3fd3660960 Update service-templates.json 2024-10-07 12:07:37 +02:00
peaklabs-dev
5898e0ac4a Merge branch 'next' of https://github.com/coollabsio/coolify into next 2024-10-07 12:07:18 +02:00
peaklabs-dev
4f4743cc7f Fix: Outline 2024-10-07 12:07:14 +02:00
Andras Bacsai
3b3362daf0 Merge pull request #3722 from danielalves96/add_ollama_with_web_ui
feat: add ollama service with open-webui and logo
2024-10-07 11:56:28 +02:00
Andras Bacsai
cd199ed81d Merge branch 'next' of github.com:coollabsio/coolify into next 2024-10-07 11:54:59 +02:00
Andras Bacsai
7e32a37729 fix: new services 2024-10-07 11:54:54 +02:00
🏔️ Peak
58c3cea0c0 Merge pull request #3127 from peaklabs-dev/service-getoutline
Feat: getoutline service
2024-10-07 11:29:57 +02:00
Andras Bacsai
1c10a43321 fix: new resource selection view
fix: new services
2024-10-07 11:19:14 +02:00
Andras Bacsai
370a0b1eec fix: use local service-templates in dev 2024-10-07 11:02:01 +02:00
peaklabs-dev
282ff4e670 Update service-templates.json 2024-10-07 10:39:21 +02:00
peaklabs-dev
af09472679 Update labelstudio.yaml 2024-10-07 10:36:08 +02:00
🏔️ Peak
053ee63606 Merge pull request #3729 from rennokki/service/labelstudio
Service: Label Studio
2024-10-07 10:21:03 +02:00
MarioZet23
6d6d6bfc87 Process coolify.traefik.middlewares label 2024-10-06 23:01:48 +02:00
Nathan James
7665a1787f Add soketi coolify 2024-10-06 21:57:15 +01:00
Leonardo Cabeza
846e45134e Merge branch 'next' into next 2024-10-06 14:13:10 -05:00
peaklabs-dev
a8d8df7d8b Fix: Directus 2024-10-06 19:42:09 +02:00
Andras Bacsai
283fe47f5c Merge pull request #3750 from coollabsio/helper-1.0.2
chore: Bump coolify-helper version to 1.0.2
2024-10-06 18:04:26 +02:00
Andras Bacsai
88367d8af1 chore: Bump coolify-helper version to 1.0.2 2024-10-06 18:04:03 +02:00
Andras Bacsai
506f733632 Merge pull request #3748 from peaklabs-dev/fix-helper-manifest
Fix: Broken helper manifests (for a short period a few hours)
2024-10-06 18:00:00 +02:00
Mateusz Kilinski
9f2e7851f7 Fix subtitle typo in server navbar 2024-10-06 17:43:22 +02:00
peaklabs-dev
8269e9d29d Fix: Update helper version 2024-10-06 17:39:16 +02:00
dependabot[bot]
8803cae54c chore(deps): bump cookie from 0.6.0 to 0.7.0
Bumps [cookie](https://github.com/jshttp/cookie) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: cookie
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-06 08:13:20 +00:00
peaklabs-dev
c1930e3dfc Revert "Update coolify-helper.yml"
This reverts commit c717b6859b.
2024-10-06 00:51:54 +02:00
🏔️ Peak
c717b6859b Update coolify-helper.yml 2024-10-06 00:51:28 +02:00
Andras Bacsai
4624a381b1 new search input on "new resource" view 2024-10-05 20:55:23 +02:00
Andras Bacsai
6928b0a2c8 chore: Update project query to order by name in lowercase 2024-10-05 15:04:47 +02:00
Andras Bacsai
2da6f66e85 chore: Update project query to order by name in uppercase 2024-10-05 15:04:31 +02:00
Andras Bacsai
a1124a885d feat: project search on frontend 2024-10-05 15:03:40 +02:00
Andras Bacsai
9448d0f0d2 feat: Add Invoice Ninja service configuration to Service model 2024-10-05 14:16:53 +02:00
Alex Renoki
e446354d97 Added Label Studio 2024-10-05 06:27:16 +03:00
Daniel Alves
c8aa09359f fix: remove not used extra host 2024-10-04 16:15:18 -03:00
Andras Bacsai
128d732438 fix: one-click services 2024-10-04 20:17:08 +02:00
Andras Bacsai
3b97bb1341 feat: Add Argilla service configuration to Service model 2024-10-04 20:16:58 +02:00
Andras Bacsai
a4c8f83d17 chore: Update password form submission in modal-confirmation component 2024-10-04 20:10:40 +02:00
Daniel Alves
0ca9885ddf feat: add ollama service with open webui and logo 2024-10-04 11:51:45 -03:00
Eric Dahl
acd6eedf40 adding version with auth 2024-10-04 10:04:29 -04:00
Andras Bacsai
4f4453b17c chore: Update proxy configuration paths for Caddy and Nginx in dev 2024-10-04 13:46:33 +02:00
Andras Bacsai
d60d90de92 chore: Update anythingllm.yaml volumes configuration 2024-10-04 13:41:14 +02:00
Andras Bacsai
ce49f4806d fix: proxy conf in dev 2024-10-04 13:25:17 +02:00
Andras Bacsai
82daf6c420 Merge pull request #2980 from rennokki/feature/ai-templates
New services: LLM & AI-related one-click services
2024-10-04 13:24:34 +02:00
Andras Bacsai
f1eb43815e Merge branch 'next' into feature/ai-templates 2024-10-04 12:44:15 +02:00
Andras Bacsai
be9041d592 chore: Update MariaDB image to version 11 and fix service environment variable orders 2024-10-04 12:20:35 +02:00
Andras Bacsai
c2c0afa0ba fix: service env orders, application env orders 2024-10-04 12:08:57 +02:00
Andras Bacsai
611f70d79f chore: Remove commented code for shared variable type validation 2024-10-04 12:08:06 +02:00
Alexander G.
59c54b8aec Merge branch 'next' into feature/ai-templates 2024-10-04 12:29:30 +03:00
Alex Renoki
f0f685b78a Added anythingllm 2024-10-04 12:29:08 +03:00
Alex Renoki
c54d77515b Added unstructured 2024-10-04 11:54:32 +03:00
Alex Renoki
9435be7cce Added Argilla 2024-10-04 11:49:04 +03:00
Alex Renoki
f2ccc4059d Added prefect 2024-10-04 11:33:01 +03:00
Alex Renoki
21c6510e9d Improved healthchecks and generations 2024-10-04 11:26:45 +03:00
Andras Bacsai
2ca73695b3 fix: tag mass redeployments 2024-10-04 10:22:58 +02:00
Alex Renoki
31193a9f66 Fixed postgres healthchecks 2024-10-04 08:49:02 +03:00
Alex Renoki
9f5257bf8a Added infisical 2024-10-04 08:48:49 +03:00
Alex Renoki
78a34dbef1 Fixed langfuse 2024-10-04 07:21:20 +03:00
Alex Renoki
f4d650b03f Fixed litellm 2024-10-04 07:09:58 +03:00
Alex Renoki
97ee34f1a1 Added searxng 2024-10-04 06:53:14 +03:00
Alex Renoki
9014062b48 Added weaviate 2024-10-04 06:43:59 +03:00
Alex Renoki
a8f230b5eb Added qdrant 2024-10-04 06:36:25 +03:00
Alex Renoki
94ee525e0a Merge branch 'feature/ai-templates' of github.com:rennokki/coolify into feature/ai-templates 2024-10-04 05:46:26 +03:00
Alex Renoki
d4ace01633 Added litellm 2024-10-04 05:40:08 +03:00
Alex Renoki
49bcd7b8e6 Bump to v2 2024-10-04 05:40:08 +03:00
Alex Renoki
5f613df4e0 Added langfuse 2024-10-04 05:40:07 +03:00
Andras Bacsai
65aeebd9fa fix: Reset description and subject fields after submitting feedback 2024-10-03 23:27:23 +02:00
Andras Bacsai
f26d2e1d0b chore: Update version to 4.0.0-beta.356 2024-10-03 23:26:32 +02:00
Andras Bacsai
a1d395ff0c Merge pull request #3708 from coollabsio/next
v4.0.0-beta.355
2024-10-03 23:04:54 +02:00
Andras Bacsai
4e9126887f Merge pull request #3707 from coollabsio/custom-traefik-middlewares
fix: parser, espacing container labels
2024-10-03 23:00:17 +02:00
Andras Bacsai
5fa650122d Merge pull request #3637 from MarioZet23/custom-traefik-middlewares
feat: Allow custom traefik middlewares
2024-10-03 22:59:46 +02:00
Andras Bacsai
ee7f8200ac fix: parser, espacing container labels 2024-10-03 22:58:06 +02:00
Andras Bacsai
d2a8f31a1c Merge branch 'next' into custom-traefik-middlewares 2024-10-03 22:44:22 +02:00
Andras Bacsai
2468d0044b chore: Update version to 4.0.0-beta.355 2024-10-03 22:39:44 +02:00
Andras Bacsai
81b8a58415 fix: scheduled backup for services view 2024-10-03 22:38:37 +02:00
Andras Bacsai
7442d19611 Merge pull request #3705 from coollabsio/next
v4.0.0-beta.354
2024-10-03 22:04:36 +02:00
Andras Bacsai
8c024ddb57 chore: Update homarr service template and remove unnecessary code 2024-10-03 22:02:18 +02:00
Andras Bacsai
d990a5691d chore: Update homarr service template and remove unnecessary code 2024-10-03 21:47:06 +02:00
Andras Bacsai
2181ef381b Merge pull request #3697 from danielalves96/add_homarr_template
feat: add homarr service tamplate and logo
2024-10-03 21:39:14 +02:00
Andras Bacsai
c80f5be974 chore: Update it-tools service template and port configuration 2024-10-03 21:38:13 +02:00
Andras Bacsai
358f6575f8 chore: Refactor modal-confirmation component 2024-10-03 21:38:08 +02:00
Andras Bacsai
de0f34734b Merge pull request #3702 from danielalves96/add-it-tools
feat: add it-tools service template and logo
2024-10-03 21:33:33 +02:00
Andras Bacsai
33cb2d150d chore: Fix application deployment queue filter logic 2024-10-03 21:32:02 +02:00
Andras Bacsai
5f07b473e9 fix: parse proxy config and check the set ports usage 2024-10-03 21:29:55 +02:00
Andras Bacsai
5bcd813792 chore: Remove commented code in Server model 2024-10-03 21:29:04 +02:00
Andras Bacsai
a0bb523507 chore: Remove debug statement in Service model 2024-10-03 21:11:14 +02:00
Andras Bacsai
0b4fc38d6b chore: Update version to 4.0.0-beta.354 2024-10-03 21:10:52 +02:00
Andras Bacsai
82c834915d Merge pull request #3703 from coollabsio/next
v4.0.0-beta.353
2024-10-03 21:03:16 +02:00
Andras Bacsai
d637675ce3 chore: Update service application view 2024-10-03 21:02:42 +02:00
Andras Bacsai
e71c04a0c7 chore: Update version to 4.0.0-beta.353 2024-10-03 20:59:10 +02:00
Andras Bacsai
885b3bdea7 Merge pull request #3701 from coollabsio/next
v4.0.0-beta.352
2024-10-03 20:53:17 +02:00
Daniel Alves
9d6757aeb7 feat: add it-tools service template and logo 2024-10-03 15:52:33 -03:00
Andras Bacsai
d84d0a816b chore: Refactor DatabaseBackupJob to handle missing team 2024-10-03 20:51:18 +02:00
Andras Bacsai
0da31c34b5 fix: add new supported database images 2024-10-03 20:47:22 +02:00
Andras Bacsai
ccdaf59ecb fix: service application view 2024-10-03 20:47:02 +02:00
Andras Bacsai
3fc9cf90ab chore: update version to 4.0.0-beta.352 2024-10-03 20:46:45 +02:00
Daniel Alves
c4be720b20 fix: update FQDN 2024-10-03 15:00:52 -03:00
Daniel Alves
aabe27efd1 feat: add homarr service tamplate and logo 2024-10-03 12:58:35 -03:00
Daniel Alves
b324303505 feat: add dozzle template 2024-10-03 11:19:05 -03:00
Andras Bacsai
a8982379c9 Merge pull request #3683 from coollabsio/next
v4.0.0-beta.351
2024-10-03 15:15:21 +02:00
Andras Bacsai
6dd0bd0742 fix: api useBuildServer 2024-10-03 15:09:56 +02:00
Andras Bacsai
1d3494a6ba fix: network handling
fix: environment variable handling
2024-10-03 15:04:40 +02:00
Andras Bacsai
ee5eb427c9 fix: bitcoin core template 2024-10-03 14:07:58 +02:00
Andras Bacsai
ad7b5e6e1c Merge pull request #3689 from ALsJourney/bitcoin_core_service
Basic Bitcoin Core node service
2024-10-03 14:00:52 +02:00
Andras Bacsai
73bd344147 fix: strapi template 2024-10-03 13:49:50 +02:00
Andras Bacsai
ef448280d8 fix: able to support more database dynamically from Coolify's UI 2024-10-03 13:49:43 +02:00
Andras Bacsai
5282248cb4 Merge pull request #3685 from statickidz/add-strapi-template
Add Strapi template
2024-10-03 13:18:40 +02:00
Andras Bacsai
14c9f25c57 feat: restart service without pulling the latest image 2024-10-03 13:17:35 +02:00
ALsJourney
44b3d08d86 Finished basic bitcoin core service 2024-10-03 13:13:25 +02:00
Andras Bacsai
5da1f48ae1 fix typo 2024-10-03 12:43:41 +02:00
Andras Bacsai
2bc1b9027c Merge pull request #3679 from alepouna/typo-fix
Update docker-registry.yaml
2024-10-03 12:43:46 +02:00
Andras Bacsai
1c7ca56756 feat: backup all databases for mysql,mariadb,postgresql 2024-10-03 12:39:45 +02:00
Adrian Barrio
d70faea845 Merge remote-tracking branch 'origin/next' into add-strapi-template 2024-10-03 11:33:35 +02:00
Adrian Barrio
fdb5cab875 feat: add strapi template 2024-10-03 11:28:27 +02:00
Andras Bacsai
bb6cb8edc9 improvement: show backup button on supported db service stacks 2024-10-03 10:48:25 +02:00
Andras Bacsai
a6ec2b92fb version++ 2024-10-03 10:23:38 +02:00
Andras Bacsai
436a1d945f Merge pull request #3663 from coollabsio/next
v4.0.0-beta.350
2024-10-03 10:09:05 +02:00
Andras Bacsai
a6a3abc273 chore: Update soketi service image to version 1.0.3 2024-10-03 10:05:54 +02:00
Andras Bacsai
c4e702f096 fix: able to select root permission easier 2024-10-03 09:57:37 +02:00
alepouna
31df222798 Update docker-registry.yaml 2024-10-03 04:04:37 +03:00
Andras Bacsai
e91939a4ea refactor: Remove unnecessary watch command from soketi service entrypoint 2024-10-02 21:24:09 +02:00
Andras Bacsai
ceccd093d2 refactor: Improve socket reconnection interval in terminal.js 2024-10-02 21:24:05 +02:00
Andras Bacsai
66bb4e0fc1 refactor: Remove inactivity timer in terminal-server.js 2024-10-02 21:23:59 +02:00
Andras Bacsai
dd3ff38df7 refactor: Encode delimiter in SshMultiplexingHelper 2024-10-02 21:23:46 +02:00
Andras Bacsai
69553ec314 refactor: Improve popup component styling and button behavior 2024-10-02 18:26:51 +02:00
Andras Bacsai
7bb1bf0ae3 refactor: Improve parsing of commands for sudo in parseCommandsByLineForSudo 2024-10-02 18:26:40 +02:00
Andras Bacsai
a1a8f1336a refactor: Fix indentation in modal-confirmation.blade.php 2024-10-02 17:35:48 +02:00
Andras Bacsai
207fe1d709 wtf wtf wtf 2024-10-02 17:27:02 +02:00
Andras Bacsai
059535a676 chore: Remove commented out code for uploading to S3 in DatabaseBackupJob 2024-10-02 16:43:01 +02:00
Andras Bacsai
765a74ca4f handle errors in databasebackupjob 2024-10-02 15:33:14 +02:00
Andras Bacsai
e03e4f2e91 refactor: Improve SSH command generation in Terminal.php and terminal-server.js 2024-10-02 15:16:55 +02:00
Andras Bacsai
0ab432d5e6 chore: Remove unnecessary command from SshMultiplexingHelper 2024-10-02 14:54:48 +02:00
Andras Bacsai
6f25b548c7 fix: realtime watch in development mode 2024-10-02 14:18:52 +02:00
Andras Bacsai
d55e4bf381 feat: Handle HTTPS domain in ConfigureCloudflareTunnels 2024-10-02 13:36:25 +02:00
Andras Bacsai
ec216254b5 chore: Update modal input in server form to prevent closing on outside click 2024-10-02 13:36:09 +02:00
Andras Bacsai
fbb36bfe8e revert modal-confirmation in dev 2024-10-02 12:01:12 +02:00
Andras Bacsai
dd782e75f5 fix: local dev s3 uploads
fix: hetzner s3 uploads (mc alias instead of mc host)
2024-10-02 11:45:30 +02:00
Andras Bacsai
2be2f0ac79 feat: support Hetzner S3 2024-10-02 10:25:45 +02:00
Andras Bacsai
97943db5f4 chore: Add missing import for Attribute class in ApplicationDeploymentQueue model 2024-10-02 09:21:54 +02:00
Andras Bacsai
bbd2748ad7 chore: Update command signature and description for cleanup application deployment queue 2024-10-02 09:21:50 +02:00
Andras Bacsai
a530804a71 feat: Add command to check application deployment queue 2024-10-02 09:21:28 +02:00
Andras Bacsai
8ca8ab82b0 refactor: Remove deployment queue when deleting an application 2024-10-02 09:20:49 +02:00
Andras Bacsai
024ad8e943 fix: cleanup stucked applicationdeploymentqueue 2024-10-02 09:20:08 +02:00
Andras Bacsai
4d86b556a4 fix: ipv6 scp should use -6 flag 2024-10-02 08:15:03 +02:00
Andras Bacsai
73e38ff951 chore: Update version numbers to 4.0.0-beta.350 in configuration files 2024-10-02 08:13:49 +02:00
Andras Bacsai
5354561c39 Merge pull request #3659 from coollabsio/next
v4.0.0-beta.349
2024-10-01 21:36:50 +02:00
Andras Bacsai
223cd37031 fix: remove autofocuses 2024-10-01 21:36:16 +02:00
Andras Bacsai
93a0725a4e Merge pull request #3613 from nfnot/main
refactor: Update search input placeholder in resource index view
2024-10-01 21:32:21 +02:00
Andras Bacsai
4d1d4598b6 chore: Update version numbers to 4.0.0-beta.349 and 4.0.0-beta.350 2024-10-01 21:31:54 +02:00
Norman
888e96448c Merge branch 'main' into main 2024-10-01 13:33:02 +03:00
Andras Bacsai
0eea31544e Merge pull request #3636 from coollabsio/next
v4.0.0-beta.348
2024-10-01 12:04:39 +02:00
Andras Bacsai
8065da9fa0 Refactor input fields to include autocomplete="off" 2024-10-01 11:55:01 +02:00
Andras Bacsai
5cd81fe255 wip: server storage check 2024-10-01 11:52:36 +02:00
Andras Bacsai
b00828d4aa chore: Refactor instanceSettings() function and improve code readability 2024-10-01 11:36:37 +02:00
Andras Bacsai
5a770f9ddb fix: Handle deletion of 'hello' in confirmation modal for dev environment 2024-10-01 11:16:16 +02:00
Andras Bacsai
903d84e0dd fix: confirmation text could be empty, then skip that part
fix: confirmation modal should not be closed by clicking away
2024-10-01 10:53:20 +02:00
Andras Bacsai
16ccc87fbe chore: Add "Not Usable" indicator for storage items 2024-10-01 10:45:03 +02:00
Andras Bacsai
e4108863a8 chore: Remove unnecessary code in DatabaseBackupJob.php 2024-10-01 10:43:04 +02:00
Andras Bacsai
83549965ca Refactor instanceSettings() function for improved code readability 2024-10-01 10:37:40 +02:00
Andras Bacsai
4d9dcfb84c chore: Update Coolify Realtime image to version 1.0.2 2024-10-01 10:34:11 +02:00
Andras Bacsai
4db50bd025 chore: Refactor instanceSettings() function to improve code readability 2024-10-01 10:33:56 +02:00
Andras Bacsai
817bd9c35d chore: Update Coolify Realtime workflow to only trigger on the main branch 2024-10-01 10:09:38 +02:00
Andras Bacsai
2e10d670be Merge pull request #3647 from derpoho/next
fixes coollabsio#3645, incorrect adding of sudo if non-root user
2024-10-01 10:00:48 +02:00
Andras Bacsai
cec0d7d1f7 Merge pull request #3651 from LEstradioto/fix-enhance-terminal-2
Fix Terminal: no more exit fallback
2024-10-01 09:52:38 +02:00
Andras Bacsai
1f45532477 fix: mixpost 2024-10-01 09:35:54 +02:00
Andras Bacsai
bcc92e1f32 fix: in dev mode do not ask confirmation on delete 2024-10-01 09:31:01 +02:00
Andras Bacsai
31cf3294bf Update UUID description in API controller and OpenAPI specification 2024-10-01 09:04:01 +02:00
Andras Bacsai
26307334a4 feat: Add query parameters for deleting configurations, volumes, docker cleanup, and connected networks 2024-10-01 09:02:29 +02:00
Andras Bacsai
9212f3b24c feat: Update resource deletion job to allow configurable options through API 2024-10-01 09:02:16 +02:00
Andras Bacsai
31b2196ad9 Merge pull request #3650 from ImBIOS/patch-1
chore: fix docs link in running state
2024-10-01 07:40:59 +02:00
Luan Estradioto
2c38af1df0 - fixes: no more exit fallback to server 2024-10-01 01:42:09 -03:00
Imamuzzaki Abu Salam
89e84c5b48 chore: fix docs link in running state 2024-10-01 10:16:16 +07:00
Marcus Pohorely
39d2fdc08b Merge branch 'main' into next 2024-09-30 23:24:42 +02:00
Marcus Pohorely
ac9f817b9f fixes #3645, incorrect adding of sudo if non-root user 2024-09-30 23:21:58 +02:00
Andras Bacsai
852e881805 Merge pull request #2351 from AlejandroAkbal/main
feat: add Mixpost template
2024-09-30 16:35:55 +02:00
Andras Bacsai
001dec6604 chore: Update Mailpit logo to use SVG format 2024-09-30 16:31:36 +02:00
Andras Bacsai
cf9c62cbf0 Merge pull request #3202 from FranckKe/add_mailpit_template
feat: add Mailpit template
2024-09-30 16:16:05 +02:00
Andras Bacsai
99a4f721f1 Merge pull request #2904 from LEstradioto/add-storage-link-to-dev-environment
add storage:link to dev environment
2024-09-30 15:48:10 +02:00
Andras Bacsai
505be9dc97 add debugbar config 2024-09-30 15:44:07 +02:00
Andras Bacsai
cfec5b3a70 feat: Add support for use_build_server in API endpoints for creating/updating applications 2024-09-30 14:18:41 +02:00
Andras Bacsai
95483f1464 Merge pull request #3615 from liberocks/feature/api-use-build-server
feat: allow specify use_build_server when creating/updating an application through API
2024-09-30 14:17:59 +02:00
Andras Bacsai
987b90ead2 fix: new dev volumes and service files
fix: new parser version (4) that will fix data layout (applications goes to /applications, services goes to /services)
2024-09-30 14:16:37 +02:00
Andras Bacsai
68f541ded3 fix: filebrowser template 2024-09-30 14:16:37 +02:00
Andras Bacsai
afdf4cd5a8 service restart does no cleanup docker 2024-09-30 14:16:37 +02:00
Andras Bacsai
11dbce1471 Merge pull request #3630 from jordanamr/fix/filebrowser-template
fix(templates): filebrowser FQDN env variable
2024-09-30 14:16:14 +02:00
Andras Bacsai
e1037ac031 Merge branch 'next' into fix/filebrowser-template 2024-09-30 14:16:01 +02:00
Andras Bacsai
88c2b1e841 Merge pull request #3609 from peaklabs-dev/fix-2185
Fix: Remove memlock from dragonfly DB as it caused problems for some users
2024-09-30 12:18:38 +02:00
Andras Bacsai
f46fd8872a Merge pull request #3608 from peaklabs-dev/add-debugbar
Feat: Add debug bar for better debugging
2024-09-30 12:15:04 +02:00
Andras Bacsai
b6b4d93658 fix: compose based terminal 2024-09-30 11:58:28 +02:00
Andras Bacsai
8385b7dfe8 fix: handle edge case when build variables and env variables are in different format 2024-09-30 11:15:23 +02:00
Andras Bacsai
a660117015 use latest helper in dev 2024-09-30 11:14:53 +02:00
Andras Bacsai
6fe31c26a3 refactor: remove unnecessary code 2024-09-30 11:14:40 +02:00
Leonardo Cabeza
6aa7cfc479 Merge branch 'next' into next 2024-09-29 20:58:27 -05:00
Andras Bacsai
cccd05f322 chore: Refactor code to improve SSH key handling and storage 2024-09-29 20:12:30 +02:00
MarioZet
1b0e2e1257 Feat. Apply all middlewares from labels to coolify router, instead of only basicauth and redirect 2024-09-29 20:08:39 +02:00
Andras Bacsai
795af8de52 Make sure key is not created with wrong private_key 2024-09-29 20:05:02 +02:00
Jordan Rey
606d46eef0 fix(templates): filebrowser FQDN env variable 2024-09-29 15:36:41 +02:00
Andras Bacsai
56ec0a3004 chore: Update version numbers to 4.0.0-beta.348 2024-09-29 13:27:45 +02:00
Norman
b84576ce87 Merge branch 'main' into main 2024-09-28 18:18:47 +03:00
Andras Bacsai
019a8bfaa6 Merge pull request #3606 from coollabsio/next
v4.0.0-beta.347
2024-09-28 11:14:33 +02:00
Andras Bacsai
fe20480fdc fix: proxy 2024-09-28 11:14:14 +02:00
Andras Bacsai
71e4b0f9d6 Refactor server form blade template 2024-09-28 11:12:03 +02:00
Andras Bacsai
bc46b0371d Add private key storage for server validation 2024-09-28 10:20:32 +02:00
liberocks
f5785b1f17 fix: save settings after assigning value 2024-09-28 11:28:48 +07:00
liberocks
2219219906 fix: edit is_build_server_enabled upon creating application on other application type 2024-09-28 11:25:00 +07:00
liberocks
9ec3233c0c feat: allow specify use_build_server when creating/updating an application 2024-09-28 11:11:43 +07:00
Norman
f2a9a04461 refactor: Update search input placeholder in resource index view 2024-09-28 01:14:54 +00:00
Andras Bacsai
7c269bd0bf chore: custom vite envs 2024-09-27 21:07:56 +02:00
peaklabs-dev
b99474ac73 Fix: Remove memlock as it caused problems for some users 2024-09-27 17:38:34 +02:00
Andras Bacsai
fcadb20d58 chore: Update database startup heading title 2024-09-27 17:32:37 +02:00
Andras Bacsai
24733d0670 Merge pull request #3572 from ejscheepers/main
Fixed NEXT_PUBLIC_API_URI in plunk.yaml
2024-09-27 17:32:18 +02:00
Andras Bacsai
d3898ee320 chore: Update database startup heading title 2024-09-27 17:31:48 +02:00
Andras Bacsai
09cd087cd0 chore: Update backup deletion labels to use language files 2024-09-27 17:29:36 +02:00
Andras Bacsai
d75e55111a chore: Update web.php to cast server port as integer 2024-09-27 17:26:27 +02:00
Andras Bacsai
8e6c7eaeda chore: Update API Tokens view to include link to Settings menu 2024-09-27 17:23:36 +02:00
Andras Bacsai
9489a0881f Merge pull request #3542 from peaklabs-dev/fix-dev-volumes
Fix: Volume issues in development
2024-09-27 17:15:39 +02:00
Andras Bacsai
691f606fa0 Merge branch 'next' into fix-dev-volumes 2024-09-27 17:13:02 +02:00
Andras Bacsai
ac60b8cb22 chore: Remove unused .env.development.example file 2024-09-27 17:11:55 +02:00
Andras Bacsai
46ae2a4ae1 update service-template 2024-09-27 17:08:32 +02:00
Andras Bacsai
f0981078ce chore: Update versions.json file 2024-09-27 17:08:04 +02:00
Andras Bacsai
0816169cb6 Merge pull request #3591 from clho40/next
Next
2024-09-27 17:07:45 +02:00
Andras Bacsai
df8c5c6bf2 Merge branch 'next' into next 2024-09-27 17:06:52 +02:00
Andras Bacsai
ea1be05c17 Merge pull request #3594 from peaklabs-dev/lock-closed-issues-and-prs
Feat: GitHub action that locks closed issues, discussions, and PRs
2024-09-27 17:05:37 +02:00
Andras Bacsai
eafba5d115 fix typo 2024-09-27 17:04:36 +02:00
Andras Bacsai
40eec96a1e Merge pull request #3568 from tiennm99/main
[Twenty] (Fix) typo
2024-09-27 17:04:04 +02:00
Andras Bacsai
46268f915f Merge pull request #3585 from peaklabs-dev/close-stale-issues-prs
Feat: GH Action that close stale issues and PRs
2024-09-27 17:03:34 +02:00
peaklabs-dev
c237cea280 Merge branch 'coollabsio:main' into add-debugbar 2024-09-27 16:59:21 +02:00
peaklabs-dev
26f18e25aa Create .gitignore 2024-09-27 16:54:24 +02:00
Andras Bacsai
8d02fb9254 chore: Refactor API Tokens component to use isApiEnabled flag 2024-09-27 16:48:17 +02:00
Andras Bacsai
c52fe571f5 Merge pull request #3576 from peaklabs-dev/fix-api-enabeled
Fix: Disable API by default and do not allow API key creation when API is disabled
2024-09-27 16:46:21 +02:00
Andras Bacsai
4995675745 Merge pull request #3564 from peaklabs-dev/remove-pr-build-gh-action
Fix: Removed some GH Actions and ordered issue templates
2024-09-27 16:44:00 +02:00
Andras Bacsai
65356e9bae fixes 2024-09-27 16:43:07 +02:00
Andras Bacsai
6726964f18 Merge pull request #3545 from peaklabs-dev/improve-cleanup
Feat: Improve Docker cleanup
2024-09-27 16:41:03 +02:00
Andras Bacsai
9a766aedc1 Merge branch 'next' into improve-cleanup 2024-09-27 16:40:48 +02:00
Andras Bacsai
96b8ddf664 chore: Add autocomplete attribute to input fields 2024-09-27 16:32:03 +02:00
Andras Bacsai
8adf4051fd refactor: Update delete server confirmation modal buttons 2024-09-27 15:45:59 +02:00
Andras Bacsai
dedf2cf87b fix: proxy fixes 2024-09-27 15:36:51 +02:00
Andras Bacsai
72e12aaf5c feat: Add ContainerStatusTypes enum for managing container status 2024-09-27 15:27:09 +02:00
Andras Bacsai
6c78580f1f refactor: Improve start proxy script to handle existing containers gracefully 2024-09-27 15:27:05 +02:00
Andras Bacsai
75c8f6c94a Update version numbers to 4.0.0-beta.347 2024-09-27 14:45:07 +02:00
Andras Bacsai
55847d90c0 Merge pull request #3518 from coollabsio/improve-seeders
Refactor: Improve seeders
2024-09-27 14:43:56 +02:00
Andras Bacsai
b374addc37 Merge pull request #3490 from peaklabs-dev/improve-release.md
Feat: Improve release.md
2024-09-27 14:40:08 +02:00
peaklabs-dev
5541c737b7 Remove all deleted seeders 2024-09-27 12:24:58 +02:00
peaklabs-dev
39015e7d6d Fix: Typo in install script 2024-09-27 12:17:54 +02:00
Andras Bacsai
4c3beb6715 Merge pull request #3586 from coollabsio/next
v4.0.0-beta.346
2024-09-27 09:53:25 +02:00
Andras Bacsai
a807016cab Merge pull request #3574 from LEstradioto/fix-enhance-terminal
Terminal fixes
2024-09-27 09:49:31 +02:00
Andras Bacsai
2c2173e773 refactor: Update environment variable name for uptime-kuma service 2024-09-27 08:48:17 +02:00
peaklabs-dev
2364fac240 Update lock-closed-issues-discussions-and-prs.yml 2024-09-26 19:10:40 +02:00
Ho Chia Leung
68c065baa0 Merge pull request #1 from clho40/clho40-logto-entrypoint-fixes
Clho40 logto entrypoint fixes
2024-09-26 16:43:29 +02:00
Ho Chia Leung
bfc4474815 merged the latest entrypoint configuration from Logto's source
The previous entrypoint configuration throws error error: type "application_type" does not exist
2024-09-26 16:41:38 +02:00
peaklabs-dev
0e31c02f9e Update lock-closed-issues-discussions-and-prs.yml 2024-09-26 14:27:58 +02:00
peaklabs-dev
6ee80c3ea7 Update lock-closed-issues-discussions-and-prs.yml 2024-09-26 14:20:21 +02:00
peaklabs-dev
b9d24913e8 Create lock-closed-issues-discussions-and-prs.yml 2024-09-26 14:10:16 +02:00
Andras Bacsai
9a1c9124ae refactor: Add support for IPv6 addresses in sslip function 2024-09-26 13:47:13 +02:00
Andras Bacsai
306c4dcbc5 chore: Update version numbers to 4.0.0-beta.346 2024-09-26 13:47:07 +02:00
peaklabs-dev
ed7e4360c8 Merge branch 'close-stale-issues-prs' of https://github.com/peaklabs-dev/coolify into close-stale-issues-prs 2024-09-26 13:39:42 +02:00
peaklabs-dev
f02db282ca Update manage-stale-issues-and-prs.yml 2024-09-26 13:39:36 +02:00
Andras Bacsai
3535dbb98f Merge pull request #3583 from coollabsio/next
v4.0.0-beta.345
2024-09-26 13:21:16 +02:00
Andras Bacsai
84b2af53d8 refactor: Fix modal input closeOutside prop in configuration.blade.php 2024-09-26 13:20:06 +02:00
Andras Bacsai
ba70451765 refactor: Update install.sh script to remove redirection of upgrade output to /dev/null 2024-09-26 13:07:06 +02:00
Andras Bacsai
f3ec4ca4a3 refactor: Improve modal confirmation titles and button labels 2024-09-26 13:07:01 +02:00
Andras Bacsai
174923de98 refactor: Update ProductionSeeder to fix issue with coolify_key assignment 2024-09-26 13:06:56 +02:00
Andras Bacsai
68ab8dc14c refactor: Add localhost as Server if it doesn't exist and not in cloud environment 2024-09-26 12:31:39 +02:00
Andras Bacsai
b218356f2d chore: Update version numbers to 4.0.0-beta.345 2024-09-26 12:31:36 +02:00
Andras Bacsai
e83aecebc3 Merge pull request #3559 from coollabsio/next
v4.0.0-beta.344
2024-09-26 12:22:48 +02:00
Andras Bacsai
0bb1f57ea7 fix: deploy key based deployments 2024-09-26 12:19:49 +02:00
peaklabs-dev
49144552b3 Merge branch 'coollabsio:main' into close-stale-issues-prs 2024-09-26 11:47:40 +02:00
peaklabs-dev
111e9ba3a3 Fix: Disable API by default 2024-09-26 10:45:18 +02:00
Andras Bacsai
d006edc485 refactor: Add localhost as Server if it doesn't exist and not in cloud environment 2024-09-26 10:37:02 +02:00
Andras Bacsai
aa28c99827 refactor: Update OS_TYPE for Asahi Linux in install.sh script 2024-09-26 10:36:59 +02:00
peaklabs-dev
47920f1191 Feat: If API is disabeled do not show API token creation stuff 2024-09-26 10:32:12 +02:00
Luan Estradioto
6bd8583eab - resize check to int
- modularize, tree-shake terminal, idempotency on navigations
2024-09-25 22:14:05 -03:00
EJS00102
92dbd44c8c Fixed NEXT_PUBLIC_API_URI in plunk.yaml 2024-09-26 00:01:38 +02:00
Katja Lutz
2cf0f4facc chore: add Nitropage service template and logo 2024-09-25 21:23:23 +02:00
tiennm99
be6caf8d76 [Twenty] (Fix) typo 2024-09-25 23:41:04 +07:00
peaklabs-dev
6cf5a2802b ordered the issue templates 2024-09-25 13:28:33 +02:00
peaklabs-dev
9c756ad6c5 Delete docker-image.yml 2024-09-25 13:28:17 +02:00
peaklabs-dev
20de0c56a4 Delete fix-php-code-style-issues 2024-09-25 13:28:13 +02:00
peaklabs-dev
0058f9aa13 Delete pr-build.yml 2024-09-25 13:28:08 +02:00
Andras Bacsai
567bbe9d0b chore: Update version numbers to 4.0.0-beta.344 2024-09-25 10:27:51 +02:00
Andras Bacsai
59d2c9748a fix: make sure to reload window if app status changes 2024-09-25 10:27:23 +02:00
Andras Bacsai
bd2e1ad9fe refactor: Fix typo in execute-container-command.blade.php 2024-09-25 10:25:35 +02:00
Andras Bacsai
43df918e43 Merge pull request #3550 from coollabsio/next
v4.0.0-beta.343
2024-09-25 09:29:54 +02:00
Andras Bacsai
1a8d15390d refactor: Update confirmation label in danger.blade.php template 2024-09-25 09:27:17 +02:00
Andras Bacsai
4af6caa79c refactor: Update checkbox component to support full width option 2024-09-25 09:16:28 +02:00
peaklabs-dev
dfd6fdf0a8 Merge pull request #3554 from coollabsio/next
Next
2024-09-25 00:00:49 +02:00
peaklabs-dev
b0635327a1 Revert "Fix: Typo"
This reverts commit ca21545d7b.
2024-09-25 00:00:26 +02:00
Andras Bacsai
8e91d958be refactor: Improve layout and add checkbox for task enablement in scheduled task form 2024-09-24 21:29:55 +02:00
Andras Bacsai
17d55630d5 refactor: Group and sort fields in StackForm by service name and password status 2024-09-24 21:17:07 +02:00
Andras Bacsai
70dfa101ef refactor: Improve label positioning in input and checkbox components 2024-09-24 21:04:04 +02:00
Andras Bacsai
d6b4e33db3 fix: exited services statuses 2024-09-24 20:40:41 +02:00
Andras Bacsai
a9670bd6eb refactor: Remove commented out code and improve environment variable handling in newParser function 2024-09-24 18:38:35 +02:00
Andras Bacsai
02165c2b9f chore: Update version numbers to 4.0.0-beta.343 2024-09-24 18:22:06 +02:00
Andras Bacsai
afbdd2eb06 fix: parser 2024-09-24 18:21:31 +02:00
Andras Bacsai
b0e6014e8f Merge pull request #3546 from coollabsio/dependabot/npm_and_yarn/rollup-3.29.5
chore(deps): bump rollup from 3.29.4 to 3.29.5
2024-09-24 14:34:17 +02:00
dependabot[bot]
0f13af2eca chore(deps): bump rollup from 3.29.4 to 3.29.5
Bumps [rollup](https://github.com/rollup/rollup) from 3.29.4 to 3.29.5.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v3.29.4...v3.29.5)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-24 12:27:43 +00:00
Andras Bacsai
d4d9268f12 Merge pull request #3506 from coollabsio/next
v4.0.0-beta.342
2024-09-24 14:26:04 +02:00
peaklabs-dev
f9dbc30812 Feat: Use the new confirmation flow 2024-09-24 13:06:03 +02:00
Andras Bacsai
62459f9a95 refactor: Remove unused code in DatabaseBackupStatusJob and PopulateSshKeysDirectorySeeder 2024-09-24 11:57:13 +02:00
Andras Bacsai
eb80a7e2f7 chore: Update Docker commands to start proxy 2024-09-24 10:53:12 +02:00
Leonardo Cabeza
a6e50ce24e Merge branch 'next' into next 2024-09-23 17:16:44 -05:00
Andras Bacsai
57d8930f9e chore: Update cleanup command to use Redis instead of queue 2024-09-23 23:48:12 +02:00
Andras Bacsai
688c27c901 fix: cloudflare tunnel configuration, ui, etc 2024-09-23 23:18:23 +02:00
Andras Bacsai
480ae3de8a Refactor Server.php to improve default 404 redirect setup 2024-09-23 22:57:24 +02:00
Andras Bacsai
21a5b4ce80 chore: Refactor pre-commit hook to improve performance and readability 2024-09-23 22:16:28 +02:00
Andras Bacsai
12a0d71e47 Merge pull request #3543 from peaklabs-dev/cf-production-ready
Feat: Make cloudflare production ready
2024-09-23 21:53:23 +02:00
peaklabs-dev
5ed7ae3d3e remove ray 2024-09-23 21:45:59 +02:00
Andras Bacsai
6dd3adba4d refactor: Simplify SSH key synchronization logic 2024-09-23 21:16:25 +02:00
Andras Bacsai
08d58eb8f8 chore: Update CONTRIBUTING.md with troubleshooting note about database migrations 2024-09-23 20:55:20 +02:00
Andras Bacsai
46a7937761 test 2024-09-23 20:54:56 +02:00
Andras Bacsai
13a5536cff test 2024-09-23 20:54:18 +02:00
Andras Bacsai
2381706465 chore: Refactor pre-commit hook to improve performance and readability 2024-09-23 20:52:13 +02:00
Andras Bacsai
7a0f054f1a test 2024-09-23 20:49:26 +02:00
Andras Bacsai
00e1464185 chore: Add modified files to the commit 2024-09-23 20:48:57 +02:00
Andras Bacsai
fac4a8aaf9 hooks? 2024-09-23 20:45:42 +02:00
Andras Bacsai
2841675691 refactor 2024-09-23 20:31:50 +02:00
Andras Bacsai
573e5c4913 refactor 2024-09-23 20:29:22 +02:00
peaklabs-dev
6934a746f7 Fix: Make helper text more clean to use a FQDN and not an URL 2024-09-23 19:57:42 +02:00
Andras Bacsai
b570ccd7d3 format 2024-09-23 19:51:31 +02:00
Andras Bacsai
68efd4b553 fix: migrations 2024-09-23 19:50:26 +02:00
Andras Bacsai
e00ec2f75b chore: Remove unused migration file for populating SSH keys and clearing mux directory 2024-09-23 19:46:45 +02:00
peaklabs-dev
1bb192f3e2 Fix: Cloudflare tunnel 2024-09-23 19:23:46 +02:00
peaklabs-dev
e039f183da Merge pull request #16 from coollabsio/next
Next
2024-09-23 18:50:19 +02:00
peaklabs-dev
7aab44645f Merge branch 'cf-production-ready' into next 2024-09-23 18:50:08 +02:00
peaklabs-dev
7c52d7ef9e Update CONTRIBUTING.md 2024-09-23 17:33:06 +02:00
peaklabs-dev
83c6dcb063 Fix: Persist DBs, services and so on stored in data/coolify 2024-09-23 17:31:20 +02:00
peaklabs-dev
6bb21da9f3 Update CONTRIBUTING.md 2024-09-23 17:20:31 +02:00
peaklabs-dev
5a46ef42c8 Feat: clean new volume name for dev volumes 2024-09-23 17:03:10 +02:00
Andras Bacsai
04e504bb8b chore: Refactor DockerCleanupJob to remove unused middleware and uniqueId method 2024-09-23 12:10:46 +02:00
Andras Bacsai
960f970822 chore: Remove unused middleware and uniqueId method in DockerCleanupJob 2024-09-23 12:03:14 +02:00
Andras Bacsai
ae62781c6a fix: cloudflared service 2024-09-23 11:36:57 +02:00
Andras Bacsai
b42c15b2e5 Merge pull request #3483 from djsisson/update-CF-Tunnel-template
Update cloudflared.yaml
2024-09-23 11:36:52 +02:00
Andras Bacsai
c77cc45e0d fix: Logto service 2024-09-23 11:33:53 +02:00
Andras Bacsai
310708f1b6 Merge pull request #3480 from JustMrMendez/patch-1
FIXES: #3322 deploy DB alterations before starting the service
2024-09-23 11:33:55 +02:00
Andras Bacsai
8386aaf95b Merge pull request #3481 from peaklabs-dev/new-pull-request-template
Feat: New pull request template
2024-09-23 11:32:24 +02:00
Andras Bacsai
6d8d91802b chore: Update install.sh version to 1.6 2024-09-23 11:30:57 +02:00
Andras Bacsai
c0a1f15d34 Merge pull request #3472 from disjukr/asahi-linux
Add support to install.sh for Asahi Linux
2024-09-23 11:29:26 +02:00
Andras Bacsai
0222aa137d feat(api): add endpoint to execute a command 2024-09-23 11:09:14 +02:00
Andras Bacsai
4d36bc4742 Merge pull request #3535 from Luca-Sordetti/main
feat(api): add an endpoint to execute a command
2024-09-23 10:53:28 +02:00
Andras Bacsai
6639379ba6 Merge pull request #3522 from InfiniBrains/main
Add hint to "build variable" check in the enviroment variable tab. Remember the user what should be doing in order to make it work.
2024-09-23 10:49:59 +02:00
Andras Bacsai
d60ff107c4 Merge pull request #3539 from peaklabs-dev/fix-dev-error
Fix: Ray error on development
2024-09-23 10:49:39 +02:00
Andras Bacsai
a468ce77f0 refactor: Update shared.php to include predefined ports for services 2024-09-23 10:46:34 +02:00
Andras Bacsai
1fb0ee0db5 refactor: Add Postiz service to compose file (disabled for now) 2024-09-23 10:46:30 +02:00
Andras Bacsai
9281dda03f refactor: Fix typo in subscription URLs 2024-09-23 10:46:19 +02:00
peaklabs-dev
d3485cea8d Update pull_request_template.md 2024-09-23 10:27:48 +02:00
peaklabs-dev
92772f9132 Merge branch 'next' into fix-dev-error 2024-09-23 10:20:26 +02:00
Andras Bacsai
6f8c4a4ce4 refactor: Update select.blade.php to improve trademarks policy display 2024-09-23 09:44:33 +02:00
Andras Bacsai
85ab772acd refactor: Update select.blade.php to improve trademarks policy display 2024-09-23 09:43:17 +02:00
Andras Bacsai
6336125ca2 Merge pull request #3516 from nathanialhenniges/main
Added link for more services to the coolfiy docs
2024-09-23 09:42:49 +02:00
Andras Bacsai
341c7e3598 refactor: Update environment variables for services in compose files 2024-09-23 09:26:53 +02:00
Andras Bacsai
ff9b68b450 refactor: Remove unnecessary code in shared.php file 2024-09-23 09:26:49 +02:00
Andras Bacsai
a072a00926 Merge pull request #3525 from gabrielcossette/fix_ghost_template_domain
Fix Ghost Template domain generation
2024-09-23 09:05:34 +02:00
Andras Bacsai
b84d39ba56 refactor: Update confirmation button text for deletion actions 2024-09-23 08:58:04 +02:00
peaklabs-dev
164a213ab1 Fix: force helper image removal 2024-09-22 20:36:41 +02:00
peaklabs-dev
696acb71fe Improve helper text for all cleanup settings 2024-09-22 20:26:45 +02:00
peaklabs-dev
ff5e445b43 Feat: Manual cleanup button and unused volumes and network deletion 2024-09-22 20:02:51 +02:00
Luca-Sordetti
4e167dc539 feat(api): add an endpoint to execute a command 2024-09-22 12:38:25 +02:00
Italo
3a352e832b Merge branch 'next' into feat-forgejo 2024-09-21 23:50:07 -03:00
peaklabs-dev
76655a7c7d Remove dev .env form nightly as there is also no dev compose in nightly 2024-09-21 01:31:17 +02:00
peaklabs-dev
a26c1f0052 Update CONTRIBUTING.md 2024-09-21 01:11:03 +02:00
peaklabs-dev
cb4f7755c3 Update CONTRIBUTING.md 2024-09-21 01:08:28 +02:00
peaklabs-dev
b7139f8af8 remove clockwork 2024-09-21 00:20:16 +02:00
peaklabs-dev
a5b3fef1d8 Fix: ray error because port is not uncommented 2024-09-21 00:20:01 +02:00
peaklabs-dev
fca64d6726 Fix: volumes on development environment 2024-09-20 23:12:08 +02:00
Gabriel Cossette
b9a026b488 Fix Ghost Template domain generation
Signed-off-by: Gabriel Cossette <gabriel.cossette@mapgears.com>
2024-09-20 14:29:11 -06:00
Alexandre Tolstenko Nogueira
a4d0f1da9e Add hint to "build variable" check in the enviroment variable tab. remember the user what should be doing in order to make it work. #3477 2024-09-20 14:12:15 -04:00
Andras Bacsai
eb9bbf3eda refactor: Improve attribute sanitization in Server model 2024-09-20 18:14:52 +02:00
Andras Bacsai
be42f15711 refactor: Update ServerSeeder and PopulateSshKeysDirectorySeeder 2024-09-20 17:28:55 +02:00
peaklabs-dev
768b39dd25 Revert "remove team seeders"
This reverts commit 000a348c90.
2024-09-20 15:45:18 +02:00
peaklabs-dev
000a348c90 remove team seeders 2024-09-20 15:42:24 +02:00
peaklabs-dev
56f7eb769d Remove commented out seeders 2024-09-20 15:42:06 +02:00
peaklabs-dev
cc77775f20 Remove not needed seeders to make management simpler 2024-09-20 15:32:56 +02:00
peaklabs-dev
ca21545d7b Fix: Typo 2024-09-20 15:01:34 +02:00
Andras Bacsai
fa375e2fd8 refactor: Update install.sh script to check if coolify-db volume exists before generating SSH key 2024-09-20 14:39:58 +02:00
Andras Bacsai
760cf8aeb5 refactor: Update PrivateKey model to use ownedByCurrentTeam() scope for cleanupUnusedKeys() 2024-09-20 13:05:51 +02:00
Andras Bacsai
f9238ce263 Merge pull request #3509 from coollabsio/delete-unused-ssh-keys
Feat: Delete unused ssh keys button
2024-09-20 13:00:54 +02:00
Andras Bacsai
c0898f0568 refactor: Remove unnecessary code in PrivateKey model 2024-09-20 12:51:02 +02:00
Andras Bacsai
5b00b66f24 refactor: Update PrivateKey model to improve code readability and maintainability 2024-09-20 12:27:55 +02:00
Andras Bacsai
b81f9114d9 chore: Update ProductionSeeder to call OauthSettingSeeder and PopulateSshKeysDirectorySeeder 2024-09-20 11:34:13 +02:00
Andras Bacsai
be8573c828 chore: Update SSH key generation in install.sh script 2024-09-20 11:04:26 +02:00
Andras Bacsai
f1881d5c35 refactor: Update CleanupHelperContainersJob to use more efficient Docker command 2024-09-20 10:08:37 +02:00
Nathanial Henniges
699e76637b Added link for more services to the coolfiy docs 2024-09-20 03:02:31 -05:00
peaklabs-dev
dbc723089b Feat: Delete unused private keys button 2024-09-19 19:27:25 +02:00
Andras Bacsai
fc6f5d82db chore: Add SSH key for localhost in ProductionSeeder 2024-09-19 17:26:59 +02:00
Juan Felipe
1b8365d559 fix code review reported issues
- fix typo on healthcheck conditions
- fix db connections
- improve some default values
- add plugins and update persistance
2024-09-19 12:07:00 -03:00
Andras Bacsai
1815c9dccf fix: store original root key in the original location 2024-09-19 16:35:10 +02:00
Andras Bacsai
7d54fe9c18 chore: Update remove-labels-and-assignees-on-close.yml 2024-09-19 13:26:27 +02:00
Andras Bacsai
e9fbb7d2b0 fix: coolify-db should not be in the managed resources 2024-09-19 13:23:48 +02:00
Andras Bacsai
2d94ebf7f1 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-09-19 13:16:10 +02:00
Andras Bacsai
c874261c5b feat: Add nullable constraint to 'fingerprint' column in private_keys table 2024-09-19 13:16:08 +02:00
Andras Bacsai
1c5fd8d668 refactor: Update getAJoke function to use HTTPS for API request 2024-09-19 13:16:05 +02:00
Andras Bacsai
874e04e844 Merge pull request #3505 from peaklabs-dev/main
Fix: Update remove-labels-and-assignees-on-close.yml for PRs
2024-09-19 13:01:40 +02:00
peaklabs-dev
c46715db3c Merge branch 'main' of https://github.com/peaklabs-dev/coolify 2024-09-19 13:01:19 +02:00
peaklabs-dev
aa0fe922ea Update remove-labels-and-assignees-on-close.yml 2024-09-19 13:01:07 +02:00
peaklabs-dev
60e9fcc202 Update remove-labels-and-assignees-on-close.yml 2024-09-19 12:55:20 +02:00
Andras Bacsai
91485b5d4d refactor: Update getAJoke function to exclude offensive jokes 2024-09-19 12:50:51 +02:00
Andras Bacsai
6ec1b9b3f5 disable remove labels for issues 2024-09-19 12:47:35 +02:00
Andras Bacsai
240352f4b2 Merge pull request #3504 from coollabsio/fix-ssh-keys
Fix ssh keys
2024-09-19 12:44:56 +02:00
Andras Bacsai
cfd9ae9817 Merge pull request #3454 from peaklabs-dev/fix-ssh-keys
Fix: SSH Keys, Multiplexing issues and a lot of other small things for dev and prod
2024-09-19 12:44:23 +02:00
Andras Bacsai
f65789bdbb fix: proxy status 2024-09-19 12:32:56 +02:00
Andras Bacsai
9518040d23 refactor: Remove CleanupSshKeysJob from schedule in Kernel.php 2024-09-19 12:06:56 +02:00
Andras Bacsai
631b4e6438 Merge branch 'next' into fix-ssh-keys 2024-09-19 11:45:12 +02:00
Andras Bacsai
d47bd047bf fixes 2024-09-19 11:24:21 +02:00
Andras Bacsai
a65b62332b refactor: Remove commented out code in Navbar.php 2024-09-19 10:57:34 +02:00
Andras Bacsai
eb10bbb888 Merge pull request #3502 from coollabsio/fix-#2546-deletion-issues
Fixes
2024-09-19 10:41:57 +02:00
Andras Bacsai
4b02debf97 Merge pull request #3024 from peaklabs-dev/fix-#2546-deletion-issues
Feat: Fix all deletion issues, add graceful shutdown to everything, new delete confirmation process
2024-09-19 10:40:27 +02:00
Andras Bacsai
430fdcf2e9 chore: Update version numbers to 4.0.0-beta.342 2024-09-19 10:35:54 +02:00
Andras Bacsai
636de24b6c Merge pull request #3501 from coollabsio/#2546
refactor: Update Docker cleanup label in Heading.php and Navbar.php
2024-09-19 10:32:09 +02:00
Andras Bacsai
2e3267ee94 Merge branch 'fix-#2546-deletion-issues' into #2546 2024-09-19 10:32:02 +02:00
Andras Bacsai
98e744e808 refactor: Update Docker cleanup label in Heading.php and Navbar.php 2024-09-19 10:27:44 +02:00
Andras Bacsai
532f5e351e fixes 2024-09-18 21:24:42 +02:00
Andras Bacsai
182087cf1b a few changes here and there 2024-09-18 21:18:47 +02:00
peaklabs-dev
0494f9a7e5 Merge branch 'coollabsio:main' into service-getoutline 2024-09-18 20:44:53 +02:00
peaklabs-dev
6676c6bd64 Merge branch 'coollabsio:main' into new-pull-request-template 2024-09-18 20:44:42 +02:00
peaklabs-dev
22326ec0b4 Merge branch 'coollabsio:main' into improve-release.md 2024-09-18 20:44:24 +02:00
peaklabs-dev
8153e2f63b Merge branch 'coollabsio:main' into fix-ssh-keys 2024-09-18 20:43:59 +02:00
peaklabs-dev
b0793c879a Merge branch 'coollabsio:main' into close-stale-issues-prs 2024-09-18 20:43:01 +02:00
peaklabs-dev
dce8555aa7 Merge branch 'coollabsio:main' into add-debugbar 2024-09-18 20:42:29 +02:00
peaklabs-dev
35857de697 Merge branch 'coollabsio:main' into cf-production-ready 2024-09-18 20:42:18 +02:00
peaklabs-dev
cb177dae58 Update RELEASE.md 2024-09-18 18:36:04 +02:00
peaklabs-dev
4e8708051d Update RELEASE.md 2024-09-18 18:34:54 +02:00
peaklabs-dev
5b27eef984 Update RELEASE.md 2024-09-18 18:28:21 +02:00
peaklabs-dev
d1f28b42e5 Update RELEASE.md 2024-09-18 18:21:08 +02:00
peaklabs-dev
513ca6f57d Update RELEASE.md 2024-09-18 18:09:03 +02:00
Andras Bacsai
5ec45d547a Merge branch 'next' into fix-#2546-deletion-issues 2024-09-18 18:05:06 +02:00
peaklabs-dev
4aed40fb65 Update RELEASE.md 2024-09-18 17:15:39 +02:00
Darren Sisson
d2cf53578a Update cloudflared.yaml
add network mode host to template, otherwise cloudflared has no access to localhost
2024-09-18 16:09:24 +01:00
Mr. Mendez
1fa42ec82d FIXES: #3322 deploy DB alterations before updating
FIXES: #3322 

[Bug]: logto stop working after pulling latest image #3322
2024-09-18 08:59:02 -04:00
Andras Bacsai
e42c7e258c Merge pull request #3479 from coollabsio/next
v4.0.0-beta.341
2024-09-18 14:45:00 +02:00
Andras Bacsai
2c210abf57 Merge pull request #3478 from dennisblume/caddy-terminal-fix
Fix WebSocket connection for Terminal page when using Caddy
2024-09-18 14:36:48 +02:00
Dennis Blume
98ba7ac28c Fix WebSocket connection for Terminal page when using Caddy 2024-09-18 13:33:38 +02:00
Andras Bacsai
5772f2c3d9 feat: Add buddy logo 2024-09-18 13:18:31 +02:00
Andras Bacsai
cb9c569d4b Merge pull request #3470 from Skeyelab/add-buddy-svg
featu update: adding budge logo
2024-09-18 13:18:10 +02:00
Andras Bacsai
5616e588e9 Merge pull request #3473 from coollabsio/dependabot/npm_and_yarn/vite-4.5.5
chore(deps-dev): bump vite from 4.5.3 to 4.5.5
2024-09-18 12:56:34 +02:00
Andras Bacsai
40e844fab4 refactor: Remove unnecessary console.log statements in terminal.blade.php 2024-09-18 12:54:31 +02:00
Andras Bacsai
2e56edd939 refactor: Update WebSocket connection initialization in terminal.blade.php 2024-09-18 09:51:20 +02:00
Andras Bacsai
8179a5c6a3 feat: custom terminal host 2024-09-18 09:43:47 +02:00
Andras Bacsai
d0518153fb fix: generated fqdn for SERVICE_FQDN_APP_3000 magic envs 2024-09-18 09:21:57 +02:00
dependabot[bot]
29236bf101 chore(deps-dev): bump vite from 4.5.3 to 4.5.5
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.5.3 to 4.5.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.5.5/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-18 07:13:17 +00:00
Andras Bacsai
2d306d56ab chore: Update version numbers to 4.0.0-beta.341 2024-09-18 09:13:04 +02:00
JongChan Choi
e5768999e4 chore: Update install.sh to support Asahi Linux 2024-09-18 13:22:07 +09:00
Eric Dahl
b076db2eed update 2024-09-17 18:45:41 -04:00
Eric Dahl
3534424dc8 adding budge logo 2024-09-17 18:41:26 -04:00
peaklabs-dev
d6f498e6bb Update RELEASE.md 2024-09-17 23:05:03 +02:00
peaklabs-dev
3e0a5191b9 Update RELEASE.md 2024-09-17 23:01:56 +02:00
peaklabs-dev
28cde9fd50 Update RELEASE.md 2024-09-17 22:52:47 +02:00
peaklabs-dev
e415a99f71 Update RELEASE.md 2024-09-17 22:28:00 +02:00
peaklabs-dev
3b6c3609c3 Update pull_request_template.md 2024-09-17 22:11:30 +02:00
peaklabs-dev
95a97cf9cd Update pull_request_template.md 2024-09-17 22:09:00 +02:00
peaklabs-dev
7b33ef705b Update pull_request_template.md 2024-09-17 22:07:19 +02:00
peaklabs-dev
39449584ac Update pull_request_template.md 2024-09-17 22:04:19 +02:00
peaklabs-dev
19458e7445 Update pull_request_template.md 2024-09-17 21:47:19 +02:00
Andras Bacsai
2d9c728a64 Merge pull request #3468 from coollabsio/next
v4.0.0-beta.340
2024-09-17 17:31:39 +02:00
Andras Bacsai
12a8e9b0e1 fix: only update helper image in DB 2024-09-17 17:29:42 +02:00
Andras Bacsai
649cc2dac2 chore: Update version numbers to 4.0.0-beta.340 2024-09-17 17:25:38 +02:00
peaklabs-dev
bf48b33e64 Update pull_request_template.md 2024-09-17 17:14:15 +02:00
peaklabs-dev
d9181bd00b Fix: Multiplexing do not write file manually 2024-09-17 16:22:53 +02:00
peaklabs-dev
d13e2c0865 Fix: Clear mux directory 2024-09-17 15:57:57 +02:00
peaklabs-dev
42ff7b19a4 Fix: Few multiplexing things 2024-09-17 15:54:22 +02:00
peaklabs-dev
283fcc87a5 Fix: SSH algorhytm text 2024-09-17 15:31:44 +02:00
peaklabs-dev
ea3501ada6 Fix: SSH Multiplexing for Jobs 2024-09-17 15:31:05 +02:00
peaklabs-dev
175f4b9ae1 use shared functions when possible 2024-09-17 14:47:02 +02:00
peaklabs-dev
2bc74c75e1 Remove duplicated code 2024-09-17 14:43:02 +02:00
peaklabs-dev
4ac2758d70 Update .env.development.example 2024-09-17 14:35:08 +02:00
peaklabs-dev
bdc0fc87f0 Fix: UI bug, do not write ssh key to disk in server dialog 2024-09-17 14:33:24 +02:00
peaklabs-dev
845d32c94c Fix: Do not delete SSH Key from disk during server validation error 2024-09-17 14:33:04 +02:00
peaklabs-dev
6a6b947fba Fix: Make sure in use private keys are not deleted 2024-09-17 14:32:44 +02:00
peaklabs-dev
2ec66fd146 FIx: Server ID 0 2024-09-17 14:08:34 +02:00
peaklabs-dev
ccbbfd8908 Fix: ID issues on dev seeders 2024-09-17 14:03:31 +02:00
peaklabs-dev
43895419ff Remove unused code 2024-09-17 13:45:05 +02:00
peaklabs-dev
1c78067386 Feat: Add ssh key fingerprint and generate one for existing keys 2024-09-17 13:20:48 +02:00
peaklabs-dev
871d09bd96 Feat: Move more functions to the PrivateKey Model 2024-09-17 13:20:27 +02:00
peaklabs-dev
2d8bda4fa6 Fix: Private key with ID 2 on dev 2024-09-17 13:06:50 +02:00
peaklabs-dev
95070ab48d Feat: SSH Key cleanup job 2024-09-17 12:57:06 +02:00
peaklabs-dev
52c4994d44 Feat: remove unused code form multiplexing 2024-09-17 12:44:59 +02:00
peaklabs-dev
144508218e Fix: SSH multiplexing 2024-09-17 12:26:11 +02:00
peaklabs-dev
f9375f91ec Feat: Create a Multiplexing Helper 2024-09-16 22:33:43 +02:00
peaklabs-dev
86722939cd Fix. Remove write to SSH key on every remote command execution 2024-09-16 21:34:27 +02:00
peaklabs-dev
70b757df5b remove old function 2024-09-16 19:53:45 +02:00
peaklabs-dev
451272bf11 Fix: Use new function names and logic everywhere 2024-09-16 19:52:55 +02:00
peaklabs-dev
a68fbefadb Fix: Populate SSH keys in dev 2024-09-16 19:34:46 +02:00
peaklabs-dev
b79b4015d7 Feat: Populate SSH key folder 2024-09-16 19:08:15 +02:00
peaklabs-dev
0bfdc1c531 Feat: Store all keys on disk by default 2024-09-16 18:45:08 +02:00
peaklabs-dev
b09017ea46 Feat: new ssh key file name on disk 2024-09-16 18:11:37 +02:00
peaklabs-dev
95fcf38d45 Feat: Add is_sftp and is_server_ssh_key coloums 2024-09-16 18:11:16 +02:00
peaklabs-dev
54c03fae41 Remove ssh key fingerprint as we can just us uuid 2024-09-16 18:10:46 +02:00
peaklabs-dev
ba636a95dc Refactor SSH Keys 2024-09-16 17:24:42 +02:00
peaklabs-dev
3aee8e030e Fix: Encrypt private SSH keys in the DB 2024-09-16 13:17:39 +02:00
peaklabs-dev
02017334e5 Fix: Make sure invalid private keys can not be added 2024-09-16 13:02:48 +02:00
peaklabs-dev
f9b7841572 Feat: Add a fingerprint to every private key on save, create... 2024-09-16 12:54:48 +02:00
peaklabs-dev
7d39a5089c Feat: Add SSH Key fingerprint to DB 2024-09-16 11:53:26 +02:00
peaklabs-dev
b41a19d94d Update manage-stale-issues-and-prs.yml 2024-09-16 11:20:46 +02:00
peaklabs-dev
4450ee382f Update manage-stale-issues-and-prs.yml 2024-09-16 10:43:31 +02:00
peaklabs-dev
220f77e737 Create manage-stale-issues-and-prs.yml 2024-09-13 19:14:15 +02:00
peaklabs-dev
b9b33c831c Fix: Made help text more clear 2024-09-12 17:17:19 +02:00
peaklabs-dev
9310af0301 Feat: New cf tunnel install flow 2024-09-12 15:50:22 +02:00
peaklabs-dev
0d3d5f40fd Fix: Css issue with advanced settings and remove cf tunnel in onboarding 2024-09-12 14:58:52 +02:00
peaklabs-dev
c402d7f543 Merge branch 'coollabsio:main' into service-getoutline 2024-09-12 13:05:27 +02:00
peaklabs-dev
894f2c66d3 Merge branch 'coollabsio:main' into add-debugbar 2024-09-12 13:03:56 +02:00
peaklabs-dev
baa236f934 Feat: Add debug bar 2024-09-11 16:46:14 +02:00
peaklabs-dev
565cb54dba Merge branch 'coollabsio:main' into service-getoutline 2024-09-09 12:07:00 +02:00
Leonardo Cabeza
14c8023197 Merge branch 'next' into next 2024-09-07 10:12:52 -05:00
Darren Sisson
44ceb097f3 fix rabbit-mq 2024-09-05 21:28:49 +01:00
peaklabs-dev
eb5765979f Merge branch 'coollabsio:main' into service-getoutline 2024-09-05 18:05:58 +02:00
ayntk-ai
fc3c69f687 Feat: more conformations and fixes 2024-09-05 17:54:32 +02:00
Franck Kerbiriou
611e80c987 Update Authentik to 2024.8.0 2024-09-05 15:47:25 +02:00
ayntk-ai
08df814408 Feat: delete volume confirmation 2024-09-04 22:33:47 +02:00
ayntk-ai
a7b78dcf41 Feat: Backup job confirmation 2024-09-04 21:29:32 +02:00
ayntk-ai
2b5df8d2fd Feat: Team deletion confirmation 2024-09-04 21:15:46 +02:00
ayntk-ai
f4263ee022 Feat: User deletion confirmation 2024-09-04 21:14:18 +02:00
ayntk-ai
44f3f6001e Feat: Redeploy all confirmation 2024-09-04 21:04:43 +02:00
ayntk-ai
9105c1aa51 Feat: GH app deletion confirmation 2024-09-04 21:00:58 +02:00
ayntk-ai
3e04a7958e Feat/Fix: Proxy stop and restart confirmation 2024-09-04 20:41:17 +02:00
ayntk-ai
505127dae5 Feat: confirm server settings 2024-09-04 20:11:04 +02:00
ayntk-ai
371fe53911 Feat: confirm server deletion 2024-09-04 20:06:22 +02:00
ayntk-ai
9515bc6162 Feat: confirm private key 2024-09-04 20:02:01 +02:00
ayntk-ai
93a4a3e09c Feat: confirm API token 2024-09-04 19:58:36 +02:00
ayntk-ai
bbbd5cbaa1 Feat: confirm scheduled tasks 2024-09-04 19:44:10 +02:00
ayntk-ai
7fe3b78d45 Feat: Environment variabel deletion 2024-09-04 19:41:10 +02:00
ayntk-ai
a29353c1ae Feat: confirm ressource operation 2024-09-04 19:34:47 +02:00
ayntk-ai
c16e914be4 Fix: DB image cleanup 2024-09-04 14:59:44 +02:00
ayntk-ai
bec974dca4 Fix application image cleanup 2024-09-04 14:54:43 +02:00
ayntk-ai
b314b08f25 Feat: stop service confirm 2024-09-04 14:34:46 +02:00
ayntk-ai
9a2d5be354 Feat: confirm file storage 2024-09-03 18:31:06 +02:00
ayntk-ai
d94e39ccc7 Feat: service confirmation 2024-09-03 16:59:51 +02:00
ayntk-ai
d5b7e9ed83 Feat: preview deployments and typos 2024-09-03 16:43:50 +02:00
ayntk-ai
f29bc52fa5 Feat: general confirm 2024-09-03 15:59:30 +02:00
ayntk-ai
3d21f1a2a4 fix checkbox hide label 2024-09-03 14:59:01 +02:00
ayntk-ai
20558d438a remove ray 2024-09-03 00:30:15 +02:00
ayntk-ai
792f6bc163 made wording more clear 2024-09-03 00:01:24 +02:00
ayntk-ai
3d1c730703 Feat: del init script 2024-09-02 23:58:48 +02:00
ayntk-ai
c3188958b4 Feat: DB start, stop confirm 2024-09-02 23:54:16 +02:00
ayntk-ai
c24fec9c45 fix step and password error 2024-09-02 23:48:05 +02:00
ayntk-ai
70043c24cf 100000000x Speed improvement first toast then submit in the background 2024-09-02 23:30:47 +02:00
ayntk-ai
ff1e08cf8b add toast dispatching 2024-09-02 23:25:18 +02:00
ayntk-ai
a4d1ae1341 Feat: ability to hide labels 2024-09-02 21:54:21 +02:00
ayntk-ai
5944ee5524 fix close modal on submit 2024-09-02 21:22:31 +02:00
ayntk-ai
1b0c5f8d69 css fix for long text 2024-09-02 19:49:57 +02:00
ayntk-ai
dfd218ec06 fixes here and there 2024-09-02 19:27:21 +02:00
Leonardo Cabeza
7645047d75 Merge branch 'next' into next 2024-09-01 11:31:27 -05:00
ayntk-ai
843e3fb599 fix checkboxes in danger 2024-08-31 21:10:44 +02:00
ayntk-ai
a97ccd206c confirm init script 2024-08-31 20:55:56 +02:00
ayntk-ai
776d41613d Fix 3 risk levels 2024-08-31 20:55:43 +02:00
ayntk-ai
f857bbc437 fix submit action 2024-08-31 19:31:23 +02:00
ayntk-ai
f8226cf892 confirm backup deletion 2024-08-31 19:31:01 +02:00
ayntk-ai
d2a0621f93 delete backup folder if empty 2024-08-31 18:38:57 +02:00
ayntk-ai
38845d7eb0 confirm backup and delete all backups of the job 2024-08-31 18:29:19 +02:00
christiankolbow
82f96fe677 add proxy-headers 2024-08-31 17:04:42 +02:00
ayntk-ai
3b3bc6c33b fix default checkbox state false or true 2024-08-31 16:19:39 +02:00
ayntk-ai
2adac01034 confirm danger 2024-08-31 16:12:08 +02:00
ayntk-ai
b656cabb33 delete project confirmation 2024-08-31 15:07:50 +02:00
ayntk-ai
830c047ccf delete environment confirmation 2024-08-31 15:06:07 +02:00
ayntk-ai
a3dd48de1d destination and dashboard confirmation 2024-08-31 14:51:59 +02:00
ayntk-ai
b118a627d0 fix password validation and password error 2024-08-31 13:41:08 +02:00
ayntk-ai
bcfca40f3a rest checkboxes on close 2024-08-31 13:10:45 +02:00
ayntk-ai
76cb473db8 fix default checkbox state 2024-08-31 13:06:55 +02:00
ayntk-ai
73dfdb83a7 fix rendering bug, more props 2024-08-31 12:55:17 +02:00
christiankolbow
48e4ebdb5d remove unnecessary variables 2024-08-31 11:08:47 +02:00
christiankolbow
028c41b011 fix: postgres healthcheck 2024-08-31 09:32:28 +02:00
christiankolbow
d53c1f99d6 fix: remove env, change timezone 2024-08-31 08:34:10 +02:00
ayntk-ai
bff6964d4a scheduled task deletion 2024-08-30 20:33:12 +02:00
ayntk-ai
b807601d19 delete user confirm 2024-08-30 20:08:14 +02:00
ayntk-ai
9136d7acdc WIP more delete confirmations 2024-08-30 20:00:04 +02:00
ayntk-ai
da0398f35d remove unused code 2024-08-29 21:47:17 +02:00
ayntk-ai
6820fcc084 Fix name for services and DBs 2024-08-29 18:53:49 +02:00
ayntk-ai
d984bec175 Ajust text 2024-08-29 18:08:58 +02:00
ayntk-ai
182af1ec18 Fix application delete 2024-08-29 18:01:16 +02:00
ayntk-ai
a22e757ab7 fix execute action on the last button 2024-08-29 15:19:19 +02:00
ayntk-ai
354c74e920 fix 2024-08-29 12:37:19 +02:00
Amit Yadav
918d8e783a added libreoffice 2024-08-29 09:40:50 +05:30
ayntk-ai
62b7900855 more props and more fixes 2024-08-28 15:40:39 +02:00
ayntk-ai
c92659994d improve button text 2024-08-28 13:41:24 +02:00
ayntk-ai
a2651ab3c1 reset modal on cancel, fix back button when there is no prior step 2024-08-28 13:38:37 +02:00
ayntk-ai
1b51d46b3d more props, nav button fixes 2024-08-28 13:31:09 +02:00
ayntk-ai
141752b9ad fix checkbox actions default display 2024-08-28 13:11:42 +02:00
ayntk-ai
73068aaa75 Refactor: Integrate tow step process in the modal component WIP 2024-08-28 12:52:04 +02:00
ayntk-ai
8d2a02dc3c change title of confirmation popup 2024-08-27 14:29:47 +02:00
ayntk-ai
4726676248 make things more clear 2024-08-27 14:19:37 +02:00
ayntk-ai
9040f5d2a1 confirm with password 2024-08-27 13:44:12 +02:00
Amit Yadav
464769881f audiobookshelf added 2024-08-27 16:48:30 +05:30
ayntk-ai
ac50d8b4d8 fix styling 2024-08-27 12:57:03 +02:00
ayntk-ai
2a581147aa new confirm delete dialog 2024-08-27 12:43:59 +02:00
Franck Kerbiriou
7e154ba3c3 Add Mailpit template 2024-08-22 22:52:20 +02:00
Franck Kerbiriou
c1eb01fac5 Upgrade Authentik 2024-08-22 22:21:13 +02:00
ayntk-ai
1e46b63041 Feat getoutline service 2024-08-20 18:32:35 +02:00
sroepges
0f7a199d81 Merge branch 'next' into main 2024-08-18 10:52:09 +02:00
christiankolbow
2f356b16b6 feat: add onedev template 2024-08-17 16:41:21 +02:00
christiankolbow
a6f457f2f3 feat: add keycloak template 2024-08-17 16:36:52 +02:00
Alex Renoki
71bb1f5e17 Added litellm 2024-08-17 06:20:42 +03:00
Alex Renoki
ce926afdaa Bump to v2 2024-08-17 06:16:38 +03:00
sroepges
b8cdb40ce5 chore: add mattermost svg to compose 2024-08-16 15:26:42 +02:00
sroepges
7f59efd27c chore: add mattermost logo as svg 2024-08-16 15:23:54 +02:00
sroepges
4f23d3880d fix: update mattermost image tag and add default port 2024-08-16 13:42:42 +02:00
Leonardo Cabeza
b4175658e9 Merge branch 'next' into next 2024-08-12 09:44:04 -05:00
Telokis
f080a4bf30 Improve healthcheck 2024-08-11 15:11:52 +02:00
ayntk-ai
5b54dc8792 Revert "improve CleanupDocker.php"
This reverts commit b5360e5e75.
2024-08-09 23:25:57 +02:00
ayntk-ai
b5360e5e75 improve CleanupDocker.php 2024-08-09 23:12:30 +02:00
ayntk-ai
840e225aa8 formatting and waring text 2024-08-09 22:43:18 +02:00
ayntk-ai
7d1179e7c8 fix cleanup images for databases 2024-08-09 22:25:39 +02:00
ayntk-ai
16a5c601e3 graceful db stop and db deletion fixes 2024-08-09 22:15:45 +02:00
ayntk-ai
c566152f37 improve graceful_shutdown_container 2024-08-09 20:28:57 +02:00
ayntk-ai
2ca6ffb84e fix public function service.php 2024-08-09 19:57:53 +02:00
ayntk-ai
41be1f7666 use public function 2024-08-09 19:39:43 +02:00
ayntk-ai
450351921e added public functions 2024-08-09 19:39:21 +02:00
ayntk-ai
a4bb87d13b simplify DeleteService.php 2024-08-09 19:22:14 +02:00
ayntk-ai
1cfddfd529 fix stop large amount of containers 2024-08-09 19:17:58 +02:00
ayntk-ai
72bcf03cbb graceful service container stop 2024-08-09 18:59:41 +02:00
ayntk-ai
d177e49e62 updated warning message and formating 2024-08-09 18:43:13 +02:00
ayntk-ai
5595853379 WIP database network, image removal 2024-08-09 03:03:40 +02:00
ayntk-ai
97c2bedda2 add delete_connected_networks function to services.php 2024-08-09 03:00:30 +02:00
ayntk-ai
d980c7a425 only run network removal on stop service if it is not a deletion operation 2024-08-09 02:59:41 +02:00
ayntk-ai
53dff4ca4f simplify uuid variabel 2024-08-09 02:58:59 +02:00
ayntk-ai
7722809c52 typo 2024-08-09 02:20:32 +02:00
ayntk-ai
e67e03f73f added comments and removed temp ones 2024-08-09 02:15:40 +02:00
ayntk-ai
86a087056e fix volume deletion for services 2024-08-09 02:11:42 +02:00
ayntk-ai
51071da700 fix order 2024-08-09 01:00:07 +02:00
Leonardo Cabeza
58dcf769e4 Merge branch 'next' into next 2024-08-08 17:45:36 -05:00
ayntk-ai
70aa05bde9 order in importance 2024-08-09 00:42:23 +02:00
ayntk-ai
0135e2b5c0 add logic 2024-08-09 00:30:11 +02:00
nekomi2
319457020c add joplin compose 2024-08-08 21:35:00 +07:00
ayntk-ai
2f95349888 Merge branch 'coollabsio:main' into fix-#2546-deletion-issues 2024-08-08 15:40:13 +02:00
ayntk-ai
4d0acee95c UI options for deletion WIP 2024-08-08 12:31:37 +02:00
ayntk-ai
74bea37b43 Merge branch 'coollabsio:main' into fix-#2546-deletion-issues 2024-08-08 11:50:57 +02:00
Leonardo Cabeza
20acb4363b Merge branch 'next' into next 2024-08-07 18:20:30 -05:00
ayntk-ai
070daee28e remove networks and cleanup unused images when stoping dockercompose build pack containers 2024-08-08 01:19:17 +02:00
ayntk-ai
df796dffa2 fix delte networks and unused images of services when deleted 2024-08-08 01:02:48 +02:00
Telokis
a219942987 Fix icon path 2024-08-06 21:44:34 +02:00
Telokis
22a58eea8f Add Organizr 2024-08-06 21:44:11 +02:00
Leonardo Cabeza
868bbaf5a9 Merge branch 'next' into next 2024-08-01 08:46:02 -05:00
Alex Renoki
18ae29ba99 Added langfuse 2024-07-31 07:11:08 +03:00
Juan Felipe
380f2f4029 feat: Add Mautic 4 and 5 to service templates 2024-07-30 18:17:38 -03:00
Leonardo Cabeza
c3e80fbc18 Merge branch 'next' into next 2024-07-26 18:58:08 -05:00
johann
6b178f8b2e added bookstack template 2024-07-24 16:09:50 +02:00
Leonardo Cabeza
e7d45ac876 Merge branch 'next' into next 2024-07-22 16:58:52 -05:00
Luan Estradioto
1138ec0dea add storage:link to dev environment 2024-07-21 13:30:28 -03:00
Leonardo Cabeza
48886cac84 feat: add easyappointments service template 2024-07-19 16:07:53 -05:00
Italo
633c8253ec Adds missing forgejo logo svg 2024-07-17 14:53:45 -04:00
Italo
fa253b981a Merge branch 'coollabsio:main' into feat-forgejo 2024-07-17 14:53:13 -04:00
Italo
45b4d1b25b Merge branch 'next' into feat-forgejo 2024-07-17 13:31:23 -04:00
Italo
e1a585c194 Healtcheck done to the process name 2024-07-13 23:32:34 -04:00
Italo
631b7096c9 Merge branch 'coollabsio:main' into feat-forgejo 2024-07-13 23:27:15 -04:00
Italo
cdb146f03c Merge branch 'next' into feat-forgejo 2024-07-10 13:12:00 -04:00
Italo
ea81d8ecfe Adds external migration configuration 2024-07-04 19:46:35 -04:00
Italo
793f91e7fb Adds healthcheck for runner 2024-06-29 23:42:26 -04:00
Italo
807304c50b Adds healthcheck for DID 2024-06-29 23:29:46 -04:00
Italo
204aaf5dfa Fixes file name 2024-06-29 23:13:45 -04:00
Italo
c1612a9e88 Fixes environment variables 2024-06-29 23:12:43 -04:00
Italo
3847076392 feat: adds forgejo service with runners 2024-06-29 18:04:15 -04:00
Márton Szücs
ac887eefd6 Adding service for paperless
fulfills: https://github.com/coollabsio/coolify/discussions/2349

This service is using a workaround for the issue mentioned here: https://github.com/coollabsio/coolify/discussions/2572
2024-06-29 18:52:06 +02:00
Anthony Ly
2637afcfb2 add service template for azimutt 2024-06-26 14:26:31 +02:00
marcomaiermm
7d1f760c53 add optional api key 2024-06-08 11:23:11 +02:00
marcomaiermm
5262676596 feat: Add Supertokens template 2024-06-08 11:14:27 +02:00
Alejandro Akbal
21fc8efb86 feat: add Mixpost template 2024-06-04 13:13:37 +01:00
430 changed files with 12119 additions and 4360 deletions

View File

@@ -22,3 +22,4 @@ yarn-error.log
/_data
.rnd
/.ssh
.ignition.json

View File

@@ -6,7 +6,7 @@ APP_KEY=
APP_URL=http://localhost
APP_PORT=8000
APP_DEBUG=true
SSH_MUX_ENABLED=false
SSH_MUX_ENABLED=true
# PostgreSQL Database Configuration
DB_DATABASE=coolify
@@ -19,11 +19,7 @@ DB_PORT=5432
# Set to true to enable Ray
RAY_ENABLED=false
# Set custom ray port
RAY_PORT=
# Clockwork Configuration
CLOCKWORK_ENABLED=false
CLOCKWORK_QUEUE_COLLECT=true
# RAY_PORT=
# Enable Laravel Telescope for debugging
TELESCOPE_ENABLED=false

View File

@@ -1 +1,13 @@
> Always use `next` branch as destination branch for PRs, not `main`
## Submit Checklist (REMOVE THIS SECTION BEFORE SUBMITTING)
- [ ] I have selected the `next` branch as the destination for my PR, not `main`.
- [ ] I have listed all changes in the `Changes` section.
- [ ] I have filled out the `Issues` section with the issue/discussion link(s) (if applicable).
- [ ] I have tested my changes.
- [ ] I have considered backwards compatibility.
- [ ] I have removed this checklist and any unused sections.
## Changes
-
## Issues
- fix #

View File

@@ -98,3 +98,4 @@ jobs:
if: always()
with:
webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }}

View File

@@ -1,17 +1,18 @@
name: PR Build (v4)
name: Coolify Realtime Development (v4)
on:
pull_request:
types:
- opened
branches-ignore: ["main", "v3"]
paths-ignore:
- .github/workflows/coolify-helper.yml
- docker/coolify-helper/Dockerfile
push:
branches: [ "next" ]
paths:
- .github/workflows/coolify-realtime.yml
- docker/coolify-realtime/Dockerfile
- docker/coolify-realtime/terminal-server.js
- docker/coolify-realtime/package.json
- docker/coolify-realtime/soketi-entrypoint.sh
env:
REGISTRY: ghcr.io
IMAGE_NAME: "coollabsio/coolify"
IMAGE_NAME: "coollabsio/coolify-realtime"
jobs:
amd64:
@@ -19,9 +20,6 @@ jobs:
permissions:
contents: read
packages: write
attestations: write
id-token: write
actions: write
steps:
- uses: actions/checkout@v4
- name: Login to ghcr.io
@@ -30,22 +28,26 @@ jobs:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Get Version
id: version
run: |
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
- name: Build image and push to registry
uses: docker/build-push-action@v5
with:
no-cache: true
context: .
file: docker/prod/Dockerfile
file: docker/coolify-realtime/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.number }}
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
labels: |
coolify.managed=true
aarch64:
runs-on: [self-hosted, arm64]
runs-on: [ self-hosted, arm64 ]
permissions:
contents: read
packages: write
attestations: write
id-token: write
actions: write
steps:
- uses: actions/checkout@v4
- name: Login to ghcr.io
@@ -54,23 +56,27 @@ jobs:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Get Version
id: version
run: |
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
- name: Build image and push to registry
uses: docker/build-push-action@v5
with:
no-cache: true
context: .
file: docker/prod/Dockerfile
file: docker/coolify-realtime/Dockerfile
platforms: linux/aarch64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.number }}-aarch64
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
labels: |
coolify.managed=true
merge-manifest:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
actions: write
needs: [amd64, aarch64]
needs: [ amd64, aarch64 ]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -84,10 +90,14 @@ jobs:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Get Version
id: version
run: |
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
- 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 }}
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
- uses: sarisia/actions-status-discord@v1
if: always()
with:
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}
webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }}

View File

@@ -2,7 +2,7 @@ name: Coolify Realtime (v4)
on:
push:
branches: [ "main", "next" ]
branches: [ "main" ]
paths:
- .github/workflows/coolify-realtime.yml
- docker/coolify-realtime/Dockerfile

View File

@@ -1,44 +0,0 @@
name: Docker Image CI
on:
# push:
# branches: [ "main" ]
# pull_request:
# branches: [ "*" ]
push:
branches: ["this-does-not-exist"]
pull_request:
branches: ["this-does-not-exist"]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: |
/usr/local/share/ca-certificates
/var/cache/apt/archives
/var/lib/apt/lists
~/.cache
key: ${{ runner.os }}-docker-${{ hashFiles('**/Dockerfile') }}
restore-keys: |
${{ runner.os }}-docker-
- name: Build the Docker image
run: |
cp .env.example .env
docker run --rm -u "$(id -u):$(id -g)" \
-v "$(pwd):/app" \
-w /app composer:2 \
composer install --ignore-platform-reqs
./vendor/bin/spin build
- name: Start the stack
run: |
./vendor/bin/spin up -d
./vendor/bin/spin exec coolify php artisan key:generate
./vendor/bin/spin exec coolify php artisan migrate:fresh --seed
- name: Test (missing E2E tests)
run: |
./vendor/bin/spin exec coolify php artisan test

View File

@@ -1,25 +0,0 @@
name: Fix PHP code style issues
on: [push]
permissions:
contents: write
jobs:
php-code-styling:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: Fix PHP code style issues
uses: aglipanci/laravel-pint-action@2.4
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Fix styling

View File

@@ -0,0 +1,17 @@
name: Lock closed Issues, Discussions, and PRs
on:
schedule:
- cron: '0 1 * * *'
jobs:
lock-threads:
runs-on: ubuntu-latest
steps:
- name: Lock threads after 30 days of inactivity
uses: dessant/lock-threads@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
issue-inactive-days: '30'
pr-inactive-days: '30'
discussion-inactive-days: '30'

View File

@@ -0,0 +1,28 @@
name: Manage Stale Issues and PRs
on:
schedule:
- cron: '0 2 * * *'
jobs:
manage-stale:
runs-on: ubuntu-latest
steps:
- name: Manage stale issues and PRs
uses: actions/stale@v9
id: stale
with:
stale-issue-message: 'This issue will be automatically closed in a few days if no response is received. Please provide an update with the requested information.'
stale-pr-message: 'This pull request will be automatically closed in a few days if no response is received. Please update your PR or comment if you would like to continue working on it.'
close-issue-message: 'This issue has been automatically closed due to inactivity.'
close-pr-message: 'This pull request has been automatically closed due to inactivity.'
days-before-stale: 14
days-before-close: 7
stale-issue-label: '⏱︎ Stale'
stale-pr-label: '⏱︎ Stale'
only-labels: '💤 Waiting for feedback'
remove-stale-when-updated: true
operations-per-run: 100
labels-to-remove-when-unstale: '⏱︎ Stale, 💤 Waiting for feedback'
close-issue-reason: 'not_planned'
exempt-all-milestones: false

View File

@@ -18,7 +18,7 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;
async function processIssue(issueNumber) {
try {
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
@@ -65,11 +65,14 @@ jobs:
}
if (context.eventName === 'pull_request' || context.eventName === 'pull_request_target') {
const { data: closedIssues } = await github.rest.search.issuesAndPullRequests({
q: `repo:${owner}/${repo} is:issue is:closed linked:${context.payload.pull_request.number}`,
per_page: 100
});
for (const issue of closedIssues.items) {
await processIssue(issue.number);
const pr = context.payload.pull_request;
if (pr.body) {
const issueReferences = pr.body.match(/#(\d+)/g);
if (issueReferences) {
for (const reference of issueReferences) {
const issueNumber = parseInt(reference.substring(1));
await processIssue(issueNumber);
}
}
}
}

1
.gitignore vendored
View File

@@ -32,3 +32,4 @@ _ide_helper_models.php
.rnd
/.ssh
scripts/load-test/*
.ignition.json

View File

@@ -12,9 +12,10 @@ You can ask for guidance anytime on our [Discord server](https://coollabs.io/dis
4. [Set up Environment Variables](#4-set-up-environment-variables)
5. [Start Coolify](#5-start-coolify)
6. [Start Development](#6-start-development)
7. [Development Notes](#7-development-notes)
8. [Create a Pull Request](#8-create-a-pull-request)
9. [Additional Contribution Guidelines](#additional-contribution-guidelines)
7. [Create a Pull Request](#7-create-a-pull-request)
8. [Development Notes](#development-notes)
9. [Resetting Development Environment](#resetting-development-environment)
10. [Additional Contribution Guidelines](#additional-contribution-guidelines)
## 1. Setup Development Environment
@@ -25,15 +26,15 @@ Follow the steps below for your operating system:
1. Install `docker-ce`, Docker Desktop (or similar):
- Docker CE (recommended):
- Install Windows Subsystem for Linux v2 (WSL2) by following this guide: [Install WSL](https://learn.microsoft.com/en-us/windows/wsl/install)
- After installing WSL2, install Docker CE for your Linux distribution by following this guide: [Install Docker Engine](https://docs.docker.com/engine/install/)
- Install Windows Subsystem for Linux v2 (WSL2) by following this guide: [Install WSL](https://learn.microsoft.com/en-us/windows/wsl/install?ref=coolify)
- After installing WSL2, install Docker CE for your Linux distribution by following this guide: [Install Docker Engine](https://docs.docker.com/engine/install/?ref=coolify)
- Make sure to choose the appropriate Linux distribution (e.g., Ubuntu) when following the Docker installation guide
- Install Docker Desktop (easier):
- Download and install [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/)
- Download and install [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/?ref=coolify)
- Ensure WSL2 backend is enabled in Docker Desktop settings
2. Install Spin:
- Follow the instructions to install Spin on Windows from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-windows#download-and-install-spin-into-wsl2)
- Follow the instructions to install Spin on Windows from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-windows#download-and-install-spin-into-wsl2?ref=coolify)
</details>
@@ -42,12 +43,12 @@ Follow the steps below for your operating system:
1. Install Orbstack, Docker Desktop (or similar):
- Orbstack (recommended, as it is a faster and lighter alternative to Docker Desktop):
- Download and install [Orbstack](https://docs.orbstack.dev/quick-start#installation)
- Download and install [Orbstack](https://docs.orbstack.dev/quick-start#installation?ref=coolify)
- Docker Desktop:
- Download and install [Docker Desktop for Mac](https://docs.docker.com/desktop/install/mac-install/)
- Download and install [Docker Desktop for Mac](https://docs.docker.com/desktop/install/mac-install/?ref=coolify)
2. Install Spin:
- Follow the instructions to install Spin on MacOS from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-macos/#download-and-install-spin)
- Follow the instructions to install Spin on MacOS from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-macos/#download-and-install-spin?ref=coolify)
</details>
@@ -56,12 +57,12 @@ Follow the steps below for your operating system:
1. Install Docker Engine, Docker Desktop (or similar):
- Docker Engine (recommended, as there is no VM overhead):
- Follow the official [Docker Engine installation guide](https://docs.docker.com/engine/install/) for your Linux distribution
- Follow the official [Docker Engine installation guide](https://docs.docker.com/engine/install/?ref=coolify) for your Linux distribution
- Docker Desktop:
- If you want a GUI, you can use [Docker Desktop for Linux](https://docs.docker.com/desktop/install/linux-install/)
- If you want a GUI, you can use [Docker Desktop for Linux](https://docs.docker.com/desktop/install/linux-install/?ref=coolify)
2. Install Spin:
- Follow the instructions to install Spin on Linux from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-linux#configure-docker-permissions)
- Follow the instructions to install Spin on Linux from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-linux#configure-docker-permissions?ref=coolify)
</details>
@@ -85,14 +86,14 @@ After installing Docker (or Orbstack) and Spin, verify the installation:
| Editor | Platform | Download Link |
|--------|----------|---------------|
| Visual Studio Code (recommended free) | Windows/macOS/Linux | [Download](https://code.visualstudio.com/download) |
| Cursor (recommended but paid) | Windows/macOS/Linux | [Download](https://www.cursor.com/) |
| Zed (very fast) | macOS/Linux | [Download](https://zed.dev/download) |
| Visual Studio Code (recommended free) | Windows/macOS/Linux | [Download](https://code.visualstudio.com/download?ref=coolify) |
| Cursor (recommended but paid) | Windows/macOS/Linux | [Download](https://www.cursor.com/?ref=coolify) |
| Zed (very fast) | macOS/Linux | [Download](https://zed.dev/download?ref=coolify) |
3. Clone the Coolify Repository from your fork to your local machine
- Use `git clone` in the command line, or
- Use GitHub Desktop (recommended):
- Download and install from [https://desktop.github.com/](https://desktop.github.com/)
- Download and install from [https://desktop.github.com/](https://desktop.github.com/?ref=coolify)
- Open GitHub Desktop and login with your GitHub account
- Click on `File` -> `Clone Repository` select `github.com` as the repository location, then select your forked Coolify repository, choose the local path and then click `Clone`
@@ -145,7 +146,36 @@ After installing Docker (or Orbstack) and Spin, verify the installation:
> TELESCOPE_ENABLED=true
> ```
## 7. Development Notes
## 7. Create a Pull Request
1. After making changes or adding a new service:
- Commit your changes to your forked repository.
- Push the changes to your GitHub account.
2. Creating the Pull Request (PR):
- Navigate to the main Coolify repository on GitHub.
- Click the "Pull requests" tab.
- Click the green "New pull request" button.
- Choose your fork and branch as the compare branch.
- Click "Create pull request".
3. Filling out the PR details:
- Give your PR a descriptive title.
- Use the Pull Request Template provided and fill in the details.
> [!IMPORTANT]
> Always set the base branch for your PR to the `next` branch of the Coolify repository, not the `main` branch.
4. Submit your PR:
- Review your changes one last time.
- Click "Create pull request" to submit.
> [!NOTE]
> Make sure your PR is out of draft mode as soon as it's ready for review. PRs that are in draft mode for a long time may be closed by maintainers.
After submission, maintainers will review your PR and may request changes or provide feedback.
## Development Notes
When working on Coolify, keep the following in mind:
@@ -164,35 +194,41 @@ When working on Coolify, keep the following in mind:
> [!IMPORTANT]
> Forgetting to migrate the database can cause problems, so make it a habit to run migrations after pulling changes or switching branches.
## 8. Create a Pull Request
## Resetting Development Environment
1. After making changes or adding a new service:
- Commit your changes to your forked repository.
- Push the changes to your GitHub account.
If you encounter issues or break your database or something else, follow these steps to start from a clean slate (works since `v4.0.0-beta.342`):
2. Creating the Pull Request (PR):
- Navigate to the main Coolify repository on GitHub.
- Click the "Pull requests" tab.
- Click the green "New pull request" button.
- Choose your fork and branch as the compare branch.
- Click "Create pull request".
1. Stop all running containers `ctrl + c`.
3. Filling out the PR details:
- Give your PR a descriptive title.
- In the description, explain the changes you've made.
- Reference any related issues by using keywords like "Fixes #123" or "Closes #456".
2. Remove all Coolify containers:
```bash
docker rm coolify coolify-db coolify-redis coolify-realtime coolify-testing-host coolify-minio coolify-vite-1 coolify-mail
```
3. Remove Coolify volumes (it is possible that the volumes have no `coolify` prefix on your machine, in that case remove the prefix from the command):
```bash
docker volume rm coolify_dev_backups_data coolify_dev_postgres_data coolify_dev_redis_data coolify_dev_coolify_data coolify_dev_minio_data
```
4. Remove unused images:
```bash
docker image prune -a
```
5. Start Coolify again:
```bash
spin up
```
6. Run database migrations and seeders:
```bash
docker exec -it coolify php artisan migrate:fresh --seed
```
After completing these steps, you'll have a fresh development setup.
> [!IMPORTANT]
> Always set the base branch for your PR to the `next` branch of the Coolify repository, not the `main` branch.
4. Submit your PR:
- Review your changes one last time.
- Click "Create pull request" to submit.
> [!NOTE]
> Make sure your PR is out of draft mode as soon as it's ready for review. PRs that are in draft mode for a long time may be closed by maintainers.
After submission, maintainers will review your PR and may request changes or provide feedback.
> Always run database migrations and seeders after switching branches or pulling updates to ensure your local database structure matches the current codebase and includes necessary seed data.
## Additional Contribution Guidelines

View File

@@ -2,35 +2,120 @@
This guide outlines the release process for Coolify, intended for developers and those interested in understanding how releases are managed and deployed.
## Table of Contents
- [Release Process](#release-process)
- [Version Types](#version-types)
- [Stable](#stable)
- [Nightly](#nightly)
- [Beta](#beta)
- [Version Availability](#version-availability)
- [Self-Hosted](#self-hosted)
- [Cloud](#cloud)
- [Manually Update to Specific Versions](#manually-update-to-specific-versions)
## Release Process
1. **Development on `next` or separate branches**
- Changes, fixes and new features are developed on the `next` or even separate branches.
1. **Development on `next` or Feature Branches**
- Improvements, fixes, and new features are developed on the `next` branch or separate feature branches.
2. **Merging to `main`**
- Once changes are ready, they are merged from `next` into the `main` branch.
- Once ready, changes are merged from the `next` branch into the `main` branch.
3. **Building the release**
- After merging to `main`, a new release is built.
- Note: A push to `main` does not automatically mean a new version is released.
3. **Building the Release**
- After merging to `main`, GitHub Actions automatically builds release images for all architectures and pushes them to the GitHub Container Registry with the version tag and the `latest` tag.
4. **Creating a GitHub release**
- A new release is created on GitHub with the new version details.
4. **Creating a GitHub Release**
- A new GitHub release is manually created with details of the changes made in the version.
5. **Updating the CDN**
- The final step is updating the version information on the CDN:
[https://cdn.coollabs.io/coolify/versions.json](https://cdn.coollabs.io/coolify/versions.json)
- To make a new version publicly available, the version information on the CDN needs to be updated: [https://cdn.coollabs.io/coolify/versions.json](https://cdn.coollabs.io/coolify/versions.json)
> [!NOTE]
> The CDN update may not occur immediately after the GitHub release. It can happen hours or even days later due to additional testing, stability checks, or potential hotfixes.
> The CDN update may not occur immediately after the GitHub release. It can take hours or even days due to additional testing, stability checks, or potential hotfixes. **The update becomes available only after the CDN is updated.**
## Version Types
<details>
<summary><strong>Stable (coming soon)</strong></summary>
- **Stable**
- The production version suitable for stable, production environments (generally recommended).
- **Update Frequency:** Every 2 to 4 weeks, with more frequent possible hotfixes.
- **Release Size:** Larger but less frequent releases. Multiple nightly versions are consolidated into a single stable release.
- **Versioning Scheme:** Follows semantic versioning (e.g., `v4.0.0`).
- **Installation Command:**
```bash
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
```
</details>
<details>
<summary><strong>Nightly</strong></summary>
- **Nightly**
- The latest development version, suitable for testing the latest changes and experimenting with new features.
- **Update Frequency:** Daily or bi-weekly updates.
- **Release Size:** Smaller, more frequent releases.
- **Versioning Scheme:** TO BE DETERMINED
- **Installation Command:**
```bash
curl -fsSL https://cdn.coollabs.io/coolify-nightly/install.sh | bash -s next
```
</details>
<details>
<summary><strong>Beta</strong></summary>
- **Beta**
- Test releases for the upcoming stable version.
- **Purpose:** Allows users to test and provide feedback on new features and changes before they become stable.
- **Update Frequency:** Available if we think beta testing is necessary.
- **Release Size:** Same size as stable release as it will become the next stabe release after some time.
- **Versioning Scheme:** Follows semantic versioning (e.g., `4.1.0-beta.1`).
- **Installation Command:**
```bash
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
```
</details>
> [!WARNING]
> Do not use nightly/beta builds in production as there is no guarantee of stability.
## Version Availability
It's important to understand that a new version released on GitHub may not immediately become available for users to update (through manual or auto-update).
When a new version is released and a new GitHub release is created, it doesn't immediately become available for your instance. Here's how version availability works for different instance types.
### Self-Hosted
- **Update Frequency:** More frequent updates, especially on the nightly release channel.
- **Update Availability:** New versions are available once the CDN has been updated.
- **Update Methods:**
1. **Manual Update in Instance Settings:**
- Go to `Settings > Update Check Frequency` and click the `Check Manually` button.
- If an update is available, an upgrade button will appear on the sidebar.
2. **Automatic Update:**
- If enabled, the instance will update automatically at the time set in the settings.
3. **Re-run Installation Script:**
- Run the installation script again to upgrade to the latest version available on the CDN:
```bash
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
```
> [!IMPORTANT]
> If you see a new release on GitHub but haven't received the update, it's likely because the CDN hasn't been updated yet. This is intentional and ensures stability and allows for hotfixes before the new version is officially released.
> If a new release is available on GitHub but your instance hasn't updated yet or no upgrade button is shown in the UI, the CDN might not have been updated yet. This intentional delay ensures stability and allows for hotfixes before official release.
### Cloud
- **Update Frequency:** Less frequent as it's a managed service.
- **Update Availability:** New versions are available once Andras has updated the cloud version manually.
- **Update Method:**
- Updates are managed by Andras, who ensures each cloud version is thoroughly tested and stable before releasing it.
> [!IMPORTANT]
> The cloud version of Coolify may be several versions behind the latest GitHub releases even if the CDN is updated. This is intentional to ensure stability and reliability for cloud users and Andras will manully update the cloud version when the update is ready.
## Manually Update to Specific Versions
@@ -42,4 +127,4 @@ To update your Coolify instance to a specific (unreleased) version, use the foll
```bash
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash -s <version>
```
-> Replace `<version>` with the version you want to update to (for example `4.0.0-beta.332`).
Replace `<version>` with the version you want to update to (for example `4.0.0-beta.332`).

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Actions\Application;
use App\Models\Application;
use Lorisleiva\Actions\Concerns\AsAction;
class GenerateConfig
{
use AsAction;
public function handle(Application $application, bool $is_json = false)
{
ray()->clearAll();
return $application->generateConfig(is_json: $is_json);
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Application;
use App\Actions\Server\CleanupDocker;
use App\Models\Application;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -9,44 +10,35 @@ class StopApplication
{
use AsAction;
public function handle(Application $application, bool $previewDeployments = false)
public function handle(Application $application, bool $previewDeployments = false, bool $dockerCleanup = true)
{
if ($application->destination->server->isSwarm()) {
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
return;
}
$servers = collect([]);
$servers->push($application->destination->server);
$application->additional_servers->map(function ($server) use ($servers) {
$servers->push($server);
});
foreach ($servers as $server) {
try {
$server = $application->destination->server;
if (! $server->isFunctional()) {
return 'Server is not functional';
}
if ($previewDeployments) {
$containers = getCurrentApplicationContainerStatus($server, $application->id, includePullrequests: true);
} else {
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
}
if ($containers->count() > 0) {
foreach ($containers as $container) {
$containerName = data_get($container, 'Names');
if ($containerName) {
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);
}
}
ray('Stopping application: '.$application->name);
if ($server->isSwarm()) {
instant_remote_process(["docker stack rm {$application->uuid}"], $server);
return;
}
$containersToStop = $application->getContainersToStop($previewDeployments);
$application->stopContainers($containersToStop, $server);
if ($application->build_pack === 'dockercompose') {
// remove network
$uuid = $application->uuid;
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
instant_remote_process(["docker network rm {$uuid}"], $server, false);
$application->delete_connected_networks($application->uuid);
}
if ($dockerCleanup) {
CleanupDocker::dispatch($server, true);
}
} catch (\Exception $e) {
ray($e->getMessage());
return $e->getMessage();
}
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Actions\CoolifyTask;
use App\Enums\ActivityTypes;
use App\Enums\ProcessStatus;
use App\Helpers\SshMultiplexingHelper;
use App\Jobs\ApplicationDeploymentJob;
use App\Models\Server;
use Illuminate\Process\ProcessResult;
@@ -137,7 +138,7 @@ class RunRemoteProcess
$command = $this->activity->getExtraProperty('command');
$server = Server::whereUuid($server_uuid)->firstOrFail();
return generateSshCommand($server, $command);
return SshMultiplexingHelper::generateSshCommand($server, $command);
}
protected function handleOutput(string $type, string $output)

View File

@@ -23,7 +23,7 @@ class StartDragonfly
$startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}";
$container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [
"echo 'Starting {$database->name}.'",
@@ -46,9 +46,6 @@ class StartDragonfly
'networks' => [
$this->database->destination->network,
],
'ulimits' => [
'memlock' => '-1',
],
'labels' => [
'coolify.managed' => 'true',
],
@@ -75,7 +72,7 @@ class StartDragonfly
],
],
];
if (!is_null($this->database->limits_cpuset)) {
if (! is_null($this->database->limits_cpuset)) {
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
}
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
@@ -118,10 +115,10 @@ class StartDragonfly
$local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
} else {
$volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
}
}
@@ -152,7 +149,7 @@ class StartDragonfly
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
$environment_variables->push("REDIS_PASSWORD={$this->database->dragonfly_password}");
}

View File

@@ -24,7 +24,7 @@ class StartKeydb
$startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes";
$container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [
"echo 'Starting {$database->name}.'",
@@ -74,7 +74,7 @@ class StartKeydb
],
],
];
if (!is_null($this->database->limits_cpuset)) {
if (! is_null($this->database->limits_cpuset)) {
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
}
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
@@ -94,10 +94,10 @@ class StartKeydb
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
if (!is_null($this->database->keydb_conf) || !empty($this->database->keydb_conf)) {
if (! is_null($this->database->keydb_conf) || ! empty($this->database->keydb_conf)) {
$docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/keydb.conf',
'source' => $this->configuration_dir.'/keydb.conf',
'target' => '/etc/keydb/keydb.conf',
'read_only' => true,
];
@@ -125,10 +125,10 @@ class StartKeydb
$local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
} else {
$volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
}
}
@@ -159,7 +159,7 @@ class StartKeydb
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
$environment_variables->push("REDIS_PASSWORD={$this->database->keydb_password}");
}

View File

@@ -21,7 +21,7 @@ class StartMariadb
$this->database = $database;
$container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [
"echo 'Starting {$database->name}.'",
@@ -69,7 +69,7 @@ class StartMariadb
],
],
];
if (!is_null($this->database->limits_cpuset)) {
if (! is_null($this->database->limits_cpuset)) {
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
}
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
@@ -89,10 +89,10 @@ class StartMariadb
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
if (!is_null($this->database->mariadb_conf) || !empty($this->database->mariadb_conf)) {
if (! is_null($this->database->mariadb_conf) || ! empty($this->database->mariadb_conf)) {
$docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/custom-config.cnf',
'source' => $this->configuration_dir.'/custom-config.cnf',
'target' => '/etc/mysql/conf.d/custom-config.cnf',
'read_only' => true,
];
@@ -120,10 +120,10 @@ class StartMariadb
$local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
} else {
$volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
}
}
@@ -154,18 +154,18 @@ class StartMariadb
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
$environment_variables->push("MARIADB_ROOT_PASSWORD={$this->database->mariadb_root_password}");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MARIADB_DATABASE'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_DATABASE'))->isEmpty()) {
$environment_variables->push("MARIADB_DATABASE={$this->database->mariadb_database}");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MARIADB_USER'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_USER'))->isEmpty()) {
$environment_variables->push("MARIADB_USER={$this->database->mariadb_user}");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
}

View File

@@ -23,7 +23,7 @@ class StartMongodb
$startCommand = 'mongod';
$container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [
"echo 'Starting {$database->name}.'",
@@ -77,7 +77,7 @@ class StartMongodb
],
],
];
if (!is_null($this->database->limits_cpuset)) {
if (! is_null($this->database->limits_cpuset)) {
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
}
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
@@ -97,19 +97,19 @@ class StartMongodb
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
if (!is_null($this->database->mongo_conf) || !empty($this->database->mongo_conf)) {
if (! is_null($this->database->mongo_conf) || ! empty($this->database->mongo_conf)) {
$docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/mongod.conf',
'source' => $this->configuration_dir.'/mongod.conf',
'target' => '/etc/mongo/mongod.conf',
'read_only' => true,
];
$docker_compose['services'][$container_name]['command'] = $startCommand . ' --config /etc/mongo/mongod.conf';
$docker_compose['services'][$container_name]['command'] = $startCommand.' --config /etc/mongo/mongod.conf';
}
$this->add_default_database();
$docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d',
'source' => $this->configuration_dir.'/docker-entrypoint-initdb.d',
'target' => '/docker-entrypoint-initdb.d',
'read_only' => true,
];
@@ -136,10 +136,10 @@ class StartMongodb
$local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
} else {
$volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
}
}
@@ -170,15 +170,15 @@ class StartMongodb
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
$environment_variables->push("MONGO_INITDB_ROOT_USERNAME={$this->database->mongo_initdb_root_username}");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MONGO_INITDB_ROOT_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_ROOT_PASSWORD'))->isEmpty()) {
$environment_variables->push("MONGO_INITDB_ROOT_PASSWORD={$this->database->mongo_initdb_root_password}");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
$environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}");
}

View File

@@ -21,7 +21,7 @@ class StartMysql
$this->database = $database;
$container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [
"echo 'Starting {$database->name}.'",
@@ -69,7 +69,7 @@ class StartMysql
],
],
];
if (!is_null($this->database->limits_cpuset)) {
if (! is_null($this->database->limits_cpuset)) {
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
}
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
@@ -89,10 +89,10 @@ class StartMysql
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
if (!is_null($this->database->mysql_conf) || !empty($this->database->mysql_conf)) {
if (! is_null($this->database->mysql_conf) || ! empty($this->database->mysql_conf)) {
$docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/custom-config.cnf',
'source' => $this->configuration_dir.'/custom-config.cnf',
'target' => '/etc/mysql/conf.d/custom-config.cnf',
'read_only' => true,
];
@@ -120,10 +120,10 @@ class StartMysql
$local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
} else {
$volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
}
}
@@ -154,18 +154,18 @@ class StartMysql
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
$environment_variables->push("MYSQL_ROOT_PASSWORD={$this->database->mysql_root_password}");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MYSQL_DATABASE'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_DATABASE'))->isEmpty()) {
$environment_variables->push("MYSQL_DATABASE={$this->database->mysql_database}");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MYSQL_USER'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_USER'))->isEmpty()) {
$environment_variables->push("MYSQL_USER={$this->database->mysql_user}");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
}

View File

@@ -37,7 +37,6 @@ class StartPostgresql
$this->generate_init_scripts();
$this->add_custom_conf();
$docker_compose = [
'services' => [
$container_name => [

View File

@@ -24,7 +24,7 @@ class StartRedis
$startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
$container_name = $this->database->uuid;
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
$this->commands = [
"echo 'Starting {$database->name}.'",
@@ -78,7 +78,7 @@ class StartRedis
],
],
];
if (!is_null($this->database->limits_cpuset)) {
if (! is_null($this->database->limits_cpuset)) {
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
}
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
@@ -98,10 +98,10 @@ class StartRedis
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;
}
if (!is_null($this->database->redis_conf) || !empty($this->database->redis_conf)) {
if (! is_null($this->database->redis_conf) || ! empty($this->database->redis_conf)) {
$docker_compose['services'][$container_name]['volumes'][] = [
'type' => 'bind',
'source' => $this->configuration_dir . '/redis.conf',
'source' => $this->configuration_dir.'/redis.conf',
'target' => '/usr/local/etc/redis/redis.conf',
'read_only' => true,
];
@@ -130,10 +130,10 @@ class StartRedis
$local_persistent_volumes = [];
foreach ($this->database->persistentStorages as $persistentStorage) {
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
$local_persistent_volumes[] = $persistentStorage->host_path . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
} else {
$volume_name = $persistentStorage->name;
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
}
}
@@ -164,7 +164,7 @@ class StartRedis
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
$environment_variables->push("REDIS_PASSWORD={$this->database->redis_password}");
}

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Database;
use App\Actions\Server\CleanupDocker;
use App\Models\StandaloneClickhouse;
use App\Models\StandaloneDragonfly;
use App\Models\StandaloneKeydb;
@@ -10,25 +11,65 @@ use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Illuminate\Support\Facades\Process;
use Lorisleiva\Actions\Concerns\AsAction;
class StopDatabase
{
use AsAction;
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database, bool $isDeleteOperation = false, bool $dockerCleanup = true)
{
$server = $database->destination->server;
if (! $server->isFunctional()) {
return 'Server is not functional';
}
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);
$this->stopContainer($database, $database->uuid, 300);
if (! $isDeleteOperation) {
if ($dockerCleanup) {
CleanupDocker::dispatch($server, true);
}
}
if ($database->is_public) {
StopDatabaseProxy::run($database);
}
return 'Database stopped successfully';
}
private function stopContainer($database, string $containerName, int $timeout = 300): void
{
$server = $database->destination->server;
$process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
$startTime = time();
while ($process->running()) {
if (time() - $startTime >= $timeout) {
$this->forceStopContainer($containerName, $server);
break;
}
usleep(100000);
}
$this->removeContainer($containerName, $server);
}
private function forceStopContainer(string $containerName, $server): void
{
instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false);
}
private function removeContainer(string $containerName, $server): void
{
instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false);
}
private function deleteConnectedNetworks($uuid, $server)
{
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
instant_remote_process(["docker network rm {$uuid}"], $server, false);
}
}

View File

@@ -543,7 +543,7 @@ class GetContainersStatus
}
}
}
$exitedServices = $exitedServices->unique('id');
$exitedServices = $exitedServices->unique('uuid');
foreach ($exitedServices as $exitedService) {
if (str($exitedService->status)->startsWith('exited')) {
continue;
@@ -651,8 +651,9 @@ class GetContainersStatus
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
}
// Check if proxy is running
$this->server->proxyType();
if (! $this->server->proxySet() || $this->server->proxy->force_stop) {
return;
}
$foundProxyContainer = $this->containers->filter(function ($value, $key) {
if ($this->server->isSwarm()) {
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';

View File

@@ -2,7 +2,6 @@
namespace App\Actions\Fortify;
use App\Models\InstanceSettings;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
@@ -20,7 +19,7 @@ class CreateNewUser implements CreatesNewUsers
*/
public function create(array $input): User
{
$settings = InstanceSettings::get();
$settings = instanceSettings();
if (! $settings->is_registration_enabled) {
abort(403);
}
@@ -48,7 +47,7 @@ class CreateNewUser implements CreatesNewUsers
$team = $user->teams()->first();
// Disable registration after first user is created
$settings = InstanceSettings::get();
$settings = instanceSettings();
$settings->is_registration_enabled = false;
$settings->save();
} else {

View File

@@ -2,7 +2,6 @@
namespace App\Actions\License;
use App\Models\InstanceSettings;
use Illuminate\Support\Facades\Http;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -13,7 +12,7 @@ class CheckResaleLicense
public function handle()
{
try {
$settings = InstanceSettings::get();
$settings = instanceSettings();
if (isDev()) {
$settings->update([
'is_resale_license_active' => true,

View File

@@ -22,7 +22,7 @@ class CheckConfiguration
];
$proxy_configuration = instant_remote_process($payload, $server, false);
if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) {
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value;
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value();
}
if (! $proxy_configuration || is_null($proxy_configuration)) {
throw new \Exception('Could not generate proxy configuration');

View File

@@ -2,14 +2,17 @@
namespace App\Actions\Proxy;
use App\Enums\ProxyTypes;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
class CheckProxy
{
use AsAction;
public function handle(Server $server, $fromUI = false)
// It should return if the proxy should be started (true) or not (false)
public function handle(Server $server, $fromUI = false): bool
{
if (! $server->isFunctional()) {
return false;
@@ -26,7 +29,7 @@ class CheckProxy
if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop) {
return false;
}
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
['uptime' => $uptime, 'error' => $error] = $server->validateConnection(false);
if (! $uptime) {
throw new \Exception($error);
}
@@ -62,22 +65,42 @@ class CheckProxy
$ip = 'host.docker.internal';
}
$connection80 = @fsockopen($ip, '80');
$connection443 = @fsockopen($ip, '443');
$port80 = is_resource($connection80) && fclose($connection80);
$port443 = is_resource($connection443) && fclose($connection443);
if ($port80) {
if ($fromUI) {
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
$portsToCheck = ['80', '443'];
try {
if ($server->proxyType() !== ProxyTypes::NONE->value) {
$proxyCompose = CheckConfiguration::run($server);
if (isset($proxyCompose)) {
$yaml = Yaml::parse($proxyCompose);
$portsToCheck = [];
if ($server->proxyType() === ProxyTypes::TRAEFIK->value) {
$ports = data_get($yaml, 'services.traefik.ports');
} elseif ($server->proxyType() === ProxyTypes::CADDY->value) {
$ports = data_get($yaml, 'services.caddy.ports');
}
if (isset($ports)) {
foreach ($ports as $port) {
$portsToCheck[] = str($port)->before(':')->value();
}
}
}
} else {
return false;
$portsToCheck = [];
}
} catch (\Exception $e) {
ray($e->getMessage());
}
if ($port443) {
if ($fromUI) {
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
} else {
return false;
if (count($portsToCheck) === 0) {
return false;
}
foreach ($portsToCheck as $port) {
$connection = @fsockopen($ip, $port);
if (is_resource($connection) && fclose($connection)) {
if ($fromUI) {
throw new \Exception("Port $port is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
} else {
return false;
}
}
}

View File

@@ -26,7 +26,7 @@ class StartProxy
}
SaveConfiguration::run($server, $configuration);
$docker_compose_yml_base64 = base64_encode($configuration);
$server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value;
$server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value();
$server->save();
if ($server->isSwarm()) {
$commands = $commands->merge([
@@ -35,7 +35,7 @@ class StartProxy
"echo 'Creating required Docker Compose file.'",
"echo 'Starting coolify-proxy.'",
'docker stack deploy -c docker-compose.yml coolify-proxy',
"echo 'Proxy started successfully.'",
"echo 'Successfully started coolify-proxy.'",
]);
} else {
$caddfile = 'import /dynamic/*.caddy';
@@ -46,11 +46,14 @@ class StartProxy
"echo 'Creating required Docker Compose file.'",
"echo 'Pulling docker image.'",
'docker compose pull',
"echo 'Stopping existing coolify-proxy.'",
'docker compose down -v --remove-orphans > /dev/null 2>&1',
'if docker ps -a --format "{{.Names}}" | grep -q "^coolify-proxy$"; then',
" echo 'Stopping and removing existing coolify-proxy.'",
' docker rm -f coolify-proxy || true',
" echo 'Successfully stopped and removed existing coolify-proxy.'",
'fi',
"echo 'Starting coolify-proxy.'",
'docker compose up -d --remove-orphans',
"echo 'Proxy started successfully.'",
"echo 'Successfully started coolify-proxy.'",
]);
$commands = $commands->merge(connectProxyToNetworks($server));
}

View File

@@ -2,7 +2,6 @@
namespace App\Actions\Server;
use App\Models\InstanceSettings;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -12,28 +11,29 @@ class CleanupDocker
public function handle(Server $server)
{
$settings = instanceSettings();
$helperImageVersion = data_get($settings, 'helper_version');
$helperImage = config('coolify.helper_image');
$helperImageWithVersion = "$helperImage:$helperImageVersion";
$commands = $this->getCommands();
$commands = [
'docker container prune -f --filter "label=coolify.managed=true" --filter "label!=coolify.proxy=true"',
'docker image prune -af --filter "label!=coolify.managed=true"',
'docker builder prune -af',
"docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi -f",
];
$serverSettings = $server->settings;
if ($serverSettings->delete_unused_volumes) {
$commands[] = 'docker volume prune -af';
}
if ($serverSettings->delete_unused_networks) {
$commands[] = 'docker network prune -f';
}
foreach ($commands as $command) {
instant_remote_process([$command], $server, false);
}
}
private function getCommands(): array
{
$settings = InstanceSettings::get();
$helperImageVersion = data_get($settings, 'helper_version');
$helperImage = config('coolify.helper_image');
$helperImageWithVersion = config('coolify.helper_image').':'.$helperImageVersion;
$commonCommands = [
'docker container prune -f --filter "label=coolify.managed=true"',
'docker image prune -af --filter "label!=coolify.managed=true"',
'docker builder prune -af',
"docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi",
];
return $commonCommands;
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Server;
use App\Events\CloudflareTunnelConfigured;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
use Symfony\Component\Yaml\Yaml;
@@ -40,12 +41,17 @@ class ConfigureCloudflared
instant_remote_process($commands, $server);
} catch (\Throwable $e) {
ray($e);
$server->settings->is_cloudflare_tunnel = false;
$server->settings->save();
throw $e;
} finally {
CloudflareTunnelConfigured::dispatch($server->team_id);
$commands = collect([
'rm -fr /tmp/cloudflared',
]);
instant_remote_process($commands, $server);
}
}
}

View File

@@ -3,7 +3,6 @@
namespace App\Actions\Server;
use App\Jobs\PullHelperImageJob;
use App\Models\InstanceSettings;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -20,7 +19,7 @@ class UpdateCoolify
public function handle($manual_update = false)
{
try {
$settings = InstanceSettings::get();
$settings = instanceSettings();
$this->server = Server::find(0);
if (! $this->server) {
return;

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Service;
use App\Actions\Server\CleanupDocker;
use App\Models\Service;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -9,11 +10,11 @@ class DeleteService
{
use AsAction;
public function handle(Service $service)
public function handle(Service $service, bool $deleteConfigurations, bool $deleteVolumes, bool $dockerCleanup, bool $deleteConnectedNetworks)
{
try {
$server = data_get($service, 'server');
if ($server->isFunctional()) {
if ($deleteVolumes && $server->isFunctional()) {
$storagesToDelete = collect([]);
$service->environment_variables()->delete();
@@ -33,13 +34,29 @@ class DeleteService
foreach ($storagesToDelete as $storage) {
$commands[] = "docker volume rm -f $storage->name";
}
$commands[] = "docker rm -f $service->uuid";
instant_remote_process($commands, $server, false);
// Execute volume deletion first, this must be done first otherwise volumes will not be deleted.
if (! empty($commands)) {
foreach ($commands as $command) {
$result = instant_remote_process([$command], $server, false);
if ($result !== 0) {
ray("Failed to execute: $command");
}
}
}
}
if ($deleteConnectedNetworks) {
$service->delete_connected_networks($service->uuid);
}
instant_remote_process(["docker rm -f $service->uuid"], $server, throwError: false);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
} finally {
if ($deleteConfigurations) {
$service->delete_configurations();
}
foreach ($service->applications()->get() as $application) {
$application->forceDelete();
}
@@ -50,6 +67,11 @@ class DeleteService
$task->delete();
}
$service->tags()->detach();
$service->forceDelete();
if ($dockerCleanup) {
CleanupDocker::dispatch($server, true);
}
}
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Service;
use App\Actions\Server\CleanupDocker;
use App\Models\Service;
use Lorisleiva\Actions\Concerns\AsAction;
@@ -9,40 +10,27 @@ class StopService
{
use AsAction;
public function handle(Service $service)
public function handle(Service $service, bool $isDeleteOperation = false, bool $dockerCleanup = true)
{
try {
$server = $service->destination->server;
if (! $server->isFunctional()) {
return 'Server is not functional';
}
ray('Stopping service: '.$service->name);
$applications = $service->applications()->get();
foreach ($applications as $application) {
if ($applications->count() < 6) {
instant_remote_process(command: ["docker stop --time=10 {$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) {
if ($dbs->count() < 6) {
instant_remote_process(command: ["docker stop --time=10 {$db->name}-{$service->uuid}"], server: $server, throwError: false);
$containersToStop = $service->getContainersToStop();
$service->stopContainers($containersToStop, $server);
if (! $isDeleteOperation) {
$service->delete_connected_networks($service->uuid);
if ($dockerCleanup) {
CleanupDocker::dispatch($server, true);
}
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) {
ray($e->getMessage());
return $e->getMessage();
}
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Console\Commands;
use App\Enums\ApplicationDeploymentStatus;
use App\Models\ApplicationDeploymentQueue;
use Illuminate\Console\Command;
class CheckApplicationDeploymentQueue extends Command
{
protected $signature = 'check:deployment-queue {--force} {--seconds=3600}';
protected $description = 'Check application deployment queue.';
public function handle()
{
$seconds = $this->option('seconds');
$deployments = ApplicationDeploymentQueue::whereIn('status', [
ApplicationDeploymentStatus::IN_PROGRESS,
ApplicationDeploymentStatus::QUEUED,
])->where('created_at', '<=', now()->subSeconds($seconds))->get();
if ($deployments->isEmpty()) {
$this->info('No deployments found in the last '.$seconds.' seconds.');
return;
}
$this->info('Found '.$deployments->count().' deployments created in the last '.$seconds.' seconds.');
foreach ($deployments as $deployment) {
if ($this->option('force')) {
$this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
$this->cancelDeployment($deployment);
} else {
$this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
if ($this->confirm('Do you want to cancel this deployment?', true)) {
$this->cancelDeployment($deployment);
}
}
}
}
private function cancelDeployment(ApplicationDeploymentQueue $deployment)
{
$deployment->update(['status' => ApplicationDeploymentStatus::FAILED]);
if ($deployment->server?->isFunctional()) {
remote_process(['docker rm -f '.$deployment->deployment_uuid], $deployment->server, false);
}
}
}

View File

@@ -7,9 +7,9 @@ use Illuminate\Console\Command;
class CleanupApplicationDeploymentQueue extends Command
{
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
protected $signature = 'cleanup:deployment-queue {--team-id=}';
protected $description = 'CleanupApplicationDeploymentQueue';
protected $description = 'Cleanup application deployment queue.';
public function handle()
{

View File

@@ -1,24 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
class CleanupQueue extends Command
{
protected $signature = 'cleanup:queue';
protected $description = 'Cleanup Queue';
public function handle()
{
echo "Running queue cleanup...\n";
$prefix = config('database.redis.options.prefix');
$keys = Redis::connection()->keys('*:laravel*');
foreach ($keys as $key) {
$keyWithoutPrefix = str_replace($prefix, '', $key);
Redis::connection()->del($keyWithoutPrefix);
}
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
class CleanupRedis extends Command
{
protected $signature = 'cleanup:redis';
protected $description = 'Cleanup Redis';
public function handle()
{
echo "Cleanup Redis keys.\n";
$prefix = config('database.redis.options.prefix');
$keys = Redis::connection()->keys('*:laravel*');
collect($keys)->each(function ($key) use ($prefix) {
$keyWithoutPrefix = str_replace($prefix, '', $key);
Redis::connection()->del($keyWithoutPrefix);
});
$queueOverlaps = Redis::connection()->keys('*laravel-queue-overlap*');
collect($queueOverlaps)->each(function ($key) {
Redis::connection()->del($key);
});
}
}

View File

@@ -2,10 +2,13 @@
namespace App\Console\Commands;
use App\Jobs\CleanupHelperContainersJob;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use App\Models\ApplicationPreview;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledTask;
use App\Models\Server;
use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
@@ -35,6 +38,27 @@ class CleanupStuckedResources extends Command
private function cleanup_stucked_resources()
{
try {
$servers = Server::all()->filter(function ($server) {
return $server->isFunctional();
});
foreach ($servers as $server) {
CleanupHelperContainersJob::dispatch($server);
}
} catch (\Throwable $e) {
echo "Error in cleaning stucked resources: {$e->getMessage()}\n";
}
try {
$applicationsDeploymentQueue = ApplicationDeploymentQueue::get();
foreach ($applicationsDeploymentQueue as $applicationDeploymentQueue) {
if (is_null($applicationDeploymentQueue->application)) {
echo "Deleting stuck application deployment queue: {$applicationDeploymentQueue->id}\n";
$applicationDeploymentQueue->delete();
}
}
} catch (\Throwable $e) {
echo "Error in cleaning stuck application deployment queue: {$e->getMessage()}\n";
}
try {
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
foreach ($applications as $application) {

View File

@@ -48,6 +48,13 @@ class Dev extends Command
echo "Generating APP_KEY.\n";
Artisan::call('key:generate');
}
// Generate STORAGE link if not exists
if (! file_exists(public_path('storage'))) {
echo "Generating STORAGE link.\n";
Artisan::call('storage:link');
}
// Seed database if it's empty
$settings = InstanceSettings::find(0);
if (! $settings) {

View File

@@ -5,10 +5,8 @@ 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;
use App\Models\Environment;
use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use App\Models\Server;
use App\Models\StandalonePostgresql;
@@ -18,7 +16,7 @@ use Illuminate\Support\Facades\Http;
class Init extends Command
{
protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments} {--cleanup-proxy-networks}';
protected $signature = 'app:init {--force-cloud}';
protected $description = 'Cleanup instance related stuffs';
@@ -26,9 +24,63 @@ class Init extends Command
public function handle()
{
if (isCloud() && ! $this->option('force-cloud')) {
echo "Skipping init as we are on cloud and --force-cloud option is not set\n";
return;
}
$this->servers = Server::all();
$this->alive();
get_public_ips();
if (isCloud()) {
} else {
$this->send_alive_signal();
get_public_ips();
}
// Backward compatibility
$this->disable_metrics();
$this->replace_slash_in_environment_name();
$this->restore_coolify_db_backup();
//
$this->update_traefik_labels();
if (! isCloud() || $this->option('force-cloud')) {
$this->cleanup_unused_network_from_coolify_proxy();
}
if (isCloud()) {
$this->cleanup_unnecessary_dynamic_proxy_configuration();
} else {
$this->cleanup_in_progress_application_deployments();
}
$this->call('cleanup:redis');
$this->call('cleanup:stucked-resources');
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));
}
} else {
try {
$localhost = $this->servers->where('id', 0)->first();
$localhost->setupDynamicProxyConfiguration();
} catch (\Throwable $e) {
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
}
$settings = instanceSettings();
if (! is_null(env('AUTOUPDATE', null))) {
if (env('AUTOUPDATE') == true) {
$settings->update(['is_auto_update_enabled' => true]);
} else {
$settings->update(['is_auto_update_enabled' => false]);
}
}
}
}
private function disable_metrics()
{
if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
foreach ($this->servers as $server) {
if ($server->settings->is_metrics_enabled === true) {
@@ -39,62 +91,6 @@ 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";
$this->cleanup_in_progress_application_deployments();
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 {
$localhost = $this->servers->where('id', 0)->first();
$localhost->setupDynamicProxyConfiguration();
} catch (\Throwable $e) {
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
}
}
$settings = InstanceSettings::get();
if (! is_null(env('AUTOUPDATE', null))) {
if (env('AUTOUPDATE') == true) {
$settings->update(['is_auto_update_enabled' => true]);
} else {
$settings->update(['is_auto_update_enabled' => false]);
}
}
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;
}
$this->cleanup_stucked_helper_containers();
$this->call('cleanup:stucked-resources');
}
private function update_traefik_labels()
@@ -108,33 +104,28 @@ class Init extends Command
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";
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()
{
if (isCloud()) {
return;
}
foreach ($this->servers as $server) {
if (! $server->isFunctional()) {
continue;
@@ -175,43 +166,36 @@ class Init extends Command
private function restore_coolify_db_backup()
{
try {
$database = StandalonePostgresql::withTrashed()->find(0);
if ($database && $database->trashed()) {
echo "Restoring coolify db backup\n";
$database->restore();
$scheduledBackup = ScheduledDatabaseBackup::find(0);
if (! $scheduledBackup) {
ScheduledDatabaseBackup::create([
'id' => 0,
'enabled' => true,
'save_s3' => false,
'frequency' => '0 0 * * *',
'database_id' => $database->id,
'database_type' => 'App\Models\StandalonePostgresql',
'team_id' => 0,
]);
if (version_compare('4.0.0-beta.179', config('version'), '<=')) {
try {
$database = StandalonePostgresql::withTrashed()->find(0);
if ($database && $database->trashed()) {
echo "Restoring coolify db backup\n";
$database->restore();
$scheduledBackup = ScheduledDatabaseBackup::find(0);
if (! $scheduledBackup) {
ScheduledDatabaseBackup::create([
'id' => 0,
'enabled' => true,
'save_s3' => false,
'frequency' => '0 0 * * *',
'database_id' => $database->id,
'database_type' => 'App\Models\StandalonePostgresql',
'team_id' => 0,
]);
}
}
}
} catch (\Throwable $e) {
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
}
}
private function cleanup_stucked_helper_containers()
{
foreach ($this->servers as $server) {
if ($server->isFunctional()) {
CleanupHelperContainersJob::dispatch($server);
} catch (\Throwable $e) {
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
}
}
}
private function alive()
private function send_alive_signal()
{
$id = config('app.id');
$version = config('version');
$settings = InstanceSettings::get();
$settings = instanceSettings();
$do_not_track = data_get($settings, 'do_not_track');
if ($do_not_track == true) {
echo "Skipping alive as do_not_track is enabled\n";
@@ -225,23 +209,7 @@ class Init extends Command
echo "Error in alive: {$e->getMessage()}\n";
}
}
// private function cleanup_ssh()
// {
// TODO: it will cleanup id.root@host.docker.internal
// try {
// $files = Storage::allFiles('ssh/keys');
// foreach ($files as $file) {
// Storage::delete($file);
// }
// $files = Storage::allFiles('ssh/mux');
// foreach ($files as $file) {
// Storage::delete($file);
// }
// } catch (\Throwable $e) {
// echo "Error in cleaning ssh: {$e->getMessage()}\n";
// }
// }
private function cleanup_in_progress_application_deployments()
{
// Cleanup any failed deployments
@@ -263,11 +231,13 @@ class Init extends Command
private function replace_slash_in_environment_name()
{
$environments = Environment::all();
foreach ($environments as $environment) {
if (str_contains($environment->name, '/')) {
$environment->name = str_replace('/', '-', $environment->name);
$environment->save();
if (version_compare('4.0.0-beta.298', config('version'), '<=')) {
$environments = Environment::all();
foreach ($environments as $environment) {
if (str_contains($environment->name, '/')) {
$environment->name = str_replace('/', '-', $environment->name);
$environment->save();
}
}
}
}

View File

@@ -39,8 +39,8 @@ class ServicesGenerate extends Command
$serviceTemplatesJson[$name] = $parsed;
}
}
$serviceTemplatesJson = json_encode($serviceTemplatesJson);
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson);
$serviceTemplatesJson = json_encode($serviceTemplatesJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson.PHP_EOL);
}
private function process_file($file)
@@ -78,7 +78,7 @@ class ServicesGenerate extends Command
if ($logo->count() > 0) {
$logo = str($logo[0])->after('# logo:')->trim()->value();
} else {
$logo = 'svgs/unknown.svg';
$logo = 'svgs/coolify.png';
}
$minversion = collect(preg_grep('/^# minversion:/', explode("\n", $content)))->values();
if ($minversion->count() > 0) {

View File

@@ -12,8 +12,8 @@ use App\Jobs\PullSentinelImageJob;
use App\Jobs\PullTemplatesFromCDN;
use App\Jobs\ScheduledTaskJob;
use App\Jobs\ServerCheckJob;
use App\Jobs\ServerStorageCheckJob;
use App\Jobs\UpdateCoolifyJob;
use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledTask;
use App\Models\Server;
@@ -28,7 +28,7 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule): void
{
$this->all_servers = Server::all();
$settings = InstanceSettings::get();
$settings = instanceSettings();
$schedule->job(new CleanupStaleMultiplexedConnections)->hourly();
@@ -43,6 +43,8 @@ class Kernel extends ConsoleKernel
$schedule->command('uploads:clear')->everyTwoMinutes();
$schedule->command('telescope:prune')->daily();
$schedule->job(new PullHelperImageJob)->everyFiveMinutes()->onOneServer();
} else {
// Instance Jobs
$schedule->command('horizon:snapshot')->everyFiveMinutes();
@@ -64,7 +66,7 @@ class Kernel extends ConsoleKernel
private function pull_images($schedule)
{
$settings = InstanceSettings::get();
$settings = instanceSettings();
$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()) {
@@ -77,16 +79,16 @@ class Kernel extends ConsoleKernel
}
})->cron($settings->update_check_frequency)->timezone($settings->instance_timezone)->onOneServer();
}
$schedule->job(new PullHelperImageJob($server))
->cron($settings->update_check_frequency)
->timezone($settings->instance_timezone)
->onOneServer();
}
$schedule->job(new PullHelperImageJob)
->cron($settings->update_check_frequency)
->timezone($settings->instance_timezone)
->onOneServer();
}
private function schedule_updates($schedule)
{
$settings = InstanceSettings::get();
$settings = instanceSettings();
$updateCheckFrequency = $settings->update_check_frequency;
$schedule->job(new CheckForUpdatesJob)
@@ -114,6 +116,7 @@ class Kernel extends ConsoleKernel
}
foreach ($servers as $server) {
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
// $schedule->job(new ServerStorageCheckJob($server))->everyMinute()->onOneServer();
$serverTimezone = $server->settings->server_timezone;
if ($server->settings->force_docker_cleanup) {
$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Enums;
enum ContainerStatusTypes: string
{
case PAUSED = 'paused';
case RESTARTING = 'restarting';
case REMOVING = 'removing';
case RUNNING = 'running';
case DEAD = 'dead';
case CREATED = 'created';
case EXITED = 'exited';
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Enums;
enum StaticImageTypes: string
{
case NGINX_ALPINE = 'nginx:alpine';
}

View File

@@ -0,0 +1,34 @@
<?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 CloudflareTunnelConfigured implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $teamId;
public function __construct($teamId = null)
{
if (is_null($teamId)) {
$teamId = auth()->user()->currentTeam()->id ?? null;
}
if (is_null($teamId)) {
throw new \Exception('Team id is null');
}
$this->teamId = $teamId;
}
public function broadcastOn(): array
{
return [
new PrivateChannel("team.{$this->teamId}"),
];
}
}

View File

@@ -65,7 +65,7 @@ class Handler extends ExceptionHandler
if ($e instanceof RuntimeException) {
return;
}
$this->settings = \App\Models\InstanceSettings::get();
$this->settings = instanceSettings();
if ($this->settings->do_not_track) {
return;
}

View File

@@ -0,0 +1,186 @@
<?php
namespace App\Helpers;
use App\Models\PrivateKey;
use App\Models\Server;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Process;
class SshMultiplexingHelper
{
public static function serverSshConfiguration(Server $server)
{
$privateKey = PrivateKey::findOrFail($server->private_key_id);
$sshKeyLocation = $privateKey->getKeyLocation();
$muxFilename = '/var/www/html/storage/app/ssh/mux/mux_'.$server->uuid;
return [
'sshKeyLocation' => $sshKeyLocation,
'muxFilename' => $muxFilename,
];
}
public static function ensureMultiplexedConnection(Server $server)
{
if (! self::isMultiplexingEnabled()) {
return;
}
$sshConfig = self::serverSshConfiguration($server);
$muxSocket = $sshConfig['muxFilename'];
$sshKeyLocation = $sshConfig['sshKeyLocation'];
self::validateSshKey($sshKeyLocation);
$checkCommand = "ssh -O check -o ControlPath=$muxSocket ";
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$checkCommand .= '-o ProxyCommand="cloudflared access ssh --hostname %h" ';
}
$checkCommand .= "{$server->user}@{$server->ip}";
$process = Process::run($checkCommand);
if ($process->exitCode() !== 0) {
self::establishNewMultiplexedConnection($server);
}
}
public static function establishNewMultiplexedConnection(Server $server)
{
$sshConfig = self::serverSshConfiguration($server);
$sshKeyLocation = $sshConfig['sshKeyLocation'];
$muxSocket = $sshConfig['muxFilename'];
$connectionTimeout = config('constants.ssh.connection_timeout');
$serverInterval = config('constants.ssh.server_interval');
$muxPersistTime = config('constants.ssh.mux_persist_time');
$establishCommand = "ssh -fNM -o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$establishCommand .= ' -o ProxyCommand="cloudflared access ssh --hostname %h" ';
}
$establishCommand .= self::getCommonSshOptions($server, $sshKeyLocation, $connectionTimeout, $serverInterval);
$establishCommand .= "{$server->user}@{$server->ip}";
$establishProcess = Process::run($establishCommand);
if ($establishProcess->exitCode() !== 0) {
throw new \RuntimeException('Failed to establish multiplexed connection: '.$establishProcess->errorOutput());
}
}
public static function removeMuxFile(Server $server)
{
$sshConfig = self::serverSshConfiguration($server);
$muxSocket = $sshConfig['muxFilename'];
$closeCommand = "ssh -O exit -o ControlPath=$muxSocket ";
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$closeCommand .= '-o ProxyCommand="cloudflared access ssh --hostname %h" ';
}
$closeCommand .= "{$server->user}@{$server->ip}";
Process::run($closeCommand);
}
public static function generateScpCommand(Server $server, string $source, string $dest)
{
$sshConfig = self::serverSshConfiguration($server);
$sshKeyLocation = $sshConfig['sshKeyLocation'];
$muxSocket = $sshConfig['muxFilename'];
$timeout = config('constants.ssh.command_timeout');
$muxPersistTime = config('constants.ssh.mux_persist_time');
$scp_command = "timeout $timeout scp ";
if ($server->isIpv6()) {
$scp_command .= '-6 ';
}
if (self::isMultiplexingEnabled()) {
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
self::ensureMultiplexedConnection($server);
}
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$scp_command .= '-o ProxyCommand="cloudflared access ssh --hostname %h" ';
}
$scp_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'), isScp: true);
$scp_command .= "{$source} {$server->user}@{$server->ip}:{$dest}";
return $scp_command;
}
public static function generateSshCommand(Server $server, string $command)
{
if ($server->settings->force_disabled) {
throw new \RuntimeException('Server is disabled.');
}
$sshConfig = self::serverSshConfiguration($server);
$sshKeyLocation = $sshConfig['sshKeyLocation'];
$muxSocket = $sshConfig['muxFilename'];
$timeout = config('constants.ssh.command_timeout');
$muxPersistTime = config('constants.ssh.mux_persist_time');
$ssh_command = "timeout $timeout ssh ";
if (self::isMultiplexingEnabled()) {
$ssh_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
self::ensureMultiplexedConnection($server);
}
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$ssh_command .= "-o ProxyCommand='cloudflared access ssh --hostname %h' ";
}
$ssh_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'));
$delimiter = Hash::make($command);
$delimiter = base64_encode($delimiter);
$command = str_replace($delimiter, '', $command);
$ssh_command .= "{$server->user}@{$server->ip} 'bash -se' << \\$delimiter".PHP_EOL
.$command.PHP_EOL
.$delimiter;
return $ssh_command;
}
private static function isMultiplexingEnabled(): bool
{
return config('constants.ssh.mux_enabled') && ! config('coolify.is_windows_docker_desktop');
}
private static function validateSshKey(string $sshKeyLocation): void
{
$checkKeyCommand = "ls $sshKeyLocation 2>/dev/null";
$keyCheckProcess = Process::run($checkKeyCommand);
if ($keyCheckProcess->exitCode() !== 0) {
throw new \RuntimeException("SSH key file not accessible: $sshKeyLocation");
}
}
private static function getCommonSshOptions(Server $server, string $sshKeyLocation, int $connectionTimeout, int $serverInterval, bool $isScp = false): string
{
$options = "-i {$sshKeyLocation} "
.'-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
.'-o PasswordAuthentication=no '
."-o ConnectTimeout=$connectionTimeout "
."-o ServerAliveInterval=$serverInterval "
.'-o RequestTTY=no '
.'-o LogLevel=ERROR ';
// Bruh
if ($isScp) {
$options .= "-P {$server->port} ";
} else {
$options .= "-p {$server->port} ";
}
return $options;
}
}

View File

@@ -132,6 +132,7 @@ class ApplicationsController extends Controller
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
'start_command' => ['type' => 'string', 'description' => 'The start command.'],
@@ -177,6 +178,7 @@ class ApplicationsController extends Controller
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -235,6 +237,7 @@ class ApplicationsController extends Controller
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
'start_command' => ['type' => 'string', 'description' => 'The start command.'],
@@ -279,6 +282,7 @@ class ApplicationsController extends Controller
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -337,6 +341,7 @@ class ApplicationsController extends Controller
'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'],
'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'],
'is_static' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application is static.'],
'static_image' => ['type' => 'string', 'enum' => ['nginx:alpine'], 'description' => 'The static image.'],
'install_command' => ['type' => 'string', 'description' => 'The install command.'],
'build_command' => ['type' => 'string', 'description' => 'The build command.'],
'start_command' => ['type' => 'string', 'description' => 'The start command.'],
@@ -381,6 +386,7 @@ class ApplicationsController extends Controller
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -468,6 +474,7 @@ class ApplicationsController extends Controller
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -552,6 +559,7 @@ class ApplicationsController extends Controller
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -602,6 +610,7 @@ class ApplicationsController extends Controller
'name' => ['type' => 'string', 'description' => 'The application name.'],
'description' => ['type' => 'string', 'description' => 'The application description.'],
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -627,7 +636,7 @@ class ApplicationsController extends Controller
private function create_application(Request $request, $type)
{
$allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths'];
$allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server', 'static_image'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
return invalidTokenResponse();
@@ -665,6 +674,8 @@ class ApplicationsController extends Controller
$fqdn = $request->domains;
$instantDeploy = $request->instant_deploy;
$githubAppUuid = $request->github_app_uuid;
$useBuildServer = $request->use_build_server;
$isStatic = $request->is_static;
$project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first();
if (! $project) {
@@ -693,8 +704,7 @@ class ApplicationsController extends Controller
if ($request->build_pack === 'dockercompose') {
$request->offsetSet('ports_exposes', '80');
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
$validationRules = [
'git_repository' => 'string|required',
'git_branch' => 'string|required',
'build_pack' => ['required', Rule::enum(BuildPackTypes::class)],
@@ -702,19 +712,21 @@ class ApplicationsController extends Controller
'docker_compose_location' => 'string',
'docker_compose_raw' => 'string|nullable',
'docker_compose_domains' => 'array|nullable',
'docker_compose_custom_start_command' => 'string|nullable',
'docker_compose_custom_build_command' => 'string|nullable',
]);
];
$validationRules = array_merge($validationRules, sharedDataApplications());
$validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$return = $this->validateDataApplications($request, $server);
if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return;
}
$application = new Application;
removeUnnecessaryFieldsFromRequest($request);
@@ -738,6 +750,14 @@ class ApplicationsController extends Controller
$application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id;
$application->save();
if (isset($isStatic)) {
$application->settings->is_static = $isStatic;
$application->settings->save();
}
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save();
}
$application->refresh();
if (! $application->settings->is_container_label_readonly_enabled) {
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
@@ -771,8 +791,7 @@ class ApplicationsController extends Controller
if ($request->build_pack === 'dockercompose') {
$request->offsetSet('ports_exposes', '80');
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
$validationRules = [
'git_repository' => 'string|required',
'git_branch' => 'string|required',
'build_pack' => ['required', Rule::enum(BuildPackTypes::class)],
@@ -781,10 +800,10 @@ class ApplicationsController extends Controller
'watch_paths' => 'string|nullable',
'docker_compose_location' => 'string',
'docker_compose_raw' => 'string|nullable',
'docker_compose_domains' => 'array|nullable',
'docker_compose_custom_start_command' => 'string|nullable',
'docker_compose_custom_build_command' => 'string|nullable',
]);
];
$validationRules = array_merge($validationRules, sharedDataApplications());
$validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
@@ -833,6 +852,10 @@ class ApplicationsController extends Controller
$application->environment_id = $environment->id;
$application->source_type = $githubApp->getMorphClass();
$application->source_id = $githubApp->id;
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save();
}
$application->save();
$application->refresh();
if (! $application->settings->is_container_label_readonly_enabled) {
@@ -867,8 +890,8 @@ class ApplicationsController extends Controller
if ($request->build_pack === 'dockercompose') {
$request->offsetSet('ports_exposes', '80');
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
$validationRules = [
'git_repository' => 'string|required',
'git_branch' => 'string|required',
'build_pack' => ['required', Rule::enum(BuildPackTypes::class)],
@@ -877,10 +900,10 @@ class ApplicationsController extends Controller
'watch_paths' => 'string|nullable',
'docker_compose_location' => 'string',
'docker_compose_raw' => 'string|nullable',
'docker_compose_domains' => 'array|nullable',
'docker_compose_custom_start_command' => 'string|nullable',
'docker_compose_custom_build_command' => 'string|nullable',
]);
];
$validationRules = array_merge($validationRules, sharedDataApplications());
$validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) {
return response()->json([
@@ -925,6 +948,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id;
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save();
}
$application->save();
$application->refresh();
if (! $application->settings->is_container_label_readonly_enabled) {
@@ -956,10 +983,13 @@ class ApplicationsController extends Controller
if (! $request->has('name')) {
$request->offsetSet('name', 'dockerfile-'.new Cuid2);
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
$validationRules = [
'dockerfile' => 'string|required',
]);
];
$validationRules = array_merge($validationRules, sharedDataApplications());
$validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
@@ -1004,6 +1034,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id;
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save();
}
$application->git_repository = 'coollabsio/coolify';
$application->git_branch = 'main';
@@ -1034,12 +1068,14 @@ class ApplicationsController extends Controller
if (! $request->has('name')) {
$request->offsetSet('name', 'docker-image-'.new Cuid2);
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
$validationRules = [
'docker_registry_image_name' => 'string|required',
'docker_registry_image_tag' => 'string',
'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required',
]);
];
$validationRules = array_merge($validationRules, sharedDataApplications());
$validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
@@ -1062,6 +1098,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id;
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save();
}
$application->git_repository = 'coollabsio/coolify';
$application->git_branch = 'main';
@@ -1108,10 +1148,12 @@ class ApplicationsController extends Controller
if (! $request->has('name')) {
$request->offsetSet('name', 'service'.new Cuid2);
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
$validationRules = [
'docker_compose_raw' => 'string|required',
]);
];
$validationRules = array_merge($validationRules, sharedDataApplications());
$validator = customApiValidator($request->all(), $validationRules);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
@@ -1259,16 +1301,10 @@ class ApplicationsController extends Controller
format: 'uuid',
)
),
new OA\Parameter(
name: 'cleanup',
in: 'query',
description: 'Delete configurations and volumes.',
required: false,
schema: new OA\Schema(
type: 'boolean',
default: true,
)
),
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
],
responses: [
new OA\Response(
@@ -1316,10 +1352,14 @@ class ApplicationsController extends Controller
'message' => 'Application not found',
], 404);
}
DeleteResourceJob::dispatch(
resource: $application,
deleteConfigurations: $cleanup,
deleteVolumes: $cleanup);
deleteConfigurations: $request->query->get('delete_configurations', true),
deleteVolumes: $request->query->get('delete_volumes', true),
dockerCleanup: $request->query->get('docker_cleanup', true),
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
);
return response()->json([
'message' => 'Application deletion request queued.',
@@ -1404,6 +1444,7 @@ class ApplicationsController extends Controller
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
],
)),
]),
@@ -1460,10 +1501,9 @@ class ApplicationsController extends Controller
], 404);
}
$server = $application->destination->server;
$allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy'];
$allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy', 'use_build_server'];
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
$validationRules = [
'name' => 'string|max:255',
'description' => 'string|nullable',
'static_image' => 'string',
@@ -1473,7 +1513,9 @@ class ApplicationsController extends Controller
'docker_compose_domains' => 'array|nullable',
'docker_compose_custom_start_command' => 'string|nullable',
'docker_compose_custom_build_command' => 'string|nullable',
]);
];
$validationRules = array_merge($validationRules, sharedDataApplications());
$validator = customApiValidator($request->all(), $validationRules);
// Validate ports_exposes
if ($request->has('ports_exposes')) {
@@ -1538,6 +1580,13 @@ class ApplicationsController extends Controller
}
$instantDeploy = $request->instant_deploy;
$use_build_server = $request->use_build_server;
if (isset($use_build_server)) {
$application->settings->is_build_server_enabled = $use_build_server;
$application->settings->save();
}
removeUnnecessaryFieldsFromRequest($request);
$data = $request->all();
@@ -2529,6 +2578,131 @@ class ApplicationsController extends Controller
}
#[OA\Post(
summary: 'Execute Command',
description: "Execute a command on the application's current container.",
path: '/applications/{uuid}/execute',
operationId: 'execute-command-application',
security: [
['bearerAuth' => []],
],
tags: ['Applications'],
parameters: [
new OA\Parameter(
name: 'uuid',
in: 'path',
description: 'UUID of the application.',
required: true,
schema: new OA\Schema(
type: 'string',
format: 'uuid',
)
),
],
requestBody: new OA\RequestBody(
required: true,
description: 'Command to execute.',
content: new OA\MediaType(
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
properties: [
'command' => ['type' => 'string', 'description' => 'Command to execute.'],
],
),
),
),
responses: [
new OA\Response(
response: 200,
description: "Execute a command on the application's current container.",
content: [
new OA\MediaType(
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Command executed.'],
'response' => ['type' => 'string'],
]
)
),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
),
new OA\Response(
response: 400,
ref: '#/components/responses/400',
),
new OA\Response(
response: 404,
ref: '#/components/responses/404',
),
]
)]
public function execute_command_by_uuid(Request $request)
{
// TODO: Need to review this from security perspective, to not allow arbitrary command execution
$allowedFields = ['command'];
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
return invalidTokenResponse();
}
$uuid = $request->route('uuid');
if (! $uuid) {
return response()->json(['message' => 'UUID is required.'], 400);
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
if (! $application) {
return response()->json(['message' => 'Application not found.'], 404);
}
$return = validateIncomingRequest($request);
if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return;
}
$validator = customApiValidator($request->all(), [
'command' => 'string|required',
]);
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
if ($validator->fails() || ! empty($extraFields)) {
$errors = $validator->errors();
if (! empty($extraFields)) {
foreach ($extraFields as $field) {
$errors->add($field, 'This field is not allowed.');
}
}
return response()->json([
'message' => 'Validation failed.',
'errors' => $errors,
], 422);
}
$container = getCurrentApplicationContainerStatus($application->destination->server, $application->id)->firstOrFail();
$status = getContainerStatus($application->destination->server, $container['Names']);
if ($status !== 'running') {
return response()->json([
'message' => 'Application is not running.',
], 400);
}
$commands = collect([
executeInDocker($container['Names'], $request->command),
]);
$res = instant_remote_process(command: $commands, server: $application->destination->server);
return response()->json([
'message' => 'Command executed.',
'response' => $res,
]);
}
private function validateDataApplications(Request $request, Server $server)
{
$teamId = getTeamIdFromToken();

View File

@@ -1541,16 +1541,10 @@ class DatabasesController extends Controller
format: 'uuid',
)
),
new OA\Parameter(
name: 'cleanup',
in: 'query',
description: 'Delete configurations and volumes.',
required: false,
schema: new OA\Schema(
type: 'boolean',
default: true,
)
),
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
],
responses: [
new OA\Response(
@@ -1595,10 +1589,14 @@ class DatabasesController extends Controller
if (! $database) {
return response()->json(['message' => 'Database not found.'], 404);
}
DeleteResourceJob::dispatch(
resource: $database,
deleteConfigurations: $cleanup,
deleteVolumes: $cleanup);
deleteConfigurations: $request->query->get('delete_configurations', true),
deleteVolumes: $request->query->get('delete_volumes', true),
dockerCleanup: $request->query->get('docker_cleanup', true),
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
);
return response()->json([
'message' => 'Database deletion request queued.',

View File

@@ -86,7 +86,7 @@ class OtherController extends Controller
if ($teamId !== '0') {
return response()->json(['message' => 'You are not allowed to enable the API.'], 403);
}
$settings = \App\Models\InstanceSettings::get();
$settings = instanceSettings();
$settings->update(['is_api_enabled' => true]);
return response()->json(['message' => 'API enabled.'], 200);
@@ -138,7 +138,7 @@ class OtherController extends Controller
if ($teamId !== '0') {
return response()->json(['message' => 'You are not allowed to disable the API.'], 403);
}
$settings = \App\Models\InstanceSettings::get();
$settings = instanceSettings();
$settings->update(['is_api_enabled' => false]);
return response()->json(['message' => 'API disabled.'], 200);

View File

@@ -308,7 +308,7 @@ class ServersController extends Controller
$projects = Project::where('team_id', $teamId)->get();
$domains = collect();
$applications = $projects->pluck('applications')->flatten();
$settings = \App\Models\InstanceSettings::get();
$settings = instanceSettings();
if ($applications->count() > 0) {
foreach ($applications as $application) {
$ip = $application->destination->server->ip;

View File

@@ -432,6 +432,10 @@ class ServicesController extends Controller
tags: ['Services'],
parameters: [
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Service UUID', schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
],
responses: [
new OA\Response(
@@ -476,7 +480,14 @@ class ServicesController extends Controller
if (! $service) {
return response()->json(['message' => 'Service not found.'], 404);
}
DeleteResourceJob::dispatch($service);
DeleteResourceJob::dispatch(
resource: $service,
deleteConfigurations: $request->query->get('delete_configurations', true),
deleteVolumes: $request->query->get('delete_volumes', true),
dockerCleanup: $request->query->get('docker_cleanup', true),
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
);
return response()->json([
'message' => 'Service deletion request queued.',
@@ -516,7 +527,8 @@ class ServicesController extends Controller
items: new OA\Items(ref: '#/components/schemas/EnvironmentVariable')
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -619,7 +631,8 @@ class ServicesController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -738,7 +751,8 @@ class ServicesController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -853,7 +867,8 @@ class ServicesController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -953,7 +968,8 @@ class ServicesController extends Controller
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1025,9 +1041,11 @@ class ServicesController extends Controller
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Service starting request queued.'],
])
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1101,9 +1119,11 @@ class ServicesController extends Controller
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Service stopping request queued.'],
])
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
@@ -1177,9 +1197,11 @@ class ServicesController extends Controller
type: 'object',
properties: [
'message' => ['type' => 'string', 'example' => 'Service restaring request queued.'],
])
]
)
),
]),
]
),
new OA\Response(
response: 401,
ref: '#/components/responses/401',

View File

@@ -2,7 +2,6 @@
namespace App\Http\Controllers;
use App\Models\InstanceSettings;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -22,7 +21,7 @@ class OauthController extends Controller
$oauthUser = get_socialite_provider($provider)->user();
$user = User::whereEmail($oauthUser->email)->first();
if (! $user) {
$settings = InstanceSettings::get();
$settings = instanceSettings();
if (! $settings->is_registration_enabled) {
abort(403, 'Registration is disabled');
}

View File

@@ -14,7 +14,7 @@ class ApiAllowed
if (isCloud()) {
return $next($request);
}
$settings = \App\Models\InstanceSettings::get();
$settings = instanceSettings();
if ($settings->is_api_enabled === false) {
return response()->json(['success' => true, 'message' => 'API is disabled.'], 403);
}

View File

@@ -12,7 +12,6 @@ use App\Models\ApplicationPreview;
use App\Models\EnvironmentVariable;
use App\Models\GithubApp;
use App\Models\GitlabApp;
use App\Models\InstanceSettings;
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
@@ -27,6 +26,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Sleep;
use Illuminate\Support\Str;
use RuntimeException;
@@ -210,7 +210,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
ray('New container name: ', $this->container_name)->green();
savePrivateKeyToFs($this->server);
$this->saved_outputs = collect();
// Set preview fqdn
@@ -962,7 +961,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
}
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
if ($this->application->compose_parsing_version === '3') {
if ((int) $this->application->compose_parsing_version >= 3) {
$envs->push("COOLIFY_URL={$this->application->fqdn}");
} else {
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
@@ -970,7 +969,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
if ($this->application->compose_parsing_version === '3') {
if ((int) $this->application->compose_parsing_version >= 3) {
$envs->push("COOLIFY_FQDN={$url}");
} else {
$envs->push("COOLIFY_URL={$url}");
@@ -1334,7 +1333,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private function prepare_builder_image()
{
$settings = InstanceSettings::get();
$settings = instanceSettings();
$helperImage = config('coolify.helper_image');
$helperImage = "{$helperImage}:{$settings->helper_version}";
// Get user home directory
@@ -1456,10 +1455,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
executeInDocker($this->deployment_uuid, 'chmod 600 /root/.ssh/id_rsa'),
],
[
executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa\" git ls-remote {$this->fullRepoUrl} {$local_branch}"),
executeInDocker($this->deployment_uuid, "GIT_SSH_COMMAND=\"ssh -o ConnectTimeout=30 -p {$this->customPort} -o Port={$this->customPort} -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git ls-remote {$this->fullRepoUrl} {$local_branch}"),
'hidden' => true,
'save' => 'git_commit_sha',
],
]
);
} else {
$this->execute_remote_command(
@@ -2211,20 +2210,40 @@ 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)
private function graceful_shutdown_container(string $containerName, int $timeout = 300)
{
try {
$this->execute_remote_command(
["docker stop --time=$timeout $containerName", 'hidden' => true, 'ignore_errors' => true],
["docker rm $containerName", 'hidden' => true, 'ignore_errors' => true]
);
$process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
$startTime = time();
while ($process->running()) {
if (time() - $startTime >= $timeout) {
$this->execute_remote_command(
["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true]
);
break;
}
usleep(100000);
}
$isRunning = $this->execute_remote_command(
["docker inspect -f '{{.State.Running}}' $containerName", 'hidden' => true, 'ignore_errors' => true]
) === 'true';
if ($isRunning) {
$this->execute_remote_command(
["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true]
);
}
} catch (\Exception $error) {
// report error if needed
$this->application_deployment_queue->addLogEntry("Error stopping container $containerName: ".$error->getMessage(), 'stderr');
}
$this->remove_container($containerName);
}
private function remove_container(string $containerName)
{
$this->execute_remote_command(
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
);

View File

@@ -2,15 +2,14 @@
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;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
{
@@ -22,7 +21,7 @@ class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
if (isDev() || isCloud()) {
return;
}
$settings = InstanceSettings::get();
$settings = instanceSettings();
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
$versions = $response->json();

View File

@@ -21,11 +21,10 @@ class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, S
{
try {
ray('Cleaning up helper containers on '.$this->server->name);
$containers = instant_remote_process(['docker container ps --filter "ancestor=ghcr.io/coollabsio/coolify-helper:next" --filter "ancestor=ghcr.io/coollabsio/coolify-helper:latest" --format \'{{json .}}\''], $this->server, false);
$containers = format_docker_command_output_to_json($containers);
if ($containers->count() > 0) {
foreach ($containers as $container) {
$containerId = data_get($container, 'ID');
$containers = instant_remote_process(['docker container ps --format \'{{json .}}\' | jq -s \'map(select(.Image | contains("ghcr.io/coollabsio/coolify-helper")))\''], $this->server, false);
$containerIds = collect(json_decode($containers))->pluck('ID');
if ($containerIds->count() > 0) {
foreach ($containerIds as $containerId) {
ray('Removing container '.$containerId);
instant_remote_process(['docker container rm -f '.$containerId], $this->server, false);
}

View File

@@ -3,12 +3,14 @@
namespace App\Jobs;
use App\Models\Server;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Storage;
class CleanupStaleMultiplexedConnections implements ShouldQueue
{
@@ -16,22 +18,65 @@ class CleanupStaleMultiplexedConnections implements ShouldQueue
public function handle()
{
Server::chunk(100, function ($servers) {
foreach ($servers as $server) {
$this->cleanupStaleConnection($server);
}
});
$this->cleanupStaleConnections();
$this->cleanupNonExistentServerConnections();
}
private function cleanupStaleConnection(Server $server)
private function cleanupStaleConnections()
{
$muxSocket = "/tmp/mux_{$server->id}";
$checkCommand = "ssh -O check -o ControlPath=$muxSocket {$server->user}@{$server->ip} 2>/dev/null";
$checkProcess = Process::run($checkCommand);
$muxFiles = Storage::disk('ssh-mux')->files();
if ($checkProcess->exitCode() !== 0) {
$closeCommand = "ssh -O exit -o ControlPath=$muxSocket {$server->user}@{$server->ip} 2>/dev/null";
Process::run($closeCommand);
foreach ($muxFiles as $muxFile) {
$serverUuid = $this->extractServerUuidFromMuxFile($muxFile);
$server = Server::where('uuid', $serverUuid)->first();
if (! $server) {
$this->removeMultiplexFile($muxFile);
continue;
}
$muxSocket = "/var/www/html/storage/app/ssh/mux/{$muxFile}";
$checkCommand = "ssh -O check -o ControlPath={$muxSocket} {$server->user}@{$server->ip} 2>/dev/null";
$checkProcess = Process::run($checkCommand);
if ($checkProcess->exitCode() !== 0) {
$this->removeMultiplexFile($muxFile);
} else {
$muxContent = Storage::disk('ssh-mux')->get($muxFile);
$establishedAt = Carbon::parse(substr($muxContent, 37));
$expirationTime = $establishedAt->addSeconds(config('constants.ssh.mux_persist_time'));
if (Carbon::now()->isAfter($expirationTime)) {
$this->removeMultiplexFile($muxFile);
}
}
}
}
private function cleanupNonExistentServerConnections()
{
$muxFiles = Storage::disk('ssh-mux')->files();
$existingServerUuids = Server::pluck('uuid')->toArray();
foreach ($muxFiles as $muxFile) {
$serverUuid = $this->extractServerUuidFromMuxFile($muxFile);
if (! in_array($serverUuid, $existingServerUuids)) {
$this->removeMultiplexFile($muxFile);
}
}
}
private function extractServerUuidFromMuxFile($muxFile)
{
return substr($muxFile, 4);
}
private function removeMultiplexFile($muxFile)
{
$muxSocket = "/var/www/html/storage/app/ssh/mux/{$muxFile}";
$closeCommand = "ssh -O exit -o ControlPath={$muxSocket} localhost 2>/dev/null";
Process::run($closeCommand);
Storage::disk('ssh-mux')->delete($muxFile);
}
}

View File

@@ -9,7 +9,6 @@ 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;
class ContainerStatusJob implements ShouldBeEncrypted, ShouldQueue
@@ -25,16 +24,6 @@ class ContainerStatusJob implements ShouldBeEncrypted, ShouldQueue
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()
{
GetContainersStatus::run($this->server);

View File

@@ -2,9 +2,7 @@
namespace App\Jobs;
use App\Actions\Database\StopDatabase;
use App\Events\BackupCreated;
use App\Models\InstanceSettings;
use App\Models\S3Storage;
use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledDatabaseBackupExecution;
@@ -23,10 +21,8 @@ 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\Str;
use Visus\Cuid2\Cuid2;
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
@@ -65,42 +61,32 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
public function __construct($backup)
{
$this->backup = $backup;
$this->team = Team::find($backup->team_id);
if (is_null($this->team)) {
return;
}
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
$this->database = data_get($this->backup, 'database');
$this->server = $this->database->service->server;
$this->s3 = $this->backup->s3;
} else {
$this->database = data_get($this->backup, 'database');
$this->server = $this->database->destination->server;
$this->s3 = $this->backup->s3;
}
}
public function middleware(): array
{
return [new WithoutOverlapping($this->backup->id)];
}
public function uniqueId(): int
{
return $this->backup->id;
}
public function handle(): void
{
try {
// Check if team is exists
if (is_null($this->team)) {
$this->backup->update(['status' => 'failed']);
StopDatabase::run($this->database);
$this->database->delete();
$this->team = Team::find($this->backup->team_id);
if (! $this->team) {
$this->backup->delete();
return;
}
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
$this->database = data_get($this->backup, 'database');
$this->server = $this->database->service->server;
$this->s3 = $this->backup->s3;
} else {
$this->database = data_get($this->backup, 'database');
$this->server = $this->database->destination->server;
$this->s3 = $this->backup->s3;
}
if (is_null($this->server)) {
throw new \Exception('Server not found?!');
}
if (is_null($this->database)) {
throw new \Exception('Database not found?!');
}
BackupCreated::dispatch($this->team->id);
@@ -250,7 +236,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
}
}
$this->backup_dir = backup_dir().'/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name;
if ($this->database->name === 'coolify-db') {
$databasesToBackup = ['coolify'];
$this->directory_name = $this->container_name = 'coolify-db';
@@ -263,6 +248,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
try {
if (str($databaseType)->contains('postgres')) {
$this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp';
if ($this->backup->dump_all) {
$this->backup_file = '/pg-dump-all-'.Carbon::now()->timestamp.'.gz';
}
$this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database,
@@ -291,6 +279,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->backup_standalone_mongodb($database);
} elseif (str($databaseType)->contains('mysql')) {
$this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp';
if ($this->backup->dump_all) {
$this->backup_file = '/mysql-dump-all-'.Carbon::now()->timestamp.'.gz';
}
$this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database,
@@ -300,6 +291,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->backup_standalone_mysql($database);
} elseif (str($databaseType)->contains('mariadb')) {
$this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp';
if ($this->backup->dump_all) {
$this->backup_file = '/mariadb-dump-all-'.Carbon::now()->timestamp.'.gz';
}
$this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database,
@@ -338,7 +332,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage());
throw $e;
} finally {
BackupCreated::dispatch($this->team->id);
if ($this->team) {
BackupCreated::dispatch($this->team->id);
}
}
}
@@ -397,7 +393,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
if ($this->postgres_password) {
$backupCommand .= " -e PGPASSWORD=$this->postgres_password";
}
$backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
if ($this->backup->dump_all) {
$backupCommand .= " $this->container_name pg_dumpall --username {$this->database->postgres_user} | gzip > $this->backup_location";
} else {
$backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
}
$commands[] = $backupCommand;
ray($commands);
@@ -418,8 +418,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
try {
$commands[] = 'mkdir -p '.$this->backup_dir;
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
ray($commands);
if ($this->backup->dump_all) {
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress | gzip > $this->backup_location";
} else {
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
}
$this->backup_output = instant_remote_process($commands, $this->server);
$this->backup_output = trim($this->backup_output);
if ($this->backup_output === '') {
@@ -437,7 +440,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
try {
$commands[] = 'mkdir -p '.$this->backup_dir;
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
if ($this->backup->dump_all) {
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress > $this->backup_location";
} else {
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
}
ray($commands);
$this->backup_output = instant_remote_process($commands, $this->server);
$this->backup_output = trim($this->backup_output);
@@ -479,34 +486,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
}
}
// private function upload_to_s3(): void
// {
// try {
// if (is_null($this->s3)) {
// return;
// }
// $key = $this->s3->key;
// $secret = $this->s3->secret;
// // $region = $this->s3->region;
// $bucket = $this->s3->bucket;
// $endpoint = $this->s3->endpoint;
// $this->s3->testConnection(shouldSave: true);
// $configName = new Cuid2;
// $s3_copy_dir = str($this->backup_location)->replace(backup_dir(), '/var/www/html/storage/app/backups/');
// $commands[] = "docker exec coolify bash -c 'mc config host add {$configName} {$endpoint} $key $secret'";
// $commands[] = "docker exec coolify bash -c 'mc cp $s3_copy_dir {$configName}/{$bucket}{$this->backup_dir}/'";
// instant_remote_process($commands, $this->server);
// $this->add_to_backup_output('Uploaded to S3.');
// } catch (\Throwable $e) {
// $this->add_to_backup_output($e->getMessage());
// throw $e;
// } finally {
// $removeConfigCommands[] = "docker exec coolify bash -c 'mc config remove {$configName}'";
// $removeConfigCommands[] = "docker exec coolify bash -c 'mc alias rm {$configName}'";
// instant_remote_process($removeConfigCommands, $this->server, false);
// }
// }
private function upload_to_s3(): void
{
try {
@@ -528,10 +507,27 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->ensureHelperImageAvailable();
$fullImageName = $this->getFullImageName();
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
if (isDev()) {
if ($this->database->name === 'coolify-db') {
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/coolify/coolify-db-'.$this->server->ip.$this->backup_file;
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
} else {
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name.$this->backup_file;
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
}
} else {
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
}
if ($this->s3->isHetzner()) {
$endpointWithoutBucket = 'https://'.str($endpoint)->after('https://')->after('.')->value();
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc alias set --path=off --api=S3v4 temporary {$endpointWithoutBucket} $key $secret";
} else {
$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);
$this->add_to_backup_output('Uploaded to S3.');
} catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage());
@@ -573,7 +569,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
private function getFullImageName(): string
{
$settings = InstanceSettings::get();
$settings = instanceSettings();
$helperImage = config('coolify.helper_image');
$latestVersion = $settings->helper_version;

View File

@@ -1,62 +0,0 @@
<?php
namespace App\Jobs;
use App\Models\ScheduledDatabaseBackup;
use App\Models\Team;
use App\Notifications\Database\DailyBackup;
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;
class DatabaseBackupStatusJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 1;
public function __construct() {}
public function handle()
{
// $teams = Team::all();
// foreach ($teams as $team) {
// $scheduled_backups = $team->scheduledDatabaseBackups()->get();
// if ($scheduled_backups->isEmpty()) {
// continue;
// }
// foreach ($scheduled_backups as $scheduled_backup) {
// $last_days_backups = $scheduled_backup->get_last_days_backup_status();
// if ($last_days_backups->isEmpty()) {
// continue;
// }
// $failed = $last_days_backups->where('status', 'failed');
// }
// }
// $scheduled_backups = ScheduledDatabaseBackup::all();
// $databases = collect();
// $teams = collect();
// foreach ($scheduled_backups as $scheduled_backup) {
// $last_days_backups = $scheduled_backup->get_last_days_backup_status();
// if ($last_days_backups->isEmpty()) {
// continue;
// }
// $failed = $last_days_backups->where('status', 'failed');
// $database = $scheduled_backup->database;
// $team = $database->team();
// $teams->put($team->id, $team);
// $databases->put("{$team->id}:{$database->name}", [
// 'failed_count' => $failed->count(),
// ]);
// }
// foreach ($databases as $name => $database) {
// [$team_id, $name] = explode(':', $name);
// $team = $teams->get($team_id);
// $team?->notify(new DailyBackup($databases));
// }
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Jobs;
use App\Actions\Application\StopApplication;
use App\Actions\Database\StopDatabase;
use App\Actions\Server\CleanupDocker;
use App\Actions\Service\DeleteService;
use App\Actions\Service\StopService;
use App\Models\Application;
@@ -30,8 +31,11 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
public function __construct(
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource,
public bool $deleteConfigurations = false,
public bool $deleteVolumes = false) {}
public bool $deleteConfigurations = true,
public bool $deleteVolumes = true,
public bool $dockerCleanup = true,
public bool $deleteConnectedNetworks = true
) {}
public function handle()
{
@@ -51,11 +55,11 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
case 'standalone-dragonfly':
case 'standalone-clickhouse':
$persistentStorages = $this->resource?->persistentStorages()?->get();
StopDatabase::run($this->resource);
StopDatabase::run($this->resource, true);
break;
case 'service':
StopService::run($this->resource);
DeleteService::run($this->resource);
StopService::run($this->resource, true);
DeleteService::run($this->resource, $this->deleteConfigurations, $this->deleteVolumes, $this->dockerCleanup, $this->deleteConnectedNetworks);
break;
}
@@ -65,12 +69,31 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
if ($this->deleteConfigurations) {
$this->resource?->delete_configurations();
}
$isDatabase = $this->resource instanceof StandalonePostgresql
|| $this->resource instanceof StandaloneRedis
|| $this->resource instanceof StandaloneMongodb
|| $this->resource instanceof StandaloneMysql
|| $this->resource instanceof StandaloneMariadb
|| $this->resource instanceof StandaloneKeydb
|| $this->resource instanceof StandaloneDragonfly
|| $this->resource instanceof StandaloneClickhouse;
$server = data_get($this->resource, 'server') ?? data_get($this->resource, 'destination.server');
if (($this->dockerCleanup || $isDatabase) && $server) {
CleanupDocker::dispatch($server, true);
}
if ($this->deleteConnectedNetworks && ! $isDatabase) {
$this->resource?->delete_connected_networks($this->resource->uuid);
}
} catch (\Throwable $e) {
ray($e->getMessage());
send_internal_notification('ContainerStoppingJob failed with: '.$e->getMessage());
throw $e;
} finally {
$this->resource->forceDelete();
if ($this->dockerCleanup) {
CleanupDocker::dispatch($server, true);
}
Artisan::queue('cleanup:stucked-resources');
}
}

View File

@@ -10,7 +10,6 @@ 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\Facades\Log;
@@ -24,17 +23,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
public ?string $usageBefore = null;
public function __construct(public Server $server) {}
public function middleware(): array
{
return [new WithoutOverlapping($this->server->id)];
}
public function uniqueId(): int
{
return $this->server->id;
}
public function __construct(public Server $server, public bool $manualCleanup = false) {}
public function handle(): void
{
@@ -42,8 +31,9 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
if (! $this->server->isFunctional()) {
return;
}
if ($this->server->settings->force_docker_cleanup) {
Log::info('DockerCleanupJob force cleanup on '.$this->server->name);
if ($this->manualCleanup || $this->server->settings->force_docker_cleanup) {
Log::info('DockerCleanupJob '.($this->manualCleanup ? 'manual' : 'force').' cleanup on '.$this->server->name);
CleanupDocker::run(server: $this->server);
return;

View File

@@ -8,7 +8,6 @@ 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\Facades\Http;
@@ -25,16 +24,6 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue
public function __construct(public GithubApp $github_app) {}
public function middleware(): array
{
return [(new WithoutOverlapping($this->github_app->uuid))];
}
public function uniqueId(): int
{
return $this->github_app->uuid;
}
public function handle()
{
try {

View File

@@ -2,14 +2,12 @@
namespace App\Jobs;
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\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
@@ -19,17 +17,7 @@ class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
public $timeout = 1000;
public function middleware(): array
{
return [(new WithoutOverlapping($this->server->uuid))];
}
public function uniqueId(): string
{
return $this->server->uuid;
}
public function __construct(public Server $server) {}
public function __construct() {}
public function handle(): void
{
@@ -37,13 +25,13 @@ class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
$versions = $response->json();
$settings = InstanceSettings::get();
$settings = instanceSettings();
$latest_version = data_get($versions, 'coolify.helper.version');
$current_version = $settings->helper_version;
if (version_compare($latest_version, $current_version, '>')) {
// New version available
$helperImage = config('coolify.helper_image');
instant_remote_process(["docker pull -q {$helperImage}:{$latest_version}"], $this->server);
// $helperImage = config('coolify.helper_image');
// instant_remote_process(["docker pull -q {$helperImage}:{$latest_version}"], $this->server);
$settings->update(['helper_version' => $latest_version]);
}
}

View File

@@ -9,7 +9,6 @@ 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;
class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
@@ -18,16 +17,6 @@ class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
public $timeout = 1000;
public function middleware(): array
{
return [(new WithoutOverlapping($this->server->uuid))];
}
public function uniqueId(): string
{
return $this->server->uuid;
}
public function __construct(public Server $server) {}
public function handle(): void

View File

@@ -13,7 +13,6 @@ use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
class ScheduledTaskJob implements ShouldQueue
@@ -56,24 +55,17 @@ class ScheduledTaskJob implements ShouldQueue
{
if ($this->resource instanceof Application) {
$timezone = $this->resource->destination->server->settings->server_timezone;
return $timezone;
} elseif ($this->resource instanceof Service) {
$timezone = $this->resource->server->settings->server_timezone;
return $timezone;
}
return 'UTC';
}
public function middleware(): array
{
return [new WithoutOverlapping($this->task->id)];
}
public function uniqueId(): int
{
return $this->task->id;
}
public function handle(): void
{
@@ -94,12 +86,12 @@ class ScheduledTaskJob implements ShouldQueue
} elseif ($this->resource->type() == 'service') {
$this->resource->applications()->get()->each(function ($application) {
if (str(data_get($application, 'status'))->contains('running')) {
$this->containers[] = data_get($application, 'name') . '-' . data_get($this->resource, 'uuid');
$this->containers[] = data_get($application, 'name').'-'.data_get($this->resource, 'uuid');
}
});
$this->resource->databases()->get()->each(function ($database) {
if (str(data_get($database, 'status'))->contains('running')) {
$this->containers[] = data_get($database, 'name') . '-' . data_get($this->resource, 'uuid');
$this->containers[] = data_get($database, 'name').'-'.data_get($this->resource, 'uuid');
}
});
}
@@ -112,8 +104,8 @@ class ScheduledTaskJob implements ShouldQueue
}
foreach ($this->containers as $containerName) {
if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container . '-' . $this->resource->uuid)) {
$cmd = "sh -c '" . str_replace("'", "'\''", $this->task->command) . "'";
if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container.'-'.$this->resource->uuid)) {
$cmd = "sh -c '".str_replace("'", "'\''", $this->task->command)."'";
$exec = "docker exec {$containerName} {$cmd}";
$this->task_output = instant_remote_process([$exec], $this->server, true);
$this->task_log->update([

View File

@@ -16,7 +16,6 @@ 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;
@@ -24,7 +23,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 3;
public $tries = 1;
public $timeout = 60;
@@ -45,16 +44,6 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
public function __construct(public Server $server) {}
public function middleware(): array
{
return [(new WithoutOverlapping($this->server->id))];
}
public function uniqueId(): int
{
return $this->server->id;
}
public function handle()
{
try {
@@ -80,7 +69,9 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
return 'No containers found.';
}
GetContainersStatus::run($this->server, $this->containers, $containerReplicates);
$this->checkLogDrainContainer();
if ($this->server->isLogDrainEnabled()) {
$this->checkLogDrainContainer();
}
}
} catch (\Throwable $e) {
@@ -93,7 +84,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
private function serverStatus()
{
['uptime' => $uptime] = $this->server->validateConnection();
['uptime' => $uptime] = $this->server->validateConnection(false);
if ($uptime) {
if ($this->server->unreachable_notification_sent === true) {
$this->server->update(['unreachable_notification_sent' => false]);
@@ -126,9 +117,6 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
private function checkLogDrainContainer()
{
if (! $this->server->isLogDrainEnabled()) {
return;
}
$foundLogDrainContainer = $this->containers->filter(function ($value, $key) {
return data_get($value, 'Name') === '/coolify-log-drain';
})->first();

View File

@@ -10,7 +10,6 @@ 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;
class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
@@ -26,16 +25,6 @@ class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
public function __construct(public Team $team) {}
public function middleware(): array
{
return [(new WithoutOverlapping($this->team->uuid))];
}
public function uniqueId(): int
{
return $this->team->uuid;
}
public function handle()
{
try {

View File

@@ -8,7 +8,6 @@ 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;
class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
@@ -26,16 +25,6 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
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()
{
if (! $this->server->isServerReady($this->tries)) {

View File

@@ -0,0 +1,59 @@
<?php
namespace App\Jobs;
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;
class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 1;
public $timeout = 60;
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 handle()
{
try {
if (! $this->server->isFunctional()) {
ray('Server is not ready.');
return 'Server is not ready.';
}
$team = $this->server->team;
$percentage = $this->server->storageCheck();
if ($percentage > 1) {
ray('Server storage is at '.$percentage.'%');
}
} catch (\Throwable $e) {
ray($e->getMessage());
return handleError($e);
}
}
}

View File

@@ -3,7 +3,6 @@
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;
@@ -23,7 +22,7 @@ class UpdateCoolifyJob implements ShouldBeEncrypted, ShouldQueue
{
try {
CheckForUpdatesJob::dispatchSync();
$settings = InstanceSettings::get();
$settings = instanceSettings();
if (! $settings->new_version_available) {
Log::info('No new version available. Skipping update.');

View File

@@ -141,7 +141,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
if (! $this->createdServer) {
return $this->dispatch('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.');
}
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
$this->serverPublicKey = $this->createdServer->privateKey->getPublicKey();
return $this->validateServer('localhost');
} elseif ($this->selectedServerType === 'remote') {
@@ -175,7 +175,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
return;
}
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
$this->serverPublicKey = $this->createdServer->privateKey->getPublicKey();
$this->updateServerDetails();
$this->currentState = 'validate-server';
}
@@ -231,17 +231,24 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
public function savePrivateKey()
{
$this->validate([
'privateKeyName' => 'required',
'privateKey' => 'required',
'privateKeyName' => 'required|string|max:255',
'privateKeyDescription' => 'nullable|string|max:255',
'privateKey' => 'required|string',
]);
$this->createdPrivateKey = PrivateKey::create([
'name' => $this->privateKeyName,
'description' => $this->privateKeyDescription,
'private_key' => $this->privateKey,
'team_id' => currentTeam()->id,
]);
$this->createdPrivateKey->save();
$this->currentState = 'create-server';
try {
$privateKey = PrivateKey::createAndStore([
'name' => $this->privateKeyName,
'description' => $this->privateKeyDescription,
'private_key' => $this->privateKey,
'team_id' => currentTeam()->id,
]);
$this->createdPrivateKey = $privateKey;
$this->currentState = 'create-server';
} catch (\Exception $e) {
$this->addError('privateKey', 'Failed to save private key: '.$e->getMessage());
}
}
public function saveServer()

View File

@@ -30,8 +30,7 @@ class Dashboard extends Component
public function cleanup_queue()
{
$this->dispatch('success', 'Cleanup started.');
Artisan::queue('cleanup:application-deployment-queue', [
Artisan::queue('cleanup:deployment-queue', [
'--team-id' => currentTeam()->id,
]);
}

View File

@@ -38,7 +38,7 @@ class Form extends Component
}
$this->destination->delete();
return redirect()->route('dashboard');
return redirect()->route('destination.all');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -47,7 +47,7 @@ class Help extends Component
]
);
$mail->subject("[HELP]: {$this->subject}");
$settings = \App\Models\InstanceSettings::get();
$settings = instanceSettings();
$type = set_transanctional_email_settings($settings);
if (! $type) {
$url = 'https://app.coolify.io/api/feedback';
@@ -61,6 +61,7 @@ class Help extends Component
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
}
$this->dispatch('success', 'Feedback sent.', 'We will get in touch with you as soon as possible.');
$this->reset('description', 'subject');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -2,13 +2,28 @@
namespace App\Livewire;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Livewire\Component;
class NavbarDeleteTeam extends Component
{
public function delete()
public $team;
public function mount()
{
$this->team = currentTeam()->name;
}
public function delete($password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
}
$currentTeam = currentTeam();
$currentTeam->delete();

View File

@@ -172,7 +172,7 @@ class Email extends Component
public function copyFromInstanceSettings()
{
$settings = \App\Models\InstanceSettings::get();
$settings = instanceSettings();
if ($settings->smtp_enabled) {
$team = currentTeam();
$team->update([

View File

@@ -4,7 +4,6 @@ namespace App\Livewire\Project\Application\Deployment;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use Illuminate\Support\Collection;
use Livewire\Component;
class Show extends Component

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Project\Application;
use App\Actions\Application\GenerateConfig;
use App\Models\Application;
use Illuminate\Support\Collection;
use Livewire\Component;
@@ -243,12 +244,19 @@ class General extends Component
public function updatedApplicationFqdn()
{
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
return str($domain)->trim()->lower();
});
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
try {
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
$this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) {
return str($domain)->trim()->lower();
});
$this->application->fqdn = $this->application->fqdn->unique()->implode(',');
$this->application->save();
} catch (\Throwable $e) {
$originalFqdn = $this->application->getOriginal('fqdn');
$this->application->fqdn = $originalFqdn;
return handleError($e, $this);
}
$this->resetDefaultLabels();
}
@@ -287,18 +295,22 @@ class General extends Component
public function resetDefaultLabels()
{
if ($this->application->settings->is_container_label_readonly_enabled) {
return;
try {
if ($this->application->settings->is_container_label_readonly_enabled) {
return;
}
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
$this->ports_exposes = $this->application->ports_exposes;
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
if ($this->application->build_pack === 'dockercompose') {
$this->loadComposeFile();
}
$this->dispatch('configurationChanged');
} catch (\Throwable $e) {
return handleError($e, $this);
}
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
$this->ports_exposes = $this->application->ports_exposes;
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();
if ($this->application->build_pack === 'dockercompose') {
$this->loadComposeFile();
}
$this->dispatch('configurationChanged');
}
public function checkFqdns($showToaster = true)
@@ -413,4 +425,16 @@ class General extends Component
$this->dispatch('configurationChanged');
}
}
public function downloadConfig()
{
$config = GenerateConfig::run($this->application, true);
$fileName = str($this->application->name)->slug()->append('_config.json');
return response()->streamDownload(function () use ($config) {
echo $config;
}, $fileName, [
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename=' . $fileName,
]);
}
}

View File

@@ -21,6 +21,8 @@ class Heading extends Component
protected string $deploymentUuid;
public bool $docker_cleanup = true;
public function getListeners()
{
$teamId = auth()->user()->currentTeam()->id;
@@ -102,7 +104,7 @@ class Heading extends Component
public function stop()
{
StopApplication::run($this->application);
StopApplication::run($this->application, false, $this->docker_cleanup);
$this->application->status = 'exited';
$this->application->save();
if ($this->application->additional_servers->count() > 0) {
@@ -135,4 +137,13 @@ class Heading extends Component
'environment_name' => $this->parameters['environment_name'],
]);
}
public function render()
{
return view('livewire.project.application.heading', [
'checkboxes' => [
['id' => 'docker_cleanup', 'label' => __('resource.docker_cleanup')],
],
]);
}
}

View File

@@ -5,7 +5,9 @@ namespace App\Livewire\Project\Application;
use App\Actions\Docker\GetContainersStatus;
use App\Models\Application;
use App\Models\ApplicationPreview;
use Illuminate\Process\InvokedProcess;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Process;
use Livewire\Component;
use Spatie\Url\Url;
use Visus\Cuid2\Cuid2;
@@ -184,17 +186,20 @@ class Previews extends Component
public function stop(int $pull_request_id)
{
try {
$server = $this->application->destination->server;
$timeout = 300;
if ($this->application->destination->server->isSwarm()) {
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
} else {
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
foreach ($containers as $container) {
$name = str_replace('/', '', $container['Names']);
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
}
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
$this->stopContainers($containers, $server, $timeout);
}
GetContainersStatus::dispatchSync($this->application->destination->server)->onQueue('high');
$this->dispatch('reloadWindow');
GetContainersStatus::run($server);
$this->application->refresh();
$this->dispatch('containerStatusUpdated');
$this->dispatch('success', 'Preview Deployment stopped.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -203,16 +208,21 @@ class Previews extends Component
public function delete(int $pull_request_id)
{
try {
$server = $this->application->destination->server;
$timeout = 300;
if ($this->application->destination->server->isSwarm()) {
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
} else {
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
foreach ($containers as $container) {
$name = str_replace('/', '', $container['Names']);
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
}
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
$this->stopContainers($containers, $server, $timeout);
}
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
ApplicationPreview::where('application_id', $this->application->id)
->where('pull_request_id', $pull_request_id)
->first()
->delete();
$this->application->refresh();
$this->dispatch('update_links');
$this->dispatch('success', 'Preview deleted.');
@@ -220,4 +230,49 @@ class Previews extends Component
return handleError($e, $this);
}
}
private function stopContainers(array $containers, $server, int $timeout)
{
$processes = [];
foreach ($containers as $container) {
$containerName = str_replace('/', '', $container['Names']);
$processes[$containerName] = $this->stopContainer($containerName, $timeout);
}
$startTime = time();
while (count($processes) > 0) {
$finishedProcesses = array_filter($processes, function ($process) {
return ! $process->running();
});
foreach (array_keys($finishedProcesses) as $containerName) {
unset($processes[$containerName]);
$this->removeContainer($containerName, $server);
}
if (time() - $startTime >= $timeout) {
$this->forceStopRemainingContainers(array_keys($processes), $server);
break;
}
usleep(100000);
}
}
private function stopContainer(string $containerName, int $timeout): InvokedProcess
{
return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
}
private function removeContainer(string $containerName, $server)
{
instant_remote_process(["docker rm -f $containerName"], $server, throwError: false);
}
private function forceStopRemainingContainers(array $containerNames, $server)
{
foreach ($containerNames as $containerName) {
instant_remote_process(["docker kill $containerName"], $server, throwError: false);
$this->removeContainer($containerName, $server);
}
}
}

View File

@@ -3,6 +3,8 @@
namespace App\Livewire\Project\Database;
use App\Models\ScheduledDatabaseBackup;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Livewire\Component;
use Spatie\Url\Url;
@@ -12,6 +14,12 @@ class BackupEdit extends Component
public $s3s;
public bool $delete_associated_backups_locally = false;
public bool $delete_associated_backups_s3 = false;
public bool $delete_associated_backups_sftp = false;
public ?string $status = null;
public array $parameters;
@@ -23,6 +31,7 @@ class BackupEdit extends Component
'backup.save_s3' => 'required|boolean',
'backup.s3_storage_id' => 'nullable|integer',
'backup.databases_to_backup' => 'nullable',
'backup.dump_all' => 'required|boolean',
];
protected $validationAttributes = [
@@ -32,6 +41,7 @@ class BackupEdit extends Component
'backup.save_s3' => 'Save to S3',
'backup.s3_storage_id' => 'S3 Storage',
'backup.databases_to_backup' => 'Databases to Backup',
'backup.dump_all' => 'Backup All Databases',
];
protected $messages = [
@@ -46,10 +56,24 @@ class BackupEdit extends Component
}
}
public function delete()
public function delete($password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
}
try {
if ($this->delete_associated_backups_locally) {
$this->deleteAssociatedBackupsLocally();
}
if ($this->delete_associated_backups_s3) {
$this->deleteAssociatedBackupsS3();
}
$this->backup->delete();
if ($this->backup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
$previousUrl = url()->previous();
$url = Url::fromString($previousUrl);
@@ -104,4 +128,66 @@ class BackupEdit extends Component
$this->dispatch('error', $e->getMessage());
}
}
public function deleteAssociatedBackupsLocally()
{
$executions = $this->backup->executions;
$backupFolder = null;
foreach ($executions as $execution) {
if ($this->backup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
$server = $this->backup->database->service->destination->server;
} else {
$server = $this->backup->database->destination->server;
}
if (! $backupFolder) {
$backupFolder = dirname($execution->filename);
}
delete_backup_locally($execution->filename, $server);
$execution->delete();
}
if ($backupFolder) {
$this->deleteEmptyBackupFolder($backupFolder, $server);
}
}
public function deleteAssociatedBackupsS3()
{
//Add function to delete backups from S3
}
public function deleteAssociatedBackupsSftp()
{
//Add function to delete backups from SFTP
}
private function deleteEmptyBackupFolder($folderPath, $server)
{
$checkEmpty = instant_remote_process(["[ -z \"$(ls -A '$folderPath')\" ] && echo 'empty' || echo 'not empty'"], $server);
if (trim($checkEmpty) === 'empty') {
instant_remote_process(["rmdir '$folderPath'"], $server);
$parentFolder = dirname($folderPath);
$checkParentEmpty = instant_remote_process(["[ -z \"$(ls -A '$parentFolder')\" ] && echo 'empty' || echo 'not empty'"], $server);
if (trim($checkParentEmpty) === 'empty') {
instant_remote_process(["rmdir '$parentFolder'"], $server);
}
}
}
public function render()
{
return view('livewire.project.database.backup-edit', [
'checkboxes' => [
['id' => 'delete_associated_backups_locally', 'label' => __('database.delete_backups_locally')],
// ['id' => 'delete_associated_backups_s3', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from the selected S3 Storage.']
// ['id' => 'delete_associated_backups_sftp', 'label' => 'All backups associated with this backup job from this database will be permanently deleted from the selected SFTP Storage.']
],
]);
}
}

View File

@@ -3,18 +3,28 @@
namespace App\Livewire\Project\Database;
use App\Models\ScheduledDatabaseBackup;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Livewire\Attributes\On;
use Livewire\Component;
class BackupExecutions extends Component
{
public ?ScheduledDatabaseBackup $backup = null;
public $database;
public $executions = [];
public $setDeletableBackup;
public $delete_backup_s3 = true;
public $delete_backup_sftp = true;
public function getListeners()
{
$userId = auth()->user()->id;
$userId = Auth::id();
return [
"echo-private:team.{$userId},BackupCreated" => 'refreshBackupExecutions',
@@ -31,19 +41,36 @@ class BackupExecutions extends Component
}
}
public function deleteBackup($exeuctionId)
#[On('deleteBackup')]
public function deleteBackup($executionId, $password)
{
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
}
$execution = $this->backup->executions()->where('id', $executionId)->first();
if (is_null($execution)) {
$this->dispatch('error', 'Backup execution not found.');
return;
}
if ($execution->scheduledDatabaseBackup->database->getMorphClass() === 'App\Models\ServiceDatabase') {
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->service->destination->server);
} else {
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
}
if ($this->delete_backup_s3) {
// Add logic to delete from S3
}
if ($this->delete_backup_sftp) {
// Add logic to delete from SFTP
}
$execution->delete();
$this->dispatch('success', 'Backup deleted.');
$this->refreshBackupExecutions();
@@ -82,16 +109,18 @@ class BackupExecutions extends Component
return $server;
}
}
return null;
}
public function getServerTimezone()
{
$server = $this->server();
if (!$server) {
if (! $server) {
return 'UTC';
}
$serverTimezone = $server->settings->server_timezone;
return $serverTimezone;
}
@@ -104,6 +133,17 @@ class BackupExecutions extends Component
} catch (\Exception $e) {
$dateObj->setTimezone(new \DateTimeZone('UTC'));
}
return $dateObj->format('Y-m-d H:i:s T');
}
public function render()
{
return view('livewire.project.database.backup-executions', [
'checkboxes' => [
['id' => 'delete_backup_s3', 'label' => 'Delete the selected backup permanently form S3 Storage'],
['id' => 'delete_backup_sftp', 'label' => 'Delete the selected backup permanently form SFTP Storage'],
],
]);
}
}

View File

@@ -56,7 +56,7 @@ class General extends Component
public function instantSaveAdvanced()
{
try {
if (!$this->server->isLogDrainEnabled()) {
if (! $this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
@@ -73,14 +73,14 @@ class General extends Component
public function instantSave()
{
try {
if ($this->database->is_public && !$this->database->public_port) {
if ($this->database->is_public && ! $this->database->public_port) {
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
if (! str($this->database->status)->startsWith('running')) {
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
@@ -95,7 +95,7 @@ class General extends Component
$this->db_url_public = $this->database->external_db_url;
$this->database->save();
} catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
$this->database->is_public = ! $this->database->is_public;
return handleError($e, $this);
}

View File

@@ -54,7 +54,7 @@ class General extends Component
public function instantSaveAdvanced()
{
try {
if (!$this->server->isLogDrainEnabled()) {
if (! $this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
@@ -88,14 +88,14 @@ class General extends Component
public function instantSave()
{
try {
if ($this->database->is_public && !$this->database->public_port) {
if ($this->database->is_public && ! $this->database->public_port) {
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
if (! str($this->database->status)->startsWith('running')) {
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
@@ -110,7 +110,7 @@ class General extends Component
$this->db_url_public = $this->database->external_db_url;
$this->database->save();
} catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
$this->database->is_public = ! $this->database->is_public;
return handleError($e, $this);
}

View File

@@ -14,6 +14,8 @@ class Heading extends Component
public array $parameters;
public $docker_cleanup = true;
public function getListeners()
{
$userId = auth()->user()->id;
@@ -54,7 +56,7 @@ class Heading extends Component
public function stop()
{
StopDatabase::run($this->database);
StopDatabase::run($this->database, false, $this->docker_cleanup);
$this->database->status = 'exited';
$this->database->save();
$this->check_status();
@@ -71,4 +73,13 @@ class Heading extends Component
$activity = StartDatabase::run($this->database);
$this->dispatch('activityMonitor', $activity->id);
}
public function render()
{
return view('livewire.project.database.heading', [
'checkboxes' => [
['id' => 'docker_cleanup', 'label' => __('resource.docker_cleanup')],
],
]);
}
}

View File

@@ -57,7 +57,7 @@ class General extends Component
public function instantSaveAdvanced()
{
try {
if (!$this->server->isLogDrainEnabled()) {
if (! $this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
@@ -94,14 +94,14 @@ class General extends Component
public function instantSave()
{
try {
if ($this->database->is_public && !$this->database->public_port) {
if ($this->database->is_public && ! $this->database->public_port) {
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
if (! str($this->database->status)->startsWith('running')) {
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
@@ -116,7 +116,7 @@ class General extends Component
$this->db_url_public = $this->database->external_db_url;
$this->database->save();
} catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
$this->database->is_public = ! $this->database->is_public;
return handleError($e, $this);
}

View File

@@ -63,7 +63,7 @@ class General extends Component
public function instantSaveAdvanced()
{
try {
if (!$this->server->isLogDrainEnabled()) {
if (! $this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
@@ -100,14 +100,14 @@ class General extends Component
public function instantSave()
{
try {
if ($this->database->is_public && !$this->database->public_port) {
if ($this->database->is_public && ! $this->database->public_port) {
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
if (! str($this->database->status)->startsWith('running')) {
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
@@ -122,7 +122,7 @@ class General extends Component
$this->db_url_public = $this->database->external_db_url;
$this->database->save();
} catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
$this->database->is_public = ! $this->database->is_public;
return handleError($e, $this);
}

View File

@@ -61,7 +61,7 @@ class General extends Component
public function instantSaveAdvanced()
{
try {
if (!$this->server->isLogDrainEnabled()) {
if (! $this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
@@ -101,14 +101,14 @@ class General extends Component
public function instantSave()
{
try {
if ($this->database->is_public && !$this->database->public_port) {
if ($this->database->is_public && ! $this->database->public_port) {
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
if (! str($this->database->status)->startsWith('running')) {
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
@@ -123,7 +123,7 @@ class General extends Component
$this->db_url_public = $this->database->external_db_url;
$this->database->save();
} catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
$this->database->is_public = ! $this->database->is_public;
return handleError($e, $this);
}

View File

@@ -62,7 +62,7 @@ class General extends Component
public function instantSaveAdvanced()
{
try {
if (!$this->server->isLogDrainEnabled()) {
if (! $this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
@@ -99,14 +99,14 @@ class General extends Component
public function instantSave()
{
try {
if ($this->database->is_public && !$this->database->public_port) {
if ($this->database->is_public && ! $this->database->public_port) {
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
if (! str($this->database->status)->startsWith('running')) {
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
@@ -121,7 +121,7 @@ class General extends Component
$this->db_url_public = $this->database->external_db_url;
$this->database->save();
} catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
$this->database->is_public = ! $this->database->is_public;
return handleError($e, $this);
}

View File

@@ -57,7 +57,7 @@ class General extends Component
public function instantSaveAdvanced()
{
try {
if (!$this->server->isLogDrainEnabled()) {
if (! $this->server->isLogDrainEnabled()) {
$this->database->is_log_drain_enabled = false;
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
@@ -88,14 +88,14 @@ class General extends Component
public function instantSave()
{
try {
if ($this->database->is_public && !$this->database->public_port) {
if ($this->database->is_public && ! $this->database->public_port) {
$this->dispatch('error', 'Public port is required.');
$this->database->is_public = false;
return;
}
if ($this->database->is_public) {
if (!str($this->database->status)->startsWith('running')) {
if (! str($this->database->status)->startsWith('running')) {
$this->dispatch('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
@@ -110,7 +110,7 @@ class General extends Component
$this->db_url_public = $this->database->external_db_url;
$this->database->save();
} catch (\Throwable $e) {
$this->database->is_public = !$this->database->is_public;
$this->database->is_public = ! $this->database->is_public;
return handleError($e, $this);
}

View File

@@ -26,7 +26,7 @@ class ScheduledBackups extends Component
public function mount(): void
{
if ($this->selectedBackupId) {
$this->setSelectedBackup($this->selectedBackupId);
$this->setSelectedBackup($this->selectedBackupId, true);
}
$this->parameters = get_route_parameters();
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
@@ -37,10 +37,13 @@ class ScheduledBackups extends Component
$this->s3s = currentTeam()->s3s;
}
public function setSelectedBackup($backupId)
public function setSelectedBackup($backupId, $force = false)
{
if ($this->selectedBackupId === $backupId && ! $force) {
return;
}
$this->selectedBackupId = $backupId;
$this->selectedBackup = $this->database->scheduledBackups->find($this->selectedBackupId);
$this->selectedBackup = $this->database->scheduledBackups->find($backupId);
if (is_null($this->selectedBackup)) {
$this->selectedBackupId = null;
}

View File

@@ -13,9 +13,12 @@ class DeleteEnvironment extends Component
public bool $disabled = false;
public string $environmentName = '';
public function mount()
{
$this->parameters = get_route_parameters();
$this->environmentName = Environment::findOrFail($this->environment_id)->name;
}
public function delete()

View File

@@ -13,9 +13,12 @@ class DeleteProject extends Component
public bool $disabled = false;
public string $projectName = '';
public function mount()
{
$this->parameters = get_route_parameters();
$this->projectName = Project::findOrFail($this->project_id)->name;
}
public function delete()

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