Compare commits

...

311 Commits

Author SHA1 Message Date
Lucas Rattz 380ff5c2f1 Improve documentation on password hash (#1901)
* Improve documentation on password hash

* Change branch name

* Add single quote docker run info

* Tag versions, docker run version

* separate docker run and compose

---------

Co-authored-by: Bernd Storath <32197462+kaaax0815@users.noreply.github.com>
2025-06-03 09:58:52 +02:00
Bernd Storath ae540593fe fix iptables alias
!skipci
2025-05-31 21:29:43 +02:00
Bernd Storath 42adeb391c update dockerfile, update workflows
!skipci
2025-05-31 21:22:56 +02:00
Bernd Storath 02589a3ce9 early fail if old password variable (#1350) 2024-09-04 18:42:42 +02:00
Philip H. 0a6645b526 Version 14: Home Assistent support, PASSWORD_HASH (inc. Helper), translation updates, bugfixes and more (#1199) 2024-08-09 21:32:32 +02:00
NPM Update Bot a18a715f6f npm: package updates 2024-08-08 18:37:52 +00:00
Philip H. ec202d8575 fix: Status Bar Color Issue in PWA on iOS 18 (#1257)
#1256
Reported by @xK1t
2024-08-08 20:37:19 +02:00
Philip H 9dd7f256ba fix: Status Bar Color Issue in PWA on iOS 18
Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-08-08 20:33:01 +02:00
Philip H. 33e95bac5e Update Ukraine translation (#1251) 2024-08-07 17:23:09 +02:00
Nikolas 72fe64385e Update i18n.js 2024-08-07 17:20:38 +02:00
NPM Update Bot 2b7c846823 npm: package updates 2024-08-07 14:11:55 +00:00
Philip H. 9275cf611a Add autocomplete attribute to password input (#1249) 2024-08-07 16:11:25 +02:00
İbrahim Çetin 95934c6008 Add autocomplete attribute to password input 2024-08-07 10:29:33 +03:00
Philip H. babb9983aa chore: Update i18n.js Turkish translations (#1238)
Thank you @babico!
2024-08-03 09:22:51 +02:00
Müslüm Barış Korkmazer c9ff248011 chore: Update i18n.js Turkish translations 2024-08-02 22:49:56 +03:00
Philip H. f9edec0ac1 Create pull_request_template.md (#1226) 2024-08-02 19:48:08 +02:00
Philip H. eb0fa90cd0 Create pull_request_template.md 2024-08-02 19:47:30 +02:00
Philip H. 1607fd1562 fixup: docker-compose.dev.yml (#1236)
Improving compose file for dev.
Some variables are missed which lead to an error.
Fixes: #1234
2024-08-01 20:09:01 +02:00
Philip H. 5938474bf8 fixup: docker-compose.dev.yml
Improving compose file for dev.
Some variables are missed which lead to an error.
Fixes: #1234
2024-08-01 17:20:11 +02:00
Philip H. ff9c1b49b6 CODEOWNERS: add maintainer 2024-08-01 17:11:01 +02:00
Philip H. 72562dc660 Spelling in How_to_generate_an_bcrypt_hash.md (#1233) 2024-08-01 07:30:28 +02:00
kikawala 4ffa6b37de Spelling in How_to_generate_an_bcrypt_hash.md 2024-07-31 17:10:25 -05:00
Philip H. 1416613cc6 README.md: Reduce confusion in the readme 2024-07-30 17:57:15 +02:00
Philip H. a3c5cf359f remove PASSWORD env unused and replace by PASSWORD_HASH (#1229) 2024-07-30 17:47:18 +02:00
pheiduck d8a48aef29 remove PASSWORD env unused and replace by PASSWORD_HASH 2024-07-30 17:45:00 +02:00
NPM Update Bot 5dad038796 npm: package updates 2024-07-27 11:05:05 +00:00
Philip H. 63f49a20ed translations: Add missing Russian translations (#1219)
thank you @mcmimik!
2024-07-27 13:04:32 +02:00
Андрей 39949d2704 translations: Add missing Russian translations 2024-07-27 14:00:48 +03:00
Андрей 13fcccb2f2 translations: Add missing Russian translations 2024-07-27 13:50:18 +03:00
NPM Update Bot 200332df4b npm: package updates 2024-07-24 15:54:42 +00:00
Philip H. 3d0070f3f6 prepare: version bump and changelog (#1211) 2024-07-24 17:54:08 +02:00
pheiduck 14fd01f4d0 prepare: version bump and changelog
Signed-off-by: pheiduck <47042125+pheiduck@users.noreply.github.com>
2024-07-24 17:46:31 +02:00
Philip H. 52bcfb056a Get the version info inside the release object (#1208)
Fix "invalid reference format" in production Docker build
2024-07-23 07:47:07 +02:00
Dartegnian d5b8d707ef Get the version info inside the release object 2024-07-23 13:01:08 +08:00
Philip H. caad2e4162 fix: responsive buttons on mobile (#1206)
* fix: buttons on mobile
* fix: lint error with workspaces
* fix: redundant css
2024-07-22 13:49:46 +02:00
tetuaoro 3d376e542f fix: redundant css 2024-07-22 13:28:30 +02:00
tetuaoro 74372dc05d fix: lint error with workspaces 2024-07-22 13:05:18 +02:00
tetuaoro b46018efd8 fix: buttons on mobile 2024-07-22 12:44:53 +02:00
NPM Update Bot 2ef264d06f npm: package updates 2024-07-20 14:35:18 +00:00
Philip H. d23c5f7d01 Bugfix: Line Charts
Fixes: #1111
2024-07-20 16:34:46 +02:00
Sergei Birukov 73f2ad4ac3 Fix #1111 2024-07-20 17:20:54 +03:00
Philip H 5a075683c4 fix typo: How_to_generate_an_bcrypt_hash.md 2024-07-15 15:20:15 +02:00
Philip H c28e5befa6 feat: PASSWORD_HASH helpers (#1180)
* feat: generate PASSWORD_HASH on the fly

* remove PASSWORD environment variable in favor of PASSWORD_HASH
* enhance password validity check server function
* update Dockerfile to include building a binary for generating hashed password
* update README with comprehensive Docker usage instructions hash generation

* fix: try fix git action docker build

* Dockerfile: use alpine-base image and install required build packages

* rewrite in js

* move files

* fix: lint errors

* some corrections

---------

Co-authored-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-07-15 10:32:38 +02:00
NPM Update Bot 42ad29b494 npm: package updates 2024-07-15 00:03:14 +00:00
tetuaoro 53dad56bb6 some corrections 2024-07-14 18:33:11 +02:00
tetuaoro f5d93f6c5a fix: lint errors 2024-07-14 18:09:13 +02:00
tetuaoro a9c798deda move files 2024-07-14 17:59:52 +02:00
tetuaoro 781d56d0ff Merge branch 'wgpassword_js' into wgpassword 2024-07-14 17:20:47 +02:00
tetuaoro 883ca34182 rewrite in js 2024-07-14 17:13:27 +02:00
Philip H cc5d45b833 Dockerfile: use alpine-base image and install required build packages 2024-07-14 16:43:53 +02:00
tetuaoro 8bfcb5d502 fix: try fix git action docker build 2024-07-12 22:39:26 +02:00
tetuaoro 9a19430dc8 feat: generate PASSWORD_HASH on the fly
* remove PASSWORD environment variable in favor of PASSWORD_HASH
* enhance password validity check server function
* update Dockerfile to include building a binary for generating hashed password
* update README with comprehensive Docker usage instructions hash generation
2024-07-12 21:24:09 +02:00
NPM Update Bot 62ea932d33 npm: package updates 2024-07-11 13:46:35 +00:00
Philip H be8a592072 refactor: optimize build config, factorize code, enhance SVG icons (#1174)
* focus on syncing configuration without shutting down current wg session
	refactor build configuration logic to optimize code structure
* enhance SVG icons for better visual appeal (https://github.com/wg-easy/wg-easy/pull/1166#issuecomment-2222418606)
* update the screenshot to reflect the latest UI changes
* fix: prevent logging private key during user creation
2024-07-11 15:45:58 +02:00
tetuaoro 9371b78a21 refactor: optimize build config, factorize code, enhance SVG icons
* focus on syncing configuration without shutting down current wg session
	refactor build configuration logic to optimize code structure
* enhance SVG icons for better visual appeal (https://github.com/wg-easy/wg-easy/pull/1166#issuecomment-2222418606)
* update the screenshot to reflect the latest UI changes
* fix: prevent logging private key during user creation
2024-07-11 12:58:25 +02:00
Philip H 4ba638c09c i18n.js: fix typo 2024-07-10 18:55:24 +02:00
NPM Update Bot 3ad2607515 npm: package updates 2024-07-10 16:49:35 +00:00
Philip H 7f05448a5d import & export configuration (#1161,#1166)
* fix: auto formatter

* Revert "i18n.js: german translation"

This reverts commit e4a7ff08c6.

* fix conficts

* feat: load configuration from file

* import json config file & update the config (restore)
* export the config and save it to json file (backup)

* fix: reload configuration

* run linter
* screenshot update

* feat: support more langs

* add translations for French, Spanish, and Italian
* change the wording for better understanding of this feature:
    - "import" to "restore"
    - "export" to "backup"
* rename functions to reflect these changes

* i18n.js: german translation

* npm: package updates

* fix: icons & buttons view

* update the viewBox of svg elements
* add cursor pointer when hover the restore button
* rebuild the css

---------

Co-authored-by: tetuaoro <tetuaoropro@gmail.com>
Co-authored-by: tetuaoro <65575727+tetuaoro@users.noreply.github.com>
2024-07-10 18:48:33 +02:00
Philip H c5b3bcd31d fix: icons & buttons view (#1167)
* update the viewBox of svg elements
* add cursor pointer when hover the restore button
* rebuild the css
2024-07-10 18:40:15 +02:00
tetuaoro 10d24fa04b fix: icons & buttons view
* update the viewBox of svg elements
* add cursor pointer when hover the restore button
* rebuild the css
2024-07-10 18:11:28 +02:00
Philip H 3dc83f9c25 start fix pr 1161 (#1165)
* feat: load configuration from file

* import json config file & update the config (restore)
* export the config and save it to json file (backup)

* fix: reload configuration

* run linter
* screenshot update

* feat: support more langs

* add translations for French, Spanish, and Italian
* change the wording for better understanding of this feature:
    - "import" to "restore"
    - "export" to "backup"
* rename functions to reflect these changes

* i18n.js: german translation

* npm: package updates

* Revert "import & export configuration"

* npm: package updates

* Revert "import & export configuration"

* fix: auto formatter

* Revert "i18n.js: german translation"

This reverts commit e4a7ff08c6.

* fix conficts

---------

Co-authored-by: Philip H <47042125+pheiduck@users.noreply.github.com>
Co-authored-by: NPM Update Bot <npmupbot@users.noreply.github.com>
2024-07-10 15:51:38 +02:00
Philip H 1a54a0b016 Merge branch 'imexport' into imexport 2024-07-10 15:49:42 +02:00
NPM Update Bot a0ed35fd76 npm: package updates 2024-07-10 15:46:36 +02:00
Philip H 8421e313b5 i18n.js: german translation 2024-07-10 15:46:36 +02:00
tetuaoro ce20bb7fcb feat: support more langs
* add translations for French, Spanish, and Italian
* change the wording for better understanding of this feature:
    - "import" to "restore"
    - "export" to "backup"
* rename functions to reflect these changes
2024-07-10 15:46:36 +02:00
tetuaoro e3ee09b755 fix: reload configuration
* run linter
* screenshot update
2024-07-10 15:46:36 +02:00
tetuaoro 03b7d8e537 feat: load configuration from file
* import json config file & update the config (restore)
* export the config and save it to json file (backup)
2024-07-10 15:46:36 +02:00
tetuaoro e7d4bbc12c Merge branch 'master' into imexport 2024-07-10 15:14:41 +02:00
tetuaoro 45087a9683 fix conficts 2024-07-10 15:01:39 +02:00
tetuaoro 39d32b0a1c Revert "i18n.js: german translation"
This reverts commit e4a7ff08c6.
2024-07-10 14:44:40 +02:00
tetuaoro f47e740861 fix: auto formatter 2024-07-10 14:34:57 +02:00
Philip H ba85c085ab Revert "import & export configuration" (#1163) 2024-07-10 13:12:56 +02:00
Philip H 9aafbd73d2 Revert "import & export configuration" 2024-07-10 12:51:45 +02:00
NPM Update Bot 9efac11680 npm: package updates 2024-07-10 10:49:29 +00:00
Philip H e5131fb707 Revert "import & export configuration" (#1162) 2024-07-10 12:48:50 +02:00
Philip H abdf96011e Revert "import & export configuration" 2024-07-10 12:44:59 +02:00
NPM Update Bot 8b2706e3c2 npm: package updates 2024-07-10 10:38:02 +00:00
Philip H 6fe197f4fd import & export configuration (#1161)
* feat: load configuration from file

* import json config file & update the config (restore)
* export the config and save it to json file (backup)

* fix: reload configuration

* run linter
* screenshot update

* feat: support more langs

* add translations for French, Spanish, and Italian
* change the wording for better understanding of this feature:
    - "import" to "restore"
    - "export" to "backup"
* rename functions to reflect these changes

* i18n.js: german translation

---------

Co-authored-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-07-10 12:37:28 +02:00
Philip H e4a7ff08c6 i18n.js: german translation 2024-07-10 12:34:11 +02:00
tetuaoro 43b193b76d feat: support more langs
* add translations for French, Spanish, and Italian
* change the wording for better understanding of this feature:
    - "import" to "restore"
    - "export" to "backup"
* rename functions to reflect these changes
2024-07-10 12:06:25 +02:00
tetuaoro 4deca34faf fix: reload configuration
* run linter
* screenshot update
2024-07-09 19:49:40 +02:00
tetuaoro 72ba79b5f2 feat: load configuration from file
* import json config file & update the config (restore)
* export the config and save it to json file (backup)
2024-07-09 17:06:19 +02:00
NPM Update Bot 94d87681c3 npm: package updates 2024-07-09 15:01:53 +00:00
Philip H e3315d92c4 Fix a small typo in README.md (#1160)
Thank you @ThreadR-r!
2024-07-09 17:01:15 +02:00
Elian (ThreadR) Freyermuth a0c495ddad Fix a small typo in README.md
Changed from "Home Assistent" to "Home Assistant"
2024-07-09 14:11:07 +02:00
NPM Update Bot c73c6c7291 npm: package updates 2024-07-08 08:42:56 +00:00
Philip H 04d1ca18b7 Fix bcrypt hash one-liner (#1154)
Thank you @steviegalluscio!
2024-07-08 10:42:23 +02:00
Stevie Galluscio 438fc7ccf9 Fix bcrypt hash one-liner 2024-07-08 10:39:29 +02:00
Philip H a2c758dbcb How_to_generate_an_bcrypt_hash.md: macos homebrew 2024-07-07 19:15:57 +02:00
NPM Update Bot 6a588ee3fa npm: package updates 2024-07-07 17:13:42 +00:00
Philip H 378464a424 How_to_generate_an_bcrypt_hash.md: macos guide 2024-07-07 19:13:03 +02:00
Philip H 7ecf7b08b1 fixup: typos 2024-07-05 22:42:38 +02:00
Philip H cea9e9302d translations: Update Traditional Chinese translations (#1145)
Thank you @bluehomewu!
2024-07-04 19:02:19 +02:00
EdwardWu 11f5122c39 translations: Update Traditional Chinese translations
* Update strings closer than Taiwanese used.

Signed-off-by: EdwardWu <bluehome.wu@gmail.com>
2024-07-04 10:47:18 +08:00
NPM Update Bot ee117ddb91 npm: package updates 2024-07-01 00:03:23 +00:00
NPM Update Bot d3e8e627e9 npm: package updates 2024-06-28 10:37:56 +00:00
Philip H 864bb00d0b Enhance bcrypt doc: rename, add one-liner, usage info, and assert (#1138)
Thank you @mathys-lopinto!
2024-06-28 12:37:22 +02:00
Mathys Lopinto 7608f91913 Right README
Push the latest README.md
2024-06-27 21:06:15 +02:00
Mathys Lopinto 4d849fc508 Enhance bcrypt doc: rename, add one-liner, usage info, and assert
- Rename the file to a more readable name
- Add one-liner command for quick execution
- Include dedicated paragraph on using the output
- Implement assert to prevent bcrypt limitation issues
- Comment the python script
- Improves clarity and usability of bcrypt documentation
- Mention documentation file in docker-compose.yml and README.me file
2024-06-27 21:00:11 +02:00
NPM Update Bot 436ccac824 npm: package updates 2024-06-27 18:34:47 +00:00
Philip H b4ca454ec5 Add: Generate.bcrypt.hash.md (#1135)
Thank you @mathys-lopinto!
2024-06-27 20:34:05 +02:00
Philip H 8044c53815 add: Fedora and Arch Linux install guide 2024-06-27 10:18:09 +02:00
Philip H e7e374cfd7 comment: created by 2024-06-26 18:11:02 +02:00
Philip H 9afd549273 Add: Generate.bcrypt.hash.md
Thanks to: @mathys-lopinto
2024-06-26 13:03:14 +02:00
Philip H 6b67f20b6a build(deps): bump docker/build-push-action from 5 to 6 (#1134) 2024-06-24 10:34:41 +02:00
dependabot[bot] d31524a531 build(deps): bump docker/build-push-action from 5 to 6
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-24 04:23:33 +00:00
NPM Update Bot 34c28805c4 npm: package updates 2024-06-24 00:04:04 +00:00
Philip H 9b6ac7cd8d docker-compose.yml: PASSWORD_HASH is replacing PASSWORD
in nightly and next releases
2024-06-20 16:15:10 +02:00
Philip H 3613d26d4a docker-compose.yml: fixup every single $ has to be $$ 2024-06-20 14:43:17 +02:00
Philip H ccacc5ea87 docker-compose.yml: refinements 2024-06-20 14:37:12 +02:00
Philip H bfd7ef9e46 docker-compose.yml: fixup use '' for hashed Password
otherwise docker think it is an env variable and set it to blank
2024-06-20 12:24:57 +02:00
NPM Update Bot 2f956248db npm: package updates 2024-06-20 09:46:37 +00:00
Philip H e0e2a6deba docker-compose.yml: add missing PASSWORD_HASH option (#1121) 2024-06-20 11:46:03 +02:00
Philip H 304506d26d docker-compose.yml: add missing PASSWORD_HASH option 2024-06-20 11:43:23 +02:00
Peter Lewis e0775c0d2e Merge pull request #1117 from wg-easy/peterlewis-patch-1
README refinements
2024-06-18 22:38:04 +01:00
Peter Lewis 8598a167ef Update README.md
Refinement
2024-06-18 22:30:28 +01:00
Philip H 6659785514 feat: introduce PASSWORD_HASH and deprecate PASSWORD (#1116)
Thank you @RobertHeim!
2024-06-18 22:50:57 +02:00
Robert Heim 34ae8e42f3 fix: lint errors 2024-06-18 22:30:55 +02:00
Robert Heim eaa4b1ebaa feat: introduce PASSWORD_HASH and deprecate PASSWORD 2024-06-18 20:17:00 +02:00
Philip H 72fbf1baf6 README.md: add docs for WG_CONFIG_PORT 2024-06-17 15:22:54 +02:00
Philip H 2a102eea93 deploy-development.yml: disable pull request
until we can deploy on other branches from contributors/authors
2024-06-16 19:02:32 +02:00
Philip H 85913b71ed CI: no cache-dependency-path
it's useless and can lead to confusion
2024-06-16 16:25:51 +02:00
NPM Update Bot 7d0e2729b6 npm: package updates 2024-06-16 14:14:58 +00:00
Philip H b5372f0dbc bring password hash back
users want to have this instead cleartext password. Mitigates security issues.
2024-06-16 16:14:19 +02:00
NPM Update Bot 390b72c94a npm: package updates 2024-06-10 00:03:26 +00:00
Philip H de22768079 config: Add support for custom client port configuration (#1090)
Thank you @adriy-be!
This will enable homeassistant support
2024-06-08 11:47:52 +02:00
Philip H 00acd1a07e config: Add support for custom client port configuration (#1080)
merge for testing (1)
2024-06-07 12:55:14 +02:00
adrien a082a40bf6 config: Add support for custom client port configuration
This commit introduces the ability to specify a custom port for the client
configuration. This feature is particularly useful when the WireGuard server
is behind a port forwarding setup, allowing clients to connect using the
correct port number.

With this change, users can now define the desired client port in the
configuration file, ensuring seamless connectivity even in scenarios where
the client's listening port differs from the standard WireGuard port.
2024-06-07 12:53:54 +02:00
Philip H 3d1e42c722 fix typo in Server.js (#1089)
Thank you @Max-42!
2024-06-07 09:04:58 +02:00
Max e6d2d95340 fix typo in Server.js 2024-06-06 23:01:39 +02:00
NPM Update Bot 1370141f03 npm: package updates 2024-06-06 15:21:02 +00:00
Philip H ac552e0384 Readme: Inform Podman users about cap-add=NET_RAW (#1085)
Thank you @iguanajuice!
2024-06-06 17:20:26 +02:00
iguanajuice ca737f9452 Inform Podman users about cap-add=NET_RAW 2024-06-04 01:17:05 -04:00
NPM Update Bot b60f0e9668 npm: package updates 2024-06-02 13:20:07 +00:00
Philip H 0e1ad23f17 Everyting owned by WeeJeWel 2024-06-02 15:19:29 +02:00
Philip H 3638e81718 Update CODEOWNERS 2024-05-30 15:28:13 +02:00
NPM Update Bot 44417d3db6 npm: package updates 2024-05-30 11:48:00 +00:00
Philip H e5e63c43e6 package.json: fixup license identifier 2024-05-30 13:47:24 +02:00
NPM Update Bot f06b7e00aa npm: package updates 2024-05-30 07:31:06 +00:00
Philip H 70d59d0fdd cdnjs: apexcharts.min.js 3.49.1 (#1081) 2024-05-30 09:30:29 +02:00
Philip H 332039de56 cdnjs: apexcharts.min.js 3.49.1 2024-05-30 09:24:58 +02:00
Philip H 13616a2f1e Security vulnerability patched and minor improvements (#1071, #1072) 2024-05-27 21:06:22 +02:00
Philip H 5e015bfdb5 Security vulnerability patched and minor improvements (#1071)
Thank you so much @davide-acanfora!
2024-05-27 20:41:46 +02:00
davide-acanfora d2d15fca2a Path traversal vulnerability resolved 2024-05-27 20:25:00 +02:00
davide-acanfora c26b536b65 Remove unnecessary bcryptjs module usage 2024-05-27 20:25:00 +02:00
davide-acanfora 859dd2f25b Replace uuid module with built in crypto for UUIDv4 generation 2024-05-27 20:25:00 +02:00
davide-acanfora e80ff54ebc Don't print release number to anyone who visits the service 2024-05-27 20:25:00 +02:00
NPM Update Bot 4bfef3c0c0 npm: package updates 2024-05-27 00:03:06 +00:00
NPM Update Bot 93d9f0b6fe npm: package updates 2024-05-24 19:35:49 +00:00
Philip H 54236eb8b4 Documentation: image tags and UI_CHART_TYPE (#1057) 2024-05-24 21:35:11 +02:00
Philip H 8249b92a34 fixup: add UI_CHART_TYPE docs 2024-05-24 21:32:17 +02:00
Philip H 4cd5d5459a Documentation: docker image versions 2024-05-23 11:32:13 +02:00
NPM Update Bot 678cf5bffb npm: package updates 2024-05-23 07:46:21 +00:00
Philip H ad80017846 Dockerfile: remove unused parts
We expose ports another way
2024-05-23 09:45:37 +02:00
Philip H 975c61df6d fixup: desktop example iOS -> macOS 2024-05-20 12:52:18 +02:00
Philip H 69726cba75 fixup: WG_PORT in config.js (#1056)
patch for v13
2024-05-18 14:15:19 +02:00
Philip H 211e0b6aa2 Update README.md 2024-05-18 14:13:03 +02:00
taohua 8df1b6ff54 Update config.js
should respect WG_PORT, not hard code 51820.
2024-05-18 14:13:03 +02:00
Philip H cf94b98482 fixup: Server.js for instance without docker (#1044)
patch for v13
2024-05-16 20:21:37 +02:00
NPM Update Bot 3844d04569 npm: package updates 2024-05-16 20:19:22 +02:00
Philip H 68c6f6252e fixup: Server.js for instance without docker
Some users can no longer access the Web UI since the release of v13.
2024-05-16 20:19:22 +02:00
Philip H 519f4efa20 Version 13: new framework, UI_CHART_TYPE, some bugfixes and more (#967)
Thanks to all contributors to get us able to release v13!
2024-05-13 20:44:16 +02:00
NPM Update Bot c6dd456a07 npm: package updates 2024-05-13 00:03:03 +00:00
NPM Update Bot a43d2201fc npm: package updates 2024-05-09 18:01:15 +00:00
Philip H 86146ccc68 Dockerfile: ensure to use latest npm cli 2024-05-09 20:00:36 +02:00
NPM Update Bot 191dd74b0c npm: package updates 2024-05-09 15:29:08 +00:00
Philip H e2eb7bc362 avoid warnings on ci
as we support nodejs latest lts version on instance without docker
2024-05-09 17:28:28 +02:00
NPM Update Bot b60461e917 npm: package updates 2024-05-09 11:21:47 +00:00
Philip H fb628bcb89 Update npm to patched version of latest nodejs lts 2024-05-09 13:21:09 +02:00
NPM Update Bot 195e307ff5 npm: package updates 2024-05-04 19:00:17 +00:00
Philip H e46efd6088 package.json: rollback nodejs 18
because of build hang on newer nodejs version stick to nodejs 18
2024-05-04 20:59:36 +02:00
NPM Update Bot 66bb13ed30 npm: package updates 2024-05-04 13:19:02 +00:00
Philip H. 34fdc313ae docker-compose: version is obsolete 2024-05-04 13:18:20 +00:00
NPM Update Bot 9b5b8c77c3 npm: package updates 2024-04-30 17:53:00 +00:00
Philip H 7d05a82dae Improved local development and Dockerfile (#1015)
Thanks @davide-acanfora
2024-04-30 19:52:20 +02:00
Philip H a25d28755c Improved local development and Dockerfile (#1014)
Thanks @davide-acanfora
2024-04-30 19:35:13 +02:00
davide-acanfora cada04ad0e Use the newer Docker Compose 2024-04-30 17:04:33 +02:00
davide-acanfora 6b2f57f2f1 Added nodemon as a dev dependency and removed unnecessary instructions from the Dockerfile 2024-04-30 17:03:48 +02:00
davide-acanfora 488e3c32b3 Fix typo in code comments 2024-04-30 17:00:53 +02:00
NPM Update Bot 4cc07c5312 npm: package updates 2024-04-29 00:03:04 +00:00
Philip H c5d328be2a fixup: Dockerfile 2024-04-27 18:55:06 +02:00
Philip H 155541fbdb fixup: add timeout otherwise it reports unhealthy 2024-04-27 18:44:05 +02:00
Philip H 3f763d2607 implement healthcheck into Dockerfile (#1009)
Thanks @CosasDePuma
2024-04-27 18:39:40 +02:00
Philip H f9656d0bfe healthcheck is integrated internally 2024-04-27 18:35:22 +02:00
Philip H 83408f6a9b add healthcheck into Dockerfile
Thanks @CosasDePuma
2024-04-27 18:34:20 +02:00
NPM Update Bot 4911082a34 npm: package updates 2024-04-27 16:19:23 +00:00
Philip H a3a69654fc docker-compose.yml: add healthcheck
Thanks @CosasDePuma
2024-04-27 18:18:51 +02:00
Philip H 4f8ee27d77 Create CODEOWNERS 2024-04-25 12:24:48 +02:00
NPM Update Bot 1e2da39a87 npm: package updates 2024-04-20 16:06:25 +00:00
Philip H e153998d29 Dockerfile: bump to nodejs 20 (#999) 2024-04-20 18:05:41 +02:00
Philip H 6b284cb27c Dockerfile: add note 2024-04-19 17:12:07 +02:00
Philip H e6db5e8b7f Dockerfile: missing operator 2024-04-19 17:12:07 +02:00
Philip H 6e7f2f730e Dockerfile: move all npm steps 2024-04-19 17:12:07 +02:00
Philip H c8224f34f9 Dockerfile: use nodejs 18 for build
workaround to get nodejs 20 working on older arm architectures
2024-04-19 17:12:07 +02:00
pheiduck 3032163814 bump to node 20 2024-04-19 17:12:07 +02:00
Philip H 2eec97ff25 Bugfix: differnt Ports usage (#973) 2024-04-19 17:11:50 +02:00
Philip H ae60493414 Merge branch 'master' into issue-972 2024-04-18 19:00:07 +02:00
Philip H e43688a091 npm: revert update-browserslist-db
is not needed
2024-04-18 18:50:29 +02:00
Philip H c29ba35d41 npm: update-browserslist-db 2024-04-18 18:47:52 +02:00
Peter Lewis a05b71c652 Update README.md
https://github.com/wg-easy/wg-easy/commit/2a3acdcad5b7c73feccdd47e4f3c5d743593e393#r141068572
2024-04-17 21:39:08 +01:00
Philip H e6b5f2e33c fixup: README 2024-04-17 19:22:07 +02:00
Philip H 187888e078 WireGuard.js: fixup ListenPort 2024-04-14 19:51:04 +02:00
Philip H f9daa3e5be Bugfix: differnt Ports usage 2024-04-14 19:51:04 +02:00
Philip H c2482f494a [combine] gitignore
Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-04-14 19:16:41 +02:00
Philip H ed369ff199 Revert "feat: cidr notation" (#969) 2024-04-06 15:21:41 +02:00
Philip H 2a3acdcad5 Merge branch 'master' into revert-888-feat-cidr-notation 2024-04-06 15:17:43 +02:00
Philip H 9be036aefa Merge branch 'production' into master 2024-04-05 19:04:13 +02:00
Philip H 9507454d3f [fixup] Grammar
Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-04-05 19:02:44 +02:00
Philip H 9e925c2ebb [prepare] version bump to 13 and updated changelog
Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-04-05 18:59:41 +02:00
Philip H 31c34367b2 Fix comment in docker-compose.yml (#960) 2024-04-03 12:34:28 +02:00
Michael van Tricht 990a7ae548 Fix comment in docker-compose.yml 2024-04-03 12:31:02 +02:00
Philip H 6dc75b7f1e i18n.js: complete words in Spanish (#956) 2024-04-01 23:39:35 +02:00
Rubén León 4868f32f1e i18n.js: complete words in Spanish 2024-04-01 23:16:57 +02:00
Philip H 61b57a885c README.md: make commands easier to copy 2024-04-01 18:04:29 +02:00
NPM Update Bot 074b3548d2 npm: package updates 2024-03-31 17:37:34 +00:00
Philip H 2dafeae54d Fix WG_DEFAULT_ADDRESS if not set (#951) 2024-03-31 19:36:57 +02:00
Philip H 2d7460f35a fix default WG_DEFAULT_ADDRESS issue (#948) 2024-03-31 19:32:22 +02:00
Utkarsh Goel 4981b72d00 fix unambiguous boolean operators 2024-03-27 22:23:37 +08:00
Utkarsh Goel 33634211a9 check for empty WG_DEFAULT_ADDRESS 2024-03-27 22:19:27 +08:00
Philip H 6045c2ada5 fix backward compatibility issue (#946) 2024-03-27 15:09:10 +01:00
Philip H bf214fb4d3 Revert "feat: cidr notation" 2024-03-27 14:41:31 +01:00
Philip H 9332e8b663 fix backward compatibility issue (#945)
in #888
fixes #943
2024-03-27 14:33:18 +01:00
Utkarsh Goel dbbfdd5357 Add backward compatibility for WG_DEFAULT_ADDRESS 2024-03-27 21:27:43 +08:00
NPM Update Bot cb63d5c67f npm: package updates 2024-03-26 16:40:32 +00:00
Philip H 3fce6e8d1d feat: cidr notation (#888) 2024-03-26 17:39:56 +01:00
Philip H 2f9364aa31 wg-easy.service: add missing WG_DEFAULT_ADDRESS_RANGE 2024-03-25 18:31:06 +01:00
Philip H 479c51d741 WG_DEFAULT_ADDRESS_RANGE / is not needed 2024-03-25 17:18:14 +01:00
Philip H 196cb63c6e README.md: add WG_DEFAULT_ADDRESS_RANGE 2024-03-25 17:15:30 +01:00
Philip H d024018bba fix cidr block calculation issue, fix post down config (#939)
Thanks for your work @utkarsh867!
2024-03-25 16:54:22 +01:00
Utkarsh Goel 5cdacd6cc3 Fix CIDR block calculation issue, fix POST_DOWN config 2024-03-25 23:28:36 +08:00
Philip H. fe7d77e481 fixup: packages 2024-03-23 20:34:43 +00:00
Philip H 6c0049770e Merge branch 'master' into feat-cidr-notation 2024-03-23 21:30:52 +01:00
Philip H f134a3671a fix: add Content-Type header for static files (#933) 2024-03-23 20:15:20 +01:00
cany748 8d00c5456a fix: add Content-Type header for static files 2024-03-23 19:37:33 +01:00
NPM Update Bot c2829d79e0 npm: package updates 2024-03-23 18:36:33 +00:00
Philip H ac47789561 revert: Workaround CVE-2023-42282
newest images have updated ip package
2024-03-23 19:35:58 +01:00
NPM Update Bot 5afb701013 npm: package updates 2024-03-21 10:42:32 +00:00
Philip H 81cae5e231 Use one command for updating with Docker Compose (#929) 2024-03-21 11:42:00 +01:00
Edgars 6c567d0082 Use one command for updating with Docker Compose
`README.md` was updated to use a one-liner to update WireGuard Easy with
Docker Compose.
A note about image tag was added to avoid confusion when one is
specified in Compose file and it is other than `latest`, as that would
result in no pull and no WireGuard Easy container recreation.
2024-03-21 09:02:08 +02:00
NPM Update Bot 62703ffbd6 npm: package updates 2024-03-20 12:38:50 +00:00
Philip H 19589e7ee7 refactor!: migrate from express to h3 (#914) 2024-03-20 13:38:14 +01:00
NPM Update Bot d40536c3fb npm: package updates 2024-03-19 21:10:54 +00:00
Philip H f979d23704 Add Chart and Auto/Light/Dark mode toggles (#925)
A banging contribution from @suxscribe. Many thanks!
2024-03-19 22:10:22 +01:00
Philip H 99c8081fe9 Add Chart and Auto/Light/Dark mode toggles (#924) 2024-03-19 21:24:14 +01:00
suxscribe 1c98e466c6 Merge branch 'feat-stats' into feat-toggles 2024-03-19 18:34:41 +03:00
Philip H 953a67bbdd fixup: Server.js 2024-03-19 14:18:29 +01:00
Philip H 5fbfb26937 fixup: Server.js 2024-03-19 14:16:44 +01:00
Philip H 74f3e2f320 fixup: Server.js 2024-03-19 14:11:03 +01:00
Philip H c107920df2 Merge branch 'master' into feat-h3 2024-03-19 14:09:42 +01:00
NPM Update Bot e666a14612 npm: package updates 2024-03-19 12:54:01 +00:00
Philip H a7ecb2a067 Fix TX/RX charts. Add UI_CHART_TYPE (#915) 2024-03-19 13:53:28 +01:00
Philip H 88b1b20e48 docker-compose.yml: add UI_CHART_TYPE 2024-03-18 21:25:08 +01:00
Sergei Birukov ac6a05f9be Fix lint errors 2024-03-18 21:25:08 +01:00
Sergei Birukov 262318df27 Disable debug 2024-03-18 21:25:08 +01:00
Sergei Birukov 98a5daf458 Fix charts on mobile 2024-03-18 21:25:08 +01:00
Sergei Birukov 71c208133d Remove .env from repo 2024-03-18 21:25:08 +01:00
Sergei Birukov aedb691b2b Fix traffic charts. Add chart vars
Add UI_TRAFFIC_STATS, UI_CHART_TYPE
2024-03-18 21:25:08 +01:00
Philip H ed0e46788a Update deploy.yml 2024-03-18 21:25:08 +01:00
Philip H 90bcdd1259 deploy.yml: rebuild v11
mistake
2024-03-18 21:25:08 +01:00
Sergei Birukov 81ccf8762d Header mobile layout 2024-03-18 10:05:12 +03:00
NPM Update Bot 90431ff9c5 npm: package updates 2024-03-18 00:03:46 +00:00
Sergei Birukov 32fc78589a Lint 2024-03-17 20:38:21 +03:00
Sergei Birukov f3a8ff6490 Add dark/light mode toggle. Add chart toggle
Change dark/light mode detection
Add saving uiShowCharts to local storage
Add saving uiTheme to local storage
Add ui buttons
2024-03-17 20:38:21 +03:00
Philip H f7bd362538 fixup: Server.js 2024-03-15 15:23:12 +01:00
Philip H 44cc5683d4 Merge branch 'master' into feat-h3 2024-03-15 15:22:17 +01:00
Philip H 4d5a5c9e0d docker-compose.yml: add UI_CHART_TYPE 2024-03-15 15:20:35 +01:00
Sergei Birukov 12b72cf389 Fix lint errors 2024-03-15 15:20:35 +01:00
Sergei Birukov ccde2fdfd3 Disable debug 2024-03-15 15:20:35 +01:00
Sergei Birukov 166a58a685 Fix charts on mobile 2024-03-15 15:20:35 +01:00
Sergei Birukov 73242c61c3 Remove .env from repo 2024-03-15 15:20:35 +01:00
Sergei Birukov 3d11730926 Fix traffic charts. Add chart vars
Add UI_TRAFFIC_STATS, UI_CHART_TYPE
2024-03-15 15:20:35 +01:00
Philip H 8ce2d44c08 Update deploy.yml 2024-03-15 15:20:35 +01:00
Philip H 3cb83dc53a deploy.yml: rebuild v11
mistake
2024-03-15 15:20:35 +01:00
NPM Update Bot e2242daef1 npm: package updates 2024-03-15 14:18:17 +00:00
Philip H ee5a2c6c5a Server.js: remove cookie setting
Proxy is recommended else use only internally
2024-03-15 15:17:41 +01:00
Philip H 68187e07a1 Merge branch 'master' into feat-cidr-notation 2024-03-14 13:49:26 +01:00
Philip H. 7596385d5a fixup: lint errors and uninstall express 2024-03-14 12:18:47 +00:00
Philip H 9a35b56f5c Update README.md 2024-03-14 09:33:08 +01:00
Philip H. 10f246ea59 fixup: Server.js 2024-03-14 08:19:29 +00:00
Philip H. 3f2495a0ea fixup: UI_TRAFFIC_STATS 2024-03-14 08:14:52 +00:00
Philip H. b7c2c81cc7 fixup: lint errors 2024-03-14 08:05:09 +00:00
Philip H b3306bee48 refactor!: migrate from express to h3 (#880) 2024-03-14 09:00:38 +01:00
Philip H 064e264ce8 Server.js: add missing ')' 2024-03-14 08:59:31 +01:00
Philip H 08c412fd14 Server.js: remove white-space 2024-03-14 08:57:38 +01:00
Philip H 8c771b054c Server.js: add UI_TRAFFIC_STATS 2024-03-14 08:55:15 +01:00
Philip H e9480a5ed5 fixup Server.js: even to master 2024-03-14 08:50:31 +01:00
Philip H 1a3cff855e Merge branch 'feat-h3' into master 2024-03-14 08:48:53 +01:00
Philip H f0ec8e45a0 i18n.js: add Hindi language translation (#902) 2024-03-13 18:55:40 +01:00
Rahil Bhimjiani d91f08eb1b i18n.js: add Hindi language translation
Signed-off-by: Rahil Bhimjiani <me@rahil.rocks>
2024-03-13 18:52:25 +01:00
Philip H 17fdb3396f Add setup and update instructions for Compose (#901) 2024-03-13 18:52:06 +01:00
Edgars 2f5878d406 Add setup and update instructions for Compose
`README.md` was updated to provide installation and update instructions
for Docker Compose as well.
2024-03-13 18:49:49 +01:00
NPM Update Bot 4dc439f041 npm: package updates 2024-03-13 17:49:02 +00:00
Philip H f69aa9dc65 PR: Build Docker images (#912) 2024-03-13 18:48:32 +01:00
Philip H 11a56ffdc5 needed for testing on our test branches 2024-03-13 13:12:16 +01:00
Philip H bad7bff98e Update deploy-pr.yml 2024-03-13 13:09:32 +01:00
Philip H a4c3bf0291 PR: Build Docker images
Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-03-13 13:05:08 +01:00
Philip H 2ffb68eeb2 [update] package.json and changelog to current release 12
Signed-off-by: Philip H <47042125+pheiduck@users.noreply.github.com>
2024-03-12 18:46:50 +01:00
Philip H a36ab8891e fixup: WireGuard.js 2024-03-06 16:43:07 +01:00
Philip H 5ee284b973 fixup: WireGuard.js 2024-03-05 19:35:38 +01:00
Philip H 754b5f29af fixup: WireGuard.js
well I was on the client side so I hope I get all stuff fixed now.
2024-03-05 19:29:14 +01:00
Philip H 84b2b61d63 Merge branch 'master' into feat-cidr-notation 2024-03-05 18:39:15 +01:00
Philip H 63faf4c507 fixup: WireGuard.js 2024-03-05 18:24:42 +01:00
Philip H 76a3d7f81d fixing stuff and formating 2024-03-05 18:15:42 +01:00
Philip H bbc919608c Update docker-compose.yml 2024-03-05 17:57:49 +01:00
Philip H 2f89765112 WireGuard.js: fixup undefined CIDR 2024-03-05 17:56:13 +01:00
Thomas Willems cb45bc1c43 update ip package to 2.0.1 2024-03-05 16:58:19 +01:00
Thomas Willems c4d4da38e7 correct CIDR notation 2024-03-05 16:58:19 +01:00
Thomas Willems 89415a2258 refactor to support CIDR and legacy notation
for WG_DEFAULT_ADDRESS
2024-03-05 16:58:19 +01:00
Thomas Willems 577af9947d introduce WG_DEFAULT_ADDRESS_RANGE (CIDR notation)
This PR allows the use of Address Ranges using the CIDR notation.

To make it backward compatible, i introduced a new env variable WG_DEFAULT_ADDRESS_RANGE (defaults to the previous default of 24).

This allows the usage of smaller subnets (or possibly larger; but i didn't test that due to restrictions on my network). Client IPs will be calculated with correct IP addresses instead of making assumptions of the address space.
2024-03-05 16:58:19 +01:00
cany748 7efdbf38e4 fix: revert ServerError.js 2024-02-28 16:27:33 +07:00
cany748 e8a160b14f refactor!: migrate from express to h3 2024-02-28 15:48:09 +07:00
36 changed files with 2282 additions and 1661 deletions
+1
View File
@@ -0,0 +1 @@
/src/node_modules
+5
View File
@@ -0,0 +1,5 @@
# Copyright (c) Emile Nijssen (WeeJeWel)
# Founder and Codeowner of WireGuard Easy (wg-easy)
# Maintained by Bernd Storath (kaaax0815)
* @WeeJeWel
* @kaaax0815
+2 -2
View File
@@ -24,13 +24,13 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- OS: [e.g. macOS 12.1]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- OS: [e.g. iOS 8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
@@ -0,0 +1,28 @@
<!--- Provide a general summary of your changes in the Title above -->
## Description
<!--- Describe your changes in detail -->
## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
## How has this been tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, tests ran to see how -->
<!--- your change affects other areas of the code, etc. -->
## Screenshots (if appropriate):
## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
-41
View File
@@ -1,41 +0,0 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "15 0 * * *"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript-typescript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
-39
View File
@@ -1,39 +0,0 @@
name: Build & Publish Nightly
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
jobs:
deploy:
name: Build & Deploy
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: production
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & Publish Docker Image
uses: docker/build-push-action@v5
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: ghcr.io/wg-easy/wg-easy:nightly
@@ -1,7 +1,6 @@
name: Build & Publish Development
name: Build Pull Request
on:
workflow_dispatch:
pull_request:
jobs:
@@ -14,8 +13,6 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: production
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -30,9 +27,9 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & Publish Docker Image
uses: docker/build-push-action@v5
- name: Build Docker Image
uses: docker/build-push-action@v6
with:
push: true
push: false
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: ghcr.io/wg-easy/wg-easy:development
tags: ghcr.io/wg-easy/wg-easy:pr
+7 -9
View File
@@ -1,23 +1,24 @@
name: Build & Publish Latest
on:
workflow_dispatch:
push:
branches:
- production
- v14
jobs:
deploy:
name: Build & Deploy
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
if: |
github.repository_owner == 'wg-easy' &&
!contains(github.event.head_commit.message, '!skipci')
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: production
ref: v14
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
@@ -32,12 +33,9 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set environment variables
run: echo RELEASE=$(cat ./src/package.json | jq -r .release) >> $GITHUB_ENV
- name: Build & Publish Docker Image
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: ghcr.io/wg-easy/wg-easy:latest, ghcr.io/wg-easy/wg-easy:${{ env.RELEASE }}
tags: ghcr.io/wg-easy/wg-easy:latest, ghcr.io/wg-easy/wg-easy:14
+2 -6
View File
@@ -3,8 +3,7 @@ name: Lint
on:
push:
branches:
- master
- production
- v14
pull_request:
jobs:
@@ -18,12 +17,9 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '18'
node-version: '20'
check-latest: true
cache: 'npm'
cache-dependency-path: |
package-lock.json
src/package-lock.json
- name: npm run lint
run: |
-43
View File
@@ -1,43 +0,0 @@
name: NPM Update Bot 🤖
on:
push:
branches: [ "master" ]
schedule:
- cron: "0 0 * * 1"
jobs:
npmupbot:
name: NPM Update Bot 🤖
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
repository: wg-easy/wg-easy
ref: master
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '18'
check-latest: true
cache: 'npm'
cache-dependency-path: |
package-lock.json
src/package-lock.json
- name: Bot 🤖 "Updating NPM Packages..."
run: |
npm install -g --silent npm-check-updates
ncu -u
npm update
cd src
ncu -u
npm update
npm run buildcss
git config --global user.name 'NPM Update Bot'
git config --global user.email 'npmupbot@users.noreply.github.com'
git add .
git commit -am "npm: package updates" || true
git push
-35
View File
@@ -1,35 +0,0 @@
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Mark stale issues and pull requests
on:
workflow_dispatch:
schedule:
- cron: '*/5 * * * *'
jobs:
stale:
runs-on: ubuntu-latest
if: github.repository_owner == 'wg-easy'
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: 30
days-before-pr-close: 14
stale-pr-message: "This PR is stale because it has been open for 30 days with no activity."
close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale."
repo-token: ${{ secrets.GITHUB_TOKEN }}
operations-per-run: 100
+1
View File
@@ -1,5 +1,6 @@
/config
/wg0.conf
/wg0.json
/src/node_modules
.DS_Store
*.swp
+13 -18
View File
@@ -1,17 +1,20 @@
# There's an issue with node:20-alpine.
# Docker deployment is canceled after 25< minutes.
# As a workaround we have to build on nodejs 18
# nodejs 20 hangs on build with armv6/armv7
FROM docker.io/library/node:lts-alpine AS build_node_modules
FROM docker.io/library/node:18-alpine AS build_node_modules
# Update npm to latest
RUN npm install -g npm@latest
# Copy Web UI
COPY src/ /app/
COPY src /app
WORKDIR /app
RUN npm ci --omit=dev &&\
mv node_modules /node_modules
# Copy build result to a new image.
# This saves a lot of disk space.
FROM docker.io/library/node:18-alpine
FROM docker.io/library/node:lts-alpine
HEALTHCHECK CMD /usr/bin/timeout 5s /bin/sh -c "/usr/bin/wg show | /bin/grep -q interface || exit 1" --interval=1m --timeout=5s --retries=3
COPY --from=build_node_modules /app /app
# Move node_modules one directory up, so during development
@@ -23,13 +26,9 @@ COPY --from=build_node_modules /app /app
# than what runs inside of docker.
COPY --from=build_node_modules /node_modules /node_modules
RUN \
# Enable this to run `npm run serve`
npm i -g nodemon &&\
# Workaround CVE-2023-42282
npm uninstall -g ip &&\
# Delete unnecessary files
npm cache clean --force && rm -rf ~/.npm
# Copy the needed wg-password scripts
COPY --from=build_node_modules /app/wgpw.sh /bin/wgpw
RUN chmod +x /bin/wgpw
# Install Linux packages
RUN apk add --no-cache \
@@ -40,15 +39,11 @@ RUN apk add --no-cache \
wireguard-tools
# Use iptables-legacy
RUN update-alternatives --install /sbin/iptables iptables /sbin/iptables-legacy 10 --slave /sbin/iptables-restore iptables-restore /sbin/iptables-legacy-restore --slave /sbin/iptables-save iptables-save /sbin/iptables-legacy-save
# Expose Ports
EXPOSE 51820/udp
EXPOSE 51821/tcp
RUN update-alternatives --install /usr/sbin/iptables iptables /usr/sbin/iptables-legacy 10 --slave /usr/sbin/iptables-restore iptables-restore /usr/sbin/iptables-legacy-restore --slave /usr/sbin/iptables-save iptables-save /usr/sbin/iptables-legacy-save
# Set Environment
ENV DEBUG=Server,WireGuard
# Run Web UI
WORKDIR /app
CMD ["/usr/bin/dumb-init", "node", "server.js"]
CMD ["/usr/bin/dumb-init", "node", "server.js"]
+45
View File
@@ -0,0 +1,45 @@
# Generating bcrypt-hashed password
With version 14 of wg-easy, a password hashed with bcrypt is needed instead of the plain-text password string. This doc explains how to generate the hash based on a plain-text password.
## Using Docker + node
- You are using docker compose
The easiest way to generate a bcrypt password hash with wgpw is using docker and node:
```sh
docker run ghcr.io/wg-easy/wg-easy:14 node -e 'const bcrypt = require("bcryptjs"); const hash = bcrypt.hashSync("YOUR_PASSWORD", 10); console.log(hash.replace(/\$/g, "$$$$"));'
```
The hashed password will get printed on your terminal. Copy it and use on the `PASSWORD_HASH` environment variable in your docker compose.
- You are using `docker run`
If you are using `docker run` for running wg-easy, you must enclose the hash string in single quotes (`'...'`). You can use this command:
```sh
docker run --rm ghcr.io/wg-easy/wg-easy:14 node -e "const bcrypt = require('bcryptjs'); const hash = bcrypt.hashSync('YOUR_PASSWORD', 10); console.log('\'' + hash + '\'');"
```
The hashed password will get printed on your terminal. Copy it and use on the `PASSWORD_HASH` environment variable in your docker run command.
## Using Docker + wgpw
`wg-password` (wgpw) is a script that generates bcrypt password hashes. You can use it with docker:
```sh
docker run ghcr.io/wg-easy/wg-easy:14 wgpw YOUR_PASSWORD
```
You will see an output similar to this:
```sh
PASSWORD_HASH='$2b$12$coPqCsPtcFO.Ab99xylBNOW4.Iu7OOA2/ZIboHN6/oyxca3MWo7fW'
```
In this example, the `$2b$12$coPqCsPtcFO.Ab99xylBNOW4.Iu7OOA2/ZIboHN6/oyxca3MWo7fW` string is your hashed password. For using it with docker-compose, you need to escape each `$` characters by adding another `$` before them, or they will get interpreted as variables. The final password you can use in docker-compose will look like this:
```sh
$$2b$$12$$coPqCsPtcFO.Ab99xylBNOW4.Iu7OOA2/ZIboHN6/oyxca3MWo7fW
```
+50 -36
View File
@@ -30,6 +30,11 @@ You have found the easiest way to install & manage WireGuard on any Linux host!
* A host with a kernel that supports WireGuard (all modern kernels).
* A host with Docker installed.
## Versions
This branch is only for the v14 release of WireGuard Easy.
For newer versions, please refer to the [master branch](https://github.com/wg-easy/wg-easy/tree/master).
## Installation
### 1. Install Docker
@@ -37,9 +42,9 @@ You have found the easiest way to install & manage WireGuard on any Linux host!
If you haven't installed Docker yet, install it by running:
```bash
$ curl -sSL https://get.docker.com | sh
$ sudo usermod -aG docker $(whoami)
$ exit
curl -sSL https://get.docker.com | sh
sudo usermod -aG docker $(whoami)
exit
```
And log in again.
@@ -48,12 +53,14 @@ And log in again.
To automatically install & run wg-easy, simply run:
<pre>
$ docker run -d \
```
docker run -d \
--name=wg-easy \
-e LANG=de \
-e WG_HOST=<b>🚨YOUR_SERVER_IP</b> \
-e PASSWORD=<b>🚨YOUR_ADMIN_PASSWORD</b> \
-e WG_HOST=<🚨YOUR_SERVER_IP> \
-e PASSWORD_HASH=<🚨YOUR_ADMIN_PASSWORD_HASH> \
-e PORT=51821 \
-e WG_PORT=51820 \
-v ~/.wg-easy:/etc/wireguard \
-p 51820:51820/udp \
-p 51821:51821/tcp \
@@ -62,17 +69,21 @@ $ docker run -d \
--sysctl="net.ipv4.conf.all.src_valid_mark=1" \
--sysctl="net.ipv4.ip_forward=1" \
--restart unless-stopped \
ghcr.io/wg-easy/wg-easy
</pre>
ghcr.io/wg-easy/wg-easy:14
```
> 💡 Replace `YOUR_SERVER_IP` with your WAN IP, or a Dynamic DNS hostname.
>
> 💡 Replace `YOUR_ADMIN_PASSWORD` with a password to log in on the Web UI.
> 💡 Replace `YOUR_ADMIN_PASSWORD_HASH` with a bcrypt password hash to log in on the Web UI. See [How_to_generate_an_bcrypt_hash.md](./How_to_generate_an_bcrypt_hash.md) for know how generate the hash.
The Web UI will now be available on `http://0.0.0.0:51821`.
> 💡 Your configuration files will be saved in `~/.wg-easy`
WireGuard Easy can be launched with Docker Compose as well - just download
[`docker-compose.yml`](docker-compose.yml), make necessary adjustments and
execute `docker compose up --detach`.
### 3. Sponsor
Are you enjoying this project? [Buy Emile a beer!](https://github.com/sponsors/WeeJeWel) 🍻
@@ -81,25 +92,27 @@ Are you enjoying this project? [Buy Emile a beer!](https://github.com/sponsors/W
These options can be configured by setting environment variables using `-e KEY="VALUE"` in the `docker run` command.
| Env | Default | Example | Description |
| - | - | - | - |
| `PORT` | `51821` | `6789` | TCP port for Web UI. |
| `WEBUI_HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. |
| `PASSWORD` | - | `foobar123` | When set, requires a password when logging in to the Web UI. |
| `WG_HOST` | - | `vpn.myserver.com` | The public hostname of your VPN server. |
| `WG_DEVICE` | `eth0` | `ens6f0` | Ethernet device the wireguard traffic should be forwarded through. |
| `WG_PORT` | `51820` | `12345` | The public UDP port of your VPN server. WireGuard will always listen on 51820 inside the Docker container. |
| `WG_MTU` | `null` | `1420` | The MTU the clients will use. Server uses default WG MTU. |
| `WG_PERSISTENT_KEEPALIVE` | `0` | `25` | Value in seconds to keep the "connection" open. If this value is 0, then connections won't be kept alive. |
| `WG_DEFAULT_ADDRESS` | `10.8.0.x` | `10.6.0.x` | Clients IP address range. |
| `WG_DEFAULT_DNS` | `1.1.1.1` | `8.8.8.8, 8.8.4.4` | DNS server clients will use. If set to blank value, clients will not use any DNS. |
| `WG_ALLOWED_IPS` | `0.0.0.0/0, ::/0` | `192.168.15.0/24, 10.0.1.0/24` | Allowed IPs clients will use. |
| `WG_PRE_UP` | `...` | - | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L19) for the default value. |
| `WG_POST_UP` | `...` | `iptables ...` | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L20) for the default value. |
| `WG_PRE_DOWN` | `...` | - | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L27) for the default value. |
| `WG_POST_DOWN` | `...` | `iptables ...` | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L28) for the default value. |
| `LANG` | `en` | `de` | Web UI language (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th). |
| `UI_TRAFFIC_STATS` | `false` | `true` | Enable detailed RX / TX client stats in Web UI |
| Env | Default | Example | Description |
| - | - | - |------------------------------------------------------------------------------------------------------------------------------------------------------|
| `PORT` | `51821` | `6789` | TCP port for Web UI. |
| `WEBUI_HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. |
| `PASSWORD_HASH` | - | `$2y$05$Ci...` | When set, requires a password when logging in to the Web UI. See [How to generate an bcrypt hash.md]("https://github.com/wg-easy/wg-easy/blob/master/How_to_generate_an_bcrypt_hash.md") for know how generate the hash. |
| `WG_HOST` | - | `vpn.myserver.com` | The public hostname of your VPN server. |
| `WG_DEVICE` | `eth0` | `ens6f0` | Ethernet device the wireguard traffic should be forwarded through. |
| `WG_PORT` | `51820` | `12345` | The public UDP port of your VPN server. WireGuard will listen on that (othwise default) inside the Docker container. |
| `WG_CONFIG_PORT`| `51820` | `12345` | The UDP port used on [Home Assistant Plugin](https://github.com/adriy-be/homeassistant-addons-jdeath/tree/main/wgeasy)
| `WG_MTU` | `null` | `1420` | The MTU the clients will use. Server uses default WG MTU. |
| `WG_PERSISTENT_KEEPALIVE` | `0` | `25` | Value in seconds to keep the "connection" open. If this value is 0, then connections won't be kept alive. |
| `WG_DEFAULT_ADDRESS` | `10.8.0.x` | `10.6.0.x` | Clients IP address range. |
| `WG_DEFAULT_DNS` | `1.1.1.1` | `8.8.8.8, 8.8.4.4` | DNS server clients will use. If set to blank value, clients will not use any DNS. |
| `WG_ALLOWED_IPS` | `0.0.0.0/0, ::/0` | `192.168.15.0/24, 10.0.1.0/24` | Allowed IPs clients will use. |
| `WG_PRE_UP` | `...` | - | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L19) for the default value. |
| `WG_POST_UP` | `...` | `iptables ...` | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L20) for the default value. |
| `WG_PRE_DOWN` | `...` | - | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L27) for the default value. |
| `WG_POST_DOWN` | `...` | `iptables ...` | See [config.js](https://github.com/wg-easy/wg-easy/blob/master/src/config.js#L28) for the default value. |
| `LANG` | `en` | `de` | Web UI language (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th, hi). |
| `UI_TRAFFIC_STATS` | `false` | `true` | Enable detailed RX / TX client stats in Web UI |
| `UI_CHART_TYPE` | `0` | `1` | UI_CHART_TYPE=0 # Charts disabled, UI_CHART_TYPE=1 # Line chart, UI_CHART_TYPE=2 # Area chart, UI_CHART_TYPE=3 # Bar chart |
> If you change `WG_PORT`, make sure to also change the exposed port.
@@ -110,14 +123,15 @@ To update to the latest version, simply run:
```bash
docker stop wg-easy
docker rm wg-easy
docker pull ghcr.io/wg-easy/wg-easy
docker pull ghcr.io/wg-easy/wg-easy:14
```
And then run the `docker run -d \ ...` command above again.
## Common Use Cases
* [Using WireGuard-Easy with Pi-Hole](https://github.com/wg-easy/wg-easy/wiki/Using-WireGuard-Easy-with-Pi-Hole)
* [Using WireGuard-Easy with nginx/SSL](https://github.com/wg-easy/wg-easy/wiki/Using-WireGuard-Easy-with-nginx-SSL)
For less common or specific edge-case scenarios, please refer to the detailed information provided in the [Wiki](https://github.com/wg-easy/wg-easy/wiki).
With Docker Compose WireGuard Easy can be updated with a single command:
`docker compose up --detach --pull always` (if an image tag is specified in the
Compose file and it is not `latest`, make sure that it is changed to the desired
one; by default it is omitted and
[defaults to `latest`](https://docs.docker.com/engine/reference/run/#image-references)). \
The WireGuard Easy container will be automatically recreated if a newer image
was pulled.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 104 KiB

+10 -3
View File
@@ -1,10 +1,17 @@
version: "3.8"
services:
wg-easy:
image: wg-easy
build:
dockerfile: ./Dockerfile
command: npm run serve
volumes:
- ./src/:/app/
# - ./data/:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
# - PASSWORD=p
# - PASSWORD_HASH=p
- WG_HOST=192.168.1.233
+8 -5
View File
@@ -1,4 +1,3 @@
version: "3.8"
volumes:
etc_wireguard:
@@ -6,15 +5,17 @@ services:
wg-easy:
environment:
# Change Language:
# (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th)
# (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th, hi)
- LANG=de
# ⚠️ Required:
# Change this to your host's public address
- WG_HOST=raspberrypi.local
# Optional:
# - PASSWORD=foobar123
# - PASSWORD_HASH=$$2y$$10$$hBCoykrB95WSzuV4fafBzOHWKu9sbyVa34GJr8VV5R/pIelfEMYyG # (needs double $$, hash of 'foobar123'; see "How_to_generate_an_bcrypt_hash.md" for generate the hash)
# - PORT=51821
# - WG_PORT=51820
# - WG_CONFIG_PORT=92820
# - WG_DEFAULT_ADDRESS=10.8.0.x
# - WG_DEFAULT_DNS=1.1.1.1
# - WG_MTU=1420
@@ -24,9 +25,10 @@ services:
# - WG_POST_UP=echo "Post Up" > /etc/wireguard/post-up.txt
# - WG_PRE_DOWN=echo "Pre Down" > /etc/wireguard/pre-down.txt
# - WG_POST_DOWN=echo "Post Down" > /etc/wireguard/post-down.txt
# - UI_TRAFFIC_STATS=true
# - UI_TRAFFIC_STATS=true
# - UI_CHART_TYPE=0 # (0 Charts disabled, 1 # Line chart, 2 # Area chart, 3 # Bar chart)
image: ghcr.io/wg-easy/wg-easy
image: ghcr.io/wg-easy/wg-easy:14
container_name: wg-easy
volumes:
- etc_wireguard:/etc/wireguard
@@ -37,6 +39,7 @@ services:
cap_add:
- NET_ADMIN
- SYS_MODULE
# - NET_RAW # ⚠️ Uncomment if using Podman
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
+5 -3
View File
@@ -1,6 +1,6 @@
{
"1": "Initial version. Enjoy!",
"2": "You can now rename a client, and update the address. Enjoy!",
"2": "You can now rename a client & update the address. Enjoy!",
"3": "Many improvements and small changes. Enjoy!",
"4": "Now with pretty charts for client's network speed. Enjoy!",
"5": "Many small improvements & feature requests. Enjoy!",
@@ -9,6 +9,8 @@
"8": "Updated to Node.js v18.",
"9": "Fixed issue running on devices with older kernels.",
"10": "Added sessionless HTTP API auth & automatic dark mode.",
"11": "Multilanguage Support & various bugfixes",
"12": "UI_TRAFFIC_STATS, Import json configurations with no PreShared-Key, allow clients with no privateKey & more."
"11": "Multilanguage Support & various bugfixes.",
"12": "UI_TRAFFIC_STATS, Import json configurations with no PreShared-Key, allow clients with no privateKey & more.",
"13": "New framework (h3), UI_CHART_TYPE, some bugfixes & more.",
"14": "Home Assistent support, PASSWORD_HASH (inc. Helper), translation updates bugfixes & more."
}
+4 -2
View File
@@ -1,8 +1,10 @@
{
"version": "1.0.1",
"scripts": {
"sudobuild": "DOCKER_BUILDKIT=1 sudo docker build --tag wg-easy .",
"build": "DOCKER_BUILDKIT=1 docker build --tag wg-easy .",
"serve": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml up",
"serve": "docker compose -f docker-compose.yml -f docker-compose.dev.yml up",
"sudostart": "sudo docker run --env WG_HOST=0.0.0.0 --name wg-easy --cap-add=NET_ADMIN --cap-add=SYS_MODULE --sysctl=\"net.ipv4.conf.all.src_valid_mark=1\" --mount type=bind,source=\"$(pwd)\"/config,target=/etc/wireguard -p 51820:51820/udp -p 51821:51821/tcp wg-easy",
"start": "docker run --env WG_HOST=0.0.0.0 --name wg-easy --cap-add=NET_ADMIN --cap-add=SYS_MODULE --sysctl=\"net.ipv4.conf.all.src_valid_mark=1\" --mount type=bind,source=\"$(pwd)\"/config,target=/etc/wireguard -p 51820:51820/udp -p 51821:51821/tcp wg-easy"
}
}
}
-1
View File
@@ -1 +0,0 @@
/node_modules
+8 -4
View File
@@ -1,15 +1,18 @@
'use strict';
const { release } = require('./package.json');
const { release: { version } } = require('./package.json');
module.exports.RELEASE = release;
module.exports.RELEASE = version;
module.exports.PORT = process.env.PORT || '51821';
module.exports.WEBUI_HOST = process.env.WEBUI_HOST || '0.0.0.0';
/** This is only kept for migration purpose. DO NOT USE! */
module.exports.PASSWORD = process.env.PASSWORD;
module.exports.PASSWORD_HASH = process.env.PASSWORD_HASH;
module.exports.WG_PATH = process.env.WG_PATH || '/etc/wireguard/';
module.exports.WG_DEVICE = process.env.WG_DEVICE || 'eth0';
module.exports.WG_HOST = process.env.WG_HOST;
module.exports.WG_PORT = process.env.WG_PORT || '51820';
module.exports.WG_CONFIG_PORT = process.env.WG_CONFIG_PORT || process.env.WG_PORT || '51820';
module.exports.WG_MTU = process.env.WG_MTU || null;
module.exports.WG_PERSISTENT_KEEPALIVE = process.env.WG_PERSISTENT_KEEPALIVE || '0';
module.exports.WG_DEFAULT_ADDRESS = process.env.WG_DEFAULT_ADDRESS || '10.8.0.x';
@@ -21,7 +24,7 @@ module.exports.WG_ALLOWED_IPS = process.env.WG_ALLOWED_IPS || '0.0.0.0/0, ::/0';
module.exports.WG_PRE_UP = process.env.WG_PRE_UP || '';
module.exports.WG_POST_UP = process.env.WG_POST_UP || `
iptables -t nat -A POSTROUTING -s ${module.exports.WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ${module.exports.WG_DEVICE} -j MASQUERADE;
iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT;
iptables -A INPUT -p udp -m udp --dport ${module.exports.WG_PORT} -j ACCEPT;
iptables -A FORWARD -i wg0 -j ACCEPT;
iptables -A FORWARD -o wg0 -j ACCEPT;
`.split('\n').join(' ');
@@ -29,9 +32,10 @@ iptables -A FORWARD -o wg0 -j ACCEPT;
module.exports.WG_PRE_DOWN = process.env.WG_PRE_DOWN || '';
module.exports.WG_POST_DOWN = process.env.WG_POST_DOWN || `
iptables -t nat -D POSTROUTING -s ${module.exports.WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ${module.exports.WG_DEVICE} -j MASQUERADE;
iptables -D INPUT -p udp -m udp --dport 51820 -j ACCEPT;
iptables -D INPUT -p udp -m udp --dport ${module.exports.WG_PORT} -j ACCEPT;
iptables -D FORWARD -i wg0 -j ACCEPT;
iptables -D FORWARD -o wg0 -j ACCEPT;
`.split('\n').join(' ');
module.exports.LANG = process.env.LANG || 'en';
module.exports.UI_TRAFFIC_STATS = process.env.UI_TRAFFIC_STATS || 'false';
module.exports.UI_CHART_TYPE = process.env.UI_CHART_TYPE || 0;
+225 -86
View File
@@ -1,15 +1,27 @@
'use strict';
const path = require('path');
const bcrypt = require('bcryptjs');
const crypto = require('node:crypto');
const { createServer } = require('node:http');
const { stat, readFile } = require('node:fs/promises');
const { resolve, sep } = require('node:path');
const express = require('express');
const expressSession = require('express-session');
const debug = require('debug')('Server');
const Util = require('./Util');
const ServerError = require('./ServerError');
const {
createApp,
createError,
createRouter,
defineEventHandler,
fromNodeMiddleware,
getRouterParam,
toNodeListener,
readBody,
setHeader,
serveStatic,
} = require('h3');
const WireGuard = require('../services/WireGuard');
const {
@@ -17,43 +29,74 @@ const {
WEBUI_HOST,
RELEASE,
PASSWORD,
PASSWORD_HASH,
LANG,
UI_TRAFFIC_STATS,
UI_CHART_TYPE,
} = require('../config');
const requiresPassword = !!PASSWORD_HASH;
/**
* Checks if `password` matches the PASSWORD_HASH.
*
* If environment variable is not set, the password is always invalid.
*
* @param {string} password String to test
* @returns {boolean} true if matching environment, otherwise false
*/
const isPasswordValid = (password) => {
if (typeof password !== 'string') {
return false;
}
if (PASSWORD_HASH) {
return bcrypt.compareSync(password, PASSWORD_HASH);
}
return false;
};
module.exports = class Server {
constructor() {
// Express
this.app = express()
.disable('etag')
.use('/', express.static(path.join(__dirname, '..', 'www')))
.use(express.json())
.use(expressSession({
secret: crypto.randomBytes(256).toString('hex'),
resave: true,
saveUninitialized: true,
cookie: {
httpOnly: true,
},
const app = createApp();
this.app = app;
app.use(fromNodeMiddleware(expressSession({
secret: crypto.randomBytes(256).toString('hex'),
resave: true,
saveUninitialized: true,
})));
const router = createRouter();
app.use(router);
router
.get('/api/release', defineEventHandler((event) => {
setHeader(event, 'Content-Type', 'application/json');
return RELEASE;
}))
.get('/api/release', (Util.promisify(async () => {
return RELEASE;
})))
.get('/api/lang', defineEventHandler((event) => {
setHeader(event, 'Content-Type', 'application/json');
return `"${LANG}"`;
}))
.get('/api/lang', (Util.promisify(async () => {
return LANG;
})))
.get('/api/ui-traffic-stats', (Util.promisify(async () => {
return UI_TRAFFIC_STATS === 'true';
})))
.get('/api/ui-traffic-stats', defineEventHandler((event) => {
setHeader(event, 'Content-Type', 'application/json');
return `"${UI_TRAFFIC_STATS}"`;
}))
// Authentication
.get('/api/session', Util.promisify(async (req) => {
const requiresPassword = !!process.env.PASSWORD;
.get('/api/ui-chart-type', defineEventHandler((event) => {
setHeader(event, 'Content-Type', 'application/json');
return `"${UI_CHART_TYPE}"`;
}))
// Authentication
.get('/api/session', defineEventHandler((event) => {
const authenticated = requiresPassword
? !!(req.session && req.session.authenticated)
? !!(event.node.req.session && event.node.req.session.authenticated)
: true;
return {
@@ -61,28 +104,37 @@ module.exports = class Server {
authenticated,
};
}))
.post('/api/session', Util.promisify(async (req) => {
const {
password,
} = req.body;
.post('/api/session', defineEventHandler(async (event) => {
const { password } = await readBody(event);
if (typeof password !== 'string') {
throw new ServerError('Missing: Password', 401);
if (!requiresPassword) {
// if no password is required, the API should never be called.
// Do not automatically authenticate the user.
throw createError({
status: 401,
message: 'Invalid state',
});
}
if (password !== PASSWORD) {
throw new ServerError('Incorrect Password', 401);
if (!isPasswordValid(password)) {
throw createError({
status: 401,
message: 'Incorrect Password',
});
}
req.session.authenticated = true;
req.session.save();
event.node.req.session.authenticated = true;
event.node.req.session.save();
debug(`New Session: ${req.session.id}`);
}))
debug(`New Session: ${event.node.req.session.id}`);
return { success: true };
}));
// WireGuard
.use((req, res, next) => {
if (!PASSWORD) {
app.use(
fromNodeMiddleware((req, res, next) => {
if (!requiresPassword || !req.url.startsWith('/api/')) {
return next();
}
@@ -90,8 +142,8 @@ module.exports = class Server {
return next();
}
if (req.path.startsWith('/api/') && req.headers['authorization']) {
if (bcrypt.compareSync(req.headers['authorization'], bcrypt.hashSync(PASSWORD, 10))) {
if (req.url.startsWith('/api/') && req.headers['authorization']) {
if (isPasswordValid(req.headers['authorization'])) {
return next();
}
return res.status(401).json({
@@ -102,25 +154,32 @@ module.exports = class Server {
return res.status(401).json({
error: 'Not Logged In',
});
})
.delete('/api/session', Util.promisify(async (req) => {
const sessionId = req.session.id;
}),
);
req.session.destroy();
const router2 = createRouter();
app.use(router2);
router2
.delete('/api/session', defineEventHandler((event) => {
const sessionId = event.node.req.session.id;
event.node.req.session.destroy();
debug(`Deleted Session: ${sessionId}`);
return { success: true };
}))
.get('/api/wireguard/client', Util.promisify(async (req) => {
.get('/api/wireguard/client', defineEventHandler(() => {
return WireGuard.getClients();
}))
.get('/api/wireguard/client/:clientId/qrcode.svg', Util.promisify(async (req, res) => {
const { clientId } = req.params;
.get('/api/wireguard/client/:clientId/qrcode.svg', defineEventHandler(async (event) => {
const clientId = getRouterParam(event, 'clientId');
const svg = await WireGuard.getClientQRCodeSVG({ clientId });
res.header('Content-Type', 'image/svg+xml');
res.send(svg);
setHeader(event, 'Content-Type', 'image/svg+xml');
return svg;
}))
.get('/api/wireguard/client/:clientId/configuration', Util.promisify(async (req, res) => {
const { clientId } = req.params;
.get('/api/wireguard/client/:clientId/configuration', defineEventHandler(async (event) => {
const clientId = getRouterParam(event, 'clientId');
const client = await WireGuard.getClient({ clientId });
const config = await WireGuard.getClientConfiguration({ clientId });
const configName = client.name
@@ -128,52 +187,132 @@ module.exports = class Server {
.replace(/(-{2,}|-$)/g, '-')
.replace(/-$/, '')
.substring(0, 32);
res.header('Content-Disposition', `attachment; filename="${configName || clientId}.conf"`);
res.header('Content-Type', 'text/plain');
res.send(config);
setHeader(event, 'Content-Disposition', `attachment; filename="${configName || clientId}.conf"`);
setHeader(event, 'Content-Type', 'text/plain');
return config;
}))
.post('/api/wireguard/client', Util.promisify(async (req) => {
const { name } = req.body;
return WireGuard.createClient({ name });
.post('/api/wireguard/client', defineEventHandler(async (event) => {
const { name } = await readBody(event);
await WireGuard.createClient({ name });
return { success: true };
}))
.delete('/api/wireguard/client/:clientId', Util.promisify(async (req) => {
const { clientId } = req.params;
return WireGuard.deleteClient({ clientId });
.delete('/api/wireguard/client/:clientId', defineEventHandler(async (event) => {
const clientId = getRouterParam(event, 'clientId');
await WireGuard.deleteClient({ clientId });
return { success: true };
}))
.post('/api/wireguard/client/:clientId/enable', Util.promisify(async (req, res) => {
const { clientId } = req.params;
.post('/api/wireguard/client/:clientId/enable', defineEventHandler(async (event) => {
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
res.end(403);
throw createError({ status: 403 });
}
return WireGuard.enableClient({ clientId });
await WireGuard.enableClient({ clientId });
return { success: true };
}))
.post('/api/wireguard/client/:clientId/disable', Util.promisify(async (req, res) => {
const { clientId } = req.params;
.post('/api/wireguard/client/:clientId/disable', defineEventHandler(async (event) => {
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
res.end(403);
throw createError({ status: 403 });
}
return WireGuard.disableClient({ clientId });
await WireGuard.disableClient({ clientId });
return { success: true };
}))
.put('/api/wireguard/client/:clientId/name', Util.promisify(async (req, res) => {
const { clientId } = req.params;
.put('/api/wireguard/client/:clientId/name', defineEventHandler(async (event) => {
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
res.end(403);
throw createError({ status: 403 });
}
const { name } = req.body;
return WireGuard.updateClientName({ clientId, name });
const { name } = await readBody(event);
await WireGuard.updateClientName({ clientId, name });
return { success: true };
}))
.put('/api/wireguard/client/:clientId/address', Util.promisify(async (req, res) => {
const { clientId } = req.params;
.put('/api/wireguard/client/:clientId/address', defineEventHandler(async (event) => {
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
res.end(403);
throw createError({ status: 403 });
}
const { address } = req.body;
return WireGuard.updateClientAddress({ clientId, address });
}))
const { address } = await readBody(event);
await WireGuard.updateClientAddress({ clientId, address });
return { success: true };
}));
.listen(PORT, WEBUI_HOST, () => {
debug(`Listening on http://${WEBUI_HOST}:${PORT}`);
const safePathJoin = (base, target) => {
// Manage web root (edge case)
if (target === '/') {
return `${base}${sep}`;
}
// Prepend './' to prevent absolute paths
const targetPath = `.${sep}${target}`;
// Resolve the absolute path
const resolvedPath = resolve(base, targetPath);
// Check if resolvedPath is a subpath of base
if (resolvedPath.startsWith(`${base}${sep}`)) {
return resolvedPath;
}
throw createError({
status: 400,
message: 'Bad Request',
});
};
// backup_restore
const router3 = createRouter();
app.use(router3);
router3
.get('/api/wireguard/backup', defineEventHandler(async (event) => {
const config = await WireGuard.backupConfiguration();
setHeader(event, 'Content-Disposition', 'attachment; filename="wg0.json"');
setHeader(event, 'Content-Type', 'text/json');
return config;
}))
.put('/api/wireguard/restore', defineEventHandler(async (event) => {
const { file } = await readBody(event);
await WireGuard.restoreConfiguration(file);
return { success: true };
}));
// Static assets
const publicDir = '/app/www';
app.use(
defineEventHandler((event) => {
return serveStatic(event, {
getContents: (id) => {
return readFile(safePathJoin(publicDir, id));
},
getMeta: async (id) => {
const filePath = safePathJoin(publicDir, id);
const stats = await stat(filePath).catch(() => {});
if (!stats || !stats.isFile()) {
return;
}
if (id.endsWith('.html')) setHeader(event, 'Content-Type', 'text/html');
if (id.endsWith('.js')) setHeader(event, 'Content-Type', 'application/javascript');
if (id.endsWith('.json')) setHeader(event, 'Content-Type', 'application/json');
if (id.endsWith('.css')) setHeader(event, 'Content-Type', 'text/css');
if (id.endsWith('.png')) setHeader(event, 'Content-Type', 'image/png');
return {
size: stats.size,
mtime: stats.mtimeMs,
};
},
});
}),
);
if (PASSWORD) {
throw new Error('DO NOT USE PASSWORD ENVIRONMENT VARIABLE. USE PASSWORD_HASH INSTEAD.\nSee https://github.com/wg-easy/wg-easy/blob/v14/How_to_generate_an_bcrypt_hash.md');
}
createServer(toNodeListener(app)).listen(PORT, WEBUI_HOST);
debug(`Listening on http://${WEBUI_HOST}:${PORT}`);
}
};
+80 -51
View File
@@ -1,10 +1,9 @@
'use strict';
const fs = require('fs').promises;
const fs = require('node:fs/promises');
const path = require('path');
const debug = require('debug')('WireGuard');
const uuid = require('uuid');
const crypto = require('node:crypto');
const QRCode = require('qrcode');
const Util = require('./Util');
@@ -14,6 +13,7 @@ const {
WG_PATH,
WG_HOST,
WG_PORT,
WG_CONFIG_PORT,
WG_MTU,
WG_DEFAULT_DNS,
WG_DEFAULT_ADDRESS,
@@ -27,54 +27,60 @@ const {
module.exports = class WireGuard {
async __buildConfig() {
this.__configPromise = Promise.resolve().then(async () => {
if (!WG_HOST) {
throw new Error('WG_HOST Environment Variable Not Set!');
}
debug('Loading configuration...');
let config;
try {
config = await fs.readFile(path.join(WG_PATH, 'wg0.json'), 'utf8');
config = JSON.parse(config);
debug('Configuration loaded.');
} catch (err) {
const privateKey = await Util.exec('wg genkey');
const publicKey = await Util.exec(`echo ${privateKey} | wg pubkey`, {
log: 'echo ***hidden*** | wg pubkey',
});
const address = WG_DEFAULT_ADDRESS.replace('x', '1');
config = {
server: {
privateKey,
publicKey,
address,
},
clients: {},
};
debug('Configuration generated.');
}
return config;
});
return this.__configPromise;
}
async getConfig() {
if (!this.__configPromise) {
this.__configPromise = Promise.resolve().then(async () => {
if (!WG_HOST) {
throw new Error('WG_HOST Environment Variable Not Set!');
const config = await this.__buildConfig();
await this.__saveConfig(config);
await Util.exec('wg-quick down wg0').catch(() => {});
await Util.exec('wg-quick up wg0').catch((err) => {
if (err && err.message && err.message.includes('Cannot find device "wg0"')) {
throw new Error('WireGuard exited with the error: Cannot find device "wg0"\nThis usually means that your host\'s kernel does not support WireGuard!');
}
debug('Loading configuration...');
let config;
try {
config = await fs.readFile(path.join(WG_PATH, 'wg0.json'), 'utf8');
config = JSON.parse(config);
debug('Configuration loaded.');
} catch (err) {
const privateKey = await Util.exec('wg genkey');
const publicKey = await Util.exec(`echo ${privateKey} | wg pubkey`, {
log: 'echo ***hidden*** | wg pubkey',
});
const address = WG_DEFAULT_ADDRESS.replace('x', '1');
config = {
server: {
privateKey,
publicKey,
address,
},
clients: {},
};
debug('Configuration generated.');
}
await this.__saveConfig(config);
await Util.exec('wg-quick down wg0').catch(() => { });
await Util.exec('wg-quick up wg0').catch((err) => {
if (err && err.message && err.message.includes('Cannot find device "wg0"')) {
throw new Error('WireGuard exited with the error: Cannot find device "wg0"\nThis usually means that your host\'s kernel does not support WireGuard!');
}
throw err;
});
// await Util.exec(`iptables -t nat -A POSTROUTING -s ${WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ' + WG_DEVICE + ' -j MASQUERADE`);
// await Util.exec('iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT');
// await Util.exec('iptables -A FORWARD -i wg0 -j ACCEPT');
// await Util.exec('iptables -A FORWARD -o wg0 -j ACCEPT');
await this.__syncConfig();
return config;
throw err;
});
// await Util.exec(`iptables -t nat -A POSTROUTING -s ${WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ' + WG_DEVICE + ' -j MASQUERADE`);
// await Util.exec('iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT');
// await Util.exec('iptables -A FORWARD -i wg0 -j ACCEPT');
// await Util.exec('iptables -A FORWARD -o wg0 -j ACCEPT');
await this.__syncConfig();
}
return this.__configPromise;
@@ -95,7 +101,7 @@ module.exports = class WireGuard {
[Interface]
PrivateKey = ${config.server.privateKey}
Address = ${config.server.address}/24
ListenPort = 51820
ListenPort = ${WG_PORT}
PreUp = ${WG_PRE_UP}
PostUp = ${WG_POST_UP}
PreDown = ${WG_PRE_DOWN}
@@ -208,7 +214,7 @@ PublicKey = ${config.server.publicKey}
${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
}AllowedIPs = ${WG_ALLOWED_IPS}
PersistentKeepalive = ${WG_PERSISTENT_KEEPALIVE}
Endpoint = ${WG_HOST}:${WG_PORT}`;
Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`;
}
async getClientQRCodeSVG({ clientId }) {
@@ -227,7 +233,9 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
const config = await this.getConfig();
const privateKey = await Util.exec('wg genkey');
const publicKey = await Util.exec(`echo ${privateKey} | wg pubkey`);
const publicKey = await Util.exec(`echo ${privateKey} | wg pubkey`, {
log: 'echo ***hidden*** | wg pubkey',
});
const preSharedKey = await Util.exec('wg genpsk');
// Calculate next IP
@@ -248,7 +256,7 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
}
// Create Client
const id = uuid.v4();
const id = crypto.randomUUID();
const client = {
id,
name,
@@ -319,9 +327,30 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
await this.saveConfig();
}
async __reloadConfig() {
await this.__buildConfig();
await this.__syncConfig();
}
async restoreConfiguration(config) {
debug('Starting configuration restore process.');
const _config = JSON.parse(config);
await this.__saveConfig(_config);
await this.__reloadConfig();
debug('Configuration restore process completed.');
}
async backupConfiguration() {
debug('Starting configuration backup.');
const config = await this.getConfig();
const backup = JSON.stringify(config, null, 2);
debug('Configuration backup completed.');
return backup;
}
// Shutdown wireguard
async Shutdown() {
await Util.exec('wg-quick down wg0').catch(() => { });
await Util.exec('wg-quick down wg0').catch(() => {});
}
};
+1013 -954
View File
File diff suppressed because it is too large Load Diff
+11 -9
View File
@@ -1,28 +1,30 @@
{
"release": "12",
"release": {
"version": "14"
},
"name": "wg-easy",
"version": "1.0.1",
"description": "The easiest way to run WireGuard VPN + Web-based Admin UI.",
"main": "server.js",
"scripts": {
"serve": "DEBUG=Server,WireGuard nodemon server.js",
"serve": "DEBUG=Server,WireGuard npx nodemon server.js",
"serve-with-password": "PASSWORD=wg npm run serve",
"lint": "eslint .",
"buildcss": "npx tailwindcss -i ./www/src/css/app.css -o ./www/css/app.css"
},
"author": "Emile Nijssen",
"license": "GPL",
"license": "CC BY-NC-SA 4.0",
"dependencies": {
"bcryptjs": "^2.4.3",
"debug": "^4.3.4",
"express": "^4.18.3",
"debug": "^4.3.6",
"express-session": "^1.18.0",
"qrcode": "^1.5.3",
"uuid": "^9.0.1"
"h3": "^1.12.0",
"qrcode": "^1.5.4"
},
"devDependencies": {
"eslint-config-athom": "^3.1.3",
"tailwindcss": "^3.4.1"
"nodemon": "^3.1.4",
"tailwindcss": "^3.4.9"
},
"nodemonConfig": {
"ignore": [
@@ -30,6 +32,6 @@
]
},
"engines": {
"node": "18"
"node": ">=18"
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ process.on('SIGTERM', async () => {
process.exit(0);
});
// Handle interupt signal
// Handle interrupt signal
process.on('SIGINT', () => {
// eslint-disable-next-line no-console
console.log('SIGINT signal received.');
+1 -1
View File
@@ -3,7 +3,7 @@
'use strict';
module.exports = {
darkMode: 'media',
darkMode: 'selector',
content: ['./www/**/*.{html,js}'],
theme: {
screens: {
+54
View File
@@ -0,0 +1,54 @@
'use strict';
// Import needed libraries
import bcrypt from 'bcryptjs';
// Function to generate hash
const generateHash = async (password) => {
try {
const salt = await bcrypt.genSalt(12);
const hash = await bcrypt.hash(password, salt);
// eslint-disable-next-line no-console
console.log(`PASSWORD_HASH='${hash}'`);
} catch (error) {
throw new Error(`Failed to generate hash : ${error}`);
}
};
// Function to compare password with hash
const comparePassword = async (password, hash) => {
try {
const match = await bcrypt.compare(password, hash);
if (match) {
// eslint-disable-next-line no-console
console.log('Password matches the hash !');
} else {
// eslint-disable-next-line no-console
console.log('Password does not match the hash.');
}
} catch (error) {
throw new Error(`Failed to compare password and hash : ${error}`);
}
};
(async () => {
try {
// Retrieve command line arguments
const args = process.argv.slice(2); // Ignore the first two arguments
if (args.length > 2) {
throw new Error('Usage : wgpw YOUR_PASSWORD [HASH]');
}
const [password, hash] = args;
if (password && hash) {
await comparePassword(password, hash);
} else if (password) {
await generateHash(password);
}
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
// eslint-disable-next-line no-process-exit
process.exit(1);
}
})();
Executable
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
# This script is intended to be run only inside a docker container, not on the development host machine
set -e
# proxy command
node /app/wgpw.mjs "$@"
+364 -219
View File
@@ -1,5 +1,5 @@
/*
! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com
! tailwindcss v3.4.9 | MIT License | https://tailwindcss.com
*/
/*
@@ -211,6 +211,8 @@ textarea {
/* 1 */
line-height: inherit;
/* 1 */
letter-spacing: inherit;
/* 1 */
color: inherit;
/* 1 */
margin: 0;
@@ -234,9 +236,9 @@ select {
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
input:where([type='button']),
input:where([type='reset']),
input:where([type='submit']) {
-webkit-appearance: button;
/* 1 */
background-color: transparent;
@@ -492,6 +494,10 @@ video {
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
::backdrop {
@@ -542,6 +548,10 @@ video {
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
.container {
@@ -590,6 +600,18 @@ video {
}
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.visible {
visibility: visible;
}
@@ -692,8 +714,8 @@ video {
margin-bottom: 2.5rem;
}
.mb-2 {
margin-bottom: 0.5rem;
.mb-4 {
margin-bottom: 1rem;
}
.mb-5 {
@@ -712,10 +734,6 @@ video {
margin-right: 0.5rem;
}
.mt-0 {
margin-top: 0px;
}
.mt-0\.5 {
margin-top: 0.125rem;
}
@@ -732,6 +750,10 @@ video {
margin-top: 0.75rem;
}
.mt-4 {
margin-top: 1rem;
}
.mt-5 {
margin-top: 1.25rem;
}
@@ -776,6 +798,11 @@ video {
display: none;
}
.size-6 {
width: 1.5rem;
height: 1.5rem;
}
.h-1 {
height: 0.25rem;
}
@@ -804,14 +831,26 @@ video {
height: 1rem;
}
.h-5 {
height: 1.25rem;
}
.h-6 {
height: 1.5rem;
}
.h-8 {
height: 2rem;
}
.min-h-screen {
min-height: 100vh;
}
.w-1 {
width: 0.25rem;
}
.w-10 {
width: 2.5rem;
}
@@ -876,6 +915,10 @@ video {
flex-grow: 1;
}
.grow-0 {
flex-grow: 0;
}
.transform {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
@@ -921,10 +964,18 @@ video {
flex-direction: column;
}
.flex-col-reverse {
flex-direction: column-reverse;
}
.flex-wrap {
flex-wrap: wrap;
}
.items-end {
align-items: flex-end;
}
.items-center {
align-items: center;
}
@@ -957,6 +1008,10 @@ video {
align-self: flex-start;
}
.self-end {
align-self: flex-end;
}
.overflow-hidden {
overflow: hidden;
}
@@ -991,6 +1046,16 @@ video {
border-radius: 0.375rem;
}
.rounded-l-full {
border-top-left-radius: 9999px;
border-bottom-left-radius: 9999px;
}
.rounded-r-full {
border-top-right-radius: 9999px;
border-bottom-right-radius: 9999px;
}
.border {
border-width: 1px;
}
@@ -1087,6 +1152,14 @@ video {
--tw-bg-opacity: 0.5;
}
.fill-gray-400 {
fill: #9ca3af;
}
.fill-gray-600 {
fill: #4b5563;
}
.p-1 {
padding: 0.25rem;
}
@@ -1141,11 +1214,6 @@ video {
padding-bottom: 0.75rem;
}
.py-5 {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.pb-1 {
padding-bottom: 0.25rem;
}
@@ -1276,6 +1344,11 @@ video {
color: rgb(17 24 39 / var(--tw-text-opacity));
}
.text-neutral-400 {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.text-red-600 {
--tw-text-opacity: 1;
color: rgb(220 38 38 / var(--tw-text-opacity));
@@ -1396,6 +1469,10 @@ video {
border-bottom-width: 0px;
}
.hover\:cursor-pointer:hover {
cursor: pointer;
}
.hover\:border-red-800:hover {
--tw-border-opacity: 1;
border-color: rgb(153 27 27 / var(--tw-border-opacity));
@@ -1463,10 +1540,43 @@ video {
opacity: 1;
}
.peer:checked ~ .peer-checked\:fill-gray-600 {
fill: #4b5563;
}
@media not all and (min-width: 768px) {
.max-md\:hidden {
display: none;
}
.max-md\:border-x-0 {
border-left-width: 0px;
border-right-width: 0px;
}
.max-md\:border-l-0 {
border-left-width: 0px;
}
.max-md\:border-r-0 {
border-right-width: 0px;
}
}
@media (min-width: 450px) {
.xxs\:flex-row {
flex-direction: row;
}
.xxs\:self-center {
align-self: center;
}
}
@media (min-width: 576px) {
.xs\:mt-6 {
margin-top: 1.5rem;
}
}
@media (min-width: 640px) {
@@ -1580,6 +1690,14 @@ video {
}
@media (min-width: 768px) {
.md\:mr-2 {
margin-right: 0.5rem;
}
.md\:block {
display: block;
}
.md\:inline-block {
display: inline-block;
}
@@ -1588,15 +1706,28 @@ video {
min-width: 6rem;
}
.md\:flex-shrink-0 {
flex-shrink: 0;
}
.md\:gap-4 {
gap: 1rem;
}
.md\:rounded {
border-radius: 0.25rem;
}
.md\:px-0 {
padding-left: 0px;
padding-right: 0px;
}
.md\:py-5 {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.md\:pb-0 {
padding-bottom: 0px;
}
@@ -1607,208 +1738,222 @@ video {
}
}
@media (prefers-color-scheme: dark) {
.dark\:border-neutral-500 {
--tw-border-opacity: 1;
border-color: rgb(115 115 115 / var(--tw-border-opacity));
}
.dark\:border-neutral-600 {
--tw-border-opacity: 1;
border-color: rgb(82 82 82 / var(--tw-border-opacity));
}
.dark\:border-neutral-800 {
--tw-border-opacity: 1;
border-color: rgb(38 38 38 / var(--tw-border-opacity));
}
.dark\:border-red-600 {
--tw-border-opacity: 1;
border-color: rgb(220 38 38 / var(--tw-border-opacity));
}
.dark\:bg-black {
--tw-bg-opacity: 1;
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-400 {
--tw-bg-opacity: 1;
background-color: rgb(163 163 163 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-500 {
--tw-bg-opacity: 1;
background-color: rgb(115 115 115 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-600 {
--tw-bg-opacity: 1;
background-color: rgb(82 82 82 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-700 {
--tw-bg-opacity: 1;
background-color: rgb(64 64 64 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-800 {
--tw-bg-opacity: 1;
background-color: rgb(38 38 38 / var(--tw-bg-opacity));
}
.dark\:bg-red-100 {
--tw-bg-opacity: 1;
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
}
.dark\:bg-red-600 {
--tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
}
.dark\:bg-red-800 {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
}
.dark\:text-gray-500 {
--tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity));
}
.dark\:text-neutral-200 {
--tw-text-opacity: 1;
color: rgb(229 229 229 / var(--tw-text-opacity));
}
.dark\:text-neutral-300 {
--tw-text-opacity: 1;
color: rgb(212 212 212 / var(--tw-text-opacity));
}
.dark\:text-neutral-400 {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.dark\:text-neutral-50 {
--tw-text-opacity: 1;
color: rgb(250 250 250 / var(--tw-text-opacity));
}
.dark\:text-neutral-500 {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
}
.dark\:text-neutral-600 {
--tw-text-opacity: 1;
color: rgb(82 82 82 / var(--tw-text-opacity));
}
.dark\:text-red-300 {
--tw-text-opacity: 1;
color: rgb(252 165 165 / var(--tw-text-opacity));
}
.dark\:text-red-600 {
--tw-text-opacity: 1;
color: rgb(220 38 38 / var(--tw-text-opacity));
}
.dark\:text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.dark\:opacity-50 {
opacity: 0.5;
}
.dark\:placeholder\:text-neutral-400::-moz-placeholder {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.dark\:placeholder\:text-neutral-400::placeholder {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.dark\:placeholder\:text-neutral-500::-moz-placeholder {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
}
.dark\:placeholder\:text-neutral-500::placeholder {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
}
.dark\:hover\:border-neutral-600:hover {
--tw-border-opacity: 1;
border-color: rgb(82 82 82 / var(--tw-border-opacity));
}
.dark\:hover\:border-red-600:hover {
--tw-border-opacity: 1;
border-color: rgb(220 38 38 / var(--tw-border-opacity));
}
.dark\:hover\:bg-neutral-500:hover {
--tw-bg-opacity: 1;
background-color: rgb(115 115 115 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-neutral-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(82 82 82 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-red-600:hover {
--tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-red-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-red-800:hover {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
}
.dark\:hover\:text-neutral-700:hover {
--tw-text-opacity: 1;
color: rgb(64 64 64 / var(--tw-text-opacity));
}
.dark\:hover\:text-red-100:hover {
--tw-text-opacity: 1;
color: rgb(254 226 226 / var(--tw-text-opacity));
}
.dark\:hover\:text-white:hover {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.dark\:focus\:border-neutral-500:focus {
--tw-border-opacity: 1;
border-color: rgb(115 115 115 / var(--tw-border-opacity));
}
.dark\:focus\:border-red-800:focus {
--tw-border-opacity: 1;
border-color: rgb(153 27 27 / var(--tw-border-opacity));
}
.focus\:dark\:border-neutral-500:focus {
--tw-border-opacity: 1;
border-color: rgb(115 115 115 / var(--tw-border-opacity));
}
.dark\:border-neutral-500:where(.dark, .dark *) {
--tw-border-opacity: 1;
border-color: rgb(115 115 115 / var(--tw-border-opacity));
}
.dark\:border-neutral-600:where(.dark, .dark *) {
--tw-border-opacity: 1;
border-color: rgb(82 82 82 / var(--tw-border-opacity));
}
.dark\:border-neutral-800:where(.dark, .dark *) {
--tw-border-opacity: 1;
border-color: rgb(38 38 38 / var(--tw-border-opacity));
}
.dark\:border-red-600:where(.dark, .dark *) {
--tw-border-opacity: 1;
border-color: rgb(220 38 38 / var(--tw-border-opacity));
}
.dark\:bg-black:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-400:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(163 163 163 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-500:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(115 115 115 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-600:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(82 82 82 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-700:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(64 64 64 / var(--tw-bg-opacity));
}
.dark\:bg-neutral-800:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(38 38 38 / var(--tw-bg-opacity));
}
.dark\:bg-red-100:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
}
.dark\:bg-red-600:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
}
.dark\:bg-red-800:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
}
.dark\:fill-neutral-400:where(.dark, .dark *) {
fill: #a3a3a3;
}
.dark\:fill-neutral-600:where(.dark, .dark *) {
fill: #525252;
}
.dark\:text-gray-500:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity));
}
.dark\:text-neutral-200:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(229 229 229 / var(--tw-text-opacity));
}
.dark\:text-neutral-300:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(212 212 212 / var(--tw-text-opacity));
}
.dark\:text-neutral-400:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.dark\:text-neutral-50:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(250 250 250 / var(--tw-text-opacity));
}
.dark\:text-neutral-500:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
}
.dark\:text-neutral-600:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(82 82 82 / var(--tw-text-opacity));
}
.dark\:text-red-300:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(252 165 165 / var(--tw-text-opacity));
}
.dark\:text-red-600:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(220 38 38 / var(--tw-text-opacity));
}
.dark\:text-white:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.dark\:opacity-50:where(.dark, .dark *) {
opacity: 0.5;
}
.dark\:placeholder\:text-neutral-400:where(.dark, .dark *)::-moz-placeholder {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.dark\:placeholder\:text-neutral-400:where(.dark, .dark *)::placeholder {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.dark\:placeholder\:text-neutral-500:where(.dark, .dark *)::-moz-placeholder {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
}
.dark\:placeholder\:text-neutral-500:where(.dark, .dark *)::placeholder {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
}
.dark\:hover\:border-neutral-600:hover:where(.dark, .dark *) {
--tw-border-opacity: 1;
border-color: rgb(82 82 82 / var(--tw-border-opacity));
}
.dark\:hover\:border-red-600:hover:where(.dark, .dark *) {
--tw-border-opacity: 1;
border-color: rgb(220 38 38 / var(--tw-border-opacity));
}
.dark\:hover\:bg-neutral-500:hover:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(115 115 115 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-neutral-600:hover:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(82 82 82 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-red-600:hover:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-red-700:hover:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
}
.dark\:hover\:bg-red-800:hover:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
}
.dark\:hover\:text-neutral-700:hover:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(64 64 64 / var(--tw-text-opacity));
}
.dark\:hover\:text-red-100:hover:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(254 226 226 / var(--tw-text-opacity));
}
.dark\:hover\:text-white:hover:where(.dark, .dark *) {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.dark\:focus\:border-neutral-500:focus:where(.dark, .dark *) {
--tw-border-opacity: 1;
border-color: rgb(115 115 115 / var(--tw-border-opacity));
}
.dark\:focus\:border-red-800:focus:where(.dark, .dark *) {
--tw-border-opacity: 1;
border-color: rgb(153 27 27 / var(--tw-border-opacity));
}
.focus\:dark\:border-neutral-500:where(.dark, .dark *):focus {
--tw-border-opacity: 1;
border-color: rgb(115 115 115 / var(--tw-border-opacity));
}
.group:hover .group-hover\:dark\:fill-neutral-500:where(.dark, .dark *) {
fill: #737373;
}
.peer:checked ~ .peer-checked\:dark\:fill-neutral-400:where(.dark, .dark *) {
fill: #a3a3a3;
}
+84 -30
View File
@@ -3,40 +3,76 @@
<head>
<title>WireGuard</title>
<meta charset="utf-8"/>
<link href="./css/app.css" rel="stylesheet">
<link rel="manifest" href="./manifest.json">
<link rel="icon" type="image/png" href="./img/favicon.png">
<link rel="apple-touch-icon" href="./img/apple-touch-icon.png">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
</head>
<style>
[v-cloak] {
display: none;
}
.line-chart .apexcharts-svg{
transform: translateY(3px);
}
</style>
<body class="bg-gray-50 dark:bg-neutral-800">
<div id="app">
<div v-cloak class="container mx-auto max-w-3xl px-3 md:px-0">
<div v-cloak class="container mx-auto max-w-3xl px-3 md:px-0 mt-4 xs:mt-6">
<div v-if="authenticated === true">
<span v-if="requiresPassword"
class="text-sm text-gray-400 dark:text-neutral-400 mb-10 mr-2 mt-3 cursor-pointer hover:underline float-right"
@click="logout">
{{$t("logout")}}
<svg class="h-3 inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
</span>
<h1 class="text-4xl dark:text-neutral-200 font-medium mt-2 mb-2">
<img src="./img/logo.png" width="32" class="inline align-middle dark:bg" />
<span class="align-middle">WireGuard</span>
</h1>
<h2 class="text-sm text-gray-400 dark:text-neutral-400 mb-10"></h2>
<div class="flex flex-col-reverse xxs:flex-row flex-auto items-center items-end gap-3">
<h1 class="text-4xl dark:text-neutral-200 font-medium flex-grow self-start mb-4">
<img src="./img/logo.png" width="32" class="inline align-middle dark:bg mr-2" /><span class="align-middle">WireGuard</span>
</h1>
<div class="flex items-center grow-0 gap-3 items-end self-end xxs:self-center">
<!-- Dark / light theme -->
<button @click="toggleTheme"
class="flex items-center justify-center w-8 h-8 rounded-full bg-gray-200 hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600 transition" :title="$t(`theme.${uiTheme}`)">
<svg v-if="uiTheme === 'light'" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round"
d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z" />
</svg>
<svg v-else-if="uiTheme === 'dark'" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5 h-5 text-neutral-400">
<path stroke-linecap="round" stroke-linejoin="round"
d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z" />
</svg>
<svg v-else xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"
class="w-5 h-5 fill-gray-600 dark:fill-neutral-400">
<path
d="M12,2.2c-5.4,0-9.8,4.4-9.8,9.8s4.4,9.8,9.8,9.8s9.8-4.4,9.8-9.8S17.4,2.2,12,2.2z M3.8,12c0-4.5,3.7-8.2,8.2-8.2v16.5C7.5,20.2,3.8,16.5,3.8,12z" />
</svg>
<path stroke-linecap="round" stroke-linejoin="round"
d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25m18 0A2.25 2.25 0 0 0 18.75 3H5.25A2.25 2.25 0 0 0 3 5.25m18 0V12a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 12V5.25" />
</svg>
</button>
<!-- Show / hide charts -->
<label v-if="uiChartType > 0" class="inline-flex items-center justify-center cursor-pointer w-8 h-8 rounded-full bg-gray-200 hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600 whitespace-nowrap transition group" :title="$t('toggleCharts')">
<input type="checkbox" value="" class="sr-only peer" v-model="uiShowCharts" @change="toggleCharts">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" fill="currentColor"
class="w-5 h-5 peer fill-gray-400 peer-checked:fill-gray-600 dark:fill-neutral-600 peer-checked:dark:fill-neutral-400 group-hover:dark:fill-neutral-500 transition">
<path
d="M18.375 2.25c-1.035 0-1.875.84-1.875 1.875v15.75c0 1.035.84 1.875 1.875 1.875h.75c1.035 0 1.875-.84 1.875-1.875V4.125c0-1.036-.84-1.875-1.875-1.875h-.75ZM9.75 8.625c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v11.25c0 1.035-.84 1.875-1.875 1.875h-.75a1.875 1.875 0 0 1-1.875-1.875V8.625ZM3 13.125c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v6.75c0 1.035-.84 1.875-1.875 1.875h-.75A1.875 1.875 0 0 1 3 19.875v-6.75Z" />
</svg>
</label>
<span v-if="requiresPassword"
class="text-sm text-gray-400 dark:text-neutral-400 cursor-pointer hover:underline"
@click="logout">
{{$t("logout")}}
<svg class="h-3 inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
</span>
</div>
</div>
<div class="text-sm text-gray-400 dark:text-neutral-400 mb-5"></div>
<div v-if="latestRelease"
class="bg-red-800 dark:bg-red-100 p-4 text-white dark:text-red-600 text-sm font-small mb-10 rounded-md shadow-lg"
:title="`v${currentRelease} → v${latestRelease.version}`">
@@ -58,15 +94,33 @@
<div class="flex-grow">
<p class="text-2xl font-medium dark:text-neutral-200">{{$t("clients")}}</p>
</div>
<div class="flex-shrink-0">
<div class="flex md:block md:flex-shrink-0">
<!-- Restore configuration -->
<label for="inputRC" :title="$t('titleRestoreConfig')"
class="hover:cursor-pointer hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 max-md:border-r-0 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 rounded-l-full md:rounded inline-flex items-center transition">
<svg inline class="w-4 md:mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"></path>
</svg>
<span class="max-md:hidden text-sm">{{$t("restore")}}</span>
<input id="inputRC" type="file" name="configurationfile" accept="text/*,.json" @change="restoreConfig" class="hidden"/>
</label>
<!-- Backup configuration -->
<a href="./api/wireguard/backup" :title="$t('titleBackupConfig')"
class="hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 max-md:border-x-0 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 md:rounded inline-flex items-center transition">
<svg inline class="w-4 md:mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 0 1-3-3m3 3a3 3 0 1 0 0 6h13.5a3 3 0 1 0 0-6m-16.5-3a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3m-19.5 0a4.5 4.5 0 0 1 .9-2.7L5.737 5.1a3.375 3.375 0 0 1 2.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 0 1 .9 2.7m0 0a3 3 0 0 1-3 3m0 3h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Zm-3 6h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Z"></path>
</svg>
<span class="max-md:hidden text-sm">{{$t("backup")}}</span>
</a>
<!-- New client -->
<button @click="clientCreate = true; clientCreateName = '';"
class="hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 rounded inline-flex items-center transition">
<svg class="w-4 mr-2" inline xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
class="hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 max-md:border-l-0 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 rounded-r-full md:rounded inline-flex items-center transition">
<svg class="w-4 md:mr-2" inline xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<span class="text-sm">{{$t("new")}}</span>
<span class="max-md:hidden text-sm">{{$t("new")}}</span>
</button>
</div>
</div>
@@ -77,17 +131,17 @@
class="relative overflow-hidden border-b last:border-b-0 border-gray-100 dark:border-neutral-600 border-solid">
<!-- Chart -->
<div class="absolute z-0 bottom-0 left-0 right-0" style="top: 60%;">
<apexchart width="100%" height="100%" :options="client.chartOptions" :series="client.transferTxSeries">
<div v-if="uiChartType" :class="`absolute z-0 bottom-0 left-0 right-0 h-6 ${uiChartType === 1 && 'line-chart'}`" >
<apexchart width="100%" height="100%" :options="chartOptionsTX" :series="client.transferTxSeries">
</apexchart>
</div>
<div class="absolute z-0 top-0 left-0 right-0" style="bottom: 60%;">
<apexchart width="100%" height="100%" :options="client.chartOptions" :series="client.transferRxSeries"
<div v-if="uiChartType" :class="`absolute z-0 top-0 left-0 right-0 h-6 ${uiChartType === 1 && 'line-chart'}`" >
<apexchart width="100%" height="100%" :options="chartOptionsRX" :series="client.transferRxSeries"
style="transform: scaleY(-1);">
</apexchart>
</div>
<div class="relative py-5 px-3 z-10 flex flex-col sm:flex-row justify-between gap-3">
<div class="relative py-3 md:py-5 px-3 z-10 flex flex-col sm:flex-row justify-between gap-3">
<div class="flex gap-3 md:gap-4 w-full items-center ">
<!-- Avatar -->
@@ -506,7 +560,7 @@
</svg>
</div>
<input type="password" name="password" :placeholder="$t('password')" v-model="password"
<input type="password" name="password" :placeholder="$t('password')" v-model="password" autocomplete="current-password"
class="px-3 py-2 text-sm dark:bg-neutral-700 text-gray-500 dark:text-gray-500 mb-5 border-2 border-gray-100 dark:border-neutral-800 rounded-lg w-full focus:border-red-800 dark:focus:border-red-800 dark:placeholder:text-neutral-400 outline-none" />
<button v-if="authenticating"
@@ -542,7 +596,7 @@
</div>
<p v-cloak class="text-center m-10 text-gray-300 dark:text-neutral-600 text-xs"> <a class="hover:underline" target="_blank"
<p v-cloak class="text-center m-10 text-gray-300 dark:text-neutral-600 text-xs"> <a class="hover:underline" target="_blank"
href="https://github.com/wg-easy/wg-easy">WireGuard Easy</a> © 2021-2024 by <a class="hover:underline" target="_blank"
href="https://emilenijssen.nl/?ref=wg-easy">Emile Nijssen</a> is licensed under <a class="hover:underline" target="_blank"
href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a> · <a class="hover:underline"
@@ -561,4 +615,4 @@
<script src="./js/app.js"></script>
</body>
</html>
</html>
+15
View File
@@ -50,6 +50,13 @@ class API {
});
}
async getChartType() {
return this.call({
method: 'get',
path: '/ui-chart-type',
});
}
async getSession() {
return this.call({
method: 'get',
@@ -131,4 +138,12 @@ class API {
});
}
async restoreConfiguration(file) {
return this.call({
method: 'put',
path: '/wireguard/restore',
body: { file },
});
}
}
+142 -34
View File
@@ -29,8 +29,24 @@ const i18n = new VueI18n({
messages,
});
const UI_CHART_TYPES = [
{ type: false, strokeWidth: 0 },
{ type: 'line', strokeWidth: 3 },
{ type: 'area', strokeWidth: 0 },
{ type: 'bar', strokeWidth: 0 },
];
const CHART_COLORS = {
rx: { light: 'rgba(128,128,128,0.3)', dark: 'rgba(255,255,255,0.3)' },
tx: { light: 'rgba(128,128,128,0.4)', dark: 'rgba(255,255,255,0.3)' },
gradient: { light: ['rgba(0,0,0,1.0)', 'rgba(0,0,0,1.0)'], dark: ['rgba(128,128,128,0)', 'rgba(128,128,128,0)'] },
};
new Vue({
el: '#app',
components: {
apexchart: VueApexCharts,
},
i18n,
data: {
authenticated: null,
@@ -52,13 +68,16 @@ new Vue({
currentRelease: null,
latestRelease: null,
isDark: null,
uiTrafficStats: false,
uiChartType: 0,
uiShowCharts: localStorage.getItem('uiShowCharts') === '1',
uiTheme: localStorage.theme || 'auto',
prefersDarkScheme: window.matchMedia('(prefers-color-scheme: dark)'),
chartOptions: {
chart: {
background: 'transparent',
type: 'bar',
stacked: false,
toolbar: {
show: false,
@@ -66,11 +85,27 @@ new Vue({
animations: {
enabled: false,
},
parentHeightOffset: 0,
sparkline: {
enabled: true,
},
},
colors: [],
stroke: {
curve: 'smooth',
},
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
type: 'vertical',
shadeIntensity: 0,
gradientToColors: CHART_COLORS.gradient[this.theme],
inverseColors: false,
opacityTo: 0,
stops: [0, 100],
},
},
colors: [
'#DDDDDD', // rx
'#EEEEEE', // tx
],
dataLabels: {
enabled: false,
},
@@ -84,10 +119,10 @@ new Vue({
show: false,
},
axisTicks: {
show: true,
show: false,
},
axisBorder: {
show: true,
show: false,
},
},
yaxis: {
@@ -153,27 +188,42 @@ new Vue({
// Debug
// client.transferRx = this.clientsPersist[client.id].transferRxPrevious + Math.random() * 1000;
// client.transferTx = this.clientsPersist[client.id].transferTxPrevious + Math.random() * 1000;
// client.latestHandshakeAt = new Date();
// this.requiresPassword = true;
this.clientsPersist[client.id].transferRxCurrent = client.transferRx - this.clientsPersist[client.id].transferRxPrevious;
this.clientsPersist[client.id].transferRxPrevious = client.transferRx;
this.clientsPersist[client.id].transferTxCurrent = client.transferTx - this.clientsPersist[client.id].transferTxPrevious;
this.clientsPersist[client.id].transferTxPrevious = client.transferTx;
if (updateCharts) {
this.clientsPersist[client.id].transferRxCurrent = client.transferRx - this.clientsPersist[client.id].transferRxPrevious;
this.clientsPersist[client.id].transferRxPrevious = client.transferRx;
this.clientsPersist[client.id].transferTxCurrent = client.transferTx - this.clientsPersist[client.id].transferTxPrevious;
this.clientsPersist[client.id].transferTxPrevious = client.transferTx;
this.clientsPersist[client.id].transferRxHistory.push(this.clientsPersist[client.id].transferRxCurrent);
this.clientsPersist[client.id].transferRxHistory.shift();
this.clientsPersist[client.id].transferTxHistory.push(this.clientsPersist[client.id].transferTxCurrent);
this.clientsPersist[client.id].transferTxHistory.shift();
this.clientsPersist[client.id].transferTxSeries = [{
name: 'Tx',
data: this.clientsPersist[client.id].transferTxHistory,
}];
this.clientsPersist[client.id].transferRxSeries = [{
name: 'Rx',
data: this.clientsPersist[client.id].transferRxHistory,
}];
client.transferTxHistory = this.clientsPersist[client.id].transferTxHistory;
client.transferRxHistory = this.clientsPersist[client.id].transferRxHistory;
client.transferMax = Math.max(...client.transferTxHistory, ...client.transferRxHistory);
client.transferTxSeries = this.clientsPersist[client.id].transferTxSeries;
client.transferRxSeries = this.clientsPersist[client.id].transferRxSeries;
}
client.transferTxCurrent = this.clientsPersist[client.id].transferTxCurrent;
client.transferRxCurrent = this.clientsPersist[client.id].transferRxCurrent;
client.transferTxHistory = this.clientsPersist[client.id].transferTxHistory;
client.transferRxHistory = this.clientsPersist[client.id].transferRxHistory;
client.transferMax = Math.max(...client.transferTxHistory, ...client.transferRxHistory);
client.hoverTx = this.clientsPersist[client.id].hoverTx;
client.hoverRx = this.clientsPersist[client.id].hoverRx;
@@ -249,15 +299,42 @@ new Vue({
.catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error));
},
toggleTheme() {
if (this.isDark) {
localStorage.theme = 'light';
document.documentElement.classList.remove('dark');
restoreConfig(e) {
e.preventDefault();
const file = e.currentTarget.files.item(0);
if (file) {
file.text()
.then((content) => {
this.api.restoreConfiguration(content)
.then((_result) => alert('The configuration was updated.'))
.catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error));
})
.catch((err) => alert(err.message || err.toString()));
} else {
localStorage.theme = 'dark';
document.documentElement.classList.add('dark');
alert('Failed to load your file!');
}
this.isDark = !this.isDark;
},
toggleTheme() {
const themes = ['light', 'dark', 'auto'];
const currentIndex = themes.indexOf(this.uiTheme);
const newIndex = (currentIndex + 1) % themes.length;
this.uiTheme = themes[newIndex];
localStorage.theme = this.uiTheme;
this.setTheme(this.uiTheme);
},
setTheme(theme) {
const { classList } = document.documentElement;
const shouldAddDarkClass = theme === 'dark' || (theme === 'auto' && this.prefersDarkScheme.matches);
classList.toggle('dark', shouldAddDarkClass);
},
handlePrefersChange(e) {
if (localStorage.theme === 'auto') {
this.setTheme(e.matches ? 'dark' : 'light');
}
},
toggleCharts() {
localStorage.setItem('uiShowCharts', this.uiShowCharts ? 1 : 0);
},
},
filters: {
@@ -267,10 +344,8 @@ new Vue({
},
},
mounted() {
this.isDark = false;
if (localStorage.theme === 'dark') {
this.isDark = true;
}
this.prefersDarkScheme.addListener(this.handlePrefersChange);
this.setTheme(this.uiTheme);
this.api = new API();
this.api.getSession()
@@ -278,7 +353,7 @@ new Vue({
this.authenticated = session.authenticated;
this.requiresPassword = session.requiresPassword;
this.refresh({
updateCharts: true,
updateCharts: this.updateCharts,
}).catch((err) => {
alert(err.message || err.toString());
});
@@ -289,7 +364,7 @@ new Vue({
setInterval(() => {
this.refresh({
updateCharts: true,
updateCharts: this.updateCharts,
}).catch(console.error);
}, 1000);
@@ -298,10 +373,17 @@ new Vue({
this.uiTrafficStats = res;
})
.catch(() => {
console.log('Failed to get ui-traffic-stats');
this.uiTrafficStats = false;
});
this.api.getChartType()
.then((res) => {
this.uiChartType = parseInt(res, 10);
})
.catch(() => {
this.uiChartType = 0;
});
Promise.resolve().then(async () => {
const lang = await this.api.getLang();
if (lang !== localStorage.getItem('lang') && i18n.availableLocales.includes(lang)) {
@@ -324,13 +406,39 @@ new Vue({
return releasesArray[0];
});
console.log(`Current Release: ${currentRelease}`);
console.log(`Latest Release: ${latestRelease.version}`);
if (currentRelease >= latestRelease.version) return;
this.currentRelease = currentRelease;
this.latestRelease = latestRelease;
}).catch((err) => console.error(err));
},
computed: {
chartOptionsTX() {
const opts = {
...this.chartOptions,
colors: [CHART_COLORS.tx[this.theme]],
};
opts.chart.type = UI_CHART_TYPES[this.uiChartType].type || false;
opts.stroke.width = UI_CHART_TYPES[this.uiChartType].strokeWidth;
return opts;
},
chartOptionsRX() {
const opts = {
...this.chartOptions,
colors: [CHART_COLORS.rx[this.theme]],
};
opts.chart.type = UI_CHART_TYPES[this.uiChartType].type || false;
opts.stroke.width = UI_CHART_TYPES[this.uiChartType].strokeWidth;
return opts;
},
updateCharts() {
return this.uiChartType > 0 && this.uiShowCharts;
},
theme() {
if (this.uiTheme === 'auto') {
return this.prefersDarkScheme.matches ? 'dark' : 'light';
}
return this.uiTheme;
},
},
});
+89 -17
View File
@@ -28,6 +28,12 @@ const messages = { // eslint-disable-line no-unused-vars
downloadConfig: 'Download Configuration',
madeBy: 'Made by',
donate: 'Donate',
toggleCharts: 'Show/hide Charts',
theme: { dark: 'Dark theme', light: 'Light theme', auto: 'Auto theme' },
restore: 'Restore',
backup: 'Backup',
titleRestoreConfig: 'Restore your configuration',
titleBackupConfig: 'Backup your configuration',
},
ua: {
name: 'Ім`я',
@@ -51,10 +57,17 @@ const messages = { // eslint-disable-line no-unused-vars
disableClient: 'Вимкнути клієнта',
enableClient: 'Увімкнути клієнта',
noClients: 'Ще немає клієнтів.',
noPrivKey: 'У цього клієнта немає відомого приватного ключа. Неможливо створити конфігурацію.',
showQR: 'Показати QR-код',
downloadConfig: 'Завантажити конфігурацію',
madeBy: 'Зроблено',
donate: 'Пожертвувати',
toggleCharts: 'Показати/сховати діаграми',
theme: { dark: 'Темна тема', light: 'Світла тема', auto: 'Автоматична тема' },
restore: 'Відновити',
backup: 'Резервна копія',
titleRestoreConfig: 'Відновити конфігурацію',
titleBackupConfig: 'Створити резервну копію конфігурації',
},
ru: {
name: 'Имя',
@@ -78,10 +91,17 @@ const messages = { // eslint-disable-line no-unused-vars
disableClient: 'Выключить клиента',
enableClient: 'Включить клиента',
noClients: 'Пока нет клиентов.',
noPrivKey: 'Невозможно создать конфигурацию: у клиента нет известного приватного ключа.',
showQR: 'Показать QR-код',
downloadConfig: 'Скачать конфигурацию',
madeBy: 'Автор',
donate: 'Поблагодарить',
toggleCharts: 'Показать/скрыть графики',
theme: { dark: 'Темная тема', light: 'Светлая тема', auto: 'Как в системе' },
restore: 'Восстановить',
backup: 'Резервная копия',
titleRestoreConfig: 'Восстановить конфигурацию',
titleBackupConfig: 'Создать резервную копию конфигурации',
},
tr: { // Müslüm Barış Korkmazer @babico
name: 'İsim',
@@ -97,19 +117,25 @@ const messages = { // eslint-disable-line no-unused-vars
deleteDialog2: 'Bu işlem geri alınamaz.',
cancel: 'İptal',
create: 'Oluştur',
createdAt: 'Şu saatte oluşturuldu: ',
createdOn: 'Şu saatte oluşturuldu: ',
lastSeen: 'Son görülme tarihi: ',
totalDownload: 'Toplam İndirme: ',
totalUpload: 'Toplam Yükleme: ',
newClient: 'Yeni Kullanıcı',
disableClient: 'İstemciyi Devre Dışı Bırak',
enableClient: 'İstemciyi Etkinleştir',
disableClient: 'Kullanıcıyı Devre Dışı Bırak',
enableClient: 'Kullanıcıyı Etkinleştir',
noClients: 'Henüz kullanıcı yok.',
noPrivKey: 'Bu istemcinin bilinen bir özel anahtarı yok. Yapılandırma oluşturulamıyor.',
showQR: 'QR Kodunu Göster',
downloadConfig: 'Yapılandırmayı İndir',
madeBy: 'Yapan Kişi: ',
donate: 'Bağış Yap',
changeLang: 'Dil Değiştir',
toggleCharts: 'Grafiği göster/gizle',
theme: { dark: 'Karanlık tema', light: 'Açık tema', auto: 'Otomatik tema' },
restore: 'Geri yükle',
backup: 'Yedekle',
titleRestoreConfig: 'Yapılandırmanızı geri yükleyin',
titleBackupConfig: 'Yapılandırmanızı yedekleyin',
},
no: { // github.com/digvalley
name: 'Navn',
@@ -191,6 +217,10 @@ const messages = { // eslint-disable-line no-unused-vars
downloadConfig: 'Télécharger la configuration',
madeBy: 'Développé par',
donate: 'Soutenir',
restore: 'Restaurer',
backup: 'Sauvegarder',
titleRestoreConfig: 'Restaurer votre configuration',
titleBackupConfig: 'Sauvegarder votre configuration',
},
de: { // github.com/florian-asche
name: 'Name',
@@ -219,6 +249,10 @@ const messages = { // eslint-disable-line no-unused-vars
downloadConfig: 'Konfiguration herunterladen',
madeBy: 'Erstellt von',
donate: 'Spenden',
restore: 'Wiederherstellen',
backup: 'Sichern',
titleRestoreConfig: 'Stelle deine Konfiguration wieder her',
titleBackupConfig: 'Sichere deine Konfiguration',
},
ca: { // github.com/guillembonet
name: 'Nom',
@@ -273,6 +307,12 @@ const messages = { // eslint-disable-line no-unused-vars
downloadConfig: 'Descargar configuración',
madeBy: 'Hecho por',
donate: 'Donar',
toggleCharts: 'Mostrar/Ocultar gráficos',
theme: { dark: 'Modo oscuro', light: 'Modo claro', auto: 'Modo automático' },
restore: 'Restaurar',
backup: 'Realizar copia de seguridad',
titleRestoreConfig: 'Restaurar su configuración',
titleBackupConfig: 'Realizar copia de seguridad de su configuración',
},
ko: {
name: '이름',
@@ -441,27 +481,27 @@ const messages = { // eslint-disable-line no-unused-vars
password: '密碼',
signIn: '登入',
logout: '登出',
updateAvailable: '有新版本可用!',
updateAvailable: '有新版本可以使用!',
update: '更新',
clients: '客戶',
new: '建',
deleteClient: '刪除客戶',
clients: '使用者',
new: '建',
deleteClient: '刪除使用者',
deleteDialog1: '您確定要刪除',
deleteDialog2: '此操作無法撤銷。',
deleteDialog2: '此作業無法復原。',
cancel: '取消',
create: '建立',
createdOn: '建立於 ',
lastSeen: '最後訪問於 ',
lastSeen: '最後存取於 ',
totalDownload: '總下載: ',
totalUpload: '總上傳: ',
newClient: '新戶',
disableClient: '禁用客戶',
enableClient: '啟用客戶',
noClients: '目前沒有客戶。',
showQR: '顯示二維碼',
downloadConfig: '下載配置',
newClient: '新戶',
disableClient: '停用使用者',
enableClient: '啟用使用者',
noClients: '目前沒有使用者。',
showQR: '顯示 QR Code',
downloadConfig: '下載 Config 檔',
madeBy: '由',
donate: '捐贈',
donate: '抖內',
},
it: {
name: 'Nome',
@@ -489,6 +529,10 @@ const messages = { // eslint-disable-line no-unused-vars
downloadConfig: 'Scarica configurazione',
madeBy: 'Realizzato da',
donate: 'Donazione',
restore: 'Ripristina',
backup: 'Backup',
titleRestoreConfig: 'Ripristina la tua configurazione',
titleBackupConfig: 'Esegui il backup della tua configurazione',
},
th: {
name: 'ชื่อ',
@@ -517,4 +561,32 @@ const messages = { // eslint-disable-line no-unused-vars
madeBy: 'สร้างโดย',
donate: 'บริจาค',
},
hi: { // github.com/rahilarious
name: 'नाम',
password: 'पासवर्ड',
signIn: 'लॉगिन',
logout: 'लॉगआउट',
updateAvailable: 'अपडेट उपलब्ध है!',
update: 'अपडेट',
clients: 'उपयोगकर्ताये',
new: 'नया',
deleteClient: 'उपयोगकर्ता हटाएँ',
deleteDialog1: 'क्या आपको पक्का हटाना है',
deleteDialog2: 'यह निर्णय पलट नहीं सकता।',
cancel: 'कुछ ना करें',
create: 'बनाएं',
createdOn: 'सर्जन तारीख ',
lastSeen: 'पिछली बार देखे गए थे ',
totalDownload: 'कुल डाउनलोड: ',
totalUpload: 'कुल अपलोड: ',
newClient: 'नया उपयोगकर्ता',
disableClient: 'उपयोगकर्ता स्थगित कीजिये',
enableClient: 'उपयोगकर्ता शुरू कीजिये',
noClients: 'अभी तक कोई भी उपयोगकर्ता नहीं है।',
noPrivKey: 'ये उपयोगकर्ता की कोई भी गुप्त चाबी नहीं हे। बना नहीं सकते।',
showQR: 'क्यू आर कोड देखिये',
downloadConfig: 'डाउनलोड कॉन्फीग्यूरेशन',
madeBy: 'सर्जक',
donate: 'दान करें',
},
};
File diff suppressed because one or more lines are too long