Compare commits

...

171 Commits

Author SHA1 Message Date
Andras Bacsai
ce3b2de5e7 Merge pull request #2656 from coollabsio/next
v4.0.0-beta.300
2024-06-24 15:40:01 +02:00
Andras Bacsai
1782f59a96 fix: MB is % lol 2024-06-24 15:38:37 +02:00
Andras Bacsai
3612096b56 Merge pull request #2655 from coollabsio/next
v4.0.0-beta.299
2024-06-24 15:22:31 +02:00
Andras Bacsai
97aa6139ea fix: get envs before sortby 2024-06-24 15:20:52 +02:00
Andras Bacsai
e5d915a7a9 chore: Move server delete component to the bottom of the page 2024-06-24 14:55:25 +02:00
Andras Bacsai
5bc31c305c chore: Update version to 4.0.0-beta.299 2024-06-24 14:48:18 +02:00
Andras Bacsai
8c7590a249 fix: remove zoom from modals 2024-06-24 14:47:55 +02:00
Andras Bacsai
d54d524cef Update blacksmith logo size in README.md 2024-06-24 14:47:46 +02:00
Andras Bacsai
a3d3ada500 fix: app deployment should be in high queue 2024-06-24 14:47:39 +02:00
Andras Bacsai
ca43d197f9 Merge pull request #2469 from coollabsio/next
v4.0.0-beta.298
2024-06-24 12:14:20 +02:00
Andras Bacsai
73692a0c73 fix: remove cloud stripe notifications 2024-06-24 11:52:12 +02:00
Andras Bacsai
50191221b9 feat: Update statusnook logo filename in compose template 2024-06-24 11:52:03 +02:00
Andras Bacsai
61236b45fb Merge pull request #2624 from goksan/statusnook-template
Statusnook template
2024-06-24 11:46:31 +02:00
Andras Bacsai
837545f4e7 Merge pull request #2625 from LEstradioto/fix-stripprefix-http
fix: stripprefix middleware correctly labeled to http
2024-06-24 11:44:02 +02:00
Andras Bacsai
31810477b2 Merge branch 'next' into fix-stripprefix-http 2024-06-24 11:42:35 +02:00
Andras Bacsai
4c652b5818 Merge pull request #2629 from Thijmen/instance-name
Ability to give a name to an instance
2024-06-24 11:23:41 +02:00
Andras Bacsai
5201818f52 fix: monaco editor
fix: apex charts
2024-06-24 11:21:39 +02:00
Thijmen Stavenuiter
7e9e333d24 Refactor code 2024-06-24 09:17:16 +00:00
Thijmen Stavenuiter
ab3b72bd1f Only set name if it is actually set in the settings 2024-06-24 09:16:47 +00:00
Andras Bacsai
f2c8a6bac5 Merge pull request #2644 from MMTE/feat/monaco-editor
[Improvement]: Add code editor to the Dockerfile and Docker Composer UI
2024-06-24 10:17:41 +02:00
Andras Bacsai
758fab9976 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-24 09:57:42 +02:00
Andras Bacsai
3c2b985769 feat: Add blacksmith logo to donations section 2024-06-24 09:57:39 +02:00
Mahdi Taleghani
73bc07c7fb Merge branch 'next' into feat/monaco-editor 2024-06-23 22:50:25 +03:30
MMTE
8298aa7124 replace monaco-editor with simple textarea as example in Dockerfile and Docker-Compose syntax 2024-06-23 22:21:28 +03:30
MMTE
92049cba92 add monaco-editor as option to textarea component 2024-06-23 22:13:50 +03:30
MMTE
83d8963051 add MonacoEditor Component 2024-06-23 22:03:22 +03:30
Andras Bacsai
bedcf6c058 chore: Update latest release version badge in README.md 2024-06-23 17:58:01 +02:00
Andras Bacsai
f8b7c17efd chore: Update README.md with latest release version badge 2024-06-23 17:55:07 +02:00
Andras Bacsai
76ba365325 fix: slash in env names
ui: placement of env switcher
2024-06-23 17:47:58 +02:00
Andras Bacsai
a3255f3ab0 fix: refresh deployable compose without reload 2024-06-22 13:33:22 +02:00
Andras Bacsai
44af75613f fix: update compose environment with UI defined variables 2024-06-22 13:33:13 +02:00
Andras Bacsai
df53e8beda refactor: Update docker-compose generation to use multi-line literal block 2024-06-22 13:32:45 +02:00
Andras Bacsai
003f97af24 fix: you can now add env variable from ui to services 2024-06-22 12:55:26 +02:00
Thijmen
c897aaafa7 Fix styling 2024-06-22 08:41:55 +00:00
Thijmen Stavenuiter
76a0659335 Improved name creation 2024-06-22 10:41:15 +02:00
Thijmen Stavenuiter
4acdd8df12 Add null check 2024-06-22 10:30:52 +02:00
Thijmen
cdabdb4558 Fix styling 2024-06-22 08:23:50 +00:00
Thijmen Stavenuiter
f43fc1e376 Linting 2024-06-22 10:23:02 +02:00
Thijmen Stavenuiter
70cd5d364c Add ability to give a name to an instance 2024-06-22 10:22:57 +02:00
LEstradioto
62aa807d0f Fix styling 2024-06-21 20:09:54 +00:00
Luan Estradioto
5f3fed3c8f fix: stripprefix middleware correctly labeled to http 2024-06-21 16:59:59 -03:00
Goksan Kadir
f95e879a58 statusnook template 2024-06-21 20:49:38 +01:00
Andras Bacsai
34508a2fd1 feat: Add API endpoint to update application by UUID 2024-06-21 21:35:02 +02:00
Andras Bacsai
cd85094113 feat: more api endpoints 2024-06-21 16:46:13 +02:00
Andras Bacsai
72033279c2 update servicetemplates 2024-06-21 15:11:40 +02:00
Andras Bacsai
9919224226 Merge pull request #2602 from ryseek/anon_key_fix
fix: Supabase realtime service health check gets 403
2024-06-21 15:11:22 +02:00
Andras Bacsai
c6a1eac586 Merge pull request #2597 from addvanced/bugfix/oauth-login-creates-user
fix: OAuth Login creates new user and ignores "Registration Allowed"-setting
2024-06-21 15:09:39 +02:00
Andras Bacsai
b657aa33fa update servicetemplates 2024-06-21 15:07:10 +02:00
Andras Bacsai
834562190e Merge pull request #2613 from martonsz/fix-authentik-pg-healthcheck
Fix healthcheck for Postgres in Authentic service
2024-06-21 15:06:35 +02:00
Andras Bacsai
453b28baf7 fix: make server charts one livewire component with one interval selector 2024-06-21 14:49:13 +02:00
Andras Bacsai
28522418ff refactor: Update gitCommitLink method to handle null values in source.html_url 2024-06-21 14:39:22 +02:00
Andras Bacsai
18bab41605 feat: preselect server and destination if only one found 2024-06-21 14:35:05 +02:00
Andras Bacsai
c17079e045 refactor: Update dashboard.blade.php to use project's default environment for redirection 2024-06-21 14:34:54 +02:00
Andras Bacsai
f5cea7d9e3 refactor: Update profile index view to display 2FA QR code in a centered container 2024-06-21 13:56:07 +02:00
Andras Bacsai
c5083ea897 feat: preselect prod or first env when selecting a project
feat: quickly switch between environments
2024-06-21 13:54:13 +02:00
Andras Bacsai
f607aa1233 feat: Add metrics warning for servers without Sentinel enabled 2024-06-21 11:20:35 +02:00
Andras Bacsai
db39458295 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-21 09:52:57 +02:00
Andras Bacsai
3d75e0773f chore: Add Treive logo to donations section 2024-06-21 09:52:54 +02:00
Márton Szücs
bdcb467208 Fix healthcheck for Postgres in Authentic compose stack
Postgres would never get in healthy state because the environment variable SERVICE_USER_POSTGRESQL was never set inside the container.
Using POSTGRES_USER container env instead which receives the value from SERVICE_USER_POSTGRESQL.

Also adding the condition "service_healthy" for the depends_on. This will make the Authentic containers to wait for Postgres and Redis to be healthy before starting.
2024-06-20 19:45:06 +02:00
andrasbacsai
575a789d1d Fix styling 2024-06-20 12:52:57 +00:00
Andras Bacsai
fcb3d71cb4 feat: add high priority queue 2024-06-20 14:52:12 +02:00
Andras Bacsai
93c890ce41 refactor: Update Project/Show component to sort environments by created_at 2024-06-20 14:24:07 +02:00
Andras Bacsai
2421f7c35c refactor: Update Application model to include getDomainsByUuid method 2024-06-20 14:07:24 +02:00
Andras Bacsai
44920944db Merge pull request #2595 from samirimtiaz1996/main
add endpoints for filtering applications by domain and managing conta…
2024-06-20 14:07:01 +02:00
Andras Bacsai
1b135be3c5 Merge branch 'next' into main 2024-06-20 14:04:51 +02:00
Andras Bacsai
fff7ec9ba7 refactor: remove commented code for docker container removal 2024-06-20 13:54:15 +02:00
Andras Bacsai
0468f255e7 fix: static build with new nixpacks build process 2024-06-20 13:48:49 +02:00
Andras Bacsai
5da5158b14 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-20 13:30:20 +02:00
Andras Bacsai
6493ce3fe0 refactor: update container name assignment in Application model 2024-06-20 13:30:17 +02:00
andrasbacsai
078772495a Fix styling 2024-06-20 11:17:53 +00:00
Andras Bacsai
c81ad5cd03 feat: container metrics 2024-06-20 13:17:06 +02:00
Andras Bacsai
439bee1203 refactor: update shared.php to use correct key for retrieving sentinel version 2024-06-20 10:50:49 +02:00
Andras Bacsai
0eccbf64f4 remove sentinel from coolify versions 2024-06-20 10:44:31 +02:00
Ruslan Suleimanov
5232ce6e52 use the right key 2024-06-19 20:54:52 +02:00
Andras Bacsai
ada278d8da Merge pull request #2591 from coollabsio/dependabot/npm_and_yarn/braces-3.0.3
chore(deps): bump braces from 3.0.2 to 3.0.3
2024-06-19 16:21:55 +02:00
Andras Bacsai
24d5fef20f Merge pull request #2593 from victorlap/allow-changing-repository
Allow changing git repository
2024-06-19 16:20:53 +02:00
Andras Bacsai
b7fd1b9f4c Merge pull request #2553 from alexzvn/main
fix: application custom labels reset after saving
2024-06-19 16:14:07 +02:00
Andras Bacsai
b16cd1ec5c Merge pull request #2519 from notjulian/main
chore: add Italian translations
2024-06-19 16:13:04 +02:00
Andras Bacsai
278ec88dd0 Merge pull request #2481 from toohard2explain/add-german-translation
chore: add German translations
2024-06-19 16:12:34 +02:00
Andras Bacsai
b91699f317 Merge pull request #2409 from eltociear/add-japanese
Add Japanese language support
2024-06-19 16:12:00 +02:00
Andras Bacsai
105ed56dfc Merge pull request #2437 from ndbiaw/next
chore: add Vietnamese translate
2024-06-19 16:11:05 +02:00
Andras Bacsai
f9267a96ed Merge pull request #2424 from alwalxed/main
🌐 Add Arabic Language Support
2024-06-19 16:10:37 +02:00
Andras Bacsai
1135f45dc9 Merge pull request #2441 from florianguigue/main
🇫🇷 Add french translations
2024-06-19 16:10:15 +02:00
Andras Bacsai
b74a75f4c6 Merge pull request #2460 from muhammedaksam/main
chore: add Turkish translations
2024-06-19 16:09:50 +02:00
Andras Bacsai
8a787f48b3 Merge pull request #2456 from lopesboa/chore/add-pt-translation
chore: add Portuguese translation
2024-06-19 16:09:28 +02:00
Andras Bacsai
e956642982 chore: Update Spanish translation for failed authentication messages 2024-06-19 16:09:00 +02:00
Andras Bacsai
d3d08168de Merge pull request #2408 from buttercubz/es-translation
Add spanish language support
2024-06-19 16:08:01 +02:00
Andras Bacsai
919f56a292 Merge branch 'next' into es-translation 2024-06-19 16:07:06 +02:00
Kenneth Thomsen
b5be17c2d2 Fixed OAuth signin bug, ignoring the 'Allow Registration'-setting and registers a new user, even when the setting is disabled 2024-06-19 13:36:00 +02:00
Andras Bacsai
5221aa0ab7 chore: Update sentinel version to 0.0.9 2024-06-19 13:29:01 +02:00
Andras Bacsai
49ad8b7cc1 loadign charts 2024-06-19 10:37:01 +02:00
Andras Bacsai
fa99cbce4a do not load charts on mount 2024-06-19 10:33:34 +02:00
Andras Bacsai
07e55b2636 chore: Update chart styling and loading text 2024-06-19 09:42:44 +02:00
Andras Bacsai
d8984c42b5 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-19 09:30:58 +02:00
Andras Bacsai
36f251e710 fix: charts 2024-06-19 09:30:56 +02:00
samirimtiaz1996
f3beacdc3f Fix styling 2024-06-19 07:07:49 +00:00
samirimtiaz1996
e023be1f25 Update api.php 2024-06-19 13:07:13 +06:00
andrasbacsai
d1a5f97f59 Fix styling 2024-06-19 06:59:46 +00:00
Andras Bacsai
a43de75b42 fix: metrics parsing 2024-06-19 08:58:57 +02:00
samirimtiaz1996
5530f7263f Update Domains.php 2024-06-19 01:35:33 +06:00
samirimtiaz1996
d688244664 add endpoints for filtering applications by domain and managing container labels
- Add /api/v1/domains?uuid={application_uuid} endpoint (GET) to filter applications by domains for a given project UUID
- Add /api/v1/domains endpoint (PUT) to update domains and regenerate container labels
- Add /api/v1/domains endpoint (DELETE) to delete domains and regenerate container labels
2024-06-19 00:59:39 +06:00
Andras Bacsai
c8155c8a32 chore: Update sentinel version to 0.0.8 2024-06-18 16:52:05 +02:00
andrasbacsai
ce15f8f1dd Fix styling 2024-06-18 14:43:18 +00:00
Andras Bacsai
23ed697b98 feat: sentinel + charts 2024-06-18 16:42:42 +02:00
Victor Lap
55854653b0 Allow changing git repository 2024-06-18 16:36:46 +02:00
Andras Bacsai
83983bbb32 fix: remove sentinel variable
fix: metrics are disabled by default
2024-06-18 13:01:23 +02:00
Andras Bacsai
768c917a0e chore: Update service template URL in constants.php 2024-06-18 12:55:37 +02:00
dependabot[bot]
039df94b86 chore(deps): bump braces from 3.0.2 to 3.0.3
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-18 10:53:27 +00:00
Andras Bacsai
f46cb7a670 chore: Update tailwindcss and vue versions in package.json 2024-06-18 12:52:17 +02:00
Andras Bacsai
9d05276f94 remove: unnecessary notification 2024-06-18 12:47:55 +02:00
andrasbacsai
dc4916dc19 Fix styling 2024-06-17 12:22:17 +00:00
Andras Bacsai
1d0a1ab16a feat: charts 2024-06-17 14:21:27 +02:00
Andras Bacsai
1ae6106782 refactor: Update README.md with new logos and fix styling 2024-06-17 10:20:02 +02:00
Alexzvn
af38d0cc07 fix: application custom labels reset after saving 2024-06-15 00:01:58 +00:00
Andras Bacsai
5ec2cbdf19 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-14 14:48:14 +02:00
Andras Bacsai
f6f44d8e8f fix: show commit message on webhooks + prs 2024-06-14 14:48:12 +02:00
andrasbacsai
2b6fc16637 Fix styling 2024-06-14 12:24:28 +00:00
Andras Bacsai
e0a2e3bd0c Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-14 14:23:42 +02:00
Andras Bacsai
1ecd0307ed feat: COOLIFY_CONTAINER_NAME predefined variable 2024-06-14 14:23:40 +02:00
andrasbacsai
f10f3456d7 Fix styling 2024-06-14 12:10:40 +00:00
Andras Bacsai
b17be37aee fix: db proxy status shown better in the UI 2024-06-14 14:09:56 +02:00
Muhammed Mustafa Akşam
ddbef494e3 Merge branch 'next' into main 2024-06-14 12:48:18 +03:00
Andras Bacsai
49d91c498e refactor: Update image sizes and add new logos to README.md 2024-06-14 09:50:16 +02:00
Muhammed Mustafa Akşam
ef1d06b8dc Merge branch 'next' into main 2024-06-13 20:57:33 +03:00
Andras Bacsai
f1562ccfd5 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-13 14:48:26 +02:00
Andras Bacsai
bfe2fb6da8 refactor: Update Docker build commands for better performance and flexibility 2024-06-13 14:48:23 +02:00
Muhammed Mustafa Akşam
6f23f6352d Merge branch 'next' into main 2024-06-13 15:18:17 +03:00
Levi
a47816f45d Update lang/de.json
Co-authored-by: Marco <59606979+marcomaiermm@users.noreply.github.com>
2024-06-13 13:56:27 +02:00
Levi
30ac392af4 Update lang/de.json
Co-authored-by: Marco <59606979+marcomaiermm@users.noreply.github.com>
2024-06-13 13:56:19 +02:00
Andras Bacsai
e4ee149085 refactor: Remove debug code for saving environment variables 2024-06-13 13:37:19 +02:00
andrasbacsai
a521d8549a Fix styling 2024-06-13 11:15:09 +00:00
Andras Bacsai
566faba6e3 fix: handle laravel deployment better 2024-06-13 13:14:24 +02:00
Andras Bacsai
e4e9de0a53 refactor: Update text color for stderr output in deployment show view 2024-06-13 12:51:55 +02:00
Andras Bacsai
07ae971ae1 feat: Add Tigris logo to other/logos directory 2024-06-13 12:29:57 +02:00
Andras Bacsai
4f5102b2dc Merge pull request #2494 from rishikesh2003/fix/add-zorin-os-type
fixadd zorin as a os type
2024-06-13 12:28:19 +02:00
Andras Bacsai
04d1915121 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-13 12:25:13 +02:00
Andras Bacsai
c35f6e926d refactor: update text color for stderr output in deployment show view 2024-06-13 12:25:10 +02:00
andrasbacsai
dffcee6cf1 Fix styling 2024-06-13 10:03:36 +00:00
Andras Bacsai
7485c1240b feat: nixpacks now could reach local dbs internally 2024-06-13 12:02:52 +02:00
Andras Bacsai
95d3ebdc2d fix: in services should edit compose file for volumes and envs 2024-06-13 10:18:35 +02:00
Julian
6bb565ee67 Create it.json
chore: add Italian translations
2024-06-13 08:30:41 +01:00
Rishikesh S
19b38074a5 add zorin as a os type 2024-06-12 19:04:00 +05:30
Levi
25649f578d Create de.json 2024-06-12 13:33:06 +02:00
Andras Bacsai
7a63a17b66 feat: add supaguide logo to donations section 2024-06-12 12:30:59 +02:00
Andras Bacsai
3424cb1f29 Merge pull request #2474 from arthurauffray/patch-1
Simple README changes
2024-06-12 12:29:56 +02:00
andrasbacsai
5c8277ea1d Fix styling 2024-06-12 10:28:52 +00:00
Andras Bacsai
9e3ffea22c Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-12 12:28:11 +02:00
Andras Bacsai
9592076d45 fix: do no truncate repositories wtih domain (git) in it 2024-06-12 12:28:09 +02:00
andrasbacsai
2e01665340 Fix styling 2024-06-12 10:21:47 +00:00
Andras Bacsai
bba0ef522c Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-12 12:21:01 +02:00
Andras Bacsai
019cdd2b3a fix: compose generator 2024-06-12 12:20:58 +02:00
andrasbacsai
ce24352974 Fix styling 2024-06-12 10:05:54 +00:00
Andras Bacsai
407b8de2b1 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-12 12:05:11 +02:00
Andras Bacsai
f332a73122 feat: cancelling a deployment will check if new could be started. 2024-06-12 12:05:08 +02:00
andrasbacsai
2335abac91 Fix styling 2024-06-12 09:35:55 +00:00
Andras Bacsai
5cf6804615 Merge branch 'next' of github.com:coollabsio/coolify into next 2024-06-12 11:35:10 +02:00
Andras Bacsai
58b04b5fc8 fix: bitbucket link 2024-06-12 11:35:07 +02:00
andrasbacsai
c58f468dc9 Fix styling 2024-06-12 09:31:14 +00:00
Andras Bacsai
e922bc207a chore: Update dependencies and remove unused code 2024-06-12 11:30:25 +02:00
arthur
784cfb8fba Updates to README: grammar, sentence structure, URL formats
- Correct grammar mistakes
- Updated URL markdown formatting to be uniform throughout (hidden protocol before URL on coolify.io domain)
- Converted plain text links to markdown URLs
2024-06-12 21:04:12 +12:00
Andras Bacsai
b53bb44e42 chore: switch to database sessions from redis 2024-06-12 09:56:13 +02:00
Muhammed Mustafa Akşam
1876e80094 chore: add Turkish translations 2024-06-11 22:46:02 +03:00
Andras Bacsai
d20b3a5b8b Merge pull request #2458 from scflode/scflode-typos
Fix some double negation typos
2024-06-11 19:11:13 +02:00
Andras Bacsai
6d9454b351 chore: Update version numbers to 4.0.0-beta.298 2024-06-11 19:05:37 +02:00
Flo Schuessel
a138bb61bc Update logs.blade.php 2024-06-11 17:35:26 +02:00
lopesboa
85d313a791 chore: add portuguese traslation 2024-06-11 12:21:03 -03:00
Flow
e282686f97 Add french translation 2024-06-11 01:35:26 +02:00
Ling
f4904047b5 chore: add Vietnamese translate 2024-06-11 03:36:11 +07:00
systematicRealm
665cd454ef 🌐 ADD: Arabic Language Support 2024-06-10 13:23:18 +03:00
Ikko Eltociear Ashimine
55e2e29696 Add Japanese language support 2024-06-10 02:50:10 +09:00
buttercubz
b60f8df17a feat: spanish translation 2024-06-09 12:14:55 -04:00
181 changed files with 3279 additions and 694 deletions

View File

@@ -1,17 +1,21 @@
![Latest Release Version](https://img.shields.io/badge/dynamic/json?labelColor=grey&color=6366f1&label=Latest_released_version&url=https%3A%2F%2Fcdn.coollabs.io%2Fcoolify%2Fversions.json&query=coolify.v4.version&style=for-the-badge
)
[![Bounty Issues](https://img.shields.io/static/v1?labelColor=grey&color=6366f1&label=Algora&message=%F0%9F%92%8E+Bounty+issues&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties/new)
[![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcoollabsio%2Fbounties%3Fstatus%3Dopen&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties?status=open)
[![Rewarded Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Fcoollabsio%2Fbounties%3Fstatus%3Dcompleted&style=for-the-badge)](https://console.algora.io/org/coollabsio/bounties?status=completed)
# About the Project
Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc.
It helps you to manage your servers, applications, databases on your own hardware, all you need is SSH connection. You can manage VPS, Bare Metal, Raspberry PI's anything.
It helps you manage your servers, applications, and databases on your own hardware; you only need an SSH connection. You can manage VPS, Bare Metal, Raspberry PIs, and anything else.
Imagine if you could have the ease of a cloud but with your own servers. That is **Coolify**.
Imagine having the ease of a cloud but with your own servers. That is **Coolify**.
No vendor lock-in, which means that all the configuration for your applications/databases/etc are saved to your server. So if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You just lose the automations and all the magic. 🪄️
No vendor lock-in, which means that all the configurations for your applications/databases/etc are saved to your server. So, if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You lose the automations and all the magic. 🪄️
For more information, take a look at our landing page [here](https://coolify.io).
For more information, take a look at our landing page at [coolify.io](https://coolify.io).
# Installation
@@ -22,36 +26,42 @@ You can find the installation script source [here](./scripts/install.sh).
# Support
Contact us [here](https://coolify.io/docs/contact).
Contact us at [coolify.io/docs/contact](https://coolify.io/docs/contact).
# Donations
To stay completely free, open-source, no feature behind paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the future development of the project.
To stay completely free and open-source, with no feature behind the paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the project's future development.
https://coolify.io/sponsorships
[coolify.io/sponsorships](https://coolify.io/sponsorships)
Thank you so much!
Special thanks to our biggest sponsors!
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
<a href="http://htznr.li/CoolifyXHetzner" target="_blank"><img src="./other/logos/hetzner.jpg" alt="hetzner logo" width="200"/></a>
<a href="https://logto.io/?ref=coolify" target="_blank"><img src="./other/logos/logto.webp" alt="logto logo" width="200"/></a>
<a href="https://bc.direct/?utm_source=coolify.io" target="_blank"><img src="./other/logos/bc.png" alt="bc direct logo" width="200"/></a>
<a href="https://www.quantcdn.io/?utm_source=coolify.io" target="_blank"><img src="./other/logos/quant.svg" alt="quantcdn logo" width="200"/></a>
<a href="https://arcjet.com/?utm_source=coolify.io" target="_blank"><img src="./other/logos/arcjet.svg" alt="arcjet logo" width="200"/></a>
<a href="http://htznr.li/CoolifyXHetzner" target="_blank"><img src="./other/logos/hetzner.jpg" alt="hetzner logo" width="150"/></a>
<a href="https://logto.io/?ref=coolify" target="_blank"><img src="./other/logos/logto.webp" alt="logto logo" width="150"/></a>
<a href="https://bc.direct/?ref=coolify.io" target="_blank"><img src="./other/logos/bc.png" alt="bc direct logo" width="200"/></a>
<a href="https://www.quantcdn.io/?ref=coolify.io" target="_blank"><img src="./other/logos/quant.svg" alt="quantcdn logo" width="150"/></a>
<a href="https://arcjet.com/?ref=coolify.io" target="_blank"><img src="./other/logos/arcjet.svg" alt="arcjet logo" width="200"/></a>
<a href="https://supa.guide/?ref=coolify.io" target="_blank"><img src="./other/logos/supaguide.png" alt="supaguide logo" width="200"/></a>
<a href="https://tigrisdata.com/?ref=coolify.io" target="_blank"><img src="./other/logos/tigris.svg" alt="tigris logo" width="140"/></a>
<a href="https://fractalnetworks.co/?ref=coolify.io" target="_blank"><img src="./other/logos/fractal.svg" alt="fractal logo" width="180"/></a>
<a href="https://coolify.ad.vin/?ref=coolify.io" target="_blank"><img src="./other/logos/advin.png" alt="advin logo" width="250"/></a>
<a href="https://trieve.ai/?ref=coolify.io" target="_blank"><img src="./other/logos/trieve_bg.png" alt="trieve logo" width="180"/></a>
<a href="https://blacksmith.sh/?ref=coolify.io" target="_blank"><img src="./other/logos/blacksmith.svg" alt="blacksmith logo" width="200"/></a>
## Github Sponsors ($40+)
<a href="https://serpapi.com/?utm_source=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
<a href="https://typebot.io/?utm_source=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
<a href="https://www.runpod.io/?utm_source=coolify.io">
<a href="https://serpapi.com/?ref=coolify.io"><img width="60px" alt="SerpAPI" src="https://github.com/serpapi.png"/></a>
<a href="https://typebot.io/?ref=coolify.io"><img src="https://pbs.twimg.com/profile_images/1509194008366657543/9I-C7uWT_400x400.jpg" width="60px" alt="typebot"/></a>
<a href="https://www.runpod.io/?ref=coolify.io">
<svg style="width:60px;height:60px;background:#fff;" xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 200 200"><g><path d="M74.5 51.1c-25.4 14.9-27 16-29.6 20.2-1.8 3-1.9 5.3-1.9 32.3 0 21.7.3 29.4 1.3 30.6 1.9 2.5 46.7 27.9 48.5 27.6 1.5-.3 1.7-3.1 2-27.7.2-21.9 0-27.8-1.1-29.5-.8-1.2-9.9-6.8-20.2-12.6-10.3-5.8-19.4-11.5-20.2-12.7-1.8-2.6-.9-5.9 1.8-7.4 1.6-.8 6.3 0 21.8 4C87.8 78.7 98 81 99.6 81c4.4 0 49.9-25.9 49.9-28.4 0-1.6-3.4-2.8-24-8.2-13.2-3.5-25.1-6.3-26.5-6.3-1.4.1-12.4 5.9-24.5 13z"></path><path d="m137.2 68.1-3.3 2.1 6.3 3.7c3.5 2 6.3 4.3 6.3 5.1 0 .9-8 6.1-19.4 12.6-10.6 6-20 11.9-20.7 12.9-1.2 1.6-1.4 7.2-1.2 29.4.3 24.8.5 27.6 2 27.9 1.8.3 46.6-25.1 48.6-27.6.9-1.2 1.2-8.8 1.2-30.2s-.3-29-1.2-30.2c-1.6-1.9-12.1-7.8-13.9-7.8-.8 0-2.9 1-4.7 2.1z"></path></g></svg></a>
<a href="https://lightspeed.run/?utm_source=coolify.io"><img src="https://github.com/lightspeedrun.png" width="60px" alt="Lightspeed.run"/></a>
<a href="https://www.flint.sh/en/home?utm_source=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></a>
<a href="https://americancloud.com/?utm_source=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
<a href="https://cryptojobslist.com/?utm_source=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
<a href="https://x.com/mrsmith9ja?utm_source=coolify.io"><img width="60px" alt="Thompson Edolo" src="https://github.com/verygreenboi.png"/></a>
<a href="https://www.uxwizz.com/?utm_source=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
<a href="https://lightspeed.run/?ref=coolify.io"><img src="https://github.com/lightspeedrun.png" width="60px" alt="Lightspeed.run"/></a>
<a href="https://www.flint.sh/en/home?ref=coolify.io"> <img src="https://github.com/Flint-company.png" width="60px" alt="FlintCompany"/></a>
<a href="https://americancloud.com/?ref=coolify.io"><img src="https://github.com/American-Cloud.png" width="60px" alt="American Cloud"/></a>
<a href="https://cryptojobslist.com/?ref=coolify.io"><img src="https://github.com/cryptojobslist.png" width="60px" alt="CryptoJobsList" /></a>
<a href="https://codext.link/coolify-io?ref=coolify.io"><img src="./other/logos/codext.jpg" width="60px" alt="Codext" /></a>
<a href="https://x.com/mrsmith9ja?ref=coolify.io"><img width="60px" alt="Thompson Edolo" src="https://github.com/verygreenboi.png"/></a>
<a href="https://www.uxwizz.com/?ref=coolify.io"><img width="60px" alt="UXWizz" src="https://github.com/UXWizz.png"/></a>
<a href="https://github.com/Flowko"><img src="https://barrad.me/_ipx/f_webp&s_300x300/younes.jpg" width="60px" alt="Younes Barrad" /></a>
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Automaze" /></a>
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
@@ -83,9 +93,9 @@ Special thanks to our biggest sponsors!
# Cloud
If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io
If you do not want to self-host Coolify, there is a paid cloud version available: [app.coolify.io](https://app.coolify.io)
For more information & pricing, take a look at our landing page [here](https://coolify.io).
For more information & pricing, take a look at our landing page [coolify.io](https://coolify.io).
## Why should I use the Cloud version?
The recommended way to use Coolify is to have one server for Coolify and one (or more) for the resources you are deploying. A server is around 4-5$/month.
@@ -109,7 +119,7 @@ By subscribing to the cloud version, you get the Coolify server for the same pri
</a>
</p>
<a href="https://www.producthunt.com/posts/coolify?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An&#0032;open&#0045;source&#0032;&#0038;&#0032;self&#0045;hostable&#0032;Heroku&#0044;&#0032;Netlify&#0032;alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<a href="https://www.producthunt.com/posts/coolify?ref=badge-featured&utm_medium=badge&utm_souce=badge-coolify" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=338273&theme=light" alt="Coolify - An&#0032;open&#0045;source&#0032;&#0038;&#0032;self&#0045;hostable&#0032;Heroku&#0044;&#0032;Netlify&#0032;alternative | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<a href="https://trendshift.io/repositories/634" target="_blank"><img src="https://trendshift.io/api/badge/repositories/634" alt="coollabsio%2Fcoolify | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Database;
use App\Events\DatabaseStatusChanged;
use App\Models\ServiceDatabase;
use App\Models\StandaloneClickhouse;
use App\Models\StandaloneDragonfly;
@@ -28,5 +29,6 @@ class StopDatabaseProxy
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
$database->is_public = false;
$database->save();
DatabaseStatusChanged::dispatch();
}
}

View File

@@ -12,12 +12,15 @@ class StartSentinel
public function handle(Server $server, $version = 'latest', bool $restart = false)
{
if ($restart) {
instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
StopSentinel::run($server);
}
$metrics_history = $server->settings->metrics_history_days;
$refresh_rate = $server->settings->metrics_refresh_rate_seconds;
$token = $server->settings->metrics_token;
instant_remote_process([
"docker run --rm --pull always -d -e \"SCHEDULER=true\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
"docker run --rm --pull always -d -e \"TOKEN={$token}\" -e \"SCHEDULER=true\" -e \"METRICS_HISTORY={$metrics_history}\" -e \"REFRESH_RATE={$refresh_rate}\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
], $server, false);
], $server, true);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Actions\Server;
use App\Models\Server;
use Lorisleiva\Actions\Concerns\AsAction;
class StopSentinel
{
use AsAction;
public function handle(Server $server)
{
instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
}
}

View File

@@ -5,6 +5,7 @@ namespace App\Console\Commands;
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;
@@ -24,6 +25,8 @@ class Init extends Command
get_public_ips();
$full_cleanup = $this->option('full-cleanup');
$cleanup_deployments = $this->option('cleanup-deployments');
$this->replace_slash_in_environment_name();
if ($cleanup_deployments) {
echo "Running cleanup deployments.\n";
$this->cleanup_in_progress_application_deployments();
@@ -150,4 +153,15 @@ class Init extends Command
echo "Error: {$e->getMessage()}\n";
}
}
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();
}
}
}
}

View File

@@ -61,7 +61,7 @@ class Kernel extends ConsoleKernel
{
$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 (config('coolify.is_sentinel_enabled')) {
if ($server->isSentinelEnabled()) {
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
}
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();

View File

@@ -11,6 +11,5 @@ class ServerMetadata extends Data
public function __construct(
public ?ProxyTypes $type,
public ?ProxyStatus $status
) {
}
) {}
}

View File

@@ -10,8 +10,5 @@ class ProxyStarted
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public $data)
{
}
public function __construct(public $data) {}
}

View File

@@ -4,6 +4,4 @@ namespace App\Exceptions;
use Exception;
class ProcessException extends Exception
{
}
class ProcessException extends Exception {}

View File

@@ -0,0 +1,183 @@
<?php
namespace App\Http\Controllers\Api;
use App\Actions\Application\StopApplication;
use App\Http\Controllers\Controller;
use App\Models\Application;
use App\Models\Project;
use Illuminate\Http\Request;
use Visus\Cuid2\Cuid2;
class Applications extends Controller
{
public function applications(Request $request)
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
}
$projects = Project::where('team_id', $teamId)->get();
$applications = collect();
$applications->push($projects->pluck('applications')->flatten());
$applications = $applications->flatten();
return response()->json($applications);
}
public function application_by_uuid(Request $request)
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
}
$uuid = $request->route('uuid');
if (! $uuid) {
return response()->json(['error' => 'UUID is required.'], 400);
}
$application = Application::where('uuid', $uuid)->first();
if (! $application) {
return response()->json(['error' => 'Application not found.'], 404);
}
return response()->json($application);
}
public function update_by_uuid(Request $request)
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
}
if ($request->collect()->count() == 0) {
return response()->json([
'message' => 'No data provided.',
], 400);
}
$application = Application::where('uuid', $request->uuid)->first();
if (! $application) {
return response()->json([
'success' => false,
'message' => 'Application not found',
], 404);
}
ray($request->collect());
// if ($request->has('domains')) {
// $existingDomains = explode(',', $application->fqdn);
// $newDomains = $request->domains;
// $filteredNewDomains = array_filter($newDomains, function ($domain) use ($existingDomains) {
// return ! in_array($domain, $existingDomains);
// });
// $mergedDomains = array_unique(array_merge($existingDomains, $filteredNewDomains));
// $application->fqdn = implode(',', $mergedDomains);
// $application->custom_labels = base64_encode(implode("\n ", generateLabelsApplication($application)));
// $application->save();
// }
return response()->json([
'message' => 'Application updated successfully.',
'application' => serialize_api_response($application),
]);
}
public function action_deploy(Request $request)
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
}
$force = $request->query->get('force') ?? false;
$instant_deploy = $request->query->get('instant_deploy') ?? false;
$uuid = $request->route('uuid');
if (! $uuid) {
return response()->json(['error' => 'UUID is required.'], 400);
}
$application = Application::where('uuid', $uuid)->first();
if (! $application) {
return response()->json(['error' => 'Application not found.'], 404);
}
$deployment_uuid = new Cuid2(7);
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
force_rebuild: $force,
is_api: true,
no_questions_asked: $instant_deploy
);
return response()->json(
[
'message' => 'Deployment request queued.',
'deployment_uuid' => $deployment_uuid->toString(),
'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
],
200
);
}
public function action_stop(Request $request)
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
}
$uuid = $request->route('uuid');
$sync = $request->query->get('sync') ?? false;
if (! $uuid) {
return response()->json(['error' => 'UUID is required.'], 400);
}
$application = Application::where('uuid', $uuid)->first();
if (! $application) {
return response()->json(['error' => 'Application not found.'], 404);
}
if ($sync) {
StopApplication::run($application);
return response()->json(['message' => 'Stopped the application.'], 200);
} else {
StopApplication::dispatch($application);
return response()->json(['message' => 'Stopping request queued.'], 200);
}
}
public function action_restart(Request $request)
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
}
$uuid = $request->route('uuid');
if (! $uuid) {
return response()->json(['error' => 'UUID is required.'], 400);
}
$application = Application::where('uuid', $uuid)->first();
if (! $application) {
return response()->json(['error' => 'Application not found.'], 404);
}
$deployment_uuid = new Cuid2(7);
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
restart_only: true,
is_api: true,
);
return response()->json(
[
'message' => 'Restart request queued.',
'deployment_uuid' => $deployment_uuid->toString(),
'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
],
200
);
}
}

View File

@@ -38,7 +38,25 @@ class Deploy extends Controller
'status',
])->sortBy('id')->toArray();
return response()->json($deployments_per_server, 200);
return response()->json(serialize_api_response($deployments_per_server), 200);
}
public function deployment_by_uuid(Request $request)
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
}
$uuid = $request->route('uuid');
if (! $uuid) {
return response()->json(['error' => 'UUID is required.'], 400);
}
$deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first()->makeHidden('logs');
if (! $deployment) {
return response()->json(['error' => 'Deployment not found.'], 404);
}
return response()->json(serialize_api_response($deployment), 200);
}
public function deploy(Request $request)

View File

@@ -3,102 +3,52 @@
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\InstanceSettings;
use App\Models\Project as ModelsProject;
use App\Models\Application;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class Domains extends Controller
{
public function domains(Request $request)
public function deleteDomains(Request $request)
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
}
$projects = ModelsProject::where('team_id', $teamId)->get();
$domains = collect();
$applications = $projects->pluck('applications')->flatten();
$settings = InstanceSettings::get();
if ($applications->count() > 0) {
foreach ($applications as $application) {
$ip = $application->destination->server->ip;
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
});
if ($ip === 'host.docker.internal') {
if ($settings->public_ipv4) {
$domains->push([
'domain' => $fqdn,
'ip' => $settings->public_ipv4,
]);
}
if ($settings->public_ipv6) {
$domains->push([
'domain' => $fqdn,
'ip' => $settings->public_ipv6,
]);
}
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
$domains->push([
'domain' => $fqdn,
'ip' => $ip,
]);
}
} else {
$domains->push([
'domain' => $fqdn,
'ip' => $ip,
]);
}
}
}
$services = $projects->pluck('services')->flatten();
if ($services->count() > 0) {
foreach ($services as $service) {
$service_applications = $service->applications;
if ($service_applications->count() > 0) {
foreach ($service_applications as $application) {
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
});
if ($ip === 'host.docker.internal') {
if ($settings->public_ipv4) {
$domains->push([
'domain' => $fqdn,
'ip' => $settings->public_ipv4,
]);
}
if ($settings->public_ipv6) {
$domains->push([
'domain' => $fqdn,
'ip' => $settings->public_ipv6,
]);
}
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
$domains->push([
'domain' => $fqdn,
'ip' => $ip,
]);
}
} else {
$domains->push([
'domain' => $fqdn,
'ip' => $ip,
]);
}
}
}
}
}
$domains = $domains->groupBy('ip')->map(function ($domain) {
return $domain->pluck('domain')->flatten();
})->map(function ($domain, $ip) {
return [
'ip' => $ip,
'domains' => $domain,
];
})->values();
$validator = Validator::make($request->all(), [
'uuid' => 'required|string|exists:applications,uuid',
'domains' => 'required|array',
'domains.*' => 'required|string|distinct',
]);
return response()->json($domains);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => 'Validation failed',
'errors' => $validator->errors(),
], 422);
}
$application = Application::where('uuid', $request->uuid)->first();
if (! $application) {
return response()->json([
'success' => false,
'message' => 'Application not found',
], 404);
}
$existingDomains = explode(',', $application->fqdn);
$domainsToDelete = $request->domains;
$updatedDomains = array_diff($existingDomains, $domainsToDelete);
$application->fqdn = implode(',', $updatedDomains);
$application->custom_labels = base64_encode(implode("\n ", generateLabelsApplication($application)));
$application->save();
return response()->json([
'success' => true,
'message' => 'Domains updated successfully',
'application' => $application,
]);
}
}

View File

@@ -3,6 +3,9 @@
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Application;
use App\Models\InstanceSettings;
use App\Models\Project;
use App\Models\Server as ModelsServer;
use Illuminate\Http\Request;
@@ -59,4 +62,106 @@ class Server extends Controller
return response()->json($server);
}
public function get_domains_by_server(Request $request)
{
$teamId = get_team_id_from_token();
if (is_null($teamId)) {
return invalid_token();
}
$uuid = $request->query->get('uuid');
if ($uuid) {
$domains = Application::getDomainsByUuid($uuid);
return response()->json([
'uuid' => $uuid,
'domains' => $domains,
]);
}
$projects = Project::where('team_id', $teamId)->get();
$domains = collect();
$applications = $projects->pluck('applications')->flatten();
$settings = InstanceSettings::get();
if ($applications->count() > 0) {
foreach ($applications as $application) {
$ip = $application->destination->server->ip;
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
});
if ($ip === 'host.docker.internal') {
if ($settings->public_ipv4) {
$domains->push([
'domain' => $fqdn,
'ip' => $settings->public_ipv4,
]);
}
if ($settings->public_ipv6) {
$domains->push([
'domain' => $fqdn,
'ip' => $settings->public_ipv6,
]);
}
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
$domains->push([
'domain' => $fqdn,
'ip' => $ip,
]);
}
} else {
$domains->push([
'domain' => $fqdn,
'ip' => $ip,
]);
}
}
}
$services = $projects->pluck('services')->flatten();
if ($services->count() > 0) {
foreach ($services as $service) {
$service_applications = $service->applications;
if ($service_applications->count() > 0) {
foreach ($service_applications as $application) {
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
});
if ($ip === 'host.docker.internal') {
if ($settings->public_ipv4) {
$domains->push([
'domain' => $fqdn,
'ip' => $settings->public_ipv4,
]);
}
if ($settings->public_ipv6) {
$domains->push([
'domain' => $fqdn,
'ip' => $settings->public_ipv6,
]);
}
if (! $settings->public_ipv4 && ! $settings->public_ipv6) {
$domains->push([
'domain' => $fqdn,
'ip' => $ip,
]);
}
} else {
$domains->push([
'domain' => $fqdn,
'ip' => $ip,
]);
}
}
}
}
}
$domains = $domains->groupBy('ip')->map(function ($domain) {
return $domain->pluck('domain')->flatten();
})->map(function ($domain, $ip) {
return [
'ip' => $ip,
'domains' => $domain,
];
})->values();
return response()->json($domains);
}
}

View File

@@ -2,8 +2,10 @@
namespace App\Http\Controllers;
use App\Models\InstanceSettings;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpKernel\Exception\HttpException;
class OauthController extends Controller
{
@@ -20,6 +22,11 @@ class OauthController extends Controller
$oauthUser = get_socialite_provider($provider)->user();
$user = User::whereEmail($oauthUser->email)->first();
if (! $user) {
$settings = InstanceSettings::get();
if (! $settings->is_registration_enabled) {
abort(403, 'Registration is disabled');
}
$user = User::create([
'name' => $oauthUser->name,
'email' => $oauthUser->email,
@@ -31,7 +38,9 @@ class OauthController extends Controller
} catch (\Exception $e) {
ray($e->getMessage());
return redirect()->route('login')->withErrors([__('auth.failed.callback')]);
$errorCode = $e instanceof HttpException ? 'auth.failed' : 'auth.failed.callback';
return redirect()->route('login')->withErrors([__($errorCode)]);
}
}
}

View File

@@ -130,12 +130,23 @@ class Bitbucket extends Controller
$deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
ApplicationPreview::create([
'git_type' => 'bitbucket',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::create([
'git_type' => 'bitbucket',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
'docker_compose_domains' => $application->docker_compose_domains,
]);
$pr_app->generate_preview_fqdn_compose();
} else {
ApplicationPreview::create([
'git_type' => 'bitbucket',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
}
queue_application_deployment(
application: $application,

View File

@@ -165,12 +165,24 @@ class Gitea extends Controller
$deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
ApplicationPreview::create([
'git_type' => 'gitea',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::create([
'git_type' => 'gitea',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
'docker_compose_domains' => $application->docker_compose_domains,
]);
$pr_app->generate_preview_fqdn_compose();
} else {
ApplicationPreview::create([
'git_type' => 'gitea',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
}
queue_application_deployment(
application: $application,

View File

@@ -170,12 +170,23 @@ class Github extends Controller
$deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
ApplicationPreview::create([
'git_type' => 'github',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::create([
'git_type' => 'github',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
'docker_compose_domains' => $application->docker_compose_domains,
]);
$pr_app->generate_preview_fqdn_compose();
} else {
ApplicationPreview::create([
'git_type' => 'github',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
}
queue_application_deployment(
application: $application,

View File

@@ -180,12 +180,23 @@ class Gitlab extends Controller
$deployment_uuid = new Cuid2(7);
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
ApplicationPreview::create([
'git_type' => 'gitlab',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
if ($application->build_pack === 'dockercompose') {
$pr_app = ApplicationPreview::create([
'git_type' => 'gitlab',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
'docker_compose_domains' => $application->docker_compose_domains,
]);
$pr_app->generate_preview_fqdn_compose();
} else {
ApplicationPreview::create([
'git_type' => 'gitlab',
'application_id' => $application->id,
'pull_request_id' => $pull_request_id,
'pull_request_html_url' => $pull_request_html_url,
]);
}
}
queue_application_deployment(
application: $application,

View File

@@ -72,14 +72,14 @@ class Stripe extends Controller
}
$subscription = Subscription::where('team_id', $teamId)->first();
if ($subscription) {
send_internal_notification('Old subscription activated for team: '.$teamId);
// send_internal_notification('Old subscription activated for team: '.$teamId);
$subscription->update([
'stripe_subscription_id' => $subscriptionId,
'stripe_customer_id' => $customerId,
'stripe_invoice_paid' => true,
]);
} else {
send_internal_notification('New subscription for team: '.$teamId);
// send_internal_notification('New subscription for team: '.$teamId);
Subscription::create([
'team_id' => $teamId,
'stripe_subscription_id' => $subscriptionId,
@@ -92,7 +92,7 @@ class Stripe extends Controller
$customerId = data_get($data, 'customer');
$planId = data_get($data, 'lines.data.0.plan.id');
if (Str::contains($excludedPlans, $planId)) {
send_internal_notification('Subscription excluded.');
// send_internal_notification('Subscription excluded.');
break;
}
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
@@ -108,33 +108,33 @@ class Stripe extends Controller
$customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
if (! $subscription) {
send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: '.$customerId);
// send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: '.$customerId);
return response('No subscription found in Coolify.');
}
$team = data_get($subscription, 'team');
if (! $team) {
send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: '.$customerId);
// send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: '.$customerId);
return response('No team found in Coolify.');
}
if (! $subscription->stripe_invoice_paid) {
SubscriptionInvoiceFailedJob::dispatch($team);
send_internal_notification('Invoice payment failed: '.$customerId);
// send_internal_notification('Invoice payment failed: '.$customerId);
} else {
send_internal_notification('Invoice payment failed but already paid: '.$customerId);
// send_internal_notification('Invoice payment failed but already paid: '.$customerId);
}
break;
case 'payment_intent.payment_failed':
$customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
if (! $subscription) {
send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: '.$customerId);
// send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: '.$customerId);
return response('No subscription found in Coolify.');
}
if ($subscription->stripe_invoice_paid) {
send_internal_notification('payment_intent.payment_failed but invoice is active for customer: '.$customerId);
// send_internal_notification('payment_intent.payment_failed but invoice is active for customer: '.$customerId);
return;
}
@@ -146,7 +146,7 @@ class Stripe extends Controller
$subscriptionId = data_get($data, 'items.data.0.subscription');
$planId = data_get($data, 'items.data.0.plan.id');
if (Str::contains($excludedPlans, $planId)) {
send_internal_notification('Subscription excluded.');
// send_internal_notification('Subscription excluded.');
break;
}
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
@@ -156,11 +156,11 @@ class Stripe extends Controller
}
if (! $subscription) {
if ($status === 'incomplete_expired') {
send_internal_notification('Subscription incomplete expired for customer: '.$customerId);
// send_internal_notification('Subscription incomplete expired for customer: '.$customerId);
return response('Subscription incomplete expired', 200);
}
send_internal_notification('No subscription found for: '.$customerId);
// send_internal_notification('No subscription found for: '.$customerId);
return response('No subscription found', 400);
}
@@ -194,7 +194,7 @@ class Stripe extends Controller
$subscription->update([
'stripe_invoice_paid' => false,
]);
send_internal_notification('Subscription paused or incomplete for customer: '.$customerId);
// send_internal_notification('Subscription paused or incomplete for customer: '.$customerId);
}
// Trial ended but subscribed, reactive servers
@@ -208,13 +208,13 @@ class Stripe extends Controller
if ($comment) {
$reason .= ' with comment: \''.$comment."'";
}
send_internal_notification($reason);
// send_internal_notification($reason);
}
if ($alreadyCancelAtPeriodEnd !== $cancelAtPeriodEnd) {
if ($cancelAtPeriodEnd) {
// send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id);
} else {
send_internal_notification('customer.subscription.updated for customer: '.$customerId);
// send_internal_notification('customer.subscription.updated for customer: '.$customerId);
}
}
break;
@@ -233,7 +233,7 @@ class Stripe extends Controller
'stripe_invoice_paid' => false,
'stripe_trial_already_ended' => true,
]);
send_internal_notification('customer.subscription.deleted for customer: '.$customerId);
// send_internal_notification('customer.subscription.deleted for customer: '.$customerId);
break;
case 'customer.subscription.trial_will_end':
// Not used for now
@@ -258,7 +258,7 @@ class Stripe extends Controller
'stripe_invoice_paid' => false,
]);
SubscriptionTrialEndedJob::dispatch($team);
send_internal_notification('Subscription paused for customer: '.$customerId);
// send_internal_notification('Subscription paused for customer: '.$customerId);
break;
default:
// Unhandled event type

View File

@@ -9,6 +9,7 @@ use App\Events\ApplicationStatusChanged;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use App\Models\ApplicationPreview;
use App\Models\EnvironmentVariable;
use App\Models\GithubApp;
use App\Models\GitlabApp;
use App\Models\Server;
@@ -339,7 +340,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private function post_deployment()
{
if ($this->server->isProxyShouldRun()) {
GetContainersStatus::dispatch($this->server);
GetContainersStatus::dispatch($this->server)->onQueue('high');
// dispatch(new ContainerStatusJob($this->server));
}
$this->next(ApplicationDeploymentStatus::FINISHED->value);
@@ -827,6 +828,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
$envs->push("COOLIFY_BRANCH={$local_branch}");
}
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
$envs->push("COOLIFY_CONTAINER_NAME={$this->container_name}");
}
foreach ($sorted_environment_variables_preview as $env) {
$real_value = $env->real_value;
if ($env->version === '4.0.0-beta.239') {
@@ -868,6 +872,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
$envs->push("COOLIFY_BRANCH={$local_branch}");
}
if ($this->application->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
$envs->push("COOLIFY_CONTAINER_NAME={$this->container_name}");
}
foreach ($sorted_environment_variables as $env) {
$real_value = $env->real_value;
if ($env->version === '4.0.0-beta.239') {
@@ -877,7 +884,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$real_value = '\''.$real_value.'\'';
} else {
$real_value = escapeEnvVariables($env->real_value);
ray($real_value);
}
}
$envs->push($env->key.'='.$real_value);
@@ -946,9 +952,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
}
private function framework_based_notification()
private function laravel_finetunes()
{
// Laravel old env variables
if ($this->pull_request_id === 0) {
$nixpacks_php_fallback_path = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
$nixpacks_php_root_dir = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
@@ -956,9 +961,22 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$nixpacks_php_fallback_path = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first();
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
}
if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) {
$this->application_deployment_queue->addLogEntry('There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/resources/laravel', 'stderr');
if (! $nixpacks_php_fallback_path) {
$nixpacks_php_fallback_path = new EnvironmentVariable();
$nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH';
$nixpacks_php_fallback_path->value = '/index.php';
$nixpacks_php_fallback_path->application_id = $this->application->id;
$nixpacks_php_fallback_path->save();
}
if (! $nixpacks_php_root_dir) {
$nixpacks_php_root_dir = new EnvironmentVariable();
$nixpacks_php_root_dir->key = 'NIXPACKS_PHP_ROOT_DIR';
$nixpacks_php_root_dir->value = '/app/public';
$nixpacks_php_root_dir->application_id = $this->application->id;
$nixpacks_php_root_dir->save();
}
return [$nixpacks_php_fallback_path, $nixpacks_php_root_dir];
}
private function rolling_update()
@@ -1005,7 +1023,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->application_deployment_queue->addLogEntry('Rolling update completed.');
}
}
$this->framework_based_notification();
}
private function health_check()
@@ -1366,17 +1383,20 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
throw new RuntimeException('Nixpacks failed to detect the application type. Please check the documentation of Nixpacks: https://nixpacks.com/docs/providers');
}
}
if ($this->saved_outputs->get('nixpacks_plan')) {
$this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan');
if ($this->nixpacks_plan) {
$this->application_deployment_queue->addLogEntry("Found application type: {$this->nixpacks_type}.");
$this->application_deployment_queue->addLogEntry("If you need further customization, please check the documentation of Nixpacks: https://nixpacks.com/docs/providers/{$this->nixpacks_type}");
$parsed = Toml::Parse($this->nixpacks_plan);
// Do any modifications here
$this->generate_env_variables();
$merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', [])));
$aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []);
if (count($aptPkgs) === 0) {
$aptPkgs = ['curl', 'wget'];
data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']);
} else {
if (! in_array('curl', $aptPkgs)) {
@@ -1388,6 +1408,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs);
}
data_set($parsed, 'variables', $merged_envs->toArray());
$is_laravel = data_get($parsed, 'variables.IS_LARAVEL', false);
if ($is_laravel) {
$variables = $this->laravel_finetunes();
data_set($parsed, 'variables.NIXPACKS_PHP_FALLBACK_PATH', $variables[0]->value);
data_set($parsed, 'variables.NIXPACKS_PHP_ROOT_DIR', $variables[1]->value);
}
$this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT);
$this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true);
}
@@ -1841,13 +1867,25 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), 'hidden' => true,
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
]);
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir}"), 'hidden' => true,
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
]);
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
}
$base64_build_command = base64_encode($build_command);
$this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
],
[
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
]
);
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
} else {
if ($this->force_rebuild) {
@@ -1866,7 +1904,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
]
);
}
$dockerfile = base64_encode("FROM {$this->application->static_image}
WORKDIR /usr/share/nginx/html/
LABEL coolify.deploymentId={$this->deployment_uuid}
@@ -1929,13 +1966,24 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
if ($this->force_rebuild) {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), 'hidden' => true,
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
]);
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
} else {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir}"), 'hidden' => true,
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
]);
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
}
$base64_build_command = base64_encode($build_command);
$this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
],
[
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
]
);
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
} else {
if ($this->force_rebuild) {
@@ -2184,10 +2232,14 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
ray($code);
if ($code !== 69420) {
// 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one
$this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr');
$this->execute_remote_command(
["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true]
);
if ($this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name)) {
// do not remove already running container
} else {
$this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr');
$this->execute_remote_command(
["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true]
);
}
}
}
}

View File

@@ -25,8 +25,7 @@ class ApplicationPullRequestUpdateJob implements ShouldBeEncrypted, ShouldQueue
public ApplicationPreview $preview,
public ProcessStatus $status,
public ?string $deployment_uuid = null
) {
}
) {}
public function handle()
{

View File

@@ -19,9 +19,7 @@ class CheckLogDrainContainerJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function middleware(): array
{

View File

@@ -14,9 +14,7 @@ class CheckResaleLicenseJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct()
{
}
public function __construct() {}
public function handle(): void
{

View File

@@ -15,9 +15,7 @@ class CleanupHelperContainersJob implements ShouldBeEncrypted, ShouldBeUnique, S
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function handle(): void
{

View File

@@ -16,10 +16,7 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct()
{
}
public function __construct() {}
// public function uniqueId(): string
// {

View File

@@ -23,9 +23,7 @@ class ContainerStatusJob implements ShouldBeEncrypted, ShouldQueue
return isDev() ? 1 : 3;
}
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function middleware(): array
{

View File

@@ -23,8 +23,7 @@ class CoolifyTask implements ShouldBeEncrypted, ShouldQueue
public bool $ignore_errors = false,
public $call_event_on_finish = null,
public $call_event_data = null
) {
}
) {}
/**
* Execute the job.

View File

@@ -18,9 +18,7 @@ class DatabaseBackupStatusJob implements ShouldBeEncrypted, ShouldQueue
public $tries = 1;
public function __construct()
{
}
public function __construct() {}
public function handle()
{

View File

@@ -28,9 +28,7 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, public bool $deleteConfigurations = false)
{
}
public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource, public bool $deleteConfigurations = false) {}
public function handle()
{

View File

@@ -22,9 +22,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
public ?int $usageBefore = null;
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function handle(): void
{
@@ -62,7 +60,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
Log::info('No need to clean up '.$this->server->name);
}
} catch (\Throwable $e) {
send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
// send_internal_notification('DockerCleanupJob failed with: '.$e->getMessage());
ray($e->getMessage());
throw $e;
}

View File

@@ -23,9 +23,7 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue
return isDev() ? 1 : 3;
}
public function __construct(public GithubApp $github_app)
{
}
public function __construct(public GithubApp $github_app) {}
public function middleware(): array
{

View File

@@ -19,9 +19,7 @@ class InstanceAutoUpdateJob implements ShouldBeEncrypted, ShouldBeUnique, Should
public $tries = 1;
public function __construct()
{
}
public function __construct() {}
public function handle(): void
{

View File

@@ -19,9 +19,7 @@ class PullCoolifyImageJob implements ShouldBeEncrypted, ShouldQueue
public $timeout = 1000;
public function __construct()
{
}
public function __construct() {}
public function handle(): void
{

View File

@@ -27,9 +27,7 @@ class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
return $this->server->uuid;
}
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function handle(): void
{

View File

@@ -28,9 +28,7 @@ class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
return $this->server->uuid;
}
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function handle(): void
{
@@ -52,7 +50,7 @@ class PullSentinelImageJob implements ShouldBeEncrypted, ShouldQueue
}
ray('Sentinel image is up to date');
} catch (\Throwable $e) {
send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
// send_internal_notification('PullSentinelImageJob failed with: '.$e->getMessage());
ray($e->getMessage());
throw $e;
}

View File

@@ -17,9 +17,7 @@ class PullTemplatesFromCDN implements ShouldBeEncrypted, ShouldQueue
public $timeout = 10;
public function __construct()
{
}
public function __construct() {}
public function handle(): void
{

View File

@@ -17,9 +17,7 @@ class PullVersionsFromCDN implements ShouldBeEncrypted, ShouldQueue
public $timeout = 10;
public function __construct()
{
}
public function __construct() {}
public function handle(): void
{

View File

@@ -14,9 +14,7 @@ class SendConfirmationForWaitlistJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public string $email, public string $uuid)
{
}
public function __construct(public string $email, public string $uuid) {}
public function handle()
{

View File

@@ -31,8 +31,7 @@ class SendMessageToDiscordJob implements ShouldBeEncrypted, ShouldQueue
public function __construct(
public string $text,
public string $webhookUrl
) {
}
) {}
/**
* Execute the job.

View File

@@ -33,8 +33,7 @@ class SendMessageToTelegramJob implements ShouldBeEncrypted, ShouldQueue
public string $token,
public string $chatId,
public ?string $topicId = null,
) {
}
) {}
/**
* Execute the job.

View File

@@ -16,9 +16,7 @@ class ServerFilesFromServerJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public ServiceApplication|ServiceDatabase|Application $resource)
{
}
public function __construct(public ServiceApplication|ServiceDatabase|Application $resource) {}
public function handle()
{

View File

@@ -24,9 +24,7 @@ class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
return isDev() ? 1 : 3;
}
public function __construct(public Team $team)
{
}
public function __construct(public Team $team) {}
public function middleware(): array
{

View File

@@ -25,9 +25,7 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
return isDev() ? 1 : 3;
}
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function middleware(): array
{
@@ -48,7 +46,7 @@ class ServerStatusJob implements ShouldBeEncrypted, ShouldQueue
if ($this->server->isFunctional()) {
$this->cleanup(notify: false);
$this->remove_unnecessary_coolify_yaml();
if (config('coolify.is_sentinel_enabled')) {
if ($this->server->isSentinelEnabled()) {
$this->server->checkSentinel();
}
}

View File

@@ -14,9 +14,7 @@ class ServerStorageSaveJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public LocalFileVolume $localFileVolume)
{
}
public function __construct(public LocalFileVolume $localFileVolume) {}
public function handle()
{

View File

@@ -15,9 +15,7 @@ class SubscriptionInvoiceFailedJob implements ShouldBeEncrypted, ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(protected Team $team)
{
}
public function __construct(protected Team $team) {}
public function handle()
{

View File

@@ -17,8 +17,7 @@ class SubscriptionTrialEndedJob implements ShouldBeEncrypted, ShouldQueue
public function __construct(
public Team $team
) {
}
) {}
public function handle(): void
{

View File

@@ -17,8 +17,7 @@ class SubscriptionTrialEndsSoonJob implements ShouldBeEncrypted, ShouldQueue
public function __construct(
public Team $team
) {
}
) {}
public function handle(): void
{

View File

@@ -9,9 +9,7 @@ use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
class MaintenanceModeDisabledNotification
{
public function __construct()
{
}
public function __construct() {}
public function handle(EventsMaintenanceModeDisabled $event): void
{

View File

@@ -9,9 +9,7 @@ class ProxyStartedNotification
{
public Server $server;
public function __construct()
{
}
public function __construct() {}
public function handle(ProxyStarted $event): void
{

View File

@@ -12,7 +12,7 @@ use Livewire\Component;
class Index extends Component
{
protected $listeners = ['serverInstalled' => 'validateServer'];
protected $listeners = ['refreshBoardingIndex' => 'validateServer'];
public string $currentState = 'welcome';

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Livewire;
//use Livewire\Component;
use Illuminate\View\Component;
use Visus\Cuid2\Cuid2;
class MonacoEditor extends Component
{
protected $listeners = [
'configurationChanged' => '$refresh',
];
public function __construct(
public ?string $id,
public ?string $name,
public ?string $type,
public ?string $monacoContent,
public ?string $value,
public ?string $label,
public ?string $placeholder,
public bool $required,
public bool $disabled,
public bool $readonly,
public bool $allowTab,
public bool $spellcheck,
public ?string $helper,
public bool $realtimeValidation,
public bool $allowToPeak,
public string $defaultClass,
public string $defaultClassInput,
public ?string $language
) {
//
}
public function render()
{
if (is_null($this->id)) {
$this->id = new Cuid2(7);
}
if (is_null($this->name)) {
$this->name = $this->id;
}
return view('components.forms.monaco-editor');
}
}

View File

@@ -54,9 +54,9 @@ class DeploymentNavbar extends Component
public function cancel()
{
$kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}";
$server_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id;
try {
$kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}";
$server_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id;
$server = Server::find($server_id);
if ($this->application_deployment_queue->logs) {
$previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR);
@@ -84,6 +84,7 @@ class DeploymentNavbar extends Component
'current_process_id' => null,
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
]);
next_after_cancel($server);
}
}
}

View File

@@ -347,7 +347,9 @@ class General extends Component
public function submit($showToaster = true)
{
try {
$this->set_redirect();
if ($this->application->isDirty('redirect')) {
$this->set_redirect();
}
$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) {

View File

@@ -45,7 +45,7 @@ class Heading extends Component
public function check_status($showNotification = false)
{
if ($this->application->destination->server->isFunctional()) {
GetContainersStatus::dispatch($this->application->destination->server);
GetContainersStatus::dispatch($this->application->destination->server)->onQueue('high');
// dispatch(new ContainerStatusJob($this->application->destination->server));
} else {
dispatch(new ServerStatusJob($this->application->destination->server));

View File

@@ -131,6 +131,12 @@ class Previews extends Component
}
}
public function add_and_deploy(int $pull_request_id, ?string $pull_request_html_url = null)
{
$this->add($pull_request_id, $pull_request_html_url);
$this->deploy($pull_request_id, $pull_request_html_url);
}
public function deploy(int $pull_request_id, ?string $pull_request_html_url = null)
{
try {
@@ -180,7 +186,7 @@ class Previews extends Component
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
}
}
GetContainersStatus::dispatchSync($this->application->destination->server);
GetContainersStatus::dispatchSync($this->application->destination->server)->onQueue('high');
$this->dispatch('reloadWindow');
} catch (\Throwable $e) {
return handleError($e, $this);

View File

@@ -12,7 +12,6 @@ use App\Actions\Database\StartPostgresql;
use App\Actions\Database\StartRedis;
use App\Actions\Database\StopDatabase;
use App\Actions\Docker\GetContainersStatus;
use App\Jobs\ContainerStatusJob;
use Livewire\Component;
class Heading extends Component

View File

@@ -25,7 +25,17 @@ class General extends Component
public ?string $db_url_public = null;
protected $listeners = ['refresh', 'save_init_script', 'delete_init_script'];
public function getListeners()
{
$userId = auth()->user()->id;
return [
"echo-private:user.{$userId},DatabaseStatusChanged" => 'database_stopped',
'refresh',
'save_init_script',
'delete_init_script',
];
}
protected $rules = [
'database.name' => 'required',
@@ -69,6 +79,11 @@ class General extends Component
$this->server = data_get($this->database, 'destination.server');
}
public function database_stopped()
{
$this->dispatch('success', 'Database proxy stopped. Database is no longer publicly accessible.');
}
public function instantSaveAdvanced()
{
try {

View File

@@ -128,8 +128,8 @@ class PublicGitRepository extends Component
) {
$this->repository_url = $this->repository_url.'.git';
}
if (str($this->repository_url)->contains('github.com')) {
$this->repository_url = str($this->repository_url)->before('.git')->value();
if (str($this->repository_url)->contains('github.com') && str($this->repository_url)->endsWith('.git')) {
$this->repository_url = str($this->repository_url)->beforeLast('.git')->value();
}
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -140,7 +140,6 @@ class PublicGitRepository extends Component
$this->get_branch();
$this->selected_branch = $this->git_branch;
} catch (\Throwable $e) {
ray($e->getMessage());
if (! $this->branch_found && $this->git_branch == 'main') {
try {
$this->git_branch = 'master';

View File

@@ -176,10 +176,12 @@ class Select extends Component
return;
}
// if (count($this->servers) === 1) {
// $server = $this->servers->first();
// $this->setServer($server);
// }
if (count($this->servers) === 1) {
$server = $this->servers->first();
if ($server instanceof Server) {
$this->setServer($server);
}
}
if (! is_null($this->server)) {
$foundServer = $this->servers->where('id', $this->server->id)->first();
if ($foundServer) {
@@ -195,6 +197,13 @@ class Select extends Component
$this->server = $server;
$this->standaloneDockers = $server->standaloneDockers;
$this->swarmDockers = $server->swarmDockers;
$count = count($this->standaloneDockers) + count($this->swarmDockers);
if ($count === 1) {
$docker = $this->standaloneDockers->first() ?? $this->swarmDockers->first();
if ($docker) {
$this->setDestination($docker->uuid);
}
}
$this->current_step = 'destinations';
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Livewire\Project\Resource;
use Illuminate\Database\Eloquent\Collection;
use Livewire\Component;
class EnvironmentSelect extends Component
{
public Collection $environments;
public string $project_uuid = '';
public string $selectedEnvironment = '';
public function mount()
{
$this->selectedEnvironment = request()->route('environment_name');
$this->project_uuid = request()->route('project_uuid');
}
public function updatedSelectedEnvironment($value)
{
if ($value === 'edit') {
return redirect()->route('project.show', [
'project_uuid' => $this->project_uuid,
]);
} else {
return redirect()->route('project.resource.index', [
'project_uuid' => $this->project_uuid,
'environment_name' => $value,
]);
}
}
}

View File

@@ -11,6 +11,8 @@ class EditCompose extends Component
public $serviceId;
protected $listeners = ['refreshEnvs' => 'mount'];
protected $rules = [
'service.docker_compose_raw' => 'required',
'service.docker_compose' => 'required',

View File

@@ -75,7 +75,6 @@ class StackForm extends Component
$this->service->parse();
$this->service->refresh();
$this->service->saveComposeConfigs();
$this->dispatch('refreshStacks');
$this->dispatch('refreshEnvs');
$this->dispatch('success', 'Service saved.');
} catch (\Throwable $e) {

View File

@@ -112,7 +112,6 @@ class All extends Component
$this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete();
} else {
$variables = parseEnvFormatToArray($this->variables);
ray($variables, $this->variables);
$this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete();
}
foreach ($variables as $key => $variable) {

View File

@@ -59,15 +59,6 @@ class Logs extends Component
}
}
public function loadMetrics()
{
return;
$server = data_get($this->resource, 'destination.server');
if ($server->isFunctional()) {
$this->cpu = $server->getMetrics();
}
}
public function mount()
{
try {
@@ -122,7 +113,6 @@ class Logs extends Component
}
$this->loadMetrics();
} catch (\Exception $e) {
return handleError($e, $this);
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Livewire\Project\Shared;
use Livewire\Component;
class Metrics extends Component
{
public $resource;
public $chartId = 'container-cpu';
public $data;
public $categories;
public int $interval = 5;
public bool $poll = true;
public function pollData()
{
if ($this->poll || $this->interval <= 10) {
$this->loadData();
if ($this->interval > 10) {
$this->poll = false;
}
}
}
public function loadData()
{
try {
$metrics = $this->resource->getMetrics($this->interval);
$cpuMetrics = collect($metrics)->map(function ($metric) {
return [$metric[0], $metric[1]];
});
$memoryMetrics = collect($metrics)->map(function ($metric) {
return [$metric[0], $metric[2]];
});
$this->dispatch("refreshChartData-{$this->chartId}-cpu", [
'seriesData' => $cpuMetrics,
]);
$this->dispatch("refreshChartData-{$this->chartId}-memory", [
'seriesData' => $memoryMetrics,
]);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function setInterval()
{
if ($this->interval <= 10) {
$this->poll = true;
}
$this->loadData();
}
public function render()
{
return view('livewire.project.shared.metrics');
}
}

View File

@@ -9,6 +9,8 @@ class Show extends Component
{
public Project $project;
public $environments;
public function mount()
{
$projectUuid = request()->route('project_uuid');
@@ -18,7 +20,8 @@ class Show extends Component
if (! $project) {
return redirect()->route('dashboard');
}
$project->load(['environments']);
$this->environments = $project->environments->sortBy('created_at');
$this->project = $project;
}

View File

@@ -0,0 +1,62 @@
<?php
namespace App\Livewire\Server;
use App\Models\Server;
use Livewire\Component;
class Charts extends Component
{
public Server $server;
public $chartId = 'server';
public $data;
public $categories;
public int $interval = 5;
public bool $poll = true;
public function pollData()
{
if ($this->poll || $this->interval <= 10) {
$this->loadData();
if ($this->interval > 10) {
$this->poll = false;
}
}
}
public function loadData()
{
try {
$cpuMetrics = $this->server->getCpuMetrics($this->interval);
$memoryMetrics = $this->server->getMemoryMetrics($this->interval);
$cpuMetrics = collect($cpuMetrics)->map(function ($metric) {
return [$metric[0], $metric[1]];
});
$memoryMetrics = collect($memoryMetrics)->map(function ($metric) {
return [$metric[0], $metric[1]];
});
$this->dispatch("refreshChartData-{$this->chartId}-cpu", [
'seriesData' => $cpuMetrics,
]);
$this->dispatch("refreshChartData-{$this->chartId}-memory", [
'seriesData' => $memoryMetrics,
]);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function setInterval()
{
if ($this->interval <= 10) {
$this->poll = true;
}
$this->loadData();
}
}

View File

@@ -21,7 +21,7 @@ class ConfigureCloudflareTunnels extends Component
$server->settings->is_cloudflare_tunnel = true;
$server->settings->save();
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
$this->dispatch('serverInstalled');
$this->dispatch('refreshServerShow');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -37,7 +37,7 @@ class ConfigureCloudflareTunnels extends Component
$server->save();
$server->settings->save();
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
$this->dispatch('serverInstalled');
$this->dispatch('refreshServerShow');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -2,6 +2,9 @@
namespace App\Livewire\Server;
use App\Actions\Server\StartSentinel;
use App\Actions\Server\StopSentinel;
use App\Jobs\PullSentinelImageJob;
use App\Models\Server;
use Livewire\Component;
@@ -36,7 +39,12 @@ class Form extends Component
'server.settings.is_build_server' => 'required|boolean',
'server.settings.concurrent_builds' => 'required|integer|min:1',
'server.settings.dynamic_timeout' => 'required|integer|min:1',
'server.settings.is_metrics_enabled' => 'required|boolean',
'server.settings.metrics_token' => 'required',
'server.settings.metrics_refresh_rate_seconds' => 'required|integer|min:1',
'server.settings.metrics_history_days' => 'required|integer|min:1',
'wildcard_domain' => 'nullable|url',
'server.settings.is_server_api_enabled' => 'required|boolean',
];
protected $validationAttributes = [
@@ -52,7 +60,11 @@ class Form extends Component
'server.settings.is_build_server' => 'Build Server',
'server.settings.concurrent_builds' => 'Concurrent Builds',
'server.settings.dynamic_timeout' => 'Dynamic Timeout',
'server.settings.is_metrics_enabled' => 'Metrics',
'server.settings.metrics_token' => 'Metrics Token',
'server.settings.metrics_refresh_rate_seconds' => 'Metrics Interval',
'server.settings.metrics_history_days' => 'Metrics History',
'server.settings.is_server_api_enabled' => 'Server API',
];
public function mount()
@@ -69,18 +81,59 @@ class Form extends Component
public function updatedServerSettingsIsBuildServer()
{
$this->dispatch('serverInstalled');
$this->dispatch('refreshServerShow');
$this->dispatch('serverRefresh');
$this->dispatch('proxyStatusUpdated');
}
public function checkPortForServerApi()
{
try {
if ($this->server->settings->is_server_api_enabled === true) {
$this->server->checkServerApi();
$this->dispatch('success', 'Server API is reachable.');
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function instantSave()
{
try {
refresh_server_connection($this->server->privateKey);
$this->validateServer(false);
$this->server->settings->save();
$this->server->save();
$this->dispatch('success', 'Server updated.');
$this->dispatch('refreshServerShow');
if ($this->server->isSentinelEnabled()) {
PullSentinelImageJob::dispatchSync($this->server);
ray('Sentinel is enabled');
if ($this->server->settings->isDirty('is_metrics_enabled')) {
$this->dispatch('reloadWindow');
}
if ($this->server->settings->isDirty('is_server_api_enabled') && $this->server->settings->is_server_api_enabled === true) {
ray('Starting sentinel');
}
} else {
ray('Sentinel is not enabled');
StopSentinel::dispatch($this->server);
}
// $this->checkPortForServerApi();
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function restartSentinel()
{
try {
$version = get_latest_sentinel_version();
StartSentinel::run($this->server, $version, true);
$this->dispatch('success', 'Sentinel restarted.');
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -14,7 +14,7 @@ class Show extends Component
public $parameters = [];
protected $listeners = ['serverInstalled' => '$refresh'];
protected $listeners = ['refreshServerShow' => '$refresh'];
public function mount()
{

View File

@@ -143,7 +143,8 @@ class ValidateAndInstall extends Component
} else {
$this->docker_version = $this->server->validateDockerEngineVersion();
if ($this->docker_version) {
$this->dispatch('serverInstalled');
$this->dispatch('refreshServerShow');
$this->dispatch('refreshBoardingIndex');
$this->dispatch('success', 'Server validated.');
} else {
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';

View File

@@ -29,6 +29,7 @@ class Configuration extends Component
'settings.public_port_min' => 'required',
'settings.public_port_max' => 'required',
'settings.custom_dns_servers' => 'nullable',
'settings.instance_name' => 'nullable',
];
protected $validationAttributes = [

View File

@@ -228,18 +228,13 @@ class Application extends BaseModel
public function gitCommitLink($link): string
{
if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) {
if (! is_null(data_get($this, 'source.html_url')) && ! is_null(data_get($this, 'git_repository')) && ! is_null(data_get($this, 'git_branch'))) {
if (str($this->source->html_url)->contains('bitbucket')) {
return "{$this->source->html_url}/{$this->git_repository}/commits/{$link}";
}
return "{$this->source->html_url}/{$this->git_repository}/commit/{$link}";
}
if (strpos($this->git_repository, 'git@') === 0) {
$git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
return "https://{$git_repository}/commit/{$link}";
}
if (str($this->git_repository)->contains('bitbucket')) {
$git_repository = str_replace('.git', '', $this->git_repository);
$url = Url::fromString($git_repository);
@@ -248,6 +243,14 @@ class Application extends BaseModel
return $url->__toString();
}
if (strpos($this->git_repository, 'git@') === 0) {
$git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
if (data_get($this, 'source.html_url')) {
return "{$this->source->html_url}/{$git_repository}/commit/{$link}";
}
return "{$git_repository}/commit/{$link}";
}
return $this->git_repository;
}
@@ -532,7 +535,7 @@ class Application extends BaseModel
public function get_last_successful_deployment()
{
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', 'finished')->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first();
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', ApplicationDeploymentStatus::FINISHED)->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first();
}
public function get_last_days_deployments()
@@ -1167,4 +1170,44 @@ class Application extends BaseModel
return $preview;
}
public static function getDomainsByUuid(string $uuid): array
{
$application = self::where('uuid', $uuid)->first();
if ($application) {
return $application->fqdns;
}
return [];
}
public function getMetrics(int $mins = 5)
{
$server = $this->destination->server;
$container_name = $this->uuid;
if ($server->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
if (str($metrics)->contains('error')) {
$error = json_decode($metrics, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$metrics = str($metrics)->explode("\n")->skip(1)->all();
$parsedCollection = collect($metrics)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
});
});
return $parsedCollection->toArray();
}
}
}

View File

@@ -109,7 +109,7 @@ class Environment extends Model
protected function name(): Attribute
{
return Attribute::make(
set: fn (string $value) => strtolower($value),
set: fn (string $value) => str($value)->lower()->trim()->replace('/', '-')->toString(),
);
}
}

View File

@@ -47,4 +47,14 @@ class InstanceSettings extends Model implements SendsEmail
return explode(',', $recipients);
}
public function getTitleDisplayName(): string
{
$instanceName = $this->instance_name;
if (! $instanceName) {
return '';
}
return "[{$instanceName}]";
}
}

View File

@@ -2,6 +2,4 @@
namespace App\Models;
class Kubernetes extends BaseModel
{
}
class Kubernetes extends BaseModel {}

View File

@@ -112,4 +112,14 @@ class Project extends BaseModel
{
return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get());
}
public function default_environment()
{
$default = $this->environments()->where('name', 'production')->first();
if (! $default) {
$default = $this->environments()->get()->sortBy('created_at')->first();
}
return $default;
}
}

View File

@@ -5,12 +5,11 @@ namespace App\Models;
use App\Actions\Server\InstallDocker;
use App\Enums\ProxyTypes;
use App\Jobs\PullSentinelImageJob;
use App\Notifications\Server\Revived;
use App\Notifications\Server\Unreachable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Support\Stringable;
@@ -462,10 +461,44 @@ $schema://$host {
Storage::disk('ssh-mux')->delete($this->muxFilename());
}
public function isSentinelEnabled()
{
return $this->isMetricsEnabled() || $this->isServerApiEnabled();
}
public function isMetricsEnabled()
{
return $this->settings->is_metrics_enabled;
}
public function isServerApiEnabled()
{
return $this->settings->is_server_api_enabled;
}
public function checkServerApi()
{
if ($this->isServerApiEnabled()) {
$server_ip = $this->ip;
if (isDev()) {
if ($this->id === 0) {
$server_ip = 'localhost';
}
}
$command = "curl -s http://{$server_ip}:12172/api/health";
$process = Process::timeout(5)->run($command);
if ($process->failed()) {
ray($process->exitCode(), $process->output(), $process->errorOutput());
throw new \Exception("Server API is not reachable on http://{$server_ip}:12172");
}
}
}
public function checkSentinel()
{
ray("Checking sentinel on server: {$this->name}");
if ($this->is_metrics_enabled) {
if ($this->isSentinelEnabled()) {
$sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false);
$sentinel_found = json_decode($sentinel_found, true);
$status = data_get($sentinel_found, '0.State.Status', 'exited');
@@ -478,21 +511,57 @@ $schema://$host {
}
}
public function getMetrics()
public function getCpuMetrics(int $mins = 5)
{
if ($this->is_metrics_enabled) {
$from = now()->subMinutes(5)->toIso8601ZuluString();
$cpu = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl http://localhost:8888/api/cpu/history?from=$from'"], $this, false);
if ($this->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$cpu = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$this->settings->metrics_token}\" http://localhost:8888/api/cpu/history?from=$from'"], $this, false);
if (str($cpu)->contains('error')) {
$error = json_decode($cpu, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$cpu = str($cpu)->explode("\n")->skip(1)->all();
$parsedCollection = collect($cpu)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $value] = explode(',', trim($line));
[$time, $cpu_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 0);
return [(int) $time, (float) $value];
return [(int) $time, (float) $cpu_usage_percent];
});
})->toArray();
});
return $parsedCollection;
return $parsedCollection->toArray();
}
}
public function getMemoryMetrics(int $mins = 5)
{
if ($this->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$memory = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$this->settings->metrics_token}\" http://localhost:8888/api/memory/history?from=$from'"], $this, false);
if (str($memory)->contains('error')) {
$error = json_decode($memory, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$memory = str($memory)->explode("\n")->skip(1)->all();
$parsedCollection = collect($memory)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $used, $free, $usedPercent] = explode(',', trim($line));
$usedPercent = number_format($usedPercent, 0);
return [(int) $time, (float) $usedPercent];
});
});
return $parsedCollection->toArray();
}
}

View File

@@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Symfony\Component\Yaml\Yaml;
class Service extends BaseModel
{
@@ -837,14 +838,34 @@ class Service extends BaseModel
$commands[] = "mkdir -p $workdir";
$commands[] = "cd $workdir";
$json = Yaml::parse($this->docker_compose);
$envs_from_coolify = $this->environment_variables()->get();
foreach ($json['services'] as $service => $config) {
$envs = collect($config['environment']);
$envs->push("COOLIFY_CONTAINER_NAME=$service-{$this->uuid}");
foreach ($envs_from_coolify as $env) {
$envs = $envs->map(function ($value) use ($env) {
if (str($value)->startsWith($env->key)) {
return "{$env->key}={$env->real_value}";
}
return $value;
});
}
$envs = $envs->unique();
data_set($json, "services.$service.environment", $envs->toArray());
}
$this->docker_compose = Yaml::dump($json, 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
$docker_compose_base64 = base64_encode($this->docker_compose);
$commands[] = "echo $docker_compose_base64 | base64 -d | tee docker-compose.yml > /dev/null";
$envs = $this->environment_variables()->get();
$commands[] = 'rm -f .env || true';
foreach ($envs as $env) {
foreach ($envs_from_coolify as $env) {
$commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
}
if ($envs->count() === 0) {
if ($envs_from_coolify->count() === 0) {
$commands[] = 'touch .env';
}
instant_remote_process($commands, $this->server);

View File

@@ -226,4 +226,33 @@ class StandaloneClickhouse extends BaseModel
{
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
}
public function getMetrics(int $mins = 5)
{
$server = $this->destination->server;
$container_name = $this->uuid;
if ($server->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
if (str($metrics)->contains('error')) {
$error = json_decode($metrics, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$metrics = str($metrics)->explode("\n")->skip(1)->all();
$parsedCollection = collect($metrics)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
});
});
return $parsedCollection->toArray();
}
}
}

View File

@@ -226,4 +226,33 @@ class StandaloneDragonfly extends BaseModel
{
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
}
public function getMetrics(int $mins = 5)
{
$server = $this->destination->server;
$container_name = $this->uuid;
if ($server->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
if (str($metrics)->contains('error')) {
$error = json_decode($metrics, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$metrics = str($metrics)->explode("\n")->skip(1)->all();
$parsedCollection = collect($metrics)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
});
});
return $parsedCollection->toArray();
}
}
}

View File

@@ -226,4 +226,33 @@ class StandaloneKeydb extends BaseModel
{
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
}
public function getMetrics(int $mins = 5)
{
$server = $this->destination->server;
$container_name = $this->uuid;
if ($server->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
if (str($metrics)->contains('error')) {
$error = json_decode($metrics, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$metrics = str($metrics)->explode("\n")->skip(1)->all();
$parsedCollection = collect($metrics)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
});
});
return $parsedCollection->toArray();
}
}
}

View File

@@ -226,4 +226,33 @@ class StandaloneMariadb extends BaseModel
{
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
}
public function getMetrics(int $mins = 5)
{
$server = $this->destination->server;
$container_name = $this->uuid;
if ($server->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
if (str($metrics)->contains('error')) {
$error = json_decode($metrics, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$metrics = str($metrics)->explode("\n")->skip(1)->all();
$parsedCollection = collect($metrics)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
});
});
return $parsedCollection->toArray();
}
}
}

View File

@@ -246,4 +246,33 @@ class StandaloneMongodb extends BaseModel
{
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
}
public function getMetrics(int $mins = 5)
{
$server = $this->destination->server;
$container_name = $this->uuid;
if ($server->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
if (str($metrics)->contains('error')) {
$error = json_decode($metrics, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$metrics = str($metrics)->explode("\n")->skip(1)->all();
$parsedCollection = collect($metrics)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
});
});
return $parsedCollection->toArray();
}
}
}

View File

@@ -227,4 +227,33 @@ class StandaloneMysql extends BaseModel
{
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
}
public function getMetrics(int $mins = 5)
{
$server = $this->destination->server;
$container_name = $this->uuid;
if ($server->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
if (str($metrics)->contains('error')) {
$error = json_decode($metrics, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$metrics = str($metrics)->explode("\n")->skip(1)->all();
$parsedCollection = collect($metrics)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
});
});
return $parsedCollection->toArray();
}
}
}

View File

@@ -227,4 +227,33 @@ class StandalonePostgresql extends BaseModel
{
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
}
public function getMetrics(int $mins = 5)
{
$server = $this->destination->server;
$container_name = $this->uuid;
if ($server->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
if (str($metrics)->contains('error')) {
$error = json_decode($metrics, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$metrics = str($metrics)->explode("\n")->skip(1)->all();
$parsedCollection = collect($metrics)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
});
});
return $parsedCollection->toArray();
}
}
}

View File

@@ -222,4 +222,33 @@ class StandaloneRedis extends BaseModel
{
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
}
public function getMetrics(int $mins = 5)
{
$server = $this->destination->server;
$container_name = $this->uuid;
if ($server->isMetricsEnabled()) {
$from = now()->subMinutes($mins)->toIso8601ZuluString();
$metrics = instant_remote_process(["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$server->settings->metrics_token}\" http://localhost:8888/api/container/{$container_name}/metrics/history?from=$from'"], $server, false);
if (str($metrics)->contains('error')) {
$error = json_decode($metrics, true);
$error = data_get($error, 'error', 'Something is not okay, are you okay?');
if ($error == 'Unauthorized') {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
}
throw new \Exception($error);
}
$metrics = str($metrics)->explode("\n")->skip(1)->all();
$parsedCollection = collect($metrics)->flatMap(function ($item) {
return collect(explode("\n", trim($item)))->map(function ($line) {
[$time, $cpu_usage_percent, $memory_usage, $memory_usage_percent] = explode(',', trim($line));
$cpu_usage_percent = number_format($cpu_usage_percent, 2);
return [(int) $time, (float) $cpu_usage_percent, (int) $memory_usage];
});
});
return $parsedCollection->toArray();
}
}
}

View File

@@ -14,9 +14,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
public $tries = 1;
public function __construct(public string $name, public Server $server, public ?string $url = null)
{
}
public function __construct(public string $name, public Server $server, public ?string $url = null) {}
public function via(object $notifiable): array
{

View File

@@ -14,9 +14,7 @@ class ContainerStopped extends Notification implements ShouldQueue
public $tries = 1;
public function __construct(public string $name, public Server $server, public ?string $url = null)
{
}
public function __construct(public string $name, public Server $server, public ?string $url = null) {}
public function via(object $notifiable): array
{

View File

@@ -16,9 +16,7 @@ class DailyBackup extends Notification implements ShouldQueue
public $tries = 1;
public function __construct(public $databases)
{
}
public function __construct(public $databases) {}
public function via(object $notifiable): array
{

View File

@@ -14,9 +14,7 @@ class GeneralNotification extends Notification implements ShouldQueue
public $tries = 1;
public function __construct(public string $message)
{
}
public function __construct(public string $message) {}
public function via(object $notifiable): array
{

View File

@@ -15,9 +15,7 @@ class DockerCleanup extends Notification implements ShouldQueue
public $tries = 1;
public function __construct(public Server $server, public string $message)
{
}
public function __construct(public Server $server, public string $message) {}
public function via(object $notifiable): array
{

View File

@@ -17,9 +17,7 @@ class ForceDisabled extends Notification implements ShouldQueue
public $tries = 1;
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function via(object $notifiable): array
{

View File

@@ -17,9 +17,7 @@ class ForceEnabled extends Notification implements ShouldQueue
public $tries = 1;
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function via(object $notifiable): array
{

View File

@@ -17,9 +17,7 @@ class HighDiskUsage extends Notification implements ShouldQueue
public $tries = 1;
public function __construct(public Server $server, public int $disk_usage, public int $cleanup_after_percentage)
{
}
public function __construct(public Server $server, public int $disk_usage, public int $cleanup_after_percentage) {}
public function via(object $notifiable): array
{

View File

@@ -24,7 +24,7 @@ class Revived extends Notification implements ShouldQueue
if ($this->server->unreachable_notification_sent === false) {
return;
}
GetContainersStatus::dispatch($server);
GetContainersStatus::dispatch($server)->onQueue('high');
// dispatch(new ContainerStatusJob($server));
}

View File

@@ -17,10 +17,7 @@ class Unreachable extends Notification implements ShouldQueue
public $tries = 1;
public function __construct(public Server $server)
{
}
public function __construct(public Server $server) {}
public function via(object $notifiable): array
{

View File

@@ -13,9 +13,7 @@ class Test extends Notification implements ShouldQueue
public $tries = 5;
public function __construct(public ?string $emails = null)
{
}
public function __construct(public ?string $emails = null) {}
public function via(object $notifiable): array
{

View File

@@ -22,9 +22,7 @@ class InvitationLink extends Notification implements ShouldQueue
return [TransactionalEmailChannel::class];
}
public function __construct(public User $user)
{
}
public function __construct(public User $user) {}
public function toMail(): MailMessage
{

View File

@@ -14,9 +14,7 @@ class Test extends Notification implements ShouldQueue
public $tries = 5;
public function __construct(public string $emails)
{
}
public function __construct(public string $emails) {}
public function via(): array
{

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