Compare commits

...

265 Commits

Author SHA1 Message Date
Donald Zou
cdd85b659c Merge pull request #1227 from WGDashboard/v4.3.3-quick-fix
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Update wgd.sh
2026-04-10 16:27:04 +08:00
Donald Zou
42f9460369 Update wgd.sh 2026-04-10 16:18:12 +08:00
Donald Zou
ba11a7a355 Merge pull request #1226 from WGDashboard/v4.3.3-quick-fix
Fixed quotation marks
2026-04-10 15:50:10 +08:00
Donald Zou
71f4449741 Fixed quotation marks 2026-04-10 15:45:26 +08:00
Donald Zou
081c63cd43 Merge pull request #1197 from WGDashboard/development
v4.3.3 Merge
2026-04-10 14:50:10 +08:00
Donald Zou
ebd538e2d8 Merge branch 'main' into development
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2026-04-03 17:39:46 +08:00
Donald Zou
b36b0469e1 Build Admin and Clients 2026-04-03 17:37:21 +08:00
Donald Zou
2b9a39f124 Added /json to whitelist, and fixed Email.is_ready() 2026-04-03 17:36:30 +08:00
Donald Zou
cd709c2807 Updated Peers Notes element 2026-04-03 17:00:52 +08:00
DaanSelen
6bc54e5d65 chore: also trigger on development branch (#1204)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* chore: also trigger on development branch

* chore: remove old debug workflow

---------

Co-authored-by: DaanSelen <dselen@systemec.nl>
2026-04-02 12:03:47 +02:00
DaanSelen
65c2e69ac6 chore: correct per review comment 2026-04-02 11:34:50 +02:00
Donald Zou
96acc10153 Merge branch 'development' of https://github.com/WGDashboard/WGDashboard into development 2026-04-02 17:34:18 +08:00
Donald Zou
1a667a60c1 Updated UpdatePeer logic 2026-04-02 17:34:02 +08:00
DaanSelen
bbc85d6a2d Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/src/static/app/development/vueuse/core-14.2.1' into development 2026-04-02 11:12:38 +02:00
DaanSelen
3995a5db47 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/src/static/app/development/vue-router-5.0.4' into development 2026-04-02 11:12:31 +02:00
DaanSelen
b8750860a8 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/src/static/app/development/vue-3.5.31' into development 2026-04-02 11:12:26 +02:00
dependabot[bot]
4711c750cc build(deps): bump cidr-tools from 11.0.8 to 11.3.2 in /src/static/app
Bumps [cidr-tools](https://github.com/silverwind/cidr-tools) from 11.0.8 to 11.3.2.
- [Release notes](https://github.com/silverwind/cidr-tools/releases)
- [Commits](https://github.com/silverwind/cidr-tools/compare/11.0.8...11.3.2)

---
updated-dependencies:
- dependency-name: cidr-tools
  dependency-version: 11.3.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-02 09:07:28 +00:00
dependabot[bot]
51bee91c32 build(deps): bump vue from 3.5.28 to 3.5.31 in /src/static/app
Bumps [vue](https://github.com/vuejs/core) from 3.5.28 to 3.5.31.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.28...v3.5.31)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-02 09:07:17 +00:00
dependabot[bot]
5fcfe97fab build(deps): bump @vueuse/core from 14.2.0 to 14.2.1 in /src/static/app
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 14.2.0 to 14.2.1.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v14.2.1/packages/core)

---
updated-dependencies:
- dependency-name: "@vueuse/core"
  dependency-version: 14.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-02 09:07:03 +00:00
dependabot[bot]
74bee75df3 build(deps): bump vue-router from 5.0.2 to 5.0.4 in /src/static/app
Bumps [vue-router](https://github.com/vuejs/router) from 5.0.2 to 5.0.4.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/compare/v5.0.2...v5.0.4)

---
updated-dependencies:
- dependency-name: vue-router
  dependency-version: 5.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-02 09:06:54 +00:00
DaanSelen
1aa1b8a51b chore: make dependabot target the dev branch (#1198)
Co-authored-by: DaanSelen <dselen@systemec.nl>
2026-04-02 11:06:00 +02:00
Donald Zou
833ee88340 Build Admin and Client 2026-04-02 16:35:00 +08:00
Daan Selen
6cb4bac673 Merge remote-tracking branch 'origin/dependabot/pip/src/psycopg-binary--3.3.3' into v4.3.3-dev 2026-04-02 16:30:11 +08:00
Daan Selen
e87c52b6f2 Merge remote-tracking branch 'origin/dependabot/docker/docker/v4.3.3-dev/golang-1.26-alpine3.23' into v4.3.3-dev 2026-04-02 16:29:51 +08:00
Ivan Prokudin
f53da6eacb Updated resolvconf call in entrypoint.sh to fix dns inside container (#1081)
* removed resolvconf call from entrypoint.sh to fix dns inside container

* Revert "removed resolvconf call from entrypoint.sh to fix dns inside container"

This reverts commit 428908ff84.

* Added resolvconf -a to save initial DNS configuration
2026-04-02 16:27:57 +08:00
Donald Zou
8d35887950 Dependabot: set target branch to development
Configure Dependabot to open updates against the development branch. Added target-branch: "development" for pip (/src), npm (/src/static/app), github-actions (/.github), docker (/docker) and docker-compose (/docker). Also added a new npm updates entry for /src/static/client. Update schedules remain weekly.
2026-04-02 16:25:32 +08:00
Daan Selen
ebf37c879d fix: white screen in development image 2026-04-02 16:15:52 +08:00
Donald Zou
21bea56426 Merging main branch into v4.3.2-dev
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2026-03-29 23:05:29 +08:00
Donald Zou
0a33132d26 Update README.md
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2026-03-29 14:53:26 +08:00
Donald Zou
b15bbce9bc Merge pull request #1173 from WGDashboard/v4.3.2-security-patch
v4.3.2 security patch
2026-03-29 14:31:34 +08:00
Donald Zou
ddaada8e4f Updated version number 2026-03-29 14:30:24 +08:00
Donald Zou
920f832297 Fixed where /api/clients paths are bypassed
Instead of adding `/client` in the white list, we isolate it out to check if the path starts with `APP_PREFIX/client` so then client will bypassed but not `/api/clients`
2026-03-28 23:33:47 +08:00
Donald Zou
22df8a1b1f Removed fileDownload and replaced by using downloadWireguardConfigurationBackup 2026-03-28 23:17:39 +08:00
mahemium
468d1c7642 v4.3.1 bug fix (#1149)
* Fix bugs

* Fixes and improvements

* Update comment

* Reduce comment
2026-03-28 23:13:08 +08:00
Daan Selen
f58226e398 chore: add the option for docker to not be dominant
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2026-03-16 19:02:51 +01:00
Dan Hollis
eeedf705aa fix: peer key validation regex (#1158)
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
* fix: peer key validation regex

* refactor: cache cleaned AllowedIPs from validation pass to avoid duplication

---------

Co-authored-by: Dan Hollis <dh@redteam.sh>
2026-03-03 09:04:30 +01:00
DaanSelen
b9c271ff4c fix: regex invalid escape sequence
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2026-03-02 10:38:16 +01:00
mahemium
ba4ac7c1ec v4.3.1 bug fix (#1149)
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
* Fix bugs

* Fixes and improvements

* Update comment

* Reduce comment
2026-02-26 12:15:52 +01:00
freetushkan
06097316ec fix: incorrect backup regex
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
* More accurate backup regex.

Fixed an issue with picking up other backups when the names partially match.

* More accurate backup regex 2.
2026-02-24 10:23:01 +01:00
DaanSelen
e71179a2d4 fix: error on startup regarding oidc config file being weird
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2026-02-23 16:35:27 +01:00
DaanSelen
bb18c55003 fix: Configuration creation bug due to failed Nonetype checking
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2026-02-16 18:54:10 +01:00
Donald Zou
8e08d20497 Merge pull request #1130 from WGDashboard/night-refac
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
refac: some WGDashboard code
2026-02-14 00:05:21 +08:00
DaanSelen
bb5d45a66d Merge remote-tracking branch 'origin/main' into v4.3.2-dev
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2026-02-12 09:45:15 +01:00
dependabot[bot]
2cdc0265b8 build(deps): bump vue-router from 4.6.4 to 5.0.2 in /src/static/app (#1131)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [vue-router](https://github.com/vuejs/router) from 4.6.4 to 5.0.2.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/compare/v4.6.4...v5.0.2)

---
updated-dependencies:
- dependency-name: vue-router
  dependency-version: 5.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-12 09:41:14 +01:00
dependabot[bot]
df295bf470 build(deps): bump cidr-tools from 11.0.6 to 11.0.8 in /src/static/app (#1132)
Bumps [cidr-tools](https://github.com/silverwind/cidr-tools) from 11.0.6 to 11.0.8.
- [Release notes](https://github.com/silverwind/cidr-tools/releases)
- [Commits](https://github.com/silverwind/cidr-tools/compare/11.0.6...11.0.8)

---
updated-dependencies:
- dependency-name: cidr-tools
  dependency-version: 11.0.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-12 09:23:38 +01:00
dependabot[bot]
dfb75d77b8 build(deps): bump gunicorn from 25.0.1 to 25.0.3 in /src (#1133)
Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 25.0.1 to 25.0.3.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/25.0.1...25.0.3)

---
updated-dependencies:
- dependency-name: gunicorn
  dependency-version: 25.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-12 09:23:17 +01:00
dependabot[bot]
18ef023817 build(deps-dev): bump @vitejs/plugin-vue in /src/static/app (#1136)
Bumps [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue) from 6.0.3 to 6.0.4.
- [Release notes](https://github.com/vitejs/vite-plugin-vue/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-vue/commits/plugin-vue@6.0.4/packages/plugin-vue)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-vue"
  dependency-version: 6.0.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-12 09:22:50 +01:00
dependabot[bot]
12da167b8c build(deps): bump is-cidr from 6.0.2 to 6.0.3 in /src/static/app (#1135)
Bumps [is-cidr](https://github.com/silverwind/is-cidr) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/silverwind/is-cidr/releases)
- [Commits](https://github.com/silverwind/is-cidr/compare/6.0.2...6.0.3)

---
updated-dependencies:
- dependency-name: is-cidr
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-12 09:22:29 +01:00
dependabot[bot]
db78f42a04 build(deps): bump vue from 3.5.27 to 3.5.28 in /src/static/app (#1134)
Bumps [vue](https://github.com/vuejs/core) from 3.5.27 to 3.5.28.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.27...v3.5.28)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-12 09:22:18 +01:00
Daan Selen
f742f9de49 refac: some WGDashboard code 2026-02-07 03:03:35 +01:00
Daan Selen
686fc139ec Merge branch 'main' into v4.3.2-dev
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2026-02-06 20:44:11 +01:00
Daan Selen
78a7211680 chore: rename the debug workflow
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2026-02-06 20:39:56 +01:00
Daan Selen
1bcb48a50e chore: make workflow trigger on dev branches 2026-02-06 20:39:54 +01:00
Daan Selen
a56297bf87 minor readme adjust 2026-02-06 20:24:42 +01:00
Daan Selen
2e01ecc455 fix: workflow triggering 2026-02-06 20:24:12 +01:00
Daan Selen
80b1b8875d test: test if the trigger on the dev branch actually works 2026-02-06 20:20:57 +01:00
Daan Selen
ebd13b4df9 refac: workflow - also trigger on dev commits 2026-02-06 20:19:25 +01:00
Daan Selen
8ca0363fd0 feat: working AWG 2.0 parameters 2026-02-06 20:18:32 +01:00
Daan Selen
07534b6f66 fix: 1116 2026-02-06 19:00:02 +01:00
Donald Zou
e4d441388d Merge pull request #1127 from WGDashboard/change_issue_template
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
feat: change issue templating
2026-02-06 07:52:47 +08:00
V.Bantserov
a8bf35b0c0 feat: Add Bulgarian (bg-BG) translation (#1128) 2026-02-05 23:23:53 +01:00
Mikhail Solovev
82f170cd57 Fixed Bug. Unpredicted grow of cumulative data counters when using MySQL DB with 7 digits float precision. (#1125)
* build(deps): bump @vue/language-server in /src/static/app (#1063)

Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.1.8 to 3.2.2.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.2.2/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.2.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump pinia from 3.0.3 to 3.0.4 in /src/static/app (#1065)

Bumps [pinia](https://github.com/vuejs/pinia) from 3.0.3 to 3.0.4.
- [Release notes](https://github.com/vuejs/pinia/releases)
- [Commits](https://github.com/vuejs/pinia/compare/v3.0.3...v3.0.4)

---
updated-dependencies:
- dependency-name: pinia
  dependency-version: 3.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps-dev): bump @vitejs/plugin-vue in /src/static/app (#1067)

Bumps [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue) from 6.0.1 to 6.0.3.
- [Release notes](https://github.com/vitejs/vite-plugin-vue/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-vue/commits/plugin-vue@6.0.3/packages/plugin-vue)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-vue"
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump electron-builder in /src/static/app (#1064)

Bumps [electron-builder](https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder) from 26.0.12 to 26.4.0.
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/master/packages/electron-builder/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/commits/electron-builder@26.4.0/packages/electron-builder)

---
updated-dependencies:
- dependency-name: electron-builder
  dependency-version: 26.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump @volar/language-server in /src/static/app (#1066)

Bumps [@volar/language-server](https://github.com/volarjs/volar.js/tree/HEAD/packages/language-server) from 2.4.26 to 2.4.27.
- [Release notes](https://github.com/volarjs/volar.js/releases)
- [Changelog](https://github.com/volarjs/volar.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/volarjs/volar.js/commits/v2.4.27/packages/language-server)

---
updated-dependencies:
- dependency-name: "@volar/language-server"
  dependency-version: 2.4.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore: change docs

* docs: correctly escape pipes

* build(deps): bump npm from 11.6.4 to 11.8.0 in /src/static/app (#1104)

Bumps [npm](https://github.com/npm/cli) from 11.6.4 to 11.8.0.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v11.6.4...v11.8.0)

---
updated-dependencies:
- dependency-name: npm
  dependency-version: 11.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump gunicorn from 23.0.0 to 24.1.1 in /src (#1103)

Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 23.0.0 to 24.1.1.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/23.0.0...24.1.1)

---
updated-dependencies:
- dependency-name: gunicorn
  dependency-version: 24.1.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump is-cidr from 6.0.1 to 6.0.2 in /src/static/app (#1102)

Bumps [is-cidr](https://github.com/silverwind/is-cidr) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/silverwind/is-cidr/releases)
- [Commits](https://github.com/silverwind/is-cidr/compare/6.0.1...6.0.2)

---
updated-dependencies:
- dependency-name: is-cidr
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump sqlalchemy from 2.0.45 to 2.0.46 in /src (#1101)

Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.45 to 2.0.46.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

---
updated-dependencies:
- dependency-name: sqlalchemy
  dependency-version: 2.0.46
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix npm audit tar vulnerabilities (#1093)

* Harden npm deps via tar override

* chore: retrigger review

---------

Co-authored-by: Super User <root@vpn.northcaptiva.com>

* build(deps): bump @vue/language-server in /src/static/app (#1100)

Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.2.2 to 3.2.4.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.2.4/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump vue from 3.5.26 to 3.5.27 in /src/static/app (#1087)

Bumps [vue](https://github.com/vuejs/core) from 3.5.26 to 3.5.27.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.26...v3.5.27)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps-dev): bump vite from 7.3.0 to 7.3.1 in /src/static/app (#1082)

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.3.0 to 7.3.1.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.1/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump psutil from 7.2.1 to 7.2.2 in /src

Bumps [psutil](https://github.com/giampaolo/psutil) from 7.2.1 to 7.2.2.
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-7.2.1...release-7.2.2)

---
updated-dependencies:
- dependency-name: psutil
  dependency-version: 7.2.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): bump gunicorn from 24.1.1 to 25.0.1 in /src

Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 24.1.1 to 25.0.1.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/24.1.1...25.0.1)

---
updated-dependencies:
- dependency-name: gunicorn
  dependency-version: 25.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Refine total_sent and total_receive with precision factor

Adjusted total_sent and total_receive calculations for accuracy.
The database engine may have lower floating-point precision than the Python engine. This can lead to situations where the current bytes count compares lower than the database data. This causes an increase in cumulative traffic each time such a precision collision occurs.

* Update WireguardConfiguration.py

It better make some fix in comparation only, and don't touch saved values.

* Update WireguardConfiguration.py

wrong symbol

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: DaanSelen <dselen@systemec.nl>
Co-authored-by: zombu4 <dom@northcaptiva.com>
Co-authored-by: Super User <root@vpn.northcaptiva.com>
2026-02-05 23:22:31 +01:00
Daan Selen
6beda22f39 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/src/static/app/electron-builder-26.7.0' 2026-02-05 18:35:47 +01:00
Daan Selen
6b1d2b4b50 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/src/static/app/vueuse/core-14.2.0' 2026-02-05 18:33:55 +01:00
Daan Selen
3f23c0f949 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/src/static/app/volar/language-server-2.4.28' 2026-02-05 18:33:43 +01:00
Daan Selen
abaaff809a Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/src/static/app/cidr-tools-11.0.6' 2026-02-05 18:33:25 +01:00
Daan Selen
eebb2e39cc feat: change issue templating 2026-02-05 18:26:57 +01:00
DaanSelen
fd2daa2934 Merge remote-tracking branch 'origin/dependabot/pip/src/psutil-7.2.2'
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2026-02-04 11:52:17 +01:00
dependabot[bot]
cc253a6a11 build(deps): bump cidr-tools from 11.0.3 to 11.0.6 in /src/static/app
Bumps [cidr-tools](https://github.com/silverwind/cidr-tools) from 11.0.3 to 11.0.6.
- [Release notes](https://github.com/silverwind/cidr-tools/releases)
- [Commits](https://github.com/silverwind/cidr-tools/compare/11.0.3...11.0.6)

---
updated-dependencies:
- dependency-name: cidr-tools
  dependency-version: 11.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 02:21:56 +00:00
dependabot[bot]
06ce640f56 build(deps): bump @volar/language-server in /src/static/app
Bumps [@volar/language-server](https://github.com/volarjs/volar.js/tree/HEAD/packages/language-server) from 2.4.27 to 2.4.28.
- [Release notes](https://github.com/volarjs/volar.js/releases)
- [Changelog](https://github.com/volarjs/volar.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/volarjs/volar.js/commits/v2.4.28/packages/language-server)

---
updated-dependencies:
- dependency-name: "@volar/language-server"
  dependency-version: 2.4.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 02:21:45 +00:00
dependabot[bot]
8c8746cb9b build(deps): bump @vueuse/core from 14.1.0 to 14.2.0 in /src/static/app
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 14.1.0 to 14.2.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v14.2.0/packages/core)

---
updated-dependencies:
- dependency-name: "@vueuse/core"
  dependency-version: 14.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 02:21:34 +00:00
dependabot[bot]
97d92d5310 build(deps): bump electron-builder in /src/static/app
Bumps [electron-builder](https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder) from 26.5.0 to 26.7.0.
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/master/packages/electron-builder/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/commits/electron-builder@26.7.0/packages/electron-builder)

---
updated-dependencies:
- dependency-name: electron-builder
  dependency-version: 26.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 02:21:16 +00:00
dependabot[bot]
a19ab85ba9 build(deps): bump gunicorn from 24.1.1 to 25.0.1 in /src
Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 24.1.1 to 25.0.1.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/24.1.1...25.0.1)

---
updated-dependencies:
- dependency-name: gunicorn
  dependency-version: 25.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 02:21:03 +00:00
dependabot[bot]
6705705e3d build(deps): bump psutil from 7.2.1 to 7.2.2 in /src
Bumps [psutil](https://github.com/giampaolo/psutil) from 7.2.1 to 7.2.2.
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-7.2.1...release-7.2.2)

---
updated-dependencies:
- dependency-name: psutil
  dependency-version: 7.2.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 02:20:57 +00:00
dependabot[bot]
8f973021fc build(deps-dev): bump vite from 7.3.0 to 7.3.1 in /src/static/app (#1082)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.3.0 to 7.3.1.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.1/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-02 11:44:36 +01:00
dependabot[bot]
23256a873c build(deps): bump vue from 3.5.26 to 3.5.27 in /src/static/app (#1087)
Bumps [vue](https://github.com/vuejs/core) from 3.5.26 to 3.5.27.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.26...v3.5.27)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-02 11:44:24 +01:00
dependabot[bot]
20847a0954 build(deps): bump @vue/language-server in /src/static/app (#1100)
Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.2.2 to 3.2.4.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.2.4/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-02 11:44:10 +01:00
Donald Zou
515bd0f806 Fixed #913 2026-02-02 16:13:27 +08:00
Donald Zou
27742d65c6 Update gunicorn.conf.py 2026-02-02 15:23:50 +08:00
DaanSelen
ddf378d617 chore: remove dependency on vulnerability for scanning 2026-01-29 14:29:55 +01:00
DaanSelen
35c45b159b docs: adjust to the database support 2026-01-29 13:32:03 +01:00
DaanSelen
654bf6d779 chore: replace old values 2026-01-29 13:25:09 +01:00
DaanSelen
fb66fd2dda feat: add support for database configuration 2026-01-29 13:23:42 +01:00
zombu4
0f98bb2537 Fix npm audit tar vulnerabilities (#1093)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* Harden npm deps via tar override

* chore: retrigger review

---------

Co-authored-by: Super User <root@vpn.northcaptiva.com>
2026-01-27 16:03:52 +01:00
dependabot[bot]
d5026a4081 build(deps): bump sqlalchemy from 2.0.45 to 2.0.46 in /src (#1101)
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.45 to 2.0.46.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

---
updated-dependencies:
- dependency-name: sqlalchemy
  dependency-version: 2.0.46
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 16:02:21 +01:00
dependabot[bot]
aeed7cf322 build(deps): bump is-cidr from 6.0.1 to 6.0.2 in /src/static/app (#1102)
Bumps [is-cidr](https://github.com/silverwind/is-cidr) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/silverwind/is-cidr/releases)
- [Commits](https://github.com/silverwind/is-cidr/compare/6.0.1...6.0.2)

---
updated-dependencies:
- dependency-name: is-cidr
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 16:02:09 +01:00
dependabot[bot]
29ebe65587 build(deps): bump gunicorn from 23.0.0 to 24.1.1 in /src (#1103)
Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 23.0.0 to 24.1.1.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/23.0.0...24.1.1)

---
updated-dependencies:
- dependency-name: gunicorn
  dependency-version: 24.1.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 16:01:59 +01:00
dependabot[bot]
ce553de03b build(deps): bump npm from 11.6.4 to 11.8.0 in /src/static/app (#1104)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Bumps [npm](https://github.com/npm/cli) from 11.6.4 to 11.8.0.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v11.6.4...v11.8.0)

---
updated-dependencies:
- dependency-name: npm
  dependency-version: 11.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 08:32:05 +01:00
DaanSelen
44d274a998 chore: remove unneeded reference 2026-01-15 09:00:20 +01:00
Daan Selen
b6e2f084e6 chore: add implicit TLS support for email 2026-01-14 23:45:42 +01:00
DaanSelen
cb0619036f chore: add oidc-providers json file to the data volume location 2026-01-09 16:09:23 +01:00
DaanSelen
3a7e16ca9e chore: rename the email settings panel 2026-01-09 16:00:21 +01:00
DaanSelen
601ccb355d docs: correctly escape pipes
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2026-01-09 15:52:20 +01:00
DaanSelen
2347c76812 chore: change docs 2026-01-09 15:48:52 +01:00
dependabot[bot]
9ffba5e33a build(deps): bump @volar/language-server in /src/static/app (#1066)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [@volar/language-server](https://github.com/volarjs/volar.js/tree/HEAD/packages/language-server) from 2.4.26 to 2.4.27.
- [Release notes](https://github.com/volarjs/volar.js/releases)
- [Changelog](https://github.com/volarjs/volar.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/volarjs/volar.js/commits/v2.4.27/packages/language-server)

---
updated-dependencies:
- dependency-name: "@volar/language-server"
  dependency-version: 2.4.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 08:34:58 +01:00
dependabot[bot]
18018d3079 build(deps): bump electron-builder in /src/static/app (#1064)
Bumps [electron-builder](https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder) from 26.0.12 to 26.4.0.
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/master/packages/electron-builder/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/commits/electron-builder@26.4.0/packages/electron-builder)

---
updated-dependencies:
- dependency-name: electron-builder
  dependency-version: 26.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 08:34:16 +01:00
dependabot[bot]
2b703d6ab8 build(deps-dev): bump @vitejs/plugin-vue in /src/static/app (#1067)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue) from 6.0.1 to 6.0.3.
- [Release notes](https://github.com/vitejs/vite-plugin-vue/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-vue/commits/plugin-vue@6.0.3/packages/plugin-vue)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-vue"
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-07 23:40:40 +01:00
dependabot[bot]
d506fdf2c1 build(deps): bump pinia from 3.0.3 to 3.0.4 in /src/static/app (#1065)
Bumps [pinia](https://github.com/vuejs/pinia) from 3.0.3 to 3.0.4.
- [Release notes](https://github.com/vuejs/pinia/releases)
- [Commits](https://github.com/vuejs/pinia/compare/v3.0.3...v3.0.4)

---
updated-dependencies:
- dependency-name: pinia
  dependency-version: 3.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-07 23:40:27 +01:00
dependabot[bot]
e5dc6086fa build(deps): bump @vue/language-server in /src/static/app (#1063)
Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.1.8 to 3.2.2.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.2.2/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.2.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-07 23:40:09 +01:00
Donald Zou
64506c9088 Merge pull request #1060 from WGDashboard/fix_domainnx_error_logoout
fix: domainnx error logoout
2026-01-05 06:35:27 +08:00
Daan Selen
d2b05b77c8 chore: make the error handling a bit more graceful 2026-01-03 13:32:55 +01:00
DaanSelen
17e23685ec refac: Email.py module with bugfix (#1058)
* refac: refactor Email.py module for clarity

* chore: rework ready method for dynamic config

* chore: implement resolvconf from v4.3.2-dev
2026-01-03 13:00:23 +01:00
Daan Selen
9a7b23748d Merge remote-tracking branch 'origin/main' into v4.3.2-dev 2026-01-03 12:41:42 +01:00
Daan Selen
6239b8237a Merge branch 'amnezia-params' into v4.3.2-dev 2026-01-03 12:36:46 +01:00
DaanSelen
dd6ffa694f chore: fallback echo defined nameserver
Update DNS configuration handling in entrypoint script.
2026-01-03 01:20:02 +01:00
dependabot[bot]
febc8818b0 build(deps): bump vue-chartjs from 5.3.2 to 5.3.3 in /src/static/app (#1041)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [vue-chartjs](https://github.com/apertureless/vue-chartjs) from 5.3.2 to 5.3.3.
- [Release notes](https://github.com/apertureless/vue-chartjs/releases)
- [Changelog](https://github.com/apertureless/vue-chartjs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/apertureless/vue-chartjs/compare/v5.3.2...v5.3.3)

---
updated-dependencies:
- dependency-name: vue-chartjs
  dependency-version: 5.3.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 18:36:15 +01:00
dependabot[bot]
8b7d372dcb build(deps-dev): bump vite from 7.2.2 to 7.3.0 in /src/static/app (#1040)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.2.2 to 7.3.0.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.0/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.0/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 18:32:46 +01:00
dependabot[bot]
66dd5c4f9f build(deps): bump vue from 3.5.24 to 3.5.25 in /src/static/app (#1039)
Bumps [vue](https://github.com/vuejs/core) from 3.5.24 to 3.5.25.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.24...v3.5.25)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 18:32:26 +01:00
dependabot[bot]
682587183f build(deps): bump sqlalchemy-utils from 0.42.0 to 0.42.1 in /src (#1038)
Bumps [sqlalchemy-utils](https://github.com/kvesteri/sqlalchemy-utils) from 0.42.0 to 0.42.1.
- [Release notes](https://github.com/kvesteri/sqlalchemy-utils/releases)
- [Changelog](https://github.com/kvesteri/sqlalchemy-utils/blob/master/CHANGES.rst)
- [Commits](https://github.com/kvesteri/sqlalchemy-utils/compare/0.42.0...0.42.1)

---
updated-dependencies:
- dependency-name: sqlalchemy-utils
  dependency-version: 0.42.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 18:32:09 +01:00
dependabot[bot]
a3da48be8c build(deps): bump dayjs from 1.11.18 to 1.11.19 in /src/static/app (#1037)
Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.11.18 to 1.11.19.
- [Release notes](https://github.com/iamkun/dayjs/releases)
- [Changelog](https://github.com/iamkun/dayjs/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/iamkun/dayjs/compare/v1.11.18...v1.11.19)

---
updated-dependencies:
- dependency-name: dayjs
  dependency-version: 1.11.19
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 18:29:56 +01:00
dependabot[bot]
e398a07f32 build(deps): bump vue-router from 4.6.3 to 4.6.4 in /src/static/app (#1042)
Bumps [vue-router](https://github.com/vuejs/router) from 4.6.3 to 4.6.4.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/compare/v4.6.3...v4.6.4)

---
updated-dependencies:
- dependency-name: vue-router
  dependency-version: 4.6.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 18:29:25 +01:00
dependabot[bot]
0745d39a50 build(deps): bump psutil from 7.1.3 to 7.2.1 in /src (#1052)
Bumps [psutil](https://github.com/giampaolo/psutil) from 7.1.3 to 7.2.1.
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-7.1.3...release-7.2.1)

---
updated-dependencies:
- dependency-name: psutil
  dependency-version: 7.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 18:29:11 +01:00
dependabot[bot]
87bdc5dde9 build(deps): bump flask-cors from 6.0.1 to 6.0.2 in /src (#1036)
Bumps [flask-cors](https://github.com/corydolphin/flask-cors) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/corydolphin/flask-cors/releases)
- [Changelog](https://github.com/corydolphin/flask-cors/blob/main/CHANGELOG.md)
- [Commits](https://github.com/corydolphin/flask-cors/compare/6.0.1...6.0.2)

---
updated-dependencies:
- dependency-name: flask-cors
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 18:28:51 +01:00
dependabot[bot]
126c200fa2 build(deps): bump sqlalchemy from 2.0.44 to 2.0.45 in /src (#1035)
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.44 to 2.0.45.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

---
updated-dependencies:
- dependency-name: sqlalchemy
  dependency-version: 2.0.45
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 18:28:35 +01:00
DaanSelen
5486b3aaf6 fix: Docker autostart issue due to invalid resolvectl (#1053)
* Update funding information in FUNDING.yml

* Update README.md

* refac: refactor the logic with which the Docker container links its files to the volume

* fix: 1032 docker autostart issue

---------

Co-authored-by: Donald Zou <donaldzou@live.hk>
Co-authored-by: DaanSelen <dselen@systemec.nl>
2025-12-30 19:19:20 +01:00
Donald Zou
c8967b449b Added checks and output to log wether configuration started properly or not 2025-12-30 21:55:37 +08:00
Donald Zou
72effeecf0 Update WireguardConfiguration.py 2025-12-30 21:47:33 +08:00
Donald Zou
dcaf5eff32 Update WireguardConfiguration.py 2025-12-30 21:38:06 +08:00
Daan
540a085821 chore: new files 2025-12-29 18:34:20 +01:00
Daan
06cdb0b59e feat: added new AWG parameters 2025-12-29 17:56:35 +01:00
DaanSelen
faf9e663b3 chore: trying to inplement the AWG parameters 2025-12-29 17:03:21 +01:00
Donald Zou
174fd527cc Update README.md
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-12-28 17:14:45 +08:00
Donald Zou
a3058d2a28 Added toggle for signing up local clients 2025-12-28 17:02:14 +08:00
Donald Zou
0d70d13d0f Updated APP_PREFIX for Admin Dashboard 2025-12-26 10:59:24 +08:00
Donald Zou
a0bbace7ee Update prefix issue 2025-12-25 16:49:32 +08:00
Donald Zou
53f7a32202 Merge pull request #1048 from leviofanh/app-prefix
Full support for the app-prefix parameter has been added to the frontend.
2025-12-25 15:41:46 +08:00
Donald Zou
c35f06b262 Merge branch 'v4.3.2-dev' into app-prefix 2025-12-25 15:39:26 +08:00
Donald Zou
6de90410d0 Build 2025-12-19 09:23:38 +08:00
Donald Zou
87ff8afefe Add peer tracking toggle to WireGuard dashboard
Introduces a 'peer_tracking' configuration option for WireGuard in DashboardConfig and updates the dashboard background thread to respect this setting. The Vue component now includes a UI toggle for peer tracking, updates the server configuration via API, and conditionally loads tracking data based on the toggle state.
2025-12-19 09:22:45 +08:00
Donald Zou
9ae5fea7b4 Build 2025-12-19 07:04:21 +08:00
Donald Zou
40e2dddc00 Optimize peer tracking table counts API and UI
Enhanced the /api/getPeerTrackingTableCounts endpoint to support returning counts for all configurations when no name is provided. Updated frontend components to fetch all tracking data in a single request, improving efficiency and user experience. Bumped dashboard version to 4.3.2.
2025-12-19 07:04:07 +08:00
leviofanh
a584bacdba Recompiled frontend to support new changes in the app-prefix mechanism (WGDasboardClient fix and vite dev server support) 2025-12-18 07:18:39 +01:00
leviofanh
e6dcef2b01 App-prefix template transferred to javascript to support the vite dev server 2025-12-18 07:14:24 +01:00
leviofanh
f58e572558 Fix: Fixed support for WGDashboardClient, now its static files are processed separately 2025-12-18 06:40:52 +01:00
leviofanh
721ac89420 Recompiled frontend for app-prefix support 2025-12-18 02:51:07 +01:00
leviofanh
9e9f07408d Added full support for the app-prefix parameter 2025-12-18 02:38:05 +01:00
Donald Zou
92651729eb Update funding information in FUNDING.yml
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-12-18 07:35:06 +08:00
Donald Zou
d51c543346 Merge pull request #1045 from WGDashboard/DaanSelen-patch-1
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
chore: english refac
2025-12-17 07:36:35 +08:00
DaanSelen
3adca64624 Update README.md 2025-12-17 00:06:12 +01:00
Donald Zou
b1d7199e84 Improve peer details access and update signin footer UI
Peer details button is now hidden for restricted peers, showing a message instead. The signin page footer layout is updated for better alignment, and the client app link is moved to the right with improved styling.
2025-12-16 17:51:45 +08:00
Donald Zou
0a64b1b377 Merge pull request #1044 from WGDashboard/client_app_button
feat: add client app button
2025-12-16 17:09:25 +08:00
Donald Zou
bc20057de6 Merge pull request #1043 from WGDashboard/main
Merging main into v4.3.2-dev
2025-12-16 07:12:19 +08:00
Donald Zou
c7c9d62e80 Update WireguardConfiguration.py 2025-12-15 21:02:22 +08:00
Donald Zou
f5ccbdf9d3 Fixed Restricted Peer might still appear after restricted 2025-12-15 17:13:46 +08:00
Donald Zou
a09342ed2a Added fallback when instance is completely disconnected from internet 2025-12-15 16:25:13 +08:00
Daan Selen
306e17eb4d refac: some exit logic
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-12-14 20:56:18 +01:00
DaanSelen
d75c7ad418 feat: add better docker stopping response (#1025) 2025-12-14 17:32:46 +01:00
Daan Selen
26171ee091 feat: add client app button 2025-12-14 16:56:24 +01:00
Donald Zou
82e10659b6 Update Wakatime badge link in README.md
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-12-14 00:28:24 +08:00
Donald Zou
b6f25ac817 Include docker directory in workflow paths 2025-12-14 00:25:37 +08:00
Donald Zou
48481ab992 Implement path filter for Docker workflow triggers
Added a path filter to restrict Docker workflow triggers to changes in the 'src' directory.
2025-12-14 00:24:21 +08:00
Donald Zou
d56a3cb5d6 Merge pull request #1031 from WGDashboard/v4.3.2-dev
v4.3.1
2025-12-13 23:26:41 +08:00
Donald Zou
d6a930c04b Bumped version number 2025-12-13 17:20:56 +08:00
Donald Zou
6fe257fa4a Update entrypoint.sh 2025-12-13 16:29:48 +08:00
Donald Zou
e5d1a64d7d Update to Python 3.14-alpine and refactor IP parsing
Bump Dockerfile base image from python:3.13.3-alpine to python:3.14-alpine for both build and runtime stages. Refactor IP address parsing in dashboard.py for improved clarity and variable naming.
2025-12-13 16:05:12 +08:00
Donald Zou
da4cec60e5 Update Dockerfile
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-12-12 23:41:25 +08:00
DaanSelen
a4d471df4f chore: downgrade python due to compatibility 2025-12-12 16:32:48 +01:00
DaanSelen
f00940b73c chore: cleanup
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2025-12-12 15:27:04 +01:00
DaanSelen
cb9cb4a0b6 chore: fix docker (hopefully) 2025-12-12 15:17:25 +01:00
DaanSelen
b695ccc5f9 chore: add openresolv back into the dependencies to blanked cover our deps
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-12-11 23:34:46 +01:00
DaanSelen
16cd4da5fc chore: add openrc to avoid no init systems (#1027)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2025-12-11 21:56:44 +01:00
Donald Zou
7be830a34e Merge pull request #1024 from WGDashboard/v4.3.0.3-dev
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumped version, corrected some vue package after update
2025-12-11 07:03:58 +08:00
Donald Zou
eb65d6fb2a Bumped version, corrected some vue package after update 2025-12-11 07:03:31 +08:00
Daan Selen
6fee7da633 chore: fix bcrypt issue 2025-12-10 23:17:45 +01:00
DaanSelen
2e8d2fe400 chore: bump WGDashboard version 2025-12-10 22:59:35 +01:00
DaanSelen
da3c34c250 chore: rename workflow
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2025-12-10 12:19:25 +01:00
dependabot[bot]
d864d12793 build(deps): bump @vue/language-server in /src/static/app (#1019)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.1.3 to 3.1.7.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.1.7/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 13:36:19 +01:00
DaanSelen
36390a4d71 refac: rework tailing of logs in the Docker entrypoint (#1021)
* chore: add debug workflow

* chore: testing fix

* chore: try next option

* refac: tailing logs

---------

Co-authored-by: DaanSelen <dselen@systemec.nl>
2025-12-09 13:16:18 +01:00
DaanSelen
5db2baf1dc fix sed command for armv7 2025-12-09 10:18:39 +01:00
DaanSelen
dc10da607d chore: bump codeql 2025-12-09 10:06:27 +01:00
dependabot[bot]
9b4abf19fa build(deps): bump @vueuse/shared in /src/static/app (#1015)
Bumps [@vueuse/shared](https://github.com/vueuse/vueuse/tree/HEAD/packages/shared) from 14.0.0 to 14.1.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v14.1.0/packages/shared)

---
updated-dependencies:
- dependency-name: "@vueuse/shared"
  dependency-version: 14.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 09:55:57 +01:00
dependabot[bot]
bf823e6cf8 build(deps): bump @volar/language-server in /src/static/app (#1017)
Bumps [@volar/language-server](https://github.com/volarjs/volar.js/tree/HEAD/packages/language-server) from 2.4.23 to 2.4.26.
- [Release notes](https://github.com/volarjs/volar.js/releases)
- [Changelog](https://github.com/volarjs/volar.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/volarjs/volar.js/commits/v2.4.26/packages/language-server)

---
updated-dependencies:
- dependency-name: "@volar/language-server"
  dependency-version: 2.4.26
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 09:55:47 +01:00
dependabot[bot]
880c94b8a3 build(deps): bump npm from 11.6.2 to 11.6.4 in /src/static/app (#1018)
Bumps [npm](https://github.com/npm/cli) from 11.6.2 to 11.6.4.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v11.6.2...v11.6.4)

---
updated-dependencies:
- dependency-name: npm
  dependency-version: 11.6.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 09:55:35 +01:00
dependabot[bot]
656eba3a57 build(deps): bump @vuepic/vue-datepicker in /src/static/app (#1020)
Bumps [@vuepic/vue-datepicker](https://github.com/Vuepic/vue-datepicker) from 12.0.1 to 12.1.0.
- [Release notes](https://github.com/Vuepic/vue-datepicker/releases)
- [Changelog](https://github.com/Vuepic/vue-datepicker/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Vuepic/vue-datepicker/compare/v12.0.1...v12.1.0)

---
updated-dependencies:
- dependency-name: "@vuepic/vue-datepicker"
  dependency-version: 12.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 09:55:17 +01:00
dependabot[bot]
4154a27f4d build(deps): bump pydantic from 2.12.4 to 2.12.5 in /src (#1014)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.4 to 2.12.5.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.12.4...v2.12.5)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.12.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 09:54:41 +01:00
dependabot[bot]
785d0d6b06 build(deps): bump psycopg[binary] from 3.2.13 to 3.3.2 in /src (#1016)
Bumps [psycopg[binary]](https://github.com/psycopg/psycopg) from 3.2.13 to 3.3.2.
- [Changelog](https://github.com/psycopg/psycopg/blob/master/docs/news.rst)
- [Commits](https://github.com/psycopg/psycopg/compare/3.2.13...3.3.2)

---
updated-dependencies:
- dependency-name: psycopg[binary]
  dependency-version: 3.3.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 09:54:21 +01:00
DaanSelen
9af2983a7b chore: absolute paths and robuust checking
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-12-08 14:51:28 +01:00
Donald Zou
83b4753d5f Merge pull request #1012 from WGDashboard/v4.3.0.2-dev
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
v4.3.0.2 Merge
2025-12-08 16:53:18 +08:00
Donald Zou
f4f0ab27ef Final Testing 2025-12-07 11:20:48 +08:00
Donald Zou
d51815e0f7 Merge pull request #1010 from WGDashboard/version-bump-v4.3.0.2
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Update Version for Admin App
2025-12-06 23:44:08 +08:00
Donald Zou
413b65e976 Merge branch 'main' into version-bump-v4.3.0.2 2025-12-06 23:42:24 +08:00
Donald Zou
b1ea6d3c1d Update Version for Admin App 2025-12-06 23:39:56 +08:00
Donald Zou
c7bed91626 Merge pull request #1009 from WGDashboard/version-bump-v4.3.0.2
Version Bump to v4.3.0.2
2025-12-06 23:34:11 +08:00
Donald Zou
b9e09bf91b Update DashboardConfig.py 2025-12-06 23:33:38 +08:00
Donald Zou
839f686d73 Merge pull request #1005 from WGDashboard/Manage-Tracking-Feature
Manage tracking feature
2025-12-06 23:28:58 +08:00
dependabot[bot]
1ead86dacc build(deps): bump vue from 3.5.22 to 3.5.24 in /src/static/app (#989)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [vue](https://github.com/vuejs/core) from 3.5.22 to 3.5.24.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.22...v3.5.24)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-05 12:06:41 +01:00
dependabot[bot]
c4265b31a9 build(deps): bump ol from 10.6.1 to 10.7.0 in /src/static/app (#990)
Bumps [ol](https://github.com/openlayers/openlayers) from 10.6.1 to 10.7.0.
- [Release notes](https://github.com/openlayers/openlayers/releases)
- [Commits](https://github.com/openlayers/openlayers/compare/v10.6.1...v10.7.0)

---
updated-dependencies:
- dependency-name: ol
  dependency-version: 10.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-05 12:06:28 +01:00
dependabot[bot]
63e6c2040f build(deps): bump @vue/language-server in /src/static/app (#991)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.1.3/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-05 12:05:54 +01:00
dependabot[bot]
35de3eb45a build(deps-dev): bump vite from 7.1.12 to 7.2.2 in /src/static/app (#992)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.12 to 7.2.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.2.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.2.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-05 12:05:44 +01:00
dependabot[bot]
7c35f81af1 build(deps): bump pinia-plugin-persistedstate in /src/static/app (#993)
Bumps [pinia-plugin-persistedstate](https://github.com/prazdevs/pinia-plugin-persistedstate) from 4.5.0 to 4.7.1.
- [Release notes](https://github.com/prazdevs/pinia-plugin-persistedstate/releases)
- [Changelog](https://github.com/prazdevs/pinia-plugin-persistedstate/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prazdevs/pinia-plugin-persistedstate/commits)

---
updated-dependencies:
- dependency-name: pinia-plugin-persistedstate
  dependency-version: 4.7.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-05 12:01:29 +01:00
DaanSelen
1a653bfe32 feat: add custom armv7 compared to amd64 and arm64 (#1007)
* feat: add separate arm/v7 building with c version

* chore: change workflow step names

* fix: my stupid path mistake

* chore: retry sed

* chore: temp comment out

* chore: add libraries and choose for C on arm7

* chore: add arm64 and amd64 back

* chore: streamline workflow

* chore: work on the workflow

* chore: try to separate matrix

* revert to old method with changes

* chore: remove invalid reference

* chore: correct deps

* chore: remove old steps

* feat: add custom armv7 images (compared to arm64 and amd64)

* chore: minor add go version in workflow

---------

Co-authored-by: DaanSelen <dselen@systemec.nl>
2025-12-05 12:00:41 +01:00
Donald Zou
024072688f Stable for SQLite and PostgreSQL 2025-12-02 14:27:51 +08:00
Donald Zou
fa59de4a09 Update PeerJobs.py 2025-12-02 14:08:55 +08:00
Donald Zou
4756556c82 Update 2025-12-02 14:00:53 +08:00
Donald Zou
66ffc12864 Update PeerJobs.py 2025-12-02 13:59:48 +08:00
Donald Zou
9f609b01de Update PeerJobs.py 2025-12-02 13:52:57 +08:00
Donald Zou
a40df0481f Update 2025-12-02 13:36:45 +08:00
Donald Zou
2de46d97cf Update PeerJobs.py 2025-12-02 13:33:36 +08:00
Donald Zou
bd6d27f282 Update 2025-12-02 13:17:24 +08:00
Donald Zou
61c9f5aab7 Update PeerJobLogger.py 2025-12-02 11:34:07 +08:00
Donald Zou
aba3c5da64 Added function to remove stale jobs logs 2025-12-02 11:24:03 +08:00
Donald Zou
2ee1b3b138 Manage Tracking Feature Done 2025-12-02 11:00:54 +08:00
Donald Zou
876a079be3 Update 2025-12-01 10:15:59 +08:00
DaanSelen
1fe03e1664 chore: temporary remove arm/v7 for testing
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-11-26 13:09:01 +01:00
DaanSelen
d55770c385 chore: bump psycopg 2025-11-26 12:42:27 +01:00
David Aghaian
cd11e0495a fix: Adds psycopg dependency in order to run properly (#997)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* fix: Adds psycopg dependency in order to run properly

* Update requirements.txt

Remove the redundant dependncy of psycopg, since [binary] package includes it

---------

Co-authored-by: aghaiand <david.aghaian@panasonic.aero>
2025-11-25 15:47:18 +01:00
dependabot[bot]
24367db1f5 build(deps): bump pydantic from 2.12.3 to 2.12.4 in /src (#988)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.3 to 2.12.4.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/v2.12.4/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.12.3...v2.12.4)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.12.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 11:13:01 +01:00
DaanSelen
bfc3e285db refac: speed up the docker container by removing unneeded steps (#985)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
* refac: speed up the docker container by removing unneeded steps

* fix: revert compose tag

---------

Co-authored-by: DaanSelen <dselen@systemec.nl>
2025-11-06 09:31:57 +01:00
Donald Zou
58dc5a70dc Merge pull request #969 from WGDashboard/Fix-965
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Fix #965
2025-11-06 06:36:11 +08:00
dependabot[bot]
c94b8a17bc build(deps): bump @vueuse/shared in /src/static/app (#977)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Bumps [@vueuse/shared](https://github.com/vueuse/vueuse/tree/HEAD/packages/shared) from 13.9.0 to 14.0.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v14.0.0/packages/shared)

---
updated-dependencies:
- dependency-name: "@vueuse/shared"
  dependency-version: 14.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-05 08:55:32 +01:00
dependabot[bot]
b18ceca439 build(deps): bump @vuepic/vue-datepicker in /src/static/app (#974)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [@vuepic/vue-datepicker](https://github.com/Vuepic/vue-datepicker) from 11.0.2 to 11.0.3.
- [Release notes](https://github.com/Vuepic/vue-datepicker/releases)
- [Changelog](https://github.com/Vuepic/vue-datepicker/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Vuepic/vue-datepicker/compare/v11.0.2...v11.0.3)

---
updated-dependencies:
- dependency-name: "@vuepic/vue-datepicker"
  dependency-version: 11.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-05 00:01:23 +01:00
dependabot[bot]
b794c342b1 build(deps): bump @vueuse/core from 13.9.0 to 14.0.0 in /src/static/app (#976)
Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 13.9.0 to 14.0.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v14.0.0/packages/core)

---
updated-dependencies:
- dependency-name: "@vueuse/core"
  dependency-version: 14.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 22:54:25 +01:00
dependabot[bot]
230fa166b8 build(deps-dev): bump vite from 7.1.11 to 7.1.12 in /src/static/app (#975)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.11 to 7.1.12.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.1.12/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.12/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 22:54:13 +01:00
dependabot[bot]
0ed5640499 build(deps): bump @vue/language-server in /src/static/app (#973)
Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.1.2/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 22:52:01 +01:00
dependabot[bot]
931d547046 build(deps): bump psycopg from 3.2.11 to 3.2.12 in /src (#972)
Bumps [psycopg](https://github.com/psycopg/psycopg) from 3.2.11 to 3.2.12.
- [Changelog](https://github.com/psycopg/psycopg/blob/master/docs/news.rst)
- [Commits](https://github.com/psycopg/psycopg/compare/3.2.11...3.2.12)

---
updated-dependencies:
- dependency-name: psycopg
  dependency-version: 3.2.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 22:51:42 +01:00
dependabot[bot]
30dd9f3e54 build(deps): bump psutil from 7.1.1 to 7.1.3 in /src (#983)
Bumps [psutil](https://github.com/giampaolo/psutil) from 7.1.1 to 7.1.3.
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-7.1.1...release-7.1.3)

---
updated-dependencies:
- dependency-name: psutil
  dependency-version: 7.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 20:16:59 +01:00
Donald Zou
c98a83e5b1 Update README with announcements and testing program
Some checks failed
Mark stale issues and pull requests / stale (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Announce WGDashboard's listing on DigitalOcean Marketplace and introduce the WGDashboard Testing Program.
2025-10-28 08:45:16 +08:00
Donald Zou
10c9b5635f Fixed duplicated data 2025-10-24 09:53:40 +08:00
Donald Zou
9dd5341fd9 Remove app access log into the database 2025-10-24 09:53:09 +08:00
Donald Zou
d763867566 Fixed stale job issue 2025-10-24 09:21:48 +08:00
Donald Zou
18531b71c7 Fixed schedule task delete issue 2025-10-24 08:47:02 +08:00
dependabot[bot]
bbeae93c05 build(deps): bump psycopg from 3.2.10 to 3.2.11 in /src (#968)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [psycopg](https://github.com/psycopg/psycopg) from 3.2.10 to 3.2.11.
- [Changelog](https://github.com/psycopg/psycopg/blob/master/docs/news.rst)
- [Commits](https://github.com/psycopg/psycopg/compare/3.2.10...3.2.11)

---
updated-dependencies:
- dependency-name: psycopg
  dependency-version: 3.2.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-23 08:53:02 +02:00
dependabot[bot]
3c297cbb96 build(deps): bump psutil from 7.1.0 to 7.1.1 in /src (#967)
Bumps [psutil](https://github.com/giampaolo/psutil) from 7.1.0 to 7.1.1.
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-7.1.0...release-7.1.1)

---
updated-dependencies:
- dependency-name: psutil
  dependency-version: 7.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-23 08:52:52 +02:00
dependabot[bot]
954f99b354 build(deps): bump pydantic from 2.12.2 to 2.12.3 in /src (#966)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.2 to 2.12.3.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.12.2...v2.12.3)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.12.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-23 08:52:44 +02:00
dependabot[bot]
3f75c0aa3b Bump @vue/language-server from 3.1.0 to 3.1.1 in /src/static/app (#942)
Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.1.1/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-23 08:47:10 +02:00
dependabot[bot]
33d8a1f991 Bump npm from 11.6.1 to 11.6.2 in /src/static/app (#953)
Bumps [npm](https://github.com/npm/cli) from 11.6.1 to 11.6.2.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v11.6.1...v11.6.2)

---
updated-dependencies:
- dependency-name: npm
  dependency-version: 11.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-23 08:46:56 +02:00
dependabot[bot]
616238735a build(deps-dev): bump vite from 7.1.7 to 7.1.11 in /src/static/app (#963)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.7 to 7.1.11.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.11/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-23 08:46:43 +02:00
DaanSelen
649c1ad292 feat: add version pinning managed by @dependabot (#960) 2025-10-23 08:46:20 +02:00
dependabot[bot]
145b18c28c build(deps): bump vue-router from 4.5.1 to 4.6.3 in /src/static/app (#964)
Bumps [vue-router](https://github.com/vuejs/router) from 4.5.1 to 4.6.3.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/compare/v4.5.1...v4.6.3)

---
updated-dependencies:
- dependency-name: vue-router
  dependency-version: 4.6.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-23 08:46:01 +02:00
dependabot[bot]
34e43de1a1 Bump is-cidr from 5.1.1 to 6.0.1 in /src/static/app (#931)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [is-cidr](https://github.com/silverwind/is-cidr) from 5.1.1 to 6.0.1.
- [Release notes](https://github.com/silverwind/is-cidr/releases)
- [Commits](https://github.com/silverwind/is-cidr/compare/5.1.1...6.0.1)

---
updated-dependencies:
- dependency-name: is-cidr
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-16 20:07:01 +02:00
dependabot[bot]
b7ed934331 Bump python from 3.13-alpine to 3.14-alpine in /docker (#952)
Bumps python from 3.13-alpine to 3.14-alpine.

---
updated-dependencies:
- dependency-name: python
  dependency-version: 3.14-alpine
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-16 20:06:38 +02:00
Donald Zou
d5a7632bb6 Merge pull request #956 from WGDashboard/security-support-patch
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
chore: update security policy
2025-10-16 11:20:38 +08:00
DaanSelen
6621c5f910 chore: update security policy 2025-10-15 13:44:02 +02:00
DaanSelen
09a3fddd87 fix: alpine error suicide bug (#950)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Co-authored-by: Daan Selen <dselen@systemec.nl>
2025-10-14 08:48:33 +02:00
Donald Zou
45510165a2 Merge pull request #951 from WGDashboard/spel-fix-01
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
chore: fix spelling errors
2025-10-14 06:19:45 +08:00
DaanSelen
508dbf8dc2 chore: fix spelling errors 2025-10-13 12:34:47 +02:00
Donald Zou
eb1d52ffba Merge pull request #932 from WGDashboard/dependabot/npm_and_yarn/src/static/app/npm-11.6.1
Some checks failed
Mark stale issues and pull requests / stale (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Bump npm from 11.6.0 to 11.6.1 in /src/static/app
2025-10-02 09:35:50 +08:00
Donald Zou
5db7351f8c Merge pull request #937 from WGDashboard/dependabot/npm_and_yarn/src/static/app/vue/language-server-3.1.0
Bump @vue/language-server from 3.0.8 to 3.1.0 in /src/static/app
2025-10-02 09:35:26 +08:00
dependabot[bot]
5ae3a56337 Bump npm from 11.6.0 to 11.6.1 in /src/static/app
Bumps [npm](https://github.com/npm/cli) from 11.6.0 to 11.6.1.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v11.6.0...v11.6.1)

---
updated-dependencies:
- dependency-name: npm
  dependency-version: 11.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-02 01:35:04 +00:00
Donald Zou
e7068b472e Merge pull request #926 from WGDashboard/dependabot/npm_and_yarn/src/static/app/uuid-13.0.0
Bump uuid from 11.1.0 to 13.0.0 in /src/static/app
2025-10-02 09:33:39 +08:00
dependabot[bot]
3dc94a35a1 Bump @vue/language-server from 3.0.8 to 3.1.0 in /src/static/app
Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.0.8 to 3.1.0.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.1.0/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-29 23:15:11 +00:00
dependabot[bot]
f82abd71a3 Bump uuid from 11.1.0 to 13.0.0 in /src/static/app
Bumps [uuid](https://github.com/uuidjs/uuid) from 11.1.0 to 13.0.0.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v11.1.0...v13.0.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 13.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-25 22:03:51 +00:00
Donald Zou
602238d794 Merge pull request #933 from WGDashboard/dependabot/npm_and_yarn/src/static/app/vue-3.5.22
Some checks failed
Mark stale issues and pull requests / stale (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Bump vue from 3.5.21 to 3.5.22 in /src/static/app
2025-09-26 06:02:36 +08:00
dependabot[bot]
4d4a15740b Bump vue from 3.5.21 to 3.5.22 in /src/static/app
Bumps [vue](https://github.com/vuejs/core) from 3.5.21 to 3.5.22.
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.21...v3.5.22)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-25 18:23:15 +00:00
DaanSelen
524d50ee07 Update dependabot.yml
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
chore: set dependabot to run weekly
2025-09-25 20:21:56 +02:00
dependabot[bot]
fc591b7fe8 Bump vite from 7.1.6 to 7.1.7 in /src/static/app (#927)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.6 to 7.1.7.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.7/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-24 10:29:38 +02:00
dependabot[bot]
c2f06193d0 Bump @vue/language-server from 3.0.7 to 3.0.8 in /src/static/app (#929)
Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.0.7 to 3.0.8.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.0.8/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.0.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-24 10:29:19 +02:00
Donald Zou
f2ead12315 Merge pull request #924 from WGDashboard/fix-#920
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Update PeerJobs.py
2025-09-22 22:51:52 +08:00
Donald Zou
ca8700ac2a Update PeerJobs.py 2025-09-22 22:50:59 +08:00
Donald Zou
10a8d22efd Merge pull request #922 from WGDashboard/docker-duplicate-hotfix
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2025-09-22 17:53:34 +08:00
Donald Zou
fc3ec61373 Merge pull request #923 from WGDashboard/remove-docker-funcs
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-09-22 05:46:38 +08:00
Daan Selen
094d1c0718 refac: remove docker functions 2025-09-21 22:04:59 +02:00
Daan Selen
0d814ec03c refac: new logic to detecting a Wireguard interface 2025-09-21 21:57:28 +02:00
dependabot[bot]
5ccfe07e12 Bump npm from 10.9.3 to 11.6.0 in /src/static/app (#901)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Bumps [npm](https://github.com/npm/cli) from 10.9.3 to 11.6.0.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v10.9.3...v11.6.0)

---
updated-dependencies:
- dependency-name: npm
  dependency-version: 11.6.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-21 17:30:31 +02:00
dependabot[bot]
101ac5e985 Bump vite from 7.1.5 to 7.1.6 in /src/static/app (#912)
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.5 to 7.1.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-19 21:00:15 +02:00
dependabot[bot]
113a780eec Bump @vue/language-server from 3.0.5 to 3.0.7 in /src/static/app (#902)
Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.0.5 to 3.0.7.
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v3.0.7/packages/language-server)

---
updated-dependencies:
- dependency-name: "@vue/language-server"
  dependency-version: 3.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-19 20:59:48 +02:00
Donald Zou
cf77610a56 Merge pull request #914 from WGDashboard/add-template
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
2025-09-19 18:24:45 +08:00
Daan Selen
84675fe521 feat: add default wg-dashboard.ini config 2025-09-19 10:32:25 +02:00
Donald Zou
5db5b35311 Update README.md
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
2025-09-17 23:31:20 +08:00
DaanSelen
ff345c9609 style: readme update 2025-09-17 16:03:35 +02:00
Daan Selen
6cccfec923 fix: fix the docker building issue
I accidentally removed a character
  we are all human.
2025-09-17 15:58:16 +02:00
Donald Zou
8231dd1463 Merge pull request #906 from WGDashboard/docker-doc-refac
refac(docs): rewrite and check the docker documents
2025-09-17 21:49:25 +08:00
Daan Selen
d8ff020d8c refac(docs): rewrite and check the docker documents 2025-09-17 15:34:39 +02:00
Daan Selen
238fb91360 chore: further expand and change the compose file
For people that want to get started quickly
2025-09-17 15:20:01 +02:00
DaanSelen
9ecc16fcc1 chore: update dependabot config 2025-09-17 11:05:31 +02:00
184 changed files with 5440 additions and 4093 deletions

View File

@@ -2,4 +2,8 @@
.github
*.md
tests/
docs/
docs/
src/db
src/wg-dashboard.ini
src/static/app
src/static/client

4
.github/FUNDING.yml vendored
View File

@@ -1,4 +1,4 @@
# These are supported funding model platforms
github: [donaldzou]
patreon: DonaldDonnyZou
github: [WGDashboard]
open_collective: wgdashboard

View File

@@ -1,6 +1,6 @@
---
name: Bug report
about: Create a report to help us improve
about: Create a report to help us improve the project!
title: ''
labels: bug
assignees: ''
@@ -11,6 +11,7 @@ assignees: ''
A clear and concise description of what the bug is.
**Expected Error / Traceback**
```
Please provide the error traceback here
```
@@ -20,9 +21,22 @@ Please provide how you run the dashboard
**OS Information:**
- OS: [e.g. Ubuntu 18.02]
- Python Version: [e.g v3.7]
- Docker: [e.g. Yes or No]
- Python Version: [e.g v37]
**Docker Details**
If using docker, provide the following:
- Image used: [e.g. ghcr.io/wgdashboard/wgdashboard:latest]
- Compose: [e.g. Yes or No]
If Docker Compose is used:
```yaml
**Your Docker Compose**
```
**Sample of your `.conf` file**
```
Please provide a sample of your configuration file that you are having problem with. You can replace your public key and private key to ABCD...
```ini
Please provide a sample of your configuration file that you are having problem with.
You should replace your public key and private key to ABCD...
```

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: WGDashboard Community Support (Github)
url: https://github.com/orgs/WGDashboard/discussions
about: Please ask and answer questions here.
- name: WGDashboard Community Support (Discord)
url: https://discord.gg/72TwzjeuWm
about: Discord Server about WGDashboard
- name: WGDashboard Issue reporting (Bugs and Feature Requests (FR))
url: https://github.com/WGDashboard/WGDashboard/issues
about: Please report bugs or feature requests here so they can be tracked

View File

@@ -1,6 +1,6 @@
---
name: Feature request
about: Suggest an idea for this project
name: Feature Request (FR)
about: Suggest an idea to extend or improve the usage of the project
title: ''
labels: enhancement
assignees: ''
@@ -10,5 +10,11 @@ assignees: ''
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Related/Adjacent Issues**
Paste a Github link to possible issues that could be solved if they exist.
**What have you done yourself?**
What have you already tried to do or considered to fix this issue?
**Describe the solution you'd like**
A clear and concise description of what you want to happen.

View File

@@ -7,25 +7,36 @@ version: 2
updates:
- package-ecosystem: "pip"
directory: "/src"
target-branch: "development"
schedule:
interval: "daily"
interval: "weekly"
- package-ecosystem: "npm"
directory: "/"
directory: "/src/static/app"
target-branch: "development"
schedule:
interval: "daily"
interval: "weekly"
- package-ecosystem: "npm"
directory: "/src/static/client"
target-branch: "development"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/.github"
target-branch: "development"
schedule:
interval: "daily"
interval: "weekly"
- package-ecosystem: "docker"
directory: "/docker"
target-branch: "development"
schedule:
interval: "daily"
interval: "weekly"
- package-ecosystem: "docker-compose"
directory: "/docker"
target-branch: "development"
schedule:
interval: "daily"
interval: "weekly"

View File

@@ -39,11 +39,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,7 +54,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v4
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -68,4 +68,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4

View File

@@ -5,8 +5,13 @@ on:
push:
branches:
- 'main'
- 'development'
- '**dev'
tags:
- '*'
paths:
- 'src/**'
- 'docker/**'
release:
types: [ published ]
@@ -44,15 +49,12 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: |
- linux/amd64
- linux/arm64
- linux/arm/v7
platforms: linux/amd64,linux/arm64,linux/arm/v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta by docs https://github.com/docker/metadata-action
- name: Extract Docker metadata from environment
id: meta
uses: docker/metadata-action@v5
with:
@@ -64,7 +66,7 @@ jobs:
type=ref,event=tag
type=sha,format=short,prefix=
- name: Build and export (multi-arch)
- name: Build and export Docker image
uses: docker/build-push-action@v6
with:
context: .
@@ -85,7 +87,7 @@ jobs:
registry: ${{ env.DOCKERHUB_PREFIX }}
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
@@ -102,7 +104,6 @@ jobs:
only-fixed: true
write-comment: true
github-token: ${{ secrets.GITHUB_TOKEN }}
exit-code: true
- name: Docker Scout Compare
uses: docker/scout-action@v1

7
.gitignore vendored
View File

@@ -1,3 +1,4 @@
*.tar
.vscode
.DS_Store
.idea
@@ -10,7 +11,7 @@ src/static/pic.xd
*.conf
private_key.txt
public_key.txt
venv/**
*venv*
log/**
release/*
src/db/wgdashboard.db
@@ -19,6 +20,8 @@ node_modules/**
*/proxy.js
src/static/app/proxy.js
.secrets
*.ini
*.pid
# Logs
logs
@@ -49,4 +52,4 @@ coverage
*.sw?
*.tsbuildinfo
.vite/*
.vite/*

View File

@@ -1,9 +1,8 @@
> [!WARNING]
> All users running WGDashboard v4.2.x or later and hosted on the public internet are strongly advised to update to the latest release immediately. For more information: [v4.3.2 Release](https://github.com/WGDashboard/WGDashboard/releases/tag/v4.3.2)
> [!TIP]
> 🎉 I'm excited to announce that WGDashboard is officially listed on DigitalOcean's Marketplace! For more information, please visit [Host WGDashboard & WireGuard with DigitalOcean](https://docs.wgdashboard.dev/host-wgdashboard-wireguard-with-digitalocean.html) for more information!
> [!NOTE]
> **Help Wanted 🎉**: Localizing WGDashboard to other languages! If you're willing to help, please visit https://github.com/WGDashboard/WGDashboard/issues/397. Many thanks!
> 🎉 To help us better understand and improve WGDashboards performance, were launching the **WGDashboard Testing Program**. As part of this program, participants will receive free WireGuard VPN access to our server in Toronto, Canada, valid for **24 hours** or up to **1GB of total traffic**—whichever comes first. If youd like to join, visit [https://wg.wgdashboard.dev/](https://wg.wgdashboard.dev/) for more details!
![](https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Posters/Banner.png)
@@ -23,9 +22,11 @@
<p align="center">
<a href="https://github.com/WGDashboard/WGDashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard?style=for-the-badge"></a>
<a href="https://wakatime.com/badge/github/donaldzou/WGDashboard"><img src="https://wakatime.com/badge/github/donaldzou/WGDashboard.svg?style=for-the-badge" alt="wakatime"></a>
<a href="https://wakatime.com/badge/github/donaldzou/WGDashboard"><img src="https://wakatime.com/badge/user/45f53c7c-9da9-4cb0-85d6-17bd38cc748b/project/5334ae20-e9a6-4c55-9fea-52d4eb9dfba6.svg?style=for-the-badge" alt="wakatime"></a>
<a href="https://hitscounter.dev"><img src="https://hitscounter.dev/api/hit?url=https%3A%2F%2Fgithub.com%2Fdonaldzou%2FWGDashboard&label=Visitor&icon=github&color=%230a58ca&style=for-the-badge"></a>
<img src="https://img.shields.io/docker/pulls/donaldzou/wgdashboard?logo=docker&label=Docker%20Image%20Pulls&labelColor=ffffff&style=for-the-badge">
<img src="https://github.com/WGDashboard/WGDashboard/actions/workflows/docker.yml/badge.svg?style=for-the-badge">
<img src="https://github.com/WGDashboard/WGDashboard/actions/workflows/codeql-analyze.yaml/badge.svg">
</p>
<p align="center"><b>This project is supported by</b></p>
<p align="center">
@@ -33,10 +34,10 @@
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
<p align="center">Monitoring WireGuard is not convenient, in most case, you'll need to login to your server and type <code>wg show</code>. That's why this project is being created, to view and manage all WireGuard configurations in a easy way.</p>
<p align="center">With all these awesome features, while keeping it <b>easy to install and use</b></p>
<p align="center">Monitoring WireGuard is not convenient, in most case, you'll need to login to your server and type <code>wg show</code>. That's why this project is being created, to view and manage all WireGuard configurations in an easy way.</p>
<p align="center">Though all these awesome features are present, we are still striving to make it <b>easy to install and use</b></p>
<p align="center"><b><i>This project is not affiliate to the official WireGuard Project</i></b></p>
<p align="center"><b><i>This project is not affiliated to the official WireGuard Project</i></b></p>
<h3 align="center">Looking for help or want to chat about this project?</h4>
<p align="center">
@@ -52,7 +53,7 @@
You can support via <br>
</p>
<p align="center">
<a align="center" href="https://github.com/sponsors/donaldzou" target="_blank"><img src="https://img.shields.io/badge/GitHub%20Sponsor-2e9a40?style=for-the-badge&logo=github"></a>
<a align="center" href="https://github.com/sponsors/WGDashboard" target="_blank"><img src="https://img.shields.io/badge/GitHub%20Sponsor-2e9a40?style=for-the-badge&logo=github"></a>
<a align="center" href="https://buymeacoffee.com/donaldzou" target="_blank"><img src="https://img.shields.io/badge/Buy%20me%20a%20coffee-ffdd00?style=for-the-badge&logo=buymeacoffee&logoColor=000000"></a>
<a align="center" href="https://patreon.com/c/DonaldDonnyZou/membership" target="_blank"><img src="https://img.shields.io/badge/Patreon-000000?style=for-the-badge&logo=patreon&logoColor=ffffff"></a>
</p>

View File

@@ -4,7 +4,5 @@
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
| 4.3 | :white_check_mark: |
| < 4.3 | :x: |

View File

@@ -3,8 +3,10 @@
# Base: Alpine
#
FROM golang:1.25-alpine AS awg-go
# Pull the current golang-alpine image.
FROM golang:1.26-alpine3.23 AS awg-go
# Install build-dependencies.
RUN apk add --no-cache \
git \
gcc \
@@ -14,18 +16,23 @@ RUN apk add --no-cache \
RUN mkdir -p /workspace && \
git clone https://github.com/WGDashboard/amneziawg-go /workspace/awg
# Enable CGO compilation for AmneziaWG
ENV CGO_ENABLED=1
# Change directory
WORKDIR /workspace/awg
RUN go mod download && \
# Compile the binaries
RUN go version && \
go mod download && \
go mod verify && \
go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"' -v -o /usr/bin
#
# AWG TOOLS BUILDING STAGE
# Base: Debian
# Base: Alpine
#
FROM alpine:latest AS awg-tools
FROM alpine:3.23 AS awg-tools
# Install needed dependencies.
RUN apk add --no-cache \
make \
git \
@@ -33,31 +40,47 @@ RUN apk add --no-cache \
linux-headers \
ca-certificates
# Get the workspace ready
RUN mkdir -p /workspace && \
git clone https://github.com/WGDashboard/amneziawg-tools /workspace/awg-tools
# Change directory
WORKDIR /workspace/awg-tools/src
# Compile and change permissions
RUN make && chmod +x wg*
#
# PIP DEPENDENCY BUILDING
# Base: Alpine
#
FROM python:3.13-alpine AS pip-builder
# Use the python-alpine image for building pip dependencies
FROM python:3.14-alpine3.23 AS pip-builder
ARG TARGETPLATFORM
# Add the build dependencies and create a Python virtual environment.
RUN apk add --no-cache \
build-base \
pkgconfig \
python3-dev \
postgresql-dev \
libffi-dev \
libpq \
linux-headers \
rust \
cargo \
&& mkdir -p /opt/wgdashboard/src \
&& python3 -m venv /opt/wgdashboard/src/venv
# Copy the requirements file into the build layer.
COPY ./src/requirements.txt /opt/wgdashboard/src
RUN if [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then \
sed -i 's|psycopg\[binary\]|psycopg[c]|' /opt/wgdashboard/src/requirements.txt; \
fi; \
cat /opt/wgdashboard/src/requirements.txt
# Install the pip packages
RUN . /opt/wgdashboard/src/venv/bin/activate && \
pip3 install --upgrade pip && \
pip3 install -r /opt/wgdashboard/src/requirements.txt
@@ -66,59 +89,57 @@ RUN . /opt/wgdashboard/src/venv/bin/activate && \
# WGDashboard RUNNING STAGE
# Base: Alpine
#
FROM python:3.13-alpine AS final
# Running with the python-alpine image.
FROM python:3.14-alpine3.23 AS final
LABEL maintainer="dselen@nerthus.nl"
# Install only the runtime dependencies
RUN apk add --no-cache \
iproute2 iptables \
bash curl \
wget unzip \
procps sudo \
tzdata wireguard-tools \
openresolv openrc
bash curl procps openrc \
tzdata wireguard-tools envsubst
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Copy only the final binaries from the builders
# Copy only the final binaries from the AWG builder stages
COPY --from=awg-go /usr/bin/amneziawg-go /usr/bin/amneziawg-go
COPY --from=awg-tools /workspace/awg-tools/src/wg /usr/bin/awg
COPY --from=awg-tools /workspace/awg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick
# Environment variables
ARG wg_net="10.0.0.1"
ARG wg_subn="24"
ARG wg_port="51820"
ENV TZ="Europe/Amsterdam" \
global_dns="9.9.9.9" \
wgd_port="10086" \
public_ip="" \
WGDASH=/opt/wgdashboard
WGDASH=/opt/wgdashboard \
dynamic_config="true"
# Create directories
RUN mkdir /data /configs -p ${WGDASH}/src /etc/amnezia/amneziawg
# Create directories needed for operation
RUN mkdir /data /configs -p ${WGDASH}/src /etc/amnezia/amneziawg \
&& echo "name_servers=${global_dns}" >> /etc/resolvconf.conf
# Copy app source and prebuilt venv only (no pip cache)
# Copy the venv and source files from local compiled locations or repos
COPY ./src ${WGDASH}/src
COPY --from=pip-builder /opt/wgdashboard/src/venv /opt/wgdashboard/src/venv
COPY ./docker/wg0.conf.template /tmp/wg0.conf.template
COPY ./docker/wg-dashboard-oidc-providers.json.template /tmp/wg-dashboard-oidc-providers.json.template
# Copy in the runtime script, essential.
COPY ./docker/entrypoint.sh /entrypoint.sh
# WireGuard interface template
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN out_adapt=$(ip -o -4 route show to default | awk '{print $NF}') \
&& echo -e "[Interface]\n\
Address = ${wg_net}/24\n\
PrivateKey =\n\
PostUp = iptables -t nat -I POSTROUTING 1 -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE\n\
PostUp = iptables -I FORWARD -i wg0 -o wg0 -j DROP\n\
PreDown = iptables -t nat -D POSTROUTING -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE\n\
PreDown = iptables -D FORWARD -i wg0 -o wg0 -j DROP\n\
ListenPort = ${wg_port}\n\
SaveConfig = true\n\
DNS = ${global_dns}" > /configs/wg0.conf.template \
&& chmod 600 /configs/wg0.conf.template
# First WireGuard interface template
RUN export out_adapt=$(ip -o -4 route show to default | awk '{print $NF}') \
&& envsubst < /tmp/wg0.conf.template > /configs/wg0.conf.template \
&& chmod 600 /configs/wg0.conf.template \
&& cat /configs/wg0.conf.template
# Set a healthcheck to determine the container its health
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
COPY ./docker/entrypoint.sh /entrypoint.sh
# Expose ports on the container
EXPOSE 10086
WORKDIR $WGDASH/src

View File

@@ -3,8 +3,8 @@ Author: @DaanSelen<br>
This document delves into how the WGDashboard Docker container has been built.<br>
Of course there are two stages (simply said), one before run-time and one at/after run-time.<br>
The `Dockerfile` describes how the container image is made, and the `entrypoint.sh` is executed after running the container. <br>
In this example, WireGuard is integrated into the container itself, so it should be a run-and-go(/out-of-the-box).<br>
The `Dockerfile` describes how the container image is made, and the `entrypoint.sh` is executed after the container is started. <br>
In this example, [WireGuard](https://www.wireguard.com/) is integrated into the container itself, so it should be a run-and-go(/out-of-the-box) experience.<br>
For more details on the source-code specific to this Docker image, refer to the source files, they have lots of comments.
<br>
@@ -18,20 +18,24 @@ For more details on the source-code specific to this Docker image, refer to the
/>
<br>
To get the container running you either pull the image from the repository, (docker.io)`donaldzou/wgdashboard:latest`.<br>
From there either use the environment variables describe below as parameters or use the Docker Compose file: `compose.yaml`.<br>
Be careful, the default generated WireGuard configuration file uses port 51820/udp. So use this port if you want to use it out of the box.<br>
Otherwise edit the configuration file in `/etc/wireguard/wg0.conf`.
To get the container running you either pull the pre-made image from a remote repository, there are 2 official options.<br>
- ghcr.io/wgdashboard/wgdashboard:<tag>
- docker.io/donaldzou/wgdashboard:<tag>
> tags should be either: latest, main, <version>, <branch-name> (if built) or <commit-sha>.
From there either use the environment variables described below as parameters or use the Docker Compose file: `compose.yaml`.<br>
Be careful, the default generated WireGuard configuration file uses port 51820/udp. So make sure to use this port if you want to use it out of the box.<br>
Otherwise edit the configuration file in WGDashboard under `Configuration Settings` -> `Edit Raw Configuration File`.
> Otherwise you need to enter the container and edit: `/etc/wireguard/wg0.conf`.
# WGDashboard: 🐳 Docker Deployment Guide
To run the container, you can either pull the image from Docker Hub or build it yourself. The image is available at:
To run the container, you can either pull the image from the Github Container Registry (ghcr.io), Docker Hub (docker.io) or build it yourself. The image is available at:
```
docker.io/donaldzou/wgdashboard:latest
```
> `docker.io` is in most cases automatically resolved by the Docker application.
> `docker.io` is in most cases automatically resolved by the Docker application. Therefor you can ofter specify: `donaldzou/wgdashboard:latest`
### 🔧 Quick Docker Run Command
@@ -44,37 +48,37 @@ docker run -d \
-p 10086:10086/tcp \
-p 51820:51820/udp \
--cap-add NET_ADMIN \
donaldzou/wgdashboard:latest
ghcr.io/wgdashboard/wgdashboard:latest
```
> ⚠️ The default WireGuard port is `51820/udp`. If you change this, update the `/etc/wireguard/wg0.conf` accordingly.
---
### 📦 Docker Compose Alternative
### 📦 Docker Compose Alternative (see the [compose file](./compose.yaml))
You can also use Docker Compose for easier configuration:
```yaml
services:
wgdashboard:
image: donaldzou/wgdashboard:latest
image: ghcr.io/wgdashboard/wgdashboard:latest
restart: unless-stopped
container_name: wgdashboard
environment:
# - tz=Europe/Amsterdam
# - global_dns=1.1.1.1
# - public_ip=YOUR_PUBLIC_IP
ports:
- 10086:10086/tcp
- 51820:51820/udp
volumes:
- aconf:/etc/amnezia/amneziawg
- conf:/etc/wireguard
- data:/data
cap_add:
- NET_ADMIN
volumes:
aconf:
conf:
data:
```
@@ -85,7 +89,7 @@ volumes:
## 🔄 Updating the Container
Updating WGDashboard is currently in **alpha** stage. While the update process may work, it's still under testing.
Updating the WGDashboard container should be through 'The Docker Way' - by pulling the newest/newer image and replacing this old one.
---
@@ -93,6 +97,7 @@ Updating WGDashboard is currently in **alpha** stage. While the update process m
| Variable | Accepted Values | Default | Example | Description |
| ------------------ | ---------------------------------------- | ----------------------- | --------------------- | ----------------------------------------------------------------------- |
| `dynamic_config` | true, yes, false, no | `true` | `true` or `no` | Turns on or off the dynamic configuration feature, on by default for Docker |
| `tz` | Timezone | `Europe/Amsterdam` | `America/New_York` | Sets the container's timezone. Useful for accurate logs and scheduling. |
| `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. |
| `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NATd. |
@@ -108,6 +113,11 @@ Updating WGDashboard is currently in **alpha** stage. While the update process m
| `email_password` | Any non-empty string | `-` | `app_password` | Password for SMTP authentication. |
| `email_from` | Valid email address | `-` | `noreply@example.com` | Email address used as the sender for notifications. |
| `email_template` | Path to template file | `-` | `your-template` | Custom template for email notifications. |
| `database_type` | `sqlite`, `postgresql`, `mariadb+mariadbconnector`, etc. | `-` | `postgresql` | Type of [sqlalchemy database engine](https://docs.sqlalchemy.org/en/21/core/engines.html). |
| `database_host` | Any non-empty string | `-` | `localhost` | IP-Address or hostname of the SQL-database server. |
| `database_port` | Any non-empty string (or int for port) | `-` | `5432` | Port for the database communication. |
| `database_username`| Valid database username | `-` | `database_user` | Database user username. |
| `database_password`| Valid database password | `-` | `database_password` | Database user password. |
---
@@ -205,4 +215,4 @@ ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
## Closing remarks:
For feedback please submit an issue to the repository. Or message dselen@nerthus.nl.
For feedback please submit an issue to the repository. Or message dselen@nerthus.nl.

View File

@@ -1,22 +1,42 @@
services:
wireguard-dashboard:
image: donaldzou/wgdashboard:latest
wgdashboard:
# Since the github organisation we recommend the ghcr.io.
# Alternatively we also still push to docker.io under donaldzou/wgdashboard.
# Both share the exact same tags. So they should be interchangable.
image: ghcr.io/wgdashboard/wgdashboard:latest
# Make sure to set the restart policy. Because for a VPN its important to come back IF it crashes.
restart: unless-stopped
container_name: wgdashboard
# Environment variables can be used to configure certain values at startup. Without having to configure it from the dashboard.
# By default its all disabled, but uncomment the following lines to apply these. (uncommenting is removing the # character)
# Refer to the documentation on https://wgdashboard.dev/ for more info on what everything means.
#environment:
#- wg_autostart=wg0
#- tz= # <--- Set container timezone, default: Europe/Amsterdam.
#- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
#- wgd_port= # <--- Set the port WGDashboard will use for its web-server.
# The following section, ports is very important for exposing more than one Wireguard/AmneziaWireguard interfaces.
# Once you create a new configuration and assign a port in the dashboard, don't forget to add it to the ports as well.
# Quick-tip: most Wireguard VPN tunnels use UDP. WGDashboard uses HTTP, so tcp.
ports:
- 10086:10086/tcp
- 51820:51820/udp
# Volumes can be configured however you'd like. The default is using docker volumes.
# If you want to use local paths, replace the path before the : with your path.
volumes:
- aconf:/etc/amnezia/amneziawg
- conf:/etc/wireguard
- data:/data
# Needed for network administration.
cap_add:
- NET_ADMIN
# The following configuration is linked to the above default volumes.
volumes:
aconf:
conf:

View File

@@ -1,28 +1,29 @@
#!/bin/bash
config_file="/data/wg-dashboard.ini"
runtime_pid=""
trap 'stop_service' SIGTERM
# Hash password with bcrypt
hash_password() {
python3 -c "import bcrypt; print(bcrypt.hashpw('$1'.encode(), bcrypt.gensalt(12)).decode())"
${WGDASH}/src/venv/bin/python3 -c "import bcrypt; print(bcrypt.hashpw('$1'.encode(), bcrypt.gensalt(12)).decode())"
}
# Function to set or update section/key/value in the INI file
set_ini() {
local section="$1" key="$2" value="$3"
local current_value
# Add section if it doesn't exist
grep -q "^\[${section}\]" "$config_file" \
|| printf "\n[%s]\n" "${section}" >> "$config_file"
# Check current value if key exists
if grep -q "^[[:space:]]*${key}[[:space:]]*=" "$config_file"; then
current_value=$(grep "^[[:space:]]*${key}[[:space:]]*=" "$config_file" | cut -d= -f2- | xargs)
# Don't display actual value if it's a password field
# Dont display actual value if it's a password field
if [[ "$key" == *"password"* ]]; then
if [ "$current_value" = "$value" ]; then
echo "- $key is already set correctly (value hidden)"
@@ -40,7 +41,7 @@ set_ini() {
fi
else
sed -i "/^\[${section}\]/a ${key} = ${value}" "$config_file"
# Don't display actual value if it's a password field
if [[ "$key" == *"password"* ]]; then
echo "- Added new setting $key (value hidden)"
@@ -52,7 +53,31 @@ set_ini() {
stop_service() {
echo "[WGDashboard] Stopping WGDashboard..."
/bin/bash ./wgd.sh stop
local max_rounds="10"
local round="0"
local runtime_pid=""
while true; do
round=$((round + 1))
if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
runtime_pid=$(cat ${WGDASH}/src/gunicorn.pid)
echo "Running as PID: ${runtime_pid}"
return 0
fi
if [[ $round -eq $max_rounds ]]; then
echo "Reached breaking point!"
return 1
fi
sleep 0.5s
done
kill $runtime_pid
exit 0
}
@@ -60,59 +85,77 @@ echo "------------------------- START ----------------------------"
echo "Starting the WGDashboard Docker container."
ensure_installation() {
echo "Quick-installing..."
# Make the wgd.sh script executable.
chmod +x "${WGDASH}"/src/wgd.sh
cd "${WGDASH}"/src || exit
# Github issue: https://github.com/donaldzou/WGDashboard/issues/723
echo "Checking for stale pids..."
if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
echo "Found stale pid, removing..."
rm ${WGDASH}/src/gunicorn.pid
fi
# Removing clear shell command from the wgd.sh script to enhance docker logging.
echo "Removing clear command from wgd.sh for better Docker logging."
sed -i '/clear/d' ./wgd.sh
# PERSISTENCE FOR databases directory
# Create required directories and links
if [ ! -d "/data/db" ]; then
echo "Creating database dir"
mkdir -p /data/db
fi
if [ ! -d "${WGDASH}/src/db" ]; then
ln -s /data/db "${WGDASH}/src/db"
if [[ ! -L "${WGDASH}/src/db" ]] && [[ -d "${WGDASH}/src/db" ]]; then
echo "Removing ${WGDASH}/src/db since its not a symbolic link."
rm -rfv "${WGDASH}/src/db"
fi
if [[ -L "${WGDASH}/src/db" ]]; then
echo "${WGDASH}/src/db is a symbolic link."
else
ln -sv /data/db "${WGDASH}/src/db"
fi
# PERSISTENCE FOR wg-dashboard-oidc-providers.json
if [ ! -f "/data/wg-dashboard-oidc-providers.json" ]; then
echo "Creating wg-dashboard-oidc-providers.json file"
cp -v /tmp/wg-dashboard-oidc-providers.json.template /data/wg-dashboard-oidc-providers.json
fi
if [[ ! -L "${WGDASH}/src/wg-dashboard-oidc-providers.json" ]] && [[ -f "${WGDASH}/src/wg-dashboard-oidc-providers.json" ]]; then
echo "Removing ${WGDASH}/src/wg-dashboard-oidc-providers.json since its not a symbolic link."
rm -fv "${WGDASH}/src/wg-dashboard-oidc-providers.json"
fi
if [[ -L "${WGDASH}/src/wg-dashboard-oidc-providers.json" ]]; then
echo "${WGDASH}/src/wg-dashboard-oidc-providers.json is a symbolic link."
else
ln -sv /data/wg-dashboard-oidc-providers.json "${WGDASH}/src/wg-dashboard-oidc-providers.json"
fi
# PERSISTENCE FOR wg-dashboard.ini
if [ ! -f "${config_file}" ]; then
echo "Creating wg-dashboard.ini file"
touch "${config_file}"
fi
if [ ! -f "${WGDASH}/src/wg-dashboard.ini" ]; then
ln -s "${config_file}" "${WGDASH}/src/wg-dashboard.ini"
if [[ ! -L "${WGDASH}/src/wg-dashboard.ini" ]] && [[ -f "${WGDASH}/src/wg-dashboard.ini" ]]; then
echo "Removing ${WGDASH}/src/wg-dashboard.ini since its not a symbolic link."
rm -fv "${WGDASH}/src/wg-dashboard.ini"
fi
if [[ -L "${WGDASH}/src/wg-dashboard.ini" ]]; then
echo "${WGDASH}/src/wg-dashboard.ini is a symbolic link."
else
ln -sv "${config_file}" "${WGDASH}/src/wg-dashboard.ini"
fi
# Create the Python virtual environment.
. "${WGDASH}/src/venv/bin/activate"
# Use the bash interpreter to install WGDashboard according to the wgd.sh script.
/bin/bash ./wgd.sh install
echo "Looks like the installation succeeded. Moving on."
# Setup WireGuard if needed
if [ ! -f "/etc/wireguard/wg0.conf" ]; then
if [ -z "$(ls -A /etc/wireguard)" ]; then
cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
echo "Setting a secure private key."
local privateKey
privateKey=$(wg genkey)
sed -i "s|^PrivateKey *=.*$|PrivateKey = ${privateKey}|g" /etc/wireguard/wg0.conf
echo "Done setting template."
else
echo "Existing wg0 configuration file found, using that."
@@ -121,51 +164,80 @@ ensure_installation() {
set_envvars() {
printf "\n------------- SETTING ENVIRONMENT VARIABLES ----------------\n"
# Check if config file is empty
if [ ! -s "${config_file}" ]; then
echo "Config file is empty. Creating initial structure."
elif [[ ${dynamic_config,,} =~ ^(false|no)$ ]]; then
echo "Dynamic configuration feature turned off, not changing anything"
return
fi
echo "Checking basic configuration:"
set_ini Peers peer_global_dns "${global_dns}"
if [ -z "${public_ip}" ]; then
public_ip=$(curl -s ifconfig.me)
echo "Automatically detected public IP: ${public_ip}"
public_ip=$(curl -s https://ifconfig.me)
if [ -z "${public_ip}" ]; then
echo "Using fallback public IP resolution website"
public_ip=$(curl -s https://api.ipify.org)
fi
if [ -z "${public_ip}" ]; then
echo "Failed to resolve publicly. Using private address."
public_ip=$(hostname -i)
fi
echo "Automatically detected public IP: ${public_ip}"
fi
set_ini Peers remote_endpoint "${public_ip}"
set_ini Server app_port "${wgd_port}"
# Account settings - process all parameters
[[ -n "$username" ]] && echo "Configuring user account:"
# Basic account variables
[[ -n "$username" ]] && set_ini Account username "${username}"
if [[ -n "$password" ]]; then
echo "- Setting password"
set_ini Account password "$(hash_password "${password}")"
fi
# Additional account variables
[[ -n "$enable_totp" ]] && set_ini Account enable_totp "${enable_totp}"
[[ -n "$totp_verified" ]] && set_ini Account totp_verified "${totp_verified}"
[[ -n "$totp_key" ]] && set_ini Account totp_key "${totp_key}"
# Welcome session
[[ -n "$welcome_session" ]] && set_ini Other welcome_session "${welcome_session}"
# If username and password are set but welcome_session isn't, disable it
if [[ -n "$username" && -n "$password" && -z "$welcome_session" ]]; then
set_ini Other welcome_session "false"
fi
# Autostart WireGuard
if [[ -n "$wg_autostart" ]]; then
echo "Configuring WireGuard autostart:"
set_ini WireGuardConfiguration autostart "${wg_autostart}"
fi
# Database (check if any settings need to be configured)
database_vars=("database_type" "database_host" "database_port" "database_username" "database_password")
for var in "${database_vars[@]}"; do
if [ -n "${!var}" ]; then
echo "Configuring database settings:"
break
fi
done
# Database (iterate through all possible fields)
database_fields=("type:database_type" "host:database_host" "port:database_port"
"username:database_username" "password:database_password")
for field_pair in "${database_fields[@]}"; do
IFS=: read -r field var <<< "$field_pair"
[[ -n "${!var}" ]] && set_ini Database "$field" "${!var}"
done
# Email (check if any settings need to be configured)
email_vars=("email_server" "email_port" "email_encryption" "email_username" "email_password" "email_from" "email_template")
for var in "${email_vars[@]}"; do
@@ -174,12 +246,12 @@ set_envvars() {
break
fi
done
# Email (iterate through all possible fields)
email_fields=("server:email_server" "port:email_port" "encryption:email_encryption"
"username:email_username" "email_password:email_password"
"send_from:email_from" "email_template:email_template")
for field_pair in "${email_fields[@]}"; do
IFS=: read -r field var <<< "$field_pair"
[[ -n "${!var}" ]] && set_ini Email "$field" "${!var}"
@@ -189,36 +261,74 @@ set_envvars() {
# Start service and monitor logs
start_and_monitor() {
printf "\n---------------------- STARTING CORE -----------------------\n"
# Due to resolvconf resetting the DNS we echo back the one we defined (or fallback to default).
resolvconf -u
# Due to some instances complaining about this, making sure its there every time.
mkdir -p /dev/net
mknod /dev/net/tun c 10 200
chmod 600 /dev/net/tun
# Actually starting WGDashboard
echo "Activating Python venv and executing the WireGuard Dashboard service."
bash ./wgd.sh start
echo "Starting WGDashboard directly with Gunicorn..."
[[ ! -d ${WGDASH}/src/log ]] && mkdir ${WGDASH}/src/log
[[ ! -d ${WGDASH}/src/download ]] && mkdir ${WGDASH}/src/download
${WGDASH}/src/venv/bin/gunicorn --config ${WGDASH}/src/gunicorn.conf.py
cp /etc/resolv.conf /etc/resolv.conf.docker
/usr/sbin/resolvconf -u
cat /etc/resolv.conf.docker | resolvconf -a docker.inet
if [ $? -ne 0 ]; then
echo "Loading WGDashboard failed... Look above for details."
fi
# Wait a second before continuing, to give the python program some time to get ready.
sleep 1
echo -e "\nEnsuring container continuation."
# Find and monitor log file
local logdir="${WGDASH}/src/log"
latestErrLog=$(find "$logdir" -name "error_*.log" -type f -print | sort -r | head -n 1)
# Only tail the logs if they are found
if [ -n "$latestErrLog" ]; then
tail -f "$latestErrLog" &
# Wait for the tail process to end.
wait $!
else
echo "No log files found to tail. Something went wrong, exiting..."
local max_rounds="10"
local round="0"
# Hang in there for 10s for Gunicorn to get ready
while true; do
round=$((round + 1))
local latest_error=$(ls -t ${WGDASH}/src/log/error_*.log 2> /dev/null | head -n 1)
if [[ $round -eq $max_rounds ]]; then
echo "Reached breaking point!"
break
fi
if [[ -z $latest_error ]]; then
echo -e "Logs not yet present! Retrying in 1 second!"
sleep 1s
else
break
fi
done
if [[ -z $latest_error ]]; then
echo -e "No error logs founds... Please investigate.\nExiting in 3 minutes..."
sleep 180s
exit 1
else
tail -f "$latest_error" &
tail_pid=$!
wait $tail_pid
fi
}
# Main execution flow
ensure_installation
set_envvars
start_and_monitor
start_and_monitor

View File

@@ -0,0 +1,16 @@
{
"Admin": {
"Provider": {
"client_id": "",
"client_secret": "",
"issuer": ""
}
},
"Client": {
"Provider": {
"client_id": "",
"client_secret": "",
"issuer": ""
}
}
}

8
docker/wg0.conf.template Normal file
View File

@@ -0,0 +1,8 @@
[Interface]
Address = ${wg_net}/24
PrivateKey =
PostUp = iptables -t nat -I POSTROUTING 1 -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE; iptables -I FORWARD -i wg0 -o wg0 -j DROP
PreDown = iptables -t nat -D POSTROUTING -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE; iptables -D FORWARD -i wg0 -o wg0 -j DROP
ListenPort = ${wg_port}
SaveConfig = true
DNS = ${global_dns}

View File

@@ -1,6 +0,0 @@
version: "1.0"
linter: jetbrains/qodana-python:2024.3
profile:
name: qodana.recommended
include:
- name: CheckDependencyLicenses

View File

@@ -4,8 +4,9 @@ from tzlocal import get_localzone
from functools import wraps
from flask import Blueprint, render_template, abort, request, Flask, current_app, session, redirect, url_for
from flask import Blueprint, render_template, abort, request, Flask, current_app, session, redirect, url_for, send_from_directory
import os
import mimetypes
from modules.WireguardConfiguration import WireguardConfiguration
from modules.DashboardConfig import DashboardConfig
@@ -53,6 +54,8 @@ def createClientBlueprint(wireguardConfigurations: dict[WireguardConfiguration],
@client.post(f'{prefix}/api/signup')
def ClientAPI_SignUp():
if not dashboardConfig.GetConfig("Clients", "sign_up")[1]:
abort(404)
data = request.get_json()
status, msg = dashboardClients.SignUp(**data)
return ResponseObject(status, msg)
@@ -192,14 +195,26 @@ def createClientBlueprint(wireguardConfigurations: dict[WireguardConfiguration],
})
return ResponseObject(status, msg)
@client.get(f'{prefix}/assets/<path:filename>')
@client.get(f'{prefix}/img/<path:filename>')
def serve_client_static(filename):
client_dist_folder = os.path.abspath("./static/dist/WGDashboardClient")
mimetype = mimetypes.guess_type(filename)[0]
subfolder = 'assets' if 'assets' in request.path else 'img'
return send_from_directory(os.path.join(client_dist_folder, subfolder), os.path.basename(filename), mimetype=mimetype)
@client.get(prefix)
def ClientIndex():
return render_template('client.html')
app_prefix = dashboardConfig.GetConfig("Server", "app_prefix")[1]
return render_template('client.html', APP_PREFIX=app_prefix)
@client.get(f'{prefix}/api/serverInformation')
def ClientAPI_ServerInformation():
return ResponseObject(data={
"ServerTimezone": str(get_localzone())
"ServerTimezone": str(get_localzone()),
"SignUp": {
"enable": dashboardConfig.GetConfig("Clients", "sign_up")[1]
}
})
@client.get(f'{prefix}/api/validateAuthentication')

View File

@@ -8,7 +8,7 @@ from datetime import datetime, timedelta
import sqlalchemy
from jinja2 import Template
from flask import Flask, request, render_template, session, send_file
from flask import Flask, request, render_template, session, send_file, current_app
from flask_cors import CORS
from icmplib import ping, traceroute
from flask.json.provider import DefaultJSONProvider
@@ -17,8 +17,7 @@ from itertools import islice
from sqlalchemy import RowMapping
from modules.Utilities import (
RegexMatch, StringToBoolean,
ValidateIPAddressesWithRange, ValidateDNSAddress,
RegexMatch, StringToBoolean, ValidateDNSAddress,
GenerateWireguardPublicKey, GenerateWireguardPrivateKey
)
from packaging import version
@@ -30,7 +29,7 @@ from modules.PeerShareLinks import PeerShareLinks
from modules.PeerJobs import PeerJobs
from modules.DashboardConfig import DashboardConfig
from modules.WireguardConfiguration import WireguardConfiguration
from modules.AmneziaWireguardConfiguration import AmneziaWireguardConfiguration
from modules.AmneziaConfiguration import AmneziaConfiguration
from client import createClientBlueprint
@@ -72,7 +71,11 @@ def ResponseObject(status=True, message=None, data=None, status_code = 200) -> F
'''
Flask App
'''
app = Flask("WGDashboard", template_folder=os.path.abspath("./static/dist/WGDashboardAdmin"))
_, APP_PREFIX_INIT = DashboardConfig().GetConfig("Server", "app_prefix")
app = Flask("WGDashboard",
template_folder=os.path.abspath("./static/dist/WGDashboardAdmin"),
static_folder=os.path.abspath("./static/dist/WGDashboardAdmin"),
static_url_path=APP_PREFIX_INIT if APP_PREFIX_INIT else '')
def peerInformationBackgroundThread():
global WireguardConfigurations
@@ -92,9 +95,13 @@ def peerInformationBackgroundThread():
c.getPeersTransfer()
c.getPeersEndpoint()
c.getPeers()
if delay == 6:
c.logPeersTraffic()
c.logPeersHistoryEndpoint()
if DashboardConfig.GetConfig('WireGuardConfiguration', 'peer_tracking')[1] is True:
print("[WGDashboard] Tracking Peers")
if delay == 6:
if c.configurationInfo.PeerTrafficTracking:
c.logPeersTraffic()
if c.configurationInfo.PeerHistoricalEndpointTracking:
c.logPeersHistoryEndpoint()
c.getRestrictedPeersList()
except Exception as e:
app.logger.error(f"[WGDashboard] Background Thread #1 Error", e)
@@ -159,10 +166,10 @@ def InitWireguardConfigurationsList(startup: bool = False):
if i in WireguardConfigurations.keys():
if WireguardConfigurations[i].configurationFileChanged():
with app.app_context():
WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i)
WireguardConfigurations[i] = AmneziaConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i)
else:
with app.app_context():
WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i, startup=startup)
WireguardConfigurations[i] = AmneziaConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i, startup=startup)
except WireguardConfiguration.InvalidConfigurationFileException as e:
app.logger.error(f"{i} have an invalid configuration file.")
@@ -194,7 +201,7 @@ with app.app_context():
DashboardConfig = DashboardConfig()
EmailSender = EmailSender(DashboardConfig)
AllPeerShareLinks: PeerShareLinks = PeerShareLinks(DashboardConfig, WireguardConfigurations)
AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations)
AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations, AllPeerShareLinks)
DashboardLogger: DashboardLogger = DashboardLogger()
DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig)
@@ -222,14 +229,7 @@ def auth_req():
if request.method.lower() == 'options':
return ResponseObject(True)
DashboardConfig.APIAccessed = False
if "api" in request.path:
if str(request.method) == "GET":
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=str(request.args))
elif str(request.method) == "POST":
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Request Args: {str(request.args)} Body:{str(request.get_json())}")
DashboardConfig.APIAccessed = False
authenticationRequired = DashboardConfig.GetConfig("Server", "auth_req")[1]
d = request.headers
if authenticationRequired:
@@ -251,17 +251,28 @@ def auth_req():
DashboardConfig.APIAccessed = True
else:
DashboardConfig.APIAccessed = False
appPrefix = APP_PREFIX if len(APP_PREFIX) > 0 else ''
whiteList = [
'/static/', 'validateAuthentication', 'authenticate', 'getDashboardConfiguration',
'getDashboardTheme', 'getDashboardVersion', 'sharePeer/get', 'isTotpEnabled', 'locale',
'/fileDownload',
'/client'
# f'/static/',
f'{appPrefix}/api/validateAuthentication',
f'{appPrefix}/api/authenticate',
# f'{appPrefix}/api/getDashboardConfiguration',
f'{appPrefix}/api/getDashboardTheme',
f'{appPrefix}/api/getDashboardVersion',
f'{appPrefix}/api/sharePeer/get',
f'{appPrefix}/api/isTotpEnabled',
f'{appPrefix}/api/locale',
]
if (("username" not in session or session.get("role") != "admin")
and (f"{(APP_PREFIX if len(APP_PREFIX) > 0 else '')}/" != request.path
and f"{(APP_PREFIX if len(APP_PREFIX) > 0 else '')}" != request.path)
and len(list(filter(lambda x : x not in request.path, whiteList))) == len(whiteList)
if (
("username" not in session or session.get("role") != "admin")
and (f"{appPrefix}/" != request.path and f"{appPrefix}" != request.path)
and not request.path.startswith(f'{appPrefix}/client')
and not request.path.startswith(f'{appPrefix}/img')
and not request.path.startswith(f'{appPrefix}/json')
and not request.path.startswith(f'{appPrefix}/assets')
and request.path not in whiteList
):
response = Flask.make_response(app, {
"status": False,
@@ -418,11 +429,11 @@ def API_addWireguardConfiguration():
)
WireguardConfigurations[data['ConfigurationName']] = (
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data=data, name=data['ConfigurationName'])) if protocol == 'wg' else (
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data, name=data['ConfigurationName']))
AmneziaConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data, name=data['ConfigurationName']))
else:
WireguardConfigurations[data['ConfigurationName']] = (
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data)) if data.get('Protocol') == 'wg' else (
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data))
AmneziaConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data))
return ResponseObject()
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration')
@@ -519,7 +530,7 @@ def API_renameWireguardConfiguration():
status, message = rc.renameConfiguration(data.get("NewConfigurationName"))
if status:
WireguardConfigurations[data.get("NewConfigurationName")] = (WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName")) if rc.Protocol == 'wg' else AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName")))
WireguardConfigurations[data.get("NewConfigurationName")] = (WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName")) if rc.Protocol == 'wg' else AmneziaConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName")))
else:
WireguardConfigurations[data.get("ConfigurationName")] = rc
return ResponseObject(status, message)
@@ -558,8 +569,8 @@ def API_getAllWireguardConfigurationBackup():
files.sort(key=lambda x: x[1], reverse=True)
for f, ct in files:
if RegexMatch(r"^(.*)_(.*)\.(conf)$", f):
s = re.search(r"^(.*)_(.*)\.(conf)$", f)
if RegexMatch(r"^(.+)_(\d+)\.(conf)$", f):
s = re.search(r"^(.+)_(\d+)\.(conf)$", f)
name = s.group(1)
if name not in existingConfiguration:
if name not in data['NonExistingConfigurations'].keys():
@@ -606,12 +617,19 @@ def API_deleteWireguardConfigurationBackup():
@app.get(f'{APP_PREFIX}/api/downloadWireguardConfigurationBackup')
def API_downloadWireguardConfigurationBackup():
configurationName = request.args.get('configurationName')
backupFileName = request.args.get('backupFileName')
configurationName = os.path.basename(request.args.get('configurationName'))
backupFileName = os.path.basename(request.args.get('backupFileName'))
if configurationName is None or configurationName not in WireguardConfigurations.keys():
return ResponseObject(False, "Configuration does not exist", status_code=404)
status, zip = WireguardConfigurations[configurationName].downloadBackup(backupFileName)
return ResponseObject(status, data=zip, status_code=(200 if status else 404))
if not status:
current_app.logger.error(f"Failed to download a requested backup.\nConfiguration Name: {configurationName}\nBackup File Name: {backupFileName}")
return ResponseObject(False, "Internal server error", status_code=500)
return send_file(os.path.join('download', zip), as_attachment=True)
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
def API_restoreWireguardConfigurationBackup():
@@ -695,15 +713,30 @@ def API_updatePeerSettings(configName):
preshared_key = data['preshared_key']
mtu = data['mtu']
keepalive = data['keepalive']
notes = data.get('notes', '')
wireguardConfig = WireguardConfigurations[configName]
foundPeer, peer = wireguardConfig.searchPeer(id)
if foundPeer:
if wireguardConfig.Protocol == 'wg':
status, msg = peer.updatePeer(name, private_key, preshared_key, dns_addresses,
allowed_ip, endpoint_allowed_ip, mtu, keepalive)
status, msg = peer.updatePeer(name,
private_key,
preshared_key,
dns_addresses,
allowed_ip,
endpoint_allowed_ip,
mtu,
keepalive,
notes)
else:
status, msg = peer.updatePeer(name, private_key, preshared_key, dns_addresses,
allowed_ip, endpoint_allowed_ip, mtu, keepalive, "off")
status, msg = peer.updatePeer(name,
private_key,
preshared_key,
dns_addresses,
allowed_ip,
endpoint_allowed_ip,
mtu,
keepalive,
notes)
wireguardConfig.getPeers()
DashboardWebHooks.RunWebHook('peer_updated', {
"configuration": wireguardConfig.Name,
@@ -853,6 +886,7 @@ def API_addPeers(configName):
mtu: int = data.get('mtu', None)
keep_alive: int = data.get('keepalive', None)
notes: str = data.get('notes', '')
preshared_key: str = data.get('preshared_key', "")
if type(mtu) is not int or mtu < 0 or mtu > 1460:
@@ -908,7 +942,7 @@ def API_addPeers(configName):
"endpoint_allowed_ip": endpoint_allowed_ip,
"mtu": mtu,
"keepalive": keep_alive,
"advanced_security": "off"
"notes": ""
})
if addedCount == bulkAddAmount:
break
@@ -951,8 +985,11 @@ def API_addPeers(configName):
for i in allowed_ips:
found = False
for subnet in availableIps.keys():
network = ipaddress.ip_network(subnet, False)
ap = ipaddress.ip_network(i)
try:
network = ipaddress.ip_network(subnet, False)
ap = ipaddress.ip_network(i)
except ValueError as e:
return ResponseObject(False, str(e))
if network.version == ap.version and ap.subnet_of(network):
found = True
@@ -970,14 +1007,13 @@ def API_addPeers(configName):
"DNS": dns_addresses,
"mtu": mtu,
"keepalive": keep_alive,
"advanced_security": "off"
"notes": notes
}]
)
return ResponseObject(status=status, message=message, data=addedPeers)
except Exception as e:
app.logger.error("Add peers failed", e)
return ResponseObject(False,
f"Add peers failed. Reason: {message}")
return ResponseObject(False, f"Add peers failed.")
return ResponseObject(False, "Configuration does not exist")
@@ -1090,12 +1126,10 @@ def API_GetPeerTraffics():
interval = request.args.get('interval', 30)
startDate = request.args.get('startDate', None)
endDate = request.args.get('endDate', None)
if type(interval) is str:
if not interval.isdigit():
return ResponseObject(False, "Interval must be integers in minutes")
interval = int(interval)
if startDate is None:
endDate = None
else:
@@ -1113,6 +1147,55 @@ def API_GetPeerTraffics():
return ResponseObject(data=p.getTraffics(interval, startDate, endDate))
return ResponseObject(False, "Peer does not exist")
@app.get(f'{APP_PREFIX}/api/getPeerTrackingTableCounts')
def API_GetPeerTrackingTableCounts():
configurationName = request.args.get("configurationName")
if configurationName and configurationName not in WireguardConfigurations.keys():
return ResponseObject(False, "Configuration does not exist")
if configurationName:
c = WireguardConfigurations.get(configurationName)
return ResponseObject(data={
"TrafficTrackingTableSize": c.getTransferTableSize(),
"HistoricalTrackingTableSize": c.getHistoricalEndpointTableSize()
})
d = {}
for i in WireguardConfigurations.keys():
c = WireguardConfigurations.get(i)
d[i] = {
"TrafficTrackingTableSize": c.getTransferTableSize(),
"HistoricalTrackingTableSize": c.getHistoricalEndpointTableSize()
}
return ResponseObject(data=d)
@app.get(f'{APP_PREFIX}/api/downloadPeerTrackingTable')
def API_DownloadPeerTackingTable():
configurationName = request.args.get("configurationName")
table = request.args.get('table')
if configurationName not in WireguardConfigurations.keys():
return ResponseObject(False, "Configuration does not exist")
if table not in ['TrafficTrackingTable', 'HistoricalTrackingTable']:
return ResponseObject(False, "Table does not exist")
c = WireguardConfigurations.get(configurationName)
return ResponseObject(
data=c.downloadTransferTable() if table == 'TrafficTrackingTable'
else c.downloadHistoricalEndpointTable())
@app.post(f'{APP_PREFIX}/api/deletePeerTrackingTable')
def API_DeletePeerTrackingTable():
data = request.get_json()
configurationName = data.get('configurationName')
table = data.get('table')
if not configurationName or configurationName not in WireguardConfigurations.keys():
return ResponseObject(False, "Configuration does not exist")
if not table or table not in ['TrafficTrackingTable', 'HistoricalTrackingTable']:
return ResponseObject(False, "Table does not exist")
c = WireguardConfigurations.get(configurationName)
return ResponseObject(
status=c.deleteTransferTable() if table == 'TrafficTrackingTable'
else c.deleteHistoryEndpointTable())
@app.get(f'{APP_PREFIX}/api/getDashboardTheme')
def API_getDashboardTheme():
return ResponseObject(data=DashboardConfig.GetConfig("Server", "dashboard_theme")[1])
@@ -1176,20 +1259,6 @@ def API_getPeerScheduleJobLogs(configName):
requestAll = True
return ResponseObject(data=AllPeerJobs.getPeerJobLogs(configName))
'''
File Download
'''
@app.get(f'{APP_PREFIX}/fileDownload')
def API_download():
file = request.args.get('file')
if file is None or len(file) == 0:
return ResponseObject(False, "Please specify a file")
if os.path.exists(os.path.join('download', file)):
return send_file(os.path.join('download', file), as_attachment=True)
else:
return ResponseObject(False, "File does not exist")
'''
Tools
'''
@@ -1207,8 +1276,9 @@ def API_ping_getAllPeersIpAddress():
ip = ipaddress.ip_network(x, strict=False)
except ValueError as e:
app.logger.error(f"Failed to parse IP address of {p.id} - {c.Name}")
if len(list(ip.hosts())) == 1:
parsed.append(str(ip.hosts()[0]))
host = list(ip.hosts())
if len(host) == 1:
parsed.append(str(host[0]))
endpoint = p.endpoint.replace(" ", "").replace("(none)", "")
if len(p.name) > 0:
cips[f"{p.name} - {p.id}"] = {
@@ -1291,12 +1361,17 @@ def API_traceroute_execute():
data=json.dumps([x['ip'] for x in result]))
d = r.json()
for i in range(len(result)):
result[i]['geo'] = d[i]
result[i]['geo'] = d[i]
return ResponseObject(data=result)
except Exception as e:
app.logger.error(f"Failed to gather the geolocation data: {e}")
return ResponseObject(data=result, message="Failed to request IP address geolocation")
return ResponseObject(data=result)
except Exception as exp:
return ResponseObject(False, exp)
except Exception as e:
app.logger.error(f"Failed to execute the traceroute: {e}")
return ResponseObject(data=[], message="Failed to traceroute the given parameter")
else:
return ResponseObject(False, "Please provide ipAddress")
@@ -1412,7 +1487,7 @@ def API_Locale_Update():
@app.get(f'{APP_PREFIX}/api/email/ready')
def API_Email_Ready():
return ResponseObject(EmailSender.ready())
return ResponseObject(EmailSender.is_ready())
@app.post(f'{APP_PREFIX}/api/email/send')
def API_Email_Send():
@@ -1666,9 +1741,9 @@ Index Page
@app.get(f'{APP_PREFIX}/')
def index():
return render_template('index.html')
return render_template('index.html', APP_PREFIX=APP_PREFIX)
if __name__ == "__main__":
startThreads()
DashboardPlugins.startThreads()
app.run(host=app_ip, debug=False, port=app_port)
app.run(host=app_ip, debug=False, port=app_port)

View File

@@ -1,4 +1,5 @@
import dashboard
import os
from datetime import datetime
global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger, Dash
app_host, app_port = dashboard.gunicornConfig()
@@ -16,11 +17,11 @@ daemon = True
pidfile = './gunicorn.pid'
wsgi_app = "dashboard:app"
accesslog = f"./log/access_{date}.log"
loglevel = "info"
loglevel = os.environ['log_level'] if 'log_level' in os.environ else 'info'
capture_output = True
errorlog = f"./log/error_{date}.log"
pythonpath = "., ./modules"
print(f"[Gunicorn] WGDashboard w/ Gunicorn will be running on {bind}", flush=True)
print(f"[Gunicorn] Access log file is at {accesslog}", flush=True)
print(f"[Gunicorn] Error log file is at {errorlog}", flush=True)
print(f"[Gunicorn] Error log file is at {errorlog}", flush=True)

View File

@@ -4,28 +4,39 @@ AmneziaWG Configuration
import random, sqlalchemy, os, subprocess, re, uuid
from flask import current_app
from .PeerJobs import PeerJobs
from .AmneziaWGPeer import AmneziaWGPeer
from .AmneziaPeer import AmneziaPeer
from .PeerShareLinks import PeerShareLinks
from .Utilities import RegexMatch
from .Utilities import RegexMatch, CheckAddress, CheckPeerKey
from .WireguardConfiguration import WireguardConfiguration
from .DashboardWebHooks import DashboardWebHooks
class AmneziaWireguardConfiguration(WireguardConfiguration):
def __init__(self, DashboardConfig,
class AmneziaConfiguration(WireguardConfiguration):
def __init__(self,
DashboardConfig,
AllPeerJobs: PeerJobs,
AllPeerShareLinks: PeerShareLinks,
DashboardWebHooks: DashboardWebHooks,
name: str = None, data: dict = None, backup: dict = None, startup: bool = False):
name: str = None,
data: dict = None,
backup: dict = None,
startup: bool = False):
self.Jc = 0
self.Jmin = 0
self.Jmax = 0
self.S1 = 0
self.S2 = 0
self.S3 = 0
self.S4 = 0
self.H1 = 1
self.H2 = 2
self.H3 = 3
self.H4 = 4
self.I1 = "0"
self.I2 = "0"
self.I3 = "0"
self.I4 = "0"
self.I5 = "0"
super().__init__(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, name, data, backup, startup, wg=False)
@@ -58,65 +69,64 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
"Jmax": self.Jmax,
"S1": self.S1,
"S2": self.S2,
"S3": self.S3,
"S4": self.S4,
"H1": self.H1,
"H2": self.H2,
"H3": self.H3,
"H4": self.H4
"H4": self.H4,
"I1": self.I1,
"I2": self.I2,
"I3": self.I3,
"I4": self.I4,
"I5": self.I5
}
def createDatabase(self, dbName = None):
def generate_column_obj():
return [
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False, primary_key=True),
sqlalchemy.Column('private_key', sqlalchemy.String(255)),
sqlalchemy.Column('DNS', sqlalchemy.Text),
sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text),
sqlalchemy.Column('name', sqlalchemy.Text),
sqlalchemy.Column('total_receive', sqlalchemy.Float),
sqlalchemy.Column('total_sent', sqlalchemy.Float),
sqlalchemy.Column('total_data', sqlalchemy.Float),
sqlalchemy.Column('endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('latest_handshake', sqlalchemy.String(255)),
sqlalchemy.Column('allowed_ip', sqlalchemy.String(255)),
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('mtu', sqlalchemy.Integer),
sqlalchemy.Column('keepalive', sqlalchemy.Integer),
sqlalchemy.Column('notes', sqlalchemy.Text),
sqlalchemy.Column('remote_endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('preshared_key', sqlalchemy.String(255))
]
if dbName is None:
dbName = self.Name
self.peersTable = sqlalchemy.Table(
dbName, self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False, primary_key=True),
sqlalchemy.Column('private_key', sqlalchemy.String(255)),
sqlalchemy.Column('DNS', sqlalchemy.Text),
sqlalchemy.Column('advanced_security', sqlalchemy.String(255)),
sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text),
sqlalchemy.Column('name', sqlalchemy.Text),
sqlalchemy.Column('total_receive', sqlalchemy.Float),
sqlalchemy.Column('total_sent', sqlalchemy.Float),
sqlalchemy.Column('total_data', sqlalchemy.Float),
sqlalchemy.Column('endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('latest_handshake', sqlalchemy.String(255)),
sqlalchemy.Column('allowed_ip', sqlalchemy.String(255)),
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('mtu', sqlalchemy.Integer),
sqlalchemy.Column('keepalive', sqlalchemy.Integer),
sqlalchemy.Column('remote_endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('preshared_key', sqlalchemy.String(255)),
extend_existing=True
f'{dbName}', self.metadata, *generate_column_obj(), extend_existing=True
)
self.peersRestrictedTable = sqlalchemy.Table(
f'{dbName}_restrict_access', self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False, primary_key=True),
sqlalchemy.Column('private_key', sqlalchemy.String(255)),
sqlalchemy.Column('DNS', sqlalchemy.Text),
sqlalchemy.Column('advanced_security', sqlalchemy.String(255)),
sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text),
sqlalchemy.Column('name', sqlalchemy.Text),
sqlalchemy.Column('total_receive', sqlalchemy.Float),
sqlalchemy.Column('total_sent', sqlalchemy.Float),
sqlalchemy.Column('total_data', sqlalchemy.Float),
sqlalchemy.Column('endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('latest_handshake', sqlalchemy.String(255)),
sqlalchemy.Column('allowed_ip', sqlalchemy.String(255)),
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('mtu', sqlalchemy.Integer),
sqlalchemy.Column('keepalive', sqlalchemy.Integer),
sqlalchemy.Column('remote_endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('preshared_key', sqlalchemy.String(255)),
extend_existing=True
f'{dbName}_restrict_access', self.metadata, *generate_column_obj(), extend_existing=True
)
self.peersDeletedTable = sqlalchemy.Table(
f'{dbName}_deleted', self.metadata, *generate_column_obj(), extend_existing=True
)
if self.DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite':
time_col_type = sqlalchemy.DATETIME
else:
time_col_type = sqlalchemy.TIMESTAMP
self.peersTransferTable = sqlalchemy.Table(
f'{dbName}_transfer', self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False),
@@ -126,38 +136,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('time', (sqlalchemy.DATETIME if self.DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite' else sqlalchemy.TIMESTAMP),
server_default=sqlalchemy.func.now()),
extend_existing=True
)
self.peersDeletedTable = sqlalchemy.Table(
f'{dbName}_deleted', self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False),
sqlalchemy.Column('private_key', sqlalchemy.String(255)),
sqlalchemy.Column('DNS', sqlalchemy.Text),
sqlalchemy.Column('advanced_security', sqlalchemy.String(255)),
sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text),
sqlalchemy.Column('name', sqlalchemy.Text),
sqlalchemy.Column('total_receive', sqlalchemy.Float),
sqlalchemy.Column('total_sent', sqlalchemy.Float),
sqlalchemy.Column('total_data', sqlalchemy.Float),
sqlalchemy.Column('endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('latest_handshake', sqlalchemy.String(255)),
sqlalchemy.Column('allowed_ip', sqlalchemy.String(255)),
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('mtu', sqlalchemy.Integer),
sqlalchemy.Column('keepalive', sqlalchemy.Integer),
sqlalchemy.Column('remote_endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('preshared_key', sqlalchemy.String(255)),
extend_existing=True
)
self.infoTable = sqlalchemy.Table(
'ConfigurationsInfo', self.metadata,
sqlalchemy.Column('ID', sqlalchemy.String(255), primary_key=True),
sqlalchemy.Column('Info', sqlalchemy.Text),
sqlalchemy.Column('time', time_col_type, server_default=sqlalchemy.func.now()),
extend_existing=True
)
@@ -165,8 +144,13 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
f'{dbName}_history_endpoint', self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False),
sqlalchemy.Column('endpoint', sqlalchemy.String(255), nullable=False),
sqlalchemy.Column('time',
(sqlalchemy.DATETIME if self.DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite' else sqlalchemy.TIMESTAMP)),
sqlalchemy.Column('time', time_col_type)
)
self.infoTable = sqlalchemy.Table(
'ConfigurationsInfo', self.metadata,
sqlalchemy.Column('ID', sqlalchemy.String(255), primary_key=True),
sqlalchemy.Column('Info', sqlalchemy.Text),
extend_existing=True
)
@@ -174,8 +158,6 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
def getPeers(self):
self.Peers.clear()
current_app.logger.info(f"Refreshing {self.Name} peer list")
if self.configurationFileChanged():
with open(self.configPath, 'r') as configFile:
p = []
@@ -213,11 +195,9 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
if tempPeer is None:
tempPeer = {
"id": i['PublicKey'],
"advanced_security": i.get('AdvancedSecurity', 'off'),
"private_key": "",
"DNS": self.DashboardConfig.GetConfig("Peers", "peer_global_DNS")[1],
"endpoint_allowed_ip": self.DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[
1],
"endpoint_allowed_ip": self.DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[1],
"name": i.get("name"),
"total_receive": 0,
"total_sent": 0,
@@ -231,6 +211,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
"cumu_data": 0,
"mtu": self.DashboardConfig.GetConfig("Peers", "peer_mtu")[1],
"keepalive": self.DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1],
"notes": "",
"remote_endpoint": self.DashboardConfig.GetConfig("Peers", "remote_endpoint")[1],
"preshared_key": i["PresharedKey"] if "PresharedKey" in i.keys() else ""
}
@@ -245,14 +226,14 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
self.peersTable.columns.id == i['PublicKey']
)
)
self.Peers.append(AmneziaWGPeer(tempPeer, self))
self.Peers.append(AmneziaPeer(tempPeer, self))
except Exception as e:
current_app.logger.error(f"{self.Name} getPeers() Error", e)
else:
with self.engine.connect() as conn:
existingPeers = conn.execute(self.peersTable.select()).mappings().fetchall()
for i in existingPeers:
self.Peers.append(AmneziaWGPeer(i, self))
self.Peers.append(AmneziaPeer(i, self))
def addPeers(self, peers: list) -> tuple[bool, list, str]:
result = {
@@ -260,6 +241,15 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
"peers": []
}
try:
cleanedAllowedIPs = {}
for p in peers:
newAllowedIPs = p['allowed_ip'].replace(" ", "")
if not CheckAddress(newAllowedIPs):
return False, [], "Allowed IPs entry format is incorrect"
if not CheckPeerKey(p["id"]):
return False, [], "Peer key format is incorrect"
cleanedAllowedIPs[p["id"]] = newAllowedIPs
with self.engine.begin() as conn:
for i in peers:
newPeer = {
@@ -280,9 +270,9 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
"cumu_data": 0,
"mtu": i['mtu'],
"keepalive": i['keepalive'],
"notes": i.get('notes', ''),
"remote_endpoint": self.DashboardConfig.GetConfig("Peers", "remote_endpoint")[1],
"preshared_key": i["preshared_key"],
"advanced_security": i['advanced_security']
"preshared_key": i["preshared_key"]
}
conn.execute(
self.peersTable.insert().values(newPeer)
@@ -295,13 +285,15 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
with open(uid, "w+") as f:
f.write(p['preshared_key'])
subprocess.check_output(
f"{self.Protocol} set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
shell=True, stderr=subprocess.STDOUT)
command = [self.Protocol, "set", self.Name, "peer", p['id'], "allowed-ips", cleanedAllowedIPs[p["id"]], "preshared-key", uid if presharedKeyExist else "/dev/null"]
subprocess.check_output(command, stderr=subprocess.STDOUT)
if presharedKeyExist:
os.remove(uid)
subprocess.check_output(
f"{self.Protocol}-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
command = [f"{self.Protocol}-quick", "save", self.Name]
subprocess.check_output(command, stderr=subprocess.STDOUT)
self.getPeers()
for p in peers:
p = self.searchPeer(p['id'])
@@ -313,7 +305,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
})
except Exception as e:
current_app.logger.error("Add peers error", e)
return False, [], str(e)
return False, [], "Internal server error"
return True, result['peers'], ""
def getRestrictedPeers(self):
@@ -321,4 +313,4 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
with self.engine.connect() as conn:
restricted = conn.execute(self.peersRestrictedTable.select()).mappings().fetchall()
for i in restricted:
self.RestrictedPeers.append(AmneziaWGPeer(i, self))
self.RestrictedPeers.append(AmneziaPeer(i, self))

120
src/modules/AmneziaPeer.py Normal file
View File

@@ -0,0 +1,120 @@
import os
from flask import current_app
import random
import re
import subprocess
import uuid
from flask import current_app
from .Peer import Peer
from .Utilities import CheckAddress, ValidateDNSAddress, GenerateWireguardPublicKey
class AmneziaPeer(Peer):
def __init__(self, tableData, configuration):
super().__init__(tableData, configuration)
def updatePeer(self, name: str, private_key: str,
preshared_key: str,
dns_addresses: str,
allowed_ip: str,
endpoint_allowed_ip: str,
mtu: int,
keepalive: int,
notes: str
) -> tuple[bool, str | None]:
if not self.configuration.getStatus():
self.configuration.toggleConfiguration()
# Before we do any compute, let us check if the given endpoint allowed ip is valid at all
if not CheckAddress(endpoint_allowed_ip):
return False, f"Endpoint Allowed IPs format is incorrect"
peers = []
for peer in self.configuration.getPeersList():
# Make sure to exclude your own data when updating since its not really relevant
if peer.id != self.id:
continue
peers.append(peer)
used_allowed_ips = []
for peer in peers:
ips = peer.allowed_ip.split(',')
ips = [ip.strip() for ip in ips]
used_allowed_ips.append(ips)
if allowed_ip in used_allowed_ips:
return False, "Allowed IP already taken by another peer"
if not ValidateDNSAddress(dns_addresses):
return False, f"DNS IP-Address or FQDN is incorrect"
if isinstance(mtu, str):
mtu = 0
if isinstance(keepalive, str):
keepalive = 0
if mtu not in range(0, 1461):
return False, "MTU format is not correct"
if keepalive < 0:
return False, "Persistent Keepalive format is not correct"
if len(private_key) > 0:
pubKey = GenerateWireguardPublicKey(private_key)
if not pubKey[0] or pubKey[1] != self.id:
return False, "Private key does not match with the public key"
try:
rand = random.Random()
uid = str(uuid.UUID(int=rand.getrandbits(128), version=4))
psk_exist = len(preshared_key) > 0
if psk_exist:
with open(uid, "w+") as f:
f.write(preshared_key)
newAllowedIPs = allowed_ip.replace(" ", "")
if not CheckAddress(newAllowedIPs):
return False, "Allowed IPs entry format is incorrect"
command = [self.configuration.Protocol, "set", self.configuration.Name, "peer", self.id, "allowed-ips", newAllowedIPs, "preshared-key", uid if psk_exist else "/dev/null"]
updateAllowedIp = subprocess.check_output(command, stderr=subprocess.STDOUT)
if psk_exist: os.remove(uid)
if len(updateAllowedIp.decode().strip("\n")) != 0:
current_app.logger.error(f"Update peer failed when updating Allowed IPs.\nInput: {newAllowedIPs}\nOutput: {updateAllowedIp.decode().strip('\n')}")
return False, "Internal server error"
command = [f"{self.configuration.Protocol}-quick", "save", self.configuration.Name]
saveConfig = subprocess.check_output(command, stderr=subprocess.STDOUT)
if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'):
current_app.logger.error("Update peer failed when saving the configuration")
return False, "Internal server error"
with self.configuration.engine.begin() as conn:
conn.execute(
self.configuration.peersTable.update().values({
"name": name,
"private_key": private_key,
"DNS": dns_addresses,
"endpoint_allowed_ip": endpoint_allowed_ip,
"mtu": mtu,
"keepalive": keepalive,
"notes": notes,
"preshared_key": preshared_key
}).where(
self.configuration.peersTable.c.id == self.id
)
)
self.configuration.getPeers()
return True, None
except subprocess.CalledProcessError as exc:
current_app.logger.error(f"Subprocess call failed:\n{exc.output.decode('UTF-8')}")
return False, "Internal server error"

View File

@@ -1,92 +0,0 @@
import os
import random
import re
import subprocess
import uuid
from .Peer import Peer
from .Utilities import ValidateIPAddressesWithRange, ValidateDNSAddress, GenerateWireguardPublicKey
class AmneziaWGPeer(Peer):
def __init__(self, tableData, configuration):
self.advanced_security = tableData["advanced_security"]
super().__init__(tableData, configuration)
def updatePeer(self, name: str, private_key: str,
preshared_key: str,
dns_addresses: str, allowed_ip: str, endpoint_allowed_ip: str, mtu: int,
keepalive: int, advanced_security: str) -> tuple[bool, str] or tuple[bool, None]:
if not self.configuration.getStatus():
self.configuration.toggleConfiguration()
existingAllowedIps = [item for row in list(
map(lambda x: [q.strip() for q in x.split(',')],
map(lambda y: y.allowed_ip,
list(filter(lambda k: k.id != self.id, self.configuration.getPeersList()))))) for item in row]
if allowed_ip in existingAllowedIps:
return False, "Allowed IP already taken by another peer"
if not ValidateIPAddressesWithRange(endpoint_allowed_ip):
return False, f"Endpoint Allowed IPs format is incorrect"
if len(dns_addresses) > 0 and not ValidateDNSAddress(dns_addresses):
return False, f"DNS format is incorrect"
if type(mtu) is str:
mtu = 0
if type(keepalive) is str:
keepalive = 0
if mtu < 0 or mtu > 1460:
return False, "MTU format is not correct"
if keepalive < 0:
return False, "Persistent Keepalive format is not correct"
if advanced_security != "on" and advanced_security != "off":
return False, "Advanced Security can only be on or off"
if len(private_key) > 0:
pubKey = GenerateWireguardPublicKey(private_key)
if not pubKey[0] or pubKey[1] != self.id:
return False, "Private key does not match with the public key"
try:
rd = random.Random()
uid = str(uuid.UUID(int=rd.getrandbits(128), version=4))
pskExist = len(preshared_key) > 0
if pskExist:
with open(uid, "w+") as f:
f.write(preshared_key)
newAllowedIPs = allowed_ip.replace(" ", "")
updateAllowedIp = subprocess.check_output(
f"{self.configuration.Protocol} set {self.configuration.Name} peer {self.id} allowed-ips {newAllowedIPs} {f'preshared-key {uid}' if pskExist else 'preshared-key /dev/null'}",
shell=True, stderr=subprocess.STDOUT)
if pskExist: os.remove(uid)
if len(updateAllowedIp.decode().strip("\n")) != 0:
return False, "Update peer failed when updating Allowed IPs"
saveConfig = subprocess.check_output(f"{self.configuration.Protocol}-quick save {self.configuration.Name}",
shell=True, stderr=subprocess.STDOUT)
if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'):
return False, "Update peer failed when saving the configuration"
with self.configuration.engine.begin() as conn:
conn.execute(
self.configuration.peersTable.update().values({
"name": name,
"private_key": private_key,
"DNS": dns_addresses,
"endpoint_allowed_ip": endpoint_allowed_ip,
"mtu": mtu,
"keepalive": keepalive,
"preshared_key": preshared_key,
"advanced_security": advanced_security
}).where(
self.configuration.peersTable.c.id == self.id
)
)
self.configuration.getPeers()
return True, None
except subprocess.CalledProcessError as exc:
return False, exc.output.decode("UTF-8").strip()

View File

@@ -8,7 +8,7 @@ import pyotp
import sqlalchemy as db
import requests
from .ConnectionString import ConnectionString
from .DatabaseConnection import ConnectionString
from .DashboardClientsPeerAssignment import DashboardClientsPeerAssignment
from .DashboardClientsTOTP import DashboardClientsTOTP
from .DashboardOIDC import DashboardOIDC

View File

@@ -1,7 +1,7 @@
import datetime
import uuid
from .ConnectionString import ConnectionString
from .DatabaseConnection import ConnectionString
from .DashboardLogger import DashboardLogger
import sqlalchemy as db
from .WireguardConfiguration import WireguardConfiguration

View File

@@ -3,7 +3,7 @@ import hashlib
import uuid
import sqlalchemy as db
from .ConnectionString import ConnectionString
from .DatabaseConnection import ConnectionString
class DashboardClientsTOTP:

View File

@@ -7,19 +7,15 @@ import sqlalchemy as db
from datetime import datetime
from typing import Any
from flask import current_app
from .ConnectionString import ConnectionString
from .Utilities import (
GetRemoteEndpoint, ValidateDNSAddress
)
from .DatabaseConnection import ConnectionString
from .Utilities import (GetRemoteEndpoint, ValidateDNSAddress)
from .DashboardAPIKey import DashboardAPIKey
class DashboardConfig:
DashboardVersion = 'v4.3.0.1'
DashboardVersion = 'v4.3.3'
ConfigurationPath = os.getenv('CONFIGURATION_PATH', '.')
ConfigurationFilePath = os.path.join(ConfigurationPath, 'wg-dashboard.ini')
def __init__(self):
if not os.path.exists(DashboardConfig.ConfigurationFilePath):
open(DashboardConfig.ConfigurationFilePath, "x")
@@ -83,9 +79,11 @@ class DashboardConfig:
},
"Clients": {
"enable": "true",
"sign_up": "true"
},
"WireGuardConfiguration": {
"autostart": ""
"autostart": "",
"peer_tracking": "false"
}
}
@@ -102,6 +100,54 @@ class DashboardConfig:
self.APIAccessed = False
self.SetConfig("Server", "version", DashboardConfig.DashboardVersion)
def EnsureDatabaseIntegrity(self, wireguardConfigurations):
expected_columns = {
'id': db.String(255),
'private_key': db.String(255),
'DNS': db.Text,
'endpoint_allowed_ip': db.Text,
'name': db.Text,
'total_receive': db.Float,
'total_sent': db.Float,
'total_data': db.Float,
'endpoint': db.String(255),
'status': db.String(255),
'latest_handshake': db.String(255),
'allowed_ip': db.String(255),
'cumu_receive': db.Float,
'cumu_sent': db.Float,
'cumu_data': db.Float,
'mtu': db.Integer,
'keepalive': db.Integer,
'notes': db.Text,
'remote_endpoint': db.String(255),
'preshared_key': db.String(255)
}
inspector = db.inspect(self.engine)
with self.engine.begin() as conn:
for cfg_name, cfg_obj in wireguardConfigurations.items():
tables_to_check = [
cfg_name,
f'{cfg_name}_restrict_access',
f'{cfg_name}_deleted'
]
for table_name in tables_to_check:
if not table_name:
continue
if not inspector.has_table(table_name):
continue
existing_columns = [c['name'] for c in inspector.get_columns(table_name)]
for col_name, col_type in expected_columns.items():
if col_name not in existing_columns:
type_str = col_type().compile(dialect=self.engine.dialect)
current_app.logger.info(f"Adding missing column '{col_name}' to table '{table_name}'")
conn.execute(db.text(f'ALTER TABLE "{table_name}" ADD COLUMN "{col_name}" {type_str}'))
def getConnectionString(self, database) -> str or None:
sqlitePath = os.path.join(DashboardConfig.ConfigurationPath, "db")
@@ -116,7 +162,7 @@ class DashboardConfig:
cn = f'sqlite:///{os.path.join(sqlitePath, f"{database}.db")}'
if not database_exists(cn):
create_database(cn)
return cn
return cn
def __createAPIKeyTable(self):
self.apiKeyTable = db.Table('DashboardAPIKeys', self.dbMetadata,

View File

@@ -4,7 +4,7 @@ Dashboard Logger Class
import uuid
import sqlalchemy as db
from flask import current_app
from .ConnectionString import ConnectionString
from .DatabaseConnection import ConnectionString
class DashboardLogger:

View File

@@ -8,7 +8,7 @@ from datetime import datetime, timedelta
import requests
from pydantic import BaseModel, field_serializer
import sqlalchemy as db
from .ConnectionString import ConnectionString
from .DatabaseConnection import ConnectionString
from flask import current_app
WebHookActions = ['peer_created', 'peer_deleted', 'peer_updated']

View File

@@ -1,14 +1,15 @@
import configparser
import os
from sqlalchemy_utils import database_exists, create_database
from flask import current_app
def ConnectionString(database) -> str:
parser = configparser.ConfigParser(strict=False)
parser.read_file(open('wg-dashboard.ini', "r+"))
sqlitePath = os.path.join("db")
if not os.path.isdir(sqlitePath):
os.mkdir(sqlitePath)
if parser.get("Database", "type") == "postgresql":
cn = f'postgresql+psycopg://{parser.get("Database", "username")}:{parser.get("Database", "password")}@{parser.get("Database", "host")}/{database}'
elif parser.get("Database", "type") == "mysql":
@@ -19,7 +20,6 @@ def ConnectionString(database) -> str:
if not database_exists(cn):
create_database(cn)
except Exception as e:
current_app.logger.error("Database error. Terminating...", e)
exit(1)
return cn

View File

@@ -1,76 +1,101 @@
import os.path
import ssl
import smtplib
# Email libaries
from email import encoders
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr
from email.utils import formatdate
class EmailSender:
def __init__(self, DashboardConfig):
self.smtp = None
self.DashboardConfig = DashboardConfig
if not os.path.exists('./attachments'):
os.mkdir('./attachments')
def Server(self):
return self.DashboardConfig.GetConfig("Email", "server")[1]
def Port(self):
return self.DashboardConfig.GetConfig("Email", "port")[1]
def Encryption(self):
return self.DashboardConfig.GetConfig("Email", "encryption")[1]
def Username(self):
return self.DashboardConfig.GetConfig("Email", "username")[1]
def Password(self):
return self.DashboardConfig.GetConfig("Email", "email_password")[1]
def SendFrom(self):
return self.DashboardConfig.GetConfig("Email", "send_from")[1]
# Thank you, @gdeeble from GitHub
def AuthenticationRequired(self):
return self.DashboardConfig.GetConfig("Email", "authentication_required")[1]
def ready(self):
if self.AuthenticationRequired():
return all([self.Server(), self.Port(), self.Encryption(), self.Username(), self.Password(), self.SendFrom()])
return all([self.Server(), self.Port(), self.Encryption(), self.SendFrom()])
self.refresh_vals()
def send(self, receiver, subject, body, includeAttachment = False, attachmentName = "") -> tuple[bool, str] | tuple[bool, None]:
if self.ready():
try:
self.smtp = smtplib.SMTP(self.Server(), port=int(self.Port()))
self.smtp.ehlo()
if self.Encryption() == "STARTTLS":
self.smtp.starttls()
if self.AuthenticationRequired():
self.smtp.login(self.Username(), self.Password())
message = MIMEMultipart()
message['Subject'] = subject
message['From'] = self.SendFrom()
message["To"] = receiver
message.attach(MIMEText(body, "plain"))
def refresh_vals(self) -> None:
self.Server = self.DashboardConfig.GetConfig("Email", "server")[1]
self.Port = self.DashboardConfig.GetConfig("Email", "port")[1]
if includeAttachment and len(attachmentName) > 0:
attachmentPath = os.path.join('./attachments', attachmentName)
if os.path.exists(attachmentPath):
attachment = MIMEBase("application", "octet-stream")
with open(os.path.join('./attachments', attachmentName), 'rb') as f:
attachment.set_payload(f.read())
encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition", f"attachment; filename= {attachmentName}",)
message.attach(attachment)
else:
self.smtp.close()
return False, "Attachment does not exist"
self.smtp.sendmail(self.SendFrom(), receiver, message.as_string())
self.smtp.close()
return True, None
except Exception as e:
return False, f"Send failed | Reason: {e}"
return False, "SMTP not configured"
self.Encryption = self.DashboardConfig.GetConfig("Email", "encryption")[1]
self.AuthRequired = self.DashboardConfig.GetConfig("Email", "authentication_required")[1]
self.Username = self.DashboardConfig.GetConfig("Email", "username")[1]
self.Password = self.DashboardConfig.GetConfig("Email", "email_password")[1]
self.SendFrom = self.DashboardConfig.GetConfig("Email", "send_from")[1]
def is_ready(self) -> bool:
self.refresh_vals()
if self.AuthRequired:
ready = all([
self.Server, self.Port, self.Encryption,
self.Username, self.Password, self.SendFrom
])
else:
ready = all([
self.Server, self.Port, self.Encryption, self.SendFrom
])
return ready
def send(self, receiver, subject, body, includeAttachment: bool = False, attachmentName: str = "") -> tuple[bool, str | None]:
if not self.is_ready():
return False, "SMTP not configured"
message = MIMEMultipart()
message['Subject'] = subject
message['From'] = self.SendFrom
message["To"] = receiver
message["Date"] = formatdate(localtime=True)
message.attach(MIMEText(body, "plain"))
if includeAttachment and len(attachmentName) > 0:
attachmentPath = os.path.join('./attachments', attachmentName)
if not os.path.exists(attachmentPath):
return False, "Attachment does not exist"
attachment = MIMEBase("application", "octet-stream")
with open(os.path.join('./attachments', attachmentName), 'rb') as f:
attachment.set_payload(f.read())
encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition", f"attachment; filename= {attachmentName}",)
message.attach(attachment)
smtp = None
try:
context = ssl.create_default_context()
if self.Encryption == "IMPLICITTLS":
smtp = smtplib.SMTP_SSL(self.Server, port=int(self.Port), context=context)
else:
smtp = smtplib.SMTP(self.Server, port=int(self.Port))
smtp.ehlo()
# Configure SMTP encryption type
if self.Encryption == "STARTTLS":
smtp.starttls(context=context)
smtp.ehlo()
# Log into the SMTP server if required
if self.AuthRequired:
smtp.login(self.Username, self.Password)
# Send the actual email from the SMTP object
smtp.sendmail(self.SendFrom, receiver, message.as_string())
return True, None
except Exception as e:
return False, f"Send failed | Reason: {e}"
finally:
if smtp:
try:
smtp.quit()
except Exception:
pass

View File

@@ -2,7 +2,7 @@ import uuid
from pydantic import BaseModel, field_serializer
import sqlalchemy as db
from .ConnectionString import ConnectionString
from .DatabaseConnection import ConnectionString
class NewConfigurationTemplate(BaseModel):

View File

@@ -10,8 +10,9 @@ from datetime import timedelta
import jinja2
import sqlalchemy as db
from .PeerJob import PeerJob
from flask import current_app
from .PeerShareLink import PeerShareLink
from .Utilities import GenerateWireguardPublicKey, ValidateIPAddressesWithRange, ValidateDNSAddress
from .Utilities import GenerateWireguardPublicKey, CheckAddress, ValidateDNSAddress
class Peer:
@@ -34,6 +35,7 @@ class Peer:
self.cumu_data = tableData["cumu_data"]
self.mtu = tableData["mtu"]
self.keepalive = tableData["keepalive"]
self.notes = tableData.get("notes", "")
self.remote_endpoint = tableData["remote_endpoint"]
self.preshared_key = tableData["preshared_key"]
self.jobs: list[PeerJob] = []
@@ -49,62 +51,89 @@ class Peer:
def __repr__(self):
return str(self.toJson())
def updatePeer(self, name: str, private_key: str,
def updatePeer(self, name: str,
private_key: str,
preshared_key: str,
dns_addresses: str, allowed_ip: str, endpoint_allowed_ip: str, mtu: int,
keepalive: int) -> tuple[bool, str] or tuple[bool, None]:
dns_addresses: str,
allowed_ip: str,
endpoint_allowed_ip: str,
mtu: int,
keepalive: int,
notes: str
) -> tuple[bool, str | None]:
if not self.configuration.getStatus():
self.configuration.toggleConfiguration()
existingAllowedIps = [item for row in list(
map(lambda x: [q.strip() for q in x.split(',')],
map(lambda y: y.allowed_ip,
list(filter(lambda k: k.id != self.id, self.configuration.getPeersList()))))) for item in row]
if allowed_ip in existingAllowedIps:
return False, "Allowed IP already taken by another peer"
if not ValidateIPAddressesWithRange(endpoint_allowed_ip):
# Before we do any compute, let us check if the given endpoint allowed ip is valid at all
if not CheckAddress(endpoint_allowed_ip):
return False, f"Endpoint Allowed IPs format is incorrect"
if len(dns_addresses) > 0 and not ValidateDNSAddress(dns_addresses):
return False, f"DNS format is incorrect"
if type(mtu) is str or mtu is None:
peers = []
for peer in self.configuration.getPeersList():
# Make sure to exclude your own data when updating since its not really relevant
if peer.id == self.id:
continue
peers.append(peer)
used_allowed_ips = []
for peer in peers:
ips = peer.allowed_ip.split(',')
ips = [ip.strip() for ip in ips]
used_allowed_ips.append(ips)
if allowed_ip in used_allowed_ips:
return False, "Allowed IP already taken by another peer"
if not ValidateDNSAddress(dns_addresses):
return False, f"DNS IP-Address or FQDN is incorrect"
if isinstance(mtu, str):
mtu = 0
if mtu < 0 or mtu > 1460:
return False, "MTU format is not correct"
if type(keepalive) is str or keepalive is None:
if isinstance(keepalive, str):
keepalive = 0
if mtu not in range(0, 1461):
return False, "MTU format is not correct"
if keepalive < 0:
return False, "Persistent Keepalive format is not correct"
if len(private_key) > 0:
pubKey = GenerateWireguardPublicKey(private_key)
if not pubKey[0] or pubKey[1] != self.id:
return False, "Private key does not match with the public key"
try:
rd = random.Random()
uid = str(uuid.UUID(int=rd.getrandbits(128), version=4))
pskExist = len(preshared_key) > 0
if pskExist:
try:
rand = random.Random()
uid = str(uuid.UUID(int=rand.getrandbits(128), version=4))
psk_exist = len(preshared_key) > 0
if psk_exist:
with open(uid, "w+") as f:
f.write(preshared_key)
newAllowedIPs = allowed_ip.replace(" ", "")
updateAllowedIp = subprocess.check_output(
f"{self.configuration.Protocol} set {self.configuration.Name} peer {self.id} allowed-ips {newAllowedIPs} {f'preshared-key {uid}' if pskExist else 'preshared-key /dev/null'}",
shell=True, stderr=subprocess.STDOUT)
if pskExist: os.remove(uid)
newAllowedIPs = allowed_ip.replace(" ", "")
if not CheckAddress(newAllowedIPs):
return False, "Allowed IPs entry format is incorrect"
command = [self.configuration.Protocol, "set", self.configuration.Name, "peer", self.id, "allowed-ips", newAllowedIPs, "preshared-key", uid if psk_exist else "/dev/null"]
updateAllowedIp = subprocess.check_output(command, stderr=subprocess.STDOUT)
if psk_exist: os.remove(uid)
if len(updateAllowedIp.decode().strip("\n")) != 0:
return False, "Update peer failed when updating Allowed IPs"
saveConfig = subprocess.check_output(f"{self.configuration.Protocol}-quick save {self.configuration.Name}",
shell=True, stderr=subprocess.STDOUT)
current_app.logger.error("Update peer failed when updating Allowed IPs")
return False, "Internal server error"
command = [f"{self.configuration.Protocol}-quick", "save", self.configuration.Name]
saveConfig = subprocess.check_output(command, stderr=subprocess.STDOUT)
if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'):
return False, "Update peer failed when saving the configuration"
current_app.logger.error("Update peer failed when saving the configuration")
return False, "Internal server error"
with self.configuration.engine.begin() as conn:
conn.execute(
self.configuration.peersTable.update().values({
@@ -114,6 +143,7 @@ class Peer:
"endpoint_allowed_ip": endpoint_allowed_ip,
"mtu": mtu,
"keepalive": keepalive,
"notes": notes,
"preshared_key": preshared_key
}).where(
self.configuration.peersTable.c.id == self.id
@@ -121,7 +151,8 @@ class Peer:
)
return True, None
except subprocess.CalledProcessError as exc:
return False, exc.output.decode("UTF-8").strip()
current_app.logger.error(f"Subprocess call failed:\n{exc.output.decode('UTF-8')}")
return False, "Internal server error"
def downloadPeer(self) -> dict[str, str]:
final = {
@@ -132,17 +163,19 @@ class Peer:
if len(filename) == 0:
filename = "UntitledPeer"
filename = "".join(filename.split(' '))
filename = f"{filename}"
illegal_filename = [".", ",", "/", "?", "<", ">", "\\", ":", "*", '|' '\"', "com1", "com2", "com3",
"com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4",
"lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "con", "nul", "prn"]
for i in illegal_filename:
filename = filename.replace(i, "")
# use previous filtering code if code below is insufficient or faulty
filename = re.sub(r'[.,/?<>\\:*|"]', '', filename).rstrip(". ") # remove special characters
reserved_pattern = r"^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\..*)?$" # match com1-9, lpt1-9, con, nul, prn, aux, nul
if re.match(reserved_pattern, filename, re.IGNORECASE):
filename = f"file_{filename}" # prepend "file_" if it matches
for i in filename:
if re.match("^[a-zA-Z0-9_=+.-]$", i):
final["fileName"] += i
interfaceSection = {
"PrivateKey": self.private_key,
"Address": self.allowed_ip,
@@ -155,7 +188,7 @@ class Peer:
if self.configuration.configurationInfo.OverridePeerSettings.DNS else self.DNS
)
}
if self.configuration.Protocol == "awg":
interfaceSection.update({
"Jc": self.configuration.Jc,
@@ -163,12 +196,19 @@ class Peer:
"Jmax": self.configuration.Jmax,
"S1": self.configuration.S1,
"S2": self.configuration.S2,
"S3": self.configuration.S3,
"S4": self.configuration.S4,
"H1": self.configuration.H1,
"H2": self.configuration.H2,
"H3": self.configuration.H3,
"H4": self.configuration.H4
"H4": self.configuration.H4,
"I1": self.configuration.I1,
"I2": self.configuration.I2,
"I3": self.configuration.I3,
"I4": self.configuration.I4,
"I5": self.configuration.I5
})
peerSection = {
"PublicKey": self.configuration.PublicKey,
"AllowedIPs": (
@@ -192,7 +232,7 @@ class Peer:
for (key, val) in combine[s]:
if val is not None and ((type(val) is str and len(val) > 0) or (type(val) is int and val > 0)):
final["file"] += f"{key} = {val}\n"
final["file"] = jinja2.Template(final["file"]).render(configuration=self.configuration)
@@ -351,4 +391,4 @@ class Peer:
hours, remainder = divmod(delta.total_seconds(), 3600)
minutes, seconds = divmod(remainder, 60)
return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"
return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"

View File

@@ -2,9 +2,13 @@
Peer Job Logger
"""
import uuid
from typing import Sequence
import sqlalchemy as db
from flask import current_app
from .ConnectionString import ConnectionString
from sqlalchemy import RowMapping
from .DatabaseConnection import ConnectionString
from .Log import Log
class PeerJobLogger:
@@ -56,4 +60,42 @@ class PeerJobLogger:
except Exception as e:
current_app.logger.error(f"Getting Peer Job Log Error", e)
return logs
return logs
return logs
def getFailingJobs(self) -> Sequence[RowMapping]:
with self.engine.connect() as conn:
table = conn.execute(
db.select(
self.jobLogTable.c.JobID
).where(
(db.or_(
self.jobLogTable.c.Status == 'false',
self.jobLogTable.c.Status == 0
) if conn.dialect.name == 'sqlite' else self.jobLogTable.c.Status == 'false')
).group_by(
self.jobLogTable.c.JobID
).having(
db.func.count(
self.jobLogTable.c.JobID
) > 10
)
).mappings().fetchall()
return table
def deleteLogs(self, LogID = None, JobID = None):
with self.engine.begin() as conn:
print(f"[WGDashboard] Deleted stale logs of JobID: {JobID}")
conn.execute(
self.jobLogTable.delete().where(
db.and_(
(self.jobLogTable.c.LogID == LogID if LogID is not None else True),
(self.jobLogTable.c.JobID == JobID if JobID is not None else True),
)
)
)
def vacuum(self):
with self.engine.begin() as conn:
if conn.dialect.name == 'sqlite':
print("[WGDashboard] SQLite Vacuuming PeerJobLogs Database")
conn.execute(db.text('VACUUM;'))

View File

@@ -1,7 +1,9 @@
"""
Peer Jobs
"""
from .ConnectionString import ConnectionString
import sqlalchemy
from .DatabaseConnection import ConnectionString
from .PeerJob import PeerJob
from .PeerJobLogger import PeerJobLogger
import sqlalchemy as db
@@ -9,7 +11,7 @@ from datetime import datetime
from flask import current_app
class PeerJobs:
def __init__(self, DashboardConfig, WireguardConfigurations):
def __init__(self, DashboardConfig, WireguardConfigurations, AllPeerShareLinks):
self.Jobs: list[PeerJob] = []
self.engine = db.create_engine(ConnectionString('wgdashboard_job'))
self.metadata = db.MetaData()
@@ -28,6 +30,8 @@ class PeerJobs:
self.__getJobs()
self.JobLogger: PeerJobLogger = PeerJobLogger(self, DashboardConfig)
self.WireguardConfigurations = WireguardConfigurations
self.AllPeerShareLinks = AllPeerShareLinks
self.cleanJob(init=True)
def __getJobs(self):
self.Jobs.clear()
@@ -116,7 +120,7 @@ class PeerJobs:
}
).where(self.peerJobTable.columns.JobID == Job.JobID)
)
self.JobLogger.log(Job.JobID, Message=f"Job is removed due to being deleted or finshed.")
self.JobLogger.log(Job.JobID, Message=f"Job is removed due to being deleted or finished.")
self.__getJobs()
self.WireguardConfigurations.get(Job.Configuration).searchPeer(Job.Peer)[1].getJobs()
return True, None
@@ -141,7 +145,7 @@ class PeerJobs:
def runJob(self):
current_app.logger.info("Running scheduled jobs")
self.cleanJob()
needToDelete = []
self.__getJobs()
for job in self.Jobs:
@@ -162,7 +166,7 @@ class PeerJobs:
if job.Action == "restrict":
s, msg = c.restrictPeers([fp.id])
elif job.Action == "delete":
s, msg = c.deletePeers([fp.id])
s, msg = c.deletePeers([fp.id], self, self.AllPeerShareLinks)
elif job.Action == "reset_total_data_usage":
s = fp.resetDataUsage("total")
c.restrictPeers([fp.id])
@@ -171,25 +175,41 @@ class PeerJobs:
self.JobLogger.log(job.JobID, s,
f"Peer {fp.id} from {c.Name} is successfully {job.Action}ed."
)
current_app.logger.info(f"Peer {fp.id} from {c.Name} is successfully {job.Action}ed.")
needToDelete.append(job)
else:
current_app.logger.info(f"Peer {fp.id} from {c.Name} is failed {job.Action}ed.")
self.JobLogger.log(job.JobID, s,
f"Peer {fp.id} from {c.Name} failed {job.Action}ed."
)
else:
current_app.logger.warning(f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed.")
self.JobLogger.log(job.JobID, False,
f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed."
)
else:
current_app.logger.warning(f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed.")
self.JobLogger.log(job.JobID, False,
f"Somehow can't find this peer {job.Peer} from {job.Configuration} failed {job.Action}ed."
)
for j in needToDelete:
self.deleteJob(j)
def cleanJob(self, init = False):
failingJobs = self.JobLogger.getFailingJobs()
with self.engine.begin() as conn:
for job in failingJobs:
conn.execute(
self.peerJobTable.update().values(
{
"ExpireDate": datetime.now()
}
).where(self.peerJobTable.columns.JobID == job.get('JobID'))
)
self.JobLogger.deleteLogs(JobID=job.get('JobID'))
self.JobLogger.log(job.get('JobID'), Message=f"Job is removed due to being stale.")
with self.engine.connect() as conn:
if init and conn.dialect.name == 'sqlite':
print("[WGDashboard] SQLite Vacuuming PeerJobs Database")
self.JobLogger.vacuum()
conn.execute(sqlalchemy.text('VACUUM;'))
def __runJob_Compare(self, x: float | datetime, y: float | datetime, operator: str):
if operator == "eq":

View File

@@ -1,4 +1,4 @@
from .ConnectionString import ConnectionString
from .DatabaseConnection import ConnectionString
from .PeerShareLink import PeerShareLink
import sqlalchemy as db
from datetime import datetime

View File

@@ -160,18 +160,41 @@ class Processes:
self.CPU_Top_10_Processes: list[Process] = []
self.Memory_Top_10_Processes: list[Process] = []
def getData(self):
while True:
try:
processes = list(psutil.process_iter())
self.CPU_Top_10_Processes = sorted(
list(map(lambda x : Process(x.name(), " ".join(x.cmdline()), x.pid, x.cpu_percent()), processes)),
key=lambda x : x.percent, reverse=True)[:20]
self.Memory_Top_10_Processes = sorted(
list(map(lambda x : Process(x.name(), " ".join(x.cmdline()), x.pid, x.memory_percent()), processes)),
key=lambda x : x.percent, reverse=True)[:20]
break
except Exception as e:
current_app.logger.error("Get processes error", e)
try:
processes = list(psutil.process_iter())
cpu_processes = []
memory_processes = []
for proc in processes:
try:
name = proc.name()
cmdline = " ".join(proc.cmdline())
pid = proc.pid
cpu_percent = proc.cpu_percent()
mem_percent = proc.memory_percent()
# Create Process object for CPU and memory tracking
cpu_process = Process(name, cmdline, pid, cpu_percent)
mem_process = Process(name, cmdline, pid, mem_percent)
cpu_processes.append(cpu_process)
memory_processes.append(mem_process)
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
# Skip processes we cant access or that no longer exist
continue
# Sort by CPU and memory usage (descending order)
cpu_sorted = sorted(cpu_processes, key=lambda p: p.percent, reverse=True)
mem_sorted = sorted(memory_processes, key=lambda p: p.percent, reverse=True)
# Get top 20 processes for each
self.CPU_Top_10_Processes = cpu_sorted[:20]
self.Memory_Top_10_Processes = mem_sorted[:20]
except Exception as e:
current_app.logger.error("Get processes error", e)
def toJson(self):
self.getData()

View File

@@ -1,6 +1,6 @@
import re, ipaddress
import subprocess
import sqlalchemy
def RegexMatch(regex, text) -> bool:
"""
@@ -18,10 +18,18 @@ def GetRemoteEndpoint() -> str:
@return:
"""
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("1.1.1.1", 80)) # Connecting to a public IP
try:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("1.1.1.1", 80)) # Connecting to a public IP
wgd_remote_endpoint = s.getsockname()[0]
return str(wgd_remote_endpoint)
except (socket.error, OSError):
pass
try:
return socket.gethostbyname(socket.gethostname())
except (socket.error, OSError):
pass
return "127.0.0.1"
def StringToBoolean(value: str):
@@ -33,31 +41,35 @@ def StringToBoolean(value: str):
return (value.strip().replace(" ", "").lower() in
("yes", "true", "t", "1", 1))
def ValidateIPAddressesWithRange(ips: str) -> bool:
s = ips.replace(" ", "").split(",")
for ip in s:
def CheckAddress(ips_str: str) -> bool:
if len(ips_str) == 0:
return False
for ip in ips_str.split(','):
stripped_ip = ip.strip()
try:
ipaddress.ip_network(ip)
except ValueError as e:
# Verify the IP-address, with the strict flag as false also allows for /32 and /128
ipaddress.ip_network(stripped_ip, strict=False)
except ValueError:
return False
return True
def ValidateIPAddresses(ips) -> bool:
s = ips.replace(" ", "").split(",")
for ip in s:
try:
ipaddress.ip_address(ip)
except ValueError as e:
return False
return True
def CheckPeerKey(peer_key: str) -> bool:
return re.match(r"^[A-Za-z0-9+/]{43}=$", peer_key)
def ValidateDNSAddress(addresses_str: str) -> tuple[bool, str | None]:
if len(addresses_str) == 0:
return False, "Got an empty list/string to check for valid DNS-addresses"
addresses = addresses_str.split(',')
for address in addresses:
stripped_address = address.strip()
if not CheckAddress(stripped_address) and not RegexMatch(r"(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]", stripped_address):
return False, f"{stripped_address} does not appear to be a valid IP-address or FQDN"
return True, None
def ValidateDNSAddress(addresses) -> tuple[bool, str]:
s = addresses.replace(" ", "").split(",")
for address in s:
if not ValidateIPAddresses(address) and not RegexMatch(
r"(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]", address):
return False, f"{address} does not appear to be an valid DNS address"
return True, ""
def ValidateEndpointAllowedIPs(IPs) -> tuple[bool, str] | tuple[bool, None]:
ips = IPs.replace(" ", "").split(",")
@@ -101,4 +113,4 @@ def ValidatePasswordStrength(password: str) -> tuple[bool, str] | tuple[bool, No
if not re.search(r'[$&+,:;=?@#|\'<>.\-^*()%!~_-]', password):
return False, "Password must contain at least 1 special character from $&+,:;=?@#|'<>.-^*()%!~_-"
return True, None
return True, None

View File

@@ -10,13 +10,18 @@ from datetime import datetime, timedelta
from itertools import islice
from flask import current_app
from .ConnectionString import ConnectionString
from .DatabaseConnection import ConnectionString
from .DashboardConfig import DashboardConfig
from .Peer import Peer
from .PeerJobs import PeerJobs
from .PeerShareLinks import PeerShareLinks
from .Utilities import StringToBoolean, GenerateWireguardPublicKey, RegexMatch, ValidateDNSAddress, \
ValidateEndpointAllowedIPs
from .Utilities import StringToBoolean, \
GenerateWireguardPublicKey, \
RegexMatch, \
ValidateDNSAddress, \
ValidateEndpointAllowedIPs, \
CheckAddress, \
CheckPeerKey
from .WireguardConfigurationInfo import WireguardConfigurationInfo, PeerGroupsClass
from .DashboardWebHooks import DashboardWebHooks
@@ -61,13 +66,14 @@ class WireguardConfiguration:
self.Protocol = "wg" if wg else "awg"
self.AllPeerJobs = AllPeerJobs
self.DashboardConfig = DashboardConfig
self.DashboardConfig.EnsureDatabaseIntegrity({self.Name: self})
self.AllPeerShareLinks = AllPeerShareLinks
self.DashboardWebHooks = DashboardWebHooks
self.configPath = os.path.join(self.__getProtocolPath(), f'{self.Name}.conf')
self.engine: sqlalchemy.Engine = sqlalchemy.create_engine(ConnectionString("wgdashboard"))
self.metadata: sqlalchemy.MetaData = sqlalchemy.MetaData()
self.dbType = self.DashboardConfig.GetConfig("Database", "type")[1]
if name is not None:
if data is not None and "Backup" in data.keys():
db = self.__importDatabase(
@@ -109,10 +115,17 @@ class WireguardConfiguration:
self.__parser["Interface"]["Jmax"] = self.Jmax
self.__parser["Interface"]["S1"] = self.S1
self.__parser["Interface"]["S2"] = self.S2
self.__parser["Interface"]["S3"] = self.S3
self.__parser["Interface"]["S4"] = self.S4
self.__parser["Interface"]["H1"] = self.H1
self.__parser["Interface"]["H2"] = self.H2
self.__parser["Interface"]["H3"] = self.H3
self.__parser["Interface"]["H4"] = self.H4
self.__parser["Interface"]["I1"] = self.I1
self.__parser["Interface"]["I2"] = self.I2
self.__parser["Interface"]["I3"] = self.I3
self.__parser["Interface"]["I4"] = self.I4
self.__parser["Interface"]["I5"] = self.I5
if "Backup" not in data.keys():
self.createDatabase()
@@ -127,8 +140,11 @@ class WireguardConfiguration:
current_app.logger.info(f"Initialized Configuration: {name}")
self.__dumpDatabase()
if self.getAutostartStatus() and not self.getStatus() and startup:
self.toggleConfiguration()
current_app.logger.info(f"Autostart Configuration: {name}")
status, ext = self.toggleConfiguration()
if not status:
current_app.logger.error(f"Failed to autostart configuration: {name}. Reason: {ext}")
else:
current_app.logger.info(f"Autostart Configuration: {name}")
self.configurationInfo: WireguardConfigurationInfo | None = None
configurationInfoJson = self.readConfigurationInfo()
@@ -140,7 +156,6 @@ class WireguardConfiguration:
if self.Status:
self.addAutostart()
def __getProtocolPath(self) -> str:
_, path = self.DashboardConfig.GetConfig("Server", "wg_conf_path") if self.Protocol == "wg" \
@@ -232,54 +247,50 @@ class WireguardConfiguration:
return True
def createDatabase(self, dbName = None):
def generate_column_obj():
return [
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False, primary_key=True),
sqlalchemy.Column('private_key', sqlalchemy.String(255)),
sqlalchemy.Column('DNS', sqlalchemy.Text),
sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text),
sqlalchemy.Column('name', sqlalchemy.Text),
sqlalchemy.Column('total_receive', sqlalchemy.Float),
sqlalchemy.Column('total_sent', sqlalchemy.Float),
sqlalchemy.Column('total_data', sqlalchemy.Float),
sqlalchemy.Column('endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('latest_handshake', sqlalchemy.String(255)),
sqlalchemy.Column('allowed_ip', sqlalchemy.String(255)),
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('mtu', sqlalchemy.Integer),
sqlalchemy.Column('keepalive', sqlalchemy.Integer),
sqlalchemy.Column('notes', sqlalchemy.Text),
sqlalchemy.Column('remote_endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('preshared_key', sqlalchemy.String(255))
]
if dbName is None:
dbName = self.Name
self.peersTable = sqlalchemy.Table(
dbName, self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False, primary_key=True),
sqlalchemy.Column('private_key', sqlalchemy.String(255)),
sqlalchemy.Column('DNS', sqlalchemy.Text),
sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text),
sqlalchemy.Column('name', sqlalchemy.Text),
sqlalchemy.Column('total_receive', sqlalchemy.Float),
sqlalchemy.Column('total_sent', sqlalchemy.Float),
sqlalchemy.Column('total_data', sqlalchemy.Float),
sqlalchemy.Column('endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('latest_handshake', sqlalchemy.String(255)),
sqlalchemy.Column('allowed_ip', sqlalchemy.String(255)),
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('mtu', sqlalchemy.Integer),
sqlalchemy.Column('keepalive', sqlalchemy.Integer),
sqlalchemy.Column('remote_endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('preshared_key', sqlalchemy.String(255)),
extend_existing=True
f'{dbName}', self.metadata, *generate_column_obj(), extend_existing=True
)
self.peersRestrictedTable = sqlalchemy.Table(
f'{dbName}_restrict_access', self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False, primary_key=True),
sqlalchemy.Column('private_key', sqlalchemy.String(255)),
sqlalchemy.Column('DNS', sqlalchemy.Text),
sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text),
sqlalchemy.Column('name', sqlalchemy.Text),
sqlalchemy.Column('total_receive', sqlalchemy.Float),
sqlalchemy.Column('total_sent', sqlalchemy.Float),
sqlalchemy.Column('total_data', sqlalchemy.Float),
sqlalchemy.Column('endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('latest_handshake', sqlalchemy.String(255)),
sqlalchemy.Column('allowed_ip', sqlalchemy.String(255)),
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('mtu', sqlalchemy.Integer),
sqlalchemy.Column('keepalive', sqlalchemy.Integer),
sqlalchemy.Column('remote_endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('preshared_key', sqlalchemy.String(255)),
extend_existing=True
f'{dbName}_restrict_access', self.metadata, *generate_column_obj(), extend_existing=True
)
self.peersDeletedTable = sqlalchemy.Table(
f'{dbName}_deleted', self.metadata, *generate_column_obj(), extend_existing=True
)
if self.DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite':
time_col_type = sqlalchemy.DATETIME
else:
time_col_type = sqlalchemy.TIMESTAMP
self.peersTransferTable = sqlalchemy.Table(
f'{dbName}_transfer', self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False),
@@ -289,8 +300,7 @@ class WireguardConfiguration:
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('time', (sqlalchemy.DATETIME if self.DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite' else sqlalchemy.TIMESTAMP),
server_default=sqlalchemy.func.now()),
sqlalchemy.Column('time', time_col_type, server_default=sqlalchemy.func.now()),
extend_existing=True
)
@@ -298,34 +308,9 @@ class WireguardConfiguration:
f'{dbName}_history_endpoint', self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False),
sqlalchemy.Column('endpoint', sqlalchemy.String(255), nullable=False),
sqlalchemy.Column('time',
(sqlalchemy.DATETIME if self.DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite' else sqlalchemy.TIMESTAMP)),
extend_existing=True
sqlalchemy.Column('time', time_col_type)
)
self.peersDeletedTable = sqlalchemy.Table(
f'{dbName}_deleted', self.metadata,
sqlalchemy.Column('id', sqlalchemy.String(255), nullable=False, primary_key=True),
sqlalchemy.Column('private_key', sqlalchemy.String(255)),
sqlalchemy.Column('DNS', sqlalchemy.Text),
sqlalchemy.Column('endpoint_allowed_ip', sqlalchemy.Text),
sqlalchemy.Column('name', sqlalchemy.Text),
sqlalchemy.Column('total_receive', sqlalchemy.Float),
sqlalchemy.Column('total_sent', sqlalchemy.Float),
sqlalchemy.Column('total_data', sqlalchemy.Float),
sqlalchemy.Column('endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('status', sqlalchemy.String(255)),
sqlalchemy.Column('latest_handshake', sqlalchemy.String(255)),
sqlalchemy.Column('allowed_ip', sqlalchemy.String(255)),
sqlalchemy.Column('cumu_receive', sqlalchemy.Float),
sqlalchemy.Column('cumu_sent', sqlalchemy.Float),
sqlalchemy.Column('cumu_data', sqlalchemy.Float),
sqlalchemy.Column('mtu', sqlalchemy.Integer),
sqlalchemy.Column('keepalive', sqlalchemy.Integer),
sqlalchemy.Column('remote_endpoint', sqlalchemy.String(255)),
sqlalchemy.Column('preshared_key', sqlalchemy.String(255)),
extend_existing=True
)
self.infoTable = sqlalchemy.Table(
'ConfigurationsInfo', self.metadata,
sqlalchemy.Column('ID', sqlalchemy.String(255), primary_key=True),
@@ -395,9 +380,7 @@ class WireguardConfiguration:
return changed
def getPeers(self):
tmpList = []
current_app.logger.info(f"Refreshing {self.Name} peer list")
tmpList = []
if self.configurationFileChanged():
with open(self.configPath, 'r') as configFile:
p = []
@@ -406,6 +389,7 @@ class WireguardConfiguration:
try:
if "[Peer]" not in content:
current_app.logger.info(f"{self.Name} config has no [Peer] section")
self.Peers = []
return
peerStarts = content.index("[Peer]")
@@ -441,8 +425,7 @@ class WireguardConfiguration:
"id": i['PublicKey'],
"private_key": "",
"DNS": self.DashboardConfig.GetConfig("Peers", "peer_global_DNS")[1],
"endpoint_allowed_ip": self.DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[
1],
"endpoint_allowed_ip": self.DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[1],
"name": i.get("name"),
"total_receive": 0,
"total_sent": 0,
@@ -456,6 +439,7 @@ class WireguardConfiguration:
"cumu_data": 0,
"mtu": self.DashboardConfig.GetConfig("Peers", "peer_mtu")[1] if len(self.DashboardConfig.GetConfig("Peers", "peer_mtu")[1]) > 0 else None,
"keepalive": self.DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1] if len(self.DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1]) > 0 else None,
"notes": "",
"remote_endpoint": self.DashboardConfig.GetConfig("Peers", "remote_endpoint")[1],
"preshared_key": i["PresharedKey"] if "PresharedKey" in i.keys() else ""
}
@@ -503,15 +487,24 @@ class WireguardConfiguration:
with self.engine.begin() as conn:
for tempPeer in self.Peers:
if tempPeer.status == "running":
endpoint = tempPeer.endpoint.rsplit(":", 1)
endpoint = tempPeer.endpoint.rsplit(":", 1)
if len(endpoint) == 2 and len(endpoint[0]) > 0:
conn.execute(
self.peersHistoryEndpointTable.insert().values({
"id": tempPeer.id,
"endpoint": endpoint[0],
"time": datetime.now()
})
)
exist = conn.execute(
self.peersHistoryEndpointTable.select().where(
sqlalchemy.and_(
self.peersHistoryEndpointTable.c.id == tempPeer.id,
self.peersHistoryEndpointTable.c.endpoint == endpoint[0]
)
)
).mappings().fetchone()
if not exist:
conn.execute(
self.peersHistoryEndpointTable.insert().values({
"id": tempPeer.id,
"endpoint": endpoint[0],
"time": datetime.now()
})
)
def addPeers(self, peers: list) -> tuple[bool, list, str]:
result = {
@@ -519,6 +512,15 @@ class WireguardConfiguration:
"peers": []
}
try:
cleanedAllowedIPs = {}
for p in peers:
newAllowedIPs = p['allowed_ip'].replace(" ", "")
if not CheckAddress(newAllowedIPs):
return False, [], "Allowed IPs entry format is incorrect"
if not CheckPeerKey(p["id"]):
return False, [], "Peer key format is incorrect"
cleanedAllowedIPs[p["id"]] = newAllowedIPs
with self.engine.begin() as conn:
for i in peers:
newPeer = {
@@ -539,6 +541,7 @@ class WireguardConfiguration:
"cumu_data": 0,
"mtu": i['mtu'],
"keepalive": i['keepalive'],
"notes": i.get("notes", ""),
"remote_endpoint": self.DashboardConfig.GetConfig("Peers", "remote_endpoint")[1],
"preshared_key": i["preshared_key"]
}
@@ -553,12 +556,15 @@ class WireguardConfiguration:
with open(uid, "w+") as f:
f.write(p['preshared_key'])
subprocess.check_output(f"{self.Protocol} set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
shell=True, stderr=subprocess.STDOUT)
command = [self.Protocol, "set", self.Name, "peer", p['id'], "allowed-ips", cleanedAllowedIPs[p["id"]], "preshared-key", uid if presharedKeyExist else "/dev/null"]
subprocess.check_output(command, stderr=subprocess.STDOUT)
if presharedKeyExist:
os.remove(uid)
subprocess.check_output(
f"{self.Protocol}-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
command = [f"{self.Protocol}-quick", "save", self.Name]
subprocess.check_output(command, stderr=subprocess.STDOUT)
self.getPeers()
for p in peers:
p = self.searchPeer(p['id'])
@@ -570,7 +576,7 @@ class WireguardConfiguration:
})
except Exception as e:
current_app.logger.error("Add peers error", e)
return False, [], str(e)
return False, [], "Internal server error"
return True, result['peers'], ""
def searchPeer(self, publicKey):
@@ -608,8 +614,16 @@ class WireguardConfiguration:
with open(uid, "w+") as f:
f.write(restrictedPeer['preshared_key'])
subprocess.check_output(f"{self.Protocol} set {self.Name} peer {restrictedPeer['id']} allowed-ips {restrictedPeer['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
shell=True, stderr=subprocess.STDOUT)
newAllowedIPs = restrictedPeer['allowed_ip'].replace(" ", "")
if not CheckAddress(newAllowedIPs):
return False, "Allowed IPs entry format is incorrect"
if not CheckPeerKey(restrictedPeer["id"]):
return False, "Peer key format is incorrect"
command = [self.Protocol, "set", self.Name, "peer", restrictedPeer["id"], "allowed-ips", newAllowedIPs, "preshared-key", uid if presharedKeyExist else "/dev/null"]
subprocess.check_output(command, stderr=subprocess.STDOUT)
if presharedKeyExist: os.remove(uid)
else:
return False, "Failed to allow access of peer " + i
@@ -629,8 +643,9 @@ class WireguardConfiguration:
found, pf = self.searchPeer(p)
if found:
try:
subprocess.check_output(f"{self.Protocol} set {self.Name} peer {pf.id} remove",
shell=True, stderr=subprocess.STDOUT)
command = [self.Protocol, "set", self.Name, "peer", pf.id, "remove"]
subprocess.check_output(command, stderr=subprocess.STDOUT)
conn.execute(
self.peersRestrictedTable.insert().from_select(
[c.name for c in self.peersTable.columns],
@@ -658,9 +673,8 @@ class WireguardConfiguration:
if not self.__wgSave():
return False, "Failed to save configuration through WireGuard"
self.getRestrictedPeers()
self.getPeers()
if numOfRestrictedPeers == len(listOfPublicKeys):
return True, f"Restricted {numOfRestrictedPeers} peer(s)"
return False, f"Restricted {numOfRestrictedPeers} peer(s) successfully. Failed to restrict {numOfFailedToRestrictPeers} peer(s)"
@@ -712,17 +726,20 @@ class WireguardConfiguration:
def __wgSave(self) -> tuple[bool, str] | tuple[bool, None]:
try:
subprocess.check_output(f"{self.Protocol}-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
command = [f"{self.Protocol}-quick", "save", self.Name]
subprocess.check_output(command, stderr=subprocess.STDOUT)
return True, None
except subprocess.CalledProcessError as e:
return False, str(e)
current_app.logger.error(f"Failed to process command:\n{str(e)}")
return False, "Internal server error"
def getPeersLatestHandshake(self):
if not self.getStatus():
self.toggleConfiguration()
try:
latestHandshake = subprocess.check_output(f"{self.Protocol} show {self.Name} latest-handshakes",
shell=True, stderr=subprocess.STDOUT)
command = [self.Protocol, "show", self.Name, "latest-handshakes"]
latestHandshake = subprocess.check_output(command, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
return "stopped"
latestHandshake = latestHandshake.decode("UTF-8").split()
@@ -761,8 +778,9 @@ class WireguardConfiguration:
if not self.getStatus():
self.toggleConfiguration()
# try:
data_usage = subprocess.check_output(f"{self.Protocol} show {self.Name} transfer",
shell=True, stderr=subprocess.STDOUT)
command = [self.Protocol, "show", self.Name, "transfer"]
data_usage = subprocess.check_output(command, stderr=subprocess.STDOUT)
data_usage = data_usage.decode("UTF-8").split("\n")
data_usage = [p.split("\t") for p in data_usage]
@@ -776,15 +794,13 @@ class WireguardConfiguration:
)
).mappings().fetchone()
if cur_i is not None:
# print(cur_i is None)
total_sent = cur_i['total_sent']
# print(cur_i is None)
total_receive = cur_i['total_receive']
cur_total_sent = float(data_usage[i][2]) / (1024 ** 3)
cur_total_receive = float(data_usage[i][1]) / (1024 ** 3)
cumulative_receive = cur_i['cumu_receive'] + total_receive
cumulative_sent = cur_i['cumu_sent'] + total_sent
if total_sent <= cur_total_sent and total_receive <= cur_total_receive:
if (total_sent * 0.999 ) <= cur_total_sent and (total_receive * 0.999) <= cur_total_receive: # An accuracy of 1K ppm is sufficient
total_sent = cur_total_sent
total_receive = cur_total_receive
else:
@@ -819,10 +835,11 @@ class WireguardConfiguration:
if not self.getStatus():
self.toggleConfiguration()
try:
data_usage = subprocess.check_output(f"{self.Protocol} show {self.Name} endpoints",
shell=True, stderr=subprocess.STDOUT)
command = [self.Protocol, "show", self.Name, "endpoints"]
data_usage = subprocess.check_output(command, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
return "stopped"
data_usage = data_usage.decode("UTF-8").split()
count = 0
with self.engine.begin() as conn:
@@ -840,14 +857,17 @@ class WireguardConfiguration:
self.getStatus()
if self.Status:
try:
check = subprocess.check_output(f"{self.Protocol}-quick down {self.Name}",
shell=True, stderr=subprocess.STDOUT)
command = [f"{self.Protocol}-quick", "down", self.Name]
check = subprocess.check_output(command, stderr=subprocess.STDOUT)
self.removeAutostart()
except subprocess.CalledProcessError as exc:
return False, str(exc.output.strip().decode("utf-8"))
else:
try:
check = subprocess.check_output(f"{self.Protocol}-quick up {self.Name}", shell=True, stderr=subprocess.STDOUT)
command = [f"{self.Protocol}-quick", "up", self.Name]
check = subprocess.check_output(command, stderr=subprocess.STDOUT)
self.addAutostart()
except subprocess.CalledProcessError as exc:
return False, str(exc.output.strip().decode("utf-8"))
@@ -914,8 +934,8 @@ class WireguardConfiguration:
files.sort(key=lambda x: x[1], reverse=True)
for f, ct in files:
if RegexMatch(f"^({self.Name})_(.*)\\.(conf)$", f):
s = re.search(f"^({self.Name})_(.*)\\.(conf)$", f)
if RegexMatch(rf"^({self.Name})_(\d+)\\.(conf)$", f):
s = re.search(rf"^({self.Name})_(\d+)\\.(conf)$", f)
date = s.group(2)
d = {
"filename": f,
@@ -988,7 +1008,7 @@ class WireguardConfiguration:
original = [l.rstrip("\n") for l in f.readlines()]
allowEdit = ["Address", "PreUp", "PostUp", "PreDown", "PostDown", "ListenPort", "Table"]
if self.Protocol == 'awg':
allowEdit += ["Jc", "Jmin", "Jmax", "S1", "S2", "H1", "H2", "H3", "H4"]
allowEdit += ["Jc", "Jmin", "Jmax", "S1", "S2", "S3", "S4", "H1", "H2", "H3", "H4", "I1", "I2", "I3", "I4", "I5"]
start = original.index("[Interface]")
try:
end = original.index("[Peer]")
@@ -1026,31 +1046,33 @@ class WireguardConfiguration:
return True
def renameConfiguration(self, newConfigurationName) -> tuple[bool, str]:
newConfigurationName = os.path.basename(newConfigurationName)
if len(newConfigurationName) > 15 or not re.match(r'^[a-zA-Z0-9_=\+\.\-]{1,15}$', newConfigurationName):
return False, "Configuration name is either too long or contains an illegal character"
newConfigurationName = newConfigurationName.replace("`", "") # double check
try:
if self.getStatus():
self.toggleConfiguration()
self.createDatabase(newConfigurationName)
with self.engine.begin() as conn:
conn.execute(
sqlalchemy.text(
f'INSERT INTO "{newConfigurationName}" SELECT * FROM "{self.Name}"'
def doRenameStatement(suffix):
newConfig = f"{newConfigurationName}{suffix}"
oldConfig = f"{self.Name}{suffix}"
conn.execute(
sqlalchemy.text(
f'INSERT INTO `{newConfig}` SELECT * FROM `{oldConfig}`'
)
)
)
conn.execute(
sqlalchemy.text(
f'INSERT INTO "{newConfigurationName}_restrict_access" SELECT * FROM "{self.Name}_restrict_access"'
)
)
conn.execute(
sqlalchemy.text(
f'INSERT INTO "{newConfigurationName}_deleted" SELECT * FROM "{self.Name}_deleted"'
)
)
conn.execute(
sqlalchemy.text(
f'INSERT INTO "{newConfigurationName}_transfer" SELECT * FROM "{self.Name}_transfer"'
)
)
doRenameStatement("")
doRenameStatement("_restrict_access")
doRenameStatement("_deleted")
doRenameStatement("_transfer")
self.AllPeerJobs.updateJobConfigurationName(self.Name, newConfigurationName)
shutil.copy(
self.configPath,
@@ -1058,8 +1080,8 @@ class WireguardConfiguration:
)
self.deleteConfiguration()
except Exception as e:
traceback.print_stack()
return False, str(e)
current_app.logger.error(f"Failed to rename configuration.\nNew Configuration Name: {newConfigurationName}\nError: {str(e)}")
return False, "Internal server error"
return True, None
def getNumberOfAvailableIP(self):
@@ -1188,7 +1210,7 @@ class WireguardConfiguration:
except Exception as e:
return False
def updateConfigurationInfo(self, key: str, value: str | dict[str, str] | dict[str, dict]) -> tuple[bool, Any, str] | tuple[
def updateConfigurationInfo(self, key: str, value: str | dict[str, str] | dict[str, dict] | bool) -> tuple[bool, Any, str] | tuple[
bool, str, None] | tuple[bool, None, None]:
if key == "Description":
self.configurationInfo.Description = value
@@ -1207,16 +1229,18 @@ class WireguardConfiguration:
for name, data in value.items():
peerGroups[name] = PeerGroupsClass(**data)
self.configurationInfo.PeerGroups = peerGroups
elif key == "PeerTrafficTracking":
self.configurationInfo.PeerTrafficTracking = value
elif key == "PeerHistoricalEndpointTracking":
self.configurationInfo.PeerHistoricalEndpointTracking = value
else:
return False, "Key does not exist", None
self.storeConfigurationInfo()
return True, None, None
def __validateOverridePeerSettings(self, key: str, value: str | int) -> tuple[bool, None] | tuple[bool, str]:
status = True
msg = None
print(value)
if key == "DNS" and value:
status, msg = ValidateDNSAddress(value)
elif key == "EndpointAllowedIPs" and value:
@@ -1226,4 +1250,59 @@ class WireguardConfiguration:
status = False
msg = "Listen Port must be >= 1 and <= 65535"
return status, msg
def getTransferTableSize(self):
with self.engine.connect() as db:
row_count = db.execute(
sqlalchemy.select(sqlalchemy.func.count()).select_from(self.peersTransferTable)
).scalar()
return int(row_count)
def getHistoricalEndpointTableSize(self):
with self.engine.connect() as db:
row_count = db.execute(
sqlalchemy.select(sqlalchemy.func.count()).select_from(self.peersHistoryEndpointTable)
).scalar()
return int(row_count)
def downloadTransferTable(self):
with self.engine.connect() as db:
data = db.execute(
self.peersTransferTable.select()
).mappings().fetchall()
return data
def downloadHistoricalEndpointTable(self):
with self.engine.connect() as db:
data = db.execute(
self.peersHistoryEndpointTable.select()
).mappings().fetchall()
return data
def deleteTransferTable(self):
try:
with self.engine.begin() as db:
db.execute(
self.peersTransferTable.delete()
)
with self.engine.connect() as conn:
if conn.dialect.name == 'sqlite':
print("[WGDashboard] SQLite Vacuuming Database")
conn.execute(sqlalchemy.text('VACUUM;'))
except Exception as e:
return False
return True
def deleteHistoryEndpointTable(self):
try:
with self.engine.begin() as db:
db.execute(
self.peersHistoryEndpointTable.delete()
)
with self.engine.connect() as conn:
if conn.dialect.name == 'sqlite':
print("[WGDashboard] SQLite Vacuuming Database")
conn.execute(sqlalchemy.text('VACUUM;'))
except Exception as e:
return False
return True

View File

@@ -18,4 +18,6 @@ class PeerGroupsClass(BaseModel):
class WireguardConfigurationInfo(BaseModel):
Description: str = ''
OverridePeerSettings: OverridePeerSettingsClass = OverridePeerSettingsClass(**{})
PeerGroups: dict[str, PeerGroupsClass] = {}
PeerGroups: dict[str, PeerGroupsClass] = {}
PeerTrafficTracking: bool = True
PeerHistoricalEndpointTracking: bool = True

View File

@@ -1,17 +1,17 @@
bcrypt
ifcfg
psutil
pyotp
Flask
flask-cors
icmplib
gunicorn
requests
tcconfig
sqlalchemy
sqlalchemy_utils
psycopg
PyMySQL
tzlocal
python-jose
pydantic
bcrypt==5.0.0
ifcfg==0.24
psutil==7.2.2
pyotp==2.9.0
Flask==3.1.2
flask-cors==6.0.2
icmplib==3.0.4
gunicorn==25.0.3
requests==2.32.5
tcconfig==0.30.1
sqlalchemy==2.0.46
sqlalchemy_utils==0.42.1
psycopg[binary]==3.3.3
PyMySQL==1.1.2
tzlocal==5.3.1
python-jose==3.5.0
pydantic==2.12.5

View File

@@ -6,14 +6,21 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="application-name" content="WGDashboard">
<meta name="apple-mobile-web-app-title" content="WGDashboard">
<link rel="manifest" href="/json/manifest.json">
<link rel="icon" href="/img/Logo-2-512x512.png">
<link rel="manifest" href="./json/manifest.json">
<link rel="icon" href="./img/Logo-2-512x512.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WGDashboard</title>
<base href="./">
<script>
const isViteDevMode = document.querySelector('script[src*="@vite/client"]') !== null;
const base = document.querySelector('base');
if (base && isViteDevMode) {
base.href = '/';
}
</script>
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "app",
"version": "4.3.0.1",
"version": "4.3.3",
"private": true,
"type": "module",
"module": "es2022",
@@ -12,35 +12,38 @@
"preview": "vite preview"
},
"dependencies": {
"@volar/language-server": "2.4.23",
"@vue/language-server": "3.0.5",
"@vuepic/vue-datepicker": "^11.0.2",
"@vueuse/core": "^13.5.0",
"@vueuse/shared": "^13.5.0",
"@volar/language-server": "2.4.28",
"@vue/language-server": "3.2.4",
"@vuepic/vue-datepicker": "^12.1.0",
"@vueuse/core": "^14.2.1",
"@vueuse/shared": "^14.1.0",
"animate.css": "^4.1.1",
"bootstrap": "^5.3.2",
"bootstrap-icons": "^1.11.3",
"cidr-tools": "^11.0.3",
"cidr-tools": "^11.3.2",
"css-color-converter": "^2.0.0",
"dayjs": "^1.11.12",
"electron-builder": "^26.0.12",
"dayjs": "^1.11.19",
"electron-builder": "^26.7.0",
"fuse.js": "^7.0.0",
"i": "^0.3.7",
"is-cidr": "^5.0.3",
"npm": "^10.5.0",
"ol": "^10.2.1",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"is-cidr": "^6.0.3",
"npm": "^11.8.0",
"ol": "^10.7.0",
"pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.7.1",
"qrcode": "^1.5.3",
"qrcodejs": "^1.0.0",
"simple-code-editor": "^2.0.9",
"uuid": "^11.1.0",
"vue": "^3.5.17",
"vue-chartjs": "^5.3.0",
"vue-router": "^4.2.5"
"uuid": "^13.0.0",
"vue": "^3.5.31",
"vue-chartjs": "^5.3.3",
"vue-router": "^5.0.4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.0",
"vite": "^7.0.5"
"@vitejs/plugin-vue": "^6.0.4",
"vite": "^7.3.1"
},
"overrides": {
"tar": "^7.5.6"
}
}

View File

@@ -2,7 +2,7 @@
import { ref, reactive } from "vue"
import LocaleText from "@/components/text/localeText.vue";
import OidcSettings from "@/components/clientComponents/clientSettingComponents/oidcSettings.vue";
import { fetchGet } from "@/utilities/fetch.js"
import { fetchGet, fetchPost } from "@/utilities/fetch.js"
const emits = defineEmits(['close'])
import { DashboardConfigurationStore } from "@/stores/DashboardConfigurationStore"
const dashboardConfigurationStore = DashboardConfigurationStore()
@@ -12,12 +12,16 @@ const values = reactive({
})
const toggling = ref(false)
const toggleClientSideApp = async () => {
const updateSettings = async (key: string) => {
toggling.value = true
await fetchGet("/api/clients/toggleStatus", {}, (res) => {
values.enableClients = res.data
await fetchPost("/api/updateDashboardConfigurationItem", {
section: "Clients",
key: key,
value: dashboardConfigurationStore.Configuration.Clients[key]
}, async (res) => {
await dashboardConfigurationStore.getConfiguration()
toggling.value = false
})
toggling.value = false
}
</script>
@@ -37,16 +41,43 @@ const toggleClientSideApp = async () => {
</h6>
<div class="form-check form-switch ms-auto">
<label class="form-check-label" for="oidc_switch">
<LocaleText :t="values.enableClients ? 'Enabled':'Disabled'"></LocaleText>
<LocaleText :t="dashboardConfigurationStore.Configuration.Clients.enable ? 'Enabled':'Disabled'"></LocaleText>
</label>
<input
:disabled="oidcStatusLoading"
v-model="values.enableClients"
@change="toggleClientSideApp()"
:disabled="toggling"
v-model="dashboardConfigurationStore.Configuration.Clients.enable"
@change="updateSettings('enable')"
class="form-check-input" type="checkbox" role="switch" id="oidc_switch">
</div>
</div>
<OidcSettings mode="Client"></OidcSettings>
<hr>
<div>
<div class="d-flex align-items-center">
<h6 class="mb-0">
<LocaleText t="Sign Up as Local Client"></LocaleText>
</h6>
<div class="form-check form-switch ms-auto">
<label class="form-check-label" for="sign_up_switch">
<LocaleText :t="dashboardConfigurationStore.Configuration.Clients.sign_up ? 'Enabled':'Disabled'"></LocaleText>
</label>
<input
:disabled="toggling"
v-model="dashboardConfigurationStore.Configuration.Clients.sign_up"
@change="updateSettings('sign_up')"
class="form-check-input" type="checkbox" role="switch" id="sign_up_switch">
</div>
</div>
<small class="text-muted mb-0">
<LocaleText t="Allow clients to sign up with Email and Password"></LocaleText>
</small>
</div>
<div>
<OidcSettings mode="Client"></OidcSettings>
<small class="text-muted mb-0">
<LocaleText t="Allow clients to access with OpenID"></LocaleText>
</small>
</div>
</div>
</div>
</div>

View File

@@ -46,14 +46,7 @@ const restoreBackup = () => {
}
const downloadBackup = () => {
fetchGet("/api/downloadWireguardConfigurationBackup", {
configurationName: route.params.id,
backupFileName: props.b.filename
}, (res) => {
if (res.status){
window.open(getUrl(`/fileDownload?file=${res.data}`), '_blank')
}
})
window.location.href = getUrl(`/api/downloadWireguardConfigurationBackup?configurationName=${route.params.id}&backupFileName=${props.b.filename}`);
}
const delaySeconds = computed(() => {

View File

@@ -77,11 +77,11 @@ const emits = defineEmits(["backup", "close"])
</div>
<div v-else-if="backups.length > 0">
<i class="bi bi-check-circle-fill me-2"></i>
<LocaleText :t="'This configuration have ' + backups.length + ' backups'"></LocaleText>
<LocaleText :t="'This configuration has ' + backups.length + ' backups'"></LocaleText>
</div>
<div v-else class="d-flex align-items-center gap-2">
<i class="bi bi-x-circle-fill me-2"></i>
<LocaleText t="This configuration have no backup"></LocaleText>
<LocaleText t="This configuration has no backup"></LocaleText>
<a role="button"
@click="emits('backup')"
class="ms-auto btn btn-sm btn-primary rounded-3">
@@ -119,4 +119,4 @@ const emits = defineEmits(["backup", "close"])
<style scoped>
</style>
</style>

View File

@@ -197,14 +197,14 @@ const deleteConfigurationModal = ref(false)
v-model="data[key]"
:id="'configuration_' + key">
</div>
<div v-for="key in ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'H1', 'H2', 'H3', 'H4']"
<div v-for="key in ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5']"
v-if="configurationInfo.Protocol === 'awg'">
<label :for="'configuration_' + key" class="form-label">
<small class="text-muted">
<LocaleText :t="key"></LocaleText>
</small>
</label>
<input type="number" class="form-control form-control-sm rounded-3"
<input type="text" class="form-control form-control-sm rounded-3"
:disabled="saving"
v-model="data[key]"
:id="'configuration_' + key">

View File

@@ -0,0 +1,31 @@
<script>
import LocaleText from "@/components/text/localeText.vue";
export default {
name: "notesInput",
components: {LocaleText},
props: {
bulk: Boolean,
data: Object,
saving: Boolean
}
}
</script>
<template>
<div :class="{inactiveField: this.bulk}">
<label for="peer_notes_textbox" class="form-label">
<small class="text-muted">
<LocaleText t="Notes"></LocaleText>
</small>
</label>
<input type="text" class="form-control form-control-sm rounded-3"
:disabled="this.saving || this.bulk"
v-model="this.data.notes"
id="peer_notes_textbox" placeholder="">
</div>
</template>
<style scoped>
</style>

View File

@@ -134,12 +134,17 @@ export default {
</div>
</div>
</div>
<div class="card-footer" role="button" @click="$emit('details')">
<div class="card-footer" role="button" @click="$emit('details')" v-if="!this.Peer.restricted">
<small class="d-flex align-items-center">
<LocaleText t="Details"></LocaleText>
<i class="bi bi-chevron-right ms-auto"></i>
</small>
</div>
<div class="card-footer" v-else>
<small class="d-flex align-items-center text-muted">
<LocaleText t="Allow access to view details"></LocaleText>
</small>
</div>
</div>
</template>

View File

@@ -15,6 +15,7 @@ import MtuInput from "@/components/configurationComponents/newPeersComponents/mt
import PersistentKeepAliveInput
from "@/components/configurationComponents/newPeersComponents/persistentKeepAliveInput.vue";
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
import NotesInput from "./newPeersComponents/notesInput.vue";
const dashboardStore = DashboardConfigurationStore()
const wireguardStore = WireguardConfigurationsStore()
@@ -27,11 +28,11 @@ const peerData = ref({
public_key: "",
DNS: dashboardStore.Configuration.Peers.peer_global_dns,
endpoint_allowed_ip: dashboardStore.Configuration.Peers.peer_endpoint_allowed_ip,
notes: "",
keepalive: parseInt(dashboardStore.Configuration.Peers.peer_keep_alive),
mtu: parseInt(dashboardStore.Configuration.Peers.peer_mtu),
preshared_key: "",
preshared_key_bulkAdd: false,
advanced_security: "off",
allowed_ips_validation: true,
})
const availableIp = ref([])
@@ -105,6 +106,7 @@ watch(() => {
<template v-if="!peerData.bulkAdd">
<hr class="mb-0 mt-2">
<NameInput :saving="saving" :data="peerData"></NameInput>
<NotesInput :saving="saving" :data="peerData"></NotesInput>
<PrivatePublicKeyInput :saving="saving" :data="peerData"></PrivatePublicKeyInput>
<AllowedIPsInput :availableIp="availableIp" :saving="saving" :data="peerData"></AllowedIPsInput>
</template>
@@ -118,7 +120,7 @@ watch(() => {
<LocaleText t="Advanced Options"></LocaleText>
</button>
</h2>
<div id="peerAddModalAccordionAdvancedOptions"
<div id="peerAddModalAccordionAdvancedOptions"
class="accordion-collapse collapse collapsed" data-bs-parent="#peerAddModalAccordion">
<div class="accordion-body rounded-bottom-3">
<div class="d-flex flex-column gap-2">

View File

@@ -37,7 +37,6 @@ export default {
mtu: parseInt(this.dashboardStore.Configuration.Peers.peer_mtu),
preshared_key: "",
preshared_key_bulkAdd: false,
advanced_security: "off",
},
availableIp: undefined,
availableIpSearchString: "",

View File

@@ -32,6 +32,7 @@ Chart.register(
import PeerSessions from "@/components/peerDetailsModalComponents/peerSessions.vue";
import PeerTraffics from "@/components/peerDetailsModalComponents/peerTraffics.vue";
import PeerEndpoints from "@/components/peerDetailsModalComponents/peerEndpoints.vue";
import { GetLocale } from "@/utilities/locale"
const props = defineProps(['selectedPeer'])
const selectedDate = ref(undefined)
defineEmits(['close'])
@@ -49,13 +50,18 @@ defineEmits(['close'])
<button type="button" class="btn-close ms-auto" @click="$emit('close')"></button>
</div>
<div class="card-body px-4">
<div>
<p class="mb-0 text-muted"><small>
<LocaleText t="Peer"></LocaleText>
</small></p>
<h2>
{{ selectedPeer.name }}
</h2>
<div class="d-flex justify-content-between align-items-start mb-2 flex-column flex-md-row">
<div>
<p class="mb-0 text-muted"><small><LocaleText t="Peer" /></small></p>
<h2 :class="{'text-muted': selectedPeer.name.length === 0 }">
{{ selectedPeer.name.length > 0 ? selectedPeer.name : GetLocale("Untitled Peer") }}
</h2>
</div>
<div v-if="selectedPeer.notes" class="text-start text-md-end">
<p class="mb-0 text-muted"><small><LocaleText t="Notes" /></small></p>
<p class="mb-0" style="white-space: pre-wrap">{{ selectedPeer.notes }}</p>
</div>
</div>
<div class="row mt-3 gy-2 gx-2 mb-2">
<div class="col-12 col-lg-3">

View File

@@ -146,8 +146,8 @@ export default {
<td v-if="showLogID"><samp class="text-muted">{{log.LogID}}</samp></td>
<td v-if="showJobID"><samp class="text-muted">{{log.JobID}}</samp></td>
<td>
<span class="badge" :class="[log.Status === '1' ? 'text-success-emphasis bg-success-subtle':'text-danger-emphasis bg-danger-subtle']">
{{log.Status === "1" ? 'Success': 'Failed'}}
<span class="badge" :class="[(log.Status === '1' || log.Status === 'true') ? 'text-success-emphasis bg-success-subtle':'text-danger-emphasis bg-danger-subtle']">
{{(log.Status === "1" || log.Status === "true") ? 'Success': 'Failed'}}
</span>
</td>
<td>{{log.Message}}</td>

View File

@@ -3,7 +3,7 @@ import ScheduleDropdown from "@/components/configurationComponents/peerScheduleJ
import {ref} from "vue";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import {fetchPost} from "@/utilities/fetch.js";
import VueDatePicker from "@vuepic/vue-datepicker";
import { VueDatePicker } from "@vuepic/vue-datepicker";
import dayjs from "dayjs";
import LocaleText from "@/components/text/localeText.vue";

View File

@@ -99,6 +99,17 @@ export default {
v-model="this.data.name"
id="peer_name_textbox" placeholder="">
</div>
<div>
<label for="peer_notes_textbox" class="form-label">
<small class="text-muted">
<LocaleText t="Notes"></LocaleText>
</small>
</label>
<input type="text" class="form-control form-control-sm rounded-3"
:disabled="this.saving"
v-model="this.data.notes"
id="peer_notes_textbox" placeholder="">
</div>
<div>
<div class="d-flex position-relative">
<label for="peer_private_key_textbox" class="form-label">

View File

@@ -3,7 +3,7 @@ import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStor
import {fetchPost} from "@/utilities/fetch.js";
import dayjs from "dayjs";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import VueDatePicker from '@vuepic/vue-datepicker';
import { VueDatePicker } from '@vuepic/vue-datepicker';
import LocaleText from "@/components/text/localeText.vue";
import PeerShareWithEmail from "@/components/configurationComponents/peerShareLinkComponents/peerShareWithEmail.vue";

View File

@@ -2,7 +2,7 @@
import dayjs from "dayjs";
import {fetchPost} from "@/utilities/fetch.js";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import VueDatePicker from "@vuepic/vue-datepicker";
import { VueDatePicker } from "@vuepic/vue-datepicker";
import LocaleText from "@/components/text/localeText.vue";
export default {

View File

@@ -61,7 +61,7 @@ const sendTestEmail = async () => {
<div class="card-header">
<h6 class="my-2 d-flex">
<i class="bi bi-envelope-fill me-2"></i>
<LocaleText t="Email Account"></LocaleText>
<LocaleText t="Email Server Settings"></LocaleText>
<span class="text-success ms-auto" v-if="emailIsReady">
<i class="bi bi-check-circle-fill me-2"></i>
<LocaleText t="Ready"></LocaleText>
@@ -115,6 +115,9 @@ const sendTestEmail = async () => {
<select class="form-select rounded-3"
v-model="store.Configuration.Email.encryption"
id="encryption">
<option value="IMPLICITTLS">
IMPLICIT TLS
</option>
<option value="STARTTLS">
STARTTLS
</option>
@@ -208,4 +211,4 @@ const sendTestEmail = async () => {
<style scoped>
</style>
</style>

View File

@@ -0,0 +1,76 @@
<script setup>
import LocaleText from "@/components/text/localeText.vue";
import ConfigurationTracking
from "@/components/settingsComponent/dashboardWireguardConfigurationTrackingComponents/configurationTracking.vue";
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
import {onMounted, ref, watch} from "vue";
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
const store = WireguardConfigurationsStore()
const dashboardStore = DashboardConfigurationStore()
const peerTrackingStatus = ref(dashboardStore.Configuration.WireGuardConfiguration.peer_tracking)
const loaded = ref(false)
const trackingData = ref({})
onMounted(async () => {
if (peerTrackingStatus.value){
await loadData()
}
})
const loadData = async () => {
await fetchGet("/api/getPeerTrackingTableCounts", {}, (ref) => {
if (ref.status){
trackingData.value = ref.data
}
loaded.value = true
})
}
watch(peerTrackingStatus, async (newVal) => {
await fetchPost("/api/updateDashboardConfigurationItem", {
section: "WireGuardConfiguration",
key: "peer_tracking",
value: newVal
}, async (res) => {
if (res.status){
dashboardStore.newMessage("Server", newVal ? "Peer tracking enabled" : "Peer tracking disabled", "success")
if (newVal) await loadData()
}
})
})
</script>
<template>
<div class="card">
<div class="card-header d-flex align-items-center">
<h6 class="my-2">
<LocaleText t="Peer Tracking"></LocaleText>
</h6>
<div class="form-check form-switch ms-auto">
<input class="form-check-input"
v-model="peerTrackingStatus"
type="checkbox" role="switch" id="peerTrackingStatus">
<label class="form-check-label" for="peerTrackingStatus">
<LocaleText t="Enabled" v-if="peerTrackingStatus"></LocaleText>
<LocaleText t="Disabled" v-else></LocaleText>
</label>
</div>
</div>
<div class="card-body d-flex flex-column gap-3" v-if="peerTrackingStatus">
<template v-if="!loaded">
<div class="spinner-border text-body m-auto"></div>
</template>
<template v-else>
<ConfigurationTracking :configuration="configuration"
:trackingData="trackingData"
v-for="configuration in store.Configurations"/>
</template>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,190 @@
<script setup>
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
import {onMounted, ref, watch} from "vue";
import LocaleText from "@/components/text/localeText.vue";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
const props = defineProps(['configuration', 'trackingData'])
const sizes = ref({
HistoricalTrackingTableSize: 0,
TrafficTrackingTableSize: 0
})
const sizeDataLoaded = ref(false)
const toggling = ref(false)
await onMounted(async () => {
sizes.value = props.trackingData[props.configuration.Name]
})
const loadSizeData = async () => {
await fetchGet("/api/getPeerTrackingTableCounts", {
configurationName: props.configuration.Name
}, (res) => {
sizes.value = res.data;
})
}
const updateToggle = async (key) => {
toggling.value = true;
await fetchPost('/api/updateWireguardConfigurationInfo', {
Name: props.configuration.Name,
Key: key,
Value: props.configuration.Info[key]
}, (res) => {
console.log(res)
toggling.value = false;
})
}
const downloading = ref(undefined);
const download = async (key) => {
downloading.value = key;
await fetchGet("/api/downloadPeerTrackingTable", {
configurationName: props.configuration.Name,
table: key
}, (res) => {
if (res.status){
const s = JSON.stringify(res.data, null, 2)
const b = new Blob([s], {
type: "application/json"
})
const url = URL.createObjectURL(b)
const a = document.createElement('a')
a.href = url;
a.download = `${props.configuration.Name}_${key}.json`
a.click()
downloading.value = undefined;
}
})
}
const confirmDelete = ref("")
const deleting = ref(undefined)
const dashboardStore = DashboardConfigurationStore()
const deleteRecord = async (key) => {
deleting.value = true;
await fetchPost('/api/deletePeerTrackingTable', {
configurationName: props.configuration.Name,
table: key
}, async(res) => {
if (res.status){
dashboardStore.newMessage('Server', 'Record deleted', 'success')
}else{
dashboardStore.newMessage('Server', 'Record delete failed', 'danger')
}
await loadSizeData()
deleting.value = false;
confirmDelete.value = ''
})
}
</script>
<template>
<div class="card">
<div class="card-header">
{{ configuration.Name }}
</div>
<div class="card-body">
<div class="row gy-2">
<div class="col-sm">
<small class="text-muted fw-bold">Peer Traffic Tracking</small>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox"
:disabled="toggling"
@change="updateToggle('PeerTrafficTracking')"
v-model="configuration.Info.PeerTrafficTracking" :id="configuration.Name + '_traffic_tracking'">
<label class="form-check-label" :for="configuration.Name + '_traffic_tracking'">
{{ configuration.Info.PeerTrafficTracking ? 'On':'Off'}}
</label>
</div>
<hr />
<div class="d-flex align-items-start align-items-md-center flex-column flex-md-row gap-2">
<h6 class="mb-0">
{{ sizes.TrafficTrackingTableSize }} <span class="text-muted fw-normal"><LocaleText t="Records"></LocaleText></span>
</h6>
<div class="ms-md-auto d-flex gap-2" v-if="confirmDelete !== 'TrafficTrackingTable'">
<button class="btn btn-sm bg-primary-subtle text-primary-emphasis rounded-3"
:class="{disabled: downloading === 'TrafficTrackingTable'}"
@click="download('TrafficTrackingTable')"
>
<i class="bi bi-download me-2 "></i>
<LocaleText :t="downloading === 'TrafficTrackingTable' ? 'Downloading...':'Download'"></LocaleText>
</button>
<button class="btn btn-sm bg-danger-subtle text-danger-emphasis rounded-3" @click="confirmDelete = 'TrafficTrackingTable'">
<i class="bi bi-trash me-2 "></i>Delete
</button>
</div>
<div class="ms-md-auto d-flex gap-2 align-items-center" v-else-if="confirmDelete === 'TrafficTrackingTable'">
<small>
<LocaleText t="Are you sure to delete?"></LocaleText>
</small>
<button class="btn btn-sm bg-danger-subtle text-danger-emphasis rounded-3"
:class="{disabled: deleting}"
@click="deleteRecord('TrafficTrackingTable')">
<i class="bi bi-check me-2 "></i>Yes
</button>
<button
:class="{disabled: deleting}"
class="btn btn-sm bg-secondary-subtle text-secondary-emphasis rounded-3" @click="confirmDelete = ''">
<i class="bi bi-x me-2 "></i>No
</button>
</div>
</div>
</div>
<div class="col-sm">
<small class="text-muted fw-bold">Peer Historical Endpoint Tracking</small>
<div class="form-check form-switch">
<input class="form-check-input"
:disabled="toggling"
@change="updateToggle('PeerHistoricalEndpointTracking')"
type="checkbox" v-model="configuration.Info.PeerHistoricalEndpointTracking" :id="configuration.Name + '_historicalEndpoint_tracking'">
<label class="form-check-label" :for="configuration.Name + '_historicalEndpoint_tracking'">
{{ configuration.Info.PeerHistoricalEndpointTracking ? 'On':'Off'}}
</label>
</div>
<hr />
<div class="d-flex align-items-start align-items-md-center flex-column flex-md-row gap-2">
<div>
<h6 class="mb-0">
{{ sizes.HistoricalTrackingTableSize }} <span class="text-muted fw-normal"><LocaleText t="Records"></LocaleText></span>
</h6>
</div>
<div class="ms-md-auto d-flex gap-2" v-if="confirmDelete !== 'HistoricalTrackingTable'">
<button
@click="download('HistoricalTrackingTable')"
:class="{disabled: downloading === 'HistoricalTrackingTable'}"
class="btn btn-sm bg-primary-subtle text-primary-emphasis rounded-3">
<i class="bi bi-download me-2 "></i>
<LocaleText :t="downloading === 'HistoricalTrackingTable' ? 'Downloading...':'Download'"></LocaleText>
</button>
<button class="btn btn-sm bg-danger-subtle text-danger-emphasis rounded-3" @click="confirmDelete = 'HistoricalTrackingTable'">
<i class="bi bi-trash me-2 "></i>Delete
</button>
</div>
<div class="ms-md-auto d-flex gap-2 align-items-center" v-else-if="confirmDelete === 'HistoricalTrackingTable'">
<small>
<LocaleText t="Are you sure to delete?"></LocaleText>
</small>
<button class="btn btn-sm bg-danger-subtle text-danger-emphasis rounded-3"
:class="{disabled: deleting}"
@click="deleteRecord('HistoricalTrackingTable')">
<i class="bi bi-check me-2 "></i>Yes
</button>
<button
:class="{disabled: deleting}"
class="btn btn-sm bg-secondary-subtle text-secondary-emphasis rounded-3" @click="confirmDelete = ''">
<i class="bi bi-x me-2 "></i>No
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -4,6 +4,8 @@ import DashboardSettingsInputWireguardConfigurationPath
from "@/components/settingsComponent/dashboardSettingsInputWireguardConfigurationPath.vue";
import DashboardSettingsWireguardConfigurationAutostart
from "@/components/settingsComponent/dashboardSettingsWireguardConfigurationAutostart.vue";
import DashboardWireguardConfigurationTracking
from "@/components/settingsComponent/dashboardWireguardConfigurationTracking.vue";
</script>
<template>
@@ -16,6 +18,7 @@ import DashboardSettingsWireguardConfigurationAutostart
>
</DashboardSettingsInputWireguardConfigurationPath>
<DashboardSettingsWireguardConfigurationAutostart></DashboardSettingsWireguardConfigurationAutostart>
<DashboardWireguardConfigurationTracking/>
</div>
</template>

View File

@@ -20,7 +20,6 @@ const squareHeight = computed(() => {
<template>
<div class="flex-grow-1 square rounded-3 border position-relative p-2"
@mouseenter="show = true"
@mouseleave="show = false"
:style="{'background-color': `rgb(13 110 253 / ${percentage*10}%)`}">

View File

@@ -49,7 +49,7 @@ const data = computed(() => {
<div class="progress" role="progressbar" style="height: 6px">
<div class="progress-bar" :style="{width: `${data?.CPU.cpu_percent}%` }"></div>
</div>
<div class="d-flex mt-2 gap-1">
<div class="d-grid mt-2 gap-1" style="grid-template-columns: repeat(10, 1fr)">
<CpuCore
v-for="(cpu, count) in data?.CPU.cpu_percent_per_cpu"
:key="count"
@@ -74,7 +74,7 @@ const data = computed(() => {
<div class="progress" role="progressbar" style="height: 6px">
<div class="progress-bar bg-success" :style="{width: `${data?.Disks.find(x => x.mountPoint === '/') ? data?.Disks.find(x => x.mountPoint === '/').percent : data?.Disks[0].percent}%` }"></div>
</div>
<div class="d-flex mt-2 gap-1">
<div class="d-grid mt-2 gap-1" style="grid-template-columns: repeat(10, 1fr)">
<StorageMount v-for="(disk, count) in data?.Disks"
v-if="data"
:key="disk.mountPoint"

View File

@@ -27,8 +27,11 @@ export const getUrl = (url) => {
if (apiKey){
return `${apiKey.host}${url}`
}
return import.meta.env.MODE === 'development' ? url
: `${window.location.protocol}//${(window.location.host + window.location.pathname + url).replace(/\/\//g, '/')}`
if (import.meta.env.MODE === 'development') {
return url;
}
// const appPrefix = window.APP_PREFIX || '';
return `./.${url}`;
}
export const fetchGet = async (url, params=undefined, callback=undefined) => {

View File

@@ -43,10 +43,17 @@ export default {
Jmax: 998,
S1: 17,
S2: 110,
S3: 1,
S4: 2,
H1: 0,
H2: 0,
H3: 0,
H4: 0
H4: 0,
I1: "0",
I2: "0",
I3: "0",
I4: "0",
I5: "0"
},
numberOfAvailableIPs: "0",
error: false,
@@ -59,14 +66,16 @@ export default {
},
created() {
this.wireguardGenerateKeypair();
let hValue = []
while ([...new Set(hValue)].length !== 4){
hValue = [this.rand(1, (2**31) - 1), this.rand(1, (2**31) - 1), this.rand(1, (2**31) - 1), this.rand(1, (2**31) - 1)]
}
this.newConfiguration.H1 = hValue[0]
this.newConfiguration.H2 = hValue[1]
this.newConfiguration.H3 = hValue[2]
this.newConfiguration.H4 = hValue[3]
// Generate 4 random numbers for H1, H2, H3, H4
['H1', 'H2', 'H3', 'H4'].forEach(key => {
this.newConfiguration[key] = this.rand(1, 2**31);
});
// Initialize I1 to I5 as "0"
['I1', 'I2', 'I3', 'I4', 'I5'].forEach(key => {
this.newConfiguration[key] = "0";
});
},
methods: {
rand(min, max){
@@ -379,7 +388,7 @@ export default {
<div class="card rounded-3"
v-if="this.newConfiguration.Protocol === 'awg'"
v-for="key in ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'H1', 'H2', 'H3', 'H4']">
v-for="key in ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5']">
<div class="card-header">{{ key }}</div>
<div class="card-body">
<input type="text"

View File

@@ -176,7 +176,10 @@ export default {
<input
v-model="this.store.CrossServerConfiguration.Enable"
:disabled="loading"
class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckChecked">
class="form-check-input"
type="checkbox"
role="switch"
id="flexSwitchCheckChecked">
<label class="form-check-label" for="flexSwitchCheckChecked">
<LocaleText t="Access Remote Server"></LocaleText>
</label>
@@ -184,10 +187,20 @@ export default {
</div>
</div>
</div>
<small class="text-muted pb-3 d-block w-100 text-center mt-3">
WGDashboard {{ this.version }} | Developed with by
<a href="https://github.com/donaldzou" target="_blank"><strong>Donald Zou</strong></a>
</small>
<div class="d-flex container-fluid align-items-center my-1 w-100">
<small class="text-muted">
WGDashboard <strong>{{ this.version }}</strong> | Made with by
<a href="https://github.com/WGDashboard"
class="text-decoration-none text-body"
target="_blank"><strong>WGDashboard</strong></a>
</small>
<a href="./client" target="_blank"
class="text-decoration-none ms-auto text-body"
style="white-space: nowrap">
<small><i class="bi bi-box-arrow-up-right me-1"></i>
<LocaleText t="Client App" /></small>
</a>
</div>
<div class="messageCentre text-body position-absolute d-flex">
<TransitionGroup name="message" tag="div"
class="position-relative flex-sm-grow-0 flex-grow-1 d-flex align-items-end ms-sm-auto flex-column gap-2">

View File

@@ -176,7 +176,7 @@ const memoryHistoricalChartData = computed(() => {
<div class="card rounded-3 h-100 shadow">
<div class="card-body p-4">
<div class="d-flex flex-column gap-3">
<div class="d-flex flex-column gap-3" style="height: 130px">
<div class="d-flex flex-column gap-3" style="min-height: 130px">
<div class="d-flex align-items-center">
<h3 class="text-muted mb-0">
<i class="bi bi-cpu-fill me-2"></i>
@@ -192,7 +192,7 @@ const memoryHistoricalChartData = computed(() => {
<div class="progress" role="progressbar" style="height: 10px">
<div class="progress-bar" :style="{width: `${data?.CPU.cpu_percent}%` }"></div>
</div>
<div class="d-flex gap-1">
<div class="d-grid gap-1" style="grid-template-columns: repeat(10, 1fr)">
<CpuCore
v-for="(cpu, count) in data?.CPU.cpu_percent_per_cpu"
:square="true"

View File

@@ -32,7 +32,7 @@ export default defineConfig(({mode}) => {
}
return {
base: "/static/dist/WGDashboardAdmin",
base: "./",
plugins: [
vue(),
],

View File

@@ -2,9 +2,10 @@
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/img/Logo-2-128x128.png">
<link rel="icon" href="./img/Logo-2-128x128.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WGDashboard Client</title>
<base href="./client/">
<style>
*{
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
@@ -28,15 +29,22 @@
}
}
</style>
<script>
const isViteDevMode = document.querySelector('script[src*="@vite/client"]') !== null;
const base = document.querySelector('base');
if (base && isViteDevMode) {
base.href = '/';
}
</script>
</head>
<body>
<div id="app">
<div id="preloader">
<div id="preloader_placeholder">
<img style="width: 100%" src="/img/Logo-2-128x128.png" alt="WGDashboard Client" />
<img style="width: 100%" src="./img/Logo-2-128x128.png" alt="WGDashboard Client" />
</div>
</div>
</div>
<script type="module" src="/src/main.js"></script>
<script type="module" src="./src/main.js"></script>
</body>
</html>

View File

@@ -1,12 +1,12 @@
{
"name": "client",
"version": "0.0.0",
"version": "v1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "client",
"version": "0.0.0",
"version": "v1.0",
"dependencies": {
"axios": "^1.9.0",
"bootstrap": "^5.3.6",
@@ -1013,9 +1013,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz",
"integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz",
"integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==",
"cpu": [
"arm"
],
@@ -1027,9 +1027,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz",
"integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz",
"integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==",
"cpu": [
"arm64"
],
@@ -1041,9 +1041,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz",
"integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz",
"integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==",
"cpu": [
"arm64"
],
@@ -1055,9 +1055,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz",
"integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz",
"integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==",
"cpu": [
"x64"
],
@@ -1069,9 +1069,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz",
"integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz",
"integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==",
"cpu": [
"arm64"
],
@@ -1083,9 +1083,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz",
"integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz",
"integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==",
"cpu": [
"x64"
],
@@ -1097,13 +1097,16 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz",
"integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz",
"integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==",
"cpu": [
"arm"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1111,13 +1114,16 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz",
"integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz",
"integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==",
"cpu": [
"arm"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1125,13 +1131,16 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz",
"integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz",
"integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1139,41 +1148,84 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz",
"integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz",
"integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==",
"cpu": [
"arm64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz",
"integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==",
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz",
"integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==",
"cpu": [
"loong64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz",
"integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==",
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz",
"integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==",
"cpu": [
"loong64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz",
"integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==",
"cpu": [
"ppc64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz",
"integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==",
"cpu": [
"ppc64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1181,13 +1233,16 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz",
"integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz",
"integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==",
"cpu": [
"riscv64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1195,13 +1250,16 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz",
"integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz",
"integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==",
"cpu": [
"riscv64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1209,13 +1267,16 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz",
"integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz",
"integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==",
"cpu": [
"s390x"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1223,13 +1284,16 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz",
"integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz",
"integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -1237,9 +1301,26 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz",
"integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz",
"integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==",
"cpu": [
"x64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz",
"integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==",
"cpu": [
"x64"
],
@@ -1247,13 +1328,27 @@
"license": "MIT",
"optional": true,
"os": [
"linux"
"openbsd"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz",
"integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz",
"integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz",
"integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==",
"cpu": [
"arm64"
],
@@ -1265,9 +1360,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz",
"integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz",
"integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==",
"cpu": [
"ia32"
],
@@ -1278,10 +1373,24 @@
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz",
"integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz",
"integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz",
"integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==",
"cpu": [
"x64"
],
@@ -1313,9 +1422,9 @@
}
},
"node_modules/@types/estree": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
@@ -1582,19 +1691,19 @@
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.9.0",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.9.0.tgz",
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz",
"integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^2.1.0"
}
},
"node_modules/birpc": {
@@ -1692,7 +1801,7 @@
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
@@ -1764,7 +1873,7 @@
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
@@ -1818,9 +1927,9 @@
"license": "MIT"
},
"node_modules/dayjs": {
"version": "1.11.13",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
"version": "1.11.20",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz",
"integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
"license": "MIT"
},
"node_modules/debug": {
@@ -1895,7 +2004,7 @@
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
@@ -1910,7 +2019,7 @@
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
@@ -1959,7 +2068,7 @@
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
@@ -1968,7 +2077,7 @@
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
@@ -1977,7 +2086,7 @@
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
@@ -1989,7 +2098,7 @@
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
@@ -2131,9 +2240,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
@@ -2151,14 +2260,15 @@
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.2.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -2197,7 +2307,7 @@
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
@@ -2225,7 +2335,7 @@
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
@@ -2249,7 +2359,7 @@
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
@@ -2289,7 +2399,7 @@
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
@@ -2308,7 +2418,7 @@
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
@@ -2320,7 +2430,7 @@
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
@@ -2335,7 +2445,7 @@
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
@@ -2574,7 +2684,7 @@
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
@@ -2583,7 +2693,7 @@
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
@@ -2592,7 +2702,7 @@
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
@@ -2799,9 +2909,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2886,10 +2996,13 @@
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/qrcode": {
"version": "1.5.4",
@@ -2930,13 +3043,13 @@
"license": "MIT"
},
"node_modules/rollup": {
"version": "4.41.1",
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.41.1.tgz",
"integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==",
"version": "4.60.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz",
"integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.7"
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -2946,26 +3059,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.41.1",
"@rollup/rollup-android-arm64": "4.41.1",
"@rollup/rollup-darwin-arm64": "4.41.1",
"@rollup/rollup-darwin-x64": "4.41.1",
"@rollup/rollup-freebsd-arm64": "4.41.1",
"@rollup/rollup-freebsd-x64": "4.41.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.41.1",
"@rollup/rollup-linux-arm-musleabihf": "4.41.1",
"@rollup/rollup-linux-arm64-gnu": "4.41.1",
"@rollup/rollup-linux-arm64-musl": "4.41.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.41.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.41.1",
"@rollup/rollup-linux-riscv64-gnu": "4.41.1",
"@rollup/rollup-linux-riscv64-musl": "4.41.1",
"@rollup/rollup-linux-s390x-gnu": "4.41.1",
"@rollup/rollup-linux-x64-gnu": "4.41.1",
"@rollup/rollup-linux-x64-musl": "4.41.1",
"@rollup/rollup-win32-arm64-msvc": "4.41.1",
"@rollup/rollup-win32-ia32-msvc": "4.41.1",
"@rollup/rollup-win32-x64-msvc": "4.41.1",
"@rollup/rollup-android-arm-eabi": "4.60.1",
"@rollup/rollup-android-arm64": "4.60.1",
"@rollup/rollup-darwin-arm64": "4.60.1",
"@rollup/rollup-darwin-x64": "4.60.1",
"@rollup/rollup-freebsd-arm64": "4.60.1",
"@rollup/rollup-freebsd-x64": "4.60.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.60.1",
"@rollup/rollup-linux-arm-musleabihf": "4.60.1",
"@rollup/rollup-linux-arm64-gnu": "4.60.1",
"@rollup/rollup-linux-arm64-musl": "4.60.1",
"@rollup/rollup-linux-loong64-gnu": "4.60.1",
"@rollup/rollup-linux-loong64-musl": "4.60.1",
"@rollup/rollup-linux-ppc64-gnu": "4.60.1",
"@rollup/rollup-linux-ppc64-musl": "4.60.1",
"@rollup/rollup-linux-riscv64-gnu": "4.60.1",
"@rollup/rollup-linux-riscv64-musl": "4.60.1",
"@rollup/rollup-linux-s390x-gnu": "4.60.1",
"@rollup/rollup-linux-x64-gnu": "4.60.1",
"@rollup/rollup-linux-x64-musl": "4.60.1",
"@rollup/rollup-openbsd-x64": "4.60.1",
"@rollup/rollup-openharmony-arm64": "4.60.1",
"@rollup/rollup-win32-arm64-msvc": "4.60.1",
"@rollup/rollup-win32-ia32-msvc": "4.60.1",
"@rollup/rollup-win32-x64-gnu": "4.60.1",
"@rollup/rollup-win32-x64-msvc": "4.60.1",
"fsevents": "~2.3.2"
}
},
@@ -3213,9 +3331,9 @@
}
},
"node_modules/vite": {
"version": "6.3.5",
"resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.5.tgz",
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,20 +1,12 @@
<script setup async>
<script setup>
import './assets/main.css'
import NotificationList from "@/components/Notification/notificationList.vue";
import {clientStore} from "@/stores/clientStore.js";
const store = clientStore()
fetch("/client/api/serverInformation")
.then(res => res.json())
.then(res => store.serverInformation = res.data)
</script>
<template>
<div data-bs-theme="dark" class="text-body bg-body vw-100 vh-100 bg-body">
<div class="d-flex vw-100 p-sm-4 overflow-y-scroll innerContainer d-flex flex-column">
<div class="mx-auto my-sm-auto position-relative"
id="listContainer"
>
<div class="mx-auto my-sm-auto position-relative" id="listContainer">
<Suspense>
<RouterView v-slot="{ Component }">
<Transition name="app" type="transition" mode="out-in">

View File

@@ -2,7 +2,7 @@
import {computed, ref} from "vue";
import ConfigurationQRCode from "@/components/Configuration/configurationQRCode.vue";
import dayjs from "dayjs";
import Duration from 'dayjs/plugin/Duration'
import Duration from 'dayjs/plugin/duration'
dayjs.extend(Duration);
const props = defineProps([
'config'
@@ -113,4 +113,4 @@ const emits = defineEmits(['select'])
background-color: #28a745 !important;
box-shadow: 0 0 0 .2rem #28a74545;
}
</style>
</style>

View File

@@ -96,7 +96,7 @@ if (route.query.Email){
</span>
</button>
</form>
<div>
<div v-if="store.serverInformation.SignUp.enable">
<hr class="my-4">
<div class="d-flex align-items-center">
<span class="text-muted">

View File

@@ -6,43 +6,43 @@ import router from "@/router/router.js";
import {createPinia} from "pinia";
import 'bootstrap/dist/js/bootstrap.bundle.js'
import {axiosPost} from "@/utilities/request.js";
import {axiosGet, axiosPost} from "@/utilities/request.js";
import {clientStore} from "@/stores/clientStore.js";
const params = new URLSearchParams(window.location.search)
const state = params.get('state')
const code = params.get('code')
const initApp = () => {
const initApp = async () => {
const app = createApp(App)
const serverInformation = await axiosGet("/api/serverInformation", {})
app.use(createPinia())
if (serverInformation){
const store = clientStore()
store.serverInformation = serverInformation.data;
}
app.use(router)
app.mount("#app")
}
function removeSearchString() {
let url = new URL(window.location.href);
url.search = ''; // Remove all query parameters
history.replaceState({}, document.title, url.toString());
}
if (state && code){
axiosPost("/api/signin/oidc", {
await axiosPost("/api/signin/oidc", {
provider: state,
code: code,
redirect_uri: window.location.protocol + '//' + window.location.host + window.location.pathname
}).then(data => {
}).then(async (data) => {
let url = new URL(window.location.href);
url.search = '';
history.replaceState({}, document.title, url.toString());
initApp()
await initApp()
if (!data.status){
const store = clientStore()
store.newNotification(data.message, 'danger')
}
})
}else{
initApp()
await initApp()
}

View File

@@ -50,6 +50,11 @@ const router = createRouter({
})
router.beforeEach(async (to, from, next) => {
const store = clientStore()
if (to.path === "/signup" && !store.serverInformation.SignUp.enable){
next('/signin')
store.newNotification("Sign up is disabled. Please contact administrator for more information", "warning")
}
if (to.path === '/signout'){
await axios.get(requestURl('/api/signout')).then(() => {
next('/signin')

View File

@@ -1,23 +1,18 @@
import axios from "axios";
import {useRouter} from "vue-router";
export const requestURl = (url) => {
return import.meta.env.MODE === 'development' ? '/client' + url
: `${window.location.protocol}//${(window.location.host + window.location.pathname + url).replace(/\/\//g, '/')}`
if (import.meta.env.MODE === 'development') {
return '/client' + url;
}
return `./.${url}`;
}
// const router = useRouter()
export const axiosPost = async (URL, body = {}) => {
try{
const res = await axios.post(requestURl(URL), body)
return res.data
} catch (error){
console.log(error)
// if (error.status === 401){
// await router.push('/signin')
// }
return undefined
}
}
@@ -28,9 +23,6 @@ export const axiosGet = async (URL, query = {}) => {
return res.data
} catch (error){
console.log(error)
// if (error.status === 401){
// await router.push('/signin')
// }
return undefined
}
}

View File

@@ -40,5 +40,5 @@ export default defineConfig({
}
}
},
base: '/static/dist/WGDashboardClient'
base: './'
})

View File

@@ -1 +1 @@
import{a7 as A,r as n,D as S,g as l,z as v}from"./index-BQYwwrw6.js";const b=A("DashboardClientAssignmentStore",()=>{const f=n({}),d=n([]),o=n({}),c=n([]),g=n(!1),r=n(""),i=S(),w=async()=>{await l("/api/clients/allClients",{},s=>{o.value=s.data})},y=async()=>{await l("/api/clients/allClientsRaw",{},s=>{c.value=s.data,console.log(c.value)})},m=s=>Object.values(o.value).flat().find(e=>e.ClientID===s),u=async(s,e)=>{await l("/api/clients/assignedClients",{ConfigurationName:s,Peer:e},a=>{d.value=a.data})};return{assignments:d,getAssignedClients:u,getClients:w,getClientsRaw:y,clients:o,unassignClient:async(s,e,a)=>{g.value=!0,await v("/api/clients/unassignClient",{AssignmentID:a},async t=>{t.status?(i.newMessage("Server","Unassign successfully!","success"),s&&e&&await u(s,e)):(i.newMessage("Server","Unassign Failed. Reason: "+t.message,"success"),console.error("Unassign Failed. Reason: "+t.message)),g.value=!1})},assignClient:async(s,e,a,t=!0)=>{r.value=a,await v("/api/clients/assignClient",{ConfigurationName:s,Peer:e,ClientID:a},async C=>{C.status?(i.newMessage("Server","Assign successfully!","success"),t&&await u(s,e)):(i.newMessage("Server","Assign Failed. Reason: "+C.message,"success"),console.error("Assign Failed. Reason: "+C.message)),r.value=""})},getClientById:m,unassigning:g,assigning:r,clientsRaw:c,allConfigurationsPeers:f,getAllConfigurationsPeers:async()=>{await l("/api/clients/allConfigurationsPeers",{},s=>{f.value=s.data})}}});export{b as D};
import{a5 as A,r as n,D as S,g as l,z as v}from"./index-DXzxfcZW.js";const b=A("DashboardClientAssignmentStore",()=>{const f=n({}),d=n([]),o=n({}),c=n([]),g=n(!1),r=n(""),i=S(),w=async()=>{await l("/api/clients/allClients",{},s=>{o.value=s.data})},y=async()=>{await l("/api/clients/allClientsRaw",{},s=>{c.value=s.data,console.log(c.value)})},m=s=>Object.values(o.value).flat().find(e=>e.ClientID===s),u=async(s,e)=>{await l("/api/clients/assignedClients",{ConfigurationName:s,Peer:e},a=>{d.value=a.data})};return{assignments:d,getAssignedClients:u,getClients:w,getClientsRaw:y,clients:o,unassignClient:async(s,e,a)=>{g.value=!0,await v("/api/clients/unassignClient",{AssignmentID:a},async t=>{t.status?(i.newMessage("Server","Unassign successfully!","success"),s&&e&&await u(s,e)):(i.newMessage("Server","Unassign Failed. Reason: "+t.message,"success"),console.error("Unassign Failed. Reason: "+t.message)),g.value=!1})},assignClient:async(s,e,a,t=!0)=>{r.value=a,await v("/api/clients/assignClient",{ConfigurationName:s,Peer:e,ClientID:a},async C=>{C.status?(i.newMessage("Server","Assign successfully!","success"),t&&await u(s,e)):(i.newMessage("Server","Assign Failed. Reason: "+C.message,"success"),console.error("Assign Failed. Reason: "+C.message)),r.value=""})},getClientById:m,unassigning:g,assigning:r,clientsRaw:c,allConfigurationsPeers:f,getAllConfigurationsPeers:async()=>{await l("/api/clients/allConfigurationsPeers",{},s=>{f.value=s.data})}}});export{b as D};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
import{_ as r,c as i,a as e,d as o,w as t,k as l,j as a,l as _,S as d,h as u}from"./index-BQYwwrw6.js";const m={name:"configuration"},p={class:"mt-md-5 mt-3 text-body"};function f(h,k,x,w,$,v){const n=u("RouterView");return e(),i("div",p,[o(n,null,{default:t(({Component:s,route:c})=>[o(l,{name:"fade2",mode:"out-in"},{default:t(()=>[(e(),a(d,null,{default:t(()=>[(e(),a(_(s),{key:c.path,class:"z-1"}))]),_:2},1024))]),_:2},1024)]),_:1})])}const B=r(m,[["render",f]]);export{B as default};

View File

@@ -0,0 +1 @@
import{_ as r,c as i,b as o,w as e,k as l,j as a,l as _,S as u,h as d,f as t}from"./index-DXzxfcZW.js";const m={name:"configuration"},f={class:"mt-md-5 mt-3 text-body"};function p(h,k,x,w,$,v){const n=d("RouterView");return t(),i("div",f,[o(n,null,{default:e(({Component:s,route:c})=>[o(l,{name:"fade2",mode:"out-in"},{default:e(()=>[(t(),a(u,null,{default:e(()=>[(t(),a(_(s),{key:c.path,class:"z-1"}))]),_:2},1024))]),_:2},1024)]),_:1})])}const B=r(m,[["render",p]]);export{B as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.fade-enter-active[data-v-9f596f5e]{transition-delay:var(--v0d365bfc)!important}.progress-bar[data-v-01ef60a9]{width:0;transition:all 1s cubic-bezier(.42,0,.22,1)}.filter a[data-v-7ed053f0]{text-decoration:none}

View File

@@ -1 +0,0 @@
.fade-enter-active[data-v-9f596f5e]{transition-delay:var(--0d365bfc)!important}.progress-bar[data-v-851170e4]{width:0;transition:all 1s cubic-bezier(.42,0,.22,1)}.filter a[data-v-7ed053f0]{text-decoration:none}

View File

@@ -1 +1 @@
import{_ as f,c as i,a as n,b as t,d as u,h as w,e as k,m as x,y,n as p,t as v,z as _,D as m,W as b,A as S,r as D,q as $,F as W,i as V}from"./index-BQYwwrw6.js";import{L as C}from"./localeText-C3GiyveB.js";const F={name:"dashboardSettingsInputWireguardConfigurationPath",components:{LocaleText:C},props:{targetData:String,title:String,warning:!1,warningText:""},setup(){const o=m(),s=b(),r=`input_${S()}`;return{store:o,uuid:r,WireguardConfigurationStore:s}},data(){return{value:"",invalidFeedback:"",showInvalidFeedback:!1,isValid:!1,timeout:void 0,changed:!1,updating:!1}},mounted(){this.value=this.store.Configuration.Server[this.targetData]},methods:{async useValidation(){this.changed&&(this.updating=!0,await _("/api/updateDashboardConfigurationItem",{section:"Server",key:this.targetData,value:this.value},o=>{o.status?(this.isValid=!0,this.showInvalidFeedback=!1,this.store.Configuration.Account[this.targetData]=this.value,clearTimeout(this.timeout),this.timeout=setTimeout(()=>this.isValid=!1,5e3),this.WireguardConfigurationStore.getConfigurations(),this.store.newMessage("Server","WireGuard configuration path saved","success")):(this.isValid=!1,this.showInvalidFeedback=!0,this.invalidFeedback=o.message),this.changed=!1,this.updating=!1}))}}},I={class:"card"},T={class:"card-header"},A={class:"my-2"},L={class:"card-body"},M={class:"form-group"},N=["for"],P={class:"d-flex gap-2 align-items-start"},B={class:"flex-grow-1"},G=["id","disabled"],z={class:"invalid-feedback fw-bold"},U=["disabled"],q={key:0,class:"bi bi-save2-fill"},E={key:1,class:"spinner-border spinner-border-sm"},K={key:0,class:"px-2 py-1 text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 d-inline-block mt-1 mb-2"};function j(o,s,r,a,c,g){const d=w("LocaleText");return n(),i("div",I,[t("div",T,[t("h6",A,[u(d,{t:"Path"})])]),t("div",L,[t("div",M,[t("label",{for:this.uuid,class:"text-muted mb-1"},[t("strong",null,[t("small",null,[u(d,{t:this.title},null,8,["t"])])])],8,N),t("div",P,[t("div",B,[x(t("input",{type:"text",class:p(["form-control rounded-3",{"is-invalid":this.showInvalidFeedback,"is-valid":this.isValid}]),id:this.uuid,"onUpdate:modelValue":s[0]||(s[0]=e=>this.value=e),onKeydown:s[1]||(s[1]=e=>this.changed=!0),disabled:this.updating},null,42,G),[[y,this.value]]),t("div",z,v(this.invalidFeedback),1)]),t("button",{onClick:s[2]||(s[2]=e=>this.useValidation()),disabled:!this.changed,class:"ms-auto btn rounded-3 border-success-subtle bg-success-subtle text-success-emphasis"},[this.updating?(n(),i("span",E)):(n(),i("i",q))],8,U)]),r.warning?(n(),i("div",K,[t("small",null,[s[3]||(s[3]=t("i",{class:"bi bi-exclamation-triangle-fill me-2"},null,-1)),u(d,{t:r.warningText},null,8,["t"])])])):k("",!0)])])])}const et=f(F,[["render",j]]),H={class:"card rounded-3"},J={class:"card-header"},O={class:"my-2"},Q={class:"card-body d-flex gap-2"},R={class:"list-group w-100"},X=["onClick"],Y={__name:"dashboardSettingsWireguardConfigurationAutostart",setup(o){const s=m(),r=b(),a=D(s.Configuration.WireGuardConfiguration.autostart),c=$(()=>r.Configurations.map(e=>e.Name)),g=async()=>{await _("/api/updateDashboardConfigurationItem",{section:"WireGuardConfiguration",key:"autostart",value:a.value},async e=>{e.status?(s.newMessage("Server","Start up configurations saved","success"),a.value=e.data):s.newMessage("Server","Start up configurations failed to save","danger")})},d=e=>{a.value.includes(e)?a.value=a.value.filter(h=>h!==e):a.value.push(e),g()};return(e,h)=>(n(),i("div",H,[t("div",J,[t("h6",O,[u(C,{t:"Toggle When Start Up"})])]),t("div",Q,[t("div",R,[(n(!0),i(W,null,V(c.value,l=>(n(),i("button",{type:"button",key:l,onClick:Z=>d(l),class:"list-group-item list-group-item-action py-2 w-100 d-flex align-items-center"},[t("samp",null,v(l),1),t("i",{class:p(["ms-auto",[a.value.includes(l)?"bi-check-circle-fill":"bi-circle"]])},null,2)],8,X))),128))])])]))}},at=f(Y,[["__scopeId","data-v-4aa2aed9"]]);export{et as D,at as a};
import{_ as f,c as i,a as t,b as u,h as w,d as k,m as x,y,n as p,t as v,z as _,D as m,W as b,A as S,f as n,r as D,q as $,F as W,i as V}from"./index-DXzxfcZW.js";import{L as C}from"./localeText-Dmcj5qqx.js";const F={name:"dashboardSettingsInputWireguardConfigurationPath",components:{LocaleText:C},props:{targetData:String,title:String,warning:!1,warningText:""},setup(){const o=m(),s=b(),r=`input_${S()}`;return{store:o,uuid:r,WireguardConfigurationStore:s}},data(){return{value:"",invalidFeedback:"",showInvalidFeedback:!1,isValid:!1,timeout:void 0,changed:!1,updating:!1}},mounted(){this.value=this.store.Configuration.Server[this.targetData]},methods:{async useValidation(){this.changed&&(this.updating=!0,await _("/api/updateDashboardConfigurationItem",{section:"Server",key:this.targetData,value:this.value},o=>{o.status?(this.isValid=!0,this.showInvalidFeedback=!1,this.store.Configuration.Account[this.targetData]=this.value,clearTimeout(this.timeout),this.timeout=setTimeout(()=>this.isValid=!1,5e3),this.WireguardConfigurationStore.getConfigurations(),this.store.newMessage("Server","WireGuard configuration path saved","success")):(this.isValid=!1,this.showInvalidFeedback=!0,this.invalidFeedback=o.message),this.changed=!1,this.updating=!1}))}}},I={class:"card"},T={class:"card-header"},A={class:"my-2"},L={class:"card-body"},M={class:"form-group"},N=["for"],P={class:"d-flex gap-2 align-items-start"},B={class:"flex-grow-1"},G=["id","disabled"],z={class:"invalid-feedback fw-bold"},U=["disabled"],q={key:0,class:"bi bi-save2-fill"},E={key:1,class:"spinner-border spinner-border-sm"},K={key:0,class:"px-2 py-1 text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 d-inline-block mt-1 mb-2"};function j(o,s,r,a,c,g){const d=w("LocaleText");return n(),i("div",I,[t("div",T,[t("h6",A,[u(d,{t:"Path"})])]),t("div",L,[t("div",M,[t("label",{for:this.uuid,class:"text-muted mb-1"},[t("strong",null,[t("small",null,[u(d,{t:this.title},null,8,["t"])])])],8,N),t("div",P,[t("div",B,[x(t("input",{type:"text",class:p(["form-control rounded-3",{"is-invalid":this.showInvalidFeedback,"is-valid":this.isValid}]),id:this.uuid,"onUpdate:modelValue":s[0]||(s[0]=e=>this.value=e),onKeydown:s[1]||(s[1]=e=>this.changed=!0),disabled:this.updating},null,42,G),[[y,this.value]]),t("div",z,v(this.invalidFeedback),1)]),t("button",{onClick:s[2]||(s[2]=e=>this.useValidation()),disabled:!this.changed,class:"ms-auto btn rounded-3 border-success-subtle bg-success-subtle text-success-emphasis"},[this.updating?(n(),i("span",E)):(n(),i("i",q))],8,U)]),r.warning?(n(),i("div",K,[t("small",null,[s[3]||(s[3]=t("i",{class:"bi bi-exclamation-triangle-fill me-2"},null,-1)),u(d,{t:r.warningText},null,8,["t"])])])):k("",!0)])])])}const et=f(F,[["render",j]]),H={class:"card rounded-3"},J={class:"card-header"},O={class:"my-2"},Q={class:"card-body d-flex gap-2"},R={class:"list-group w-100"},X=["onClick"],Y={__name:"dashboardSettingsWireguardConfigurationAutostart",setup(o){const s=m(),r=b(),a=D(s.Configuration.WireGuardConfiguration.autostart),c=$(()=>r.Configurations.map(e=>e.Name)),g=async()=>{await _("/api/updateDashboardConfigurationItem",{section:"WireGuardConfiguration",key:"autostart",value:a.value},async e=>{e.status?(s.newMessage("Server","Start up configurations saved","success"),a.value=e.data):s.newMessage("Server","Start up configurations failed to save","danger")})},d=e=>{a.value.includes(e)?a.value=a.value.filter(h=>h!==e):a.value.push(e),g()};return(e,h)=>(n(),i("div",H,[t("div",J,[t("h6",O,[u(C,{t:"Toggle When Start Up"})])]),t("div",Q,[t("div",R,[(n(!0),i(W,null,V(c.value,l=>(n(),i("button",{type:"button",key:l,onClick:Z=>d(l),class:"list-group-item list-group-item-action py-2 w-100 d-flex align-items-center"},[t("samp",null,v(l),1),t("i",{class:p(["ms-auto",[a.value.includes(l)?"bi-check-circle-fill":"bi-circle"]])},null,2)],8,X))),128))])])]))}},at=f(Y,[["__scopeId","data-v-4aa2aed9"]]);export{et as D,at as a};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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