Browse Source

Merge branch 'feat-koa' into fixup-koa

pull/792/head
Philip H 3 years ago
committed by GitHub
parent
commit
60adb4129b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      .github/workflows/npm-update-bot.yml
  2. 1
      .gitignore
  3. 1
      README.md
  4. 3
      docker-compose.yml
  5. 1
      src/config.js
  6. 10
      src/lib/Server.js
  7. 111
      src/package-lock.json
  8. 2
      src/package.json
  9. 461
      src/www/css/app.css
  10. 78
      src/www/index.html
  11. 7
      src/www/js/api.js
  12. 15
      src/www/js/app.js
  13. 194
      src/www/js/i18n.js
  14. 10
      src/www/js/vendor/apexcharts.min.js
  15. 1
      src/www/js/vendor/timeago.full.min.js
  16. 1
      src/www/js/vendor/timeago.min.js
  17. 6
      src/www/js/vendor/vue-i18n.min.js
  18. 6
      src/www/js/vendor/vue.min.js
  19. 22
      wg-easy.service

1
.github/workflows/npm-update-bot.yml

@ -34,6 +34,7 @@ jobs:
cd src
ncu -u
npm update
npm run buildcss
git config --global user.name 'NPM Update Bot'
git config --global user.email '[email protected]'
git add .

1
.gitignore

@ -2,3 +2,4 @@
/wg0.conf
/wg0.json
.DS_Store
*.swp

1
README.md

@ -50,6 +50,7 @@ To automatically install & run wg-easy, simply run:
<pre>
$ docker run -d \
--name=wg-easy \
-e LANG=de
-e WG_HOST=<b>🚨YOUR_SERVER_IP</b> \
-e PASSWORD=<b>🚨YOUR_ADMIN_PASSWORD</b> \
-v ~/.wg-easy:/etc/wireguard \

3
docker-compose.yml

@ -5,6 +5,9 @@ volumes:
services:
wg-easy:
environment:
# Change Language:
# (Supports: en, ru, tr, no, pl, fr, de)
- LANG=de
# ⚠️ Required:
# Change this to your host's public address
- WG_HOST=raspberrypi.local

1
src/config.js

@ -28,3 +28,4 @@ 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 || '';
module.exports.LANG = process.env.LANG || 'en';

10
src/lib/Server.js

@ -16,7 +16,11 @@ const ServerError = require('./ServerError');
const WireGuard = require('../services/WireGuard');
const {
PORT, WEBUI_HOST, RELEASE, PASSWORD,
PORT,
WEBUI_HOST,
RELEASE,
PASSWORD,
LANG,
} = require('../config');
module.exports = class Server {
@ -48,6 +52,10 @@ module.exports = class Server {
ctx.body = RELEASE;
})
.get('/api/lang', (Util.promisify(async () => {
return LANG;
})))
// Authentication
.get('/api/session', async (ctx) => {
const requiresPassword = !!process.env.PASSWORD;

111
src/package-lock.json

@ -422,9 +422,9 @@
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.20",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
"version": "0.3.22",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
"integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@ -515,16 +515,16 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.18.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz",
"integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==",
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.0.tgz",
"integrity": "sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "6.18.1",
"@typescript-eslint/type-utils": "6.18.1",
"@typescript-eslint/utils": "6.18.1",
"@typescript-eslint/visitor-keys": "6.18.1",
"@typescript-eslint/scope-manager": "6.19.0",
"@typescript-eslint/type-utils": "6.19.0",
"@typescript-eslint/utils": "6.19.0",
"@typescript-eslint/visitor-keys": "6.19.0",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
@ -550,15 +550,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "6.18.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz",
"integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==",
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.0.tgz",
"integrity": "sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "6.18.1",
"@typescript-eslint/types": "6.18.1",
"@typescript-eslint/typescript-estree": "6.18.1",
"@typescript-eslint/visitor-keys": "6.18.1",
"@typescript-eslint/scope-manager": "6.19.0",
"@typescript-eslint/types": "6.19.0",
"@typescript-eslint/typescript-estree": "6.19.0",
"@typescript-eslint/visitor-keys": "6.19.0",
"debug": "^4.3.4"
},
"engines": {
@ -578,13 +578,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "6.18.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz",
"integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==",
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz",
"integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.18.1",
"@typescript-eslint/visitor-keys": "6.18.1"
"@typescript-eslint/types": "6.19.0",
"@typescript-eslint/visitor-keys": "6.19.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
@ -595,13 +595,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "6.18.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz",
"integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==",
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz",
"integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "6.18.1",
"@typescript-eslint/utils": "6.18.1",
"@typescript-eslint/typescript-estree": "6.19.0",
"@typescript-eslint/utils": "6.19.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
},
@ -622,9 +622,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "6.18.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz",
"integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==",
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz",
"integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
@ -635,13 +635,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "6.18.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz",
"integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==",
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz",
"integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.18.1",
"@typescript-eslint/visitor-keys": "6.18.1",
"@typescript-eslint/types": "6.19.0",
"@typescript-eslint/visitor-keys": "6.19.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -663,17 +663,17 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "6.18.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz",
"integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==",
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz",
"integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.18.1",
"@typescript-eslint/types": "6.18.1",
"@typescript-eslint/typescript-estree": "6.18.1",
"@typescript-eslint/scope-manager": "6.19.0",
"@typescript-eslint/types": "6.19.0",
"@typescript-eslint/typescript-estree": "6.19.0",
"semver": "^7.5.4"
},
"engines": {
@ -688,12 +688,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "6.18.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz",
"integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==",
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz",
"integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.18.1",
"@typescript-eslint/types": "6.19.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
@ -4291,13 +4291,13 @@
}
},
"node_modules/safe-array-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz",
"integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz",
"integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.2.1",
"call-bind": "^1.0.5",
"get-intrinsic": "^1.2.2",
"has-symbols": "^1.0.3",
"isarray": "^2.0.5"
},
@ -4370,14 +4370,15 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz",
"integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==",
"dependencies": {
"define-data-property": "^1.1.1",
"get-intrinsic": "^1.2.1",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.2",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.0"
"has-property-descriptors": "^1.0.1"
},
"engines": {
"node": ">= 0.4"

2
src/package.json

@ -8,7 +8,7 @@
"serve": "DEBUG=Server,WireGuard 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 --watch"
"buildcss": "npx tailwindcss -i ./www/src/css/app.css -o ./www/css/app.css"
},
"author": "Emile Nijssen",
"license": "GPL",

461
src/www/css/app.css

@ -1,5 +1,5 @@
/*
! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com
! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com
*/
/*
@ -32,9 +32,11 @@
4. Use the user's configured `sans` font-family by default.
5. Use the user's configured `sans` font-feature-settings by default.
6. Use the user's configured `sans` font-variation-settings by default.
7. Disable tap highlights on iOS
*/
html {
html,
:host {
line-height: 1.5;
/* 1 */
-webkit-text-size-adjust: 100%;
@ -44,12 +46,14 @@ html {
-o-tab-size: 4;
tab-size: 4;
/* 3 */
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
/* 4 */
font-feature-settings: normal;
/* 5 */
font-variation-settings: normal;
/* 6 */
-webkit-tap-highlight-color: transparent;
/* 7 */
}
/*
@ -121,8 +125,10 @@ strong {
}
/*
1. Use the user's configured `mono` font family by default.
2. Correct the odd `em` font sizing in all browsers.
1. Use the user's configured `mono` font-family by default.
2. Use the user's configured `mono` font-feature-settings by default.
3. Use the user's configured `mono` font-variation-settings by default.
4. Correct the odd `em` font sizing in all browsers.
*/
code,
@ -131,8 +137,12 @@ samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/* 1 */
font-size: 1em;
font-feature-settings: normal;
/* 2 */
font-variation-settings: normal;
/* 3 */
font-size: 1em;
/* 4 */
}
/*
@ -191,6 +201,10 @@ select,
textarea {
font-family: inherit;
/* 1 */
font-feature-settings: inherit;
/* 1 */
font-variation-settings: inherit;
/* 1 */
font-size: 100%;
/* 1 */
font-weight: inherit;
@ -341,6 +355,14 @@ menu {
padding: 0;
}
/*
Reset default styling for dialogs.
*/
dialog {
padding: 0;
}
/*
Prevent resizing textareas horizontally by default.
*/
@ -746,6 +768,10 @@ video {
height: 3rem;
}
.h-14 {
height: 3.5rem;
}
.h-2 {
height: 0.5rem;
}
@ -758,12 +784,12 @@ video {
height: 0.75rem;
}
.h-4 {
height: 1rem;
.h-32 {
height: 8rem;
}
.h-5 {
height: 1.25rem;
.h-4 {
height: 1rem;
}
.h-6 {
@ -907,6 +933,12 @@ video {
overflow-y: auto;
}
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.rounded {
border-radius: 0.25rem;
}
@ -1027,10 +1059,6 @@ video {
padding: 0.5rem;
}
.p-2\.5 {
padding: 0.625rem;
}
.p-3 {
padding: 0.75rem;
}
@ -1067,11 +1095,6 @@ video {
padding-right: 1.25rem;
}
.py-1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
@ -1398,200 +1421,6 @@ video {
opacity: 1;
}
:is(.dark .dark\:border-green-600) {
--tw-border-opacity: 1;
border-color: rgb(22 163 74 / var(--tw-border-opacity));
}
:is(.dark .dark\:border-neutral-500) {
--tw-border-opacity: 1;
border-color: rgb(115 115 115 / var(--tw-border-opacity));
}
:is(.dark .dark\:border-neutral-600) {
--tw-border-opacity: 1;
border-color: rgb(82 82 82 / var(--tw-border-opacity));
}
:is(.dark .dark\:bg-black) {
--tw-bg-opacity: 1;
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
}
:is(.dark .dark\:bg-green-100) {
--tw-bg-opacity: 1;
background-color: rgb(220 252 231 / var(--tw-bg-opacity));
}
:is(.dark .dark\:bg-green-600) {
--tw-bg-opacity: 1;
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
}
:is(.dark .dark\:bg-neutral-200) {
--tw-bg-opacity: 1;
background-color: rgb(229 229 229 / var(--tw-bg-opacity));
}
:is(.dark .dark\:bg-neutral-400) {
--tw-bg-opacity: 1;
background-color: rgb(163 163 163 / var(--tw-bg-opacity));
}
:is(.dark .dark\:bg-neutral-500) {
--tw-bg-opacity: 1;
background-color: rgb(115 115 115 / var(--tw-bg-opacity));
}
:is(.dark .dark\:bg-neutral-600) {
--tw-bg-opacity: 1;
background-color: rgb(82 82 82 / var(--tw-bg-opacity));
}
:is(.dark .dark\:bg-neutral-700) {
--tw-bg-opacity: 1;
background-color: rgb(64 64 64 / var(--tw-bg-opacity));
}
:is(.dark .dark\:bg-neutral-800) {
--tw-bg-opacity: 1;
background-color: rgb(38 38 38 / var(--tw-bg-opacity));
}
:is(.dark .dark\:bg-red-100) {
--tw-bg-opacity: 1;
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
}
:is(.dark .dark\:text-green-600) {
--tw-text-opacity: 1;
color: rgb(22 163 74 / var(--tw-text-opacity));
}
:is(.dark .dark\:text-neutral-200) {
--tw-text-opacity: 1;
color: rgb(229 229 229 / var(--tw-text-opacity));
}
:is(.dark .dark\:text-neutral-300) {
--tw-text-opacity: 1;
color: rgb(212 212 212 / var(--tw-text-opacity));
}
:is(.dark .dark\:text-neutral-400) {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
:is(.dark .dark\:text-neutral-50) {
--tw-text-opacity: 1;
color: rgb(250 250 250 / var(--tw-text-opacity));
}
:is(.dark .dark\:text-neutral-500) {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
}
:is(.dark .dark\:text-neutral-600) {
--tw-text-opacity: 1;
color: rgb(82 82 82 / var(--tw-text-opacity));
}
:is(.dark .dark\:text-red-300) {
--tw-text-opacity: 1;
color: rgb(252 165 165 / var(--tw-text-opacity));
}
:is(.dark .dark\:text-red-600) {
--tw-text-opacity: 1;
color: rgb(220 38 38 / var(--tw-text-opacity));
}
:is(.dark .dark\:opacity-50) {
opacity: 0.5;
}
:is(.dark .dark\:placeholder\:text-neutral-400)::-moz-placeholder {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
:is(.dark .dark\:placeholder\:text-neutral-400)::placeholder {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
:is(.dark .dark\:placeholder\:text-neutral-500)::-moz-placeholder {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
}
:is(.dark .dark\:placeholder\:text-neutral-500)::placeholder {
--tw-text-opacity: 1;
color: rgb(115 115 115 / var(--tw-text-opacity));
}
:is(.dark .dark\:hover\:border-green-600:hover) {
--tw-border-opacity: 1;
border-color: rgb(22 163 74 / var(--tw-border-opacity));
}
:is(.dark .dark\:hover\:border-neutral-600:hover) {
--tw-border-opacity: 1;
border-color: rgb(82 82 82 / var(--tw-border-opacity));
}
:is(.dark .dark\:hover\:bg-green-600:hover) {
--tw-bg-opacity: 1;
background-color: rgb(22 163 74 / var(--tw-bg-opacity));
}
:is(.dark .dark\:hover\:bg-neutral-500:hover) {
--tw-bg-opacity: 1;
background-color: rgb(115 115 115 / var(--tw-bg-opacity));
}
:is(.dark .dark\:hover\:bg-neutral-600:hover) {
--tw-bg-opacity: 1;
background-color: rgb(82 82 82 / var(--tw-bg-opacity));
}
:is(.dark .dark\:hover\:bg-red-200:hover) {
--tw-bg-opacity: 1;
background-color: rgb(254 202 202 / var(--tw-bg-opacity));
}
:is(.dark .dark\:hover\:bg-red-800:hover) {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
}
:is(.dark .dark\:hover\:text-green-100:hover) {
--tw-text-opacity: 1;
color: rgb(220 252 231 / var(--tw-text-opacity));
}
:is(.dark .dark\:hover\:text-neutral-700:hover) {
--tw-text-opacity: 1;
color: rgb(64 64 64 / var(--tw-text-opacity));
}
:is(.dark .dark\:hover\:text-white:hover) {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
:is(.dark .dark\:focus\:border-neutral-500:focus) {
--tw-border-opacity: 1;
border-color: rgb(115 115 115 / var(--tw-border-opacity));
}
:is(.dark .focus\:dark\:border-neutral-500):focus {
--tw-border-opacity: 1;
border-color: rgb(115 115 115 / var(--tw-border-opacity));
}
@media (min-width: 640px) {
.sm\:mx-0 {
margin-left: 0px;
@ -1716,3 +1545,209 @@ video {
padding-bottom: 0px;
}
}
@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));
}
}

78
src/www/index.html

@ -3,13 +3,7 @@
<head>
<title>WireGuard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'media',
}
</script>
<!-- <link href="./css/vendor/tailwind.min.css" rel="stylesheet"> -->
<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">
@ -32,7 +26,8 @@
<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">
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"
@ -50,13 +45,13 @@
:title="`v${currentRelease} → v${latestRelease.version}`">
<div class="container mx-auto flex flex-row flex-auto items-center">
<div class="flex-grow">
<p class="font-bold">There is an update available!</p>
<p class="font-bold">{{$t("updateAvailable")}}</p>
<p>{{latestRelease.changelog}}</p>
</div>
<a href="https://github.com/wg-easy/wg-easy#updating" target="_blank"
class="p-3 rounded-md bg-white dark:bg-red-100 float-right font-sm font-semibold text-red-800 dark:text-red-600 flex-shrink-0 border-2 border-red-800 dark:border-red-600 hover:border-white dark:hover:border-red-600 hover:text-white dark:hover:text-red-100 hover:bg-red-800 dark:hover:bg-red-600 transition-all">
Update
{{$t("update")}}
</a>
</div>
</div>
@ -64,7 +59,7 @@
<div class="shadow-md rounded-lg bg-white dark:bg-neutral-700 overflow-hidden">
<div class="flex flex-row flex-auto items-center p-3 px-5 border-b-2 border-gray-100 dark:border-neutral-600">
<div class="flex-grow">
<p class="text-2xl font-medium dark:text-neutral-200">Clients</p>
<p class="text-2xl font-medium dark:text-neutral-200">{{$t("clients")}}</p>
</div>
<div class="flex-shrink-0">
<button @click="clientCreate = true; clientCreateName = '';"
@ -74,7 +69,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<span class="text-sm">New</span>
<span class="text-sm">{{$t("new")}}</span>
</button>
</div>
</div>
@ -94,7 +89,6 @@
style="transform: scaleY(-1);">
</apexchart>
</div>
<div class="relative p-5 z-10 flex flex-col md:flex-row justify-between">
<div class="flex items-center pb-2 md:pb-0">
<div class="h-10 w-10 mr-5 rounded-full bg-gray-50 relative">
@ -118,7 +112,7 @@
<!-- Name -->
<div class="text-gray-700 dark:text-neutral-200 group"
:title="'Created on ' + dateTime(new Date(client.createdAt))">
:title="$t('createdOn') + dateTime(new Date(client.createdAt))">
<!-- Show -->
<input v-show="clientEditNameId === client.id" v-model="clientEditName"
@ -171,7 +165,7 @@
</span>
<!-- Transfer TX -->
<span v-if="client.transferTx" :title="'Total Download: ' + bytes(client.transferTx)">
<span v-if="client.transferTx" :title="$t('totalDownload') + bytes(client.transferTx)">
·
<svg class="align-middle h-3 inline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
@ -183,7 +177,7 @@
</span>
<!-- Transfer RX -->
<span v-if="client.transferRx" :title="'Total Upload: ' + bytes(client.transferRx)">
<span v-if="client.transferRx" :title="$t('totalUpload') + bytes(client.transferRx)">
·
<svg class="align-middle h-3 inline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
@ -196,7 +190,7 @@
<!-- Last seen -->
<span v-if="client.latestHandshakeAt"
:title="'Last seen on ' + dateTime(new Date(client.latestHandshakeAt))">
:title="$t('lastSeen') + dateTime(new Date(client.latestHandshakeAt))">
· {{new Date(client.latestHandshakeAt) | timeago}}
</span>
</div>
@ -207,19 +201,22 @@
<div class="text-gray-400 dark:text-neutral-400 flex gap-1 items-center justify-between">
<!-- Enable/Disable -->
<div @click="disableClient(client)" v-if="client.enabled === true" title="Disable Client"
<div @click="disableClient(client)" v-if="client.enabled === true" :title="$t('disableClient')"
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-red-800 cursor-pointer hover:bg-red-700 transition-all">
<div class="rounded-full w-4 h-4 m-1 ml-5 bg-white"></div>
</div>
<div @click="enableClient(client)" v-if="client.enabled === false" title="Enable Client"
<div @click="enableClient(client)" v-if="client.enabled === false" :title="$t('enableClient')"
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-gray-200 dark:bg-neutral-400 cursor-pointer hover:bg-gray-300 dark:hover:bg-neutral-500 transition-all">
<div class="rounded-full w-4 h-4 m-1 bg-white"></div>
</div>
<!-- Show QR-->
<button
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition"
title="Show QR Code" @click="qrcode = `./api/wireguard/client/${client.id}/qrcode.svg`">
:title="$t('showQR')" @click="qrcode = `./api/wireguard/client/${client.id}/qrcode.svg`">
<svg class="w-5" 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"
@ -230,7 +227,7 @@
<!-- Download Config -->
<a :href="'./api/wireguard/client/' + client.id + '/configuration'" download
class="align-middle inline-block bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition"
title="Download Configuration">
:title="$t('downloadConfig')">
<svg class="w-5" 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"
@ -239,9 +236,10 @@
</a>
<!-- Delete -->
<button
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition"
title="Delete Client" @click="clientDelete = client">
:title="$t('deleteClient')" @click="clientDelete = client">
<svg class="w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
@ -256,7 +254,7 @@
</div>
<div v-if="clients && clients.length === 0">
<p class="text-center m-10 text-gray-400 dark:text-neutral-400 text-sm">
There are no clients yet.<br /><br />
{{$t("noClients")}}<br /><br />
<button @click="clientCreate = true; clientCreateName = '';"
class="bg-red-800 hover:bg-red-700 text-white border-2 border-none 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"
@ -264,7 +262,7 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<span class="text-sm">New Client</span>
<span class="text-sm">{{$t("newClient")}}</span>
</button>
</p>
</div>
@ -340,13 +338,13 @@
</div>
<div class="flex-grow mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-neutral-200" id="modal-headline">
New Client
{{$t("newClient")}}
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">
<input
class="rounded p-2 border-2 dark:bg-neutral-700 dark:text-neutral-200 border-gray-100 dark:border-neutral-600 focus:border-gray-200 focus:dark:border-neutral-500 dark:placeholder:text-neutral-400 outline-none w-full"
type="text" v-model.trim="clientCreateName" placeholder="Name" />
type="text" v-model.trim="clientCreateName" :placeholder="$t('name')" />
</p>
</div>
</div>
@ -355,15 +353,15 @@
<div class="bg-gray-50 dark:bg-neutral-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button v-if="clientCreateName.length" type="button" @click="createClient(); clientCreate = null"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-800 text-base font-medium text-white hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm">
Create
{{$t("create")}}
</button>
<button v-else type="button"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-200 dark:bg-neutral-400 text-base font-medium text-white dark:text-neutral-300 sm:ml-3 sm:w-auto sm:text-sm cursor-not-allowed">
Create
{{$t("create")}}
</button>
<button type="button" @click="clientCreate = null"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-neutral-500 shadow-sm px-4 py-2 bg-white dark:bg-neutral-500 text-base font-medium text-gray-700 dark:text-neutral-50 hover:bg-gray-50 dark:hover:bg-neutral-600 dark:hover:border-neutral-600 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
{{$t("cancel")}}
</button>
</div>
</div>
@ -415,12 +413,11 @@
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-neutral-200" id="modal-headline">
Delete Client
{{$t("deleteClient")}}
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500 dark:text-neutral-300">
Are you sure you want to delete <strong>{{clientDelete.name}}</strong>?
This action cannot be undone.
{{$t("deleteDialog1")}} <strong>{{clientDelete.name}}</strong>? {{$t("deleteDialog2")}}
</p>
</div>
</div>
@ -429,11 +426,11 @@
<div class="bg-gray-50 dark:bg-neutral-600 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" @click="deleteClient(clientDelete); clientDelete = null"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 dark:bg-red-600 text-base font-medium text-white dark:text-white hover:bg-red-700 dark:hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm">
Delete
{{$t("deleteClient")}}
</button>
<button type="button" @click="clientDelete = null"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-neutral-500 shadow-sm px-4 py-2 bg-white dark:bg-neutral-500 text-base font-medium text-gray-700 dark:text-neutral-50 hover:bg-gray-50 dark:hover:bg-neutral-600 dark:hover:border-neutral-600 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
{{$t("cancel")}}
</button>
</div>
</div>
@ -454,7 +451,7 @@
</svg>
</div>
<input type="password" name="password" placeholder="Password" v-model="password"
<input type="password" name="password" :placeholder="$t('password')" v-model="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"
@ -469,10 +466,10 @@
</button>
<input v-if="!authenticating && password" type="submit"
class="bg-red-800 dark:bg-red-800 w-full rounded shadow py-2 text-sm text-white dark:text-white hover:bg-red-700 dark:hover:bg-red-700 transition cursor-pointer"
value="Sign In">
:value="$t('signIn')">
<input v-if="!authenticating && !password" type="submit"
class="bg-gray-200 dark:bg-neutral-800 w-full rounded shadow py-2 text-sm text-white dark:text-white cursor-not-allowed"
value="Sign In">
:value="$t('signIn')">
</form>
</div>
@ -494,17 +491,18 @@
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"
href="https://github.com/sponsors/WeeJeWel" target="_blank">Donate</a></p>
href="https://github.com/sponsors/WeeJeWel" target="_blank">{{$t("donate")}}</a></p>
</div>
<script src="./js/vendor/vue.min.js"></script>
<script src="./js/vendor/vue-i18n.min.js"></script>
<script src="./js/vendor/apexcharts.min.js"></script>
<script src="./js/vendor/vue-apexcharts.min.js"></script>
<script src="./js/vendor/sha512.min.js"></script>
<script src="./js/vendor/timeago.min.js"></script>
<script src="./js/vendor/timeago.full.min.js"></script>
<script src="./js/api.js"></script>
<script src="./js/i18n.js"></script>
<script src="./js/app.js"></script>
</body>

7
src/www/js/api.js

@ -36,6 +36,13 @@ class API {
});
}
async getLang() {
return this.call({
method: 'get',
path: '/lang',
});
}
async getSession() {
return this.call({
method: 'get',

15
src/www/js/app.js

@ -23,8 +23,15 @@ function bytes(bytes, decimals, kib, maxunit) {
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
}
const i18n = new VueI18n({
locale: localStorage.getItem('lang') || 'en',
fallbackLocale: 'en',
messages,
});
new Vue({
el: '#app',
i18n,
data: {
authenticated: null,
authenticating: false,
@ -255,7 +262,7 @@ new Vue({
filters: {
bytes,
timeago: (value) => {
return timeago().format(value);
return timeago.format(value, i18n.locale);
},
},
mounted() {
@ -286,6 +293,12 @@ new Vue({
}, 1000);
Promise.resolve().then(async () => {
const lang = await this.api.getLang();
if (lang !== localStorage.getItem('lang') && i18n.availableLocales.includes(lang)) {
localStorage.setItem('lang', lang);
i18n.locale = lang;
}
const currentRelease = await this.api.getRelease();
const latestRelease = await fetch('https://wg-easy.github.io/wg-easy/changelog.json')
.then((res) => res.json())

194
src/www/js/i18n.js

@ -0,0 +1,194 @@
'use strict';
const messages = { // eslint-disable-line no-unused-vars
en: {
name: 'Name',
password: 'Password',
signIn: 'Sign In',
logout: 'Logout',
updateAvailable: 'There is an update available!',
update: 'Update',
clients: 'Clients',
new: 'New',
deleteClient: 'Delete Client',
deleteDialog1: 'Are you sure you want to delete',
deleteDialog2: 'This action cannot be undone.',
cancel: 'Cancel',
create: 'Create',
createdOn: 'Created on ',
lastSeen: 'Last seen on ',
totalDownload: 'Total Download: ',
totalUpload: 'Total Upload: ',
newClient: 'New Client',
disableClient: 'Disable Client',
enableClient: 'Enable Client',
noClients: 'There are no clients yet.',
showQR: 'Show QR Code',
downloadConfig: 'Download Configuration',
madeBy: 'Made by',
donate: 'Donate',
},
ru: {
name: 'Имя',
password: 'Пароль',
signIn: 'Войти',
logout: 'Выйти',
updateAvailable: 'Доступно обновление!',
update: 'Обновить',
clients: 'Клиенты',
new: 'Создать',
deleteClient: 'Удалить клиента',
deleteDialog1: 'Вы уверены, что хотите удалить',
deleteDialog2: 'Это действие невозможно отменить.',
cancel: 'Закрыть',
create: 'Создать',
createdOn: 'Создано в ',
lastSeen: 'Последнее подключение в ',
totalDownload: 'Всего скачано: ',
totalUpload: 'Всего загружено: ',
newClient: 'Создать клиента',
disableClient: 'Выключить клиента',
enableClient: 'Включить клиента',
noClients: 'Пока нет клиентов.',
showQR: 'Показать QR-код',
downloadConfig: 'Скачать конфигурацию',
madeBy: 'Автор',
donate: 'Поблагодарить',
},
tr: { // Müslüm Barış Korkmazer @babico
name: 'İsim',
password: 'Şifre',
signIn: 'Giriş Yap',
logout: 'Çıkış Yap',
updateAvailable: 'Mevcut bir güncelleme var!',
update: 'Güncelle',
clients: 'Kullanıcılar',
new: 'Yeni',
deleteClient: 'Kullanıcı Sil',
deleteDialog1: 'Silmek istediğine emin misin',
deleteDialog2: 'Bu işlem geri alınamaz.',
cancel: 'İptal',
create: 'Oluştur',
createdAt: 'Ş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',
noClients: 'Henüz kullanıcı yok.',
showQR: 'QR Kodunu Göster',
downloadConfig: 'Yapılandırmayı İndir',
madeBy: 'Yapan Kişi: ',
donate: 'Bağış Yap',
changeLang: 'Dil Değiştir',
},
no: { // github.com/digvalley
name: 'Navn',
password: 'Passord',
signIn: 'Logg Inn',
logout: 'Logg Ut',
updateAvailable: 'En ny oppdatering er tilgjengelig!',
update: 'Oppdater',
clients: 'Klienter',
new: 'Ny',
deleteClient: 'Slett Klient',
deleteDialog1: 'Er du sikker på at du vil slette?',
deleteDialog2: 'Denne handlingen kan ikke angres',
cancel: 'Avbryt',
create: 'Opprett',
createdOn: 'Opprettet ',
lastSeen: 'Sist sett ',
totalDownload: 'Total Nedlasting: ',
totalUpload: 'Total Opplasting: ',
newClient: 'Ny Klient',
disableClient: 'Deaktiver Klient',
enableClient: 'Aktiver Klient',
noClients: 'Ingen klienter opprettet enda.',
showQR: 'Vis QR Kode',
downloadConfig: 'Last Ned Konfigurasjon',
madeBy: 'Laget av',
donate: 'Doner',
},
pl: { // github.com/archont94
name: 'Nazwa',
password: 'Hasło',
signIn: 'Zaloguj się',
logout: 'Wyloguj się',
updateAvailable: 'Dostępna aktualizacja!',
update: 'Aktualizuj',
clients: 'Klienci',
new: 'Stwórz klienta',
deleteClient: 'Usuń klienta',
deleteDialog1: 'Jesteś pewny że chcesz usunąć',
deleteDialog2: 'Tej akcji nie da się cofnąć.',
cancel: 'Anuluj',
create: 'Stwórz',
createdOn: 'Utworzono ',
lastSeen: 'Ostatnio widziany ',
totalDownload: 'Całkowite pobieranie: ',
totalUpload: 'Całkowite wysyłanie: ',
newClient: 'Nowy klient',
disableClient: 'Wyłączenie klienta',
enableClient: 'Włączenie klienta',
noClients: 'Nie ma jeszcze klientów.',
showQR: 'Pokaż kod QR',
downloadConfig: 'Pobierz konfigurację',
madeBy: 'Stworzone przez',
donate: 'Wsparcie autora',
},
fr: { // github.com/clem3109
name: 'Nom',
password: 'Mot de passe',
signIn: 'Se Connecter',
logout: 'Se déconnecter',
updateAvailable: 'Une mise à jour est disponible !',
update: 'Mise à jour',
clients: 'Clients',
new: 'Nouveau',
deleteClient: 'Supprimer ce client',
deleteDialog1: 'Êtes-vous que vous voulez supprimer',
deleteDialog2: 'Cette action ne peut pas être annulée.',
cancel: 'Annuler',
create: 'Créer',
createdOn: 'Créé le ',
lastSeen: 'Dernière connexion le ',
totalDownload: 'Téléchargement total : ',
totalUpload: 'Téléversement total : ',
newClient: 'Nouveau client',
disableClient: 'Désactiver ce client',
enableClient: 'Activer ce client',
noClients: 'Aucun client pour le moment.',
showQR: 'Afficher le code à réponse rapide (QR Code)',
downloadConfig: 'Télécharger la configuration',
madeBy: 'Développé par',
donate: 'Soutenir',
},
de: { // github.com/florian-asche
name: 'Name',
password: 'Passwort',
signIn: 'Anmelden',
logout: 'Abmelden',
updateAvailable: 'Eine Aktualisierung steht zur Verfügung!',
update: 'Aktualisieren',
clients: 'Clients',
new: 'Neu',
deleteClient: 'Client löschen',
deleteDialog1: 'Möchtest du wirklich löschen?',
deleteDialog2: 'Diese Aktion kann nicht rückgängig gemacht werden.',
cancel: 'Abbrechen',
create: 'Erstellen',
createdOn: 'Erstellt am ',
lastSeen: 'Zuletzt Online ',
totalDownload: 'Gesamt Download: ',
totalUpload: 'Gesamt Upload: ',
newClient: 'Neuer Client',
disableClient: 'Client deaktivieren',
enableClient: 'Client aktivieren',
noClients: 'Es wurden noch keine Clients konfiguriert.',
showQR: 'Zeige den QR Code',
downloadConfig: 'Konfiguration herunterladen',
madeBy: 'Erstellt von',
donate: 'Spenden',
},
};

10
src/www/js/vendor/apexcharts.min.js

File diff suppressed because one or more lines are too long

1
src/www/js/vendor/timeago.full.min.js

File diff suppressed because one or more lines are too long

1
src/www/js/vendor/timeago.min.js

@ -1 +0,0 @@
!function(t,e){"object"==typeof module&&module.exports?module.exports=e(t):t.timeago=e(t)}("undefined"!=typeof window?window:this,function(){function t(t){return t instanceof Date?t:isNaN(t)?/^\d+$/.test(t)?new Date(e(t,10)):(t=(t||"").trim().replace(/\.\d+/,"").replace(/-/,"/").replace(/-/,"/").replace(/T/," ").replace(/Z/," UTC").replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"),new Date(t)):new Date(e(t))}function e(t){return parseInt(t)}function n(t,n,r){n=d[n]?n:d[r]?r:"en";var i=0;for(agoin=t<0?1:0,t=Math.abs(t);t>=l[i]&&i<p;i++)t/=l[i];return t=e(t),i*=2,t>(0===i?9:1)&&(i+=1),d[n](t,i)[agoin].replace("%s",t)}function r(e,n){return n=n?t(n):new Date,(n-t(e))/1e3}function i(t){for(var e=1,n=0,r=Math.abs(t);t>=l[n]&&n<p;n++)t/=l[n],e*=l[n];return r%=e,r=r?e-r:e,Math.ceil(r)}function o(t){return t.getAttribute?t.getAttribute(h):t.attr?t.attr(h):void 0}function a(t,e){function a(o,c,f,s){var d=r(c,t);o.innerHTML=n(d,f,e),u["k"+s]=setTimeout(function(){a(o,c,f,s)},1e3*i(d))}var u={};return e||(e="en"),this.format=function(i,o){return n(r(i,t),o,e)},this.render=function(t,e){void 0===t.length&&(t=[t]);for(var n=0;n<t.length;n++)a(t[n],o(t[n]),e,++c)},this.cancel=function(){for(var t in u)clearTimeout(u[t]);u={}},this.setLocale=function(t){e=t},this}function u(t,e){return new a(t,e)}var c=0,f="second_minute_hour_day_week_month_year".split("_"),s="秒_分钟_小时_天_周_月_年".split("_"),d={en:function(t,e){if(0===e)return["just now","right now"];var n=f[parseInt(e/2)];return t>1&&(n+="s"),[t+" "+n+" ago","in "+t+" "+n]},zh_CN:function(t,e){if(0===e)return["刚刚","片刻后"];var n=s[parseInt(e/2)];return[t+n+"前",t+n+"后"]}},l=[60,60,24,7,365/7/12,12],p=6,h="datetime";return u.register=function(t,e){d[t]=e},u});

6
src/www/js/vendor/vue-i18n.min.js

File diff suppressed because one or more lines are too long

6
src/www/js/vendor/vue.min.js

File diff suppressed because one or more lines are too long

22
wg-easy.service

@ -0,0 +1,22 @@
[Unit]
Description=Wireguard VPN + + Web-based Admin UI
After=network-online.target nss-lookup.target
[Service]
Environment="WG_HOST=raspberrypi.local" # Change this to your host's public address or static public ip.
Environment="PASSWORD=REPLACEME" # When set, requires a password when logging in to the Web UI, to disable add a hashtag
#Environment="WG_DEFAULT_ADDRESS=10.0.8.x" #Clients IP address range.
#Environment="WG_DEFAULT_DNS=10.0.8.1, 1.1.1.1" #DNS server clients will use. If set to blank value, clients will not use any DNS.
#Environment="WG_ALLOWED_IPS=0.0.0.0/0,::/0" #Allowed IPs clients will use.
#Environment="WG_DEVICE=ens1" #Ethernet device the wireguard traffic should be forwarded through.
#Environment="PORT=80" #TCP port for Web UI. Default : 51821
#Environment="WG_MTU=1420" #The MTU the clients will use. Server uses default WG MTU
#Environment="WG_PERSISTENT_KEEPALIVE=25" #Value in seconds to keep the "connection" open. If this value is 0, then connections won't be kept alive.
Type=simple
RemainAfterExit=no
ExecStart=/usr/bin/env node /app/server.js
Restart=on-failure
RestartSec=1
[Install]
WantedBy=multi-user.target
Loading…
Cancel
Save