Compare commits

..

265 Commits

Author SHA1 Message Date
Mauricio Siu
01e5cf0852 Merge pull request #580 from Dokploy/canary
v0.10.2
2024-10-22 21:17:38 -06:00
Mauricio Siu
8faa6ae1cf chore(version): bump version 2024-10-22 20:29:57 -06:00
Mauricio Siu
76ed1107c2 refactor(dokploy): add -r flag to read the enviroments vars 2024-10-22 20:19:49 -06:00
Mauricio Siu
cff5049096 Merge pull request #579 from Dokploy/578-unable-to-reset-my-password
fix(dokploy): use the exact path of functions #578
2024-10-22 20:02:43 -06:00
Mauricio Siu
cb5ca100a6 fix(dokploy): use the exact path of functions #578 2024-10-22 20:00:29 -06:00
Mauricio Siu
03d1e974dd Update LICENSE.MD 2024-10-22 16:53:12 -06:00
Mauricio Siu
b609d72d1c Merge pull request #576 from Dokploy/541-install-failed-due-to-docker-swarm-initialize-failed
fix(installation): exit of script when docker swarm init fails
2024-10-21 21:58:22 -06:00
Mauricio Siu
d7071fba60 fix(installation): exit of script when docker swarm init fails 2024-10-21 21:50:26 -06:00
Mauricio Siu
76585991ec Merge pull request #559 from eremannisto/fix/improve-faq-questions-and-answers
fix: Improve `FAQ` questions and answers
2024-10-21 21:31:00 -06:00
Mauricio Siu
da6efcf733 Merge pull request #406 from benbristow/fix/directus
fix: directus healthchecks (fix race condition starting up), volumes for uploads & extensions, add secret/db password generation, bump version to 11.0.2
2024-10-21 21:28:52 -06:00
Mauricio Siu
64b0770cfb Merge pull request #575 from Dokploy/574-clone-github-gitlab-and-bitbucket-submodules
feat(dokploy): add recurse submodules to providers #331
2024-10-21 21:25:18 -06:00
Mauricio Siu
1ec83a3236 feat(dokploy): add recurse submodules to providers #331 2024-10-21 21:14:40 -06:00
Mauricio Siu
df9fad088f Merge pull request #567 from Dokploy/canary
v0.10.1
2024-10-18 23:12:12 -06:00
Mauricio Siu
7d5a660f4d chore: bump version 2024-10-18 21:56:39 -06:00
Mauricio Siu
f7f0cbf318 Merge pull request #566 from arioberek/canary
fix: update reset password link URL
2024-10-18 09:34:51 -06:00
Arielton Oberek
841c0731aa fix: remove language prefix from reset password URL 2024-10-18 10:41:07 -03:00
Arielton Oberek
137cd25267 fix: reset password button URL 2024-10-18 10:35:08 -03:00
Mauricio Siu
4dcd16c41e Merge pull request #564 from Dokploy/fix/git-ssh
fix(git): remove old references to ssh files to use the tmp file
2024-10-18 00:28:51 -06:00
Mauricio Siu
60497fe59d refactor: add husky 2024-10-18 00:10:01 -06:00
Mauricio Siu
8536945a60 styles: lint 2024-10-17 23:58:51 -06:00
Mauricio Siu
e0a8d8258c fix(git): remove old references to ssh files to use the tmp file 2024-10-17 23:55:27 -06:00
Mauricio Siu
fe19cdb5e4 fix(setup): import directly from specific path 2024-10-16 15:57:51 -06:00
Mauricio Siu
c4654a9619 chore(contributing): update contributing 2024-10-16 13:23:25 -06:00
Ere Männistö
0a123a652b Improve FAQ questions and answers
- Improve questions and answers
- Fix typos
2024-10-15 19:54:41 +03:00
Mauricio Siu
5d437c29b2 refactor: remove header and navbar 2024-10-13 22:58:01 -06:00
Mauricio Siu
53f345ab1d feat: add privacy & terms 2024-10-13 22:54:02 -06:00
Mauricio Siu
2644b638d1 Merge pull request #553 from Dokploy/canary
v0.10.0
2024-10-13 16:30:00 -06:00
Mauricio Siu
8785282133 chore(version): bump version 2024-10-13 11:51:52 -06:00
Mauricio Siu
35c084af1d Merge pull request #550 from Dokploy/fix/env-parsing
fix(logs): improve logs in remote server when is error, and fix the e…
2024-10-13 11:01:28 -06:00
Mauricio Siu
b63488baba Merge pull request #547 from Dokploy/536-implement-custom-certificates-in-external-server
feat(certificates): create certificates in a remote server
2024-10-13 11:01:16 -06:00
Mauricio Siu
7e5f21b28e style: lint 2024-10-13 02:28:04 -06:00
Mauricio Siu
8d41bafb93 fix(logs): improve logs in remote server when is error, and fix the env parsing to allow any values in enviroment variables 2024-10-13 02:27:33 -06:00
Mauricio Siu
6f5049efd5 styles: lint 2024-10-12 19:33:56 -06:00
Mauricio Siu
6dd6b636e5 feat(certificates): create certificates in a remote server 2024-10-12 19:09:50 -06:00
Mauricio Siu
8488d530f3 Merge pull request #546 from Dokploy/542-cannot-save-server-address-without-lets-encrypt-email
fix(traefik): allow to save domain without letsencrypt email when the…
2024-10-12 16:58:50 -06:00
Mauricio Siu
6c61d5cdf5 Merge pull request #545 from mezotv/fix-sponsor-size
fix(website): improve sponsor logo alignment
2024-10-12 16:45:40 -06:00
Mauricio Siu
8036455c2d fix(traefik): allow to save domain without letsencrypt email when the cert is none #542 2024-10-12 16:44:10 -06:00
Dominik Koch
bf44eeab3d style: format code 2024-10-12 22:29:32 +00:00
Dominik Koch
0cd185696d fix: add target blank to links 2024-10-12 22:26:48 +00:00
Mauricio Siu
339697437a Merge pull request #544 from Dokploy/533-the-traefik-labels-get-duplicated-if-i-have-two-services-and-two-domains-in-one-docker-compose-file
fix(compose): refetch compose file when enter to the modal to prevent…
2024-10-12 16:26:43 -06:00
Dominik Koch
e0b15fe971 fix: logo alignment 2024-10-12 22:23:07 +00:00
Mauricio Siu
afc5ea43da fix(compose): refetch compose file when enter to the modal to prevent show duplicate labels 2024-10-12 16:19:49 -06:00
Mauricio Siu
bb337e819e Merge pull request #539 from mezotv/fix-typo
Fix typos and grammar errors
2024-10-12 16:11:27 -06:00
Mauricio Siu
160dd10f77 Merge pull request #538 from mezotv/update-website-sponsors
Add organization sponsors to website
2024-10-12 16:10:46 -06:00
Dominik Koch
49b096fef6 style: format code 2024-10-12 21:08:58 +00:00
Dominik Koch
4828b840cb fix: lxaer logo 2024-10-12 23:05:30 +02:00
Dominik Koch
67af70448b Update README.md 2024-10-12 23:03:09 +02:00
Dominik Koch
946acf5245 style: format code 2024-10-12 20:59:49 +00:00
Dominik Koch
390b3a835a fix: new lxaer logo 2024-10-12 22:54:46 +02:00
Dominik Koch
2842bf9a91 Update README.md 2024-10-12 22:51:56 +02:00
Dominik Koch
8a0e10f6f4 Update README.md 2024-10-12 22:51:03 +02:00
Mauricio Siu
67efa82b91 Merge pull request #537 from mezotv/organization-readme
Add missing organization to readme
2024-10-11 12:41:27 -06:00
Dominik Koch
546d6b87ea fix: get rid of typos and fix grammar 2024-10-11 20:03:20 +02:00
Dominik Koch
d012d19253 feat(web): add organization sponsors 2024-10-11 11:28:10 +02:00
Dominik Koch
e925ed9ea4 fix: add missing organization to readme 2024-10-11 11:21:20 +02:00
Mauricio Siu
0c05809d7d refactor: remove 2024-10-10 23:38:16 -06:00
Mauricio Siu
29f1631950 refactor: add dynamic import queue 2024-10-10 23:34:56 -06:00
Mauricio Siu
9c0c58035a chore; add logs for process env 2024-10-10 22:40:02 -06:00
Mauricio Siu
f4262569dd chore: add cat 2024-10-10 22:37:36 -06:00
Mauricio Siu
99cf6eae49 refactor: adjust enviroment variables 2024-10-10 22:27:25 -06:00
Mauricio Siu
0488546706 refactor: print env 2024-10-10 22:17:19 -06:00
Mauricio Siu
dc32cd71e5 refactor: add env to dist 2024-10-10 22:01:14 -06:00
Mauricio Siu
efdfd5d13c chore: print envs 2024-10-10 21:52:50 -06:00
Mauricio Siu
d31cab76f0 refactor: add flag 2024-10-10 21:52:09 -06:00
Mauricio Siu
00ed202127 refactor: add .env 2024-10-10 21:40:43 -06:00
Mauricio Siu
aaa4ca297d refacctor: add missing envs 2024-10-10 21:32:27 -06:00
Mauricio Siu
629871e683 refactor: add option to ci/cd 2024-10-10 21:22:13 -06:00
Mauricio Siu
a237c651c3 chore: add autoprefixer 2024-10-07 01:47:01 -06:00
Mauricio Siu
4f9b0d9d59 chore: remove .env 2024-10-07 01:45:16 -06:00
Mauricio Siu
b701a0b504 Merge pull request #531 from Dokploy/feat/cloud
Feat/cloud
2024-10-07 01:44:00 -06:00
Mauricio Siu
39036202bb test: fix mock zip drop 2024-10-07 01:34:35 -06:00
Mauricio Siu
4225ad83e7 chore: add docker images 2024-10-07 01:28:22 -06:00
Mauricio Siu
38c4e0ede1 chore: add build server 2024-10-07 01:16:59 -06:00
Mauricio Siu
25a64c703f chore: add pnpm run build 2024-10-07 01:08:47 -06:00
Mauricio Siu
ab5871add7 chore: biome 2024-10-07 01:05:47 -06:00
Mauricio Siu
2b0e009f6a refactor: remove feature tag 2024-10-07 00:59:07 -06:00
Mauricio Siu
9b6ea99eea refactor: remove unused files 2024-10-07 00:49:54 -06:00
Mauricio Siu
c4cf545d85 Merge branch 'canary' into feat/cloud 2024-10-07 00:31:23 -06:00
Mauricio Siu
1edd717432 Merge pull request #524 from lorenzomigliorero/feat/domains-link
feat: add dropdown link
2024-10-07 00:26:23 -06:00
Mauricio Siu
5b88af6158 refactor: remove findAdmin 2024-10-06 15:04:54 -06:00
Mauricio Siu
e995d894d8 refactor: remove docker from cloud 2024-10-06 14:27:14 -06:00
Mauricio Siu
5f56512e56 refactor: update queue jobs 2024-10-06 14:16:31 -06:00
Mauricio Siu
24e4930fc1 refactor: use connection IORedis 2024-10-06 02:57:46 -06:00
Mauricio Siu
541728805f refactor: add health path to middleware 2024-10-06 02:46:07 -06:00
Mauricio Siu
9e4bac1386 refactor: update ioredis connection 2024-10-06 02:44:35 -06:00
Mauricio Siu
ed8d32d050 chore: add type module 2024-10-06 02:25:02 -06:00
Mauricio Siu
7fb66bc58b refactor:add remote cron jobs 2024-10-06 02:19:15 -06:00
Mauricio Siu
58c06fba86 refactor(cloud): add api key for autentication between servers 2024-10-06 01:56:53 -06:00
Mauricio Siu
3cf27a068a refactor: add authorization 2024-10-06 01:37:39 -06:00
Mauricio Siu
7cfbea3f60 refactor: update 2024-10-05 22:18:33 -06:00
Mauricio Siu
7907e33431 refactor: update namefile 2024-10-05 22:17:44 -06:00
Mauricio Siu
89f3078ce5 refactor: update package name 2024-10-05 22:15:57 -06:00
Mauricio Siu
f3ce69b656 refactor: rename builders to server 2024-10-05 22:15:47 -06:00
Mauricio Siu
43555cdabe feat(schedules): add schedules server 2024-10-05 22:11:38 -06:00
Mauricio Siu
651bf3a303 refactor: update ref 2024-10-05 18:42:08 -06:00
Mauricio Siu
4cde1a8a7d refactor: close docker logs 2024-10-05 18:20:28 -06:00
Mauricio Siu
405efcac0b refactor: update logs listener 2024-10-05 17:34:17 -06:00
Mauricio Siu
8397de0dca refactor: close connections when the ws is not ready 2024-10-05 17:17:46 -06:00
Mauricio Siu
06b58e6495 refactor: close connection ws 2024-10-05 16:46:02 -06:00
Mauricio Siu
24db4006cf refactor: add close ws 2024-10-05 16:45:53 -06:00
Mauricio Siu
84009c5e9b refactor: add close websocket 2024-10-05 16:04:28 -06:00
Mauricio Siu
e32afde973 refactor: add status ok 2024-10-05 14:37:45 -06:00
Mauricio Siu
fecffac573 refactor: test rollback 2024-10-05 14:27:00 -06:00
Mauricio Siu
b4448e013c test: rollback 2024-10-05 14:16:23 -06:00
Mauricio Siu
c3f06a6272 feat(cloud): add healtchecks 2024-10-05 13:34:00 -06:00
Mauricio Siu
2be724f780 Update README.md 2024-10-05 02:39:50 -06:00
Mauricio Siu
bf78326c96 chore: use the same tailwindcss version 2024-10-05 01:30:09 -06:00
Mauricio Siu
4ca8722c6e refactor: optimize dockerfile 2024-10-05 01:21:39 -06:00
Mauricio Siu
e56e1eb687 refactor: add autoprefixer 2024-10-05 01:15:52 -06:00
Mauricio Siu
5a5c302bdc chore: optimize image 2024-10-05 01:11:15 -06:00
Mauricio Siu
997dc85985 refactor(api): remove deploy normal compose on the same dokploy server 2024-10-05 00:34:41 -06:00
Mauricio Siu
09ef851372 refactor(cloud): add validation to prevent create applications without server 2024-10-04 21:31:22 -06:00
Mauricio Siu
7c4987d84d refactor(cloud): add validation to prevent access to shared resources 2024-10-04 20:44:57 -06:00
Mauricio Siu
5cebf5540a refactor(cloud): add deploy to external API 2024-10-04 18:53:46 -06:00
Mauricio Siu
3df2f8e58c refactor(terminal): use ssh2 instead of cmd 2024-10-04 17:24:17 -06:00
Mauricio Siu
a642d36a23 refactor: hide handler when is cloud version 2024-10-04 15:20:47 -06:00
Mauricio Siu
72bceec62d refactor: add try catch 2024-10-04 15:16:01 -06:00
Mauricio Siu
3d32314e80 chore: update lock 2024-10-04 15:14:56 -06:00
Mauricio Siu
7259830ac1 chore: add experimental specifier resolution flag 2024-10-04 15:14:30 -06:00
Mauricio Siu
daa87c0dc7 refactor(cloud): add is cloud flag to cluters 2024-10-04 14:40:31 -06:00
Mauricio Siu
a2ee55e0e9 Merge pull request #527 from kikoncuo/fix/application-create-missing-return
fix: Application create endpoint returns value
2024-10-04 13:39:21 -06:00
Enrique
de6aeac243 fix(application.create): add missing return statement and align response with application.one 2024-10-04 11:46:14 +02:00
Lorenzo Migliorero
f640b4a87f stop propagation 2024-10-04 11:03:11 +02:00
Mauricio Siu
3747db08d4 refactor(cloud): add validation to prevent execute in cloud version 2024-10-04 01:12:14 -06:00
Mauricio Siu
ab4677ac0e refactor(auth): set null when the findAdmin is null 2024-10-04 01:01:30 -06:00
Mauricio Siu
172d55311e chore(cloud): add migrations 2024-10-04 00:04:52 -06:00
Mauricio Siu
3ce25e2ac8 chore(migrations): remove migrations 2024-10-03 23:47:34 -06:00
Mauricio Siu
388ded9aa5 feat(cloud): add deploy on remote worker 2024-10-03 23:46:26 -06:00
Mauricio Siu
767d3e1944 refactor(cloud): add validation to prevent access to shared resources 2024-10-03 19:50:17 -06:00
Mauricio Siu
ec1d6c7430 refactor(cloud): add validation to prevent access to resources from another admin 2024-10-03 19:48:49 -06:00
Mauricio Siu
8abeae5e63 refactor(cloud): validate all the routes to prevent get access from private resource 2024-10-03 19:34:38 -06:00
Mauricio Siu
cc90d9ec9b Merge branch 'canary' into feat/cloud 2024-10-03 13:39:06 -06:00
Mauricio Siu
6b5de00fb0 chore: update dev builder command 2024-10-03 12:01:32 -06:00
Mauricio Siu
5ed96fb0ce Merge pull request #523 from lorenzomigliorero/fix/watch-mode
fix: tsx watch flag unwanted reload
2024-10-03 11:01:57 -06:00
Mauricio Siu
a73af1d578 Merge pull request #522 from lorenzomigliorero/fix/bitbucket-repositories-length
fix: bitbucket repositories length
2024-10-03 11:00:25 -06:00
Mauricio Siu
5867a27901 Merge pull request #459 from seppulcro/feat/add-roundcube-template
feat: add roundcube template
2024-10-03 10:59:41 -06:00
Lorenzo Migliorero
8f7bffc349 add fragment 2024-10-03 15:32:45 +02:00
Lorenzo Migliorero
21ee22d4f5 add dropdown link 2024-10-03 15:27:48 +02:00
Lorenzo Migliorero
fb72132a4b remove watch flag 2024-10-03 14:05:12 +02:00
Lorenzo Migliorero
ca904c15d9 remove console.log 2024-10-03 13:55:28 +02:00
Lorenzo Migliorero
682863f83e fix: repo length 2024-10-03 13:54:40 +02:00
Mauricio Siu
acd722678e Merge pull request #521 from Dokploy/canary
v0.9.4
2024-10-03 02:12:03 -06:00
Mauricio Siu
3750977f41 Merge pull request #520 from Dokploy/487-private-docker-container-pull-failed-despite-having-docker-registry-configured-in-registry
fix(registry): add option to login the registry in the remote server
2024-10-03 02:02:51 -06:00
Mauricio Siu
9b401059b0 fix(registry): add option to login the registry in the remote server 2024-10-03 01:56:50 -06:00
Mauricio Siu
6a3ef5c860 Merge pull request #519 from Dokploy/514-failing-to-refresh-docker-composeyml-from-github-repo
fix(compose): delete content when is remote server
2024-10-03 01:32:50 -06:00
seppulcro
a36518a8f0 fix: set static docker image version 2024-10-03 08:09:21 +01:00
Mauricio Siu
a5eb4b0a72 fix(compose): delete content when is remote server 2024-10-03 01:00:35 -06:00
Mauricio Siu
b5c0876dd4 Merge pull request #518 from Dokploy/515-non-admin-users-are-not-able-to-set-up-database-backup
fix(destinations): change admin to protected procedure
2024-10-03 00:53:23 -06:00
Mauricio Siu
9745d12ac8 fix(destinations): change admin to protected procedure 2024-10-03 00:48:00 -06:00
Mauricio Siu
5c72e5a452 refactor: filter by adminId 2024-10-03 00:45:46 -06:00
Mauricio Siu
600f4b2106 refactor: apply migration 2024-10-03 00:15:36 -06:00
Mauricio Siu
c12d37fe0a refactor: revent 2024-10-03 00:06:11 -06:00
Mauricio Siu
bba8d00ba2 refactor: update dockerfile 2024-10-02 23:56:09 -06:00
Mauricio Siu
78665ffbfa refactor(tabs): hide when is cloud version 2024-10-02 23:51:37 -06:00
Mauricio Siu
d41c8c70c3 feat: add ssh key 2024-10-02 22:50:01 -06:00
Mauricio Siu
f13e5d449c Revert "refactor: stash"
This reverts commit d256998677.
2024-10-02 22:37:14 -06:00
Mauricio Siu
d256998677 refactor: stash 2024-10-02 21:55:54 -06:00
Mauricio Siu
4aaf04ce74 Merge pull request #506 from AprilNEA/fix/domin-port-number-convert
Fix port input value becoming NaN
2024-10-02 13:10:17 -06:00
Mauricio Siu
ecfca9419a refactor: remove innecessary conversion 2024-10-02 13:02:20 -06:00
AprilNEA
dfd6764320 styles: format code with prettier 2024-10-02 18:22:21 +00:00
Mauricio Siu
73bf5274f5 chore(version): bump version 2024-10-01 14:28:11 -06:00
AprilNEA
fc38a42587 fix: convert final value 2024-10-01 14:36:45 +00:00
Mauricio Siu
9b255964fe Merge pull request #511 from Dokploy/509-create-compose-modal-remains-open-after-clicking-create
509 create compose modal remains open after clicking create
2024-09-30 21:33:56 -06:00
Mauricio Siu
29f55ca1a0 Merge pull request #496 from missuo/canary
feat: add update option
2024-09-30 15:04:03 -06:00
Mauricio Siu
6a5fb8faff fix(multi-server): show the servers ip instead of the main ip #502 2024-09-30 15:00:32 -06:00
Mauricio Siu
5c225c8d42 fix(modal): close the modal after the creation #509 2024-09-30 15:00:01 -06:00
AprilNEA
c1c5fc978b fix: fix number convert when string empty 2024-09-30 08:35:49 +00:00
Mauricio Siu
ffd19f591d chore: add package manager 2024-09-30 01:00:53 -06:00
Mauricio Siu
81a41a7f31 refactor: update healtcheck 2024-09-30 00:54:49 -06:00
Mauricio Siu
1c9b704ecc refactor: update redis url 2024-09-30 00:51:07 -06:00
Mauricio Siu
edf1fdedf0 refactor: update paths build 2024-09-30 00:47:47 -06:00
Mauricio Siu
8484649071 refactor: update 2024-09-30 00:44:33 -06:00
Mauricio Siu
1e68248611 refactor: update image 2024-09-30 00:41:21 -06:00
Mauricio Siu
b3e35c5838 refactor: upate dockerfile 2024-09-30 00:40:43 -06:00
Mauricio Siu
ddd4ba8135 refactor: add dockerfile 2024-09-30 00:39:53 -06:00
Mauricio Siu
539544d0de refactor: update 2024-09-30 00:38:22 -06:00
Mauricio Siu
123b5d098b refactor: update install 2024-09-30 00:32:54 -06:00
Mauricio Siu
796a9ca11f refactor: add builder workspace 2024-09-30 00:31:56 -06:00
Mauricio Siu
2872ef3ccb feat(api): add dockerfile api 2024-09-30 00:27:14 -06:00
Mauricio Siu
06a772e344 chore: add dotenv 2024-09-29 22:55:41 -06:00
Mauricio Siu
e99666f4c0 fix(esm): add tsc alias 2024-09-29 21:43:25 -06:00
Mauricio Siu
bd243d79e2 refactor: remove logs 2024-09-29 21:01:24 -06:00
Vincent Yang
18b4b23f79 feat: add update option for canary and feature tag 2024-09-29 22:45:39 -04:00
Mauricio Siu
071a9d5104 refactor: cleanup dependencies 2024-09-29 20:35:25 -06:00
Mauricio Siu
61ebd1b16e refactor(server): remove files 2024-09-29 19:14:41 -06:00
Mauricio Siu
9836c988a0 refactor(build): update imports 2024-09-29 18:53:32 -06:00
Mauricio Siu
03d7738032 refactor(dockerfile): update dockerfile 2024-09-29 18:49:07 -06:00
Mauricio Siu
98aa474975 refactor(test): update paths and mocks 2024-09-29 18:44:07 -06:00
Mauricio Siu
7bd6b66551 refactor(multi-server): update path imports 2024-09-29 18:04:45 -06:00
Mauricio Siu
727e50648e Merge pull request #501 from Dokploy/canary
v0.9.3
2024-09-29 16:30:43 -06:00
Mauricio Siu
0b2b20caeb chore(version): bump version 2024-09-29 16:24:35 -06:00
Mauricio Siu
6cc64b4454 refactior(terminal): add port to server connect 2024-09-29 16:24:09 -06:00
Mauricio Siu
349bc89851 Merge pull request #500 from Dokploy/canary
v0.9.2
2024-09-29 14:16:56 -06:00
Mauricio Siu
7046d05f63 Merge pull request #499 from Dokploy/fix/update-ports-validation-
fix(multi-server): remove string validation on port
2024-09-29 14:07:00 -06:00
Mauricio Siu
cef21ac8b5 fix(multi-server): remove string validation on port 2024-09-29 13:31:38 -06:00
Mauricio Siu
2ae7e562bb refactor(server): remove files 2024-09-29 13:28:24 -06:00
Mauricio Siu
e4b998c608 refactor(server): update imports 2024-09-29 11:55:29 -06:00
Mauricio Siu
9b7aacc934 refactor(server): split logic in to packages 2024-09-29 02:28:58 -06:00
Vincent Young
7027f39c48 feat: add update option 2024-09-28 15:06:20 -04:00
Mauricio Siu
9f6f872536 Merge pull request #495 from Dokploy/canary
v0.9.1
2024-09-28 12:00:04 -06:00
Mauricio Siu
cb03b153ac Merge pull request #494 from Dokploy/fix/swagger-token
Fix/swagger token
2024-09-28 02:31:39 -06:00
Mauricio Siu
e5d7a0cb10 Merge pull request #493 from Dokploy/491-bug-nixpacks-publish-directory-issues-with-multi-level-paths
fix(nixpacks): adjust build path on nixpacks static
2024-09-28 02:30:29 -06:00
Mauricio Siu
bf65bc9462 chore(lint): format 2024-09-28 02:25:06 -06:00
Mauricio Siu
b48b9765cd fix(nixpacks): adjust build path on nixpacks static 2024-09-28 02:23:30 -06:00
Mauricio Siu
7cce02f74d Merge pull request #489 from Dokploy/docs/488-env-editor-not-support-multiline-variables
docs: add explanation how to use multiline env variables
2024-09-27 22:38:36 -06:00
Ben
3dc3672406 docs: add explanation how to use multiline env variables 2024-09-27 19:30:41 +02:00
Mauricio Siu
bbfe095045 chore(version): bump version 2024-09-27 11:23:44 -06:00
Mauricio Siu
65c1001751 fix(swagger): add mising validation 2024-09-27 11:22:52 -06:00
Mauricio Siu
5212bde021 Merge pull request #486 from Dokploy/333-add-flexible-wwwnon-www-redirect-option
#333 add flexible www/non-www redirect option
2024-09-27 00:42:30 -06:00
Ben
9059f42b03 refactor: display certificate select field after https switch field inside add-domain dialogs 2024-09-26 13:53:10 +02:00
Ben
3b9f5d6f5c feat: add presets for add-redirect dialog 2024-09-26 13:14:14 +02:00
seppulcro
0aff344bc0 fix: change tags for roundcube template 2024-09-24 17:27:11 +01:00
seppulcro
4715f34e15 fix: mdx-components formatting with biome 2024-09-24 17:01:24 +01:00
seppulcro
59386ed4b7 fix: Update docs; Fix useMDXComponents for li override: add missing id for correct remark-gfm footnotes functionallity 2024-09-23 16:13:16 +01:00
Mauricio Siu
21dee4abac refactor: update social networks 2024-09-22 20:03:48 -06:00
Mauricio Siu
e378d89477 Merge pull request #475 from Dokploy/canary
v0.9.0
2024-09-22 19:38:12 -06:00
Mauricio Siu
b04c1206e4 refactor(multi-server): update logs 2024-09-22 19:28:19 -06:00
Mauricio Siu
639bc0e8db chore(version): bump version 2024-09-22 19:17:39 -06:00
Mauricio Siu
9a850d388d Merge pull request #453 from mpcref/patch-1
Generate valid appName on changing name
2024-09-22 19:07:09 -06:00
Mauricio Siu
6c5c374139 feat(docs): add multi server docs and troubleshooting 2024-09-22 18:54:32 -06:00
Mauricio Siu
0b05f8b83c Merge pull request #469 from Dokploy/139-multi-server-feature
139 multi server feature
2024-09-22 17:20:26 -06:00
Mauricio Siu
63d5b775e6 refactor(multi-server): add config 2024-09-22 17:18:07 -06:00
Mauricio Siu
cb16de63df refactor(multi-server): copy the right value 2024-09-22 17:12:28 -06:00
Mauricio Siu
31a4a0814e refactor: remove logs 2024-09-22 16:51:31 -06:00
Mauricio Siu
14302ed240 refactor: remove imports 2024-09-22 16:49:07 -06:00
Mauricio Siu
31c55f772d refactor(multi-server): remove logs 2024-09-22 16:37:57 -06:00
Mauricio Siu
63e7eacae9 chore(version): bump version 2024-09-19 16:37:00 -06:00
Mauricio Siu
f4ab588516 Merge pull request #466 from Dokploy/canary
v0.8.3
2024-09-19 16:01:27 -06:00
seppulcro
5b0bf99cbf fix: update roundcube template remove bad chars 2024-09-19 14:40:20 +01:00
seppulcro
8e227a3286 fix: update roundcube template to match spec 2024-09-19 08:37:37 +01:00
seppulcro
869e58739f feat: add roundcube template 2024-09-17 19:57:54 +01:00
Mauricio Siu
4d8a0ba58f Merge pull request #457 from Dokploy/canary
v0.8.2
2024-09-16 15:57:20 -06:00
Michiel Crefcoeur
847d6ecab1 add trailing comma's 2024-09-16 22:21:12 +02:00
Michiel Crefcoeur
8f83ecb9ef formatting 2024-09-16 22:20:02 +02:00
Michiel Crefcoeur
2f9448dde9 corrections 2024-09-16 22:13:30 +02:00
Michiel Crefcoeur
e1ec0aee69 replaceAll 2024-09-16 10:50:46 -07:00
Michiel Crefcoeur
7f378b12ae and for database 2024-09-16 07:37:23 -07:00
Michiel Crefcoeur
fac984d299 same thing for compose 2024-09-16 07:35:59 -07:00
Michiel Crefcoeur
4f3eb7b362 Generate valid appName on changing name
The same can probably also be done at other places. Should probably be solved in a generic way.
2024-09-16 07:25:16 -07:00
Mauricio Siu
e88cd11041 Merge pull request #427 from Dokploy/canary
v0.8.1
2024-09-07 13:25:36 -06:00
Mauricio Siu
5f174a883b Merge pull request #424 from Dokploy/canary
v0.8.0
2024-09-07 00:55:08 -06:00
Ben Bristow
f8721d3e04 fix: remove 'networks' section 2024-09-02 21:20:27 +01:00
Ben Bristow
a6c7c3b031 fix: directus healthchecks (fix race condition starting up), volumes for uploads & extensions, add secret/db password generation, bump version to 11.0.2 2024-09-02 21:11:03 +01:00
Mauricio Siu
536a6ba2ff Merge pull request #397 from Dokploy/canary
v0.7.3
2024-08-30 00:27:59 -06:00
Mauricio Siu
213fa08210 Merge pull request #382 from Dokploy/canary
v0.7.2
2024-08-26 15:52:49 -06:00
Mauricio Siu
d5c6a601d8 Merge pull request #367 from Dokploy/canary
v0.7.1
2024-08-19 16:03:39 -06:00
Mauricio Siu
452793c8e5 Merge pull request #359 from Dokploy/canary
v0.7.0
2024-08-18 10:26:52 -06:00
Mauricio Siu
385fbf4af5 Merge pull request #355 from Dokploy/canary
v0.6.3
2024-08-16 22:26:35 -06:00
Mauricio Siu
3590f3bed2 Merge pull request #332 from Dokploy/canary
v0.6.2
2024-08-07 21:48:49 -06:00
Mauricio Siu
9b2fcaea31 Merge pull request #317 from Dokploy/canary
v0.6.1
2024-08-03 15:46:05 -06:00
Mauricio Siu
5abcc82215 Merge pull request #312 from Dokploy/canary
v0.6.0
2024-08-02 10:47:43 -06:00
Mauricio Siu
ee855452e3 Merge pull request #303 from Dokploy/canary
chore: add slash to version
2024-08-01 02:06:43 -06:00
Mauricio Siu
d000b526d3 Merge pull request #302 from Dokploy/canary
v0.5.1
2024-08-01 01:58:15 -06:00
Mauricio Siu
9bf88b90c3 Merge pull request #280 from Dokploy/canary
v0.5.0
2024-07-27 15:20:43 -06:00
Mauricio Siu
b1a48d4636 refactor: update job 2024-07-22 03:51:07 -06:00
Mauricio Siu
c34c4b244e Merge pull request #251 from Dokploy/canary
v0.4.0
2024-07-22 03:38:47 -06:00
Mauricio Siu
bb59a0cd3f Merge pull request #230 from Dokploy/canary
v0.3.3
2024-07-18 00:11:10 -06:00
Mauricio Siu
44e6a117dd Merge pull request #208 from Dokploy/canary
v0.3.2
2024-07-11 23:21:32 -06:00
Mauricio Siu
bfdc73f8d1 Merge pull request #197 from Dokploy/canary
v0.3.1
2024-07-06 12:01:07 -06:00
Mauricio Siu
64ada7020a Merge pull request #185 from Dokploy/canary
v0.3.0
2024-07-01 00:01:16 -06:00
Mauricio Siu
4706adc0c0 Merge pull request #174 from Dokploy/canary
v0.2.5
2024-06-29 13:29:39 -06:00
Mauricio Siu
e01d92d1d9 Merge pull request #161 from Dokploy/canary
v0.2.4
2024-06-23 19:40:45 -06:00
Mauricio Siu
fe22890311 Merge pull request #156 from Dokploy/canary
v0.2.3
2024-06-21 11:50:40 -06:00
Mauricio Siu
2b7c7632f4 Merge pull request #136 from Dokploy/canary
v0.2.2
2024-06-08 22:06:39 -06:00
Mauricio Siu
1b7244e841 Merge pull request #127 from Dokploy/canary
v0.2.1
2024-06-07 02:52:03 -06:00
432 changed files with 16641 additions and 3752 deletions

View File

@@ -11,13 +11,12 @@ jobs:
command: |
cp apps/dokploy/.env.production.example .env.production
cp apps/dokploy/.env.production.example apps/dokploy/.env.production
- run:
name: Build and push AMD64 image
command: |
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_TOKEN
if [ "${CIRCLE_BRANCH}" == "139-multi-server-feature" ]; then
TAG="feature"
elif [ "${CIRCLE_BRANCH}" == "main" ]; then
if [ "${CIRCLE_BRANCH}" == "main" ]; then
TAG="latest"
else
TAG="canary"
@@ -40,9 +39,7 @@ jobs:
name: Build and push ARM64 image
command: |
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_TOKEN
if [ "${CIRCLE_BRANCH}" == "139-multi-server-feature" ]; then
TAG="feature"
elif [ "${CIRCLE_BRANCH}" == "main" ]; then
if [ "${CIRCLE_BRANCH}" == "main" ]; then
TAG="latest"
else
TAG="canary"
@@ -65,7 +62,7 @@ jobs:
VERSION=$(node -p "require('./apps/dokploy/package.json').version")
echo $VERSION
TAG="latest"
docker manifest create dokploy/dokploy:${TAG} \
dokploy/dokploy:${TAG}-amd64 \
dokploy/dokploy:${TAG}-arm64
@@ -75,12 +72,6 @@ jobs:
dokploy/dokploy:${TAG}-amd64 \
dokploy/dokploy:${TAG}-arm64
docker manifest push dokploy/dokploy:${VERSION}
elif [ "${CIRCLE_BRANCH}" == "139-multi-server-feature" ]; then
TAG="feature"
docker manifest create dokploy/dokploy:${TAG} \
dokploy/dokploy:${TAG}-amd64 \
dokploy/dokploy:${TAG}-arm64
docker manifest push dokploy/dokploy:${TAG}
else
TAG="canary"
docker manifest create dokploy/dokploy:${TAG} \
@@ -98,14 +89,12 @@ workflows:
only:
- main
- canary
- 139-multi-server-feature
- build-arm64:
filters:
branches:
only:
- main
- canary
- 139-multi-server-feature
- combine-manifests:
requires:
- build-amd64
@@ -115,4 +104,3 @@ workflows:
only:
- main
- canary
- 139-multi-server-feature

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

After

Width:  |  Height:  |  Size: 248 KiB

View File

@@ -1,4 +1,4 @@
name: Build Docs & Website Docker images
name: Build Docker images
on:
push:
@@ -48,3 +48,74 @@ jobs:
push: true
tags: dokploy/website:latest
platforms: linux/amd64
build-and-push-cloud-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.cloud
push: true
tags: |
siumauricio/cloud:${{ github.ref_name == 'main' && 'main' || 'canary' }}
platforms: linux/amd64
build-and-push-schedule-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.schedule
push: true
tags: |
siumauricio/schedule:${{ github.ref_name == 'main' && 'main' || 'canary' }}
platforms: linux/amd64
build-and-push-server-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.server
push: true
tags: |
siumauricio/server:${{ github.ref_name == 'main' && 'main' || 'canary' }}
platforms: linux/amd64

View File

@@ -18,6 +18,7 @@ jobs:
node-version: 18.18.0
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm run server:build
- run: pnpm biome ci
- run: pnpm typecheck
@@ -32,6 +33,7 @@ jobs:
node-version: 18.18.0
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm run server:build
- run: pnpm build
parallel-tests:
@@ -44,4 +46,5 @@ jobs:
node-version: 18.18.0
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm run server:build
- run: pnpm test

1
.husky/commit-msg Normal file
View File

@@ -0,0 +1 @@
npx commitlint --edit "$1"

6
.husky/install.mjs Normal file
View File

@@ -0,0 +1,6 @@
// Skip Husky install in production and CI
if (process.env.NODE_ENV === "production" || process.env.CI === "true") {
process.exit(0);
}
const husky = (await import("husky")).default;
console.log(husky());

2
.husky/pre-commit Normal file
View File

@@ -0,0 +1,2 @@
pnpm run check
git add .

View File

@@ -71,6 +71,12 @@ Run the command that will spin up all the required services and files.
pnpm run dokploy:setup
```
Build the server package (If you make any changes after in the packages/server folder, you need to rebuild and run this command)
```bash
pnpm run server:build
```
Now run the development server.
```bash

View File

@@ -15,7 +15,9 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
# Deploy only the dokploy app
ENV NODE_ENV=production
RUN pnpm --filter=@dokploy/server build
RUN pnpm --filter=./apps/dokploy run build
RUN pnpm --filter=./apps/dokploy --prod deploy /prod/dokploy
RUN cp -R /usr/src/app/apps/dokploy/.next /prod/dokploy/.next

52
Dockerfile.cloud Normal file
View File

@@ -0,0 +1,52 @@
FROM node:18-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
FROM base AS build
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y python3 make g++ git && rm -rf /var/lib/apt/lists/*
# Install dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm --filter=@dokploy/server --filter=./apps/dokploy install --frozen-lockfile
# Deploy only the dokploy app
ENV NODE_ENV=production
RUN pnpm --filter=@dokploy/server build
RUN pnpm --filter=./apps/dokploy run build
RUN pnpm --filter=./apps/dokploy --prod deploy /prod/dokploy
RUN cp -R /usr/src/app/apps/dokploy/.next /prod/dokploy/.next
RUN cp -R /usr/src/app/apps/dokploy/dist /prod/dokploy/dist
FROM base AS dokploy
WORKDIR /app
# Set production
ENV NODE_ENV=production
RUN apt-get update && apt-get install -y curl unzip apache2-utils && rm -rf /var/lib/apt/lists/*
# Copy only the necessary files
COPY --from=build /prod/dokploy/.next ./.next
COPY --from=build /prod/dokploy/dist ./dist
COPY --from=build /prod/dokploy/next.config.mjs ./next.config.mjs
COPY --from=build /prod/dokploy/public ./public
COPY --from=build /prod/dokploy/package.json ./package.json
COPY --from=build /prod/dokploy/drizzle ./drizzle
COPY --from=build /prod/dokploy/components.json ./components.json
COPY --from=build /prod/dokploy/node_modules ./node_modules
# Install RCLONE
RUN curl https://rclone.org/install.sh | bash
# tsx
RUN pnpm install -g tsx
EXPOSE 3000
CMD [ "pnpm", "start" ]

36
Dockerfile.schedule Normal file
View File

@@ -0,0 +1,36 @@
FROM node:18-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
FROM base AS build
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y python3 make g++ git && rm -rf /var/lib/apt/lists/*
# Install dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm --filter=@dokploy/server --filter=./apps/schedules install --frozen-lockfile
# Deploy only the dokploy app
ENV NODE_ENV=production
RUN pnpm --filter=@dokploy/server build
RUN pnpm --filter=./apps/schedules run build
RUN pnpm --filter=./apps/schedules --prod deploy /prod/schedules
RUN cp -R /usr/src/app/apps/schedules/dist /prod/schedules/dist
FROM base AS dokploy
WORKDIR /app
# Set production
ENV NODE_ENV=production
# Copy only the necessary files
COPY --from=build /prod/schedules/dist ./dist
COPY --from=build /prod/schedules/package.json ./package.json
COPY --from=build /prod/schedules/node_modules ./node_modules
CMD HOSTNAME=0.0.0.0 && pnpm start

36
Dockerfile.server Normal file
View File

@@ -0,0 +1,36 @@
FROM node:18-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
FROM base AS build
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y python3 make g++ git && rm -rf /var/lib/apt/lists/*
# Install dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm --filter=@dokploy/server --filter=./apps/api install --frozen-lockfile
# Deploy only the dokploy app
ENV NODE_ENV=production
RUN pnpm --filter=@dokploy/server build
RUN pnpm --filter=./apps/api run build
RUN pnpm --filter=./apps/api --prod deploy /prod/api
RUN cp -R /usr/src/app/apps/api/dist /prod/api/dist
FROM base AS dokploy
WORKDIR /app
# Set production
ENV NODE_ENV=production
# Copy only the necessary files
COPY --from=build /prod/api/dist ./dist
COPY --from=build /prod/api/package.json ./package.json
COPY --from=build /prod/api/node_modules ./node_modules
CMD HOSTNAME=0.0.0.0 && pnpm start

View File

@@ -17,10 +17,10 @@ See the License for the specific language governing permissions and limitations
## Additional Terms for Specific Features
The following additional terms apply to the multi-node support and Docker Compose file support features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License:
The following additional terms apply to the multi-node support, Docker Compose file and Multi Server features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License:
- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support and Docker Compose file support, will always be free to use in the self-hosted version.
- **Restriction on Resale**: The multi-node support and Docker Compose file support features cannot be sold or offered as a service by any party other than the copyright holder without prior written consent.
- **Modification Distribution**: Any modifications to the multi-node support and Docker Compose file support features must be distributed freely and cannot be sold or offered as a service.
- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support, Docker Compose file support and Multi Server, will always be free to use in the self-hosted version.
- **Restriction on Resale**: The multi-node support, Docker Compose file support and Multi Server features cannot be sold or offered as a service by any party other than the copyright holder without prior written consent.
- **Modification Distribution**: Any modifications to the multi-node support, Docker Compose file support and Multi Server features must be distributed freely and cannot be sold or offered as a service.
For further inquiries or permissions, please contact us directly.

View File

@@ -32,6 +32,7 @@ Dokploy include multiples features to make your life easier.
- **Docker Management**: Easily deploy and manage Docker containers.
- **CLI/API**: Manage your applications and databases using the command line or trought the API.
- **Notifications**: Get notified when your deployments are successful or failed (Slack, Discord, Telegram, Email, etc.)
- **Multi Server**: Deploy and manager your applications remotely to external servers.
- **Self-Hosted**: Self-host Dokploy on your VPS.
## 🚀 Getting Started
@@ -58,7 +59,14 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
### Hero Sponsors 🎖
<a href="https://www.hostinger.com/vps-hosting?ref=dokploy" target="_blank" ><img src=".github/sponsors/hostinger.jpg" alt="Hostinger" width="200"/></a>
<div style="display: flex; align-items: center; gap: 20px;">
<a href="https://www.hostinger.com/vps-hosting?ref=dokploy" target="_blank" style="display: inline-block;">
<img src=".github/sponsors/hostinger.jpg" alt="Hostinger" height="50"/>
</a>
<a href="https://www.lxaer.com/?ref=dokploy" target="_blank" style="display: inline-block;">
<img src=".github/sponsors/lxaer.png" alt="LX Aer" height="50"/>
</a>
</div>
### Premium Supporters 🥇
@@ -81,6 +89,7 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
<div style="display: flex; gap: 30px; flex-wrap: wrap;">
<a href="https://steamsets.com/?ref=dokploy"><img src="https://avatars.githubusercontent.com/u/111978405?s=200&v=4" width="60px" alt="Steamsets.com"/></a>
<a href="https://rivo.gg/?ref=dokploy"><img src="https://avatars.githubusercontent.com/u/126797452?s=200&v=4" width="60px" alt="Rivo.gg"/></a>
</div>
#### Organizations:

View File

@@ -1,15 +1,33 @@
{
"name": "my-app",
"name": "@dokploy/api",
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts"
"dev": "PORT=4000 tsx watch src/index.ts",
"build": "tsc --project tsconfig.json",
"start": "node --experimental-specifier-resolution=node dist/index.js",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"pino": "9.4.0",
"pino-pretty": "11.2.2",
"@hono/zod-validator": "0.3.0",
"zod": "^3.23.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"@dokploy/server": "workspace:*",
"@hono/node-server": "^1.12.1",
"hono": "^4.5.8",
"dotenv": "^16.3.1"
"dotenv": "^16.3.1",
"redis": "4.7.0",
"@nerimity/mimiqueue": "1.2.3"
},
"devDependencies": {
"typescript": "^5.4.2",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@types/node": "^20.11.17",
"tsx": "^4.7.1"
}
},
"packageManager": "pnpm@9.5.0"
}

View File

@@ -1,66 +1,61 @@
import { serve } from "@hono/node-server";
import { config } from "dotenv";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { validateLemonSqueezyLicense } from "./utils";
config();
import "dotenv/config";
import { zValidator } from "@hono/zod-validator";
import { Queue } from "@nerimity/mimiqueue";
import { createClient } from "redis";
import { logger } from "./logger";
import { type DeployJob, deployJobSchema } from "./schema";
import { deploy } from "./utils";
const app = new Hono();
app.use(
"/*",
cors({
origin: ["http://localhost:3000", "http://localhost:3001"], // Ajusta esto a los orígenes de tu aplicación Next.js
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowHeaders: ["Content-Type", "Authorization"],
exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
maxAge: 600,
credentials: true,
}),
);
export const LEMON_SQUEEZY_API_KEY = process.env.LEMON_SQUEEZY_API_KEY;
export const LEMON_SQUEEZY_STORE_ID = process.env.LEMON_SQUEEZY_STORE_ID;
app.get("/v1/health", (c) => {
return c.text("Hello Hono!");
const redisClient = createClient({
url: process.env.REDIS_URL,
});
app.post("/v1/validate-license", async (c) => {
const { licenseKey } = await c.req.json();
app.use(async (c, next) => {
if (c.req.path === "/health") {
return next();
}
const authHeader = c.req.header("X-API-Key");
if (!licenseKey) {
return c.json({ error: "License key is required" }, 400);
if (process.env.API_KEY !== authHeader) {
return c.json({ message: "Invalid API Key" }, 403);
}
try {
const licenseValidation = await validateLemonSqueezyLicense(licenseKey);
if (licenseValidation.valid) {
return c.json({
valid: true,
message: "License is valid",
metadata: licenseValidation.meta,
});
}
return c.json(
{
valid: false,
message: licenseValidation.error || "Invalid license",
},
400,
);
} catch (error) {
console.error("Error during license validation:", error);
return c.json({ error: "Internal server error" }, 500);
}
return next();
});
const port = 4000;
console.log(`Server is running on port ${port}`);
serve({
fetch: app.fetch,
port,
app.post("/deploy", zValidator("json", deployJobSchema), (c) => {
const data = c.req.valid("json");
const res = queue.add(data, { groupName: data.serverId });
return c.json(
{
message: "Deployment Added",
},
200,
);
});
app.get("/health", async (c) => {
return c.json({ status: "ok" });
});
const queue = new Queue({
name: "deployments",
process: async (job: DeployJob) => {
logger.info("Deploying job", job);
return await deploy(job);
},
redisClient,
});
(async () => {
await redisClient.connect();
await redisClient.flushAll();
logger.info("Redis Cleaned");
})();
const port = Number.parseInt(process.env.PORT || "3000");
logger.info("Starting Deployments Server ✅", port);
serve({ fetch: app.fetch, port });

10
apps/api/src/logger.ts Normal file
View File

@@ -0,0 +1,10 @@
import pino from "pino";
export const logger = pino({
transport: {
target: "pino-pretty",
options: {
colorize: true,
},
},
});

24
apps/api/src/schema.ts Normal file
View File

@@ -0,0 +1,24 @@
import { z } from "zod";
export const deployJobSchema = z.discriminatedUnion("applicationType", [
z.object({
applicationId: z.string(),
titleLog: z.string(),
descriptionLog: z.string(),
server: z.boolean().optional(),
type: z.enum(["deploy", "redeploy"]),
applicationType: z.literal("application"),
serverId: z.string(),
}),
z.object({
composeId: z.string(),
titleLog: z.string(),
descriptionLog: z.string(),
server: z.boolean().optional(),
type: z.enum(["deploy", "redeploy"]),
applicationType: z.literal("compose"),
serverId: z.string(),
}),
]);
export type DeployJob = z.infer<typeof deployJobSchema>;

View File

@@ -1,28 +1,96 @@
import { LEMON_SQUEEZY_API_KEY, LEMON_SQUEEZY_STORE_ID } from ".";
import {
deployApplication,
deployCompose,
deployRemoteApplication,
deployRemoteCompose,
rebuildApplication,
rebuildCompose,
rebuildRemoteApplication,
rebuildRemoteCompose,
updateApplicationStatus,
updateCompose,
} from "@dokploy/server";
import type { DeployJob } from "./schema";
import type { LemonSqueezyLicenseResponse } from "./types";
export const validateLemonSqueezyLicense = async (
licenseKey: string,
): Promise<LemonSqueezyLicenseResponse> => {
try {
const response = await fetch(
"https://api.lemonsqueezy.com/v1/licenses/validate",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": LEMON_SQUEEZY_API_KEY as string,
},
body: JSON.stringify({
license_key: licenseKey,
store_id: LEMON_SQUEEZY_STORE_ID as string,
}),
},
);
// const LEMON_SQUEEZY_API_KEY = process.env.LEMON_SQUEEZY_API_KEY;
// const LEMON_SQUEEZY_STORE_ID = process.env.LEMON_SQUEEZY_STORE_ID;
// export const validateLemonSqueezyLicense = async (
// licenseKey: string,
// ): Promise<LemonSqueezyLicenseResponse> => {
// try {
// const response = await fetch(
// "https://api.lemonsqueezy.com/v1/licenses/validate",
// {
// method: "POST",
// headers: {
// "Content-Type": "application/json",
// "x-api-key": LEMON_SQUEEZY_API_KEY as string,
// },
// body: JSON.stringify({
// license_key: licenseKey,
// store_id: LEMON_SQUEEZY_STORE_ID as string,
// }),
// },
// );
return response.json();
// return response.json();
// } catch (error) {
// console.error("Error validating license:", error);
// return { valid: false, error: "Error validating license" };
// }
// };
export const deploy = async (job: DeployJob) => {
try {
if (job.applicationType === "application") {
await updateApplicationStatus(job.applicationId, "running");
if (job.server) {
if (job.type === "redeploy") {
await rebuildRemoteApplication({
applicationId: job.applicationId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
} else if (job.type === "deploy") {
await deployRemoteApplication({
applicationId: job.applicationId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
}
}
} else if (job.applicationType === "compose") {
await updateCompose(job.composeId, {
composeStatus: "running",
});
if (job.server) {
if (job.type === "redeploy") {
await rebuildRemoteCompose({
composeId: job.composeId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
} else if (job.type === "deploy") {
await deployRemoteCompose({
composeId: job.composeId,
titleLog: job.titleLog,
descriptionLog: job.descriptionLog,
});
}
}
}
} catch (error) {
console.error("Error validating license:", error);
return { valid: false, error: "Error validating license" };
console.log(error);
if (job.applicationType === "application") {
await updateApplicationStatus(job.applicationId, "error");
} else if (job.applicationType === "compose") {
await updateCompose(job.composeId, {
composeStatus: "error",
});
}
}
return true;
};

View File

@@ -2,11 +2,12 @@
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"moduleResolution": "Node",
"strict": true,
"skipLibCheck": true,
"types": ["node"],
"outDir": "dist",
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
},
"exclude": ["node_modules", "dist"]
}

View File

@@ -74,7 +74,7 @@ export function generateMetadata({
},
twitter: {
card: "summary_large_image",
creator: "@siumauricio",
creator: "@getdokploy",
title: page.data.title,
description: page.data.description,
images: [

View File

@@ -15,6 +15,8 @@ Configure the source of your code, the way your application is built, and also m
If you need to assign environment variables to your application, you can do so here.
In case you need to use a multiline variable, you can wrap it in double quotes just like this `'"here_is_my_private_key"'`.
## Monitoring
Four graphs will be displayed for the use of memory, CPU, disk, and network. Note that the information is only updated if you are viewing the current page, otherwise it will not be updated.

View File

@@ -26,6 +26,8 @@ Actions like deploying, updating, and deleting your database, and stopping it.
If you need to assign environment variables to your application, you can do so here.
In case you need to use a multiline variable, you can wrap it in double quotes just like this `'"here_is_my_private_key"'`.
## Monitoring
Four graphs will be displayed for the use of memory, CPU, disk, and network. Note that the information is only updated if you are viewing the current page, otherwise it will not be updated.

View File

@@ -1,22 +1,23 @@
---
title: 'Comparison'
description: 'A comparison of Dokploy, CapRover, Dokku, and Coolify'
title: "Comparison"
description: "A comparison of Dokploy, CapRover, Dokku, and Coolify"
---
Comparison of the following deployment tools:
| Feature | Dokploy | CapRover | Dokku | Coolify |
|-----------------------------------|---------------------------------------|--------------------------------------|--------------------------------------|--------------------------------------|
| **User Interface** | ✅ | ✅ | ❌ | ✅ |
| **Docker compose support** | | ❌ | ❌ | ✅ |
| **API/CLI** | ✅ | ✅ | ✅ | ✅ |
| **Multi node support** | ✅ | ✅ | ❌ | ✅ |
| **Traefik Integration** | ✅ | ✅ | Available via Plugins | ✅ |
| **User Permission Management** | ✅ | ❌ | ❌ | ✅ |
| **Advanced User Permission Management** | ✅ | ❌ | ❌ | ❌ |
| **Terminal Access Built In** | ✅ | ❌ | ❌ | ✅ |
| **Database Support** | ✅ | ✅ | ❌ | ✅ |
| **Monitoring** | ✅ | ✅ | ❌ | ❌ |
| **Backups** | ✅ | Available via Plugins | Available via Plugins | ✅ |
| **Open Source** | ✅ | ✅ | ✅ | ✅ |
| **Cloud/Paid Version** | ❌ | ✅ | ❌ | ✅ |
| Feature | Dokploy | CapRover | Dokku | Coolify |
| --------------------------------------- | ------- | --------------------- | --------------------- | ------- |
| **User Interface** | ✅ | ✅ | ❌ | ✅ |
| **Docker compose support** || ❌ | ❌ | ✅ |
| **API/CLI** | ✅ | ✅ | ✅ | ✅ |
| **Multi node support** | ✅ | ✅ | ❌ | ✅ |
| **Traefik Integration** | ✅ | ✅ | Available via Plugins | ✅ |
| **User Permission Management** | ✅ | ❌ | ❌ | ✅ |
| **Advanced User Permission Management** | ✅ | ❌ | ❌ | ❌ |
| **Terminal Access Built In** | ✅ | ❌ | ❌ | ✅ |
| **Database Support** | ✅ | ✅ | ❌ | ✅ |
| **Monitoring** | ✅ | ✅ | ❌ | ❌ |
| **Backups** | ✅ | Available via Plugins | Available via Plugins | ✅ |
| **Open Source** | ✅ | ✅ | ✅ | ✅ |
| **Multi Server Support** | ✅ | ❌ | ❌ | ✅ |
| **Cloud/Paid Version** | ❌ | ✅ | ✅ | ✅ |

View File

@@ -64,6 +64,9 @@
"docker/overview",
"---Monitoring---",
"monitoring/overview",
"---Multi Server---",
"multi-server/overview",
"multi-server/example",
"---Cluster---",
"cluster/overview",
"---Deployments---",

View File

@@ -0,0 +1,117 @@
---
title: Example
description: "Example to setup a remote server and deploy application in a VPS."
---
import { Callout } from "fumadocs-ui/components/callout";
Multi server allows you to deploy your apps remotely to different servers without needing to build and run them where the Dokploy UI is installed.
## Requirements
1. To install Dokploy UI, follow the [installation guide](en/docs/core/get-started/installation).
2. Create an SSH key by going to `/dashboard/settings/ssh-keys` and add a new key. Be sure to copy the public key.
<ImageZoom
src="/assets/ssh-keys.png"
alt="Architecture Diagram"
width={1000}
height={600}
className="rounded-lg"
/>
3. Decide which remote server to deploy your apps on. We recommend these reliable providers:
- [Hostinger](https://www.hostinger.com/vps-hosting?ref=dokploy) Get 20% off with this [referral link](https://www.hostinger.com/vps-hosting?REFERRALCODE=1SIUMAURICI97).
- [DigitalOcean](https://www.digitalocean.com/pricing/droplets#basic-droplets) Get $200 credits for free with this [referral link](https://m.do.co/c/db24efd43f35).
- [Hetzner](https://www.hetzner.com/cloud/) Get €20 credits with this [referral link](https://hetzner.cloud/?ref=vou4fhxJ1W2D).
- [Linode](https://www.linode.com/es/pricing/#compute-shared).
- [Vultr](https://www.vultr.com/pricing/#cloud-compute).
- [Scaleway](https://www.scaleway.com/en/pricing/?tags=baremetal,available).
- [Google Cloud](https://cloud.google.com/).
- [AWS](https://aws.amazon.com/ec2/pricing/).
4. When creating the server, it should ask for SSH keys. Ideally, use your computer's public key and the key you generated in the previous step. Here's how to add the public key in Hostinger:
<ImageZoom
src="/assets/hostinger-add-sshkey.png"
alt="Adding SSH key"
width={1000}
height={600}
className="rounded-lg"
/>
<Callout>The steps are similar across other providers.</Callout>
5. Copy the servers IP address and ensure you know the username (often `root`). Fill in all fields and click `Create`.
<ImageZoom
src="/assets/multi-server-add-server.png"
alt="Add server"
width={1000}
height={600}
className="rounded-lg"
/>
6. To test connectivity, open the server dropdown and click `Enter Terminal`. If everything is correct, you should be able to interact with the remote server.
7. Click `Setup Server` to proceed. There are two tabs: SSH Keys and Deployments. This guide explains the easy way, but you can follow the manual process via the Dokploy UI if you prefer.
<ImageZoom
src="/assets/multi-server-setup-2.png"
alt="Setup process"
width={1000}
height={600}
className="rounded-lg"
/>
8. Click `Deployments`, then `Setup Server`. If everything is correct, you should see output similar to this:
<ImageZoom
src="/assets/multi-server-setup-3.png"
alt="Server setup output"
width={1000}
height={600}
className="rounded-lg"
/>
<Callout>
You only need to run this setup once. If Dokploy updates later, check the
release notes to see if rerunning this command is required.
</Callout>
9. You're ready to deploy your apps! Let's test it out:
<ImageZoom
src="/assets/multi-server-add-app.png"
alt="Add app"
width={1000}
height={600}
className="rounded-lg"
/>
10. To check which server an app belongs to, youll see the server name at the top. If no server is selected, it defaults to `Dokploy Server`. Click `Deploy` to start building your app on the remote server. You can check the `Logs` tab to see the build process. For this example, well use a test repo:
Repo: `https://github.com/Dokploy/examples.git`
Branch: `main`
Build Path: `/astro`
<ImageZoom
src="/assets/multi-server-setup-app.png"
alt="App setup"
width={1000}
height={600}
className="rounded-lg"
/>
11. Once the build is done, go to `Domains` and create a free domain. Just click `Create` and youre good to go! 🎊
{" "}
<ImageZoom
src="/assets/multi-server-finish.png"
alt="Finished setup"
width={1000}
height={600}
className="rounded-lg"
/>

View File

@@ -0,0 +1,29 @@
---
title: Overview
description: "Deploy your apps to multiple servers remotely."
---
import { Callout } from "fumadocs-ui/components/callout";
Multi server allows you to deploy your apps remotely to different servers without needing to build and run them where the Dokploy UI is installed.
To use the multi-server feature, you need to have Dokploy UI installed either locally or on a remote server. We recommend using a remote server for better connectivity, security, and isolation, for remote instances we install only a traefik instance.
If you plan to only deploy apps to remote servers and use Dokploy UI for managing deployments, Dokploy will use around 250 MB of RAM and minimal CPU, so a low-resource server should be sufficient.
All the features we have documented previously are supported by Dokploy Multi Server. The only feature not supported is remote server monitoring, due to performance reasons. However, all functionalities should work the same as when deploying on the same server where Dokploy UI is installed.
## Features
1. **Enter the terminal**: Allows you to access the terminal of the remote server.
2. **Setup Server**: Allows you to configure the remote server.
- **SSH Keys**: Steps to add SSH keys to the remote server.
- **Deployments**: Steps to configure the remote server for deploying applications.
3. **Edit Server**: Allows you to modify the remote server's details, such as SSH key, name, description, IP, etc.
4. **View Actions**: Lets you perform actions like managing the Traefik instance, storage, and activating Docker cleanup.
5. **Show Traefik File System**: Displays the contents of the remote server's directory.
6. **Show Docker Containers**: Shows the Docker containers running on the remote server.
<Callout>
Remote server monitoring is not supported due to performance reasons.
</Callout>

View File

@@ -31,8 +31,7 @@ The following templates are available:
- **Wordpress**: Open Source Content Management System
- **Open WebUI**: Free and Open Source ChatGPT Alternative
- **Teable**: Open Source Airtable Alternative, Developer Friendly, No-code Database Built on Postgres
- **Roundcube**: Free and open source webmail software for the masses, written in PHP, uses SMTP[^1].
## Create your own template
@@ -41,3 +40,5 @@ We accept contributions to upload new templates to the dokploy repository.
Make sure to follow the guidelines for creating a template:
[Steps to create your own template](https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#templates)
[^1]: Please note that if you're self-hosting a mail server you need port 25 to be open for SMTP (Mail Transmission Protocol that allows you to send and receive) to work properly. Some VPS providers like [Hetzner](https://docs.hetzner.com/cloud/servers/faq/#why-can-i-not-send-any-mails-from-my-server) block this port by default for new clients.

View File

@@ -3,4 +3,90 @@ title: Overview
description: Solve the most common problems that occur when using Dokploy.
---
WIP
## Applications Domain Not Working?
You see the deployment succeeded, and logs are running, but the domain isn't working? Here's what to check:
1. **Correct Port Mapping**: Ensure the domain is using the correct port for your application. For example, if you're using Next.js, the port should be `3000`, or for Laravel, it should be `8000`. If you change the app port, update the domain to reflect that.
2. **Avoid Using `Ports` in Advanced Settings**: Generally, there's no need to use the `Ports` feature unless you want to access your app via `IP:port`. Leaving this feature enabled may interfere with your domain.
3. **Let's Encrypt Certificates**: It's crucial to point the domain to your servers IP **before** adding it in Dokploy. If the domain is added first, the certificate wont be generated, and you may need to recreate the domain or restart Traefik.
4. **Listen on 0.0.0.0, Not 127.0.0.1**: If your app is bound to `127.0.0.1` (which is common in Vite apps), switch it to `0.0.0.0` to allow external access.
## Logs and Monitoring Not Working After Changing Application Placement?
This is expected behavior. If the application is running on a different node (worker), the UI wont have access to logs or monitoring, as they're not on the same node.
## Mounts Are Causing My Application Not to Run?
Docker Swarm won't run your application if there are invalid mounts, even if the deployment shows as successful. Double-check your mounts to ensure they are valid.
## Volumes in Docker Compose Not Working?
For Docker Compose, all file mounts defined in the `volumes` section will be stored in the `files` folder. This is the default directory structure:
## I added a volume to my docker compose, but is not finding the volume?
For docker compose all the file mounts you've created in the volumes section will be stored to files folder, this is the default structure of the docker compose.
```
/application-name
/code
/files
```
So instead of using this invalid way to mount a volume:
```yaml
volumes:
- "/folder:/path/in/container" ❌
```
You should use this format:
```yaml
volumes:
- "../files/my-database:/var/lib/mysql" ✅
- "../files/my-configs:/etc/my-app/config" ✅
```
## Logs Not Loading When Deploying to a Remote Server?
There are a few potential reasons for this:
1. **Slow Server:**: If the server is too slow, it may struggle to handle concurrent requests, leading to SSL handshake errors.
2. **Insufficient Disk Space:** If the server doesn't have enough disk space, the logs may not load.
## Docker Compose Domain Not Working?
When adding a domain in your Docker Compose file, its not necessary to expose the ports directly. Simply specify the port where your app is running. Exposing the ports can lead to conflicts with other applications or ports.
Example of what not to do:
```yaml
services:
app:
image: dokploy/dokploy:latest
ports:
- 3000:3000
```
Recommended approach:
```yaml
services:
app:
image: dokploy/dokploy:latest
ports:
- 3000
- 80
```
Then, when creating the domain in Dokploy, specify the service name and port, like this:
```yaml
domain: my-app.com
serviceName: app
port: 3000
```

View File

@@ -10,8 +10,10 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
p: ({ children }) => (
<p className="text-[#3E4342] dark:text-muted-foreground">{children}</p>
),
li: ({ children }) => (
<li className="text-[#3E4342] dark:text-muted-foreground">{children}</li>
li: ({ children, id }) => (
<li {...{ id }} className="text-[#3E4342] dark:text-muted-foreground">
{children}
</li>
),
};
}

View File

@@ -21,15 +21,11 @@
"react-ga4": "^2.1.0"
},
"devDependencies": {
"tsx": "4.15.7",
"@biomejs/biome": "1.8.1",
"autoprefixer": "10.4.12",
"@types/mdx": "^2.0.13",
"@types/node": "^20.14.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.4",
"tailwindcss": "^3.4.1",
"typescript": "^5.4.5"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

View File

@@ -1,5 +1,5 @@
import { addSuffixToAllProperties } from "@/server/utils/docker/compose";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { addSuffixToAllProperties } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToConfigsRoot } from "@/server/utils/docker/compose/configs";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToConfigsRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToConfigsInServices } from "@/server/utils/docker/compose/configs";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToConfigsInServices } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,9 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import {
addSuffixToAllConfigs,
addSuffixToConfigsRoot,
} from "@/server/utils/docker/compose/configs";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToAllConfigs, addSuffixToConfigsRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,5 +1,5 @@
import type { Domain } from "@/server/api/services/domain";
import { createDomainLabels } from "@/server/utils/docker/domain";
import type { Domain } from "@dokploy/server";
import { createDomainLabels } from "@dokploy/server";
import { describe, expect, it } from "vitest";
describe("createDomainLabels", () => {

View File

@@ -1,4 +1,4 @@
import { addDokployNetworkToRoot } from "@/server/utils/docker/domain";
import { addDokployNetworkToRoot } from "@dokploy/server";
import { describe, expect, it } from "vitest";
describe("addDokployNetworkToRoot", () => {

View File

@@ -1,4 +1,4 @@
import { addDokployNetworkToService } from "@/server/utils/docker/domain";
import { addDokployNetworkToService } from "@dokploy/server";
import { describe, expect, it } from "vitest";
describe("addDokployNetworkToService", () => {

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToNetworksRoot } from "@/server/utils/docker/compose/network";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToNetworksRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToServiceNetworks } from "@/server/utils/docker/compose/network";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToServiceNetworks } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,10 +1,10 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { generateRandomHash } from "@dokploy/server";
import {
addSuffixToAllNetworks,
addSuffixToServiceNetworks,
} from "@/server/utils/docker/compose/network";
import { addSuffixToNetworksRoot } from "@/server/utils/docker/compose/network";
import type { ComposeSpecification } from "@/server/utils/docker/types";
} from "@dokploy/server";
import { addSuffixToNetworksRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToSecretsRoot } from "@/server/utils/docker/compose/secrets";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToSecretsRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { dump, load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToSecretsInServices } from "@/server/utils/docker/compose/secrets";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToSecretsInServices } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,5 +1,5 @@
import { addSuffixToAllSecrets } from "@/server/utils/docker/compose/secrets";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { addSuffixToAllSecrets } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,8 +1,8 @@
import {
addSuffixToAllServiceNames,
addSuffixToServiceNames,
} from "@/server/utils/docker/compose/service";
import type { ComposeSpecification } from "@/server/utils/docker/types";
} from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,9 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import {
addSuffixToAllVolumes,
addSuffixToVolumesRoot,
} from "@/server/utils/docker/compose/volume";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToAllVolumes, addSuffixToVolumesRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToVolumesRoot } from "@/server/utils/docker/compose/volume";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToVolumesRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,6 +1,6 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { addSuffixToVolumesInServices } from "@/server/utils/docker/compose/volume";
import type { ComposeSpecification } from "@/server/utils/docker/types";
import { generateRandomHash } from "@dokploy/server";
import { addSuffixToVolumesInServices } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,9 +1,9 @@
import { generateRandomHash } from "@/server/utils/docker/compose";
import { generateRandomHash } from "@dokploy/server";
import {
addSuffixToAllVolumes,
addSuffixToVolumesInServices,
} from "@/server/utils/docker/compose/volume";
import type { ComposeSpecification } from "@/server/utils/docker/types";
} from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";

View File

@@ -1,9 +1,9 @@
import fs from "node:fs/promises";
import path from "node:path";
import { paths } from "@/server/constants";
import { paths } from "@dokploy/server/dist/constants";
const { APPLICATIONS_PATH } = paths();
import type { ApplicationNested } from "@/server/utils/builders";
import { unzipDrop } from "@/server/utils/builders/drop";
import type { ApplicationNested } from "@dokploy/server";
import { unzipDrop } from "@dokploy/server";
import AdmZip from "adm-zip";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
@@ -81,14 +81,17 @@ const baseApp: ApplicationNested = {
username: null,
dockerContextPath: null,
};
//
vi.mock("@/server/constants", () => ({
paths: () => ({
APPLICATIONS_PATH: "./__test__/drop/zips/output",
}),
// APPLICATIONS_PATH: "./__test__/drop/zips/output",
}));
vi.mock("@dokploy/server/dist/constants", async (importOriginal) => {
const actual = await importOriginal();
return {
// @ts-ignore
...actual,
paths: () => ({
APPLICATIONS_PATH: "./__test__/drop/zips/output",
}),
};
});
describe("unzipDrop using real zip files", () => {
// const { APPLICATIONS_PATH } = paths();
beforeAll(async () => {
@@ -102,15 +105,19 @@ describe("unzipDrop using real zip files", () => {
it("should correctly extract a zip with a single root folder", async () => {
baseApp.appName = "single-file";
// const appName = "single-file";
const outputPath = path.join(APPLICATIONS_PATH, baseApp.appName, "code");
const zip = new AdmZip("./__test__/drop/zips/single-file.zip");
const zipBuffer = zip.toBuffer();
const file = new File([zipBuffer], "single.zip");
await unzipDrop(file, baseApp);
const files = await fs.readdir(outputPath, { withFileTypes: true });
expect(files.some((f) => f.name === "test.txt")).toBe(true);
try {
const outputPath = path.join(APPLICATIONS_PATH, baseApp.appName, "code");
const zip = new AdmZip("./__test__/drop/zips/single-file.zip");
console.log(`Output Path: ${outputPath}`);
const zipBuffer = zip.toBuffer();
const file = new File([zipBuffer], "single.zip");
await unzipDrop(file, baseApp);
const files = await fs.readdir(outputPath, { withFileTypes: true });
expect(files.some((f) => f.name === "test.txt")).toBe(true);
} catch (err) {
console.log(err);
} finally {
}
});
it("should correctly extract a zip with a single root folder and a subfolder", async () => {

View File

@@ -1,4 +1,4 @@
import { parseRawConfig, processLogs } from "@/server/utils/access-log/utils";
import { parseRawConfig, processLogs } from "@dokploy/server";
import { describe, expect, it } from "vitest";
const sampleLogEntry = `{"ClientAddr":"172.19.0.1:56732","ClientHost":"172.19.0.1","ClientPort":"56732","ClientUsername":"-","DownstreamContentSize":0,"DownstreamStatus":304,"Duration":14729375,"OriginContentSize":0,"OriginDuration":14051833,"OriginStatus":304,"Overhead":677542,"RequestAddr":"s222-umami-c381af.traefik.me","RequestContentSize":0,"RequestCount":122,"RequestHost":"s222-umami-c381af.traefik.me","RequestMethod":"GET","RequestPath":"/dashboard?_rsc=1rugv","RequestPort":"-","RequestProtocol":"HTTP/1.1","RequestScheme":"http","RetryAttempts":0,"RouterName":"s222-umami-60e104-47-web@docker","ServiceAddr":"10.0.1.15:3000","ServiceName":"s222-umami-60e104-47-web@docker","ServiceURL":{"Scheme":"http","Opaque":"","User":null,"Host":"10.0.1.15:3000","Path":"","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":""},"StartLocal":"2024-08-25T04:34:37.306691884Z","StartUTC":"2024-08-25T04:34:37.306691884Z","entryPointName":"web","level":"info","msg":"","time":"2024-08-25T04:34:37Z"}`;

View File

@@ -5,11 +5,12 @@ vi.mock("node:fs", () => ({
default: fs,
}));
import type { Admin } from "@/server/api/services/admin";
import { createDefaultServerTraefikConfig } from "@/server/setup/traefik-setup";
import { loadOrCreateConfig } from "@/server/utils/traefik/application";
import type { FileConfig } from "@/server/utils/traefik/file-types";
import { updateServerTraefik } from "@/server/utils/traefik/web-server";
import type { Admin, FileConfig } from "@dokploy/server";
import {
createDefaultServerTraefikConfig,
loadOrCreateConfig,
updateServerTraefik,
} from "@dokploy/server";
import { beforeEach, expect, test, vi } from "vitest";
const baseAdmin: Admin = {

View File

@@ -1,7 +1,7 @@
import type { Domain } from "@/server/api/services/domain";
import type { Redirect } from "@/server/api/services/redirect";
import type { ApplicationNested } from "@/server/utils/builders";
import { createRouterConfig } from "@/server/utils/traefik/domain";
import type { Domain } from "@dokploy/server";
import type { Redirect } from "@dokploy/server";
import type { ApplicationNested } from "@dokploy/server";
import { createRouterConfig } from "@dokploy/server";
import { expect, test } from "vitest";
const baseApp: ApplicationNested = {

View File

@@ -13,4 +13,9 @@ export default defineConfig({
exclude: ["**/node_modules/**", "**/dist/**", "**/.docker/**"],
pool: "forks",
},
define: {
"process.env": {
NODE: "test",
},
},
});

View File

@@ -17,7 +17,7 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Input, NumberInput } from "@/components/ui/input";
import {
Select,
SelectContent,
@@ -125,28 +125,14 @@ export const UpdatePort = ({ portId }: Props) => {
<FormItem>
<FormLabel>Published Port</FormLabel>
<FormControl>
<Input
placeholder="1-65535"
{...field}
value={field.value?.toString() || ""}
onChange={(e) => {
const value = e.target.value;
if (value === "") {
field.onChange(0);
} else {
const number = Number.parseInt(value, 10);
if (!Number.isNaN(number)) {
field.onChange(number);
}
}
}}
/>
<NumberInput placeholder="1-65535" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="targetPort"
@@ -154,22 +140,7 @@ export const UpdatePort = ({ portId }: Props) => {
<FormItem>
<FormLabel>Target Port</FormLabel>
<FormControl>
<Input
placeholder="1-65535"
{...field}
value={field.value?.toString() || ""}
onChange={(e) => {
const value = e.target.value;
if (value === "") {
field.onChange(0);
} else {
const number = Number.parseInt(value, 10);
if (!Number.isNaN(number)) {
field.onChange(number);
}
}
}}
/>
<Input placeholder="1-65535" {...field} />
</FormControl>
<FormMessage />

View File

@@ -19,6 +19,15 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -36,6 +45,36 @@ const AddRedirectchema = z.object({
type AddRedirect = z.infer<typeof AddRedirectchema>;
// Default presets
const redirectPresets = [
// {
// label: "Allow www & non-www.",
// redirect: {
// regex: "",
// permanent: false,
// replacement: "",
// },
// },
{
id: "to-www",
label: "Redirect to www",
redirect: {
regex: "^https?://(?:www.)?(.+)",
permanent: true,
replacement: "https://www.$${1}",
},
},
{
id: "to-non-www",
label: "Redirect to non-www",
redirect: {
regex: "^https?://www.(.+)",
permanent: true,
replacement: "https://$${1}",
},
},
];
interface Props {
applicationId: string;
children?: React.ReactNode;
@@ -43,9 +82,10 @@ interface Props {
export const AddRedirect = ({
applicationId,
children = <PlusIcon className="h-4 w-4" />,
children = <PlusIcon className="w-4 h-4" />,
}: Props) => {
const [isOpen, setIsOpen] = useState(false);
const [presetSelected, setPresetSelected] = useState("");
const utils = api.useUtils();
const { mutateAsync, isLoading, error, isError } =
@@ -81,19 +121,36 @@ export const AddRedirect = ({
await utils.application.readTraefikConfig.invalidate({
applicationId,
});
setIsOpen(false);
onDialogToggle(false);
})
.catch(() => {
toast.error("Error to create the redirect");
});
};
const onDialogToggle = (open: boolean) => {
setIsOpen(open);
// commented for the moment because not reseting the form if accidentally closed the dialog can be considered as a feature instead of a bug
// setPresetSelected("");
// form.reset();
};
const onPresetSelect = (presetId: string) => {
const redirectPreset = redirectPresets.find(
(preset) => preset.id === presetId,
)?.redirect;
if (!redirectPreset) return;
const { regex, permanent, replacement } = redirectPreset;
form.reset({ regex, permanent, replacement }, { keepDefaultValues: true });
setPresetSelected(presetId);
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<Dialog open={isOpen} onOpenChange={onDialogToggle}>
<DialogTrigger asChild>
<Button>{children}</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
<DialogHeader>
<DialogTitle>Redirects</DialogTitle>
<DialogDescription>
@@ -102,6 +159,24 @@ export const AddRedirect = ({
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<div className="md:col-span-2">
<Label>Presets</Label>
<Select onValueChange={onPresetSelect} value={presetSelected}>
<SelectTrigger>
<SelectValue placeholder="No preset selected" />
</SelectTrigger>
<SelectContent>
{redirectPresets.map((preset) => (
<SelectItem key={preset.label} value={preset.id}>
{preset.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<Separator />
<Form {...form}>
<form
id="hook-form-add-redirect"
@@ -142,7 +217,7 @@ export const AddRedirect = ({
control={form.control}
name="permanent"
render={({ field }) => (
<FormItem className="mt-4 flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<FormItem className="flex flex-row items-center justify-between p-3 mt-4 border rounded-lg shadow-sm">
<div className="space-y-0.5">
<FormLabel>Permanent</FormLabel>
<FormDescription>

View File

@@ -80,7 +80,7 @@ export const ShowApplicationResources = ({ applicationId }: Props) => {
<CardHeader>
<CardTitle className="text-xl">Resources</CardTitle>
<CardDescription>
If you want to decrease or increase the resources to a specific
If you want to decrease or increase the resources to a specific.
application or database
</CardDescription>
</CardHeader>

View File

@@ -16,20 +16,37 @@ interface Props {
export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => {
const [data, setData] = useState("");
const endOfLogsRef = useRef<HTMLDivElement>(null);
const wsRef = useRef<WebSocket | null>(null); // Ref to hold WebSocket instance
useEffect(() => {
if (!open || !logPath) return;
setData("");
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}${serverId ? `&serverId=${serverId}` : ""}`;
const ws = new WebSocket(wsUrl);
wsRef.current = ws; // Store WebSocket instance in ref
ws.onmessage = (e) => {
setData((currentData) => currentData + e.data);
};
return () => ws.close();
ws.onerror = (error) => {
console.error("WebSocket error: ", error);
};
ws.onclose = () => {
console.log("WebSocket connection closed");
wsRef.current = null; // Clear reference on close
};
return () => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
ws.close();
wsRef.current = null;
}
};
}, [logPath, open]);
const scrollToBottom = () => {
@@ -45,7 +62,15 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => {
open={open}
onOpenChange={(e) => {
onClose();
if (!e) setData("");
if (!e) {
setData("");
}
if (wsRef.current) {
if (wsRef.current.readyState === WebSocket.OPEN) {
wsRef.current.close();
}
}
}}
>
<DialogContent className={"sm:max-w-5xl overflow-y-auto max-h-screen"}>

View File

@@ -18,7 +18,7 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Input, NumberInput } from "@/components/ui/input";
import {
Select,
SelectContent,
@@ -140,7 +140,7 @@ export const AddDomain = ({
<DialogTrigger className="" asChild>
{children}
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
<DialogHeader>
<DialogTitle>Domain</DialogTitle>
<DialogDescription>{dictionary.dialogDescription}</DialogDescription>
@@ -228,19 +228,36 @@ export const AddDomain = ({
<FormItem>
<FormLabel>Container Port</FormLabel>
<FormControl>
<Input
placeholder={"3000"}
{...field}
onChange={(e) => {
field.onChange(Number.parseInt(e.target.value));
}}
/>
<NumberInput placeholder={"3000"} {...field} />
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>
<FormField
control={form.control}
name="https"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between p-3 mt-4 border rounded-lg shadow-sm">
<div className="space-y-0.5">
<FormLabel>HTTPS</FormLabel>
<FormDescription>
Automatically provision SSL Certificate.
</FormDescription>
<FormMessage />
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
{form.getValues().https && (
<FormField
control={form.control}
@@ -270,28 +287,6 @@ export const AddDomain = ({
)}
/>
)}
<FormField
control={form.control}
name="https"
render={({ field }) => (
<FormItem className="mt-4 flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>HTTPS</FormLabel>
<FormDescription>
Automatically provision SSL Certificate.
</FormDescription>
<FormMessage />
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
</form>

View File

@@ -1,358 +0,0 @@
import { UseFormGetValues } from "react-hook-form";
import { z } from "zod";
export const providersData = [
{
name: "S3",
type: "s3",
properties: [
{
name: "accessKey",
type: "text",
label: "Access Key",
description: "Your S3 Access Key",
required: true,
default: "",
},
{
name: "secretAccessKey",
type: "password",
label: "Secret Access Key",
description: "Your S3 Secret Access Key",
required: true,
default: "",
},
{
name: "region",
type: "text",
label: "Region",
description: "AWS Region, e.g., us-east-1",
required: true,
default: "",
},
{
name: "endpoint",
type: "text",
label: "Endpoint",
description: "S3 Endpoint URL",
required: true,
default: "https://s3.amazonaws.com",
},
{
name: "bucket",
type: "text",
label: "Bucket Name",
description: "Name of the S3 bucket",
required: true,
default: "",
},
{
name: "provider",
type: "select",
label: "S3 Provider",
description: "Select your S3 provider",
required: false,
default: "AWS",
options: ["AWS", "Ceph", "Minio", "Alibaba", "Other"],
},
{
name: "storageClass",
type: "text",
label: "Storage Class",
description: "S3 Storage Class, e.g., STANDARD, REDUCED_REDUNDANCY",
required: false,
default: "",
},
{
name: "acl",
type: "text",
label: "ACL",
description: "Access Control List settings for S3",
required: false,
default: "",
},
],
},
{
name: "GCS",
type: "gcs",
properties: [
{
name: "serviceAccountFile",
type: "text",
label: "Service Account File",
description:
"Path to the JSON file containing your service account key",
required: false,
default: "",
},
{
name: "clientId",
type: "text",
label: "Client ID",
description:
"Your GCS OAuth Client ID (required if Service Account File not provided)",
required: false,
default: "",
},
{
name: "clientSecret",
type: "password",
label: "Client Secret",
description:
"Your GCS OAuth Client Secret (required if Service Account File not provided)",
required: false,
default: "",
},
{
name: "projectNumber",
type: "text",
label: "Project Number",
description:
"Your GCS Project Number (required if Service Account File not provided)",
required: false,
default: "",
},
{
name: "bucket",
type: "text",
label: "Bucket Name",
description: "Name of the GCS bucket",
required: true,
default: "",
},
{
name: "objectAcl",
type: "text",
label: "Object ACL",
description: "Access Control List for objects uploaded to GCS",
required: false,
default: "",
},
{
name: "bucketAcl",
type: "text",
label: "Bucket ACL",
description: "Access Control List for the GCS bucket",
required: false,
default: "",
},
],
},
{
name: "Azure Blob",
type: "azureblob",
properties: [
{
name: "account",
type: "text",
label: "Account Name",
description: "Your Azure Storage account name",
required: true,
default: "",
},
{
name: "key",
type: "password",
label: "Account Key",
description: "Your Azure Storage account access key",
required: true,
default: "",
},
{
name: "endpoint",
type: "text",
label: "Endpoint",
description: "Custom endpoint for Azure Blob Storage (if any)",
required: false,
default: "",
},
{
name: "container",
type: "text",
label: "Container Name",
description: "Name of the Azure Blob container",
required: true,
default: "",
},
],
},
{
name: "Dropbox",
type: "dropbox",
properties: [
{
name: "token",
type: "password",
label: "Access Token",
description: "Your Dropbox access token",
required: true,
default: "",
},
{
name: "path",
type: "text",
label: "Destination Path",
description: "Path in Dropbox where the files will be uploaded",
required: false,
default: "/",
},
],
},
{
name: "FTP",
type: "ftp",
properties: [
{
name: "host",
type: "text",
label: "FTP Host",
description: "Hostname or IP address of the FTP server",
required: true,
default: "",
},
{
name: "port",
type: "number",
label: "FTP Port",
description: "Port number of the FTP server",
required: false,
default: 21,
},
{
name: "user",
type: "text",
label: "Username",
description: "FTP username",
required: true,
default: "",
},
{
name: "pass",
type: "password",
label: "Password",
description: "FTP password",
required: true,
default: "",
},
{
name: "secure",
type: "checkbox",
label: "Use FTPS",
description: "Enable FTPS (FTP over SSL/TLS)",
required: false,
default: false,
},
{
name: "path",
type: "text",
label: "Destination Path",
description: "Remote path on the FTP server",
required: false,
default: "/",
},
],
},
];
/**
* S3 Provider Schema
*/
export const s3Schema = z.object({
accessKey: z.string().nonempty({ message: "Access Key is required" }),
secretAccessKey: z
.string()
.nonempty({ message: "Secret Access Key is required" }),
region: z.string().nonempty({ message: "Region is required" }),
endpoint: z
.string()
.nonempty({ message: "Endpoint is required" })
.default("https://s3.amazonaws.com"),
bucket: z.string().nonempty({ message: "Bucket Name is required" }),
provider: z
.enum(["AWS", "Ceph", "Minio", "Alibaba", "Other"])
.optional()
.default("AWS"),
storageClass: z.string().optional(),
acl: z.string().optional(),
});
/**
* Azure Blob Storage Provider Schema
*/
export const azureBlobSchema = z.object({
account: z.string().nonempty({ message: "Account Name is required" }),
key: z.string().nonempty({ message: "Account Key is required" }),
endpoint: z.string().optional(),
container: z.string().nonempty({ message: "Container Name is required" }),
});
/**
* Dropbox Provider Schema
*/
export const dropboxSchema = z.object({
token: z.string().nonempty({ message: "Access Token is required" }),
path: z.string().optional().default("/"),
});
/**
* FTP Provider Schema
*/
export const ftpSchema = z.object({
host: z.string().nonempty({ message: "FTP Host is required" }),
port: z.number().optional().default(21),
user: z.string().nonempty({ message: "Username is required" }),
pass: z.string().nonempty({ message: "Password is required" }),
secure: z.boolean().optional().default(false),
path: z.string().optional().default("/"),
});
/**
* Exporting all schemas in a single object for convenience
*/
export const providerSchemas = {
s3: s3Schema,
azureblob: azureBlobSchema,
dropbox: dropboxSchema,
ftp: ftpSchema,
};
export const getObjectSchema = (schema: z.ZodTypeAny) => {
const initialValues: any = {};
if (schema instanceof z.ZodObject) {
const shape = schema._def.shape();
for (const [key, fieldSchema] of Object.entries(shape)) {
if ("_def" in fieldSchema && "defaultValue" in fieldSchema._def) {
initialValues[key] = fieldSchema._def.defaultValue();
} else {
initialValues[key] = "";
}
}
}
return initialValues;
};
export const mergeFormValues = (
schema: z.ZodTypeAny,
values: Record<string, any>,
) => {
const initialSchemaObj = getObjectSchema(schema);
const properties: any = {};
for (const key in values) {
const keysMatch = Object.keys(initialSchemaObj).filter((k) => k === key);
if (keysMatch.length === 0) {
continue;
}
properties[keysMatch[0] as keyof typeof initialSchemaObj] =
values[key] || "";
}
return properties;
};

View File

@@ -52,7 +52,7 @@ export const ShowDomains = ({ applicationId }: Props) => {
<div className="flex w-full flex-col items-center justify-center gap-3">
<GlobeIcon className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To access to the application is required to set at least 1
To access the application it is required to set at least 1
domain
</span>
<div className="flex flex-row gap-4 flex-wrap">

View File

@@ -21,20 +21,38 @@ export const ShowDeploymentCompose = ({
}: Props) => {
const [data, setData] = useState("");
const endOfLogsRef = useRef<HTMLDivElement>(null);
const wsRef = useRef<WebSocket | null>(null); // Ref to hold WebSocket instance
useEffect(() => {
if (!open || !logPath) return;
setData("");
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}&serverId=${serverId}`;
const ws = new WebSocket(wsUrl);
wsRef.current = ws; // Store WebSocket instance in ref
ws.onmessage = (e) => {
setData((currentData) => currentData + e.data);
};
return () => ws.close();
ws.onerror = (error) => {
console.error("WebSocket error: ", error);
};
ws.onclose = () => {
console.log("WebSocket connection closed");
wsRef.current = null;
};
return () => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
ws.close();
wsRef.current = null;
}
};
}, [logPath, open]);
const scrollToBottom = () => {
@@ -50,7 +68,15 @@ export const ShowDeploymentCompose = ({
open={open}
onOpenChange={(e) => {
onClose();
if (!e) setData("");
if (!e) {
setData("");
}
if (wsRef.current) {
if (wsRef.current.readyState === WebSocket.OPEN) {
wsRef.current.close();
}
}
}}
>
<DialogContent className={"sm:max-w-5xl overflow-y-auto max-h-screen"}>

View File

@@ -18,7 +18,7 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Input, NumberInput } from "@/components/ui/input";
import {
Select,
SelectContent,
@@ -161,7 +161,7 @@ export const AddDomainCompose = ({
<DialogTrigger className="" asChild>
{children}
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
<DialogHeader>
<DialogTitle>Domain</DialogTitle>
<DialogDescription>{dictionary.dialogDescription}</DialogDescription>
@@ -190,7 +190,7 @@ export const AddDomainCompose = ({
{errorServices?.message}
</AlertBlock>
)}
<div className="flex flex-row gap-4 w-full items-end">
<div className="flex flex-row items-end w-full gap-4">
<FormField
control={form.control}
name="serviceName"
@@ -364,19 +364,36 @@ export const AddDomainCompose = ({
<FormItem>
<FormLabel>Container Port</FormLabel>
<FormControl>
<Input
placeholder={"3000"}
{...field}
onChange={(e) => {
field.onChange(Number.parseInt(e.target.value));
}}
/>
<NumberInput placeholder={"3000"} {...field} />
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>
<FormField
control={form.control}
name="https"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between p-3 mt-4 border rounded-lg shadow-sm">
<div className="space-y-0.5">
<FormLabel>HTTPS</FormLabel>
<FormDescription>
Automatically provision SSL Certificate.
</FormDescription>
<FormMessage />
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
{https && (
<FormField
control={form.control}
@@ -406,28 +423,6 @@ export const AddDomainCompose = ({
)}
/>
)}
<FormField
control={form.control}
name="https"
render={({ field }) => (
<FormItem className="mt-4 flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>HTTPS</FormLabel>
<FormDescription>
Automatically provision SSL Certificate.
</FormDescription>
<FormMessage />
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
</form>

View File

@@ -53,7 +53,7 @@ export const ShowDomainsCompose = ({ composeId }: Props) => {
<div className="flex w-full flex-col items-center justify-center gap-3">
<GlobeIcon className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To access to the application is required to set at least 1
To access to the application it is required to set at least 1
domain
</span>
<div className="flex flex-row gap-4 flex-wrap">

View File

@@ -119,7 +119,6 @@ export const ComposeActions = ({ composeId }: Props) => {
</DropdownMenuContent>
</DropdownMenu>
)}
{data?.server?.name}
</div>
);
};

View File

@@ -77,7 +77,6 @@ export const ComposeFileEditor = ({ composeId }: Props) => {
});
})
.catch((e) => {
console.log(e);
toast.error("Error to update the compose config");
});
};

View File

@@ -11,7 +11,7 @@ import {
} from "@/components/ui/dialog";
import { api } from "@/utils/api";
import { Puzzle, RefreshCw } from "lucide-react";
import { useState } from "react";
import { useEffect, useState } from "react";
import { toast } from "sonner";
interface Props {
@@ -34,6 +34,16 @@ export const ShowConvertedCompose = ({ composeId }: Props) => {
const { mutateAsync, isLoading } = api.compose.fetchSourceType.useMutation();
useEffect(() => {
if (isOpen) {
mutateAsync({ composeId })
.then(() => {
refetch();
})
.catch((err) => {});
}
}, [isOpen]);
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>

View File

@@ -1,7 +1,7 @@
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Terminal } from "@xterm/xterm";
import React, { useEffect } from "react";
import React, { useEffect, useRef } from "react";
import { FitAddon } from "xterm-addon-fit";
import "@xterm/xterm/css/xterm.css";
@@ -18,12 +18,24 @@ export const DockerLogsId: React.FC<Props> = ({
}) => {
const [term, setTerm] = React.useState<Terminal>();
const [lines, setLines] = React.useState<number>(40);
const wsRef = useRef<WebSocket | null>(null); // Ref to hold WebSocket instance
const createTerminal = (): Terminal => {
useEffect(() => {
// if (containerId === "select-a-container") {
// return;
// }
const container = document.getElementById(id);
if (container) {
container.innerHTML = "";
}
if (wsRef.current) {
console.log(wsRef.current);
if (wsRef.current.readyState === WebSocket.OPEN) {
wsRef.current.close();
}
wsRef.current = null;
}
const termi = new Terminal({
cursorBlink: true,
cols: 80,
@@ -45,7 +57,7 @@ export const DockerLogsId: React.FC<Props> = ({
const wsUrl = `${protocol}//${window.location.host}/docker-container-logs?containerId=${containerId}&tail=${lines}${serverId ? `&serverId=${serverId}` : ""}`;
const ws = new WebSocket(wsUrl);
wsRef.current = ws;
const fitAddon = new FitAddon();
termi.loadAddon(fitAddon);
// @ts-ignore
@@ -54,6 +66,10 @@ export const DockerLogsId: React.FC<Props> = ({
termi.focus();
setTerm(termi);
ws.onerror = (error) => {
console.error("WebSocket error: ", error);
};
ws.onmessage = (e) => {
termi.write(e.data);
};
@@ -62,12 +78,14 @@ export const DockerLogsId: React.FC<Props> = ({
console.log(e.reason);
termi.write(`Connection closed!\nReason: ${e.reason}\n`);
wsRef.current = null;
};
return () => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
ws.close();
wsRef.current = null;
}
};
return termi;
};
useEffect(() => {
createTerminal();
}, [lines, containerId]);
useEffect(() => {

View File

@@ -79,7 +79,7 @@ export const ShowMariadbResources = ({ mariadbId }: Props) => {
<CardHeader>
<CardTitle className="text-xl">Resources</CardTitle>
<CardDescription>
If you want to decrease or increase the resources to a specific
If you want to decrease or increase the resources to a specific.
application or database
</CardDescription>
</CardHeader>

View File

@@ -44,7 +44,7 @@ export const ShowBackupMariadb = ({ mariadbId }: Props) => {
<div className="flex flex-col gap-0.5">
<CardTitle className="text-xl">Backups</CardTitle>
<CardDescription>
Add backup to your database to save the data to a different
Add backups to your database to save the data to a different
providers.
</CardDescription>
</div>
@@ -62,8 +62,8 @@ export const ShowBackupMariadb = ({ mariadbId }: Props) => {
<div className="flex flex-col items-center gap-3">
<DatabaseBackup className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To create a backup is required to set at least 1 provider. Please,
go to{" "}
To create a backup it is required to set at least 1 provider.
Please, go to{" "}
<Link
href="/dashboard/settings/server"
className="text-foreground"

View File

@@ -48,6 +48,7 @@ export const ShowExternalMariadbCredentials = ({ mariadbId }: Props) => {
const { data, refetch } = api.mariadb.one.useQuery({ mariadbId });
const { mutateAsync, isLoading } = api.mariadb.saveExternalPort.useMutation();
const [connectionUrl, setConnectionUrl] = useState("");
const getIp = data?.server?.ipAddress || ip;
const form = useForm<DockerProvider>({
defaultValues: {},
resolver: zodResolver(DockerProviderSchema),
@@ -79,7 +80,7 @@ export const ShowExternalMariadbCredentials = ({ mariadbId }: Props) => {
const buildConnectionUrl = () => {
const port = form.watch("externalPort") || data?.externalPort;
return `mariadb://${data?.databaseUser}:${data?.databasePassword}@${ip}:${port}/${data?.databaseName}`;
return `mariadb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`;
};
setConnectionUrl(buildConnectionUrl());
@@ -90,7 +91,7 @@ export const ShowExternalMariadbCredentials = ({ mariadbId }: Props) => {
form,
data?.databaseName,
data?.databaseUser,
ip,
getIp,
]);
return (
<>

View File

@@ -79,7 +79,7 @@ export const ShowMongoResources = ({ mongoId }: Props) => {
<CardHeader>
<CardTitle className="text-xl">Resources</CardTitle>
<CardDescription>
If you want to decrease or increase the resources to a specific
If you want to decrease or increase the resources to a specific.
application or database
</CardDescription>
</CardHeader>

View File

@@ -44,8 +44,8 @@ export const ShowBackupMongo = ({ mongoId }: Props) => {
<div className="flex flex-col gap-0.5">
<CardTitle className="text-xl">Backups</CardTitle>
<CardDescription>
Add backup to your database to save the data to a different
providers.
Add backups to your database to save the data to a different
provider.
</CardDescription>
</div>
@@ -62,8 +62,8 @@ export const ShowBackupMongo = ({ mongoId }: Props) => {
<div className="flex flex-col items-center gap-3">
<DatabaseBackup className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To create a backup is required to set at least 1 provider. Please,
go to{" "}
To create a backup it is required to set at least 1 provider.
Please, go to{" "}
<Link
href="/dashboard/settings/server"
className="text-foreground"

View File

@@ -48,7 +48,7 @@ export const ShowExternalMongoCredentials = ({ mongoId }: Props) => {
const { data, refetch } = api.mongo.one.useQuery({ mongoId });
const { mutateAsync, isLoading } = api.mongo.saveExternalPort.useMutation();
const [connectionUrl, setConnectionUrl] = useState("");
const getIp = data?.server?.ipAddress || ip;
const form = useForm<DockerProvider>({
defaultValues: {},
resolver: zodResolver(DockerProviderSchema),
@@ -80,7 +80,7 @@ export const ShowExternalMongoCredentials = ({ mongoId }: Props) => {
const buildConnectionUrl = () => {
const port = form.watch("externalPort") || data?.externalPort;
return `mongodb://${data?.databaseUser}:${data?.databasePassword}@${ip}:${port}`;
return `mongodb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}`;
};
setConnectionUrl(buildConnectionUrl());
@@ -90,7 +90,7 @@ export const ShowExternalMongoCredentials = ({ mongoId }: Props) => {
data?.databasePassword,
form,
data?.databaseUser,
ip,
getIp,
]);
return (

View File

@@ -30,7 +30,7 @@ export const ShowVolumes = ({ mongoId }: Props) => {
<div>
<CardTitle className="text-xl">Volumes</CardTitle>
<CardDescription>
If you want to persist data in this mongo use the following config
If you want to persist data in this mongo use the following config.
to setup the volumes
</CardDescription>
</div>

View File

@@ -191,7 +191,7 @@ export const DockerMonitoring = ({
<CardHeader>
<CardTitle className="text-xl">Monitoring</CardTitle>
<CardDescription>
Watch the usage of your server in the current app
Watch the usage of your server in the current app.
</CardDescription>
</CardHeader>
<CardContent className="flex flex-col gap-4">

View File

@@ -79,7 +79,7 @@ export const ShowMysqlResources = ({ mysqlId }: Props) => {
<CardHeader>
<CardTitle className="text-xl">Resources</CardTitle>
<CardDescription>
If you want to decrease or increase the resources to a specific
If you want to decrease or increase the resources to a specific.
application or database
</CardDescription>
</CardHeader>

View File

@@ -44,8 +44,8 @@ export const ShowBackupMySql = ({ mysqlId }: Props) => {
<div className="flex flex-col gap-0.5">
<CardTitle className="text-xl">Backups</CardTitle>
<CardDescription>
Add backup to your database to save the data to a different
providers.
Add backups to your database to save the data to a different
provider.
</CardDescription>
</div>
@@ -62,8 +62,8 @@ export const ShowBackupMySql = ({ mysqlId }: Props) => {
<div className="flex flex-col items-center gap-3">
<DatabaseBackup className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To create a backup is required to set at least 1 provider. Please,
go to{" "}
To create a backup it is required to set at least 1 provider.
Please, go to{" "}
<Link
href="/dashboard/settings/server"
className="text-foreground"

View File

@@ -48,7 +48,7 @@ export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => {
const { data, refetch } = api.mysql.one.useQuery({ mysqlId });
const { mutateAsync, isLoading } = api.mysql.saveExternalPort.useMutation();
const [connectionUrl, setConnectionUrl] = useState("");
const getIp = data?.server?.ipAddress || ip;
const form = useForm<DockerProvider>({
defaultValues: {},
resolver: zodResolver(DockerProviderSchema),
@@ -80,7 +80,7 @@ export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => {
const buildConnectionUrl = () => {
const port = form.watch("externalPort") || data?.externalPort;
return `mysql://${data?.databaseUser}:${data?.databasePassword}@${ip}:${port}/${data?.databaseName}`;
return `mysql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`;
};
setConnectionUrl(buildConnectionUrl());
@@ -91,7 +91,7 @@ export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => {
data?.databaseName,
data?.databaseUser,
form,
ip,
getIp,
]);
return (
<>

View File

@@ -79,7 +79,7 @@ export const ShowPostgresResources = ({ postgresId }: Props) => {
<CardHeader>
<CardTitle className="text-xl">Resources</CardTitle>
<CardDescription>
If you want to decrease or increase the resources to a specific
If you want to decrease or increase the resources to a specific.
application or database
</CardDescription>
</CardHeader>

View File

@@ -45,8 +45,8 @@ export const ShowBackupPostgres = ({ postgresId }: Props) => {
<div className="flex flex-col gap-0.5">
<CardTitle className="text-xl">Backups</CardTitle>
<CardDescription>
Add backup to your database to save the data to a different
providers.
Add backups to your database to save the data to a different
provider.
</CardDescription>
</div>
@@ -63,8 +63,8 @@ export const ShowBackupPostgres = ({ postgresId }: Props) => {
<div className="flex flex-col items-center gap-3">
<DatabaseBackup className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To create a backup is required to set at least 1 provider. Please,
go to{" "}
To create a backup it is required to set at least 1 provider.
Please, go to{" "}
<Link
href="/dashboard/settings/server"
className="text-foreground"

View File

@@ -48,6 +48,7 @@ export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => {
const { data, refetch } = api.postgres.one.useQuery({ postgresId });
const { mutateAsync, isLoading } =
api.postgres.saveExternalPort.useMutation();
const getIp = data?.server?.ipAddress || ip;
const [connectionUrl, setConnectionUrl] = useState("");
const form = useForm<DockerProvider>({
@@ -79,10 +80,9 @@ export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => {
useEffect(() => {
const buildConnectionUrl = () => {
const hostname = window.location.hostname;
const port = form.watch("externalPort") || data?.externalPort;
return `postgresql://${data?.databaseUser}:${data?.databasePassword}@${ip}:${port}/${data?.databaseName}`;
return `postgresql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`;
};
setConnectionUrl(buildConnectionUrl());
@@ -92,7 +92,7 @@ export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => {
data?.databasePassword,
form,
data?.databaseName,
ip,
getIp,
]);
return (

View File

@@ -145,7 +145,10 @@ export const AddApplication = ({ projectId, projectName }: Props) => {
{...field}
onChange={(e) => {
const val = e.target.value?.trim() || "";
form.setValue("appName", `${slug}-${val}`);
form.setValue(
"appName",
`${slug}-${val.toLowerCase().replaceAll(" ", "-")}`,
);
field.onChange(val);
}}
/>

View File

@@ -39,7 +39,7 @@ import { slugify } from "@/lib/slug";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { CircuitBoard, HelpCircle } from "lucide-react";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
@@ -71,6 +71,7 @@ interface Props {
export const AddCompose = ({ projectId, projectName }: Props) => {
const utils = api.useUtils();
const [visible, setVisible] = useState(false);
const slug = slugify(projectName);
const { data: servers } = api.server.withSSHKey.useQuery();
const { mutateAsync, isLoading, error, isError } =
@@ -101,6 +102,7 @@ export const AddCompose = ({ projectId, projectName }: Props) => {
})
.then(async () => {
toast.success("Compose Created");
setVisible(false);
await utils.project.one.invalidate({
projectId,
});
@@ -111,7 +113,7 @@ export const AddCompose = ({ projectId, projectName }: Props) => {
};
return (
<Dialog>
<Dialog open={visible} onOpenChange={setVisible}>
<DialogTrigger className="w-full">
<DropdownMenuItem
className="w-full cursor-pointer space-x-3"
@@ -149,7 +151,10 @@ export const AddCompose = ({ projectId, projectName }: Props) => {
{...field}
onChange={(e) => {
const val = e.target.value?.trim() || "";
form.setValue("appName", `${slug}-${val}`);
form.setValue(
"appName",
`${slug}-${val.toLowerCase()}`,
);
field.onChange(val);
}}
/>

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