Compare commits

...

162 Commits

Author SHA1 Message Date
cmcooper1980
f16b00908d Refactor URL and agent list rendering in Compose.vue 2026-04-13 18:50:27 -05:00
cmcooper1980
3b9f0b9a4f Remove commented-out code and clean imports 2026-04-12 04:22:28 -05:00
cmcooper1980
ca9c8b4ba1 Implement variable highlighting in Compose.vue
Added a variable highlighting feature to the editor using CodeMirror.
2026-04-12 03:02:44 -05:00
Justin Wiebe
b02f5e092e Add resource usage stats to the compose page (#700)
Co-authored-by: cmcooper1980 <31871143+cmcooper1980@users.noreply.github.com>
2026-04-11 23:42:38 -05:00
Lance Cain
078f762631 Container control buttons (#649)
Co-authored-by: cmcooper1980 <31871143+cmcooper1980@users.noreply.github.com>
additional QOL commits by @Dracrius
2026-04-11 18:58:45 -05:00
Julian
e589d4ec7e feat: dockge set/update agent friendly name (#414)
Co-authored-by: cmcooper1980 <31871143+cmcooper1980@users.noreply.github.com>
2026-04-11 14:46:45 -05:00
Matthew McConnell
7e324d9015 Improved stack list ui when using agents (#800) 2026-04-11 14:27:16 -05:00
cmcooper1980
72a941712d Fix formatting and capitalization in README.md
Corrected formatting and capitalization in README.
2026-04-10 17:21:23 -05:00
Richy HBM
46ce4228a5 Allow specifying which user the stack files should belong to (#83)
Co-authored-by: cmcooper1980 <31871143+cmcooper1980@users.noreply.github.com>
2026-04-10 17:14:18 -05:00
Avish Jha
cc180562fc feat (i18n): add Magahi and Maithili languages (#876) 2026-01-21 18:51:44 +08:00
Copilot
27bc4cd25c Add automated nightly release workflow (#915)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: louislam <1336778+louislam@users.noreply.github.com>
Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
2026-01-20 21:40:06 +08:00
Louis Lam
52605de5cd Revert package.json (#901) 2025-12-24 10:04:49 +08:00
Kevin
420c3af66d Global.env editor and usage in docker operations (#387)
Co-authored-by: Paco Culebras <69261057+pacoculebras@users.noreply.github.com>
Co-authored-by: Paco Culebras <pculebras@me.com>
Co-authored-by: Cyril59310 <70776486+cyril59310@users.noreply.github.com>
Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
Co-authored-by: cmcooper1980 <31871143+cmcooper1980@users.noreply.github.com>
2025-12-23 03:41:04 -06:00
Joshua Anderson
98cd537ba8 Replace editor with Codemirror (#786) 2025-12-23 02:00:49 -06:00
CampaniaGuy
33fb84b4db Theme options enabled in settings (#575) 2025-12-20 03:23:50 -06:00
Niraj Yadav
f2575d5c05 Remove logging of terminal buffer to console (#582)
Signed-off-by: Niraj Yadav <niryadav@redhat.com>
Co-authored-by: cmcooper1980 <31871143+cmcooper1980@users.noreply.github.com>
2025-12-20 03:17:14 -06:00
cmcooper1980
65e2e26c43 Fix paste handling for lowercase 'v' key 2025-12-20 02:30:05 -06:00
Dimariqe
cbb6b87a37 Add clipboard copy/paste support to terminal component (#822)
Co-authored-by: cmcooper1980 <31871143+cmcooper1980@users.noreply.github.com>
2025-12-20 01:44:30 -06:00
Lukáš Ondrejka
98cba39004 Implement RIGHT and LEFT keys for cursor navigation in terminal (#637)
Co-authored-by: cmcooper1980 <31871143+cmcooper1980@users.noreply.github.com>
2025-12-19 17:09:46 -06:00
Davide Marcoli
e31f766516 Add swiss german language file (#783) 2025-04-17 11:58:09 +08:00
Cyril59310
27bfe723d7 Add Translation keys (#768) 2025-04-14 20:08:40 +08:00
Louis Lam
69818d665d Fix: npm ci omit dev now 2025-04-14 20:07:37 +08:00
Louis Lam
bac498f97f Update to 1.5.0 2025-03-31 01:37:19 +08:00
Louis Lam
3e37f38fc7 Fix: request service status during add mode 2025-03-30 17:51:47 +08:00
Louis Lam
6dff52cc73 Fix: generate compose without project name 2025-03-30 17:35:33 +08:00
Louis Lam
7fcc4c510c Update README.md 2025-03-30 07:19:28 +08:00
Louis Lam
0ceb6336dd Console Improvements (#767) 2025-03-30 07:14:33 +08:00
Louis Lam
c62b91682e Switch back to npm (#766) 2025-03-29 16:17:49 +08:00
Louis Lam
970a826d5a Translations update from Kuma Weblate (#641) 2025-03-29 15:58:35 +08:00
Alex Nagy
86c7dfdb5b Translated using Weblate (Hungarian)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/hu/
2025-03-29 07:17:30 +00:00
yasin
6c357fb603 Translated using Weblate (Turkish)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/tr/
2025-03-29 07:17:30 +00:00
Casper
90255f05cb Translated using Weblate (Danish)
Currently translated at 95.3% (124 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/da/
2025-03-29 07:17:30 +00:00
Albert Parlys
c40c463788 Translated using Weblate (Indonesian)
Currently translated at 92.3% (120 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/id/
2025-03-29 07:17:30 +00:00
Alex Nagy
9fedd8790d Translated using Weblate (Hungarian)
Currently translated at 99.2% (129 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/hu/
2025-03-29 07:17:30 +00:00
Dragos Bunduc
65c719d95d Translated using Weblate (Romanian)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ro/
2025-03-29 07:17:30 +00:00
Aluisio
fb349e06b1 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/pt_BR/
2025-03-29 07:17:30 +00:00
Celer21
322f4ccee8 Translated using Weblate (Czech)
Currently translated at 97.6% (127 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/cs/
2025-03-29 07:17:30 +00:00
vipnetant
38d424d8bc Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/zh_Hans/
2025-03-29 07:17:30 +00:00
JIAN YI CHEN
4438adc04a Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/zh_Hant/
2025-03-29 07:17:30 +00:00
Aindriú Mac Giolla Eoin
053ea3643c Translated using Weblate (Irish)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ga/
2025-03-29 07:17:30 +00:00
Jordi Garcia
b7b1435d62 Translated using Weblate (Spanish)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/es/
2025-03-29 07:17:30 +00:00
Marco
9830bc345a Translated using Weblate (German)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/de/
2025-03-29 07:17:30 +00:00
Gunnar Norin
9d8fbf1af2 Translated using Weblate (Swedish)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/sv/
2025-03-29 07:17:30 +00:00
wial88
fb366cbf24 Translated using Weblate (German)
Currently translated at 99.2% (129 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/de/
2025-03-29 07:17:30 +00:00
retmas-gh
fd3e4910e2 Translated using Weblate (Polish)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/pl/
2025-03-29 07:17:30 +00:00
Celer21
6c0d8da11e Translated using Weblate (Czech)
Currently translated at 76.9% (100 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/cs/
2025-03-29 07:17:30 +00:00
Donker_Jumala
a2f96913c2 Translated using Weblate (Japanese)
Currently translated at 97.6% (127 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ja/
2025-03-29 07:17:29 +00:00
stanol
40fc0ebb06 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/uk/
2025-03-29 07:17:29 +00:00
Cyril59310
be562ce66e Translated using Weblate (French)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/fr/
2025-03-29 07:17:29 +00:00
MrEddX
3fff0dbd51 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (130 of 130 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/bg/
2025-03-29 07:17:29 +00:00
APEX
71e773ae9f Fix: configs.content syntax not supported despite being valid in Docker Compose 2.23.1+ (#740)
Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
2025-03-29 15:17:25 +08:00
Louis Lam
74e9efd471 Update dependencies 2025-03-29 15:14:25 +08:00
Louis Lam
d451e06e84 Update dependencies 2025-01-04 18:14:17 +08:00
turnah
a65a9f5549 fix bug 176: preserve YAML comments when reordering items by matching… (#685) 2024-12-31 15:43:17 +08:00
Cyril59310
9b73e44cd9 Remove useless scrollbar (#642) 2024-12-31 15:41:15 +08:00
Louis Lam
81818a19d1 Update dependencies 2024-12-26 16:22:24 +08:00
Louis Lam
1372bd2ce1 Remove the tsx workaround as it had been fixed in upstream 2024-12-26 16:22:08 +08:00
Louis Lam
01906205f0 1.5.x (#636) 2024-10-14 14:20:34 +08:00
Louis Lam
28337c5430 Add ARMv7 for CI 2024-10-14 14:14:52 +08:00
Louis Lam
5baf48db63 Also update ci's node version to 22 2024-10-14 00:53:24 +08:00
Cyril59310
b2c8fdab75 Add Translation keys (#506) 2024-10-14 00:51:59 +08:00
Louis Lam
e12525fa42 Pin node-pty-prebuilt-multiarch 2024-10-13 21:11:38 +08:00
Louis Lam
3e3f67c6b7 Update css for terminal 2024-10-13 21:11:20 +08:00
Louis Lam
020faa49d2 Merge branch 'master' into 1.5.X 2024-10-13 20:43:58 +08:00
Louis Lam
df95d7ce9d Translations update from Kuma Weblate (#396) 2024-10-13 20:41:38 +08:00
Gerge
7a2524c542 Translated using Weblate (Hungarian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/hu/
2024-10-13 12:40:45 +00:00
teatower
6ceaa70cdd Translated using Weblate (German)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/de/
2024-10-13 12:40:45 +00:00
MatyiFKBT
caea8996da Translated using Weblate (Hungarian)
Currently translated at 99.1% (113 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/hu/
2024-10-13 12:40:45 +00:00
stanol
39e3d5a07c Translated using Weblate (Ukrainian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/uk/
2024-10-13 12:40:45 +00:00
p00d
723afb5bc2 Translated using Weblate (Italian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/it/
2024-10-13 12:40:45 +00:00
Yahor Haurylenka
3b3b3a7940 Translated using Weblate (Belarusian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/be/
2024-10-13 12:40:45 +00:00
Yahor Haurylenka
f9309a0650 Added translation using Weblate (Belarusian) 2024-10-13 12:40:45 +00:00
Bram Van Mol
54c2be7abe Translated using Weblate (Dutch)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/nl/
2024-10-13 12:40:45 +00:00
er2de2
48db1c73a8 Translated using Weblate (Polish)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/pl/
2024-10-13 12:40:45 +00:00
Rumplin
88f696d9b1 Translated using Weblate (Slovenian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/sl/
2024-10-13 12:40:45 +00:00
Paco Culebras
f80cfca64b Translated using Weblate (Catalan)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ca/
2024-10-13 12:40:45 +00:00
Doğukan Kurnaz
1ddd70791a Translated using Weblate (Turkish)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/tr/
2024-10-13 12:40:45 +00:00
Ahmed Talal
5f01347d2f Translated using Weblate (Arabic)
Currently translated at 96.4% (110 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ar/
2024-10-13 12:40:45 +00:00
Gerge
04c9a8669d Translated using Weblate (Hungarian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/hu/
2024-10-13 12:40:45 +00:00
Ismael
91b7c18c52 Translated using Weblate (Spanish)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/es/
2024-10-13 12:40:45 +00:00
Aindriú Mac Giolla Eoin
9cef4ad9ee Translated using Weblate (Irish)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ga/
2024-10-13 12:40:45 +00:00
Aindriú Mac Giolla Eoin
e7dd099f17 Added translation using Weblate (Irish) 2024-10-13 12:40:45 +00:00
letterlock
d27fd2919b Translated using Weblate (Danish)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/da/
2024-10-13 12:40:45 +00:00
Cosmo Abdon
e2f5796470 Translated using Weblate (Portuguese)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/pt/
2024-10-13 12:40:45 +00:00
Renan Bernordi
88f26f53c5 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/pt_BR/
2024-10-13 12:40:45 +00:00
Yoswaris Lawpaiboon
ccd9d96227 Translated using Weblate (Thai)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/th/
2024-10-13 12:40:45 +00:00
Simen Daehlin
a8dcfe4ccd Translated using Weblate (Norwegian Bokmål)
Currently translated at 27.1% (31 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/nb_NO/
2024-10-13 12:40:45 +00:00
Aji Priyo Wibowo
941ec0056a Translated using Weblate (Indonesian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/id/
2024-10-13 12:40:45 +00:00
Yoswaris Lawpaiboon
1bb6f2532c Translated using Weblate (Thai)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/th/
2024-10-13 12:40:45 +00:00
Gunnar Norin
6fb24adc66 Translated using Weblate (Swedish)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/sv/
2024-10-13 12:40:45 +00:00
Faisal Alali
c4fe952121 Translated using Weblate (Arabic)
Currently translated at 92.9% (106 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ar/
2024-10-13 12:40:45 +00:00
Simen Daehlin
59bfe79c40 Added translation using Weblate (Norwegian Bokmål) 2024-10-13 12:40:45 +00:00
Dominika Jadowska
9e89f49e38 Fixed: An agent do not disconnect correctly after removed (#617) 2024-10-13 20:40:40 +08:00
Louis Lam
19beb02b1e Update to Node.js 22 from 18, as the upstream cpu 100% issues has been fixed. 2024-10-13 20:36:04 +08:00
Louis Lam
9dd68372c2 Update dependencies 2024-10-13 20:20:47 +08:00
Cyril59310
109222f024 fix default compose version obsolete (#507) 2024-05-01 11:20:13 +08:00
Suman Jayapathi
5ad42a6dab docker compose version is obsolete (#473) 2024-04-20 12:47:20 +08:00
Grant Birkinbine
74c8baef93 Update json-yaml-validate to latest version (#446) 2024-03-13 16:28:02 +08:00
Aindriú Mac Giolla Eoin
c7ea2f9ee9 Added Irish language (#425) 2024-02-12 13:08:14 +08:00
Louis Lam
4a9173f5dc Update to 1.4.2 2024-01-22 00:54:58 +08:00
Louis Lam
3d641090c0 Translations update from Kuma Weblate (#381) 2024-01-22 00:52:35 +08:00
nazo6
32527100a0 Translated using Weblate (Japanese)
Currently translated at 95.6% (109 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ja/
2024-01-21 16:51:02 +00:00
catfishlty
30c69583a7 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/zh_Hans/
2024-01-21 16:51:02 +00:00
Alanimdeo
69cbe16745 Translated using Weblate (Korean)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ko/
2024-01-21 16:51:01 +00:00
DevMirza
f5df9a777c Translated using Weblate (Urdu)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ur/
2024-01-19 18:56:15 +00:00
stanol
c33a469972 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/uk/
2024-01-18 18:16:31 +00:00
Daniil Isakov
f667467091 Translated using Weblate (Russian)
Currently translated at 96.4% (110 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ru/
2024-01-18 18:16:31 +00:00
Cyril59310
2ff27b4073 Translated using Weblate (French)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/fr/
2024-01-18 18:16:28 +00:00
Marco
8ad6702932 Translated using Weblate (German)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/de/
2024-01-18 18:16:28 +00:00
MrEddX
6a7d7b5e43 Translated using Weblate (Bulgarian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/bg/
2024-01-18 18:16:28 +00:00
Louis Lam
66747b7a73 Workaround fix for tsx issue (#380) 2024-01-19 02:13:43 +08:00
Cyril59310
051cc11eaa Add translate key (#368) 2024-01-18 03:36:28 +08:00
Louis Lam
2e22f95720 Translations update from Kuma Weblate (#376) 2024-01-18 03:15:12 +08:00
Paco Culebras
4d10dc75a7 Translated using Weblate (Catalan)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ca/
2024-01-17 19:14:48 +00:00
Paco Culebras
af2d40eeac Added translation using Weblate (Catalan) 2024-01-17 19:14:48 +00:00
Paco Culebras
5420c73960 Update i18n.ts with catalan language (#377) 2024-01-18 03:14:43 +08:00
Cyril59310
2e6e2bda38 fix placeholder (#342) 2024-01-14 04:56:55 +08:00
Gerge
36c3f01d00 Add Hungarian (#344) 2024-01-14 04:56:43 +08:00
Louis Lam
7e05f51676 Translations update from Kuma Weblate (#340) 2024-01-14 04:56:30 +08:00
Aji Priyo Wibowo
f9baa9180f Translated using Weblate (Indonesian)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/id/
2024-01-13 03:56:14 +00:00
Dennis Mastrup
3204020749 Translated using Weblate (Danish)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/da/
2024-01-10 22:56:14 +00:00
401Unauthorized
6d8487c879 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/zh_Hans/
2024-01-10 22:56:14 +00:00
Tomasz Steć
cb72629596 Translated using Weblate (Polish)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/pl/
2024-01-10 22:56:14 +00:00
Gerge
658c2828e2 Translated using Weblate (Hungarian)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/hu/
2024-01-08 21:56:14 +00:00
queeup
22b9f04426 Translated using Weblate (Turkish)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/tr/
2024-01-08 21:56:14 +00:00
Ecoboaea Denis
0992408fa0 Translated using Weblate (Italian)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/it/
2024-01-08 21:56:14 +00:00
Gerge
a24b2199fa Added translation using Weblate (Hungarian) 2024-01-06 17:08:13 +00:00
dng-nguyn
b7b1708696 Translated using Weblate (Vietnamese)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/vi/
2024-01-04 14:56:14 +00:00
Louis Lam
17566fcd95 [README.md] Fix star badge 2024-01-04 03:12:43 +08:00
Louis Lam
ec6bdea711 Fix the remote url is undefined (#338) 2024-01-04 00:43:39 +08:00
Louis Lam
f8ad8c45fd Update to 1.4.0 2024-01-03 20:16:04 +08:00
Louis Lam
c239f40acc Translations update from Kuma Weblate (#324) 2024-01-03 19:58:50 +08:00
AICHIGUA
8efa58e0d0 Translated using Weblate (Chinese (Simplified))
Currently translated at 88.4% (100 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/zh_Hans/
2024-01-03 11:22:49 +00:00
Zandor Smith
dbbefa6c09 Translated using Weblate (Dutch)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/nl/
2024-01-02 18:01:29 +00:00
dng-nguyn
a253f8ab25 Translated using Weblate (Vietnamese)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/vi/
2024-01-02 18:01:29 +00:00
dng-nguyn
701b0158b1 Translated using Weblate (Vietnamese)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/vi/
2024-01-02 18:01:29 +00:00
dng-nguyn
c94eb4805d Added translation using Weblate (Vietnamese) 2024-01-02 18:01:29 +00:00
Louis Lam
4670121dfa Translated using Weblate (Chinese (Traditional))
Currently translated at 90.2% (102 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/zh_Hant/
2024-01-02 18:01:29 +00:00
Louis Lam
9d5d062420 Translated using Weblate (Chinese (Traditional))
Currently translated at 88.4% (100 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/zh_Hant/
2024-01-02 18:01:29 +00:00
Marco
21f7a677a3 Translated using Weblate (German)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/de/
2024-01-02 18:01:29 +00:00
demonisius
25026b1ed5 Translated using Weblate (Russian)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ru/
2024-01-02 18:01:29 +00:00
Abner Santana
afe433dbfa Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/pt_BR/
2024-01-02 18:01:29 +00:00
Christian Bergschneider
480c498974 Translated using Weblate (German)
Currently translated at 99.1% (112 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/de/
2024-01-02 18:01:29 +00:00
Marco
8fe75feb69 Translated using Weblate (German)
Currently translated at 99.1% (112 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/de/
2024-01-02 18:01:29 +00:00
stanol
17046b500b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/uk/
2024-01-02 18:01:29 +00:00
Gunnar Norin
75ff8e1d5c Translated using Weblate (Swedish)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/sv/
2024-01-02 18:01:29 +00:00
Alanimdeo
660da44938 Translated using Weblate (Korean)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ko/
2024-01-02 18:01:29 +00:00
Cyril59310
193f975c4c Translated using Weblate (French)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/fr/
2024-01-02 18:01:29 +00:00
Ismael
7f1b03edab Translated using Weblate (Spanish)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/es/
2024-01-02 18:01:29 +00:00
MrEddX
900ab8978f Translated using Weblate (Bulgarian)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/bg/
2024-01-02 18:01:29 +00:00
Louis Lam
11e71f373d Translated using Weblate (Japanese)
Currently translated at 82.3% (93 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/ja/
2024-01-02 18:01:29 +00:00
Louis Lam
8bd432a4b6 Translated using Weblate (Chinese (Traditional))
Currently translated at 88.4% (100 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/zh_Hant/
2024-01-02 18:01:29 +00:00
Louis Lam
7795bcab03 Translated using Weblate (Chinese (Simplified))
Currently translated at 87.6% (99 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/zh_Hans/
2024-01-02 18:01:29 +00:00
Salvatore Cahyo
3ad7302e03 Translated using Weblate (Indonesian)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/id/
2024-01-02 18:01:29 +00:00
Abner Santana
3140947174 Translated using Weblate (Portuguese)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/pt/
2024-01-02 18:01:29 +00:00
Cyril59310
2c7b938f69 Translated using Weblate (French)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/fr/
2024-01-02 18:01:29 +00:00
Raphael
d3a595b02f Translated using Weblate (German)
Currently translated at 89.3% (101 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/de/
2024-01-02 18:01:29 +00:00
Louis Lam
4b93794cda Translated using Weblate (English)
Currently translated at 100.0% (113 of 113 strings)

Translation: Dockge/dockge
Translate-URL: https://weblate.kuma.pet/projects/dockge/dockge/en/
2024-01-02 18:01:29 +00:00
Louis Lam
8b8a9d0f1f Also apply the final release to the beta tag and the nightly tag 2024-01-03 02:01:17 +08:00
dng-nguyn
d4546e1a85 Added Vietnamese language (#332) 2024-01-02 03:45:37 +08:00
Louis Lam
b8cff4cc51 Fix the hostname for the current agent 2023-12-30 19:31:23 +08:00
Louis Lam
cc02eee50c [README.md] Add links to badges 2023-12-27 18:23:27 +08:00
82 changed files with 13566 additions and 6189 deletions

View File

@@ -14,50 +14,31 @@ jobs:
ci:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest, ARM64]
node: [18.17.1] # Can be changed
os: [ubuntu-latest, windows-latest, macos-latest, ARM, ARM64]
node: [22] # Can be changed
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Code
run: | # Mainly for Windows
git config --global core.autocrlf false
git config --global core.eol lf
uses: actions/checkout@v4
- run: git config --global core.autocrlf false # Mainly for Windows
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{matrix.node}}
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
run: npm install
- name: Lint
run: pnpm run lint
run: npm run lint
- name: Check Typescript
run: pnpm run check-ts
run: npm run check-ts
- name: Build
run: pnpm run build:frontend
run: npm run build:frontend
# more things can be add later like tests etc..

View File

@@ -16,27 +16,5 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Close Incorrect Issue
run: node extra/close-incorrect-issue.js ${{ secrets.GITHUB_TOKEN }} ${{ github.event.issue.number }} ${{ github.event.issue.user.login }}

View File

@@ -17,11 +17,11 @@ jobs:
json-yaml-validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: json-yaml-validate
id: json-yaml-validate
uses: GrantBirki/json-yaml-validate@v1.3.0
uses: GrantBirki/json-yaml-validate@v2.6.1
with:
comment: "false" # enable comment mode
exclude_file: ".github/config/exclude.txt" # gitignore style file for exclusions

52
.github/workflows/nightly-release.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Nightly Release
on:
schedule:
# Runs at 2:00 AM UTC every day
- cron: "0 2 * * *"
workflow_dispatch: # Allow manual trigger
permissions: {}
jobs:
release-nightly:
runs-on: ubuntu-latest
timeout-minutes: 120
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Use Node.js 22
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: npm clean-install --no-fund
- name: Run release-nightly
run: npm run release-nightly

View File

@@ -58,8 +58,7 @@ I personally do not like something that requires so many configurations before y
## Tools
- [`Node.js`](https://nodejs.org/) >= 20
- [`pnpm`](https://pnpm.io/)
- [`Node.js`](https://nodejs.org/) >= 22.14.0
- [`git`](https://git-scm.com/)
- IDE that supports [`ESLint`](https://eslint.org/) and EditorConfig (I am using [`IntelliJ IDEA`](https://www.jetbrains.com/idea/))
- A SQLite GUI tool (f.ex. [`SQLite Expert Personal`](https://www.sqliteexpert.com/download.html) or [`DBeaver Community`](https://dbeaver.io/download/))
@@ -67,14 +66,14 @@ I personally do not like something that requires so many configurations before y
## Install Dependencies for Development
```bash
pnpm install
npm install
```
## Dev Server
```
pnpm run dev:frontend
pnpm run dev:backend
npm run dev:frontend
npm run dev:backend
```
## Backend Dev Server
@@ -94,7 +93,7 @@ You can use Vue.js devtools Chrome extension for debugging.
### Build the frontend
```bash
pnpm run build
npm run build
```
## Database Migration
@@ -117,7 +116,7 @@ Both frontend and backend share the same package.json. However, the frontend dep
Should only be done by the maintainer.
```bash
pnpm update
npm update
````
It should update the patch release version only.

View File

@@ -6,7 +6,7 @@
A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager.
![GitHub Repo stars](https://img.shields.io/github/stars/louislam/dockge?logo=github) ![Docker Pulls](https://img.shields.io/docker/pulls/louislam/dockge?logo=docker) ![Docker Image Version (latest semver)](https://img.shields.io/docker/v/louislam/dockge/latest?label=docker%20image%20ver.) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/louislam/dockge/master?logo=github)
[![GitHub Repo stars](https://img.shields.io/github/stars/louislam/dockge?logo=github&style=flat)](https://github.com/louislam/dockge) [![Docker Pulls](https://img.shields.io/docker/pulls/louislam/dockge?logo=docker)](https://hub.docker.com/r/louislam/dockge/tags) [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/louislam/dockge/latest?label=docker%20image%20ver.)](https://hub.docker.com/r/louislam/dockge/tags) [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/louislam/dockge/master?logo=github)](https://github.com/louislam/dockge/commits/master/)
<img src="https://github.com/louislam/dockge/assets/1336778/26a583e1-ecb1-4a8d-aedf-76157d714ad7" width="900" alt="" />
@@ -81,9 +81,52 @@ curl "https://dockge.kuma.pet/compose.yaml?port=5001&stacksPath=/opt/stacks" --o
- port=`5001`
- stacksPath=`/opt/stacks`
Also, once compose is generated/downloaded, add the `PUID` and `PGID` section below to your compose `environment:` section to set stack ownership, otherwise default is `root`
```
# Both PUID and PGID must be set for it to do anything
- PUID=1000 # Set the stack file/dir ownership to this user
- PGID=1000 # Set the stack file/dir ownership to this group
```
Interactive compose.yaml generator is available on:
https://dockge.kuma.pet
### -OR-
Copy and paste your compose from the following:
If you want to store your stacks in another directory, you can change the `DOCKGE_STACKS_DIR` environment variable and volumes.
compose:
```
services:
dockge:
image: louislam/dockge:1
restart: unless-stopped
ports:
# Host Port:Container Port
- 5001:5001
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/app/data
# If you want to use private registries, you need to share the auth file with Dockge:
# - /root/.docker/:/root/.docker
# Stacks Directory
# Your stacks directory in the host (The paths inside container must be the same as the host)
# ⚠️ If you did it wrong, your data could end up be written into a wrong path.
# ✔️ CORRECT EXAMPLE: - /my-stacks:/my-stacks (Both paths match)
# ❌ WRONG EXAMPLE: - /docker:/my-stacks (Both paths do not match)
- /opt/stacks:/opt/stacks
environment:
# Tell Dockge where your stacks directory is
- DOCKGE_STACKS_DIR=/opt/stacks
# Both PUID and PGID must be set for it to do anything
- PUID=1000 # Set the stack file/dir ownership to this user
- PGID=1000 # Set the stack file/dir ownership to this group
```
## How to Update
```bash
@@ -106,7 +149,7 @@ docker compose pull && docker compose up -d
## Motivations
- I have been using Portainer for some time, but for the stack management, I am sometimes not satisfied with it. For example, sometimes when I try to deploy a stack, the loading icon keeps spinning for a few minutes without progress. And sometimes error messages are not clear.
- Try to develop with ES Module + TypeScript (Originally, I planned to use Deno or Bun.js, but they don't have support for arm64, so I stepped back to Node.js)
- Try to develop with ES Module + TypeScript
If you love this project, please consider giving it a ⭐.

View File

@@ -76,12 +76,14 @@ export class AgentManager {
* @param url
* @param username
* @param password
* @param name
*/
async add(url : string, username : string, password : string) : Promise<Agent> {
async add(url: string, username: string, password: string, name: string): Promise<Agent> {
let bean = R.dispense("agent") as Agent;
bean.url = url;
bean.username = username;
bean.password = password;
bean.name = name;
await R.store(bean);
return bean;
}
@@ -98,12 +100,31 @@ export class AgentManager {
if (bean) {
await R.trash(bean);
let endpoint = bean.endpoint;
this.disconnect(endpoint);
this.sendAgentList();
delete this.agentSocketList[endpoint];
} else {
throw new Error("Agent not found");
}
}
/**
*
* @param url
* @param updatedName
*/
async update(url: string, updatedName: string) {
const agent = await R.findOne("agent", " url = ? ", [
url,
]);
if (agent) {
agent.name = updatedName;
await R.store(agent);
} else {
throw new Error("Agent not found");
}
}
connect(url : string, username : string, password : string) {
let obj = new URL(url);
let endpoint = obj.host;
@@ -276,6 +297,8 @@ export class AgentManager {
url: "",
username: "",
endpoint: "",
name: "",
updatedName: "",
};
for (let endpoint in list) {

View File

@@ -17,6 +17,7 @@ export class DockerSocketHandler extends AgentSocketHandler {
callbackResult({
ok: true,
msg: "Deployed",
msgi18n: true,
}, callback);
stack.joinCombinedTerminal(socket);
} catch (e) {
@@ -30,7 +31,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
await this.saveStack(server, name, composeYAML, composeENV, isAdd);
callbackResult({
ok: true,
"msg": "Saved"
msg: "Saved",
msgi18n: true,
}, callback);
server.sendStackList();
} catch (e) {
@@ -56,7 +58,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
server.sendStackList();
callbackResult({
ok: true,
msg: "Deleted"
msg: "Deleted",
msgi18n: true,
}, callback);
} catch (e) {
@@ -94,7 +97,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
server.sendStackList();
callbackResult({
ok: true,
msg: "Updated"
msg: "Updated",
msgi18n: true,
}, callback);
} catch (e) {
callbackError(e, callback);
@@ -114,7 +118,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
await stack.start(socket);
callbackResult({
ok: true,
msg: "Started"
msg: "Started",
msgi18n: true,
}, callback);
server.sendStackList();
@@ -138,9 +143,12 @@ export class DockerSocketHandler extends AgentSocketHandler {
await stack.stop(socket);
callbackResult({
ok: true,
msg: "Stopped"
msg: "Stopped",
msgi18n: true,
}, callback);
server.sendStackList();
stack.leaveCombinedTerminal(socket);
} catch (e) {
callbackError(e, callback);
}
@@ -159,7 +167,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
await stack.restart(socket);
callbackResult({
ok: true,
msg: "Restarted"
msg: "Restarted",
msgi18n: true,
}, callback);
server.sendStackList();
} catch (e) {
@@ -180,7 +189,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
await stack.update(socket);
callbackResult({
ok: true,
msg: "Updated"
msg: "Updated",
msgi18n: true,
}, callback);
server.sendStackList();
} catch (e) {
@@ -201,7 +211,8 @@ export class DockerSocketHandler extends AgentSocketHandler {
await stack.down(socket);
callbackResult({
ok: true,
msg: "Downed"
msg: "Downed",
msgi18n: true,
}, callback);
server.sendStackList();
} catch (e) {
@@ -229,6 +240,84 @@ export class DockerSocketHandler extends AgentSocketHandler {
}
});
// Docker stats
agentSocket.on("dockerStats", async (callback) => {
try {
checkLogin(socket);
const dockerStats = Object.fromEntries(await server.getDockerStats());
callbackResult({
ok: true,
dockerStats,
}, callback);
server.sendStackList();
} catch (e) {
callbackError(e, callback);
}
});
// Start a service
agentSocket.on("startService", async (stackName: unknown, serviceName: unknown, callback) => {
try {
checkLogin(socket);
if (typeof (stackName) !== "string" || typeof (serviceName) !== "string") {
throw new ValidationError("Stack name and service name must be strings");
}
const stack = await Stack.getStack(server, stackName);
await stack.startService(socket, serviceName);
stack.joinCombinedTerminal(socket); // Ensure the combined terminal is joined
callbackResult({
ok: true,
msg: "Service " + serviceName + " started"
}, callback);
server.sendStackList();
} catch (e) {
callbackError(e, callback);
}
});
// Stop a service
agentSocket.on("stopService", async (stackName: unknown, serviceName: unknown, callback) => {
try {
checkLogin(socket);
if (typeof (stackName) !== "string" || typeof (serviceName) !== "string") {
throw new ValidationError("Stack name and service name must be strings");
}
const stack = await Stack.getStack(server, stackName);
await stack.stopService(socket, serviceName);
callbackResult({
ok: true,
msg: "Service " + serviceName + " stopped"
}, callback);
server.sendStackList();
} catch (e) {
callbackError(e, callback);
}
});
agentSocket.on("restartService", async (stackName: unknown, serviceName: unknown, callback) => {
try {
checkLogin(socket);
if (typeof stackName !== "string" || typeof serviceName !== "string") {
throw new Error("Invalid stackName or serviceName");
}
const stack = await Stack.getStack(server, stackName, true);
await stack.restartService(socket, serviceName);
callbackResult({
ok: true,
msg: "Service " + serviceName + " restarted"
}, callback);
} catch (e) {
callbackError(e, callback);
}
});
// getExternalNetworkList
agentSocket.on("getDockerNetworkList", async (callback) => {
try {

View File

@@ -38,6 +38,11 @@ export class TerminalSocketHandler extends AgentSocketHandler {
try {
checkLogin(socket);
// Throw an error if console is not enabled
if (!server.config.enableConsole) {
throw new ValidationError("Console is not enabled.");
}
// TODO: Reset the name here, force one main terminal for now
terminalName = "console";
@@ -66,6 +71,18 @@ export class TerminalSocketHandler extends AgentSocketHandler {
}
});
// Check if MainTerminal is enabled
agentSocket.on("checkMainTerminal", async (callback) => {
try {
checkLogin(socket);
callbackResult({
ok: server.config.enableConsole,
}, callback);
} catch (e) {
callbackError(e, callback);
}
});
// Interactive Terminal for containers
agentSocket.on("interactiveTerminal", async (stackName : unknown, serviceName : unknown, shell : unknown, callback) => {
try {

View File

@@ -136,6 +136,11 @@ export class DockgeServer {
stacksDir: {
type: String,
optional: true,
},
enableConsole: {
type: Boolean,
optional: true,
defaultValue: false,
}
});
@@ -149,6 +154,7 @@ export class DockgeServer {
this.config.hostname = args.hostname || process.env.DOCKGE_HOSTNAME || undefined;
this.config.dataDir = args.dataDir || process.env.DOCKGE_DATA_DIR || "./data/";
this.config.stacksDir = args.stacksDir || process.env.DOCKGE_STACKS_DIR || defaultStacksDir;
this.config.enableConsole = args.enableConsole || process.env.DOCKGE_ENABLE_CONSOLE === "true" || false;
this.stacksDir = this.config.stacksDir;
log.debug("server", this.config);
@@ -631,6 +637,35 @@ export class DockgeServer {
return list;
}
async getDockerStats() : Promise<Map<string, object>> {
let stats = new Map<string, object>();
try {
let res = await childProcessAsync.spawn("docker", [ "stats", "--format", "json", "--no-stream" ], {
encoding: "utf-8",
});
if (!res.stdout) {
return stats;
}
let lines = res.stdout?.toString().split("\n");
for (let line of lines) {
try {
let obj = JSON.parse(line);
stats.set(obj.Name, obj);
} catch (e) {
}
}
return stats;
} catch (e) {
log.error("getDockerStats", e);
return stats;
}
}
get stackDirFullPath() {
return path.resolve(this.stacksDir);
}

View File

@@ -7,6 +7,7 @@ export async function up(knex: Knex): Promise<void> {
table.string("url", 255).notNullable().unique();
table.string("username", 255).notNullable();
table.string("password", 255).notNullable();
table.string("name", 255);
table.boolean("active").notNullable().defaultTo(true);
});
}

View File

@@ -23,6 +23,7 @@ export class Agent extends BeanModel {
url: this.url,
username: this.username,
endpoint: this.endpoint,
name: this.name,
};
}

View File

@@ -18,6 +18,8 @@ import {
import { passwordStrength } from "check-password-strength";
import jwt from "jsonwebtoken";
import { Settings } from "../settings";
import fs, { promises as fsAsync } from "fs";
import path from "path";
export class MainSocketHandler extends SocketHandler {
create(socket : DockgeSocket, server : DockgeServer) {
@@ -242,6 +244,12 @@ export class MainSocketHandler extends SocketHandler {
checkLogin(socket);
const data = await Settings.getSettings("general");
if (fs.existsSync(path.join(server.stacksDir, "global.env"))) {
data.globalENV = fs.readFileSync(path.join(server.stacksDir, "global.env"), "utf-8");
} else {
data.globalENV = "# VARIABLE=value #comment";
}
callback({
ok: true,
data: data,
@@ -270,6 +278,16 @@ export class MainSocketHandler extends SocketHandler {
if (!currentDisabledAuth && data.disableAuth) {
await doubleCheckPassword(socket, currentPassword);
}
// Handle global.env
if (data.globalENV && data.globalENV != "# VARIABLE=value #comment") {
await fsAsync.writeFile(path.join(server.stacksDir, "global.env"), data.globalENV);
} else {
await fsAsync.rm(path.join(server.stacksDir, "global.env"), {
recursive: true,
force: true
});
}
delete data.globalENV;
await Settings.setSettings("general", data);
@@ -311,7 +329,12 @@ export class MainSocketHandler extends SocketHandler {
throw new ValidationError("dockerRunCommand must be a string");
}
const composeTemplate = composerize(dockerRunCommand);
// Option: 'latest' | 'v2x' | 'v3x'
let composeTemplate = composerize(dockerRunCommand, "", "latest");
// Remove the first line "name: <your project name>"
composeTemplate = composeTemplate.split("\n").slice(1).join("\n");
callback({
ok: true,
composeTemplate,

View File

@@ -20,7 +20,7 @@ export class ManageAgentSocketHandler extends SocketHandler {
let data = requestData as LooseObject;
let manager = socket.instanceManager;
await manager.test(data.url, data.username, data.password);
await manager.add(data.url, data.username, data.password);
await manager.add(data.url, data.username, data.password, data.name);
// connect to the agent
manager.connect(data.url, data.username, data.password);
@@ -66,5 +66,27 @@ export class ManageAgentSocketHandler extends SocketHandler {
callbackError(e, callback);
}
});
// updateAgent
socket.on("updateAgent", async (name : string, updatedName : string, callback : unknown) => {
try {
log.debug("manage-agent-socket-handler", "updateAgent");
checkLogin(socket);
let manager = socket.instanceManager;
await manager.update(name, updatedName);
server.disconnectAllSocketClients(undefined, socket.id);
manager.sendAgentList();
callbackResult({
ok: true,
msg: "agentUpdatedSuccessfully",
msgi18n: true,
}, callback);
} catch (e) {
callbackError(e, callback);
}
});
}
}

View File

@@ -93,7 +93,7 @@ export class Stack {
* Get the status of the stack from `docker compose ps --format json`
*/
async ps() : Promise<object> {
let res = await childProcessAsync.spawn("docker", [ "compose", "ps", "--format", "json" ], {
let res = await childProcessAsync.spawn("docker", this.getComposeOptions("ps", "--format", "json"), {
cwd: this.path,
encoding: "utf-8",
});
@@ -195,20 +195,18 @@ export class Stack {
}
// Write or overwrite the compose.yaml
await fsAsync.writeFile(path.join(dir, this._composeFileName), this.composeYAML);
const envPath = path.join(dir, ".env");
// Write or overwrite the .env
// If .env is not existing and the composeENV is empty, we don't need to write it
if (await fileExists(envPath) || this.composeENV.trim() !== "") {
await fsAsync.writeFile(envPath, this.composeENV);
fs.writeFileSync(path.join(dir, this._composeFileName), this.composeYAML);
if (process.env.PUID && process.env.PGID) {
const uid = Number(process.env.PUID);
const gid = Number(process.env.PGID);
fs.lchownSync(dir, uid, gid);
fs.chownSync(path.join(dir, this._composeFileName), uid, gid);
}
}
async deploy(socket : DockgeSocket) : Promise<number> {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "up", "-d", "--remove-orphans" ], this.path);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", this.getComposeOptions("up", "-d", "--remove-orphans"), this.path);
if (exitCode !== 0) {
throw new Error("Failed to deploy, please check the terminal output for more information.");
}
@@ -217,7 +215,7 @@ export class Stack {
async delete(socket: DockgeSocket) : Promise<number> {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down", "--remove-orphans" ], this.path);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", this.getComposeOptions("down", "--remove-orphans"), this.path);
if (exitCode !== 0) {
throw new Error("Failed to delete, please check the terminal output for more information.");
}
@@ -407,9 +405,22 @@ export class Stack {
return stack;
}
getComposeOptions(command : string, ...extraOptions : string[]) {
//--env-file ./../global.env --env-file .env
let options = [ "compose", command, ...extraOptions ];
if (fs.existsSync(path.join(this.server.stacksDir, "global.env"))) {
if (fs.existsSync(path.join(this.path, ".env"))) {
options.splice(1, 0, "--env-file", "./.env");
}
options.splice(1, 0, "--env-file", "../global.env");
}
console.log(options);
return options;
}
async start(socket: DockgeSocket) {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "up", "-d", "--remove-orphans" ], this.path);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", this.getComposeOptions("up", "-d", "--remove-orphans"), this.path);
if (exitCode !== 0) {
throw new Error("Failed to start, please check the terminal output for more information.");
}
@@ -418,7 +429,7 @@ export class Stack {
async stop(socket: DockgeSocket) : Promise<number> {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "stop" ], this.path);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", this.getComposeOptions("stop"), this.path);
if (exitCode !== 0) {
throw new Error("Failed to stop, please check the terminal output for more information.");
}
@@ -427,7 +438,7 @@ export class Stack {
async restart(socket: DockgeSocket) : Promise<number> {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "restart" ], this.path);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", this.getComposeOptions("restart"), this.path);
if (exitCode !== 0) {
throw new Error("Failed to restart, please check the terminal output for more information.");
}
@@ -436,7 +447,7 @@ export class Stack {
async down(socket: DockgeSocket) : Promise<number> {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "down" ], this.path);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", this.getComposeOptions("down"), this.path);
if (exitCode !== 0) {
throw new Error("Failed to down, please check the terminal output for more information.");
}
@@ -445,7 +456,7 @@ export class Stack {
async update(socket: DockgeSocket) {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "pull" ], this.path);
let exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", this.getComposeOptions("pull"), this.path);
if (exitCode !== 0) {
throw new Error("Failed to pull, please check the terminal output for more information.");
}
@@ -457,7 +468,7 @@ export class Stack {
return exitCode;
}
exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", [ "compose", "up", "-d", "--remove-orphans" ], this.path);
exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", this.getComposeOptions("up", "-d", "--remove-orphans"), this.path);
if (exitCode !== 0) {
throw new Error("Failed to restart, please check the terminal output for more information.");
}
@@ -466,7 +477,7 @@ export class Stack {
async joinCombinedTerminal(socket: DockgeSocket) {
const terminalName = getCombinedTerminalName(socket.endpoint, this.name);
const terminal = Terminal.getOrCreateTerminal(this.server, terminalName, "docker", [ "compose", "logs", "-f", "--tail", "100" ], this.path);
const terminal = Terminal.getOrCreateTerminal(this.server, terminalName, "docker", this.getComposeOptions("logs", "-f", "--tail", "100"), this.path);
terminal.enableKeepAlive = true;
terminal.rows = COMBINED_TERMINAL_ROWS;
terminal.cols = COMBINED_TERMINAL_COLS;
@@ -487,7 +498,7 @@ export class Stack {
let terminal = Terminal.getTerminal(terminalName);
if (!terminal) {
terminal = new InteractiveTerminal(this.server, terminalName, "docker", [ "compose", "exec", serviceName, shell ], this.path);
terminal = new InteractiveTerminal(this.server, terminalName, "docker", this.getComposeOptions("exec", serviceName, shell), this.path);
terminal.rows = TERMINAL_ROWS;
log.debug("joinContainerTerminal", "Terminal created");
}
@@ -497,10 +508,10 @@ export class Stack {
}
async getServiceStatusList() {
let statusList = new Map<string, number>();
let statusList = new Map<string, Array<object>>();
try {
let res = await childProcessAsync.spawn("docker", [ "compose", "ps", "--format", "json" ], {
let res = await childProcessAsync.spawn("docker", this.getComposeOptions("ps", "--format", "json"), {
cwd: this.path,
encoding: "utf-8",
});
@@ -511,13 +522,23 @@ export class Stack {
let lines = res.stdout?.toString().split("\n");
const addLine = (obj: { Service: string, State: string, Name: string, Health: string }) => {
if (!statusList.has(obj.Service)) {
statusList.set(obj.Service, []);
}
statusList.get(obj.Service)?.push({
status: obj.Health || obj.State,
name: obj.Name
});
};
for (let line of lines) {
try {
let obj = JSON.parse(line);
if (obj.Health === "") {
statusList.set(obj.Service, obj.State);
if (obj instanceof Array) {
obj.forEach(addLine);
} else {
statusList.set(obj.Service, obj.Health);
addLine(obj);
}
} catch (e) {
}
@@ -528,6 +549,35 @@ export class Stack {
log.error("getServiceStatusList", e);
return statusList;
}
}
async startService(socket: DockgeSocket, serviceName: string) {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
const exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", ["compose", "up", "-d", serviceName], this.path);
if (exitCode !== 0) {
throw new Error(`Failed to start service ${serviceName}, please check logs for more information.`);
}
return exitCode;
}
async stopService(socket: DockgeSocket, serviceName: string): Promise<number> {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
const exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", ["compose", "stop", serviceName], this.path);
if (exitCode !== 0) {
throw new Error(`Failed to stop service ${serviceName}, please check logs for more information.`);
}
return exitCode;
}
async restartService(socket: DockgeSocket, serviceName: string): Promise<number> {
const terminalName = getComposeTerminalName(socket.endpoint, this.name);
const exitCode = await Terminal.exec(this.server, socket, terminalName, "docker", ["compose", "restart", serviceName], this.path);
if (exitCode !== 0) {
throw new Error(`Failed to restart service ${serviceName}, please check logs for more information.`);
}
return exitCode;
}
}

View File

@@ -4,7 +4,6 @@ import * as pty from "@homebridge/node-pty-prebuilt-multiarch";
import { LimitQueue } from "./utils/limit-queue";
import { DockgeSocket } from "./util-server";
import {
allowedCommandList, allowedRawKeys,
PROGRESS_TERMINAL_ROWS,
TERMINAL_COLS,
TERMINAL_ROWS
@@ -16,7 +15,6 @@ import { log } from "./log";
* Terminal for running commands, no user interaction
*/
export class Terminal {
protected static terminalMap : Map<string, Terminal> = new Map();
protected _ptyProcess? : pty.IPty;
@@ -272,6 +270,11 @@ export class MainTerminal extends InteractiveTerminal {
constructor(server : DockgeServer, name : string) {
let shell;
// Throw an error if console is not enabled
if (!server.config.enableConsole) {
throw new Error("Console is not enabled.");
}
if (os.platform() === "win32") {
if (commandExistsSync("pwsh.exe")) {
shell = "pwsh.exe";
@@ -285,21 +288,6 @@ export class MainTerminal extends InteractiveTerminal {
}
public write(input : string) {
// For like Ctrl + C
if (allowedRawKeys.includes(input)) {
super.write(input);
return;
}
// Check if the command is allowed
const cmdParts = input.split(" ");
const executable = cmdParts[0].trim();
log.debug("console", "Executable: " + executable);
log.debug("console", "Executable length: " + executable.length);
if (!allowedCommandList.includes(executable)) {
throw new Error("Command not allowed.");
}
super.write(input);
}
}

View File

@@ -30,6 +30,7 @@ export interface Arguments {
hostname? : string;
dataDir? : string;
stacksDir? : string;
enableConsole? : boolean;
}
// Some config values are required

View File

@@ -107,17 +107,6 @@ export const COMBINED_TERMINAL_ROWS = 20;
export const ERROR_TYPE_VALIDATION = 1;
export const allowedCommandList : string[] = [
"docker",
"ls",
"cd",
"dir",
];
export const allowedRawKeys = [
"\u0003", // Ctrl + C
];
export const acceptedComposeFileNames = [
"compose.yaml",
"docker-compose.yaml",
@@ -236,42 +225,63 @@ export function copyYAMLComments(doc : Document, src : Document) {
/**
* Copy yaml comments from srcItems to items
* Typescript is super annoying here, so I have to use any here
* TODO: Since comments are belong to the array index, the comments will be lost if the order of the items is changed or removed or added.
* Attempts to preserve comments by matching content rather than just array indices
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function copyYAMLCommentsItems(items : any, srcItems : any) {
function copyYAMLCommentsItems(items: any, srcItems: any) {
if (!items || !srcItems) {
return;
}
// First pass - try to match items by their content
for (let i = 0; i < items.length; i++) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const item : any = items[i];
const item: any = items[i];
// Try to find matching source item by content
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const srcItem : any = srcItems[i];
const srcIndex = srcItems.findIndex((srcItem: any) =>
JSON.stringify(srcItem.value) === JSON.stringify(item.value) &&
JSON.stringify(srcItem.key) === JSON.stringify(item.key)
);
if (!srcItem) {
continue;
}
if (srcIndex !== -1) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const srcItem: any = srcItems[srcIndex];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const nextSrcItem: any = srcItems[srcIndex + 1];
if (item.key && srcItem.key) {
item.key.comment = srcItem.key.comment;
item.key.commentBefore = srcItem.key.commentBefore;
}
if (item.key && srcItem.key) {
item.key.comment = srcItem.key.comment;
item.key.commentBefore = srcItem.key.commentBefore;
}
if (srcItem.comment) {
item.comment = srcItem.comment;
}
if (srcItem.comment) {
item.comment = srcItem.comment;
}
if (item.value && srcItem.value) {
if (typeof item.value === "object" && typeof srcItem.value === "object") {
item.value.comment = srcItem.value.comment;
item.value.commentBefore = srcItem.value.commentBefore;
// Handle comments between array items
if (nextSrcItem && nextSrcItem.commentBefore) {
if (items[i + 1]) {
items[i + 1].commentBefore = nextSrcItem.commentBefore;
}
}
if (item.value.items && srcItem.value.items) {
copyYAMLCommentsItems(item.value.items, srcItem.value.items);
// Handle trailing comments after array items
if (srcItem.value && srcItem.value.comment) {
if (item.value) {
item.value.comment = srcItem.value.comment;
}
}
if (item.value && srcItem.value) {
if (typeof item.value === "object" && typeof srcItem.value === "object") {
item.value.comment = srcItem.value.comment;
item.value.commentBefore = srcItem.value.commentBefore;
if (item.value.items && srcItem.value.items) {
copyYAMLCommentsItems(item.value.items, srcItem.value.items);
}
}
}
}
@@ -289,6 +299,7 @@ function copyYAMLCommentsItems(items : any, srcItems : any) {
* - "8000-9000:80"
* - "127.0.0.1:8001:8001"
* - "127.0.0.1:5000-5010:5000-5010"
* - "0.0.0.0:8080->8080/tcp"
* - "6060:6060/udp"
* @param input
* @param hostname
@@ -298,9 +309,19 @@ export function parseDockerPort(input : string, hostname : string) {
let display;
const parts = input.split("/");
const part1 = parts[0];
let part1 = parts[0];
let protocol = parts[1] || "tcp";
// coming from docker ps, split host part
const arrow = part1.indexOf("->");
if (arrow >= 0) {
part1 = part1.split("->")[0];
const colon = part1.indexOf(":");
if (colon >= 0) {
part1 = part1.split(":")[1];
}
}
// Split the last ":"
const lastColon = part1.lastIndexOf(":");

View File

@@ -1,4 +1,3 @@
version: "3.8"
services:
dockge:
image: louislam/dockge:1

View File

@@ -1,7 +1,4 @@
# Due to the bug of #145, Node.js's version cannot be changed, unless upstream is fixed.
FROM node:18.17.1-bookworm-slim
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
FROM node:22-bookworm-slim
RUN apt update && apt install --yes --no-install-recommends \
curl \
ca-certificates \
@@ -20,5 +17,4 @@ RUN apt update && apt install --yes --no-install-recommends \
docker-ce-cli \
docker-compose-plugin \
&& rm -rf /var/lib/apt/lists/* \
&& npm install pnpm -g \
&& pnpm install -g tsx
&& npm install -g tsx

View File

@@ -9,8 +9,8 @@ FROM louislam/dockge:build-healthcheck AS build_healthcheck
FROM louislam/dockge:base AS build
WORKDIR /app
COPY --chown=node:node ./package.json ./package.json
COPY --chown=node:node ./pnpm-lock.yaml ./pnpm-lock.yaml
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
COPY --chown=node:node ./package-lock.json ./package-lock.json
RUN npm ci --omit=dev
############################################
# ⭐ Main Image
@@ -22,6 +22,13 @@ COPY --from=build /app/node_modules /app/node_modules
COPY --chown=node:node . .
RUN mkdir ./data
# It is just for safe, as by default, it is disabled in the latest Node.js now.
# Read more:
# - https://github.com/sagemathinc/cocalc/issues/6963
# - https://github.com/microsoft/node-pty/issues/630#issuecomment-1987212447
ENV UV_USE_IO_URING=0
VOLUME /app/data
EXPOSE 5001
HEALTHCHECK --interval=60s --timeout=30s --start-period=60s --retries=5 CMD extra/healthcheck
@@ -32,4 +39,4 @@ CMD ["tsx", "./backend/index.ts"]
# Mark as Nightly
############################################
FROM release AS nightly
RUN pnpm run mark-as-nightly
RUN npm run mark-as-nightly

View File

@@ -1,4 +1,3 @@
version: "3.8"
services:
mariadb:
image: mariadb:latest

View File

@@ -1,4 +1,3 @@
version: '3.8'
services:
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'

View File

@@ -1,4 +1,3 @@
version: '3.8'
services:
uptime-kuma:
image: louislam/uptime-kuma:1

View File

@@ -11,12 +11,17 @@ declare module 'vue' {
Appearance: typeof import('./src/components/settings/Appearance.vue')['default']
ArrayInput: typeof import('./src/components/ArrayInput.vue')['default']
ArraySelect: typeof import('./src/components/ArraySelect.vue')['default']
BButton: typeof import('bootstrap-vue-next')['BButton']
BDropdown: typeof import('bootstrap-vue-next')['BDropdown']
BDropdownItem: typeof import('bootstrap-vue-next')['BDropdownItem']
BFormGroup: typeof import('bootstrap-vue-next')['BFormGroup']
BFormInput: typeof import('bootstrap-vue-next')['BFormInput']
BModal: typeof import('bootstrap-vue-next')['BModal']
Confirm: typeof import('./src/components/Confirm.vue')['default']
Container: typeof import('./src/components/Container.vue')['default']
DockerStat: typeof import('./src/components/DockerStat.vue')['default']
General: typeof import('./src/components/settings/General.vue')['default']
GlobalEnv: typeof import('./src/components/settings/GlobalEnv.vue')['default']
HiddenInput: typeof import('./src/components/HiddenInput.vue')['default']
Login: typeof import('./src/components/Login.vue')['default']
NetworkInput: typeof import('./src/components/NetworkInput.vue')['default']
@@ -29,4 +34,7 @@ declare module 'vue' {
TwoFADialog: typeof import('./src/components/TwoFADialog.vue')['default']
Uptime: typeof import('./src/components/Uptime.vue')['default']
}
export interface ComponentCustomProperties {
vBModal: typeof import('bootstrap-vue-next')['vBModal']
}
}

View File

@@ -11,7 +11,7 @@
<button class="btn btn-normal btn-sm mt-3" @click="addField">{{ $t("addListItem", [ displayName ]) }}</button>
</div>
<div v-else>
Long syntax is not supported here. Please use the YAML editor.
{{ $t("LongSyntaxNotSupported") }}
</div>
</div>
</template>

View File

@@ -4,7 +4,7 @@
<ul v-if="isArrayInited" class="list-group">
<li v-for="(value, index) in array" :key="index" class="list-group-item">
<select v-model="array[index]" class="no-bg domain-input">
<option value="">Select a network...</option>
<option value="">{{ $t(`Select a network...`) }}</option>
<option v-for="option in options" :key="option" :value="option">{{ option }}</option>
</select>

View File

@@ -1,7 +1,7 @@
<template>
<div class="shadow-box big-padding mb-3 container">
<div class="row">
<div class="col-7">
<div class="col-5">
<h4>{{ name }}</h4>
<div class="image mb-2">
<span class="me-1">{{ imageName }}:</span><span class="tag">{{ imageTag }}</span>
@@ -9,17 +9,40 @@
<div v-if="!isEditMode">
<span class="badge me-1" :class="bgStyle">{{ status }}</span>
<a v-for="port in envsubstService.ports" :key="port" :href="parsePort(port).url" target="_blank">
<a v-for="port in (ports ?? envsubstService.ports)" :key="port" :href="parsePort(port).url" target="_blank">
<span class="badge me-1 bg-secondary">{{ parsePort(port).display }}</span>
</a>
</div>
</div>
<div class="col-5">
<div class="col-7">
<div class="function">
<router-link v-if="!isEditMode" class="btn btn-normal" :to="terminalRouteLink" disabled="">
<font-awesome-icon icon="terminal" />
Bash
</router-link>
<div class="btn-group me-2" role="group">
<router-link v-if="!isEditMode && (status === 'running' || status === 'healthy')" class="btn btn-normal" :to="terminalRouteLink" disabled="">
<font-awesome-icon icon="terminal" />
Bash
</router-link>
<button v-if="this.serviceCount > 1 && !isEditMode && status !== 'running' && status !== 'healthy'"
class="btn btn-primary"
:disabled="processing"
@click="startService">
<font-awesome-icon icon="play" class="me-1" />
{{ $t("startStack") }}
</button>
<button v-if="this.serviceCount > 1 && !isEditMode && (status === 'running' || status === 'healthy' || status === 'unhealthy')"
class="btn btn-normal"
:disabled="processing"
@click="restartService">
<font-awesome-icon icon="rotate" class="me-1" />
{{ $t("restartStack") }}
</button>
<button v-if="this.serviceCount > 1 && !isEditMode && (status === 'running' || status === 'healthy' || status === 'unhealthy')"
class="btn btn-normal"
:disabled="processing"
@click="stopService">
<font-awesome-icon icon="stop" class="me-1" />
{{ $t("stopStack") }}
</button>
</div>
</div>
</div>
</div>
@@ -35,6 +58,32 @@
{{ $t("deleteContainer") }}
</button>
</div>
<div v-else-if="statsInstances.length > 0" class="mt-2">
<div class="d-flex align-items-center gap-3">
<template v-if="!expandedStats">
<div class="stats">
{{ $t('CPU') }}: {{ statsInstances[0].CPUPerc }}
</div>
<div class="stats">
{{ $t('memoryAbbreviated') }}: {{ statsInstances[0].MemUsage }}
</div>
</template>
<div class="d-flex flex-grow-1 justify-content-end">
<button class="btn btn-sm btn-normal" @click="expandedStats = !expandedStats">
<font-awesome-icon :icon="expandedStats ? 'chevron-up' : 'chevron-down'" />
</button>
</div>
</div>
<transition name="slide-fade" appear>
<div v-if="expandedStats" class="d-flex flex-column gap-3 mt-2">
<DockerStat
v-for="stat in statsInstances"
:key="stat.Name"
:stat="stat"
/>
</div>
</transition>
</div>
<transition name="slide-fade" appear>
<div v-if="isEditMode && showConfig" class="config mt-3">
@@ -116,7 +165,7 @@
</label>
<div v-if="networkList.length === 0 && service.networks && service.networks.length > 0" class="text-warning mb-3">
No networks available. You need to add internal networks or enable external networks in the right side first.
{{ $t("NoNetworksAvailable") }}
</div>
<ArraySelect name="networks" :display-name="$t('network')" placeholder="Network Name" :options="networkList" />
@@ -127,7 +176,7 @@
<label class="form-label">
{{ $t("dependsOn") }}
</label>
<ArrayInput name="depends_on" :display-name="$t('dependsOn')" placeholder="Container Name" />
<ArrayInput name="depends_on" :display-name="$t('dependsOn')" :placeholder="$t(`containerName`)" />
</div>
</div>
</transition>
@@ -138,10 +187,12 @@
import { defineComponent } from "vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { parseDockerPort } from "../../../common/util-common";
import DockerStat from "./DockerStat.vue";
export default defineComponent({
components: {
FontAwesomeIcon,
DockerStat
},
props: {
name: {
@@ -156,16 +207,24 @@ export default defineComponent({
type: Boolean,
default: false,
},
status: {
type: String,
default: "N/A",
serviceStatus: {
type: Object,
default: null,
},
dockerStats: {
type: Object,
default: null
}
},
emits: [
"start-service",
"stop-service",
"restart-service"
],
data() {
return {
showConfig: false,
expandedStats: false,
};
},
computed: {
@@ -230,6 +289,10 @@ export default defineComponent({
return this.jsonObject.services[this.name];
},
serviceCount() {
return Object.keys(this.jsonObject.services).length;
},
jsonObject() {
return this.$parent.$parent.jsonConfig;
},
@@ -266,6 +329,22 @@ export default defineComponent({
return "";
}
},
statsInstances() {
if (!this.serviceStatus) {
return [];
}
return this.serviceStatus
.map(s => this.dockerStats[s.name])
.filter(s => !!s)
.sort((a, b) => a.Name.localeCompare(b.Name));
},
status() {
if (!this.serviceStatus) {
return "N/A";
}
return this.serviceStatus[0].status;
}
},
mounted() {
if (this.first) {
@@ -274,11 +353,26 @@ export default defineComponent({
},
methods: {
parsePort(port) {
return parseDockerPort(port, this.stack.primaryHostname);
if (this.stack.endpoint) {
return parseDockerPort(port, this.stack.primaryHostname);
} else {
let hostname = this.$root.info.primaryHostname || location.hostname;
return parseDockerPort(port, hostname);
}
},
remove() {
delete this.jsonObject.services[this.name];
},
startService() {
this.$emit("start-service", this.name);
},
stopService() {
this.$emit("stop-service", this.name);
},
restartService() {
this.$emit("restart-service", this.name);
}
}
});
</script>
@@ -303,5 +397,10 @@ export default defineComponent({
align-items: center;
justify-content: end;
}
.stats {
font-size: 0.8rem;
color: #6c757d;
}
}
</style>

View File

@@ -0,0 +1,94 @@
<template>
<div class="stats-container">
<div class="stats-title">
{{ stat.Name }}
</div>
<div class="d-flex justify-content-between stats gap-2 mt-1">
<div class="stat">
<div class="stat-label">
{{ $t('CPU') }}
</div>
<div>
{{ stat.CPUPerc }}
</div>
</div>
<div class="stat">
<div class="stat-label">
{{ $t('memory') }}
</div>
<div>
{{ stat.MemUsage }} ({{ stat.MemPerc }})
</div>
</div>
<div class="stat">
<div class="stat-label">
{{ $t('networkIO') }}
</div>
<div>
{{ stat.NetIO }}
</div>
</div>
<div class="stat">
<div class="stat-label">
{{ $t('blockIO') }}
</div>
<div>
{{ stat.BlockIO }}
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
stat: {
type: Object,
required: true
}
},
};
</script>
<style lang="scss" scoped>
.stats-container {
container-type: inline-size;
.stats {
container-type: inline-size;
.stat {
display: flex;
flex-direction: column;
gap: 4px;
}
@container (width < 420px) {
flex-direction: column;
.stat {
flex-direction: row;
}
.stat-label::after {
content: ':'
}
}
}
}
.stats {
font-size: 0.8rem;
color: #6c757d;
}
.stat-label {
font-weight: bold;
}
.stats-title {
font-size: 0.9rem;
color: var(--bs-heading-color);
}
</style>

View File

@@ -3,7 +3,7 @@
<h5>{{ $t("Internal Networks") }}</h5>
<ul class="list-group">
<li v-for="(networkRow, index) in networkList" :key="index" class="list-group-item">
<input v-model="networkRow.key" type="text" class="no-bg domain-input" placeholder="Network name..." />
<input v-model="networkRow.key" type="text" class="no-bg domain-input" :placeholder="$t(`Network name...`)" />
<font-awesome-icon icon="times" class="action remove ms-2 me-3 text-danger" @click="remove(index)" />
</li>
</ul>

View File

@@ -3,7 +3,8 @@
<div class="list-header">
<div class="header-top">
<!-- TODO -->
<button v-if="false" class="btn btn-outline-normal ms-2" :class="{ 'active': selectMode }" type="button" @click="selectMode = !selectMode">
<button v-if="false" class="btn btn-outline-normal ms-2" :class="{ 'active': selectMode }" type="button"
@click="selectMode = !selectMode">
{{ $t("Select") }}
</button>
@@ -28,34 +29,36 @@
<!-- TODO: Selection Controls -->
<div v-if="selectMode && false" class="selection-controls px-2 pt-2">
<input
v-model="selectAll"
class="form-check-input select-input"
type="checkbox"
/>
<input v-model="selectAll" class="form-check-input select-input" type="checkbox" />
<button class="btn-outline-normal" @click="pauseDialog"><font-awesome-icon icon="pause" size="sm" /> {{ $t("Pause") }}</button>
<button class="btn-outline-normal" @click="resumeSelected"><font-awesome-icon icon="play" size="sm" /> {{ $t("Resume") }}</button>
<button class="btn-outline-normal" @click="pauseDialog"><font-awesome-icon icon="pause" size="sm" /> {{
$t("Pause") }}</button>
<button class="btn-outline-normal" @click="resumeSelected"><font-awesome-icon icon="play" size="sm" />
{{ $t("Resume") }}</button>
<span v-if="selectedStackCount > 0">
{{ $t("selectedStackCount", [ selectedStackCount ]) }}
{{ $t("selectedStackCount", [selectedStackCount]) }}
</span>
</div>
</div>
<div ref="stackList" class="stack-list" :class="{ scrollbar: scrollbar }" :style="stackListStyle">
<div v-if="Object.keys(sortedStackList).length === 0" class="text-center mt-3">
<div v-if="agentStackList[0] && agentStackList[0].stacks.length === 0" class="text-center mt-3">
<router-link to="/compose">{{ $t("addFirstStackMsg") }}</router-link>
</div>
<StackListItem
v-for="(item, index) in sortedStackList"
:key="index"
:stack="item"
:isSelectMode="selectMode"
:isSelected="isSelected"
:select="select"
:deselect="deselect"
/>
<div class="stack-list-inner" v-for="(agent, index) in agentStackList" :key="index">
<div v-if="$root.agentCount > 1" class="p-2 agent-select"
@click="closedAgents.set(agent.endpoint, !closedAgents.get(agent.endpoint))">
<span class="me-1">
<font-awesome-icon v-show="closedAgents.get(agent.endpoint)" icon="chevron-circle-right" />
<font-awesome-icon v-show="!closedAgents.get(agent.endpoint)" icon="chevron-circle-down" />
</span>
<span v-if="agent.endpoint === 'current'">{{ $t("currentEndpoint") }}</span>
<span v-else>{{ agent.endpoint }}</span>
</div>
<StackListItem v-show="$root.agentCount === 1 || !closedAgents.get(agent.endpoint)"
v-for="(item, index) in agent.stacks" :key="index" :stack="item" :isSelectMode="selectMode"
:isSelected="isSelected" :select="select" :deselect="deselect" />
</div>
</div>
</div>
@@ -92,7 +95,8 @@ export default {
status: null,
active: null,
tags: null,
}
},
closedAgents: new Map(),
};
},
computed: {
@@ -119,7 +123,7 @@ export default {
* Returns a sorted list of stacks based on the applied filters and search text.
* @returns {Array} The sorted list of stacks.
*/
sortedStackList() {
agentStackList() {
let result = Object.values(this.$root.completeStackList);
result = result.filter(stack => {
@@ -187,6 +191,29 @@ export default {
return m1.name.localeCompare(m2.name);
});
// Group stacks by endpoint, sorting them so the local endpoint is first
// and the rest are sorted alphabetically
result = [
...result.reduce((acc, stack) => {
const endpoint = stack.endpoint || 'current';
if (!acc.has(endpoint)) {
acc.set(endpoint, []);
}
acc.get(endpoint).push(stack);
return acc;
}, new Map()).entries()
].map(([endpoint, stacks]) => ({
endpoint,
stacks
})).sort((a, b) => {
if (a.endpoint === 'current' && b.endpoint !== 'current') {
return -1;
} else if (a.endpoint !== 'current' && b.endpoint === 'current') {
return 1;
}
return a.endpoint.localeCompare(b.endpoint);
});
return result;
},
@@ -221,7 +248,7 @@ export default {
},
watch: {
searchText() {
for (let stack of this.sortedStackList) {
for (let stack of this.agentStackList) {
if (!this.selectedStacks[stack.id]) {
if (this.selectAll) {
this.disableSelectAllWatcher = true;
@@ -236,7 +263,7 @@ export default {
this.selectedStacks = {};
if (this.selectAll) {
this.sortedStackList.forEach((item) => {
this.agentStackList.forEach((item) => {
this.selectedStacks[item.id] = true;
});
}
@@ -331,7 +358,7 @@ export default {
pauseSelected() {
Object.keys(this.selectedStacks)
.filter(id => this.$root.stackList[id].active)
.forEach(id => this.$root.getSocket().emit("pauseStack", id, () => {}));
.forEach(id => this.$root.getSocket().emit("pauseStack", id, () => { }));
this.cancelSelectMode();
},
@@ -342,7 +369,7 @@ export default {
resumeSelected() {
Object.keys(this.selectedStacks)
.filter(id => !this.$root.stackList[id].active)
.forEach(id => this.$root.getSocket().emit("resumeStack", id, () => {}));
.forEach(id => this.$root.getSocket().emit("resumeStack", id, () => { }));
this.cancelSelectMode();
},
@@ -444,4 +471,15 @@ export default {
gap: 10px;
}
.agent-select {
cursor: pointer;
font-size: 14px;
font-weight: 500;
color: $dark-font-color3;
padding-left: 10px;
padding-right: 10px;
display: flex;
align-items: center;
user-select: none;
}
</style>

View File

@@ -3,7 +3,6 @@
<Uptime :stack="stack" :fixed-width="true" class="me-2" />
<div class="title">
<span>{{ stackName }}</span>
<div v-if="$root.agentCount > 1" class="endpoint">{{ endpointDisplay }}</div>
</div>
</router-link>
</template>

View File

@@ -101,6 +101,14 @@ export default {
this.terminal.open(this.$refs.terminal);
this.terminal.focus();
// Add right-click context menu handler for paste
this.$refs.terminal.addEventListener('contextmenu', this.handleContextMenu);
// Add selection handler for copy to clipboard
this.terminal.onSelectionChange(() => {
this.handleSelection();
});
// Notify parent component when data is received
this.terminal.onCursorMove(() => {
console.debug("onData triggered");
@@ -135,6 +143,7 @@ export default {
window.removeEventListener("resize", this.onResizeEvent); // Remove the resize event listener from the window object.
this.$root.unbindTerminal(this.name);
this.terminal.dispose();
this.$refs.terminal?.removeEventListener('contextmenu', this.handleContextMenu);
},
methods: {
@@ -154,17 +163,27 @@ export default {
},
removeInput() {
const textAfterCursorLength = this.terminalInputBuffer.length - this.cursorPosition;
const spaces = " ".repeat(textAfterCursorLength);
const backspaceCount = this.terminalInputBuffer.length;
const backspaces = "\b \b".repeat(backspaceCount);
this.cursorPosition = 0;
this.terminal.write(backspaces);
this.terminal.write(spaces + backspaces);
this.terminalInputBuffer = "";
},
clearCurrentLine() {
// Move cursor to the beginning of the input and clear it
const backspaces = "\b".repeat(this.cursorPosition);
const spaces = " ".repeat(this.terminalInputBuffer.length);
const moreBackspaces = "\b".repeat(this.terminalInputBuffer.length);
this.terminal.write(backspaces + spaces + moreBackspaces);
},
mainTerminalConfig() {
this.terminal.onKey(e => {
const code = e.key.charCodeAt(0);
console.debug("Encode: " + JSON.stringify(e.key));
// Optional: keep for debugging
// console.debug("Encode: " + JSON.stringify(e.key));
if (e.key === "\r") {
// Return if no input
@@ -180,35 +199,65 @@ export default {
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, buffer + e.key, (err) => {
this.$root.toastError(err.msg);
});
} else if (code === 127) { // Backspace
} else if (e.key === "\u007F") { // Backspace
if (this.cursorPosition > 0) {
this.terminal.write("\b \b");
// Remove character to the left of cursor
const beforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition - 1);
const afterCursor = this.terminalInputBuffer.slice(this.cursorPosition);
this.terminalInputBuffer = beforeCursor + afterCursor;
this.cursorPosition--;
this.terminalInputBuffer = this.terminalInputBuffer.slice(0, -1);
// Redraw the line
this.terminal.write("\b" + afterCursor + " \b".repeat(afterCursor.length + 1));
}
} else if (e.key === "\u001B\u005B\u0033\u007E") { // Delete key
if (this.cursorPosition < this.terminalInputBuffer.length) {
// Remove character to the right of cursor
const beforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition);
const afterCursor = this.terminalInputBuffer.slice(this.cursorPosition + 1);
this.terminalInputBuffer = beforeCursor + afterCursor;
// Redraw the line from cursor position
this.terminal.write(afterCursor + " \b".repeat(afterCursor.length + 1));
}
} else if (e.key === "\u001B\u005B\u0041" || e.key === "\u001B\u005B\u0042") { // UP OR DOWN
// Do nothing
} else if (e.key === "\u001B\u005B\u0043") { // RIGHT
// TODO
if (this.cursorPosition < this.terminalInputBuffer.length) {
this.terminal.write(this.terminalInputBuffer[this.cursorPosition]);
this.cursorPosition++;
}
} else if (e.key === "\u001B\u005B\u0044") { // LEFT
// TODO
if (this.cursorPosition > 0) {
this.terminal.write("\b");
this.cursorPosition--;
}
} else if (e.key === "\u0003") { // Ctrl + C
console.debug("Ctrl + C");
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, e.key);
this.removeInput();
} else if (e.key === "\u0016" || (e.domEvent?.ctrlKey && e.key.toLowerCase() === "v")) { // Ctrl + V
this.handlePaste();
} else if (e.key === "\u0009" || e.key.startsWith("\u001B")) { // TAB or other special keys
// Do nothing
} else {
const textBeforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition);
const textAfterCursor = this.terminalInputBuffer.slice(this.cursorPosition);
this.terminalInputBuffer = textBeforeCursor + e.key + textAfterCursor;
this.terminal.write(e.key + textAfterCursor + "\b".repeat(textAfterCursor.length));
this.cursorPosition++;
this.terminalInputBuffer += e.key;
console.log(this.terminalInputBuffer);
this.terminal.write(e.key);
}
});
},
interactiveTerminalConfig() {
this.terminal.onKey(e => {
// Handle Ctrl+V for paste
if (e.key === "\u0016" || (e.domEvent?.ctrlKey && e.key.toLowerCase() === "v")) {
this.handlePaste();
return;
}
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, e.key, (res) => {
if (!res.ok) {
this.$root.toastRes(res);
@@ -239,7 +288,87 @@ export default {
let rows = this.terminal.rows;
let cols = this.terminal.cols;
this.$root.emitAgent(this.endpoint, "terminalResize", this.name, rows, cols);
}
},
/**
* Handle clipboard paste operation
*/
async handlePaste() {
try {
const text = await navigator.clipboard.readText();
if (text) {
this.pasteText(text);
}
} catch (error) {
console.error("Failed to read from clipboard:", error);
}
},
/**
* Paste text into the terminal based on current mode
*/
pasteText(text) {
if (this.mode === "mainTerminal") {
// For main terminal, insert text at current cursor position
const beforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition);
const afterCursor = this.terminalInputBuffer.slice(this.cursorPosition);
// Update the buffer with inserted text
this.terminalInputBuffer = beforeCursor + text + afterCursor;
// Clear the current line and rewrite it
this.clearCurrentLine();
this.terminal.write(this.terminalInputBuffer);
// Move cursor to the correct position (after the pasted text)
this.cursorPosition += text.length;
const backspaces = "\b".repeat(afterCursor.length);
this.terminal.write(backspaces);
} else if (this.mode === "interactive") {
// For interactive terminal, send directly to server
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, text, (res) => {
if (!res.ok) {
this.$root.toastRes(res);
}
});
}
},
/**
* Handle right-click context menu for paste operation
*/
handleContextMenu(event) {
// Prevent default context menu
event.preventDefault();
// Only handle paste for modes that support input
if (this.mode === "mainTerminal" || this.mode === "interactive") {
this.handlePaste();
}
},
/**
* Handle text selection in terminal - copy to clipboard
*/
handleSelection() {
const selectedText = this.terminal.getSelection();
if (selectedText && selectedText.length > 0) {
this.copyToClipboard(selectedText);
}
},
/**
* Copy text to clipboard
*/
async copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.debug("Text copied to clipboard:", text);
} catch (error) {
console.error("Failed to copy to clipboard:", error);
}
},
}
};
</script>
@@ -247,13 +376,12 @@ export default {
<style scoped lang="scss">
.main-terminal {
height: 100%;
overflow-x: scroll;
}
</style>
<style lang="scss">
.terminal {
padding: 10px 15px;
background-color: black !important;
height: 100%;
}
</style>

View File

@@ -14,7 +14,7 @@
</option>
</select>
</div>
<div v-show="false" class="my-4">
<div v-show="true" class="my-4">
<label for="timezone" class="form-label">{{ $t("Theme") }}</label>
<div>
<div

View File

@@ -47,7 +47,7 @@
<input
v-model="settings.primaryHostname"
class="form-control"
placeholder="(Unset: Follow current hostname)"
:placeholder="$t(`CurrentHostname`)"
/>
<button class="btn btn-outline-primary" type="button" @click="autoGetPrimaryHostname">
{{ $t("autoGet") }}

View File

@@ -0,0 +1,97 @@
<template>
<div>
<div v-if="settingsLoaded" class="my-4">
<form class="my-4" autocomplete="off" @submit.prevent="saveGeneral">
<div class="shadow-box mb-3 editor-box edit-mode">
<code-mirror
ref="editor"
v-model="settings.globalENV"
:extensions="extensionsEnv"
minimal
wrap="true"
dark="true"
tab="true"
:hasFocus="editorFocus"
@change="onChange"
/>
</div>
<div class="my-4">
<!-- Save Button -->
<div>
<button class="btn btn-primary" type="submit">
{{ $t("Save") }}
</button>
</div>
</div>
</form>
</div>
</div>
</template>
<script>
import CodeMirror from "vue-codemirror6";
import { python } from "@codemirror/lang-python"; // good enough for .env key=value highlighting
import { dracula as editorTheme } from "thememirror";
import { lineNumbers, EditorView } from "@codemirror/view";
import { ref } from "vue";
export default {
name: "GlobalEnv",
components: {
CodeMirror,
},
setup() {
const editorFocus = ref(false);
const focusEffectHandler = (state, focusing) => {
editorFocus.value = focusing;
return null;
};
const extensionsEnv = [
editorTheme,
python(),
lineNumbers(),
EditorView.focusChangeEffect.of(focusEffectHandler),
];
return { editorFocus, extensionsEnv };
},
computed: {
settings() {
return this.$parent.$parent.$parent.settings;
},
saveSettings() {
return this.$parent.$parent.$parent.saveSettings;
},
settingsLoaded() {
return this.$parent.$parent.$parent.settingsLoaded;
},
},
methods: {
/** Save the settings */
saveGeneral() {
this.saveSettings();
},
onChange() {
// hook for future live validation if desired
},
},
};
</script>
<style scoped lang="scss">
.editor-box {
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
&.edit-mode {
background-color: #2c2f38 !important;
}
}
</style>

View File

@@ -28,6 +28,13 @@ const languageList = {
"nl": "Nederlands",
"ro": "Română",
"id": "Bahasa Indonesia (Indonesian)",
"vi": "Tiếng Việt",
"hu": "Magyar",
"ca": "Català",
"ga": "Gaeilge",
"de-CH": "Schwiizerdütsch",
"mag": "मगही",
"mai": "मैथिली",
};
let messages = {

View File

@@ -38,6 +38,7 @@ import {
faAward,
faLink,
faChevronDown,
faChevronUp,
faSignOutAlt,
faPen,
faExternalLinkSquareAlt,
@@ -54,6 +55,8 @@ import {
faTerminal, faWarehouse, faHome, faRocket,
faRotate,
faCloudArrowDown, faArrowsRotate,
faChevronCircleRight,
faChevronCircleDown,
} from "@fortawesome/free-solid-svg-icons";
library.add(
@@ -88,6 +91,7 @@ library.add(
faAward,
faLink,
faChevronDown,
faChevronUp,
faSignOutAlt,
faPen,
faExternalLinkSquareAlt,
@@ -109,6 +113,8 @@ library.add(
faRotate,
faCloudArrowDown,
faArrowsRotate,
faChevronCircleRight,
faChevronCircleDown,
);
export { FontAwesomeIcon };

View File

@@ -35,7 +35,7 @@
"restartPolicyAlways": "دائماً",
"restartPolicyOnFailure": "عند الفشل",
"restartPolicyNo": "لا",
"environmentVariable": "متغير البيئة | متغيرات البيئة",
"environmentVariable": "متغير | متغيرات",
"restartPolicy": "سياسة إعادة التشغيل",
"containerName": "اسم الحاوية",
"port": "منفذ | منافذ",
@@ -98,5 +98,16 @@
"url": "رابط | روابط",
"extra": "إضافات",
"reverseProxyMsg1": "هل تستدخم خادم عكسي؟",
"connecting...": "جاري الاتصال بخادم المقبس…"
"connecting...": "جاري الاتصال بخادم المقبس…",
"newUpdate": "تحديث جديد",
"currentEndpoint": "السياق: الوكيل الحالي",
"dockgeURL": "رابط Dockge (مثلا http://127.0.0.1:5001)",
"agentOnline": "متصل",
"agentOffline": "غير متصل",
"connecting": "جاري الإتصال",
"connect": "ارتبط",
"dockgeAgent": "سيرفر Dockge",
"removeAgent": "حذف الوكيل",
"removeAgentMsg": "هل انت متأكد من حذف هذا الوكيل؟",
"LongSyntaxNotSupported": "كتابة النصوص المدعومة غير المدعومة هنا. الرجاء استخدام محرر YAML."
}

116
frontend/src/lang/be.json Normal file
View File

@@ -0,0 +1,116 @@
{
"active": "акт.",
"LongSyntaxNotSupported": "Доўгі сінтаксіс тут не падтрымліваецца. Выкарыстоўвайце рэдактар YAML.",
"removeAgentMsg": "Вы ўпэўнены, што хочаце выдаліць гэтага агента?",
"languageName": "Беларуская",
"Create your admin account": "Стварыце ўліковы запіс адміністратара",
"authIncorrectCreds": "Няправільны лагін ці пароль.",
"PasswordsDoNotMatch": "Паролі не супадаюць.",
"Repeat Password": "Паўтарыце пароль",
"Create": "Стварыць",
"signedInDisp": "Аўтарызаваны як {0}",
"signedInDispDisabled": "Аўтарызацыя выключана.",
"home": "Галоўная",
"console": "Кансоль",
"registry": "Рэестр (Registry)",
"compose": "Compose",
"addFirstStackMsg": "Стварыце свой першы стэк!",
"stackName": "Назва стэка",
"deployStack": "Разгарнуць",
"deleteStack": "Выдаліць",
"stopStack": "Спыніць",
"restartStack": "Перазапусціць",
"updateStack": "Абнавіць",
"startStack": "Запусціць",
"downStack": "Спыніць і дэактываваць",
"editStack": "Рэдагаваць",
"discardStack": "Скасаваць",
"saveStackDraft": "Захаваць",
"notAvailableShort": "Н/Д",
"deleteStackMsg": "Вы ўпэўнены, што хочаце выдаліць гэты стэк?",
"stackNotManagedByDockgeMsg": "Дадзены стэк не кіруецца Dockge.",
"primaryHostname": "Імя хоста",
"general": "Агульныя",
"container": "Кантэйнер | Кантэйнеры",
"scanFolder": "Сканаваць папку стэкаў",
"dockerImage": "Вобраз",
"restartPolicyUnlessStopped": "Пакуль не будзе спынены",
"restartPolicyAlways": "Заўсёды",
"restartPolicyOnFailure": "Пры падзенні",
"restartPolicyNo": "Ніколі",
"environmentVariable": "Зменная асяроддзя | Зменныя асяроддзя",
"restartPolicy": "Палітыка рэстарту",
"containerName": "Імя кантэйнера",
"port": "Порт | Порты",
"volume": "Сховішча | Сховішчы",
"network": "Сетка | Сеткі",
"dependsOn": "Залежнасць кантэйнера | Залежнасці кантэйнера",
"addListItem": "Дадаць {0}",
"deleteContainer": "Выдаліць",
"addContainer": "Дадаць кантэйнер",
"addNetwork": "Дадаць сетку",
"disableauth.message1": "Вы ўпэўнены, што хочаце <strong>адключыць аўтэнтыфікацыю</strong>?",
"Show update if available": "Паказаць абнаўленне, калі яно даступна",
"Also check beta release": "Атрымліваць бэта-версіі",
"disableauth.message2": "Гэта прызначана для сцэнарыяў, <strong>калі вы збіраецеся выкарыстоўваць староннюю аўтэнтыфікацыю</strong> перад Dockge, напрыклад, Cloudflare Access, Authelia або іншыя механізмы аўтэнтыфікацыі.",
"passwordNotMatchMsg": "Паўторны пароль не супадае.",
"autoGet": "Аўта",
"add": "Дадаць",
"Edit": "Змяніць",
"applyToYAML": "Ужыць да YAML",
"createExternalNetwork": "Стварыць",
"addInternalNetwork": "Дадаць",
"Save": "Захаваць",
"Language": "Мова",
"Current User": "Бягучы карыстальнік",
"Change Password": "Змяніць пароль",
"Current Password": "Бягучы пароль",
"New Password": "Новы пароль",
"Repeat New Password": "Паўтарыце новы пароль",
"Update Password": "Абнавіць пароль",
"Advanced": "Пашыраныя",
"Please use this option carefully!": "Выкарыстоўвайце гэтую опцыю асцярожна!",
"Enable Auth": "Уключыць аўтэнтыфікацыю",
"Disable Auth": "Адключыць аўтэнтыфікацыю",
"I understand, please disable": "Я разумею, адключыце",
"Leave": "Пакінуць",
"Frontend Version": "Версія знешняга інтэрфейсу",
"Check Update On GitHub": "Праверыць абнаўленні на GitHub",
"Remember me": "Запомніць мяне",
"Login": "Лагін",
"Username": "Імя карыстальніка",
"Password": "Пароль",
"Settings": "Налады",
"Logout": "Выйсці",
"Lowercase only": "Толькі ніжні рэгістр",
"Convert to Compose": "Пераўтварыць у Compose",
"Docker Run": "Docker Run",
"exited": "спын.",
"inactive": "неакт.",
"Appearance": "Знешні выгляд",
"Security": "Бяспека",
"About": "Аб праграме",
"Allowed commands:": "Дазволеныя каманды:",
"Internal Networks": "Унутраныя сеткі",
"External Networks": "Знешнія сеткі",
"No External Networks": "Няма знешніх сетак",
"reverseProxyMsg1": "Выкарыстоўваеце зваротны проксі?",
"reverseProxyMsg2": "Праверце, як наладзіць яго для WebSocket",
"Cannot connect to the socket server.": "Не ўдалося падключыцца да сокет-сервера.",
"reconnecting...": "Перападключэнне…",
"connecting...": "Падключэнне да сокет-сервера…",
"url": "URL-адрас | URL-адрасы",
"extra": "Дадаткова",
"newUpdate": "Даступна абнаўленне",
"dockgeAgent": "Агент Dockge | Агенты Dockge",
"currentEndpoint": "Бягучы",
"dockgeURL": "URL-адрас Dockge (напрыклад: http://127.0.0.1:5001)",
"agentOnline": "У сетцы",
"agentOffline": "Не ў сетцы",
"connecting": "Падключэнне",
"connect": "Падключыць",
"addAgent": "Дадаць Агента",
"agentAddedSuccessfully": "Агент паспяхова дададзены.",
"agentRemovedSuccessfully": "Агент паспяхова выдалены.",
"removeAgent": "Выдаліць агента"
}

View File

@@ -92,11 +92,41 @@
"External Networks": "Външни мрежи",
"No External Networks": "Не са налични външни мрежи",
"reverseProxyMsg2": "Проверете как да го конфигурирате за WebSocket",
"downStack": "Спри и изключи",
"downStack": "Спри & Неактивен",
"reverseProxyMsg1": "Използвате ревърс прокси?",
"Cannot connect to the socket server.": "Не може да се свърже със сокет сървъра.",
"url": "URL адрес | URL адреси",
"extra": "Допълнително",
"reconnecting...": "Повторно свързване…",
"connecting...": "Свързване със сокет сървъра…"
"connecting...": "Свързване със сокет сървъра…",
"newUpdate": "Нова актуализация",
"currentEndpoint": "Текущ",
"dockgeURL": "Dockge URL адрес (напр. http://127.0.0.1:5001)",
"agentOnline": "Онлайн",
"agentOffline": "Офлайн",
"connect": "Свържи",
"addAgent": "Добави агент",
"agentAddedSuccessfully": "Агентът е добавен успешно.",
"removeAgent": "Премахни агент",
"removeAgentMsg": "Сигурни ли сте, че желаете да премахнете този агент?",
"dockgeAgent": "Dockge агент | Dockge агенти",
"connecting": "Свързване",
"agentRemovedSuccessfully": "Агентът е премахнат успешно.",
"LongSyntaxNotSupported": "Дългият синтаксис не се поддържа тук. Моля, използвайте YAML редактора.",
"Started": "Стартиран",
"Updated": "Актуализиран",
"Deleted": "Изтрит",
"Deployed": "Внедрен",
"Stopped": "Спрян",
"Restarted": "Рестартиран",
"Switch to sh": "Превключи на \"sh\"",
"terminal": "Терминал",
"New Container Name...": "Ново име на контейнер...",
"Network name...": "Име на мрежата...",
"Select a network...": "Изберете мрежа...",
"Lost connection to the socket server. Reconnecting...": "Изгубена връзка със сокет сървъра. Повторно свързване...",
"Saved": "Запазено",
"Downed": "Свален",
"CurrentHostname": "(Не е зададено: Следвай текущото име на хост)",
"NoNetworksAvailable": "Няма налични мрежи. Първо трябва да добавите вътрешни мрежи или да активирате външни мрежи в дясната страна."
}

116
frontend/src/lang/ca.json Normal file
View File

@@ -0,0 +1,116 @@
{
"Create your admin account": "Crea el teu compte d'administrador",
"Repeat Password": "Repeteix la contrasenya",
"Create": "Crea",
"signedInDisp": "S'ha iniciat sessió com a {0}",
"home": "Inici",
"console": "Consola",
"registry": "Registre",
"compose": "Compondre",
"addFirstStackMsg": "Compondre la teva primera pila!",
"stackName": "Nom de la pila",
"deployStack": "Desplegar",
"deleteStack": "Eliminar",
"stopStack": "Aturar",
"restartStack": "Reiniciar",
"updateStack": "Actualitzar",
"startStack": "Inicia",
"downStack": "Atura i inactiva",
"languageName": "Català",
"authIncorrectCreds": "Usuari o contrasenya incorrecte.",
"PasswordsDoNotMatch": "Les contrasenyes no coincideixen.",
"signedInDispDisabled": "Autenticació deshabilitada.",
"discardStack": "Descartar",
"saveStackDraft": "Guardar",
"notAvailableShort": "N/D",
"primaryHostname": "Nom del host primari",
"general": "General",
"container": "Contenidor | Contenidors",
"scanFolder": "Escaneja la carpeta de piles",
"dockerImage": "Imatge",
"restartPolicyAlways": "Sempre",
"restartPolicyOnFailure": "En cas de fallada",
"restartPolicyNo": "No",
"environmentVariable": "Variable d'entorn | Variables d'entorn",
"restartPolicy": "Política de reinici",
"containerName": "Nom del contenidor",
"port": "Port | Ports",
"volume": "Volum | Volums",
"network": "Xarxa | Xarxes",
"addListItem": "Afegir {0}",
"deleteContainer": "Eliminar",
"addContainer": "Afegir contenidor",
"addNetwork": "Afegir xarxa",
"passwordNotMatchMsg": "La contrasenya repetida no coincideix.",
"autoGet": "Obtenir automàticament",
"add": "Afegir",
"Edit": "Editar",
"applyToYAML": "Aplicar a YAML",
"createExternalNetwork": "Crear",
"addInternalNetwork": "Afegir",
"Save": "Guardar",
"Language": "Idioma",
"Current User": "Usuari actual",
"Change Password": "Canviar la contrasenya",
"Current Password": "Contrasenya actual",
"New Password": "Nova contrasenya",
"stackNotManagedByDockgeMsg": "Aquesta pila no està gestionada per Dockge.",
"Update Password": "Actualitzar contrasenya",
"Advanced": "Avançat",
"Disable Auth": "Deshabilitar autenticació",
"Leave": "Sortir",
"Frontend Version": "Versió del frontend",
"Check Update On GitHub": "Comprova les actualitzacions a GitHub",
"Show update if available": "Mostra si hi ha disponible una nova actualització",
"Also check beta release": "Comprovar també la versió beta",
"Remember me": "Recorda'm",
"Login": "Inici de sesió",
"Username": "Usuari",
"Settings": "Configuració",
"Logout": "Tanca sessió",
"Lowercase only": "Només minúscules",
"Convert to Compose": "Convertir a Compose",
"Docker Run": "Executar Docker",
"active": "actiu",
"exited": "finalitzat",
"inactive": "inactiu",
"Appearance": "Aparença",
"Security": "Seguretat",
"About": "Sobre",
"Allowed commands:": "Comandes permeses:",
"Internal Networks": "Xarxes internes",
"External Networks": "Xarxes externes",
"No External Networks": "No hi ha xarxes externes",
"reverseProxyMsg1": "Estàs fent servir un proxy invers?",
"reverseProxyMsg2": "Comproveu com configurar-lo per a WebSocket",
"Cannot connect to the socket server.": "No es pot connectar al servidor del socket.",
"reconnecting...": "S'està tornant a connectar…",
"connecting...": "S'està connectant al servidor del socket…",
"url": "URL | URLs",
"extra": "Extra",
"newUpdate": "Nova actualització",
"dockgeAgent": "Agent Dockge | Agents Dockge",
"currentEndpoint": "Actual",
"dockgeURL": "URL de Dockge (ex. http://127.0.0.1:5001)",
"agentOnline": "En línia",
"agentOffline": "Fora de línia",
"connecting": "Connectant",
"connect": "Connectar",
"addAgent": "Afegir agent",
"agentAddedSuccessfully": "Agent afegit correctament.",
"agentRemovedSuccessfully": "Agent eliminat correctament.",
"removeAgent": "Eliminar agent",
"removeAgentMsg": "Esteu segur que voleu eliminar aquest agent?",
"editStack": "Editar",
"deleteStackMsg": "Estàs segur que vols eliminar aquesta pila?",
"restartPolicyUnlessStopped": "A menys que s'aturi",
"dependsOn": "Dependència del contenidor | Dependències del contenidor",
"disableauth.message1": "Esteu segur que voleu <strong>desactivar l'autenticació</strong>?",
"disableauth.message2": "Està dissenyat per a escenaris <strong>on voleu implementar l'autenticació de tercers</strong> per davant de Dockge, com ara Cloudflare Access, Authelia o altres mecanismes d'autenticació.",
"Repeat New Password": "Repetiu la nova contrasenya",
"Please use this option carefully!": "Si us plau, utilitzeu aquesta opció amb cura!",
"Enable Auth": "Habilitar autenticació",
"I understand, please disable": "Ho entenc, si us plau deshabilita",
"Password": "Contrasenya",
"LongSyntaxNotSupported": "La sintaxi llarga no està suportada aquí. Si us plau, fes servir l'editor YAML."
}

View File

@@ -19,7 +19,7 @@
"restartStack": "Restartovat",
"updateStack": "Aktualizovat",
"startStack": "Spustit",
"downStack": "Zastavit & Vypnout",
"downStack": "Zastavit & Zneaktivnit",
"editStack": "Upravit",
"discardStack": "Zahodit",
"saveStackDraft": "Uložit",
@@ -97,5 +97,33 @@
"extra": "Extra",
"reverseProxyMsg1": "Používáte Reverzní proxy server?",
"reverseProxyMsg2": "Podívat se jak to nastavit pro WebSocket",
"Cannot connect to the socket server.": "Nelze se připojit k serveru ."
"Cannot connect to the socket server.": "Nelze se připojit k serveru .",
"Lost connection to the socket server. Reconnecting...": "Ztraceno spojení se serverem. Obnovuji spojení...",
"newUpdate": "Nová aktualizace",
"dockgeAgent": "Dockge Agent | Dockge Agenti",
"agentOnline": "Online",
"connecting": "Připojování",
"agentOffline": "Offline",
"dockgeURL": "Dockge URL (např. http://127.0.0.1:5001)",
"LongSyntaxNotSupported": "Dlouhá syntaxe zde není podporována. Použijte, prosím, YAML editor.",
"connecting...": "Připojování k socket serveru…",
"connect": "Připojit",
"addAgent": "Přidat Agenta",
"agentAddedSuccessfully": "Agent byl úspěšně přidán.",
"agentRemovedSuccessfully": "Agend byl úspěšně odebrán.",
"removeAgent": "Odebrat Agenta",
"removeAgentMsg": "Opravdu chcete tohoto agenta odebrat?",
"Saved": "Uloženo",
"Deployed": "Nasazeno",
"Deleted": "Odstraněno",
"Updated": "Aktualizovat",
"Started": "Spuštěno",
"Stopped": "Zastaveno",
"Restarted": "Restartováno",
"Switch to sh": "Přepnout na sh shell",
"terminal": "Terminál",
"New Container Name...": "Název nového kontejneru...",
"Network name...": "Název sítě...",
"Select a network...": "Vyberte síť...",
"NoNetworksAvailable": "Žádná síť není dostupná. Musíte přidat interní síť nebo povolit externí sítě v pravé části."
}

View File

@@ -1,32 +1,32 @@
{
"languageName": "Dansk",
"authIncorrectCreds": "Forkert brugernavn eller adgangskode.",
"PasswordsDoNotMatch": "Adgangskode stemmer ikke overens.",
"PasswordsDoNotMatch": "Adgangskoder stemmer ikke overens.",
"Repeat Password": "Gentag adgangskode",
"Create": "Opret",
"signedInDisp": "Logget ind som {0}",
"signedInDispDisabled": "Auth Deaktiveret.",
"signedInDispDisabled": "Godkendelse deaktiveret.",
"home": "Hjem",
"console": "Konsol",
"registry": "Registry",
"compose": "Compose",
"stackName": "Stack-navn",
"registry": "Register",
"compose": "Komponer",
"stackName": "Stak-navn",
"deployStack": "Udrulle",
"deleteStack": "Slet",
"stopStack": "Stop",
"restartStack": "Genstart",
"updateStack": "Opdatere",
"updateStack": "Opdater",
"startStack": "Start",
"downStack": "Stop & Sluk",
"editStack": "Editere",
"discardStack": "Annuller",
"downStack": "Stop & Deaktiver",
"editStack": "Rediger",
"discardStack": "Kassér",
"saveStackDraft": "Gem",
"notAvailableShort": "Ugyldig",
"stackNotManagedByDockgeMsg": "Denne stack administreres ikke af Dockge.",
"notAvailableShort": "N/A",
"stackNotManagedByDockgeMsg": "Denne stak administreres ikke af Dockge.",
"primaryHostname": "Primært værtsnavn",
"general": "Generelt",
"container": "Container | Containere",
"scanFolder": "Scan Stack-mappe",
"scanFolder": "Scan Stak-mappe",
"dockerImage": "Billede",
"restartPolicyUnlessStopped": "Medmindre stoppet",
"restartPolicyAlways": "Altid",
@@ -37,7 +37,7 @@
"port": "Port | Porte",
"volume": "Volumen | Voluminer",
"network": "Netværk | Netværker",
"dependsOn": "Container Dependency | Container Dependencies",
"dependsOn": "Containerafhængighed | Containerafhængigheder",
"addListItem": "Tilføj {0}",
"deleteContainer": "Slet",
"addNetwork": "Tilføj Netværk",
@@ -46,7 +46,7 @@
"add": "Tilføj",
"Edit": "Redigere",
"applyToYAML": "Anvend til YAML",
"createExternalNetwork": "Skabe",
"createExternalNetwork": "Skab",
"addInternalNetwork": "Tilføj",
"Save": "Gem",
"Language": "Sprog",
@@ -58,11 +58,11 @@
"Update Password": "Opdater adgangskode",
"Advanced": "Avanceret",
"Please use this option carefully!": "Brug venligst denne indstilling forsigtigt!",
"Enable Auth": "Aktiver Auth",
"Disable Auth": "Deaktiver Auth",
"Enable Auth": "Aktiver godkendelse",
"Disable Auth": "Deaktiver godkendelse",
"I understand, please disable": "Jeg forstår, venligst deaktiver",
"Leave": "Forlad",
"Frontend Version": "Frontend Version",
"Frontend Version": "Version",
"Check Update On GitHub": "Tjek opdatering på GitHub",
"Also check beta release": "Tjek også betaversionen",
"Remember me": "Husk mig",
@@ -72,7 +72,7 @@
"Settings": "Indstillinger",
"Logout": "Log ud",
"Convert to Compose": "Konverter til Compose",
"active": "aktive",
"active": "aktiv",
"exited": "forladt",
"inactive": "inaktive",
"Appearance": "Udseende",
@@ -91,13 +91,36 @@
"url": "URL | URL'er",
"extra": "Ekstra",
"Create your admin account": "Opret din administratorkonto",
"addFirstStackMsg": "Compose din første stack!",
"deleteStackMsg": "Er du sikker på, at du vil slette denne stack?",
"addFirstStackMsg": "Komponer din første stak!",
"deleteStackMsg": "Er du sikker på, at du vil slette denne stak?",
"environmentVariable": "Miljøvariabel | miljøvariabler",
"addContainer": "Tilføj Container",
"disableauth.message1": "Er du sikker på, at du vil <strong>deaktivere godkendelse</strong>?",
"disableauth.message2": "Det er designet til scenarier <strong>hvor du har til hensigt at implementere tredjepartsgodkendelse</strong> foran Dockge såsom Cloudflare Access, Authelia eller andre godkendelsesmekanismer.",
"Show update if available": "Vis opdatering, hvis tilgængelig",
"Lowercase only": "Kun små bogstaver",
"newUpdate": "Ny Opdatering"
"newUpdate": "Ny Opdatering",
"dockgeAgent": "Dockge Agent | Dockge Agenter",
"currentEndpoint": "Nuværende",
"dockgeURL": "Dockge URL (f.eks. http://127.0.0.1:5001)",
"agentOnline": "Online",
"agentOffline": "Offline",
"connecting": "Tilslutter",
"connect": "Tilslut",
"addAgent": "Tilføj agent",
"agentAddedSuccessfully": "Agent succesfuld tilføjet.",
"agentRemovedSuccessfully": "Agent succesfuld fjernet.",
"removeAgent": "Fjern agent",
"removeAgentMsg": "Er du sikker på at du vil fjerne denne agent?",
"LongSyntaxNotSupported": "Langt syntaks er ikke understøttet her. Forsøg venligst med YAML-editoren.",
"Saved": "Gemt",
"Deleted": "Slettet",
"Updated": "Opdateret",
"Started": "Startet",
"Stopped": "Stoppet",
"Restarted": "Genstartet",
"terminal": "Terminal",
"Network name...": "Netværksnavn...",
"Select a network...": "Vælg et netværk...",
"Deployed": "Udrullet"
}

View File

@@ -0,0 +1,132 @@
{
"languageName": "Schwiizerdütsch",
"Create your admin account": "Erstell dis Admin-Konto",
"authIncorrectCreds": "Falsche Benutzername oder falsches Passwort.",
"PasswordsDoNotMatch": "Passwörter stimmed nöd überein.",
"Repeat Password": "Passwort wiederhole",
"Create": "Erstelle",
"signedInDisp": "Agmeldet als {0}",
"signedInDispDisabled": "Ameldig deaktiviert.",
"home": "Startsiite",
"console": "Konsole",
"registry": "Container Registry",
"compose": "Compose",
"addFirstStackMsg": "Stell din erste Stack zämme!",
"stackName": "Stack-Name",
"deployStack": "Deploye",
"deleteStack": "Lösche",
"stopStack": "Ahalte",
"restartStack": "Neustarte",
"updateStack": "Aktualisiere",
"startStack": "Starte",
"editStack": "Bearbeite",
"discardStack": "Verwerfe",
"saveStackDraft": "Speicher",
"notAvailableShort": "N/V",
"deleteStackMsg": "Wotsch de Stack würklich lösche?",
"stackNotManagedByDockgeMsg": "De Stack wird nöd vo Dockge verwaltet.",
"primaryHostname": "Primäre Hostname",
"general": "Allgemein",
"container": "Container",
"scanFolder": "Stacks-Ordner durchsueche",
"dockerImage": "Image",
"restartPolicyUnlessStopped": "Falls nöd gstoppt",
"restartPolicyAlways": "Immer",
"restartPolicyOnFailure": "Bimene Fehler",
"restartPolicyNo": "Kein Neustart",
"environmentVariable": "Umgebigsvariable",
"restartPolicy": "Neustart Richtlinie",
"containerName": "Container-Name",
"port": "Port / Ports",
"volume": "Volume / Volumes",
"network": "Netzwerk | Netzwerke",
"dependsOn": "Container-Abhängigkeit/e",
"addListItem": "{0} hinzuefüege",
"deleteContainer": "Lösche",
"addContainer": "Container hinzuefüege",
"addNetwork": "Netzwerk hinzuefüege",
"disableauth.message1": "Bisch der sicher, dass du d'<strong>Ameldung deaktiviere</strong> wotsch?",
"disableauth.message2": "Es isch für Szenarien vorgseh, <strong>in dene du beabsichtigsch, e Drittabüter-Authentifizierig</strong> vor Dockge z'implementiere, wie zum Bispiel Cloudflare Access, Authelia oder anderi Authentifizierigsmechanisme.",
"passwordNotMatchMsg": "s'wiederholte Passwort stimmt nöd überein.",
"autoGet": "Automatisch lade",
"add": "Hinzuefüege",
"Edit": "Bearbeite",
"applyToYAML": "Uf s'YAML awende",
"createExternalNetwork": "Erstelle",
"addInternalNetwork": "Hinzuefüege",
"Save": "Speichere",
"Language": "Sprach",
"Current User": "Aktuelle Benutzer",
"Change Password": "Passwort ändere",
"Current Password": "Aktuells Passwort",
"New Password": "Neus Passwort",
"Repeat New Password": "Neus Passwort wiederhole",
"Update Password": "Passwort aktualisiere",
"Advanced": "Erwiitert",
"Please use this option carefully!": "Bitte verwend die Option sorgfältig!",
"Enable Auth": "Ameldig aktiviere",
"Disable Auth": "Ameldig deaktiviere",
"I understand, please disable": "Ich verstah, bitte deaktiviere",
"Leave": "Verlah",
"Frontend Version": "Frontend Version",
"Check Update On GitHub": "Update uf GitHub überprüefe",
"Show update if available": "Update azeige, wenn verfüegbar",
"Also check beta release": "Au Beta-Version überprüefe",
"Remember me": "Agmeldet blibe",
"Login": "Amelde",
"Username": "Benutzername",
"Password": "Passwort",
"Settings": "Istellige",
"Logout": "Abmelde",
"Lowercase only": "Nur Chliibuechstabe",
"Convert to Compose": "In Compose-Syntax umwandle",
"Docker Run": "Docker Run",
"active": "aktiv",
"exited": "beendet",
"inactive": "inaktiv",
"Appearance": "Erschiinigsbild",
"Security": "Sicherheit",
"About": "Über",
"Allowed commands:": "Zueglasseni Befehl:",
"Internal Networks": "Interni Netzwerk",
"External Networks": "Externi Netzwerk",
"No External Networks": "Kei externi Netzwerk",
"Cannot connect to the socket server.": "Kei Verbindig zum Socket Server.",
"reverseProxyMsg1": "Wird en Reverse Proxy benutzt?",
"reconnecting...": "Erneute Verbindigsufbau…",
"downStack": "Stoppe & Deaktiviere",
"extra": "Extra",
"url": "URL / URLs",
"reverseProxyMsg2": "Lern wie er für WebSockets z'konfiguriere isch.",
"connecting...": "Verbindigsufbau zum Socket Server…",
"newUpdate": "Neues Update",
"dockgeAgent": "Dockge Agent | Dockge Agente",
"currentEndpoint": "Aktuell",
"dockgeURL": "Dockge URL (z. B. http://127.0.0.1:5001)",
"agentOnline": "Online",
"agentOffline": "Offline",
"connecting": "Verbinde",
"connect": "Verbinde",
"addAgent": "Agent Hinzuefüege",
"agentAddedSuccessfully": "Agent erfolgriich hinzuegfüegt.",
"agentRemovedSuccessfully": "Agent erfolgriich entfernt.",
"removeAgent": "Agent Entferne",
"removeAgentMsg": "Bisch der sicher, dass du de Agent entferne wotsch?",
"LongSyntaxNotSupported": "Lange Syntax wird nöd unterstützt. Bitte verwend de YAML-Editor.",
"Lost connection to the socket server. Reconnecting...": "Verbindig zum Socket Server verlore. Verbinde...",
"Saved": "Gspeicheret",
"Deleted": "Glöscht",
"Started": "Gstartet",
"Stopped": "Gstoppt",
"Restarted": "Neugstartet",
"New Container Name...": "Neue Container Name...",
"Network name...": "Netzwerkname...",
"Select a network...": "Netzwerk uswähle...",
"Updated": "Aktualisiert",
"Deployed": "Deployed",
"Switch to sh": "Zu sh wechsle",
"terminal": "Terminal",
"CurrentHostname": "(nöd gsetzt: verwendet aktuelli Hostname)",
"Downed": "Abegfahre",
"NoNetworksAvailable": "Kei Netzwerk verfüegbar. Du muesch zersch interni Netzwerk hinzuefüege oder externi Netzwerk uf de rechte Siite aktiviere."
}

View File

@@ -94,9 +94,39 @@
"Cannot connect to the socket server.": "Keine Verbindung zum Socket Server.",
"reverseProxyMsg1": "Wird ein Reverse Proxy genutzt?",
"reconnecting...": "Erneuter Verbindungsaufbau…",
"downStack": "Stoppen & Aus",
"downStack": "Stoppen & Deaktivieren",
"extra": "Extra",
"url": "URL / URLs",
"reverseProxyMsg2": "Lerne wie dieser für WebSockets zu konfigurieren ist.",
"connecting...": "Verbindungsaufbau zum Socket Server…"
"connecting...": "Verbindungsaufbau zum Socket Server…",
"newUpdate": "Neues Update",
"dockgeAgent": "Dockge Agent | Dockge Agenten",
"currentEndpoint": "Aktuell",
"dockgeURL": "Dockge URL (z. B. http://127.0.0.1:5001)",
"agentOnline": "Online",
"agentOffline": "Offline",
"connecting": "Verbinden",
"connect": "Verbinden",
"addAgent": "Agent Hinzufügen",
"agentAddedSuccessfully": "Agent erfolgreich hinzugefügt.",
"agentRemovedSuccessfully": "Agent erfolgreich entfernt.",
"removeAgent": "Agent Entfernen",
"removeAgentMsg": "Bist Du sicher, dass Du diesen Agent entfernen möchtest?",
"LongSyntaxNotSupported": "Lange Syntax wird nicht unterstützt. Bitte verwende den YAML-Editor.",
"Lost connection to the socket server. Reconnecting...": "Verbindung zu Socket Server verloren. Verbinden...",
"Saved": "Gespeichert",
"Deleted": "Gelöscht",
"Started": "Gestartet",
"Stopped": "Gestoppt",
"Restarted": "Neugestartet",
"New Container Name...": "Neuer Container Name...",
"Network name...": "Netzwerkname...",
"Select a network...": "Netzwerk auswählen...",
"Updated": "Aktualisiert",
"Deployed": "Deployed",
"Switch to sh": "Zu sh wechseln",
"terminal": "Terminal",
"CurrentHostname": "(nicht gesetzt: verwende aktuellen Hostname)",
"Downed": "Heruntergefahren",
"NoNetworksAvailable": "Keine Netzwerke verfügbar. Du musst zunächst interne Netzwerke hinzufügen oder externe Netzwerke auf der rechten Seite aktivieren."
}

View File

@@ -19,12 +19,13 @@
"restartStack": "Restart",
"updateStack": "Update",
"startStack": "Start",
"downStack": "Stop & Down",
"downStack": "Stop & Inactive",
"editStack": "Edit",
"discardStack": "Discard",
"saveStackDraft": "Save",
"notAvailableShort": "N/A",
"deleteStackMsg": "Are you sure you want to delete this stack?",
"cancel": "Cancel",
"stackNotManagedByDockgeMsg": "This stack is not managed by Dockge.",
"primaryHostname": "Primary Hostname",
"general": "General",
@@ -95,6 +96,7 @@
"reverseProxyMsg1": "Using a Reverse Proxy?",
"reverseProxyMsg2": "Check how to config it for WebSocket",
"Cannot connect to the socket server.": "Cannot connect to the socket server.",
"Lost connection to the socket server. Reconnecting...": "Lost connection to the socket server. Reconnecting...",
"reconnecting...": "Reconnecting…",
"connecting...": "Connecting to the socket server…",
"url": "URL | URLs",
@@ -111,5 +113,34 @@
"agentAddedSuccessfully": "Agent added successfully.",
"agentRemovedSuccessfully": "Agent removed successfully.",
"removeAgent": "Remove Agent",
"removeAgentMsg": "Are you sure you want to remove this agent?"
"removeAgentMsg": "Are you sure you want to remove this agent?",
"GlobalEnv": "Global .env",
"LongSyntaxNotSupported": "Long syntax is not supported here. Please use the YAML editor.",
"name": "Dockge Agent Display name",
"updatedName": "New Dockge Agent Display name",
"Saved": "Saved",
"Deployed": "Deployed",
"Deleted": "Deleted",
"Updated": "Updated",
"Started": "Started",
"Stopped": "Stopped",
"Restarted": "Restarted",
"Downed": "Downed",
"Switch to sh": "Switch to sh",
"terminal": "Terminal",
"CurrentHostname": "(Unset: Follow current hostname)",
"New Container Name...": "New Container Name...",
"Network name...": "Network name...",
"Select a network...": "Select a network...",
"NoNetworksAvailable": "No networks available. You need to add internal networks or enable external networks in the right side first.",
"CPU": "CPU",
"memory": "Memory",
"memoryAbbreviated": "Mem",
"networkIO": "Network I/O",
"blockIO": "Block I/O",
"Console is not enabled": "Console is not enabled",
"ConsoleNotEnabledMSG1": "Console is a powerful tool that allows you to execute any commands such as <code>docker</code>, <code>rm</code> within the Dockge's container in this Web UI.",
"ConsoleNotEnabledMSG2": "It might be dangerous since this Dockge container is connecting to the host's Docker daemon. Also Dockge could be possibly taken down by commands like <code>rm -rf</code>" ,
"ConsoleNotEnabledMSG3": "If you understand the risk, you can enable it by setting <code>DOCKGE_ENABLE_CONSOLE=true</code> in the environment variables.",
"confirmLeaveStack": "You are currently editing a stack. Are you sure you want to leave?"
}

View File

@@ -1,5 +1,5 @@
{
"languageName": "Español",
"languageName": "Inglés",
"Create your admin account": "Crea tu cuenta de administrador",
"authIncorrectCreds": "Nombre de usuario o contraseña incorrectos.",
"PasswordsDoNotMatch": "Las contraseñas no coinciden.",
@@ -94,10 +94,39 @@
"reverseProxyMsg1": "¿Usando un proxy inverso?",
"reverseProxyMsg2": "Compruebe cómo configurarlo para WebSocket",
"newUpdate": "Nueva actualización",
"downStack": "Detener y finalizar",
"downStack": "Detener y desactivar",
"Cannot connect to the socket server.": "No se puede conectar al servidor del socket.",
"reconnecting...": "Reconectando…",
"connecting...": "Conectando al servidor del socket…",
"url": "URL | URLs",
"extra": "Extra"
"url": "Dirección URL | Direcciones URLs",
"extra": "Addicional",
"currentEndpoint": "Actual",
"dockgeURL": "URL de Dockge (ej. http://127.0.0.1:5001)",
"agentOnline": "En línea",
"agentOffline": "Desconectado",
"connect": "Conectar",
"addAgent": "Añadir Agente",
"agentAddedSuccessfully": "Agente añadido satisfactoriamente.",
"removeAgent": "Eliminar Agente",
"removeAgentMsg": "¿Estás seguro que deseas eliminar este agente?",
"dockgeAgent": "Agentes Dockge",
"connecting": "Conectando",
"agentRemovedSuccessfully": "Agente eliminado satisfactoriamente.",
"LongSyntaxNotSupported": "No hay soporte para la sintaxis larga. Por favor use el editor de YAML.",
"Lost connection to the socket server. Reconnecting...": "Se ha perdido la conexión con el servidor de socket. Reconectando...",
"Saved": "Guardado",
"Deployed": "Desplegado",
"Deleted": "Eliminado",
"Updated": "Actualizado",
"Started": "Arrancado",
"Stopped": "Parado",
"Restarted": "Reseteado",
"Downed": "Caído",
"Switch to sh": "Cambiar a sh",
"terminal": "Terminal",
"CurrentHostname": "(Vacío: Seguir hostname actual)",
"New Container Name...": "Nombre del nuevo Container...",
"Network name...": "Nombre de la red...",
"Select a network...": "Selecciona una red...",
"NoNetworksAvailable": "No hay redes disponibles. Primero debes agregar redes internas o habilitar redes externas en el lado derecho."
}

View File

@@ -95,9 +95,38 @@
"connecting...": "Connexion au serveur socket…",
"url": "URL | URLs",
"extra": "Supplémentaire",
"downStack": "Arrêter et désactiver",
"downStack": "Arrêtez et rendre inactif",
"reverseProxyMsg1": "Utilisez vous un proxy inverse ?",
"Cannot connect to the socket server.": "Impossible de se connecter au serveur socket.",
"reconnecting...": "Reconnexion…",
"newUpdate": "Nouvelle mise à jour"
"newUpdate": "Nouvelle mise à jour",
"dockgeURL": "URL de Dockge (e.g. http://127.0.0.1:5001)",
"agentOnline": "En ligne",
"agentOffline": "Hors ligne",
"connecting": "Connexion",
"addAgent": "Ajouter un agent",
"agentAddedSuccessfully": "Agent ajouté avec succès.",
"agentRemovedSuccessfully": "Agent supprimé avec succès.",
"removeAgent": "Supprimer l'agent",
"dockgeAgent": "Dockge Agent | Dockge Agents",
"currentEndpoint": "Actuel",
"connect": "Connecter",
"removeAgentMsg": "Êtes-vous sûr de vouloir supprimer cet agent ?",
"LongSyntaxNotSupported": "La syntaxe longue n'est pas prise en charge ici. Veuillez utiliser l'éditeur YAML.",
"Saved": "Enregistré",
"Deployed": "Déployé",
"Deleted": "Supprimé",
"Updated": "Mis à jour",
"Started": "démarrer",
"Stopped": "Arrêté",
"Restarted": "Redémarré",
"Switch to sh": "Passer à sh",
"terminal": "Terminal",
"New Container Name...": "Nouveau nom du conteneur...",
"Network name...": "Nom du réseau...",
"Select a network...": "Sélectionnez un réseau...",
"Downed": "Abattu",
"Lost connection to the socket server. Reconnecting...": "Connexion au serveur socket perdue. Reconnexion...",
"CurrentHostname": "(Non défini: suivre le nom d'hôte actuel)",
"NoNetworksAvailable": "Aucun réseau disponible. Vous devez d'abord ajouter des réseaux internes ou activer les réseaux externes sur le côté droit."
}

132
frontend/src/lang/ga.json Normal file
View File

@@ -0,0 +1,132 @@
{
"Create your admin account": "Cruthaigh do chuntas riaracháin",
"authIncorrectCreds": "Ainm úsáideora nó pasfhocal mícheart.",
"PasswordsDoNotMatch": "Níl na pasfhocail comhthráthacha.",
"Repeat Password": "Athscríobh an Pasfhocal",
"Create": "Cruthaigh",
"signedInDisp": "Sínithe isteach mar {0}",
"languageName": "Gaeilge",
"console": "Consól",
"registry": "Clárlann",
"compose": "Scríobh",
"stackName": "Ainm an Staca",
"deployStack": "Deighil",
"deleteStack": "Scrios",
"stopStack": "Stad",
"restartStack": "Atosaigh",
"updateStack": "Nuashonraigh",
"startStack": "Tosaigh",
"downStack": "Stad & Neamhghníomhach",
"editStack": "Cuir in Eagar",
"discardStack": "Caith amach",
"saveStackDraft": "Sábháil",
"deleteStackMsg": "An bhfuil tú cinnte go bhfuil tú ag iarraidh an staca seo a scriosadh?",
"primaryHostname": "Príomhainm óstáin",
"general": "Ginearálta",
"container": "Coimeádán | Coimeádáin",
"scanFolder": "Scanáil Fillteáin na dStacanna",
"dockerImage": "Íomha",
"restartPolicyUnlessStopped": "Mura Stadfar",
"restartPolicyAlways": "I gcónaí",
"restartPolicyOnFailure": "Ar theip",
"restartPolicyNo": "Níl",
"environmentVariable": "Athróg Timpeallacht | Athróga Timpeallacht",
"restartPolicy": "Polasaí Atosaigh",
"port": "Port | Portanna",
"volume": "Toirt | Toirteanna",
"network": "Líonra | Líonraí",
"dependsOn": "Spleáchas Coimeádán | Spleáchais Coimeádán",
"addListItem": "Cuir {0}",
"deleteContainer": "Scrios",
"addContainer": "Cuir Coimeádán leis",
"addNetwork": "Cuir Líonra leis",
"add": "Cuir",
"Edit": "Cuir in eagar",
"applyToYAML": "Déan iarratas ar YAML",
"createExternalNetwork": "Cruthaigh",
"disableauth.message1": "An bhfuil tú cinnte gur mhaith leat <strong>fíordheimhniú a dhíchumasú</strong>?",
"passwordNotMatchMsg": "Ní hionann an pasfhocal athfhillteach.",
"autoGet": "Faigh Uathoibríoch",
"addInternalNetwork": "Cuir",
"Save": "Sábháil",
"Language": "Teanga",
"Current User": "Úsáideoir Reatha",
"New Password": "Pasfhocal Nua",
"Current Password": "Pasfhocal Reatha",
"Change Password": "Athraigh do Phasfhocal",
"Repeat New Password": "Déan Pasfhocal Nua arís",
"Update Password": "Nuashonraigh Pasfhocal",
"Advanced": "Ardleibhéal",
"Please use this option carefully!": "Bain úsáid as an rogha seo go cúramach, le do thoil!",
"Enable Auth": "Cumasaigh Auth",
"Disable Auth": "Auth dhíchumasú",
"I understand, please disable": "Tuigim, le do thoil, múch",
"Leave": "Fág",
"Frontend Version": "Leagan Frontend",
"Check Update On GitHub": "Seiceáil an Nuashonrú ar GitHub",
"Show update if available": "Taispeáin an Nuashonrú más ar fáil",
"Also check beta release": "Seiceáil an scaoileadh beta freisin",
"Remember me": "Cuimhnigh orm",
"Login": "Logáil isteach",
"Username": "Ainm úsáideora",
"Password": "Pasfhocal",
"Logout": "Logáil Amach",
"Lowercase only": "Cás íochtair amháin",
"Convert to Compose": "Tiontaigh go Compóidh",
"Docker Run": "Docker Rith",
"exited": "scoir",
"inactive": "neamhghníomhach",
"Appearance": "Dealramh",
"Security": "Slándáil",
"About": "Maidir le",
"Allowed commands:": "Orduithe ceadaithe:",
"Internal Networks": "Líonraí Inmheánacha",
"External Networks": "Líonraí Seachtracha",
"No External Networks": "Gan Líonraí Seachtracha",
"reverseProxyMsg1": "Ag Úsáid Seachfhreastalaí Réabhlóideach?",
"reverseProxyMsg2": "Seiceáil conas é a shocraigh don WebSocket",
"Cannot connect to the socket server.": "Ní féidir ceangal a dhéanamh leis an freastalaí soicéad.",
"reconnecting...": "Ag athcheangal…",
"connecting...": "Ag nascadh leis an freastalaí soicéad…",
"url": "URL | URLanna",
"extra": "Breise",
"newUpdate": "Nuashonrú Nua",
"dockgeAgent": "Aighne Dockge | Aighnithe Dockge",
"currentEndpoint": "Reatha",
"dockgeURL": "Dockge URL (e.g. http://127.0.0.1:5001)",
"agentOnline": "Ar Líne",
"agentOffline": "As Líne",
"connecting": "Ag Nascadh",
"connect": "Ceangail",
"addAgent": "Cuir Aighne",
"agentAddedSuccessfully": "Aighne curtha leis go rathúil.",
"agentRemovedSuccessfully": "Aighne bhaint as go rathúil.",
"removeAgent": "Bain Aighne",
"removeAgentMsg": "An bhfuil tú cinnte gur mhaith leat an t-aighne seo a bhaint?",
"LongSyntaxNotSupported": "Ní thacaítear leis an níochán fada anseo. Úsáid an Eagarthóir YAML, le do thoil.",
"signedInDispDisabled": "Auth Díchumasaithe.",
"home": "Abhaile",
"addFirstStackMsg": "Scríobh do chéad stac!",
"notAvailableShort": "Níl ar Fáil",
"stackNotManagedByDockgeMsg": "Ní bhainistítear an staca seo ag Dockge.",
"containerName": "Ainm na gCoimeádán",
"disableauth.message2": "Tá sé deartha do chúinsí <strong>ina bhfuil sé beartaithe agat tríú páirtí athbhreithniú a chur i bhfeidhm</strong> os comhair Dockge cosúil le Rochtain Cloudflare, Authelia nó múnlaí deimhniú eile.",
"Settings": "Socruithe",
"active": "gníomhach",
"Lost connection to the socket server. Reconnecting...": "Ceangal caillte leis an bhfreastalaí soicéad. Ag athcheangal...",
"Saved": "Shábháil",
"Deployed": "Imlonnaithe",
"Deleted": "Scriosta",
"Updated": "Nuashonraithe",
"Started": "Thosaigh",
"Stopped": "Stopadh",
"Restarted": "Atosaigh",
"Downed": "Tugtha anuas",
"Switch to sh": "Athraigh go sh",
"terminal": "Teirminéal",
"CurrentHostname": "(Díshuiteáil: Lean an t-óstainm reatha)",
"New Container Name...": "Ainm Gabhdáin Nua...",
"Network name...": "Ainm líonra...",
"Select a network...": "Roghnaigh líonra...",
"NoNetworksAvailable": "Níl líonraí ar fáil. Ní mór duit líonraí inmheánacha a chur leis nó líonraí seachtracha a chumasú ar an taobh deas ar dtús."
}

132
frontend/src/lang/hu.json Normal file
View File

@@ -0,0 +1,132 @@
{
"languageName": "Angol",
"Repeat Password": "Jelszó Ismétlése",
"Create": "Létrehozás",
"signedInDisp": "Bejelentkezve {0}-ként",
"home": "Főképernyő",
"registry": "Bejegyzések",
"compose": "Összeállít",
"addFirstStackMsg": "Állítsd össze az első stack-odat!",
"stackName": "Stack Neve",
"deployStack": "Telepítés",
"deleteStack": "Törlés",
"stopStack": "Leállítás",
"restartStack": "Újraindítás",
"downStack": "Leállítva",
"editStack": "Szerkesztés",
"discardStack": "Eldobás",
"saveStackDraft": "Mentés",
"notAvailableShort": "N/A",
"stackNotManagedByDockgeMsg": "Ez a stack nem a Dockge kezelése alatt áll.",
"primaryHostname": "Elsődleges Gazdagépnév",
"general": "Általános",
"container": "Konténer | Konténerek",
"scanFolder": "Stack Mappa Beolvasása",
"dockerImage": "Applikáció-kép",
"restartPolicyNo": "Nem",
"environmentVariable": "Környezeti Változó | Környezeti Változók",
"containerName": "Konténer Neve",
"port": "Port | Portok",
"volume": "Tárhely | Tárhelyek",
"network": "Hálózat | Hálózatok",
"addListItem": "{0} Hozzáadása",
"deleteContainer": "Törlés",
"addContainer": "Konténer Hozzáadása",
"addNetwork": "Hálózat Hozzáadása",
"add": "Hozzáadás",
"Edit": "Szerkesztés",
"applyToYAML": "Alkalmazás YAML Formátumba",
"addInternalNetwork": "Hozzáadás",
"Save": "Mentés",
"Language": "Nyelv",
"Current User": "Jelenlegi Felhasználó",
"Change Password": "Jelszó Módosítása",
"Current Password": "Jelenlegi Jelszó",
"New Password": "Új Jelszó",
"Update Password": "Jelszó Cserélése",
"Advanced": "Fejlett",
"Enable Auth": "Hitelesítés Bekapcsolása",
"Disable Auth": "Hitelesítés Kikapcsolása",
"I understand, please disable": "Megértettem, kérem kapcsolja ki",
"Leave": "Kilépés",
"Frontend Version": "Frontend Verzió",
"Also check beta release": "Béta kiadás is",
"Remember me": "Emlékezz rám",
"Login": "Belépés",
"Username": "Felhasználónév",
"Password": "Jelszó",
"Settings": "Beállítások",
"Convert to Compose": "Átalakítás Compose-ra",
"Docker Run": "Docker Futtatása",
"active": "aktív",
"inactive": "inaktív",
"Appearance": "Megjelenés",
"Security": "Biztonság",
"Allowed commands:": "Megengedett parancsok:",
"Internal Networks": "Belső Hálózatok",
"External Networks": "Kölső Hálózatok",
"No External Networks": "Nincs Külső Hálózat",
"reverseProxyMsg1": "Proxy-t használ?",
"reverseProxyMsg2": "Javasolt WebSocket konfiguráció megtekintése",
"reconnecting...": "Újracsatlakozás…",
"extra": "Extra",
"newUpdate": "Új Frissítés",
"currentEndpoint": "Jelenlegi",
"agentOnline": "Online",
"dockgeAgent": "Dockge Felügyelő | Dockge Felügyelők",
"agentOffline": "Offline",
"connecting": "Csatlakozás",
"connect": "Csatlakozás",
"agentAddedSuccessfully": "Felügyelő hozzáadva.",
"agentRemovedSuccessfully": "Felügyelő törölve.",
"removeAgent": "Felügyelő Törlése",
"addAgent": "Felügyelő Hozzáadása",
"removeAgentMsg": "Biztos hogy törli ezt a Felügyelőt?",
"Create your admin account": "Adminisztrátor felhasználó létrehozása",
"authIncorrectCreds": "Helytelen felhasználónév vagy jelszó.",
"PasswordsDoNotMatch": "Jelszavak nem eggyeznek.",
"signedInDispDisabled": "Hitelesítés Kikapcsolva.",
"console": "Konzol",
"updateStack": "Frissítés",
"startStack": "Indítás",
"deleteStackMsg": "Biztos hogy törli ezt a stack-ot?",
"restartPolicyUnlessStopped": "Ha Nincs Leállítva",
"restartPolicyAlways": "Mindig",
"restartPolicyOnFailure": "Hibába Futáskor",
"restartPolicy": "Újraindítási Elv",
"dependsOn": "Konténer Függés | Konténer Függései",
"disableauth.message1": "Biztos hogy szeretné a <strong>hitelesítést kikapcsolni</strong>?",
"disableauth.message2": "Olyan esetekre ahol <strong>harmadfél általi hitelesítést szeretne alkalmazni</strong> a Dockge elött, mint például Cloudflare Access, Authelia vagy egyéb hitelesítő program.",
"passwordNotMatchMsg": "Az ismételt jelszó nem eggyezik.",
"autoGet": "Automatikus Megszerzés",
"createExternalNetwork": "Készítés",
"Repeat New Password": "Új Jelszó Megerősítése",
"Please use this option carefully!": "Ezt a lehetőséget használja óvatosan!",
"Check Update On GitHub": "Fríssítés Keresése Github-on",
"Show update if available": "Elérhető frissítések megjelenítése",
"Logout": "Kilépés",
"Lowercase only": "Csak kisbetűvel",
"exited": "végzett",
"About": "Az Alkalmazásról",
"Cannot connect to the socket server.": "A Socket csatlakozás nem elérhető.",
"connecting...": "Csatlakozás a socket szerver-hez…",
"url": "URL | URL-ek",
"dockgeURL": "Dockge URL (pl. http://127.0.0.1:5001)",
"LongSyntaxNotSupported": "A hosszú szintaxis itt nem támogatott. Használja a YAML szerkesztőt.",
"Lost connection to the socket server. Reconnecting...": "Megszakadt a kapcsolat a socket szerverrel. Újracsatlakozás...",
"Saved": "Elmentve",
"Deployed": "Telepítve",
"Deleted": "Törölve",
"Updated": "Frissítve",
"Started": "Indult",
"Stopped": "Megállítva",
"Restarted": "Újraindítva",
"Downed": "Leállított",
"Switch to sh": "Váltás shell-re",
"terminal": "Terminál",
"CurrentHostname": "(Nincs beállítva: Az aktuális gazdagépnév követése)",
"New Container Name...": "Új konténer név...",
"Network name...": "Hálózat név...",
"Select a network...": "Válasszon ki egy hálózatot...",
"NoNetworksAvailable": "Nincs elérhető hálózat. Először hozzá kell adnia belső hálózatokat, vagy engedélyeznie kell a külső hálózatokat a jobb oldalon."
}

View File

@@ -15,7 +15,7 @@
"stopStack": "Hentikan",
"restartStack": "Mulai ulang",
"updateStack": "Pembaruan",
"downStack": "Hentikan & Turun",
"downStack": "Hentikan & Tidak aktif",
"editStack": "Sunting",
"discardStack": "Buang",
"saveStackDraft": "Simpan",
@@ -99,5 +99,24 @@
"Please use this option carefully!": "Mohon berhati - hati menggunakan opsi ini!",
"Also check beta release": "Juga cek keluaran beta",
"Allowed commands:": "Perintah yang diperbolehkan:",
"reverseProxyMsg2": "Lihat cara mengonfigurasinya untuk WebSocket"
"reverseProxyMsg2": "Lihat cara mengonfigurasinya untuk WebSocket",
"dockgeURL": "Alamat Dockge (cth. http://127.0.0.1:5001)",
"connecting": "Menghubungkan",
"addAgent": "Tambah Agen",
"agentAddedSuccessfully": "Agen sukses ditambahkan.",
"agentRemovedSuccessfully": "Agen sukses dihapus.",
"removeAgent": "Hapus Agen",
"connect": "Hubungkan",
"dockgeAgent": "Agen Dockge",
"currentEndpoint": "Sekarang",
"agentOnline": "Terhubung",
"agentOffline": "Terputus",
"removeAgentMsg": "Apakah anda yakin untuk menghapus agen ini?",
"LongSyntaxNotSupported": "Sintaks yang panjang tidak didukung di sini. Silakan gunakan editor YAML.",
"Saved": "Tersimpan",
"Deleted": "Terhapus",
"Updated": "Telah diperbaharui",
"Started": "Aplikasi dimulai",
"Stopped": "Aplikasi dihentikan",
"Restarted": "Aplikasi memuat kembali"
}

View File

@@ -19,7 +19,7 @@
"restartStack": "Riavvia",
"updateStack": "Aggiorna",
"startStack": "Avvia",
"downStack": "Stop & Down",
"downStack": "Stop & Inattivo",
"editStack": "Modifica",
"discardStack": "Annulla",
"saveStackDraft": "Salva",
@@ -92,11 +92,25 @@
"Internal Networks": "Reti interne",
"External Networks": "Reti esterne",
"No External Networks": "Nessuna rete esterna",
"reverseProxyMsg1": "Utilizzando un proxy inverso?",
"reverseProxyMsg2": "Controlla come configurarlo per WebSocket",
"Cannot connect to the socket server.": "Impossibile connettersi al server socket.",
"connecting...": "Connessione al server socket…",
"reverseProxyMsg1": "Stai usando Reverse Proxy?",
"reverseProxyMsg2": "Verifica come configurarlo per il WebSocket",
"Cannot connect to the socket server.": "impossibile collegarsi al socket server",
"connecting...": "connettendosi al socket server…",
"extra": "Extra",
"reconnecting...": "Riconnessione…",
"url": "Indirizzo | Indirizzi"
"url": "URL | URLs",
"newUpdate": "Nuovo aggiornamento",
"dockgeAgent": "Agente Dockge | Agenti Dockge",
"currentEndpoint": "Corrente",
"agentOnline": "Online",
"agentOffline": "Offline",
"connecting": "In connessione",
"connect": "Connetti",
"dockgeURL": "Dockge URL (ad esempio http://127.0.0.1:5001)",
"agentRemovedSuccessfully": "Agente rimosso con successo.",
"removeAgent": "Rimuovi Agente",
"removeAgentMsg": "Sei sicuro di voler rimuovere questo agente?",
"addAgent": "Aggungi Agente",
"agentAddedSuccessfully": "Agente aggiunto correttamente.",
"LongSyntaxNotSupported": "La sintassi lunga non è supportata qui. Utilizzare l'editor YAML."
}

View File

@@ -34,7 +34,7 @@
"primaryHostname": "主ホスト名",
"container": "コンテナ",
"dependsOn": "コンテナ依存関係",
"downStack": "停止して削除",
"downStack": "停止して非アクティブ化",
"notAvailableShort": "N/A",
"restartPolicyUnlessStopped": "手動で停止されるまで",
"restartPolicyAlways": "常時",
@@ -94,5 +94,36 @@
"I understand, please disable": "理解しました。無効化してください",
"Lowercase only": "小文字のみ",
"reverseProxyMsg1": "リバースプロキシを使用していますか?",
"connecting...": "ソケットサーバーに接続中…"
"connecting...": "ソケットサーバーに接続中…",
"newUpdate": "新しいバージョン",
"dockgeAgent": "Dockge エージェント",
"dockgeURL": "DockgeのURL (例: http://127.0.0.1:5001)",
"agentOnline": "オンライン",
"agentOffline": "オフライン",
"connecting": "接続中",
"connect": "接続",
"addAgent": "エージェントを追加",
"agentAddedSuccessfully": "エージェントが正常に追加されました。",
"agentRemovedSuccessfully": "エージェントは正常に削除されました。",
"removeAgent": "エージェントを削除",
"removeAgentMsg": "本当にこのエージェントを削除しますか?",
"url": "URL | URL",
"About": "Dockgeについて",
"Docker Run": "Docker Run to Compose",
"LongSyntaxNotSupported": "長い構文はここではサポートされていません。YAMLエディタを使用してください。",
"Lost connection to the socket server. Reconnecting...": "ソケットサーバーとの接続が失われました。再接続中です...",
"extra": "追加設定",
"Saved": "保存済み",
"Deployed": "デプロイ済み",
"Deleted": "削除済み",
"Updated": "アップデート済み",
"Started": "開始済み",
"Stopped": "停止済み",
"Restarted": "再起動済み",
"Switch to sh": "shに切り替え",
"terminal": "ターミナル",
"New Container Name...": "新しいコンテナ名...",
"Network name...": "ネットワーク名...",
"Select a network...": "ネットワークを選択...",
"NoNetworksAvailable": "利用可能なネットワークがありません。まず右側の内部ネットワークを追加するか、外部ネットワークを有効にする必要があります。"
}

View File

@@ -92,11 +92,25 @@
"External Networks": "외부 네트워크",
"No External Networks": "외부 네트워크 없음",
"reverseProxyMsg2": "여기서 WebSocket을 위한 설정을 확인해 보세요",
"downStack": "정지 & Down",
"downStack": "정지 & 비활성화",
"reverseProxyMsg1": "리버스 프록시를 사용하고 계신가요?",
"Cannot connect to the socket server.": "소켓 서버에 연결하지 못했습니다.",
"connecting...": "소켓 서버에 연결하는 중…",
"extra": "기타",
"url": "URL | URL",
"reconnecting...": "재연결 중…"
"reconnecting...": "재연결 중…",
"newUpdate": "새 업데이트",
"dockgeURL": "Dockge URL (예. http://127.0.0.1:5001)",
"agentOnline": "온라인",
"agentOffline": "오프라인",
"connect": "연결",
"addAgent": "에이전트 추가",
"agentAddedSuccessfully": "에이전트를 성공적으로 추가했습니다.",
"removeAgent": "에이전트 삭제",
"removeAgentMsg": "정말로 이 에이전트를 삭제하시겠습니까?",
"dockgeAgent": "Dockge 에이전트",
"currentEndpoint": "현재",
"connecting": "연결 중",
"agentRemovedSuccessfully": "에이전트를 성공적으로 삭제했습니다.",
"LongSyntaxNotSupported": "긴 문법은 여기서 지원되지 않습니다. YAML 에디터를 사용하세요."
}

View File

@@ -0,0 +1,34 @@
{
"Create your admin account": "Lag din administrator konto",
"authIncorrectCreds": "Brukernavn eller passord stemmer ikke.",
"PasswordsDoNotMatch": "Passord stemmer ikke.",
"Repeat Password": "Gjenta passord",
"Create": "Lag",
"signedInDisp": "Logg in som {0}",
"signedInDispDisabled": "Auth deaktivert.",
"home": "Hjem",
"console": "Konsoll",
"registry": "Register",
"compose": "Skriv",
"addFirstStackMsg": "Lag din første stack!",
"stackName": "Navn på stack",
"deployStack": "Utplassere",
"deleteStack": "Slett",
"stopStack": "Stoppe",
"restartStack": "Omstart",
"updateStack": "Oppdater",
"downStack": "Stop & Inaktiver",
"editStack": "Rediger",
"discardStack": "Kast",
"saveStackDraft": "Lagre",
"notAvailableShort": "N/A",
"deleteStackMsg": "Er du sikker på at du vil slette denne stacken?",
"stackNotManagedByDockgeMsg": "Denne stacken er ikke styrt av Dockge.",
"primaryHostname": "Primært vertsnavn",
"general": "Generell",
"container": "Container | Containers",
"scanFolder": "Skann Stacks mappe",
"dockerImage": "Bilde",
"languageName": "Engelsk",
"startStack": "Start"
}

View File

@@ -1,14 +1,14 @@
{
"languageName": "Nederlands",
"authIncorrectCreds": "Onjuiste gebruikersnaam of wachtwoord.",
"PasswordsDoNotMatch": "Paswoorden komen niet overeen.",
"PasswordsDoNotMatch": "Wachtwoorden komen niet overeen.",
"Repeat Password": "Herhaal wachtwoord",
"Create": "Aanmaken",
"signedInDisp": "Ingelogd als {0}",
"home": "Startpagina",
"home": "Home",
"console": "Console",
"registry": "Register",
"compose": "Samenstellen",
"compose": "Nieuwe stack",
"stackName": "Stack naam",
"deployStack": "Opzetten",
"deleteStack": "Verwijder",
@@ -16,11 +16,11 @@
"restartStack": "Herstart",
"updateStack": "Update",
"startStack": "Start",
"downStack": "Stop & Down",
"downStack": "Stop & Afsluiten",
"editStack": "Bewerken",
"discardStack": "Verwijderen",
"saveStackDraft": "Opslaan",
"notAvailableShort": "NVT",
"notAvailableShort": "n.v.t.",
"stackNotManagedByDockgeMsg": "Deze stack wordt niet beheerd door Dockge.",
"primaryHostname": "Primaire hostnaam",
"general": "Algemeen",
@@ -83,9 +83,9 @@
"reverseProxyMsg1": "Reverse proxy in gebruik?",
"reverseProxyMsg2": "Controleer hoe te configureren voor WebSocket",
"Cannot connect to the socket server.": "Kan geen verbinding maken met de socket server.",
"reconnecting...": "Herverbinden...",
"connecting...": "Verbinden met de socket server...",
"url": "Url(s)",
"reconnecting...": "Herverbinden",
"connecting...": "Verbinden met de socket server",
"url": "Adres(sen)",
"extra": "Extra",
"Create your admin account": "Creëer je beheerders-account",
"addFirstStackMsg": "Maak je eerste stack!",
@@ -98,5 +98,19 @@
"Please use this option carefully!": "Wees voorzichtig met deze optie!",
"Also check beta release": "Controleer ook op beta releases",
"Convert to Compose": "Converteer naar compose",
"External Networks": "Externe netwerken"
"External Networks": "Externe netwerken",
"newUpdate": "Nieuwe update",
"dockgeAgent": "Dockge Agent | Dockge Agenten",
"currentEndpoint": "Huidige",
"dockgeURL": "Dockge Adres (bijv. http://127.0.0.1:5001)",
"agentOnline": "Online",
"agentOffline": "Offline",
"connecting": "Verbinden",
"connect": "Verbind",
"addAgent": "Agent toevoegen",
"agentAddedSuccessfully": "Agent toegevoegd.",
"agentRemovedSuccessfully": "Agent verwijderd.",
"removeAgent": "Verwijder agent",
"removeAgentMsg": "Weet je zeker dat je deze agent wilt verwijderen?",
"LongSyntaxNotSupported": "Lange syntax wordt hier niet ondersteund. Gebruik de YAML editor."
}

View File

@@ -12,7 +12,7 @@
"registry": "Rejestr",
"compose": "Stwórz",
"addFirstStackMsg": "Stwórz swój pierwszy stos!",
"stackName" : "Nazwa stosu",
"stackName": "Nazwa stosu",
"deployStack": "Wdroż",
"deleteStack": "Usuń",
"stopStack": "Zatrzymaj",
@@ -22,7 +22,7 @@
"editStack": "Edytuj",
"discardStack": "Odrzuć",
"saveStackDraft": "Zapisz",
"notAvailableShort" : "N/A",
"notAvailableShort": "N/A",
"deleteStackMsg": "Czy na pewno chcesz usunąć ten stos?",
"stackNotManagedByDockgeMsg": "Ten stos nie jest zarządzany przez Dockge.",
"primaryHostname": "Podstawowa nazwa hosta",
@@ -90,5 +90,43 @@
"Allowed commands:": "Dozwolone polecenia:",
"Internal Networks": "Sieci wewnętrzne",
"External Networks": "Sieci zewnętrzne",
"No External Networks": "Brak sieci zewnętrznych"
"No External Networks": "Brak sieci zewnętrznych",
"newUpdate": "Nowa Aktualizacja",
"dockgeAgent": "Agent Dockge | Agenci Dockge",
"currentEndpoint": "Obecny",
"connecting": "Łączenie",
"connect": "Połącz",
"addAgent": "Dodaj Agenta",
"agentAddedSuccessfully": "Agent pomyślnie dodany.",
"agentRemovedSuccessfully": "Agent pomyślnie usunięty.",
"removeAgent": "Usuń Agenta",
"removeAgentMsg": "Czy na pewno usunąć tego Agenta?",
"dockgeURL": "Dockge URL (e.g. http://127.0.0.1:5001)",
"agentOnline": "Online",
"agentOffline": "Offline",
"downStack": "Zatrzymaj i Dezaktywuj",
"reverseProxyMsg1": "Używasz Serwer Proxy?",
"reverseProxyMsg2": "Sprawdź w jaki sposób skonfigurować dla WebSocketów",
"Cannot connect to the socket server.": "Brak połączenia z socket serwera.",
"connecting...": "Łączenie z socketem serwera…",
"extra": "Ekstra",
"url": "URL | URLe",
"reconnecting...": "Wznawianie połączenia…",
"LongSyntaxNotSupported": "Nieobsługiwana składnia. Użyj edytora YAML.",
"Saved": "Zapisano",
"Switch to sh": "Przełącz na sh",
"terminal": "Terminal",
"Restarted": "Zrestartowano",
"Deployed": "Wdrożono",
"Deleted": "Usunięto",
"Updated": "Zaktualizowano",
"Started": "Uruchomiono",
"Stopped": "Zatrzymano",
"Downed": "Położono",
"Lost connection to the socket server. Reconnecting...": "Utracono połączenie z socketem serwera. Ponawiam połączenie...",
"New Container Name...": "Nazwa nowego kontenera...",
"Network name...": "Nazwa sieci...",
"Select a network...": "Wybierz sieć...",
"NoNetworksAvailable": "Brak dostępnych sieci. Musisz najpierw dodać sieć wewnętrzną lub włączyć sieci zewnętrzne po prawej stronie.",
"CurrentHostname": "(Odznacze: Podążaj za aktualnym hostem)"
}

View File

@@ -92,11 +92,41 @@
"External Networks": "Redes externas",
"No External Networks": "Sem redes externas",
"reverseProxyMsg2": "Veja como configurar para WebSocket",
"downStack": "Parar & Encerrar",
"downStack": "Parar & Inativar",
"reverseProxyMsg1": "Utiliza proxy reverso?",
"Cannot connect to the socket server.": "Não é possível conectar ao socket server.",
"connecting...": "Conectando ao socket server…",
"url": "URL | URLs",
"extra": "Extra",
"reconnecting...": "Reconectando…"
"reconnecting...": "Reconectando…",
"newUpdate": "Nova Atualização",
"dockgeAgent": "Agente Dockge | Agentes Dockge",
"currentEndpoint": "Atual",
"dockgeURL": "Dockge URL (ex. http://127.0.0.1:5001)",
"agentOnline": "Online",
"agentOffline": "Offline",
"connecting": "Conectando",
"connect": "Conectar",
"addAgent": "Adicionar agente",
"agentAddedSuccessfully": "Agente adicionado com sucesso.",
"agentRemovedSuccessfully": "Agente removido com sucesso.",
"removeAgent": "Remover Agente",
"removeAgentMsg": "Tem certeza de que deseja remover este agente?",
"LongSyntaxNotSupported": "Sintaxe longa não é suportada aqui. Por favor, use o editor de YAML.",
"Lost connection to the socket server. Reconnecting...": "Conexão perdida com o servidor de socket. Reconectando...",
"Saved": "Salvo",
"Deployed": "Implantado",
"Deleted": "Excluído",
"Updated": "Alterado",
"Started": "Iniciado",
"Stopped": "Parado",
"Restarted": "Reiniciado",
"Downed": "Finalizado",
"Switch to sh": "Mudar para sh",
"terminal": "Terminal",
"CurrentHostname": "(Não definido: seguir o nome do host atual)",
"New Container Name...": "Nome do novo container...",
"Network name...": "Nome da rede...",
"Select a network...": "Selecione uma rede...",
"NoNetworksAvailable": "Nenhuma rede disponível. Você precisa adicionar redes internas ou habilitar redes externas no lado direito primeiro."
}

View File

@@ -12,7 +12,7 @@
"registry": "Registro",
"compose": "Compor",
"addFirstStackMsg": "Componha sua primeira pilha!",
"stackName" : "Nome da Pilha",
"stackName": "Nome da Pilha",
"deployStack": "Implantar",
"deleteStack": "Excluir",
"stopStack": "Parar",
@@ -22,7 +22,7 @@
"editStack": "Editar",
"discardStack": "Descartar",
"saveStackDraft": "Salvar",
"notAvailableShort" : "N/D",
"notAvailableShort": "N/D",
"deleteStackMsg": "Tem certeza de que deseja excluir esta pilha?",
"stackNotManagedByDockgeMsg": "Esta pilha não é gerenciada pelo Dockge.",
"primaryHostname": "Nome do Host Primário",
@@ -90,5 +90,27 @@
"Allowed commands:": "Comandos permitidos:",
"Internal Networks": "Redes Internas",
"External Networks": "Redes Externas",
"No External Networks": "Sem Redes Externas"
"No External Networks": "Sem Redes Externas",
"newUpdate": "Nova Atualização",
"currentEndpoint": "Atual",
"dockgeURL": "Dockge URL (e.g. http://127.0.0.1:5001)",
"agentOnline": "Online",
"agentOffline": "Offline",
"connecting": "Conectando",
"addAgent": "Adicionar Agente",
"agentAddedSuccessfully": "Agente adicionado com sucesso.",
"agentRemovedSuccessfully": "Agente removido com sucesso.",
"removeAgent": "Remover Agente",
"downStack": "Parar & Desativar",
"dockgeAgent": "Dockge Agente | Dockge Agentes",
"connect": "Conectar",
"removeAgentMsg": "Tem certeza de que deseja remover este agente?",
"reverseProxyMsg1": "Usando um Proxy Reverso?",
"reverseProxyMsg2": "Verifique para configurá-lo como WebSocket",
"Cannot connect to the socket server.": "Não é possível se conectar ao servidor socket.",
"url": "URL | URL's",
"extra": "Extra",
"reconnecting...": "Reconectando…",
"connecting...": "Conectando ao servidor de socket…",
"LongSyntaxNotSupported": "Sintaxes longas não são suportadas aqui. Por favor, utilize um editor YAML."
}

View File

@@ -86,11 +86,11 @@
"reverseProxyMsg1": "Folosești un proxy invers?",
"reverseProxyMsg2": "Verificați cum să-l configurați pentru WebSocket",
"Cannot connect to the socket server.": "Nu se poate conecta la serverul socket.",
"reconnecting...": "Reconectare...",
"connecting...": "Se conectează la serverul socket...",
"reconnecting...": "Reconectare",
"connecting...": "Se conectează la serverul socket",
"url": "URL | URLs",
"extra": "Suplimentar",
"downStack": "Opriți & Coborâți",
"downStack": "Opriți & Inactiv",
"saveStackDraft": "Salvați",
"restartPolicyUnlessStopped": "Dacă nu este oprit",
"environmentVariable": "Variabila de mediu | Variabile de mediu",
@@ -98,5 +98,35 @@
"Please use this option carefully!": "Vă rugăm să utilizați această opțiune cu atenție!",
"Show update if available": "Afișează actualizarea dacă este disponibilă",
"disableauth.message1": "Sigur doriți să <strong>dezactivați autentificarea</strong>?",
"disableauth.message2": "Este conceput pentru scenarii <strong>în care intenționați să implementați autentificarea terță</strong> în fața Dockge-lui, cum ar fi Cloudflare Access, Authelia sau alte mecanisme de autentificare."
"disableauth.message2": "Este conceput pentru scenarii <strong>în care intenționați să implementați autentificarea terță</strong> în fața Dockge-lui, cum ar fi Cloudflare Access, Authelia sau alte mecanisme de autentificare.",
"newUpdate": "Actualizare nouă",
"dockgeAgent": "Agent Dockge | Agenții Dockge",
"currentEndpoint": "Actual",
"dockgeURL": "Dockge URL (de ex. http://127.0.0.1:5001)",
"agentOnline": "Online",
"agentOffline": "Offline",
"connecting": "Se conectează",
"addAgent": "Adaugă Agent",
"connect": "Conectează",
"agentRemovedSuccessfully": "Agentul a fost eliminat cu succes.",
"removeAgent": "Șterge Agentul",
"removeAgentMsg": "Ești sigur că vrei să elimini acest agent?",
"LongSyntaxNotSupported": "Sintaxa lungă nu este acceptată aici. Vă rugăm să utilizați editorul YAML.",
"agentAddedSuccessfully": "Agentul a fost adăugat cu succes.",
"Lost connection to the socket server. Reconnecting...": "S-a pierdut conexiunea cu serverul socket. Se reconectează...",
"Saved": "Salvat",
"Deployed": "Lansat",
"Deleted": "Șters",
"Updated": "Actualizat",
"Started": "Pornit",
"Stopped": "Oprit",
"Restarted": "Repornit",
"Downed": "Coborât",
"Switch to sh": "Schimbă la sh",
"terminal": "Terminal",
"CurrentHostname": "(Nesetat: Urmăriți numele de host curent)",
"New Container Name...": "Nume nou de container...",
"Network name...": "Numele rețelei...",
"Select a network...": "Selectați o rețea...",
"NoNetworksAvailable": "Nu există rețele disponibile. Mai întâi trebuie să adăugați rețele interne sau să activați rețele externe în partea dreaptă."
}

View File

@@ -1,8 +1,8 @@
{
"languageName": "Русский",
"Create your admin account": "Создайте учетку администратора",
"Create your admin account": "Создайте учетную запись администратора",
"authIncorrectCreds": "Неверный логин или пароль.",
"PasswordsDoNotMatch": "Пароль не совпадает.",
"PasswordsDoNotMatch": "Пароли не совпадают.",
"Repeat Password": "Повторите пароль",
"Create": "Создать",
"signedInDisp": "Авторизован как {0}",
@@ -10,7 +10,7 @@
"home": "Главная",
"console": "Консоль",
"registry": "Реестр (Registry)",
"compose": "Составить (Compose)",
"compose": "Compose",
"addFirstStackMsg": "Создайте свой первый стек!",
"stackName": "Имя стека",
"deployStack": "Развернуть",
@@ -24,9 +24,9 @@
"saveStackDraft": "Сохранить",
"notAvailableShort": "Н/Д",
"deleteStackMsg": "Вы уверены что хотите удалить этот стек?",
"stackNotManagedByDockgeMsg": "Данный стек не обслуживается Dockge.",
"stackNotManagedByDockgeMsg": "Данный стек не управляется Dockge.",
"primaryHostname": "Имя хоста",
"general": "Главное",
"general": "Основные",
"container": "Контейнер | Контейнеры",
"scanFolder": "Сканировать папку стеков",
"dockerImage": "Образ",
@@ -43,12 +43,12 @@
"dependsOn": "Зависимость контейнера | Зависимости контейнера",
"addListItem": "Добавить {0}",
"deleteContainer": "Удалить",
"addContainer": "Добавить Контейнер",
"addNetwork": "Добавить Сеть",
"disableauth.message1": "Вы уверены что хотите <strong>выключить авторизацию</strong>?",
"disableauth.message2": "Он предназначен для сценариев, <strong>где вы собираетесь реализовать стороннюю аутентификацию</strong> перед Dockge, например Cloudflare Access, Authelia или другие механизмы аутентификации.",
"addContainer": "Добавить контейнер",
"addNetwork": "Добавить сеть",
"disableauth.message1": "Вы уверены что хотите <strong>отключить аутентификацию</strong>?",
"disableauth.message2": "Это предназначено для сценариев, <strong>когда Вы собираетесь использовать стороннюю аутентификацию</strong> перед Dockge, например Cloudflare Access, Authelia или другие механизмы аутентификации.",
"passwordNotMatchMsg": "Повторный пароль не совпадает.",
"autoGet": "Auto Get",
"autoGet": "Авто",
"add": "Добавить",
"Edit": "Изменить",
"applyToYAML": "Применить к YAML",
@@ -62,16 +62,16 @@
"New Password": "Новый пароль",
"Repeat New Password": "Повторите новый пароль",
"Update Password": "Обновить пароль",
"Advanced": "Продвинутые опции",
"Advanced": "Расширенные",
"Please use this option carefully!": "Пожалуйста, используйте эту опцию осторожно!",
"Enable Auth": "Включить аутентификацию",
"Disable Auth": "Отключить аутентификацию",
"I understand, please disable": "Я понимаю, пожалуйста, отключите",
"Leave": "Покинуть",
"Frontend Version": "Версия внешнего интерфейса",
"Check Update On GitHub": "Проверьте обновление на GitHub",
"Check Update On GitHub": "Проверить обновления на GitHub",
"Show update if available": "Показать обновление, если оно доступно",
"Also check beta release": "Также проверьте бета-версию",
"Also check beta release": "Получать бета-версии",
"Remember me": "Запомнить меня",
"Login": "Логин",
"Username": "Имя пользователя",
@@ -80,10 +80,10 @@
"Logout": "Выйти",
"Lowercase only": "Только нижний регистр",
"Convert to Compose": "Преобразовать в Compose",
"Docker Run": "Запустить Docker",
"active": "активный",
"exited": "завершенный",
"inactive": "неактинвый",
"Docker Run": "Docker Run",
"active": "акт.",
"exited": "ост.",
"inactive": "неакт.",
"Appearance": "Внешний вид",
"Security": "Безопасность",
"About": "О продукте",
@@ -91,13 +91,26 @@
"Internal Networks": "Внутренние сети",
"External Networks": "Внешние сети",
"No External Networks": "Нет внешних сетей",
"downStack": "Остановить и выключить",
"reverseProxyMsg1": "Использовать Реверс Прокси?",
"downStack": "Остановить и деактивировать",
"reverseProxyMsg1": "Используете обратный прокси?",
"reconnecting...": "Переподключение…",
"Cannot connect to the socket server.": "Не удается подключиться к серверу сокетов.",
"url": "URL адрес(а)",
"Cannot connect to the socket server.": "Не удается подключиться к сокет-серверу.",
"url": "URL-адрес | URL-адреса",
"extra": "Дополнительно",
"reverseProxyMsg2": "Проверьте, как настроить его для WebSocket",
"connecting...": "Подключение к серверу сокетов…",
"newUpdate": "Доступно обновление"
"connecting...": "Подключение к сокет-серверу…",
"newUpdate": "Доступно обновление",
"currentEndpoint": "Текущий",
"agentOnline": "В сети",
"agentOffline": "Не в сети",
"connecting": "Подключение",
"connect": "Подключить",
"addAgent": "Добавить Агента",
"agentAddedSuccessfully": "Агент успешно добавлен.",
"removeAgent": "Удалить агента",
"removeAgentMsg": "Вы уверены, что хотите удалить этого агента?",
"dockgeAgent": "Агент Dockge | Агенты Dockge",
"dockgeURL": "URL-адрес Dockge (например: http://127.0.0.1:5001)",
"agentRemovedSuccessfully": "Агент успешно удален.",
"LongSyntaxNotSupported": "Длинный синтаксис здесь не поддерживается. Пожалуйста, используйте редактор YAML."
}

View File

@@ -91,7 +91,7 @@
"Internal Networks": "Notranja omrežja",
"External Networks": "Zunanja omrežja",
"No External Networks": "Ni zunanjih omrežij",
"downStack": "Ustavi & Odstrani",
"downStack": "Ustavi & Deaktiviraj",
"connecting...": "Povezovanje s strežnikom…",
"reverseProxyMsg1": "Uporabljate obratni proxy?",
"extra": "Dodatno",
@@ -99,5 +99,18 @@
"newUpdate": "Nova posodobitev",
"reverseProxyMsg2": "Preverite, kako ga konfigurirati za WebSocket",
"Cannot connect to the socket server.": "Ni mogoče vzpostaviti povezave s strežnikom vtičnic.",
"url": "URL | URL-ji"
"url": "URL | URL-ji",
"currentEndpoint": "Trenutni",
"dockgeURL": "Dockge URL (npr. http://127.0.0.1:5001)",
"agentOnline": "Aktivno",
"agentOffline": "Neaktivno",
"connecting": "Povezujem",
"connect": "Poveži",
"addAgent": "Dodaj agenta",
"dockgeAgent": "Dockge agent | Dockge agenti",
"agentAddedSuccessfully": "Agent dodan uspešno.",
"agentRemovedSuccessfully": "Agent uspešno odstranjen.",
"removeAgent": "Odstrani agent",
"removeAgentMsg": "Ali ste prepričani, da želite odstraniti agenta?",
"LongSyntaxNotSupported": "Long syntax-a ni podprta tukaj. Prosim uporabite YAML urejevalnik."
}

View File

@@ -19,7 +19,7 @@
"restartStack": "Starta om",
"updateStack": "Uppdatera",
"startStack": "Starta",
"downStack": "Stoppa & Ner",
"downStack": "Stoppa & Inaktivera",
"editStack": "Redigera",
"discardStack": "Kasta",
"saveStackDraft": "Spara",
@@ -99,5 +99,34 @@
"url": "URL | URLer",
"extra": "Extra",
"reconnecting...": "Återansluter…",
"newUpdate": "Ny uppdatering"
"newUpdate": "Ny uppdatering",
"currentEndpoint": "Nuvarande",
"dockgeURL": "Dockge URL (ex. http://127.0.0.1:5001)",
"agentOnline": "On-line",
"agentOffline": "Off-line",
"connecting": "Ansluter",
"connect": "Ansluten",
"addAgent": "Lägg till agent",
"agentRemovedSuccessfully": "Agent borttagen.",
"removeAgent": "Ta bort agent",
"removeAgentMsg": "Är du säker att du vill ta bort denna agent?",
"dockgeAgent": "Dockge agenter | Dockge agenter",
"agentAddedSuccessfully": "Agent tillagd.",
"LongSyntaxNotSupported": "Lång syntax stöds inte här. Använd YAML-redigeraren.",
"Lost connection to the socket server. Reconnecting...": "Tappade anslutning till socket-server. Återansluter...",
"Saved": "Sparad",
"Deployed": "Uppsatt",
"Deleted": "Borttagen",
"Updated": "Uppdaterad",
"Started": "Startad",
"Stopped": "Stoppad",
"Restarted": "Omstartad",
"Downed": "Nedstängd",
"Switch to sh": "Byt till sh",
"terminal": "Terminal",
"CurrentHostname": "(Ej angedd: Följ nuvarande värdnamn)",
"New Container Name...": "Nytt kontainernamn...",
"Network name...": "Nätverksnamn...",
"Select a network...": "Välj ett nätverk...",
"NoNetworksAvailable": "Inga nätverk tillgängliga. Du måste lägga till interna nätverk eller aktivera externa nätverk på högra sidan först."
}

View File

@@ -1,17 +1,17 @@
{
"languageName": "ไทย",
"languageName": "อังกฤษ",
"Create your admin account": "สร้างบัญชีผู้ดูแลระบบของคุณ",
"authIncorrectCreds": "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง",
"PasswordsDoNotMatch": "รหัสผ่านไม่ตรงกัน",
"Repeat Password": "ยืนยันรหัสผ่าน",
"Create": "สร้าง",
"signedInDisp": "ลงชื่อเข้าใช้ในชื่อ {0}",
"signedInDisp": "ลงชื่อเข้าใช้ในนาม {0}",
"signedInDispDisabled": "ปิดใช้งาน Auth",
"home": "หน้าหลักe",
"home": "หน้าหลัก",
"console": "คอนโซล",
"registry": "Registry",
"compose": "Compose",
"addFirstStackMsg": "Compose stack แรกของคุณ",
"addFirstStackMsg": "Compose stack แรกของคุณ!",
"stackName": "ชื่อ Stack",
"deployStack": "ปรับใช้",
"deleteStack": "ลบ",
@@ -19,7 +19,7 @@
"restartStack": "เริ่มใหม่",
"updateStack": "อัปเดต",
"startStack": "เริ่มต้น",
"downStack": "หยุดและปิด",
"downStack": "หยุดการทำงาน",
"editStack": "แก้ไข",
"discardStack": "ยกเลิก",
"saveStackDraft": "บันทึก",
@@ -98,5 +98,19 @@
"connecting...": "กำลังเชื่อมต่อกับเซิร์ฟเวอร์ socket…",
"url": "URL | URLs",
"extra": "พิเศษ",
"reconnecting...": "กำลังเชื่อมต่อใหม่…"
"reconnecting...": "กำลังเชื่อมต่อใหม่…",
"newUpdate": "อัปเดตใหม่",
"dockgeAgent": "เอเย่นต์ Dockge | เอเย่นต์ Dockge",
"currentEndpoint": "ปัจุบัน",
"agentOnline": "ออนไลน์",
"agentOffline": "ออฟไลน์",
"connecting": "กำลังเชื่อมต่อ",
"connect": "เชื่อมต่อ",
"addAgent": "เพิ่มเอเย่นต์",
"agentAddedSuccessfully": "เพิ่มเอเย่นต์สำเร็จ",
"agentRemovedSuccessfully": "ลบเอเย่นต์สำเร็จ",
"removeAgent": "ลบเอเย่นต์",
"removeAgentMsg": "คุณแน่ใจหรือไม่ที่จะลบเอเย่นต์นี้?",
"dockgeURL": "ลิ้งก์ Dockge (เช่น http://127.0.0.1:5001)",
"LongSyntaxNotSupported": "Syntax แบบยาสไม่รองรับที่นี่ กรุณาใช้ตัวแก้ไข YAML"
}

View File

@@ -7,22 +7,22 @@
"Create": "Oluştur",
"signedInDisp": "{0} olarak oturum açıldı",
"signedInDispDisabled": "Yetkilendirme Devre Dışı.",
"home": "Anasayfa",
"home": "Ana Sayfa",
"console": "Konsol",
"registry": "Kayıt Defteri",
"compose": "Compose",
"compose": "Oluştur",
"addFirstStackMsg": "İlk yığınınızı oluşturun!",
"stackName": "Yığın Adı",
"deployStack": "Dağıtmak",
"deployStack": "Dağıt",
"deleteStack": "Sil",
"stopStack": "Dudur",
"stopStack": "Durdur",
"restartStack": "Yeniden Başlat",
"updateStack": "Güncelle",
"startStack": "Başlat",
"editStack": "Düzenle",
"discardStack": "Çıkar",
"discardStack": "İptal Et",
"saveStackDraft": "Kaydet",
"notAvailableShort": "N/A",
"notAvailableShort": "YOK",
"deleteStackMsg": "Bu yığını silmek istediğinizden emin misiniz?",
"stackNotManagedByDockgeMsg": "Bu yığın Dockge tarafından yönetilmemektedir.",
"primaryHostname": "Birincil Ana Bilgisayar Adı",
@@ -45,19 +45,19 @@
"deleteContainer": "Sil",
"addContainer": "Konteyner Ekle",
"addNetwork": "Ağ Ekle",
"disableauth.message1": "<strong>Kimlik doğrulamayı devre dışı</strong> bırakmak istediğinizden emin misiniz?",
"disableauth.message2": "Cloudflare Access, Authelia veya diğer kimlik doğrulama mekanizmaları gibi Uptime Kuma'nın önünde <strong>üçüncü taraf kimlik doğrulaması uygulamak</strong> istediğiniz senaryolar için tasarlanmıştır.",
"disableauth.message1": "<strong>Kimlik doğrulamayı devre dışı bırakmak</strong> istediğinizden emin misiniz?",
"disableauth.message2": "Cloudflare Access, Authelia veya diğer kimlik doğrulama mekanizmaları Dockge önünde <strong>üçüncü taraf kimlik doğrulaması uygulamak</strong> istediğiniz senaryolar için tasarlanmıştır.",
"passwordNotMatchMsg": "Tekrarlanan parola eşleşmiyor.",
"autoGet": "Otomatik Al",
"add": "Ekle",
"Edit": "Düzenle",
"applyToYAML": "YAML'ye uygulayın",
"applyToYAML": "YAML dosyasına uygula",
"createExternalNetwork": "Oluştur",
"addInternalNetwork": "Ekle",
"Save": "Kaydet",
"Language": "Dil",
"Current User": "Mevcut Kullanıcı",
"Change Password": "Mevcut Parola",
"Change Password": "Parolayı Değiştir",
"Current Password": "Mevcut Parola",
"New Password": "Yeni Parola",
"Repeat New Password": "Yeni Parolayı Tekrarla",
@@ -66,31 +66,31 @@
"Please use this option carefully!": "Lütfen bu seçeneği dikkatli kullanın!",
"Enable Auth": "Kimlik Doğrulamayı Etkinleştir",
"Disable Auth": "Kimlik Doğrulamayı Devre Dışı Bırak",
"I understand, please disable": "Anlıyorum, lütfen devre dışı bırakın",
"I understand, please disable": "Anlıyorum, lütfen devre dışı bırak",
"Leave": "Ayrıl",
"Frontend Version": "Frontend Versiyon",
"Frontend Version": "Ön Uç Sürümü",
"Check Update On GitHub": "GitHub'da Güncellemeyi Kontrol Edin",
"Show update if available": "Varsa güncellemeyi göster",
"Also check beta release": "Ayrıca beta sürümünü kontrol edin",
"Remember me": "Beni Hatırla",
"Remember me": "Beni hatırla",
"Login": "Oturum Aç",
"Username": "Kullanıcı Adı",
"Password": "Parola",
"Settings": "Ayarlar",
"Logout": "Oturumu Kapat",
"Lowercase only": "Yalnızca küçük harf",
"Convert to Compose": "Compose'a Dönüştür",
"Convert to Compose": "Compose dosyasına dönüştür",
"Docker Run": "Docker Run",
"active": "aktif",
"exited": ıkış yaptı",
"inactive": "aktif değil",
"active": "etkin",
"exited": ıktı",
"inactive": "devre dışı",
"Appearance": "Görünüm",
"Security": "Güvenlik",
"About": "Hakkında",
"Allowed commands:": "İzin verilen komutlar:",
"Internal Networks": "İç Ağlar",
"External Networks": "Dış Ağlar",
"No External Networks": "Dış Ağ Yok",
"Internal Networks": "Dahili Ağlar",
"External Networks": "Harici Ağlar",
"No External Networks": "Harici Ağ Yok",
"extra": "Ekstra",
"reverseProxyMsg1": "Ters Proxy mi kullanıyorsunuz?",
"reverseProxyMsg2": "WebSocket için nasıl yapılandırma yapılacağını kontrol edin",
@@ -98,5 +98,35 @@
"connecting...": "Soket sunucusuna bağlanıyor…",
"url": "URL | URLler",
"Cannot connect to the socket server.": "Soket sunucusuna bağlanılamıyor.",
"downStack": "Durdur & Kapat"
"downStack": "Durdur ve Devre Dışı Bırak",
"newUpdate": "Yeni Güncelleme",
"dockgeAgent": "Dockge Aracısı | Dockge Aracıları",
"currentEndpoint": "Varsayılan",
"dockgeURL": "Dockge URL (ör. http://127.0.0.1:5001)",
"agentOnline": "Çevrimiçi",
"agentOffline": "Çevrimdışı",
"connecting": "Bağlanıyor",
"connect": "Bağlan",
"addAgent": "Aracı Ekle",
"agentAddedSuccessfully": "Aracı başarıyla eklendi.",
"agentRemovedSuccessfully": "Aracı başarıyla kaldırıldı.",
"removeAgent": "Aracıyı Kaldır",
"removeAgentMsg": "Bu aracıyı kaldırmak istediğinize emin misiniz?",
"LongSyntaxNotSupported": "Uzun syntax burada desteklenmiyor. Lütfen YAML editörünü kullanın.",
"Lost connection to the socket server. Reconnecting...": "Soket sunucusuna bağlantı kesildi. Yeniden bağlanılıyor...",
"NoNetworksAvailable": "Kullanılabilir ağ yok. Önce dahili ağları eklemeniz veya sağ tarafta harici ağları etkinleştirmeniz gerekir.",
"Saved": "Kayıtlı",
"Deployed": "Deploy Edildi",
"Deleted": "Silindi",
"Updated": "Güncellendi",
"Started": "Başladı",
"Stopped": "Durdu",
"Restarted": "Yeniden başlatıldı",
"Downed": "Düştü",
"Switch to sh": "sh'ye çevir",
"terminal": "Terminal",
"CurrentHostname": "(Ayarlanmamış: Mevcut hostname'i takip et)",
"New Container Name...": "Yeni Konteyner Adı...",
"Network name...": "Ağ adı...",
"Select a network...": "Bir ağ seçin..."
}

View File

@@ -80,7 +80,7 @@
"Logout": "Вийти",
"Lowercase only": "Тільки нижній регістр",
"Convert to Compose": "Конвертувати в Compose",
"Docker Run": "Запустити Docker",
"Docker Run": "Docker Run",
"active": "активно",
"exited": "завершено",
"inactive": "неактивно",
@@ -99,5 +99,34 @@
"url": "URL-адреса | URL-адреси",
"reverseProxyMsg2": "Перевірте, як налаштувати його для WebSocket",
"extra": "Додатково",
"newUpdate": "Оновлення"
"newUpdate": "Оновлення",
"currentEndpoint": "Поточний",
"agentOnline": "Онлайн",
"agentOffline": "Офлайн",
"connecting": "Підключення",
"connect": "Підключитися",
"addAgent": "Додати агент",
"removeAgent": "Видалити агент",
"dockgeAgent": "Dockge-агент | Dockge-агенти",
"dockgeURL": "Dockge URL (напр. http://127.0.0.1:5001)",
"agentRemovedSuccessfully": "Агент успішно видалено.",
"agentAddedSuccessfully": "Агент успішно додано.",
"removeAgentMsg": "Ви впевнені, що хочете видалити цей агент?",
"LongSyntaxNotSupported": "Довгий синтаксис тут не підтримується. Будь ласка, використовуйте редактор YAML.",
"Saved": "Збережено",
"Deployed": "Розгорнуто",
"Deleted": "Видалено",
"Updated": "Оновлено",
"Started": "Запущено",
"Stopped": "Зупинено",
"Downed": "Вимкнено",
"Switch to sh": "Перемкнути на sh",
"terminal": "Термінал",
"New Container Name...": "Нова назва контейнера...",
"Network name...": "Назва мережі...",
"Select a network...": "Вибрати мережу...",
"Lost connection to the socket server. Reconnecting...": "Втрачено зв'язок з сервером сокетів. Повторне підключення...",
"Restarted": "Перезапущено",
"CurrentHostname": "(Не встановлено: використовувати поточну назву хосту)",
"NoNetworksAvailable": "Немає доступних мереж. Спочатку потрібно додати внутрішні мережі або увімкнути зовнішні мережі в правій частині."
}

View File

@@ -97,6 +97,20 @@
"connecting...": "ساکٹ سرور سے منسلک ہو رہا ہے…",
"url": "یو آر ایل | یو آر ایل",
"extra": "اضافی",
"downStack": "اسٹاپ اینڈ ڈاؤن",
"reverseProxyMsg2": "اسے WebSocket کے لیے ترتیب دینے کا طریقہ چیک کریں"
"downStack": "روکیں اور غیر فعال",
"reverseProxyMsg2": "اسے WebSocket کے لیے ترتیب دینے کا طریقہ چیک کریں",
"newUpdate": "نئی تازہ کاری",
"dockgeAgent": "ڈاکج ایجنٹ | ڈاکج ایجنٹس",
"currentEndpoint": "کرنٹ",
"dockgeURL": "Dockge URL (جیسے http://127.0.0.1:5001)",
"agentOnline": "آن لائن",
"agentOffline": "آف لائن",
"connecting": "جڑ رہا ہے",
"connect": "جڑیں",
"addAgent": "ایجنٹ شامل کریں",
"agentAddedSuccessfully": "ایجنٹ کامیابی کے ساتھ شامل ہو گیا۔",
"agentRemovedSuccessfully": "ایجنٹ کو کامیابی سے ہٹا دیا گیا۔",
"removeAgent": "ایجنٹ کو ہٹا دیں",
"removeAgentMsg": "کیا آپ واقعی اس ایجنٹ کو ہٹانا چاہتے ہیں؟",
"LongSyntaxNotSupported": "لمبا نحو یہاں تعاون یافتہ نہیں ہے۔ براہ کرم YAML ایڈیٹر استعمال کریں۔"
}

115
frontend/src/lang/vi.json Normal file
View File

@@ -0,0 +1,115 @@
{
"authIncorrectCreds": "Sai tên người dùng hoặc mật khẩu.",
"PasswordsDoNotMatch": "Mật khẩu không khớp.",
"Repeat Password": "Lặp Lại Mật Khẩu",
"Create": "Tạo",
"signedInDisp": "Đã đăng nhập với tư cách {0}",
"home": "Trang chủ",
"console": "Console",
"compose": "Compose",
"registry": "Registry",
"stackName": "Tên Stack",
"deployStack": "Triển khai",
"deleteStack": "Xoá",
"stopStack": "Dừng",
"restartStack": "Khởi động lại",
"signedInDispDisabled": "Đã Tắt Xác Thực Đăng Nhập.",
"startStack": "Bắt đầu",
"downStack": "Dừng & Ngưng hoạt động",
"editStack": "Chỉnh sửa",
"saveStackDraft": "Lưu",
"notAvailableShort": "N/A",
"deleteStackMsg": "Bạn có chắc chắn muốn xoá stack này?",
"primaryHostname": "Tên Host Chính",
"scanFolder": "Quét Thư Mục Stack",
"restartPolicyAlways": "Luôn Luôn",
"restartPolicyOnFailure": "Khi Có Lỗi",
"restartPolicyNo": "Không",
"environmentVariable": "Biến Môi Trường | Các Biến Môi Trường",
"restartPolicy": "Chính Sách Khởi Động Lại",
"containerName": "Tên Container",
"port": "Cổng | Cổng",
"addListItem": "Thêm {0}",
"deleteContainer": "Xoá",
"addContainer": "Thêm Container",
"addNetwork": "Thêm Mạng",
"passwordNotMatchMsg": "Mật khẩu nhập lại không khớp.",
"autoGet": "Tự Động Lấy",
"add": "Thêm",
"Edit": "Chỉnh sửa",
"applyToYAML": "Áp dụng cho YAML",
"createExternalNetwork": "Tạo",
"addInternalNetwork": "Thêm",
"Save": "Lưu",
"Language": "Ngôn ngữ",
"Current User": "Người Dùng Hiện Tại",
"Change Password": "Đổi Mật Khẩu",
"Current Password": "Mật Khẩu Hiện Tại",
"New Password": "Mật Khẩu Mới",
"Repeat New Password": "Nhập Lại Mật Khẩu Mới",
"Update Password": "Cập Nhật Mật Khẩu",
"Advanced": "Nâng cao",
"Please use this option carefully!": "Vui lòng sử dụng tuỳ chọn này cẩn thận!",
"Enable Auth": "Kích Hoạt Xác Thực Đăng Nhập",
"Disable Auth": "Vô Hiệu Xác Thực Đăng Nhập",
"I understand, please disable": "Tôi hiểu, vui lòng vô hiệu",
"Leave": "Rời",
"Frontend Version": "Phiên Bản Giao Diện Người Dùng",
"Check Update On GitHub": "Kiểm Tra Cập Nhật Trên Github",
"Also check beta release": "Kiểm tra cả bản phát hành beta",
"Remember me": "Ghi nhớ tôi",
"Login": "Đăng nhập",
"Username": "Tên người dùng",
"Password": "Mật khẩu",
"Settings": "Cài đặt",
"Logout": "Đăng xuất",
"Lowercase only": "Chỉ viết thường",
"Convert to Compose": "Chuyển đổi sang Compose",
"Docker Run": "Chạy Docker",
"active": "hoạt động",
"exited": "đã thoát",
"inactive": "không hoạt động",
"Security": "Bảo Mật",
"Appearance": "Giao Diện",
"About": "Về",
"Allowed commands:": "Các lệnh được cho phép:",
"Internal Networks": "Mạng Nội Bộ",
"External Networks": "Mạng Ngoại Vi",
"No External Networks": "Không Có Mạng Ngoại Vi",
"reverseProxyMsg1": "Đang sử dụng Reverse Proxy?",
"reverseProxyMsg2": "Xem cách để cấu hình nó cho WebSocket",
"Cannot connect to the socket server.": "Không thể kết nối tới máy chủ socket.",
"reconnecting...": "Đang kết nối lại…",
"connecting...": "Đang kết nối tới máy chủ socket…",
"url": "URL",
"extra": "Bổ sung",
"newUpdate": "Cập Nhật Mới",
"dockgeAgent": "Dockge Agent",
"currentEndpoint": "Đang sử dụng",
"dockgeURL": "URL của Dockge (v.d. http://127.0.0.1:5001)",
"agentOnline": "Trực tuyến",
"agentOffline": "Ngoại tuyến",
"connecting": "Đang kết nối",
"connect": "Kết nối",
"addAgent": "Thêm Agent",
"agentAddedSuccessfully": "Agent đã được thêm thành công.",
"agentRemovedSuccessfully": "Agent đã được xoá thành công.",
"removeAgent": "Xoá Agent",
"removeAgentMsg": "Bạn có chắc chắn muốn xoá agent này?",
"languageName": "Tiếng Việt",
"Create your admin account": "Tạo tài khoản admin của bạn",
"addFirstStackMsg": "Tạo stack đầu tiên của bạn!",
"volume": "Volume | Volume",
"updateStack": "Cập nhật",
"network": "Mạng | Mạng",
"discardStack": "Huỷ",
"stackNotManagedByDockgeMsg": "Stack này không được quản lý bởi Dockge.",
"dependsOn": "Container Phụ Thuộc | Các Container Phụ Thuộc",
"general": "Tổng Quan",
"disableauth.message1": "Bạn có chắc chắn muốn <strong>tắt xác thực đăng nhập</strong>?",
"container": "Container",
"disableauth.message2": "Nó được thiết kế trong hoàn cảnh <strong>mà bạn dự định triển khai xác thực đăng nhập bên thứ ba</strong> trước Dockge như là Cloudflare Access, Authelia hay các phương thức xác minh đăng nhập khác.",
"dockerImage": "Image",
"Show update if available": "Hiển thị cập nhật nếu có",
"restartPolicyUnlessStopped": "Trừ Khi Dừng Lại"
}

View File

@@ -98,5 +98,35 @@
"Cannot connect to the socket server.": "无法连接到socket服务器。",
"url": "网址 | 网址",
"extra": "额外",
"downStack": "停止并删除"
"downStack": "停止并置于非活动状态",
"newUpdate": "新版本",
"dockgeURL": "Dockge地址 (例如 http://127.0.0.1:5001)",
"agentOnline": "在线",
"agentOffline": "离线",
"connecting": "连接中",
"connect": "连接",
"dockgeAgent": "Dockge代理",
"currentEndpoint": "当前",
"addAgent": "添加代理",
"agentRemovedSuccessfully": "代理移除成功。",
"removeAgent": "移除代理",
"removeAgentMsg": "您确定要移除此代理?",
"agentAddedSuccessfully": "代理添加成功。",
"LongSyntaxNotSupported": "此处不支持Long syntax请使用YAML编辑器。",
"Lost connection to the socket server. Reconnecting...": "已断开socket服务器连接重新连接中...",
"Saved": "已保存",
"Deployed": "已部署",
"Deleted": "已删除",
"Updated": "已更新",
"Started": "已启动",
"Stopped": "已停止",
"Restarted": "已重启",
"Switch to sh": "切换至sh",
"terminal": "终端",
"CurrentHostname": "未设置:沿用当前主机名",
"New Container Name...": "新的容器名称...",
"Network name...": "网络名称...",
"Select a network...": "选择网络...",
"NoNetworksAvailable": "网络不可用.你需要在正确的方向先添加内部网络或者启用外部网络.",
"Downed": "已宕机"
}

View File

@@ -1,5 +1,5 @@
{
"languageName": "繁體中文(台灣)",
"languageName": "繁體中文 (台灣)",
"Create your admin account": "建立您的管理員帳號",
"authIncorrectCreds": "使用者名稱或密碼錯誤。",
"PasswordsDoNotMatch": "兩次輸入的密碼不一致。",
@@ -10,7 +10,7 @@
"home": "首頁",
"console": "主控台",
"registry": "映像倉庫",
"compose": "Compose",
"compose": "撰寫",
"addFirstStackMsg": "組合您的第一個堆疊!",
"stackName": "堆疊名稱",
"deployStack": "部署",
@@ -20,7 +20,7 @@
"updateStack": "更新",
"startStack": "啟動",
"editStack": "編輯",
"discardStack": "棄",
"discardStack": "棄",
"saveStackDraft": "儲存",
"notAvailableShort": "不可用",
"deleteStackMsg": "您確定要刪除這個堆疊嗎?",
@@ -46,7 +46,7 @@
"addContainer": "新增容器",
"addNetwork": "新增網路",
"disableauth.message1": "您確定要<strong>停用身份驗證</strong>嗎?",
"disableauth.message2": "該選項設計用於某些場景,<strong>例如在 Dockge 之上接入第三方認證</strong>,如 Cloudflare Access、Authelia 或其他證機制。如果您不清楚這個選項的作用,請不要停用驗證!",
"disableauth.message2": "該選項設計用於某些場景,<strong>例如在 Dockge 之介接接第三方身份驗證</strong>如 Cloudflare Access、Authelia 或其他身份驗證機制。",
"passwordNotMatchMsg": "兩次輸入的密碼不一致。",
"autoGet": "自動取得",
"add": "新增",
@@ -91,12 +91,42 @@
"Internal Networks": "內部網路",
"External Networks": "外部網路",
"No External Networks": "無外部網路",
"downStack": "停止",
"downStack": "停止及未啟動化",
"reverseProxyMsg1": "在使用反向代理嗎?",
"reverseProxyMsg2": "點擊這裡了解如何為 WebSocket 配置反向代理",
"Cannot connect to the socket server.": "無法連接到 Socket 伺服器。",
"reconnecting...": "重新連線中…",
"connecting...": "連線至 Socket 伺服器中…",
"url": "網址 | 網址",
"extra": "額外"
"extra": "額外",
"newUpdate": "新版本",
"currentEndpoint": "目前",
"dockgeURL": "Dockge URL例如http://127.0.0.1:5001",
"agentOnline": "線上",
"connecting": "正在連線",
"agentOffline": "離線",
"Lost connection to the socket server. Reconnecting...": "與伺服器斷線。正在重新連線...",
"dockgeAgent": "Dockge代理 | Dockge代理",
"Saved": "已儲存",
"Switch to sh": "切換到 sh",
"NoNetworksAvailable": "沒有可以使用的網路。您需要先在右側新增內部網路或啟用外部網路。",
"LongSyntaxNotSupported": "這裡不支援長語法。請使用 YAML 編輯器。",
"connect": "連接",
"addAgent": "新增代理",
"agentAddedSuccessfully": "代理新增成功。",
"agentRemovedSuccessfully": "代理刪除成功。",
"Deployed": "已佈署",
"Deleted": "已刪除",
"Updated": "已更新",
"Started": "開始",
"Stopped": "已停止",
"Restarted": "重新啟動",
"Downed": "斷線",
"terminal": "終端",
"CurrentHostname": "(取消設定:依據目前主機名稱)",
"New Container Name...": "新容器名稱...",
"Network name...": "網路名稱...",
"Select a network...": "選擇網路...",
"removeAgent": "刪除代理",
"removeAgentMsg": "您確定要刪除這個代理嗎?"
}

View File

@@ -131,10 +131,15 @@ export default defineComponent({
methods: {
endpointDisplayFunction(endpoint : string) {
if (endpoint) {
return endpoint;
} else {
return this.$t("currentEndpoint");
for (const [k, v] of Object.entries(this.$data.agentList)) {
if (endpoint) {
if (endpoint === v["endpoint"] && v["name"] !== "") {
return v["name"];
}
if (endpoint === v["endpoint"] && v["name"] === "" ) {
return endpoint;
}
}
}
},
@@ -203,7 +208,7 @@ export default defineComponent({
socket.on("disconnect", () => {
console.log("disconnect");
this.socketIO.connectionErrorMsg = "Lost connection to the socket server. Reconnecting...";
this.socketIO.connectionErrorMsg = `${this.$t("Lost connection to the socket server. Reconnecting...")}`;
this.socketIO.connected = false;
});
@@ -279,7 +284,6 @@ export default defineComponent({
});
socket.on("agentList", (res) => {
console.log(res);
if (res.ok) {
this.agentList = res.agentList;
}

View File

@@ -1,10 +1,10 @@
<template>
<transition name="slide-fade" appear>
<div>
<h1 v-if="isAdd" class="mb-3">Compose</h1>
<h1 v-if="isAdd" class="mb-3">{{ $t("compose") }}</h1>
<h1 v-else class="mb-3">
<Uptime :stack="globalStack" :pill="true" /> {{ stack.name }}
<span v-if="$root.agentCount > 1" class="agent-name">
<span v-if="$root.agentCount > 1 && endpoint !== ''" class="agent-name">
({{ endpointDisplay }})
</span>
</h1>
@@ -63,8 +63,8 @@
<!-- URLs -->
<div v-if="urls.length > 0" class="mb-3">
<a v-for="(url, index) in urls" :key="index" target="_blank" :href="url.url">
<span class="badge bg-secondary me-2">{{ url.display }}</span>
<a v-for="(urlItem, index) in urls" :key="index" target="_blank" :href="urlItem.url">
<span class="badge bg-secondary me-2">{{ urlItem.display }}</span>
</a>
</div>
@@ -98,8 +98,8 @@
<div class="mt-3">
<label for="name" class="form-label">{{ $t("dockgeAgent") }}</label>
<select v-model="stack.endpoint" class="form-select">
<option v-for="(agent, endpoint) in $root.agentList" :key="endpoint" :value="endpoint" :disabled="$root.agentStatusList[endpoint] != 'online'">
({{ $root.agentStatusList[endpoint] }}) {{ (endpoint) ? endpoint : $t("currentEndpoint") }}
<option v-for="(agent, agentEndpoint) in $root.agentList" :key="agentEndpoint" :value="agentEndpoint" :disabled="$root.agentStatusList[agentEndpoint] != 'online'">
({{ $root.agentStatusList[agentEndpoint] }}) {{ (agent.name !== '') ? agent.name : agent.url || $t("Current") }}
</option>
</select>
</div>
@@ -112,7 +112,7 @@
<div v-if="isEditMode" class="input-group mb-3">
<input
v-model="newContainerName"
placeholder="New Container Name..."
:placeholder="$t(`New Container Name...`)"
class="form-control"
@keyup.enter="addContainer"
/>
@@ -128,7 +128,11 @@
:name="name"
:is-edit-mode="isEditMode"
:first="name === Object.keys(jsonConfig.services)[0]"
:status="serviceStatusList[name]"
:serviceStatus="serviceStatusList[name]"
:dockerStats="dockerStats"
@start-service="startService"
@stop-service="stopService"
@restart-service="restartService"
/>
</div>
@@ -150,7 +154,7 @@
<!-- Combined Terminal Output -->
<div v-show="!isEditMode">
<h4 class="mb-3">Terminal</h4>
<h4 class="mb-3">{{ $t("terminal") }}</h4>
<Terminal
ref="combinedTerminal"
class="mb-3 terminal"
@@ -158,7 +162,7 @@
:endpoint="endpoint"
:rows="combinedTerminalRows"
:cols="combinedTerminalCols"
style="height: 350px;"
style="height: 315px;"
></Terminal>
</div>
</div>
@@ -167,16 +171,18 @@
<!-- YAML editor -->
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
<prism-editor
<code-mirror
ref="editor"
v-model="stack.composeYAML"
class="yaml-editor"
:highlight="highlighterYAML"
line-numbers :readonly="!isEditMode"
@input="yamlCodeChange"
@focus="editorFocus = true"
@blur="editorFocus = false"
></prism-editor>
:extensions="extensions"
minimal
wrap="true"
dark="true"
tab="true"
:disabled="!isEditMode"
:hasFocus="editorFocus"
@change="yamlCodeChange"
/>
</div>
<div v-if="isEditMode" class="mb-3">
{{ yamlError }}
@@ -186,15 +192,18 @@
<div v-if="isEditMode">
<h4 class="mb-3">.env</h4>
<div class="shadow-box mb-3 editor-box" :class="{'edit-mode' : isEditMode}">
<prism-editor
<code-mirror
ref="editor"
v-model="stack.composeENV"
class="env-editor"
:highlight="highlighterENV"
line-numbers :readonly="!isEditMode"
@focus="editorFocus = true"
@blur="editorFocus = false"
></prism-editor>
:extensions="extensionsEnv"
minimal
wrap="true"
dark="true"
tab="true"
:disabled="!isEditMode"
:hasFocus="editorFocus"
@change="yamlCodeChange"
/>
</div>
</div>
@@ -212,15 +221,6 @@
<NetworkInput />
</div>
</div>
<!-- <div class="shadow-box big-padding mb-3">
<div class="mb-3">
<label for="name" class="form-label"> Search Templates</label>
<input id="name" v-model="name" type="text" class="form-control" placeholder="Search..." required>
</div>
<prism-editor v-if="false" v-model="yamlConfig" class="yaml-editor" :highlight="highlighter" line-numbers @input="yamlCodeChange"></prism-editor>
</div>-->
</div>
</div>
@@ -229,7 +229,7 @@
</div>
<!-- Delete Dialog -->
<BModal v-model="showDeleteDialog" :okTitle="$t('deleteStack')" okVariant="danger" @ok="deleteDialog">
<BModal v-model="showDeleteDialog" :cancelTitle="$t('cancel')" :okTitle="$t('deleteStack')" okVariant="danger" @ok="deleteDialog">
{{ $t("deleteStackMsg") }}
</BModal>
</div>
@@ -237,13 +237,13 @@
</template>
<script>
import { highlight, languages } from "prismjs/components/prism-core";
import { PrismEditor } from "vue-prism-editor";
import "prismjs/components/prism-yaml";
import CodeMirror from "vue-codemirror6";
import { yaml } from "@codemirror/lang-yaml";
import { python } from "@codemirror/lang-python";
import { dracula as editorTheme } from "thememirror";
import { lineNumbers, EditorView, Decoration, ViewPlugin } from "@codemirror/view";
import { parseDocument, Document } from "yaml";
import "prismjs/themes/prism-tomorrow.css";
import "vue-prism-editor/dist/prismeditor.min.css";
import { RangeSetBuilder } from "@codemirror/state";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import {
COMBINED_TERMINAL_COLS,
@@ -257,8 +257,9 @@ import {
import { BModal } from "bootstrap-vue-next";
import NetworkInput from "../components/NetworkInput.vue";
import dotenv from "dotenv";
import { ref } from "vue";
const template = `version: "3.8"
const template = `
services:
nginx:
image: nginx:latest
@@ -271,17 +272,50 @@ const envDefault = "# VARIABLE=value #comment";
let yamlErrorTimeout = null;
let serviceStatusTimeout = null;
let prismjsSymbolDefinition = {
"symbol": {
pattern: /(?<!\$)\$(\{[^{}]*\}|\w+)/,
let dockerStatsTimeout = null;
// Highlight $VAR and ${VAR}
const variableHighlight = ViewPlugin.fromClass(class {
constructor(view) {
this.decorations = this.buildDecorations(view);
}
};
update(update) {
if (update.docChanged || update.viewportChanged) {
this.decorations = this.buildDecorations(update.view);
}
}
buildDecorations(view) {
const builder = new RangeSetBuilder();
for (const { from, to } of view.visibleRanges) {
const text = view.state.doc.sliceString(from, to);
const variableRegex = /\$\{?[A-Za-z0-9_]+\}?/g;
let match;
while ((match = variableRegex.exec(text)) !== null) {
const start = from + match.index;
const end = start + match[0].length;
builder.add(
start,
end,
Decoration.mark({ class: "cm-variable-highlight" })
);
}
}
return builder.finish();
}
}, {
decorations: v => v.decorations
});
export default {
components: {
NetworkInput,
FontAwesomeIcon,
PrismEditor,
CodeMirror,
BModal,
},
beforeRouteUpdate(to, from, next) {
@@ -290,10 +324,38 @@ export default {
beforeRouteLeave(to, from, next) {
this.exitConfirm(next);
},
setup() {
const editorFocus = ref(false);
const focusEffectHandler = (state, focusing) => {
editorFocus.value = focusing;
return null;
};
const extensions = [
editorTheme,
yaml(),
variableHighlight,
lineNumbers(),
EditorView.focusChangeEffect.of(focusEffectHandler)
];
const extensionsEnv = [
editorTheme,
python(),
variableHighlight,
lineNumbers(),
EditorView.focusChangeEffect.of(focusEffectHandler)
];
return {
extensions,
extensionsEnv,
editorFocus };
},
yamlDoc: null, // For keeping the yaml comments
data() {
return {
editorFocus: false,
jsonConfig: {},
envsubstJSONConfig: {},
yamlError: "",
@@ -306,15 +368,16 @@ export default {
},
serviceStatusList: {},
dockerStats: {},
isEditMode: false,
submitted: false,
showDeleteDialog: false,
newContainerName: "",
stopServiceStatusTimeout: false,
stopDockerStatsTimeout: false,
};
},
computed: {
endpointDisplay() {
return this.$root.endpointDisplayFunction(this.endpoint);
},
@@ -478,6 +541,7 @@ export default {
}
this.requestServiceStatus();
this.requestDockerStats();
},
unmounted() {
@@ -490,7 +554,19 @@ export default {
}, 5000);
},
startDockerStatsTimeout() {
clearTimeout(dockerStatsTimeout);
dockerStatsTimeout = setTimeout(async () => {
this.requestDockerStats();
}, 5000);
},
requestServiceStatus() {
// Do not request if it is add mode
if (this.isAdd) {
return;
}
this.$root.emitAgent(this.endpoint, "serviceStatusList", this.stack.name, (res) => {
if (res.ok) {
this.serviceStatusList = res.serviceStatusList;
@@ -501,9 +577,20 @@ export default {
});
},
requestDockerStats() {
this.$root.emitAgent(this.endpoint, "dockerStats", (res) => {
if (res.ok) {
this.dockerStats = res.dockerStats;
}
if (!this.stopDockerStatsTimeout) {
this.startDockerStatsTimeout();
}
});
},
exitConfirm(next) {
if (this.isEditMode) {
if (confirm("You are currently editing a stack. Are you sure you want to leave?")) {
if (confirm(this.$t("confirmLeaveStack"))) {
this.exitAction();
next();
} else {
@@ -518,7 +605,9 @@ export default {
exitAction() {
console.log("exitAction");
this.stopServiceStatusTimeout = true;
this.stopDockerStatsTimeout = true;
clearTimeout(serviceStatusTimeout);
clearTimeout(dockerStatsTimeout);
// Leave Combined Terminal
console.debug("leaveCombinedTerminal", this.endpoint, this.stack.name);
@@ -659,46 +748,6 @@ export default {
this.isEditMode = false;
},
highlighterYAML(code) {
if (!languages.yaml_with_symbols) {
languages.yaml_with_symbols = languages.insertBefore("yaml", "punctuation", {
"symbol": prismjsSymbolDefinition["symbol"]
});
}
return highlight(code, languages.yaml_with_symbols);
},
highlighterENV(code) {
if (!languages.docker_env) {
languages.docker_env = {
"comment": {
pattern: /(^#| #).*$/m,
greedy: true
},
"keyword": {
pattern: /^\w*(?=[:=])/m,
greedy: true
},
"value": {
pattern: /(?<=[:=]).*?((?= #)|$)/m,
greedy: true,
inside: {
"string": [
{
pattern: /^ *'.*?(?<!\\)'/m,
},
{
pattern: /^ *".*?(?<!\\)"|^.*$/m,
inside: prismjsSymbolDefinition
},
],
},
},
};
}
return highlight(code, languages.docker_env);
},
yamlToJSON(yaml) {
let doc = parseDocument(yaml);
if (doc.errors.length > 0) {
@@ -755,7 +804,7 @@ export default {
},
checkYAML() {
// TODO: implement validation
},
addContainer() {
@@ -786,6 +835,44 @@ export default {
this.stack.name = this.stack?.name?.toLowerCase();
},
startService(serviceName) {
this.processing = true;
this.$root.emitAgent(this.endpoint, "startService", this.stack.name, serviceName, (res) => {
this.processing = false;
this.$root.toastRes(res);
if (res.ok) {
this.requestServiceStatus(); // Refresh service status
}
});
},
stopService(serviceName) {
this.processing = true;
this.$root.emitAgent(this.endpoint, "stopService", this.stack.name, serviceName, (res) => {
this.processing = false;
this.$root.toastRes(res);
if (res.ok) {
this.requestServiceStatus(); // Refresh service status
}
});
},
restartService(serviceName) {
this.processing = true;
this.$root.emitAgent(this.endpoint, "restartService", this.stack.name, serviceName, (res) => {
this.processing = false;
this.$root.toastRes(res);
if (res.ok) {
this.requestServiceStatus(); // Refresh service status
}
});
},
}
};
</script>
@@ -797,12 +884,14 @@ export default {
height: 200px;
}
:deep(.cm-variable-highlight) {
color: #fe6000;
font-weight: 600;
}
.editor-box {
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
&.edit-mode {
background-color: #2c2f38 !important;
}
}
.agent-name {

View File

@@ -1,35 +1,28 @@
<template>
<transition name="slide-fade" appear>
<div>
<h1 class="mb-3">Console</h1>
<div v-if="!processing">
<h1 class="mb-3">{{ $t("console") }}</h1>
<div>
<p>
{{ $t("Allowed commands:") }}
<template v-for="(command, index) in allowedCommandList" :key="command">
<code>{{ command }}</code>
<Terminal v-if="enableConsole" class="terminal" :rows="20" mode="mainTerminal" name="console" :endpoint="endpoint"></Terminal>
<!-- No comma at the end -->
<span v-if="index !== allowedCommandList.length - 1">, </span>
</template>
</p>
<div v-else class="alert alert-warning shadow-box" role="alert">
<h4 class="alert-heading">{{ $t("Console is not enabled") }}</h4>
<p v-html="$t('ConsoleNotEnabledMSG1')"></p>
<p v-html="$t('ConsoleNotEnabledMSG2')"></p>
<p v-html="$t('ConsoleNotEnabledMSG3')"></p>
</div>
<Terminal class="terminal" :rows="20" mode="mainTerminal" name="console" :endpoint="endpoint"></Terminal>
</div>
</transition>
</template>
<script>
import { allowedCommandList } from "../../../common/util-common";
export default {
components: {
},
data() {
return {
allowedCommandList,
processing: true,
enableConsole: false,
};
},
computed: {
@@ -38,10 +31,13 @@ export default {
},
},
mounted() {
this.$root.emitAgent(this.endpoint, "checkMainTerminal", (res) => {
this.enableConsole = res.ok;
this.processing = false;
});
},
methods: {
}
};
</script>

View File

@@ -1,10 +1,10 @@
<template>
<transition name="slide-fade" appear>
<div>
<h1 class="mb-3">Terminal - {{ serviceName }} ({{ stackName }})</h1>
<h1 class="mb-3">{{$t("terminal")}} - {{ serviceName }} ({{ stackName }})</h1>
<div class="mb-3">
<router-link :to="sh" class="btn btn-normal me-2">Switch to sh</router-link>
<router-link :to="sh" class="btn btn-normal me-2">{{ $t("Switch to sh") }}</router-link>
</div>
<Terminal class="terminal" :rows="20" mode="interactive" :name="terminalName" :stack-name="stackName" :service-name="serviceName" :shell="shell" :endpoint="endpoint"></Terminal>

View File

@@ -29,7 +29,7 @@
<!-- Docker Run -->
<h2 class="mb-3">{{ $t("Docker Run") }}</h2>
<div class="mb-3">
<textarea id="name" v-model="dockerRunCommand" type="text" class="form-control docker-run" required placeholder="docker run ..."></textarea>
<textarea id="name" v-model="dockerRunCommand" type="text" class="form-control docker-run shadow-box" required placeholder="docker run ..."></textarea>
</div>
<button class="btn-normal btn mb-4" @click="convertDockerRun">{{ $t("Convert to Compose") }}</button>
@@ -49,13 +49,25 @@
</template>
<!-- Agent Display Name -->
<span v-if="endpoint === ''">{{ $t("currentEndpoint") }}</span>
<a v-else :href="agent.url" target="_blank">{{ endpoint }}</a>
<template v-if="$root.agentStatusList[endpoint]">
<span v-if="endpoint === '' && agent.name === ''" class="badge bg-secondary me-2">Current</span>
<span v-else-if="agent.name === ''" :href="agent.url" class="me-2">{{ endpoint }}</span>
<span v-else :href="agent.url" class="me-2">{{ agent.name }}</span>
</template>
<!-- Edit Name -->
<font-awesome-icon v-if="agent.name !== ''" icon="pen-to-square" @click="showEditAgentNameDialog[agent.name] = !showEditAgentNameDialog[agent.Name]" />
<!-- Edit Dialog -->
<BModal v-model="showEditAgentNameDialog[agent.name]" :no-close-on-backdrop="true" :close-on-esc="true" :okTitle="$t('Update Name')" okVariant="info" @ok="updateName(agent.url, agent.updatedName)">
<label for="Update Name" class="form-label">Current value: {{ $t(agent.name) }}</label>
<input id="updatedName" v-model="agent.updatedName" type="text" class="form-control" optional>
</BModal>
<!-- Remove Button -->
<font-awesome-icon v-if="endpoint !== ''" class="ms-2 remove-agent" icon="trash" @click="showRemoveAgentDialog[agent.url] = !showRemoveAgentDialog[agent.url]" />
<!-- Remoe Agent Dialog -->
<!-- Remove Agent Dialog -->
<BModal v-model="showRemoveAgentDialog[agent.url]" :okTitle="$t('removeAgent')" okVariant="danger" @ok="removeAgent(agent.url)">
<p>{{ agent.url }}</p>
{{ $t("removeAgentMsg") }}
@@ -81,6 +93,11 @@
<input id="password" v-model="agent.password" type="password" class="form-control" required autocomplete="new-password">
</div>
<div class="mb-3">
<label for="name" class="form-label">{{ $t("Friendly Name") }}</label>
<input id="name" v-model="agent.name" type="text" class="form-control" optional>
</div>
<button type="submit" class="btn btn-primary" :disabled="connectingAgent">
<template v-if="connectingAgent">{{ $t("connecting") }}</template>
<template v-else>{{ $t("connect") }}</template>
@@ -121,11 +138,14 @@ export default {
dockerRunCommand: "",
showAgentForm: false,
showRemoveAgentDialog: {},
showEditAgentNameDialog: {},
connectingAgent: false,
agent: {
url: "http://",
username: "",
password: "",
name: "",
updatedName: "",
}
};
},
@@ -199,6 +219,19 @@ export default {
});
},
updateName(url, updatedName) {
this.$root.getSocket().emit("updateAgent", url, updatedName, (res) => {
this.$root.toastRes(res);
if (res.ok) {
this.showAgentForm = false;
this.agent = {
updatedName: "",
};
}
});
},
getStatusNum(statusName) {
let num = 0;
@@ -286,7 +319,7 @@ export default {
}
},
},
}
};
</script>
@@ -326,7 +359,6 @@ table {
}
.docker-run {
background-color: $dark-bg !important;
border: none;
font-family: 'JetBrains Mono', monospace;
font-size: 15px;

View File

@@ -83,6 +83,9 @@ export default {
security: {
title: this.$t("Security"),
},
globalEnv: {
title: this.$t("GlobalEnv"),
},
about: {
title: this.$t("About"),
},

View File

@@ -14,6 +14,7 @@ const Settings = () => import("./pages/Settings.vue");
import Appearance from "./components/settings/Appearance.vue";
import General from "./components/settings/General.vue";
const Security = () => import("./components/settings/Security.vue");
const GlobalEnv = () => import("./components/settings/GlobalEnv.vue");
import About from "./components/settings/About.vue";
const routes = [
@@ -78,6 +79,10 @@ const routes = [
path: "security",
component: Security,
},
{
path: "globalEnv",
component: GlobalEnv,
},
{
path: "about",
component: About,

View File

@@ -36,7 +36,7 @@ textarea.form-control {
}
::placeholder {
color: $dark-font-color3 !important;
color: $dark-font-color3;
}
.incident a,
@@ -422,9 +422,8 @@ optgroup {
// Floating Label
.form-floating > .form-control:focus ~ label::after, .form-floating > .form-control:not(:placeholder-shown) ~ label::after, .form-floating > .form-control-plaintext ~ label::after, .form-floating > .form-select ~ label::after {
background-color: transparent;
}
.form-floating > label {
.dark & {
color: $dark-font-color3 !important;
@@ -594,9 +593,6 @@ optgroup {
color: $primary;
}
.prism-editor__textarea {
outline: none !important;
}
h5.settings-subheading::after {
content: "";
@@ -657,13 +653,6 @@ $shadow-box-padding: 20px;
}
}
.main-terminal {
.xterm-viewport {
border-radius: 10px;
background-color: $dark-bg !important;
}
}
code {
padding: .2em .4em;
margin: 0;
@@ -684,18 +673,25 @@ code {
color: $dark-font-color3;
}
// Vue Prism Editor bug - workaround
// https://github.com/koca/vue-prism-editor/issues/87
/*
.prism-editor__textarea {
width: 999999px !important;
.cm-gutters {
background-color: transparent !important;
}
.prism-editor__editor {
white-space: pre !important;
.dark [contenteditable="true"] {
background-color: transparent !important;
}
.cm-editor {
background-color: transparent !important;
}
.cm-activeLine, .cm-activeLineGutter {
background-color: transparent !important;
}
.cm-selectionBackground {
background-color: #74c2ff3d !important;
}
.cm-focused {
outline: none !important;
}
.prism-editor__container {
overflow-x: scroll !important;
}*/
// Localization
@import "localization.scss";

10697
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,65 +1,67 @@
{
"name": "dockge",
"version": "1.4.0-beta.0",
"version": "1.5.0",
"type": "module",
"engines": {
"node": ">= 18.0.0 && <= 18.17.1"
"node": ">= 22.14.0"
},
"scripts": {
"fmt": "eslint \"**/*.{ts,vue}\" --fix",
"lint": "eslint \"**/*.{ts,vue}\"",
"check-ts": "tsc --noEmit",
"start": "tsx ./backend/index.ts",
"dev": "concurrently -k -r \"wait-on tcp:5000 && pnpm run dev:backend \" \"pnpm run dev:frontend\"",
"dev": "concurrently -k -r \"wait-on tcp:5000 && npm run dev:backend \" \"npm run dev:frontend\"",
"dev:backend": "cross-env NODE_ENV=development tsx watch --inspect ./backend/index.ts",
"dev:frontend": "cross-env NODE_ENV=development vite --host --config ./frontend/vite.config.ts",
"release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && pnpm run build:frontend && npm run build:docker",
"release-beta": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && pnpm run build:frontend && npm run build:docker-beta",
"release-final": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && npm run build:frontend && npm run build:docker",
"release-beta": "tsx ./extra/test-docker.ts && tsx extra/update-version.ts && npm run build:frontend && npm run build:docker-beta",
"build:frontend": "vite build --config ./frontend/vite.config.ts",
"build:docker-base": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:base -f ./docker/Base.Dockerfile . --push",
"build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push",
"build:docker": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:latest -t louislam/dockge:1 -t louislam/dockge:$VERSION -t louislam/dockge:beta -t louislam/dockge:nightly --target release -f ./docker/Dockerfile . --push",
"build:docker-beta": "node ./extra/env2arg.js docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:beta -t louislam/dockge:$VERSION --target release -f ./docker/Dockerfile . --push",
"build:docker-nightly": "pnpm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
"build:healthcheck": "docker buildx build -f docker/BuildHealthCheck.Dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:build-healthcheck . --push",
"release-nightly": "npm run build:frontend && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/dockge:nightly -t ghcr.io/louislam/dockge:nightly --target nightly -f ./docker/Dockerfile . --push",
"start-docker": "docker run --rm -p 5001:5001 --name dockge louislam/dockge:latest",
"mark-as-nightly": "tsx ./extra/mark-as-nightly.ts",
"reformat-changelog": "tsx ./extra/reformat-changelog.ts",
"reset-password": "tsx ./extra/reset-password.ts"
},
"dependencies": {
"@homebridge/node-pty-prebuilt-multiarch": "~0.11.12",
"@homebridge/node-pty-prebuilt-multiarch": "0.11.14",
"@inventage/envsubst": "^0.16.0",
"@louislam/sqlite3": "~15.1.6",
"bcryptjs": "~2.4.3",
"check-password-strength": "~2.0.7",
"check-password-strength": "~2.0.10",
"command-exists": "~1.2.9",
"compare-versions": "~6.1.0",
"composerize": "~1.4.1",
"croner": "~7.0.5",
"dayjs": "~1.11.10",
"dotenv": "~16.3.1",
"express": "~4.18.2",
"express-static-gzip": "~2.1.7",
"http-graceful-shutdown": "~3.1.13",
"compare-versions": "~6.1.1",
"composerize": "~1.7.1",
"croner": "~8.1.2",
"dayjs": "~1.11.13",
"dotenv": "~16.3.2",
"express": "~4.21.2",
"express-static-gzip": "~2.1.8",
"http-graceful-shutdown": "~3.1.14",
"jsonwebtoken": "~9.0.2",
"jwt-decode": "~3.1.2",
"knex": "~2.5.1",
"limiter-es6-compat": "~2.1.2",
"mysql2": "~3.6.5",
"mysql2": "~3.12.0",
"promisify-child-process": "~4.1.2",
"redbean-node": "~0.3.3",
"semver": "^7.5.4",
"socket.io": "~4.7.2",
"socket.io-client": "~4.7.2",
"timezones-list": "~3.0.2",
"semver": "^7.7.1",
"socket.io": "~4.8.1",
"socket.io-client": "~4.8.1",
"timezones-list": "~3.0.3",
"ts-command-line-args": "~2.5.1",
"tsx": "~4.6.2",
"tsx": "~4.19.3",
"type-fest": "~4.3.3",
"yaml": "~2.3.4"
},
"devDependencies": {
"@actions/github": "^6.0.0",
"@fontsource/jetbrains-mono": "^5.0.18",
"@codemirror/lang-python": "^6.1.7",
"@codemirror/lang-yaml": "^6.1.2",
"@fontsource/jetbrains-mono": "^5.2.5",
"@fortawesome/fontawesome-svg-core": "6.4.2",
"@fortawesome/free-regular-svg-icons": "6.4.2",
"@fortawesome/free-solid-svg-icons": "6.4.2",
@@ -68,32 +70,33 @@
"@types/bootstrap": "~5.2.10",
"@types/command-exists": "~1.2.3",
"@types/express": "~4.17.21",
"@types/jsonwebtoken": "~9.0.5",
"@types/semver": "^7.5.6",
"@types/jsonwebtoken": "~9.0.9",
"@types/semver": "^7.7.0",
"@typescript-eslint/eslint-plugin": "~6.8.0",
"@typescript-eslint/parser": "~6.8.0",
"@vitejs/plugin-vue": "~4.5.2",
"@vitejs/plugin-vue": "~5.2.3",
"@xterm/addon-fit": "beta",
"@xterm/xterm": "beta",
"bootstrap": "5.3.2",
"bootstrap-vue-next": "~0.14.10",
"codemirror": "^6.0.1",
"concurrently": "^8.2.2",
"cross-env": "~7.0.3",
"eslint": "~8.50.0",
"eslint-plugin-jsdoc": "~46.8.2",
"eslint-plugin-vue": "~9.17.0",
"prismjs": "~1.29.0",
"eslint-plugin-vue": "~9.32.0",
"sass": "~1.68.0",
"thememirror": "^2.0.1",
"typescript": "~5.2.2",
"unplugin-vue-components": "~0.25.2",
"vite": "~5.0.10",
"vite": "~5.4.15",
"vite-plugin-compression": "~0.5.1",
"vue": "~3.3.13",
"vue": "~3.5.13",
"vue-codemirror6": "^1.3.13",
"vue-eslint-parser": "~9.3.2",
"vue-i18n": "~9.5.0",
"vue-prism-editor": "2.0.0-alpha.2",
"vue-qrcode": "~2.2.0",
"vue-router": "~4.2.5",
"vue-i18n": "~10.0.6",
"vue-qrcode": "~2.2.2",
"vue-router": "~4.5.0",
"vue-toastification": "2.0.0-rc.5",
"wait-on": "^7.2.0",
"xterm-addon-web-links": "~0.9.0"

5649
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff