Compare commits

...
16 Commits
Author SHA1 Message Date
Evgeny VolfertsandGitHub fa13569d4b Add new AmneziaWG parameters (implemented in 3.0/3.1) (#1363)
Docker Build and Push / docker_build (push) Waiting to run
Docker Build and Push / docker_scan (push) Blocked by required conditions
2026-10-29 10:46:10 +01:00
Evgeny VolfertsandGitHub d4661a8f0f fix: clean up endpoint history tables on rename and delete (#1367) 2026-10-29 10:43:21 +01:00
Mikhail SolovevandGitHub fefde651d2 Bug fix. Remove redundant break statement in IP allocation loop. (#1294)
Docker Build and Push / docker_build (push) Canceled after 0s
Docker Build and Push / docker_scan (push) Canceled after 0s
* Remove redundant break statement in IP allocation loop.

* Fix: append IP to allowed_ips list instead of overwriting it
2026-10-28 17:55:57 +01:00
LEE Kai-ZeandGitHub e6f6201d25 Fix: backup regex, SQL cleanup, and restore metadata conflict (#1324)
Docker Build and Push / docker_build (push) Canceled after 0s
Docker Build and Push / docker_scan (push) Canceled after 0s
* fix: backup filename regex dot escaping

* fix: delete paired .sql file when removing backup

* fix: restore backup metadata conflict
2026-10-28 15:01:42 +01:00
unixroandGitHub c4cbbdb51b fix: addPeers uses OverridePeerSettings for DNS and AllowedIPs fallback (#1310)
* fix: addPeers uses OverridePeerSettings for DNS and AllowedIPs fallback

When adding a peer via the API without explicit DNS or endpoint_allowed_ip
values, the endpoint fell back directly to global wg-dashboard.ini defaults,
ignoring any per-configuration OverridePeerSettings. This caused new peers to
be stored with the wrong DNS and AllowedIPs in the database even when the
configuration had overrides set.

Resolve by fetching the configuration's OverridePeerSettings before the global
defaults. The priority is now: request body → OverridePeerSettings → global
default. The existing config lookup that happened later in the function is
removed as it is now done earlier.

Note: downloadPeer already applied OverridePeerSettings at download time, so
client configs were correct; only the stored peer data was affected.

* fix: addPeers applies OverridePeerSettings for MTU and PersistentKeepalive

Extend the peer-settings priority chain (request body → OverridePeerSettings
→ global default) to MTU and PersistentKeepalive, matching the same pattern
already applied to DNS and EndpointAllowedIPs.

* style: align addPeers OverridePeerSettings fallback with project code style
2026-10-28 15:01:12 +01:00
neolegshishovandGitHub 2df57f9405 Fix: batch IP geolocation requests (limit 100), add deduplication and caching (#1232)
Docker Build and Push / docker_build (push) Canceled after 0s
Docker Build and Push / docker_scan (push) Canceled after 0s
- Added batching of IP addresses into chunks of 100 to comply with ip-api limits
- Implemented deduplication to avoid repeated geolocation requests for identical IPs
- Added in-memory cache to reuse results across batches
- Preserved original order and length of geolocation list in API response
- Improved error handling around external API failures
2026-10-27 21:16:57 +01:00
DaanSelenGitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0018a9b1e2 chore: merge development into main (#1398)
CodeQL / Analyze (javascript) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
Docker Build and Push / docker_build (push) Canceled after 0s
Docker Build and Push / docker_scan (push) Canceled after 0s
* build(deps): bump golang in /docker

Bumps golang from 1.26-alpine3.24 to 1.27-alpine3.24.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.27-alpine3.24
  dependency-type: direct:production
...

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

* build(deps): bump python in /docker

Bumps python from 3.14-alpine3.24 to 3.15-alpine3.24.

---
updated-dependencies:
- dependency-name: python
  dependency-version: 3.15-alpine3.23
  dependency-type: direct:production
...

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

* build(deps): bump sqlalchemy-utils from 0.42.1 to 0.43.0 in /src

Bumps [sqlalchemy-utils](https://github.com/kvesteri/sqlalchemy-utils) from 0.42.1 to 0.43.0.
- [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.1...0.43.0)

---
updated-dependencies:
- dependency-name: sqlalchemy-utils
  dependency-version: 0.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump bootstrap-icons in /src/static/client

Bumps [bootstrap-icons](https://github.com/twbs/icons) from 1.13.1 to 1.13.2.
- [Release notes](https://github.com/twbs/icons/releases)
- [Commits](https://github.com/twbs/icons/compare/v1.13.1...v1.13.2)

---
updated-dependencies:
- dependency-name: bootstrap-icons
  dependency-version: 1.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump pinia from 3.0.4 to 4.1.0 in /src/static/client

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

---
updated-dependencies:
- dependency-name: pinia
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* build(deps): bump @vueuse/shared in /src/static/app

Bumps [@vueuse/shared](https://github.com/vueuse/vueuse/tree/HEAD/packages/shared) from 14.4.0 to 15.0.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v15.0.0/packages/shared)

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

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

* build(deps): bump dayjs from 1.11.19 to 1.11.23 in /src/static/app

Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.11.19 to 1.11.23.
- [Release notes](https://github.com/iamkun/dayjs/releases)
- [Changelog](https://github.com/iamkun/dayjs/blob/v1.11.23/CHANGELOG.md)
- [Commits](https://github.com/iamkun/dayjs/compare/v1.11.19...v1.11.23)

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

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

* build(deps): bump cidr-tools from 12.0.2 to 13.0.4 in /src/static/app

Bumps [cidr-tools](https://github.com/silverwind/cidr-tools) from 12.0.2 to 13.0.4.
- [Release notes](https://github.com/silverwind/cidr-tools/releases)
- [Commits](https://github.com/silverwind/cidr-tools/compare/12.0.2...13.0.4)

---
updated-dependencies:
- dependency-name: cidr-tools
  dependency-version: 13.0.4
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* build(deps): bump pymysql from 1.2.0 to 1.2.3 in /src

Bumps [pymysql](https://github.com/PyMySQL/PyMySQL) from 1.2.0 to 1.2.3.
- [Release notes](https://github.com/PyMySQL/PyMySQL/releases)
- [Changelog](https://github.com/PyMySQL/PyMySQL/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PyMySQL/PyMySQL/compare/v1.2.0...v1.2.3)

---
updated-dependencies:
- dependency-name: pymysql
  dependency-version: 1.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

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

Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.2.7 to 3.3.13.
- [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.3.13/packages/language-server)

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

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

* build(deps): bump dayjs from 1.11.21 to 1.11.23 in /src/static/client

Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.11.21 to 1.11.23.
- [Release notes](https://github.com/iamkun/dayjs/releases)
- [Changelog](https://github.com/iamkun/dayjs/blob/v1.11.23/CHANGELOG.md)
- [Commits](https://github.com/iamkun/dayjs/compare/v1.11.21...v1.11.23)

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

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

* 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.28 to 2.4.30.
- [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.30/packages/language-server)

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

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

* build(deps): bump vue-router from 5.1.0 to 5.4.0 in /src/static/client

Bumps [vue-router](https://github.com/vuejs/router) from 5.1.0 to 5.4.0.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/compare/v5.1.0...v5.4.0)

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

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

* build(deps): bump axios from 1.18.0 to 1.20.0 in /src/static/client

Bumps [axios](https://github.com/axios/axios) from 1.18.0 to 1.20.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.18.0...v1.20.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump ol from 10.9.0 to 10.11.0 in /src/static/app

Bumps [ol](https://github.com/openlayers/openlayers) from 10.9.0 to 10.11.0.
- [Release notes](https://github.com/openlayers/openlayers/releases)
- [Commits](https://github.com/openlayers/openlayers/compare/v10.9.0...v10.11.0)

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

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

* build(deps-dev): bump vite from 8.0.16 to 8.3.5 in /src/static/app

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.16 to 8.3.5.
- [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/v8.3.5/packages/vite)

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

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

* build(deps): bump uuid from 14.0.0 to 14.0.3 in /src/static/app

Bumps [uuid](https://github.com/uuidjs/uuid) from 14.0.0 to 14.0.3.
- [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/v14.0.0...v14.0.3)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 14.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

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

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

---
updated-dependencies:
- dependency-name: pinia
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* build(deps): bump vue-chartjs from 5.3.3 to 5.3.4 in /src/static/app

Bumps [vue-chartjs](https://github.com/apertureless/vue-chartjs) from 5.3.3 to 5.3.4.
- [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.3...v5.3.4)

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

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

* build(deps): bump bootstrap-icons in /src/static/app

Bumps [bootstrap-icons](https://github.com/twbs/icons) from 1.13.1 to 1.13.2.
- [Release notes](https://github.com/twbs/icons/releases)
- [Commits](https://github.com/twbs/icons/compare/v1.13.1...v1.13.2)

---
updated-dependencies:
- dependency-name: bootstrap-icons
  dependency-version: 1.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump npm from 11.13.0 to 12.2.1 in /src/static/app

Bumps [npm](https://github.com/npm/cli) from 11.13.0 to 12.2.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.13.0...v12.2.1)

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

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

* 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.7.0 to 26.18.0.
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/electron-builder@26.18.0/packages/electron-builder/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/commits/electron-builder@26.18.0/packages/electron-builder)

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

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

* chore: remove arm/v7 due to deprecation

* chore: bump some frontend dependencies

* chore: attempt to mitigate jose vuln

* refactor: rework wgd.sh functions

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-26 15:59:07 +01:00
dded6a86df Merge commit from fork
User-controlled template sources were rendered with the unrestricted
jinja2.Template, allowing authenticated users to achieve remote code
execution. All rendering now goes through a single shared
RenderSandboxedTemplate() helper backed by jinja2.sandbox, which denies
unsafe attribute access (e.g. __globals__, __class__) while keeping the
intended templating features working:

- POST /api/email/preview: Body and Subject (GHSA-8q4m-52pw-hfmw)
- POST /api/email/send: Body and Subject (same sink class)
- POST /api/updateWireguardConfigurationInfo (OverridePeerSettings):
  values were rendered before validation, so the payload executed
  before __validateOverridePeerSettings could reject it
- Peer.downloadPeer(): rendered configuration file includes raw
  OverridePeerSettings values; also rendered for dashboard clients
  via GetAssignedPeers

Verified: lipsum.__globals__, __class__.__mro__, TemplateReference,
cycler/joiner/namespace escape chains all raise SecurityError; normal
templates ({{ configuration.* }}, {{ peer.* }}, filters, loops) render
unchanged.

Co-authored-by: DaanSelen <80752476+DaanSelen@users.noreply.github.com>
2026-10-26 10:29:51 +01:00
DaanSelenGitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4ee6b462dc chore: bring even more dependencies up to date (#1396)
CodeQL / Analyze (javascript) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
Docker Build and Push / docker_build (push) Canceled after 0s
Docker Build and Push / docker_scan (push) Canceled after 0s
* build(deps): bump golang in /docker

Bumps golang from 1.26-alpine3.24 to 1.27-alpine3.24.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.27-alpine3.24
  dependency-type: direct:production
...

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

* build(deps): bump python in /docker

Bumps python from 3.14-alpine3.24 to 3.15-alpine3.24.

---
updated-dependencies:
- dependency-name: python
  dependency-version: 3.15-alpine3.23
  dependency-type: direct:production
...

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

* build(deps): bump sqlalchemy-utils from 0.42.1 to 0.43.0 in /src

Bumps [sqlalchemy-utils](https://github.com/kvesteri/sqlalchemy-utils) from 0.42.1 to 0.43.0.
- [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.1...0.43.0)

---
updated-dependencies:
- dependency-name: sqlalchemy-utils
  dependency-version: 0.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump bootstrap-icons in /src/static/client

Bumps [bootstrap-icons](https://github.com/twbs/icons) from 1.13.1 to 1.13.2.
- [Release notes](https://github.com/twbs/icons/releases)
- [Commits](https://github.com/twbs/icons/compare/v1.13.1...v1.13.2)

---
updated-dependencies:
- dependency-name: bootstrap-icons
  dependency-version: 1.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump pinia from 3.0.4 to 4.1.0 in /src/static/client

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

---
updated-dependencies:
- dependency-name: pinia
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* build(deps): bump @vueuse/shared in /src/static/app

Bumps [@vueuse/shared](https://github.com/vueuse/vueuse/tree/HEAD/packages/shared) from 14.4.0 to 15.0.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v15.0.0/packages/shared)

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

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

* build(deps): bump dayjs from 1.11.19 to 1.11.23 in /src/static/app

Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.11.19 to 1.11.23.
- [Release notes](https://github.com/iamkun/dayjs/releases)
- [Changelog](https://github.com/iamkun/dayjs/blob/v1.11.23/CHANGELOG.md)
- [Commits](https://github.com/iamkun/dayjs/compare/v1.11.19...v1.11.23)

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

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

* build(deps): bump cidr-tools from 12.0.2 to 13.0.4 in /src/static/app

Bumps [cidr-tools](https://github.com/silverwind/cidr-tools) from 12.0.2 to 13.0.4.
- [Release notes](https://github.com/silverwind/cidr-tools/releases)
- [Commits](https://github.com/silverwind/cidr-tools/compare/12.0.2...13.0.4)

---
updated-dependencies:
- dependency-name: cidr-tools
  dependency-version: 13.0.4
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* build(deps): bump pymysql from 1.2.0 to 1.2.3 in /src

Bumps [pymysql](https://github.com/PyMySQL/PyMySQL) from 1.2.0 to 1.2.3.
- [Release notes](https://github.com/PyMySQL/PyMySQL/releases)
- [Changelog](https://github.com/PyMySQL/PyMySQL/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PyMySQL/PyMySQL/compare/v1.2.0...v1.2.3)

---
updated-dependencies:
- dependency-name: pymysql
  dependency-version: 1.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

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

Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.2.7 to 3.3.13.
- [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.3.13/packages/language-server)

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

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

* build(deps): bump dayjs from 1.11.21 to 1.11.23 in /src/static/client

Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.11.21 to 1.11.23.
- [Release notes](https://github.com/iamkun/dayjs/releases)
- [Changelog](https://github.com/iamkun/dayjs/blob/v1.11.23/CHANGELOG.md)
- [Commits](https://github.com/iamkun/dayjs/compare/v1.11.21...v1.11.23)

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

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

* 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.28 to 2.4.30.
- [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.30/packages/language-server)

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

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

* build(deps): bump vue-router from 5.1.0 to 5.4.0 in /src/static/client

Bumps [vue-router](https://github.com/vuejs/router) from 5.1.0 to 5.4.0.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/compare/v5.1.0...v5.4.0)

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

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

* build(deps): bump axios from 1.18.0 to 1.20.0 in /src/static/client

Bumps [axios](https://github.com/axios/axios) from 1.18.0 to 1.20.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.18.0...v1.20.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump ol from 10.9.0 to 10.11.0 in /src/static/app

Bumps [ol](https://github.com/openlayers/openlayers) from 10.9.0 to 10.11.0.
- [Release notes](https://github.com/openlayers/openlayers/releases)
- [Commits](https://github.com/openlayers/openlayers/compare/v10.9.0...v10.11.0)

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

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

* build(deps-dev): bump vite from 8.0.16 to 8.3.5 in /src/static/app

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.16 to 8.3.5.
- [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/v8.3.5/packages/vite)

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

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

* build(deps): bump uuid from 14.0.0 to 14.0.3 in /src/static/app

Bumps [uuid](https://github.com/uuidjs/uuid) from 14.0.0 to 14.0.3.
- [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/v14.0.0...v14.0.3)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 14.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

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

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

---
updated-dependencies:
- dependency-name: pinia
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* build(deps): bump vue-chartjs from 5.3.3 to 5.3.4 in /src/static/app

Bumps [vue-chartjs](https://github.com/apertureless/vue-chartjs) from 5.3.3 to 5.3.4.
- [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.3...v5.3.4)

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

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

* build(deps): bump bootstrap-icons in /src/static/app

Bumps [bootstrap-icons](https://github.com/twbs/icons) from 1.13.1 to 1.13.2.
- [Release notes](https://github.com/twbs/icons/releases)
- [Commits](https://github.com/twbs/icons/compare/v1.13.1...v1.13.2)

---
updated-dependencies:
- dependency-name: bootstrap-icons
  dependency-version: 1.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump npm from 11.13.0 to 12.2.1 in /src/static/app

Bumps [npm](https://github.com/npm/cli) from 11.13.0 to 12.2.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.13.0...v12.2.1)

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

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

* 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.7.0 to 26.18.0.
- [Release notes](https://github.com/electron-userland/electron-builder/releases)
- [Changelog](https://github.com/electron-userland/electron-builder/blob/electron-builder@26.18.0/packages/electron-builder/CHANGELOG.md)
- [Commits](https://github.com/electron-userland/electron-builder/commits/electron-builder@26.18.0/packages/electron-builder)

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

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

* chore: remove arm/v7 due to deprecation

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-26 00:54:45 +01:00
DaanSelenandGitHub e27dd986bc feat: rebuild frontend on every commit (#1383)
* feat: rebuild frontend on every commit

* chore: revert to latest
2026-10-26 00:26:03 +01:00
DaanSelenGitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e6448f2ffd chore: bump all dependencies (#1371)
CodeQL / Analyze (javascript) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
Docker Build and Push / docker_build (push) Canceled after 0s
Docker Build and Push / docker_scan (push) Canceled after 0s
* build(deps): bump psycopg from 3.3.4 to 3.3.6 in /src

Bumps [psycopg](https://github.com/psycopg/psycopg) from 3.3.4 to 3.3.6.
- [Changelog](https://github.com/psycopg/psycopg/blob/master/docs/news.rst)
- [Commits](https://github.com/psycopg/psycopg/compare/3.3.4...3.3.6)

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

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

* build(deps): bump golang in /docker

Bumps golang from 1.26-alpine3.23 to 1.27-alpine3.23.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.27-alpine3.23
  dependency-type: direct:production
...

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

* build(deps): bump pydantic from 2.13.4 to 2.14.1 in /src

Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.13.4 to 2.14.1.
- [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.13.4...v2.14.1)

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

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

* build(deps): bump vue from 3.5.35 to 3.5.44 in /src/static/client

Bumps [vue](https://github.com/vuejs/core) from 3.5.35 to 3.5.44.
- [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.35...v3.5.44)

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

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

* build(deps-dev): bump vite-plugin-vue-devtools in /src/static/client

Bumps [vite-plugin-vue-devtools](https://github.com/vuejs/devtools/tree/HEAD/packages/vite) from 8.1.2 to 8.2.1.
- [Release notes](https://github.com/vuejs/devtools/releases)
- [Commits](https://github.com/vuejs/devtools/commits/v8.2.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite-plugin-vue-devtools
  dependency-version: 8.1.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

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

* build(deps): bump is-cidr from 6.0.3 to 7.0.3 in /src/static/app

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

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

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

* build(deps): bump tzlocal from 5.3.1 to 5.4.5 in /src

Bumps [tzlocal](https://github.com/regebro/tzlocal) from 5.3.1 to 5.4.5.
- [Changelog](https://github.com/regebro/tzlocal/blob/master/CHANGES.txt)
- [Commits](https://github.com/regebro/tzlocal/compare/5.3.1...5.4.5)

---
updated-dependencies:
- dependency-name: tzlocal
  dependency-version: 5.4.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump gunicorn from 26.0.0 to 26.2.0 in /src

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

---
updated-dependencies:
- dependency-name: gunicorn
  dependency-version: 26.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump fuse.js from 7.3.0 to 7.5.0 in /src/static/app

Bumps [fuse.js](https://github.com/krisk/Fuse) from 7.3.0 to 7.5.0.
- [Release notes](https://github.com/krisk/Fuse/releases)
- [Changelog](https://github.com/krisk/Fuse/blob/main/CHANGELOG.md)
- [Commits](https://github.com/krisk/Fuse/compare/v7.3.0...v7.5.0)

---
updated-dependencies:
- dependency-name: fuse.js
  dependency-version: 7.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

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

Bumps [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue) from 6.0.6 to 6.0.10.
- [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.10/packages/plugin-vue)

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

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

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

Bumps [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue) from 6.0.6 to 6.0.10.
- [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.10/packages/plugin-vue)

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

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

* build(deps): bump sqlalchemy from 2.0.51 to 2.1.6 in /src

Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.51 to 2.1.6.
- [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.52
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump uuid from 14.0.0 to 14.0.3 in /src/static/client

Bumps [uuid](https://github.com/uuidjs/uuid) from 14.0.0 to 14.0.3.
- [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/v14.0.0...v14.0.3)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 14.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump vue-router from 5.0.6 to 5.4.0 in /src/static/app

Bumps [vue-router](https://github.com/vuejs/router) from 5.0.6 to 5.4.0.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/compare/v5.0.6...v5.4.0)

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

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

* build(deps): bump @vueuse/core from 14.3.0 to 15.0.0 in /src/static/app

Bumps [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) from 14.3.0 to 15.0.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v15.0.0/packages/core)

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

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

* build(deps-dev): bump vite from 8.0.16 to 8.3.5 in /src/static/client

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.16 to 8.3.5.
- [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/v8.3.5/packages/vite)

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

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-25 23:35:26 +01:00
DaanSelenandGitHub e2b5a59291 chore: remove legacy (#1372) 2026-10-25 23:34:44 +01:00
+2 b83c0f85ba merge Development into main to bring it up (#1326)
* build(deps): bump sqlalchemy from 2.0.46 to 2.0.49 in /src

Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.46 to 2.0.49.
- [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.49
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump ol from 10.7.0 to 10.8.0 in /src/static/app

Bumps [ol](https://github.com/openlayers/openlayers) from 10.7.0 to 10.8.0.
- [Release notes](https://github.com/openlayers/openlayers/releases)
- [Commits](https://github.com/openlayers/openlayers/compare/v10.7.0...v10.8.0)

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

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

* build(deps): bump vue from 3.5.31 to 3.5.32 in /src/static/app

Bumps [vue](https://github.com/vuejs/core) from 3.5.31 to 3.5.32.
- [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.31...v3.5.32)

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

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

* build(deps): bump fuse.js from 7.1.0 to 7.3.0 in /src/static/app

Bumps [fuse.js](https://github.com/krisk/Fuse) from 7.1.0 to 7.3.0.
- [Release notes](https://github.com/krisk/Fuse/releases)
- [Changelog](https://github.com/krisk/Fuse/blob/main/CHANGELOG.md)
- [Commits](https://github.com/krisk/Fuse/compare/v7.1.0...v7.3.0)

---
updated-dependencies:
- dependency-name: fuse.js
  dependency-version: 7.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

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

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.3.1 to 8.0.8.
- [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/v8.0.8/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.8
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

* build(deps-dev): bump vite from 6.4.1 to 8.0.8 in /src/static/client

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.4.1 to 8.0.8.
- [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/v8.0.8/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.8
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

* build(deps): bump cidr-tools from 11.3.2 to 11.3.3 in /src/static/app

Bumps [cidr-tools](https://github.com/silverwind/cidr-tools) from 11.3.2 to 11.3.3.
- [Release notes](https://github.com/silverwind/cidr-tools/releases)
- [Commits](https://github.com/silverwind/cidr-tools/compare/11.3.2...11.3.3)

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

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

* build(deps): bump uuid from 11.1.0 to 13.0.0 in /src/static/client

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>

* build(deps): bump oidc-client-ts in /src/static/client

Bumps [oidc-client-ts](https://github.com/authts/oidc-client-ts) from 3.2.1 to 3.5.0.
- [Release notes](https://github.com/authts/oidc-client-ts/releases)
- [Commits](https://github.com/authts/oidc-client-ts/compare/v3.2.1...v3.5.0)

---
updated-dependencies:
- dependency-name: oidc-client-ts
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump pydantic from 2.12.5 to 2.13.0 in /src

Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.5 to 2.13.0.
- [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.5...v2.13.0)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump vue from 3.5.16 to 3.5.32 in /src/static/client

Bumps [vue](https://github.com/vuejs/core) from 3.5.16 to 3.5.32.
- [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.16...v3.5.32)

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

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

* fix: misspell (#1231)

* build(deps-dev): bump vite-plugin-vue-devtools in /src/static/client

Bumps [vite-plugin-vue-devtools](https://github.com/vuejs/devtools/tree/HEAD/packages/vite) from 7.7.6 to 8.1.1.
- [Release notes](https://github.com/vuejs/devtools/releases)
- [Commits](https://github.com/vuejs/devtools/commits/v8.1.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite-plugin-vue-devtools
  dependency-version: 8.1.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

* build(deps-dev): bump vite-plugin-vue-devtools in /src/static/client

Bumps [vite-plugin-vue-devtools](https://github.com/vuejs/devtools/tree/HEAD/packages/vite) from 7.7.6 to 8.1.1.
- [Release notes](https://github.com/vuejs/devtools/releases)
- [Commits](https://github.com/vuejs/devtools/commits/v8.1.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite-plugin-vue-devtools
  dependency-version: 8.1.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

* build(deps): bump vue from 3.5.16 to 3.5.32 in /src/static/client

Bumps [vue](https://github.com/vuejs/core) from 3.5.16 to 3.5.32.
- [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.16...v3.5.32)

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

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

* build(deps): bump pydantic from 2.12.5 to 2.13.0 in /src

Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.5 to 2.13.0.
- [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.5...v2.13.0)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump sqlalchemy from 2.0.46 to 2.0.49 in /src

Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.46 to 2.0.49.
- [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.49
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* feat: commit PR 1237 to development

* Add preshared key default toggle  (#1207)

* Add previous PR changes

* Import lowercase dayjs

* Build dist

* Revert "Build dist"

This reverts commit 152da2ca10
Dist not to be built at @DaanSelen request

* build(deps): bump pydantic from 2.13.0 to 2.13.3 in /src

Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.13.0 to 2.13.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.13.0...v2.13.3)

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

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

* build(deps): bump uuid from 13.0.0 to 14.0.0 in /src/static/app

Bumps [uuid](https://github.com/uuidjs/uuid) from 13.0.0 to 14.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/v13.0.0...v14.0.0)

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

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

* build(deps-dev): bump vite from 8.0.8 to 8.0.9 in /src/static/app

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.8 to 8.0.9.
- [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/v8.0.9/packages/vite)

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

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

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

Bumps [@vue/language-server](https://github.com/vuejs/language-tools/tree/HEAD/packages/language-server) from 3.2.4 to 3.2.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.2.7/packages/language-server)

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

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

* build(deps): bump ol from 10.8.0 to 10.9.0 in /src/static/app

Bumps [ol](https://github.com/openlayers/openlayers) from 10.8.0 to 10.9.0.
- [Release notes](https://github.com/openlayers/openlayers/releases)
- [Commits](https://github.com/openlayers/openlayers/compare/v10.8.0...v10.9.0)

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

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

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

Bumps [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue) from 5.2.4 to 6.0.6.
- [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.6/packages/plugin-vue)

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

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

* build(deps): bump axios from 1.14.0 to 1.15.1 in /src/static/client

Bumps [axios](https://github.com/axios/axios) from 1.14.0 to 1.15.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.14.0...v1.15.1)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.15.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump uuid from 13.0.0 to 14.0.0 in /src/static/client

Bumps [uuid](https://github.com/uuidjs/uuid) from 13.0.0 to 14.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/v13.0.0...v14.0.0)

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

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

* build(deps): bump pinia from 3.0.2 to 3.0.4 in /src/static/client

Bumps [pinia](https://github.com/vuejs/pinia) from 3.0.2 to 3.0.4.
- [Release notes](https://github.com/vuejs/pinia/releases)
- [Commits](https://github.com/vuejs/pinia/compare/v3.0.2...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>

* Refactor peer schedule job API to use proper REST verbs (#1239)

* Fixed quotation marks

* Update wgd.sh

* Refactor peer schedule job API to use proper REST verbs

Replace single POST endpoints with POST/PUT/DELETE for peer schedule jobs.
Add require_fields decorator for request validation. Add fetchPut and
fetchDelete helpers in the frontend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add local development with Docker section to docker/README.md

Explains how to mount src/ as a volume for live code editing
using a compose-local.yaml setup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Update README dev section with frontend build instructions

Replace Vite dev server tip with npm install/build workflow
and docker restart step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Donald Zou <donaldzou@live.hk>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* A patch to support a wide range of AmneziaWG versions. (#1210)

Support for all keys ever used in AmneziaWG has been added. Setting an empty value to a key will prevent it from being used or rendered in the configuration. This ensures flexible support for all versions and specific configurations.

* Fix config info loss when renaming a tunnel. (#1254)

This migrates data to the new tunnel when renaming, such as: Description, OverridePeerSettings, PeerGroups, PeerHistoricalEndpointTracking, PeerTrafficTracking.

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

Bumps [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue) from 6.0.4 to 6.0.6.
- [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.6/packages/plugin-vue)

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

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

* build(deps): bump bootstrap from 5.3.6 to 5.3.8 in /src/static/client

Bumps [bootstrap](https://github.com/twbs/bootstrap) from 5.3.6 to 5.3.8.
- [Release notes](https://github.com/twbs/bootstrap/releases)
- [Commits](https://github.com/twbs/bootstrap/compare/v5.3.6...v5.3.8)

---
updated-dependencies:
- dependency-name: bootstrap
  dependency-version: 5.3.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* Fix TOTP verification with valid window

* build(deps): bump pymysql from 1.1.2 to 1.1.3 in /src

Bumps [pymysql](https://github.com/PyMySQL/PyMySQL) from 1.1.2 to 1.1.3.
- [Release notes](https://github.com/PyMySQL/PyMySQL/releases)
- [Changelog](https://github.com/PyMySQL/PyMySQL/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PyMySQL/PyMySQL/compare/v1.1.2...v1.1.3)

---
updated-dependencies:
- dependency-name: pymysql
  dependency-version: 1.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump psycopg from 3.3.3 to 3.3.4 in /src

Bumps [psycopg](https://github.com/psycopg/psycopg) from 3.3.3 to 3.3.4.
- [Changelog](https://github.com/psycopg/psycopg/blob/master/docs/news.rst)
- [Commits](https://github.com/psycopg/psycopg/compare/3.3.3...3.3.4)

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

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

* build(deps): bump npm from 11.12.1 to 11.13.0 in /src/static/app

Bumps [npm](https://github.com/npm/cli) from 11.12.1 to 11.13.0.
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/v11.13.0/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v11.12.1...v11.13.0)

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

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

* build(deps): bump @vueuse/shared in /src/static/app

Bumps [@vueuse/shared](https://github.com/vueuse/vueuse/tree/HEAD/packages/shared) from 14.2.1 to 14.3.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v14.3.0/packages/shared)

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

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

* build(deps): bump vue-router from 5.0.4 to 5.0.6 in /src/static/app

Bumps [vue-router](https://github.com/vuejs/router) from 5.0.4 to 5.0.6.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/compare/v5.0.4...v5.0.6)

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

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

* build(deps-dev): bump vite from 8.0.9 to 8.0.10 in /src/static/app

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.9 to 8.0.10.
- [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/v8.0.10/packages/vite)

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

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

* build(deps): bump cidr-tools from 11.3.3 to 11.3.5 in /src/static/app

Bumps [cidr-tools](https://github.com/silverwind/cidr-tools) from 11.3.3 to 11.3.5.
- [Release notes](https://github.com/silverwind/cidr-tools/releases)
- [Commits](https://github.com/silverwind/cidr-tools/compare/11.3.3...11.3.5)

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

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

* build(deps): bump axios from 1.15.1 to 1.16.0 in /src/static/client

Bumps [axios](https://github.com/axios/axios) from 1.15.1 to 1.16.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.15.1...v1.16.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* change email attachmentname to peerName if not empty

* Use network_address for single-address IPs

When parsing allowed_ip entries in API_ping_getAllPeersIpAddress(), detect networks with a single address (ip.num_addresses == 1) and append ip.network_address directly to the parsed list. This replaces the previous host-list construction (list(ip.hosts()) / host[0]) to simplify logic and avoid iterating hosts for /32 or single-address ranges while keeping existing error logging for invalid addresses.

* build(deps): bump requests from 2.32.5 to 2.34.0 in /src

Bumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.34.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.34.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump gunicorn from 25.0.3 to 26.0.0 in /src

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

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

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

* build(deps): bump pydantic from 2.13.3 to 2.13.4 in /src

Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.13.3 to 2.13.4.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/v2.13.4/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.13.3...v2.13.4)

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

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

* build(deps-dev): bump vite from 8.0.8 to 8.0.12 in /src/static/client

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.8 to 8.0.12.
- [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/v8.0.12/packages/vite)

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

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

* Fix Python 3.11 SyntaxError in AmneziaPeer.py (#1290)

Python 3.11 raises 'f-string expression part cannot include a backslash'
on line 91, where strip('\n') sits inside an f-string {...}. PEP 701
lifted this restriction in 3.12.

Bind the decoded output to a local so the strip call happens outside
the f-string, which also removes the duplicate decode+strip already
performed on the preceding length check.

Closes #1289

Signed-off-by: Truffle <truffleagent@gmail.com>

* build(deps): bump vue from 3.5.32 to 3.5.35 in /src/static/client

Bumps [vue](https://github.com/vuejs/core) from 3.5.32 to 3.5.35.
- [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.32...v3.5.35)

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

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

* build(deps-dev): bump vite-plugin-vue-devtools in /src/static/client

Bumps [vite-plugin-vue-devtools](https://github.com/vuejs/devtools/tree/HEAD/packages/vite) from 8.1.1 to 8.1.2.
- [Release notes](https://github.com/vuejs/devtools/releases)
- [Commits](https://github.com/vuejs/devtools/commits/v8.1.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite-plugin-vue-devtools
  dependency-version: 8.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

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

* build(deps): bump vue-router from 4.5.1 to 5.1.0 in /src/static/client

Bumps [vue-router](https://github.com/vuejs/router) from 4.5.1 to 5.1.0.
- [Release notes](https://github.com/vuejs/router/releases)
- [Commits](https://github.com/vuejs/router/compare/v4.5.1...v5.1.0)

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

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

* build(deps): bump requests from 2.34.0 to 2.34.2 in /src

Bumps [requests](https://github.com/psf/requests) from 2.34.0 to 2.34.2.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.34.0...v2.34.2)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.34.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump @vuepic/vue-datepicker in /src/static/app

Bumps [@vuepic/vue-datepicker](https://github.com/Vuepic/vue-datepicker) from 12.1.0 to 14.0.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.1.0...v14.0.0)

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

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

* build(deps): bump flask-cors from 6.0.2 to 6.0.5 in /src

Bumps [flask-cors](https://github.com/corydolphin/flask-cors) from 6.0.2 to 6.0.5.
- [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.2...6.0.5)

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

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

* build(deps): bump pymysql from 1.1.3 to 1.2.0 in /src

Bumps [pymysql](https://github.com/PyMySQL/PyMySQL) from 1.1.3 to 1.2.0.
- [Release notes](https://github.com/PyMySQL/PyMySQL/releases)
- [Changelog](https://github.com/PyMySQL/PyMySQL/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PyMySQL/PyMySQL/compare/v1.1.3...v1.2.0)

---
updated-dependencies:
- dependency-name: pymysql
  dependency-version: 1.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps-dev): bump vite from 8.0.10 to 8.0.16 in /src/static/app

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.10 to 8.0.16.
- [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/v8.0.16/packages/vite)

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

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

* build(deps): bump cidr-tools from 11.3.5 to 12.0.2 in /src/static/app

Bumps [cidr-tools](https://github.com/silverwind/cidr-tools) from 11.3.5 to 12.0.2.
- [Release notes](https://github.com/silverwind/cidr-tools/releases)
- [Commits](https://github.com/silverwind/cidr-tools/compare/11.3.5...12.0.2)

---
updated-dependencies:
- dependency-name: cidr-tools
  dependency-version: 12.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* build(deps-dev): bump vite from 8.0.12 to 8.0.16 in /src/static/client

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.12 to 8.0.16.
- [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/v8.0.16/packages/vite)

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

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

* build(deps): bump dayjs from 1.11.20 to 1.11.21 in /src/static/client

Bumps [dayjs](https://github.com/iamkun/dayjs) from 1.11.20 to 1.11.21.
- [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.20...v1.11.21)

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

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

* build(deps): bump pyotp from 2.9.0 to 2.10.0 in /src

Bumps [pyotp](https://github.com/pyauth/pyotp) from 2.9.0 to 2.10.0.
- [Release notes](https://github.com/pyauth/pyotp/releases)
- [Changelog](https://github.com/pyauth/pyotp/blob/main/Changes.rst)
- [Commits](https://github.com/pyauth/pyotp/compare/v2.9.0...v2.10.0)

---
updated-dependencies:
- dependency-name: pyotp
  dependency-version: 2.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump alpine from 3.23 to 3.24 in /docker

Bumps alpine from 3.23 to 3.24.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: '3.24'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* build(deps): bump sqlalchemy from 2.0.49 to 2.0.51 in /src

Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.49 to 2.0.51.
- [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.51
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* build(deps): bump axios from 1.16.0 to 1.18.0 in /src/static/client

Bumps [axios](https://github.com/axios/axios) from 1.16.0 to 1.18.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.16.0...v1.18.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* Added OIDC for admin side

* Updated OIDC Algorithm Fallback

* chore: remove dist

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Truffle <truffleagent@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Daan Selen <dselen@nerthus.nl>
Co-authored-by: neolegshishov <143963760+neolegshishov@users.noreply.github.com>
Co-authored-by: Tom Mills <accounts+github@tomjmills.co.uk>
Co-authored-by: Giuseppe Marinelli <35118227+BeppeMarnell@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: freetushkan <freetushkan@users.noreply.github.com>
Co-authored-by: sneds91 <sneds91@gmail.com>
Co-authored-by: sgtdeagle <46221457+sgtdeagle@users.noreply.github.com>
Co-authored-by: Truffle <truffleagent@gmail.com>
2026-10-25 22:53:58 +01:00
Evgeny VolfertsandGitHub b6da11ec2d fix: use database dialect for configuration backup and restore (#1370) 2026-10-25 22:32:12 +01:00
MaxandGitHub 58fa826862 Update README.md for Docker (#1292)
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-06-05 18:54:35 +02:00
Mikhail SolovevandGitHub fedf7db8a4 Quote table and column identifiers using SQLAlchemy dialect preparer when adding missing columns to avoid SQL injection and syntax errors. (#1237)
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
2026-04-16 20:56:16 +02:00
166 changed files with 5009 additions and 5897 deletions
+8 -3
View File
@@ -1,9 +1,14 @@
.git
.github
*.md
tests/
docs/
*.md
**/node_modules
**/venv
**/__pycache__
**/*.pyc
src/db
src/wg-dashboard.ini
src/static/app
src/static/client
+2 -2
View File
@@ -49,7 +49,7 @@ 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
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -74,7 +74,7 @@ jobs:
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
platforms: linux/amd64,linux/arm64
docker_scan:
if: ${{ github.event_name != 'pull_request' }}
+2
View File
@@ -32,6 +32,8 @@ yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
dist
node_modules
.DS_Store
dist-ssr
-76
View File
@@ -1,76 +0,0 @@
FROM golang:1.24 AS awg-go
RUN git clone https://github.com/WGDashboard/amneziawg-go /awg
WORKDIR /awg
RUN go mod download && \
go mod verify && \
go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"' -v -o /usr/bin
FROM alpine:latest AS awg-tools
RUN apk update && apk add --no-cache \
make git build-base linux-headers \
&& git clone https://github.com/WGDashboard/amneziawg-tools \
&& cd amneziawg-tools/src \
&& make \
&& chmod +x wg*
FROM alpine:latest
LABEL maintainer="dselen@nerthus.nl"
RUN apk update && apk add --no-cache \
iproute2 iptables bash curl wget unzip procps sudo \
tzdata wireguard-tools python3 py3-psutil py3-bcrypt openresolv
COPY --from=awg-go /usr/bin/amneziawg-go /usr/bin/amneziawg-go
COPY --from=awg-tools /amneziawg-tools/src/wg /usr/bin/awg
COPY --from=awg-tools /amneziawg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick
# Declaring environment variables, change Peernet to an address you like, standard is a 24 bit subnet.
ARG wg_net="10.0.0.1" \
wg_port="51820"
# Following ENV variables are changable on container runtime because /entrypoint.sh handles that. See compose.yaml for more info.
ENV TZ="Europe/Amsterdam" \
global_dns="9.9.9.9" \
wgd_port="10086" \
public_ip=""
# Using WGDASH -- like wg_net functionally as a ARG command. But it is needed in entrypoint.sh so it needs to be exported as environment variable.
ENV WGDASH=/opt/wgdashboard
# Doing WireGuard Dashboard installation measures. Modify the git clone command to get the preferred version, with a specific branch for example.
RUN mkdir /data \
&& mkdir /configs \
&& mkdir -p ${WGDASH}/src \
&& mkdir -p /etc/amnezia/amneziawg
COPY ./src ${WGDASH}/src
# Generate basic WireGuard interface. Echoing the WireGuard interface config for readability, adjust if you want it for efficiency.
# Also setting the pipefail option, verbose: https://github.com/hadolint/hadolint/wiki/DL4006.
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
# Defining a way for Docker to check the health of the container. In this case: checking the gunicorn process.
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
# Copy the basic entrypoint.sh script.
COPY ./docker/entrypoint.sh /entrypoint.sh
# Exposing the default WireGuard Dashboard port for web access.
EXPOSE 10086
WORKDIR $WGDASH
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
+42 -9
View File
@@ -4,7 +4,7 @@
#
# Pull the current golang-alpine image.
FROM golang:1.26-alpine3.23 AS awg-go
FROM golang:1.27-alpine3.24 AS awg-go-builder
# Install build-dependencies.
RUN apk add --no-cache \
@@ -30,7 +30,7 @@ RUN go version && \
# AWG TOOLS BUILDING STAGE
# Base: Alpine
#
FROM alpine:3.23 AS awg-tools
FROM alpine:3.24 AS awg-tools-builder
# Install needed dependencies.
RUN apk add --no-cache \
@@ -55,7 +55,7 @@ RUN make && chmod +x wg*
#
# Use the python-alpine image for building pip dependencies
FROM python:3.14-alpine3.23 AS pip-builder
FROM python:3.15-alpine3.24 AS pip-builder
ARG TARGETPLATFORM
@@ -85,13 +85,32 @@ RUN . /opt/wgdashboard/src/venv/bin/activate && \
pip3 install --upgrade pip && \
pip3 install -r /opt/wgdashboard/src/requirements.txt
#
# WGDashboard FRONTEND BUILDING
# Base: Node Alpine
#
FROM node:26-alpine3.24 AS frontend-builder
WORKDIR /workspace
# Copy frontend source, package files and lockfiles
COPY ./src/static/ ./
# Build the admin frontend
WORKDIR /workspace/app
RUN npm ci && npm run build
# Build the client frontend
WORKDIR /workspace/client
RUN npm ci && npm run build
#
# WGDashboard RUNNING STAGE
# Base: Alpine
#
# Running with the python-alpine image.
FROM python:3.14-alpine3.23 AS final
FROM python:3.15-alpine3.24 AS final
LABEL maintainer="dselen@nerthus.nl"
# Install only the runtime dependencies
@@ -102,9 +121,9 @@ RUN apk add --no-cache \
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# 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
COPY --from=awg-go-builder /usr/bin/amneziawg-go /usr/bin/amneziawg-go
COPY --from=awg-tools-builder /workspace/awg-tools/src/wg /usr/bin/awg
COPY --from=awg-tools-builder /workspace/awg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick
# Environment variables
ARG wg_net="10.0.0.1"
@@ -121,11 +140,25 @@ ENV TZ="Europe/Amsterdam" \
RUN mkdir /data /configs -p ${WGDASH}/src /etc/amnezia/amneziawg \
&& echo "name_servers=${global_dns}" >> /etc/resolvconf.conf
# Copy the venv and source files from local compiled locations or repos
# Copy application source
COPY ./src ${WGDASH}/src
COPY --from=pip-builder /opt/wgdashboard/src/venv /opt/wgdashboard/src/venv
# Copy Python dependencies
COPY --from=pip-builder \
/opt/wgdashboard/src/venv \
${WGDASH}/src/venv
RUN rm -rf ${WGDASH}/src/static/dist \
${WGDASH}/src/static/app \
${WGDASH}/src/static/client
COPY --from=frontend-builder \
/workspace/dist/ \
${WGDASH}/src/static/dist/
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
+70 -1
View File
@@ -105,7 +105,7 @@ Updating the WGDashboard container should be through 'The Docker Way' - by pulli
| `username` | Any non‐empty string | `-` | `admin` | Username for the WGDashboard web interface account. |
| `password` | Any non‐empty string | `-` | `s3cr3tP@ss` | Password for the WGDashboard web interface account (stored hashed). |
| `enable_totp` | `true`, `false` | `true` | `false` | Enable TOTP‐based two‐factor authentication for the account. |
| `wg_autostart` | Wireguard interface name | `false` | `true` | Auto‐start the WireGuard client when the container launches. |
| `wg_autostart` | Wireguard interface name | `- ` | `wg0` | Automatically start the specified WireGuard interface when the container starts |
| `email_server` | SMTP server address | `-` | `smtp.gmail.com` | SMTP server for sending email notifications. |
| `email_port` | SMTP port number | `-` | `587` | Port for connecting to the SMTP server. |
| `email_encryption` | `TLS`, `SSL`, etc. | `-` | `TLS` | Encryption method for email communication. |
@@ -213,6 +213,75 @@ ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
- Access the web interface via `http://your-ip:10086` (or whichever port you specified in the compose).
- The first time run will auto-generate WireGuard keys and configs (configs are generated from the template).
## 🧑‍💻 Local Development with Docker
You can develop against WGDashboard locally by mounting the `src/` directory into the container. This lets you edit Python and frontend code on your host and see changes reflected immediately (with a service restart for Python).
Create a `docker/compose-local.yaml` alongside the existing `compose.yaml`:
```yaml
services:
wgdashboard:
image: ghcr.io/wgdashboard/wgdashboard:latest
restart: unless-stopped
container_name: wgdashboard
ports:
- 10086:10086/tcp
- 51820:51820/udp
volumes:
- aconf:/etc/amnezia/amneziawg
- conf:/etc/wireguard
- data:/data
# Mount local src for live editing
- ../src:/opt/wgdashboard/src
# Keep venv in a named volume so it isn't overwritten by the mount
- venv:/opt/wgdashboard/src/venv
cap_add:
- NET_ADMIN
volumes:
aconf:
conf:
data:
venv:
```
The key additions compared to the production compose file:
- `../src:/opt/wgdashboard/src` — mounts your local `src/` directory into the container so code changes are reflected without rebuilding the image.
- `venv:/opt/wgdashboard/src/venv` — keeps the Python virtual environment in a named Docker volume. Without this, the host mount would overwrite the venv created during image build.
To start the development container:
```bash
cd docker
docker compose -f compose-local.yaml up -d
```
After editing Python files (e.g. `src/dashboard.py`), restart the container to pick up changes:
```bash
docker restart wgdashboard
```
For frontend changes, install dependencies and rebuild the Vue app on your host:
```bash
cd src/static/app
npm install
npm run build
```
Then restart the container so it serves the updated dist files:
```bash
docker restart wgdashboard
```
---
## Closing remarks:
For feedback please submit an issue to the repository. Or message dselen@nerthus.nl.
+1 -1
View File
@@ -100,7 +100,7 @@ def createClientBlueprint(wireguardConfigurations: dict[WireguardConfiguration],
date = datetime.datetime.now(tz=datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')
emailSender = EmailSender(dashboardConfig)
if not emailSender.ready():
if not emailSender.is_ready():
return ResponseObject(False, "We can't send you an email due to your Administrator has not setup email service. Please contact your administrator.")
data = request.get_json()
+191 -74
View File
@@ -2,12 +2,13 @@ import logging
import random, shutil, sqlite3, configparser, hashlib, ipaddress, json, os, secrets, subprocess
import time, re, uuid, bcrypt, psutil, pyotp, threading
import traceback
from functools import wraps
from urllib.parse import unquote
from uuid import uuid4
from zipfile import ZipFile
from datetime import datetime, timedelta
import sqlalchemy
from jinja2 import Template
from flask import Flask, request, render_template, session, send_file, current_app
from flask_cors import CORS
from icmplib import ping, traceroute
@@ -18,7 +19,8 @@ from sqlalchemy import RowMapping
from modules.Utilities import (
RegexMatch, StringToBoolean, ValidateDNSAddress,
GenerateWireguardPublicKey, GenerateWireguardPrivateKey
GenerateWireguardPublicKey, GenerateWireguardPrivateKey,
RenderSandboxedTemplate
)
from packaging import version
from modules.Email import EmailSender
@@ -30,6 +32,7 @@ from modules.PeerJobs import PeerJobs
from modules.DashboardConfig import DashboardConfig
from modules.WireguardConfiguration import WireguardConfiguration
from modules.AmneziaConfiguration import AmneziaConfiguration
from modules.DashboardOIDC import DashboardOIDC
from client import createClientBlueprint
@@ -68,6 +71,21 @@ def ResponseObject(status=True, message=None, data=None, status_code = 200) -> F
response.content_type = "application/json"
return response
def require_fields(*fields):
"""Decorator that validates required fields in request.json."""
def decorator(f):
@wraps(f)
def decorated(*args, **kwargs):
data = request.json
if data is None:
return ResponseObject(False, "Request body must be JSON", status_code=400)
missing = [field for field in fields if field not in data]
if missing:
return ResponseObject(False, f"Missing required fields: {', '.join(missing)}", status_code=400)
return f(*args, **kwargs)
return decorated
return decorator
'''
Flask App
'''
@@ -208,6 +226,7 @@ with app.app_context():
NewConfigurationTemplates: NewConfigurationTemplates = NewConfigurationTemplates()
InitWireguardConfigurationsList(startup=True)
DashboardClients: DashboardClients = DashboardClients(WireguardConfigurations)
AdminOIDC = DashboardOIDC("Admin")
app.register_blueprint(createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients))
_, APP_PREFIX = DashboardConfig.GetConfig("Server", "app_prefix")
@@ -262,6 +281,8 @@ def auth_req():
f'{appPrefix}/api/sharePeer/get',
f'{appPrefix}/api/isTotpEnabled',
f'{appPrefix}/api/locale',
f'{appPrefix}/api/oidc/providers',
f'{appPrefix}/api/oidc/authenticate',
]
@@ -299,6 +320,38 @@ def API_ValidateAuthentication():
def API_RequireAuthentication():
return ResponseObject(data=DashboardConfig.GetConfig("Server", "auth_req")[1])
# OIDC for Admin
@app.get(f'{APP_PREFIX}/api/oidc/providers')
def API_OIDC_GetProviders():
_, oidc = DashboardConfig.GetConfig("OIDC", "admin_enable")
if not oidc:
return ResponseObject(status=False, message="OIDC is disabled")
return ResponseObject(data=AdminOIDC.GetProviders())
@app.post(f'{APP_PREFIX}/api/oidc/authenticate')
def API_OIDC_Authenticate():
_, oidc = DashboardConfig.GetConfig("OIDC", "admin_enable")
if not oidc:
return ResponseObject(False, "OIDC is disabled")
requestData = request.get_json()
status, data = AdminOIDC.VerifyToken(**requestData)
if not status:
return ResponseObject(False, "OIDC Authentication Failed. Reason: " + data)
session['role'] = 'admin'
authToken = hashlib.sha256(f'${data['sid']}{datetime.now()}{app.secret_key}'.encode()).hexdigest()
session['username'] = authToken
session['signInMethod'] = 'OIDC'
session['signInPayload'] = {
"Provider": requestData.get('provider'),
"Payload": data
}
resp = ResponseObject()
resp.set_cookie('authToken', authToken)
return resp
@app.post(f'{APP_PREFIX}/api/authenticate')
def API_AuthenticateLogin():
data = request.get_json()
@@ -318,7 +371,10 @@ def API_AuthenticateLogin():
totpEnabled = DashboardConfig.GetConfig("Account", "enable_totp")[1]
totpValid = False
if totpEnabled:
totpValid = pyotp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).now() == data['totp']
totp_code = str(data.get("totp", "")).strip()
totpValid = pyotp.TOTP(
DashboardConfig.GetConfig("Account", "totp_key")[1]
).verify(totp_code, valid_window=1)
if (valid
and data['username'] == DashboardConfig.GetConfig("Account", "username")[1]
@@ -327,6 +383,7 @@ def API_AuthenticateLogin():
authToken = hashlib.sha256(f"{data['username']}{datetime.now()}".encode()).hexdigest()
session['role'] = 'admin'
session['username'] = authToken
session['signInMethod'] = 'local'
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
resp.set_cookie("authToken", authToken)
session.permanent = True
@@ -342,6 +399,14 @@ def API_AuthenticateLogin():
def API_SignOut():
resp = ResponseObject(True, "")
resp.delete_cookie("authToken")
if session.get('signInMethod') == "OIDC":
status, oidc_config = AdminOIDC.GetProviderConfiguration(session.get('signInPayload').get("Provider"))
signOut = requests.get(
oidc_config.get("end_session_endpoint"),
params={
'id_token_hint': session.get('signInPayload').get("Payload").get('sid')
}
)
session.clear()
return resp
@@ -880,8 +945,17 @@ def API_addPeers(configName):
allowed_ips: list[str] = data.get('allowed_ips', [])
allowed_ips_validation: bool = data.get('allowed_ips_validation', True)
endpoint_allowed_ip: str = data.get('endpoint_allowed_ip', DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[1])
dns_addresses: str = data.get('DNS', DashboardConfig.GetConfig("Peers", "peer_global_DNS")[1])
config = WireguardConfigurations.get(configName)
overridePeerSettings = config.configurationInfo.OverridePeerSettings
endpoint_allowed_ip: str = data.get('endpoint_allowed_ip', (
overridePeerSettings.EndpointAllowedIPs
if overridePeerSettings.EndpointAllowedIPs else DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[1]
))
dns_addresses: str = data.get('DNS', (
overridePeerSettings.DNS
if overridePeerSettings.DNS else DashboardConfig.GetConfig("Peers", "peer_global_DNS")[1]
))
mtu: int = data.get('mtu', None)
@@ -890,7 +964,10 @@ def API_addPeers(configName):
preshared_key: str = data.get('preshared_key', "")
if type(mtu) is not int or mtu < 0 or mtu > 1460:
default: str = DashboardConfig.GetConfig("Peers", "peer_mtu")[1]
default: str = (
str(overridePeerSettings.MTU)
if overridePeerSettings.MTU else DashboardConfig.GetConfig("Peers", "peer_mtu")[1]
)
if default.isnumeric():
try:
mtu = int(default)
@@ -899,7 +976,10 @@ def API_addPeers(configName):
else:
mtu = 0
if type(keep_alive) is not int or keep_alive < 0:
default = DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1]
default = (
str(overridePeerSettings.PersistentKeepalive)
if overridePeerSettings.PersistentKeepalive else DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1]
)
if default.isnumeric():
try:
keep_alive = int(default)
@@ -908,7 +988,6 @@ def API_addPeers(configName):
else:
keep_alive = 0
config = WireguardConfigurations.get(configName)
if not config.getStatus():
config.toggleConfiguration()
ipStatus, availableIps = config.getAvailableIP(-1)
@@ -975,8 +1054,7 @@ def API_addPeers(configName):
if ipStatus:
for subnet in availableIps.keys():
for ip in availableIps[subnet]:
allowed_ips = [ip]
break
allowed_ips.append(ip)
break
else:
return ResponseObject(False, "No more available IP can assign")
@@ -1075,23 +1153,36 @@ def API_GetPeerHistoricalEndpoints():
if not configurationName or not id:
return ResponseObject(False, "Please provide configurationName and id")
fp, p = WireguardConfigurations.get(configurationName).searchPeer(id)
if fp:
result = p.getEndpoints()
geo = {}
try:
r = requests.post(f"http://ip-api.com/batch?fields=city,country,lat,lon,query",
data=json.dumps([x['endpoint'] for x in result]))
d = r.json()
except Exception as e:
return ResponseObject(data=result, message="Failed to request IP address geolocation. " + str(e))
return ResponseObject(data={
"endpoints": p.getEndpoints(),
"geolocation": d
})
if not fp:
return ResponseObject(False, "Peer does not exist")
endpoints = p.getEndpoints()
ips = [x['endpoint'] for x in endpoints]
unique_ips = list(set(ips))
def chunks(lst, size=100):
for i in range(0, len(lst), size):
yield lst[i:i + size]
geo_cache = {}
try:
for batch in chunks(unique_ips, 100):
r = requests.post(
"http://ip-api.com/batch?fields=city,country,lat,lon,query",
data=json.dumps(batch),
timeout=5
)
r.raise_for_status()
data = r.json()
for item in data:
geo_cache[item["query"]] = item
geolocation = [geo_cache[ip] for ip in ips]
except Exception as e:
return ResponseObject(
data={"endpoints": endpoints},
message="Failed to request IP address geolocation. " + str(e)
)
return ResponseObject(data={
"endpoints": endpoints,
"geolocation": geolocation
})
@app.get(f'{APP_PREFIX}/api/getPeerSessions')
def API_GetPeerSessions():
@@ -1204,52 +1295,75 @@ def API_getDashboardTheme():
def API_getDashboardVersion():
return ResponseObject(data=DashboardConfig.GetConfig("Server", "version")[1])
@app.post(f'{APP_PREFIX}/api/savePeerScheduleJob')
@app.post(f'{APP_PREFIX}/api/PeerScheduleJob')
@require_fields('Configuration', 'Peer', 'Field', 'Operator', 'Value', 'Action')
def API_savePeerScheduleJob():
data = request.json
if "Job" not in data.keys():
return ResponseObject(False, "Please specify job")
job: dict = data['Job']
if "Peer" not in job.keys() or "Configuration" not in job.keys():
return ResponseObject(False, "Please specify peer and configuration")
configuration = WireguardConfigurations.get(job['Configuration'])
configuration = WireguardConfigurations.get(data['Configuration'])
if configuration is None:
return ResponseObject(False, "Configuration does not exist")
f, fp = configuration.searchPeer(job['Peer'])
if not f:
return ResponseObject(False, "Peer does not exist")
return ResponseObject(False, "Configuration does not exist", status_code=404)
peerKey = unquote(data['Peer'])
found, _ = configuration.searchPeer(peerKey)
if not found:
return ResponseObject(False, "Peer does not exist", status_code=404)
s, p = AllPeerJobs.saveJob(PeerJob(
job['JobID'], job['Configuration'], job['Peer'], job['Field'], job['Operator'], job['Value'],
job['CreationDate'], job['ExpireDate'], job['Action']))
if s:
return ResponseObject(s, data=p)
return ResponseObject(s, message=p)
jobID = data.get('JobID', str(uuid4()))
if len(AllPeerJobs.searchJobById(jobID)) > 0:
return ResponseObject(False, "Job already exists", status_code=409)
@app.post(f'{APP_PREFIX}/api/deletePeerScheduleJob')
success, result = AllPeerJobs.saveJob(PeerJob(
jobID, data['Configuration'], peerKey, data['Field'], data['Operator'], data['Value'],
datetime.now(), data.get('ExpireDate'), data['Action']))
if success:
return ResponseObject(success, data=result)
return ResponseObject(success, message=result)
@app.put(f'{APP_PREFIX}/api/PeerScheduleJob')
@require_fields('JobID', 'Configuration', 'Peer', 'Field', 'Operator', 'Value', 'Action')
def API_updatePeerScheduleJob():
data = request.json
configuration = WireguardConfigurations.get(data['Configuration'])
if configuration is None:
return ResponseObject(False, "Configuration does not exist", status_code=404)
peerKey = unquote(data['Peer'])
found, _ = configuration.searchPeer(peerKey)
if not found:
return ResponseObject(False, "Peer does not exist", status_code=404)
existing = AllPeerJobs.searchJobById(data['JobID'])
if len(existing) == 0:
return ResponseObject(False, "Job does not exist", status_code=404)
success, result = AllPeerJobs.saveJob(PeerJob(
data['JobID'], data['Configuration'], peerKey, data['Field'], data['Operator'], data['Value'],
datetime.now(), data.get('ExpireDate'), data['Action']))
if success:
return ResponseObject(success, data=result)
return ResponseObject(success, message=result)
@app.delete(f'{APP_PREFIX}/api/PeerScheduleJob')
@require_fields('JobID', 'Configuration', 'Peer')
def API_deletePeerScheduleJob():
data = request.json
if "Job" not in data.keys():
return ResponseObject(False, "Please specify job")
job: dict = data['Job']
if "Peer" not in job.keys() or "Configuration" not in job.keys():
return ResponseObject(False, "Please specify peer and configuration")
configuration = WireguardConfigurations.get(job['Configuration'])
configuration = WireguardConfigurations.get(data['Configuration'])
if configuration is None:
return ResponseObject(False, "Configuration does not exist")
# f, fp = configuration.searchPeer(job['Peer'])
# if not f:
# return ResponseObject(False, "Peer does not exist")
return ResponseObject(False, "Configuration does not exist", status_code=404)
s, p = AllPeerJobs.deleteJob(PeerJob(
job['JobID'], job['Configuration'], job['Peer'], job['Field'], job['Operator'], job['Value'],
job['CreationDate'], job['ExpireDate'], job['Action']))
if s:
return ResponseObject(s)
return ResponseObject(s, message=p)
peerKey = unquote(data['Peer'])
success, result = AllPeerJobs.deleteJob(PeerJob(
data['JobID'], data['Configuration'], peerKey, data.get('Field', ''),
data.get('Operator', ''), data.get('Value', ''),
datetime.now(), data.get('ExpireDate'), data.get('Action', '')))
if success:
return ResponseObject(success, message="Job deleted successfully")
return ResponseObject(success, message=result)
@app.get(f'{APP_PREFIX}/api/getPeerScheduleJobLogs/<configName>')
@app.get(f'{APP_PREFIX}/api/PeerScheduleJobLogs/<configName>')
def API_getPeerScheduleJobLogs(configName):
if configName not in WireguardConfigurations.keys():
return ResponseObject(False, "Configuration does not exist")
@@ -1274,11 +1388,11 @@ def API_ping_getAllPeersIpAddress():
for x in allowed_ip:
try:
ip = ipaddress.ip_network(x, strict=False)
if ip.num_addresses == 1:
parsed.append(str(ip.network_address))
except ValueError as e:
app.logger.error(f"Failed to parse IP address of {p.id} - {c.Name}")
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}"] = {
@@ -1415,11 +1529,15 @@ def API_Welcome_GetTotpLink():
@app.post(f'{APP_PREFIX}/api/Welcome_VerifyTotpLink')
def API_Welcome_VerifyTotpLink():
data = request.get_json()
totp = pyotp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).now()
if totp == data['totp']:
totp_code = str(data.get("totp", "")).strip()
totpValid = pyotp.TOTP(
DashboardConfig.GetConfig("Account", "totp_key")[1]
).verify(totp_code, valid_window=1)
if totpValid:
DashboardConfig.SetConfig("Account", "totp_verified", "true")
DashboardConfig.SetConfig("Account", "enable_totp", "true")
return ResponseObject(totp == data['totp'])
return ResponseObject(totpValid)
@app.post(f'{APP_PREFIX}/api/Welcome_Finish')
def API_Welcome_Finish():
@@ -1505,13 +1623,13 @@ def API_Email_Send():
if configuration is not None:
fp, p = configuration.searchPeer(data.get('Peer'))
if fp:
template = Template(body)
download = p.downloadPeer()
body = template.render(peer=p.toJson(), configurationFile=download)
subject = Template(data.get('Subject', '')).render(peer=p.toJson(), configurationFile=download)
body = RenderSandboxedTemplate(body, peer=p.toJson(), configurationFile=download)
subject = RenderSandboxedTemplate(data.get('Subject', ''), peer=p.toJson(), configurationFile=download)
if data.get('IncludeAttachment', False):
u = str(uuid4())
attachmentName = f'{u}.conf'
peerName = p.toJson().get('name', '').strip()
attachmentName = f'{peerName if peerName else u}.conf'
with open(os.path.join('./attachments', attachmentName,), 'w+') as f:
f.write(download['file'])
@@ -1536,11 +1654,10 @@ def API_Email_PreviewBody():
return ResponseObject(False, "Peer does not exist")
try:
template = Template(body)
download = p.downloadPeer()
return ResponseObject(data={
"Body": Template(body).render(peer=p.toJson(), configurationFile=download),
"Subject": Template(subject).render(peer=p.toJson(), configurationFile=download)
"Body": RenderSandboxedTemplate(body, peer=p.toJson(), configurationFile=download),
"Subject": RenderSandboxedTemplate(subject, peer=p.toJson(), configurationFile=download)
})
except Exception as e:
return ResponseObject(False, message=str(e))
+32 -6
View File
@@ -32,11 +32,24 @@ class AmneziaConfiguration(WireguardConfiguration):
self.H2 = 2
self.H3 = 3
self.H4 = 4
self.I1 = "0"
self.I2 = "0"
self.I3 = "0"
self.I4 = "0"
self.I5 = "0"
self.I1 = ""
self.I2 = ""
self.I3 = ""
self.I4 = ""
self.I5 = ""
self.HeaderProtectionKey = ""
self.ContentPaddingAddition = 0
self.RekeyAfterTime = 0
self.RekeyTimeout = 0
self.RejectAfterTime = 0
self.KeepaliveTimeout = 0
self.MaxHandshakeAttempts = 0
self.RandomTrailers = 0
self.DisableCookies = 0
self.J1 = ""
self.J2 = ""
self.J3 = ""
self.Itime = ""
super().__init__(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, name, data, backup, startup, wg=False)
@@ -79,7 +92,20 @@ class AmneziaConfiguration(WireguardConfiguration):
"I2": self.I2,
"I3": self.I3,
"I4": self.I4,
"I5": self.I5
"I5": self.I5,
"HeaderProtectionKey": self.HeaderProtectionKey,
"ContentPaddingAddition": self.ContentPaddingAddition,
"RekeyAfterTime": self.RekeyAfterTime,
"RekeyTimeout": self.RekeyTimeout,
"RejectAfterTime": self.RejectAfterTime,
"KeepaliveTimeout": self.KeepaliveTimeout,
"MaxHandshakeAttempts": self.MaxHandshakeAttempts,
"RandomTrailers": self.RandomTrailers,
"DisableCookies": self.DisableCookies,
"J1": self.J1,
"J2": self.J2,
"J3": self.J3,
"Itime": self.Itime
}
def createDatabase(self, dbName = None):
+3 -2
View File
@@ -87,8 +87,9 @@ class AmneziaPeer(Peer):
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')}")
output = updateAllowedIp.decode().strip("\n")
if len(output) != 0:
current_app.logger.error(f"Update peer failed when updating Allowed IPs.\nInput: {newAllowedIPs}\nOutput: {output}")
return False, "Internal server error"
command = [f"{self.configuration.Protocol}-quick", "save", self.configuration.Name]
+7 -3
View File
@@ -12,7 +12,7 @@ from .Utilities import (GetRemoteEndpoint, ValidateDNSAddress)
from .DashboardAPIKey import DashboardAPIKey
class DashboardConfig:
DashboardVersion = 'v4.3.3'
DashboardVersion = 'v4.3.4'
ConfigurationPath = os.getenv('CONFIGURATION_PATH', '.')
ConfigurationFilePath = os.path.join(ConfigurationPath, 'wg-dashboard.ini')
@@ -51,7 +51,8 @@ class DashboardConfig:
"peer_display_mode": "grid",
"remote_endpoint": GetRemoteEndpoint(),
"peer_MTU": "1420",
"peer_keep_alive": "21"
"peer_keep_alive": "21",
"peer_preshared_key_default": "false"
},
"Other": {
"welcome_session": "true"
@@ -146,7 +147,10 @@ class DashboardConfig:
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}'))
preparer = self.engine.dialect.identifier_preparer
quoted_table = preparer.quote_identifier(table_name)
quoted_column = preparer.quote_identifier(col_name)
conn.execute(db.text(f"ALTER TABLE {quoted_table} ADD COLUMN {quoted_column} {type_str}"))
def getConnectionString(self, database) -> str or None:
sqlitePath = os.path.join(DashboardConfig.ConfigurationPath, "db")
+7 -2
View File
@@ -85,10 +85,15 @@ class DashboardOIDC:
key = next(k for k in jwks["keys"] if k["kid"] == kid)
alg = key.get("alg") or headers.get("alg")
supported_algs = oidc_config.get("id_token_signing_alg_values_supported")
if supported_algs and alg not in supported_algs:
return False, f"Unsupported token signing algorithm: {alg}"
payload = jwt.decode(
id_token,
key,
algorithms=[key["alg"]],
algorithms=[alg],
audience=provider_info.get('client_id'),
issuer=issuer,
access_token=access_token
@@ -134,7 +139,7 @@ class DashboardOIDC:
'openid_configuration': oidc_config
}
self.provider_secret[k] = providers[k]['client_secret']
current_app.logger.info(f"Registered OIDC Provider: {k}")
current_app.logger.info(f"Registered OIDC Provider[{self.mode}]: {k}")
except Exception as e:
current_app.logger.error(f"Failed to register OIDC config for {k}", exc_info=e)
except Exception as e:
+17 -4
View File
@@ -7,12 +7,12 @@ import json
import os, subprocess, uuid, random, re
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, CheckAddress, ValidateDNSAddress
from .Utilities import GenerateWireguardPublicKey, CheckAddress, ValidateDNSAddress, \
RenderSandboxedTemplate
class Peer:
@@ -206,7 +206,20 @@ class Peer:
"I2": self.configuration.I2,
"I3": self.configuration.I3,
"I4": self.configuration.I4,
"I5": self.configuration.I5
"I5": self.configuration.I5,
"HeaderProtectionKey": self.configuration.HeaderProtectionKey,
"ContentPaddingAddition": self.configuration.ContentPaddingAddition,
"RekeyAfterTime": self.configuration.RekeyAfterTime,
"RekeyTimeout": self.configuration.RekeyTimeout,
"RejectAfterTime": self.configuration.RejectAfterTime,
"KeepaliveTimeout": self.configuration.KeepaliveTimeout,
"MaxHandshakeAttempts": self.configuration.MaxHandshakeAttempts,
"RandomTrailers": self.configuration.RandomTrailers,
"DisableCookies": self.configuration.DisableCookies,
"J1": self.configuration.J1,
"J2": self.configuration.J2,
"J3": self.configuration.J3,
"Itime": self.configuration.Itime
})
peerSection = {
@@ -233,7 +246,7 @@ class Peer:
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)
final["file"] = RenderSandboxedTemplate(final["file"], configuration=self.configuration)
if self.configuration.Protocol == "awg":
+15
View File
@@ -1,6 +1,21 @@
import re, ipaddress
import subprocess
import sqlalchemy
from jinja2.sandbox import SandboxedEnvironment
sandboxEnvironment = SandboxedEnvironment()
def RenderSandboxedTemplate(source: str, **context) -> str:
"""
Render a Jinja2 template within a sandboxed environment. Template sources built with
user-provided content (e.g. email body, peer configuration file) must go through this
function, so unsafe attribute access (such as __globals__) is denied and template
injection cannot escalate into arbitrary code execution.
@param source: Template source string, may contain user-provided content
@param context: Variables passed into the template
@return: Rendered template
"""
return sandboxEnvironment.from_string(source).render(**context)
def RegexMatch(regex, text) -> bool:
"""
+75 -35
View File
@@ -3,7 +3,6 @@ WireGuard Configuration
"""
from typing import Any
import jinja2
import sqlalchemy, random, shutil, configparser, ipaddress, os, subprocess, time, re, uuid, psutil, traceback
from zipfile import ZipFile
from datetime import datetime, timedelta
@@ -21,7 +20,8 @@ from .Utilities import StringToBoolean, \
ValidateDNSAddress, \
ValidateEndpointAllowedIPs, \
CheckAddress, \
CheckPeerKey
CheckPeerKey, \
RenderSandboxedTemplate
from .WireguardConfigurationInfo import WireguardConfigurationInfo, PeerGroupsClass
from .DashboardWebHooks import DashboardWebHooks
@@ -109,23 +109,40 @@ class WireguardConfiguration:
}
if self.Protocol == 'awg':
self.__parser["Interface"]["Jc"] = self.Jc
self.__parser["Interface"]["Jc"] = self.Jc
self.__parser["Interface"]["Jmin"] = self.Jmin
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
values = {
"Jc": self.Jc,
"Jmin": self.Jmin,
"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,
"I1": self.I1,
"I2": self.I2,
"I3": self.I3,
"I4": self.I4,
"I5": self.I5,
"HeaderProtectionKey": self.HeaderProtectionKey,
"ContentPaddingAddition": self.ContentPaddingAddition,
"RekeyAfterTime": self.RekeyAfterTime,
"RekeyTimeout": self.RekeyTimeout,
"RejectAfterTime": self.RejectAfterTime,
"KeepaliveTimeout": self.KeepaliveTimeout,
"MaxHandshakeAttempts": self.MaxHandshakeAttempts,
"RandomTrailers": self.RandomTrailers,
"DisableCookies": self.DisableCookies,
"J1": self.J1,
"J2": self.J2,
"J3": self.J3,
"Itime": self.Itime
}
for key, value in values.items():
if value != None and str(value).strip():
self.__parser["Interface"][key] = str(value)
if "Backup" not in data.keys():
self.createDatabase()
@@ -232,17 +249,16 @@ class WireguardConfiguration:
self.Status = self.getStatus()
def __dropDatabase(self):
existingTables = [self.Name, f'{self.Name}_restrict_access', f'{self.Name}_transfer', f'{self.Name}_deleted']
existingTables = [self.Name, f'{self.Name}_restrict_access', f'{self.Name}_transfer', f'{self.Name}_deleted', f'{self.Name}_history_endpoint']
try:
with self.engine.begin() as conn:
for t in existingTables:
for name in existingTables:
table = sqlalchemy.Table(name, sqlalchemy.MetaData())
conn.execute(
sqlalchemy.text(
f'DROP TABLE "{t}"'
sqlalchemy.schema.DropTable(table, if_exists=True)
)
)
except Exception as e:
current_app.logger.error("Dropping table failed")
except Exception:
current_app.logger.exception("Dropping table failed")
return False
return True
@@ -308,7 +324,8 @@ 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', time_col_type)
sqlalchemy.Column('time', time_col_type),
extend_existing=True
)
self.infoTable = sqlalchemy.Table(
@@ -327,11 +344,19 @@ class WireguardConfiguration:
rows = conn.execute(i.select()).mappings().fetchall()
for row in rows:
insert_stmt = i.insert().values(dict(row))
yield str(insert_stmt.compile(compile_kwargs={"literal_binds": True}))
yield str(
insert_stmt.compile(
dialect=self.engine.dialect,
compile_kwargs={"literal_binds": True},
)
)
def __importDatabase(self, sqlFilePath, restore = False) -> bool:
if not restore:
self.__dropDatabase()
if not self.__dropDatabase():
raise RuntimeError(
f"Failed to drop tables for configuration {self.Name}"
)
self.createDatabase()
if not os.path.exists(sqlFilePath):
return False
@@ -934,8 +959,8 @@ class WireguardConfiguration:
files.sort(key=lambda x: x[1], reverse=True)
for f, ct in files:
if RegexMatch(rf"^({self.Name})_(\d+)\\.(conf)$", f):
s = re.search(rf"^({self.Name})_(\d+)\\.(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,
@@ -976,7 +1001,11 @@ class WireguardConfiguration:
if backupFileName not in backups:
return False
try:
os.remove(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', backupFileName))
backupPath = os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup')
for ext in ('.conf', '.sql'):
f = os.path.join(backupPath, os.path.splitext(backupFileName)[0] + ext)
if os.path.exists(f):
os.remove(f)
except Exception as e:
return False
return True
@@ -1007,8 +1036,10 @@ class WireguardConfiguration:
with open(self.configPath, 'r') as f:
original = [l.rstrip("\n") for l in f.readlines()]
allowEdit = ["Address", "PreUp", "PostUp", "PreDown", "PostDown", "ListenPort", "Table"]
awgKeys = []
if self.Protocol == 'awg':
allowEdit += ["Jc", "Jmin", "Jmax", "S1", "S2", "S3", "S4", "H1", "H2", "H3", "H4", "I1", "I2", "I3", "I4", "I5"]
awgKeys = ["Jc", "Jmin", "Jmax", "S1", "S2", "S3", "S4", "H1", "H2", "H3", "H4", "I1", "I2", "I3", "I4", "I5", "HeaderProtectionKey", "ContentPaddingAddition", "RekeyAfterTime", "RekeyTimeout", "RejectAfterTime", "KeepaliveTimeout", "MaxHandshakeAttempts", "RandomTrailers", "DisableCookies", "J1", "J2", "J3", "Itime"]
allowEdit += awgKeys
start = original.index("[Interface]")
try:
end = original.index("[Peer]")
@@ -1022,7 +1053,10 @@ class WireguardConfiguration:
if split[0] not in allowEdit:
new.append(original[line])
for key in allowEdit:
new.insert(1, f"{key} = {str(newData[key]).strip()}")
val = str(newData.get(key, "")).strip()
if key in awgKeys and val == "":
continue
new.insert(1, f"{key} = {val}")
new.append("")
for line in range(end, len(original)):
new.append(original[line])
@@ -1072,6 +1106,12 @@ class WireguardConfiguration:
doRenameStatement("_restrict_access")
doRenameStatement("_deleted")
doRenameStatement("_transfer")
doRenameStatement("_history_endpoint")
conn.execute(
self.infoTable.update()
.where(self.infoTable.c.ID == self.Name)
.values(ID=newConfigurationName)
)
self.AllPeerJobs.updateJobConfigurationName(self.Name, newConfigurationName)
shutil.copy(
@@ -1217,7 +1257,7 @@ class WireguardConfiguration:
elif key == "OverridePeerSettings":
for (key, val) in value.items():
try:
status, msg = self.__validateOverridePeerSettings(key, jinja2.Template(val).render(configuration=self.toJson()))
status, msg = self.__validateOverridePeerSettings(key, RenderSandboxedTemplate(val, configuration=self.toJson()))
if not status:
return False, msg, key
except Exception as e:
+11 -11
View File
@@ -1,17 +1,17 @@
bcrypt==5.0.0
ifcfg==0.24
psutil==7.2.2
pyotp==2.9.0
pyotp==2.10.0
Flask==3.1.2
flask-cors==6.0.2
flask-cors==6.0.5
icmplib==3.0.4
gunicorn==25.0.3
requests==2.32.5
gunicorn==26.2.0
requests==2.34.2
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
sqlalchemy==2.1.6
sqlalchemy_utils==0.43.0
psycopg[binary]==3.3.6
PyMySQL==1.2.3
tzlocal==5.4.5
python-jose[cryptography]==3.5.0
pydantic==2.14.1
+27 -1
View File
@@ -11,6 +11,26 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WGDashboard</title>
<base href="./">
<style>
#preloader{
width: 100vw; height: 100vh; display: flex
}
#preloader_placeholder{
width: 50px; height: 50px; background-color: #000; margin: auto;
animation: 3s ease-in-out fadeInPreloader infinite;
}
@keyframes fadeInPreloader {
0%{
opacity: 0;
}
50%{
opacity: 1;
}
100%{
opacity: 0;
}
}
</style>
<script>
const isViteDevMode = document.querySelector('script[src*="@vite/client"]') !== null;
const base = document.querySelector('base');
@@ -20,7 +40,13 @@
</script>
</head>
<body>
<div id="app"></div>
<div id="app">
<div id="preloader">
<div id="preloader_placeholder">
<img style="width: 100%" src="./img/Logo-2-128x128.png" alt="WGDashboard Admin" />
</div>
</div>
</div>
<script type="module" src="./src/main.js"></script>
</body>
</html>
+2212 -3086
View File
File diff suppressed because it is too large Load Diff
+21 -21
View File
@@ -1,6 +1,6 @@
{
"name": "app",
"version": "4.3.3",
"version": "4.3.4",
"private": true,
"type": "module",
"module": "es2022",
@@ -12,36 +12,36 @@
"preview": "vite preview"
},
"dependencies": {
"@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",
"@volar/language-server": "2.4.30",
"@vue/language-server": "3.3.13",
"@vuepic/vue-datepicker": "^14.0.0",
"@vueuse/core": "^15.0.0",
"@vueuse/shared": "^15.0.0",
"animate.css": "^4.1.1",
"bootstrap": "^5.3.2",
"bootstrap-icons": "^1.11.3",
"cidr-tools": "^11.3.2",
"bootstrap-icons": "^1.13.2",
"cidr-tools": "^13.0.4",
"css-color-converter": "^2.0.0",
"dayjs": "^1.11.19",
"electron-builder": "^26.7.0",
"fuse.js": "^7.0.0",
"dayjs": "^1.11.23",
"electron-builder": "^26.18.0",
"fuse.js": "^7.5.0",
"i": "^0.3.7",
"is-cidr": "^6.0.3",
"npm": "^11.8.0",
"ol": "^10.7.0",
"pinia": "^3.0.4",
"is-cidr": "^7.0.3",
"npm": "^12.2.1",
"ol": "^10.11.0",
"pinia": "^4.1.0",
"pinia-plugin-persistedstate": "^4.7.1",
"qrcode": "^1.5.3",
"qrcodejs": "^1.0.0",
"simple-code-editor": "^2.0.9",
"uuid": "^13.0.0",
"vue": "^3.5.31",
"vue-chartjs": "^5.3.3",
"vue-router": "^5.0.4"
"uuid": "^14.0.3",
"vue": "^3.5.32",
"vue-chartjs": "^5.3.4",
"vue-router": "^5.4.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.4",
"vite": "^7.3.1"
"@vitejs/plugin-vue": "^6.0.10",
"vite": "^8.3.5"
},
"overrides": {
"tar": "^7.5.6"
@@ -197,7 +197,7 @@ const deleteConfigurationModal = ref(false)
v-model="data[key]"
:id="'configuration_' + key">
</div>
<div v-for="key in ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5']"
<div v-for="key in ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5', 'HeaderProtectionKey', 'ContentPaddingAddition', 'RekeyAfterTime', 'RekeyTimeout', 'RejectAfterTime', 'KeepaliveTimeout', 'MaxHandshakeAttempts', 'RandomTrailers', 'DisableCookies']"
v-if="configurationInfo.Protocol === 'awg'">
<label :for="'configuration_' + key" class="form-label">
<small class="text-muted">
@@ -6,18 +6,27 @@ export default {
components: {LocaleText},
props: {
data: Object,
saving: Boolean
saving: Boolean,
defaultEnabled: Boolean
},
data(){
return{
enable: false
}
},
mounted() {
const hasKey = !!(this.data && this.data.preshared_key && this.data.preshared_key.length > 0)
if (hasKey || this.defaultEnabled){
this.enable = true
}
},
watch:{
enable(){
if (this.enable){
if (this.enable) {
if (!this.data.preshared_key){
this.data.preshared_key = window.wireguard.generateKeypair().presharedKey
}else {
}
} else {
this.data.preshared_key = ""
}
}
@@ -32,7 +32,7 @@ const peerData = ref({
keepalive: parseInt(dashboardStore.Configuration.Peers.peer_keep_alive),
mtu: parseInt(dashboardStore.Configuration.Peers.peer_mtu),
preshared_key: "",
preshared_key_bulkAdd: false,
preshared_key_bulkAdd: Boolean(dashboardStore.Configuration.Peers.peer_preshared_key_default),
allowed_ips_validation: true,
})
const availableIp = ref([])
@@ -128,7 +128,7 @@ watch(() => {
<EndpointAllowedIps :saving="saving" :data="peerData"></EndpointAllowedIps>
<div class="row gy-3">
<div class="col-sm" v-if="!peerData.bulkAdd">
<PresharedKeyInput :saving="saving" :data="peerData" :bulk="peerData.bulkAdd"></PresharedKeyInput>
<PresharedKeyInput :saving="saving" :data="peerData" :bulk="peerData.bulkAdd" :defaultEnabled="Boolean(dashboardStore.Configuration.Peers.peer_preshared_key_default)"></PresharedKeyInput>
</div>
<div class="col-sm">
@@ -36,7 +36,7 @@ export default {
keepalive: parseInt(this.dashboardStore.Configuration.Peers.peer_keep_alive),
mtu: parseInt(this.dashboardStore.Configuration.Peers.peer_mtu),
preshared_key: "",
preshared_key_bulkAdd: false,
preshared_key_bulkAdd: Boolean(this.dashboardStore.Configuration.Peers.peer_preshared_key_default),
},
availableIp: undefined,
availableIpSearchString: "",
@@ -133,7 +133,7 @@ export default {
<hr class="mb-0 mt-2">
<div class="row gy-3">
<div class="col-sm" v-if="!this.data.bulkAdd">
<PresharedKeyInput :saving="saving" :data="data" :bulk="this.data.bulkAdd"></PresharedKeyInput>
<PresharedKeyInput :saving="saving" :data="data" :bulk="this.data.bulkAdd" :defaultEnabled="Boolean(this.dashboardStore.Configuration.Peers.peer_preshared_key_default)"></PresharedKeyInput>
</div>
<div class="col-sm">
@@ -26,7 +26,7 @@ export default {
methods: {
async fetchLog(){
this.dataLoading = true;
await fetchGet(`/api/getPeerScheduleJobLogs/${this.configurationInfo.Name}`, {}, (res) => {
await fetchGet(`/api/PeerScheduleJobLogs/${this.configurationInfo.Name}`, {}, (res) => {
this.data = res.data;
this.logFetchTime = dayjs().format("YYYY-MM-DD HH:mm:ss")
this.dataLoading = false;
@@ -2,7 +2,7 @@
import ScheduleDropdown from "@/components/configurationComponents/peerScheduleJobsComponents/scheduleDropdown.vue";
import {ref} from "vue";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import {fetchPost} from "@/utilities/fetch.js";
import {fetchPost, fetchPut, fetchDelete} from "@/utilities/fetch.js";
import { VueDatePicker } from "@vuepic/vue-datepicker";
import dayjs from "dayjs";
import LocaleText from "@/components/text/localeText.vue";
@@ -46,9 +46,8 @@ export default {
methods: {
save(){
if (this.job.Field && this.job.Operator && this.job.Action && this.job.Value){
fetchPost(`/api/savePeerScheduleJob`, {
Job: this.job
}, (res) => {
const fn = this.newJob ? fetchPost : fetchPut;
fn(`/api/PeerScheduleJob`, this.job, (res) => {
if (res.status){
this.edit = false;
this.store.newMessage("Server", "Peer job saved", "success")
@@ -84,9 +83,7 @@ export default {
},
delete(){
if(this.job.CreationDate){
fetchPost(`/api/deletePeerScheduleJob`, {
Job: this.job
}, (res) => {
fetchDelete(`/api/PeerScheduleJob`, this.job, (res) => {
if (!res.status){
this.store.newMessage("Server", res.message, "danger")
this.$emit('delete')
@@ -22,6 +22,8 @@ import PeersDefaultSettingsInput from "@/components/settingsComponent/peersDefau
targetData="peer_mtu" title="MTU"></PeersDefaultSettingsInput>
<PeersDefaultSettingsInput
targetData="peer_keep_alive" title="Persistent Keepalive"></PeersDefaultSettingsInput>
<PeersDefaultSettingsInput
targetData="peer_preshared_key_default" title="Pre-Shared Key Default"></PeersDefaultSettingsInput>
<PeersDefaultSettingsInput
targetData="remote_endpoint" title="Peer Remote Endpoint"
:warning="true" warningText="This will be changed globally, and will be apply to all peer's QR code and configuration file."
@@ -31,6 +31,11 @@ export default {
mounted() {
this.value = this.store.Configuration.Peers[this.targetData];
},
computed: {
isBoolean(){
return typeof this.value === "boolean"
}
},
methods:{
async useValidation(){
if(this.changed){
@@ -67,7 +72,14 @@ export default {
<LocaleText :t="this.title"></LocaleText>
</small></strong>
</label>
<input type="text" class="form-control"
<div v-if="isBoolean" class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch"
v-model="this.value"
:id="this.uuid"
@change="this.changed = true; useValidation()"
:disabled="this.updating">
</div>
<input v-else type="text" class="form-control"
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
:id="this.uuid"
v-model="this.value"
@@ -0,0 +1,58 @@
<script setup async>
import {computed, onMounted, ref} from "vue";
import {fetchGet} from "@/utilities/fetch.js";
import LocaleText from "@/components/text/localeText.vue";
const providers = ref([])
const oidcEnabled = ref(false)
await fetchGet("/api/oidc/providers", {}, async (res) => {
if (res.status){
await Promise.all(Object.entries(res.data).map(([key, value]) => {
return fetch(`${value.issuer}/.well-known/openid-configuration`).then((res) => {
providers.value.push({
Provider: key,
ProviderInfo: value
})
}).catch(() => {
console.log("Failed to request " + key)
})
}))
oidcEnabled.value = providers.value.length > 0
}
})
console.log(providers.value)
const oidcUrl = (provider) => {
const params = new URLSearchParams({
client_id: provider.ProviderInfo.client_id,
redirect_uri: window.location.protocol + '//' + window.location.host + window.location.pathname,
response_type: 'code',
state: provider.Provider,
scope: 'openid email profile'
})
console.log(params.entries())
const url = new URL(provider.ProviderInfo.openid_configuration.authorization_endpoint)
url.search = params.toString()
return url
}
</script>
<template>
<div>
<div v-if="oidcEnabled">
<hr class="mb-4"/>
<div class="d-flex gap-2">
<a class="btn bg-body border w-100 rounded-3"
:href="oidcUrl(provider)"
v-for="provider in providers" style="flex: 1 1 0;">
<LocaleText :t="'Sign In With ' + provider.Provider"/>
</a>
</div>
</div>
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,11 @@
<script setup>
</script>
<template>
</template>
<style scoped>
</style>
+56 -9
View File
@@ -7,18 +7,65 @@ import '@vuepic/vue-datepicker/dist/main.css'
import {createApp, markRaw} from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router/router.js'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
const app = createApp(App)
const params = new URLSearchParams(window.location.search)
const state = params.get('state')
const code = params.get('code')
app.use(router)
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate)
pinia.use(({ store }) => {
if (state && code) {
window.history.replaceState({}, '', window.location.pathname);
console.log('After replaceState:', window.location.href);
}
const initApp = async () => {
const { default: router } = await import('./router/router.js')
const app = createApp(App)
app.use(router)
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate)
pinia.use(({ store }) => {
store.$router = markRaw(router)
})
})
app.use(pinia)
app.mount('#app')
app.use(pinia)
app.mount('#app')
console.log('After router import:', window.location.href)
}
export const getUrl = (url) => {
if (import.meta.env.MODE === 'development') {
return url;
}
return `./.${url}`;
}
if (state && code){
await fetch(getUrl('/api/oidc/authenticate'), {
method: "POST",
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
provider: state,
code: code,
redirect_uri: window.location.protocol + '//' + window.location.host + window.location.pathname
})
}).then(res => res.json()).then( async (res) => {
if (res.status){
window.location.replace(window.location.protocol + '//' + window.location.host + window.location.pathname)
}else{
await initApp()
const store = DashboardConfigurationStore()
store.newMessage('Server OIDC', res.message, 'danger')
}
})
}else{
await initApp()
}
+52
View File
@@ -4,6 +4,7 @@ const getHeaders = () => {
let headers = {
"Content-Type": "application/json"
}
try{
const store = DashboardConfigurationStore();
const crossServer = store.getActiveCrossServer();
if (crossServer){
@@ -16,17 +17,22 @@ const getHeaders = () => {
}
}
}
}catch (e) {
}
return headers
}
export const getUrl = (url) => {
try{
const store = DashboardConfigurationStore();
const apiKey = store.getActiveCrossServer();
if (apiKey){
return `${apiKey.host}${url}`
}
}catch (e){}
if (import.meta.env.MODE === 'development') {
return url;
}
@@ -80,3 +86,49 @@ export const fetchPost = async (url, body, callback) => {
router.push({path: '/signin'})
})
}
export const fetchPut = async (url, body, callback) => {
await fetch(`${getUrl(url)}`, {
headers: getHeaders(),
method: "PUT",
body: JSON.stringify(body)
}).then((x) => {
const store = DashboardConfigurationStore();
if (!x.ok){
if (x.status !== 200){
if (x.status === 401){
store.newMessage("WGDashboard", "Sign in session ended, please sign in again", "warning")
}
throw new Error(x.statusText)
}
}else{
return x.json()
}
}).then(x => callback ? callback(x) : undefined).catch(x => {
console.log("Error:", x)
router.push({path: '/signin'})
})
}
export const fetchDelete = async (url, body, callback) => {
await fetch(`${getUrl(url)}`, {
headers: getHeaders(),
method: "DELETE",
body: JSON.stringify(body)
}).then((x) => {
const store = DashboardConfigurationStore();
if (!x.ok){
if (x.status !== 200){
if (x.status === 401){
store.newMessage("WGDashboard", "Sign in session ended, please sign in again", "warning")
}
throw new Error(x.statusText)
}
}else{
return x.json()
}
}).then(x => callback ? callback(x) : undefined).catch(x => {
console.log("Error:", x)
router.push({path: '/signin'})
})
}
+24 -9
View File
@@ -49,11 +49,20 @@ export default {
H2: 0,
H3: 0,
H4: 0,
I1: "0",
I2: "0",
I3: "0",
I4: "0",
I5: "0"
I1: "",
I2: "",
I3: "",
I4: "",
I5: "",
HeaderProtectionKey: "",
ContentPaddingAddition: "0-64",
RekeyAfterTime: "100-140",
RekeyTimeout: "4-7",
RejectAfterTime: "160-200",
KeepaliveTimeout: "8-12",
MaxHandshakeAttempts: "14-20",
RandomTrailers: "on",
DisableCookies: "on"
},
numberOfAvailableIPs: "0",
error: false,
@@ -72,9 +81,15 @@ export default {
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";
// Initialize HeaderProtectionKey
let headerProtectionKey = new Uint8Array(32);
window.crypto.getRandomValues(headerProtectionKey);
this.newConfiguration.HeaderProtectionKey = btoa(String.fromCharCode(...headerProtectionKey));
// S1-S4 are recommended be the same to work with RandomTrailers and must 12 or more to work with HeaderProtectionKey
let constantS = 12 + this.rand(1, 2**5);
['S1', 'S2', 'S3', 'S4'].forEach(key => {
this.newConfiguration[key] = constantS;
});
},
methods: {
@@ -388,7 +403,7 @@ export default {
<div class="card rounded-3"
v-if="this.newConfiguration.Protocol === 'awg'"
v-for="key in ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5']">
v-for="key in ['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4', 'I1', 'I2', 'I3', 'I4', 'I5', 'HeaderProtectionKey', 'ContentPaddingAddition', 'RekeyAfterTime', 'RekeyTimeout', 'RejectAfterTime', 'KeepaliveTimeout', 'MaxHandshakeAttempts', 'RandomTrailers', 'DisableCookies']">
<div class="card-header">{{ key }}</div>
<div class="card-body">
<input type="text"
+5 -2
View File
@@ -7,10 +7,11 @@ import {GetLocale} from "@/utilities/locale.js";
import LocaleText from "@/components/text/localeText.vue";
import SignInInput from "@/components/signIn/signInInput.vue";
import SignInTOTP from "@/components/signIn/signInTOTP.vue";
import SignInOIDC from "@/components/signIn/signInOIDC.vue";
export default {
name: "signin",
components: {SignInTOTP, SignInInput, LocaleText, RemoteServerList, Message},
components: {SignInOIDC, SignInTOTP, SignInInput, LocaleText, RemoteServerList, Message},
async setup(){
const store = DashboardConfigurationStore()
let theme = "dark"
@@ -171,7 +172,7 @@ export default {
</form>
<RemoteServerList v-else></RemoteServerList>
<div class="d-flex mt-3" v-if="!this.store.IsElectronApp">
<div class="d-flex mt-3 flex-column" v-if="!this.store.IsElectronApp">
<div class="form-check form-switch ms-auto">
<input
v-model="this.store.CrossServerConfiguration.Enable"
@@ -184,6 +185,8 @@ export default {
<LocaleText t="Access Remote Server"></LocaleText>
</label>
</div>
<SignInOIDC />
</div>
</div>
</div>
+1461 -1772
View File
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -1,6 +1,6 @@
{
"name": "client",
"version": "v1.0",
"version": "v1.0.1",
"private": true,
"type": "module",
"scripts": {
@@ -9,20 +9,20 @@
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.9.0",
"bootstrap": "^5.3.6",
"bootstrap-icons": "^1.13.1",
"dayjs": "^1.11.13",
"oidc-client-ts": "^3.2.1",
"pinia": "^3.0.2",
"axios": "^1.20.0",
"bootstrap": "^5.3.8",
"bootstrap-icons": "^1.13.2",
"dayjs": "^1.11.23",
"oidc-client-ts": "^3.5.0",
"pinia": "^4.1.0",
"qrcode": "^1.5.4",
"uuid": "^11.1.0",
"vue": "^3.5.13",
"vue-router": "^4.5.1"
"uuid": "^14.0.3",
"vue": "^3.5.44",
"vue-router": "^5.4.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",
"vite": "^6.2.4",
"vite-plugin-vue-devtools": "^7.7.2"
"@vitejs/plugin-vue": "^6.0.10",
"vite": "^8.3.5",
"vite-plugin-vue-devtools": "^8.2.1"
}
}
+9 -6
View File
@@ -2,7 +2,6 @@ import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from "@/router/router.js";
import {createPinia} from "pinia";
import 'bootstrap/dist/js/bootstrap.bundle.js'
@@ -13,7 +12,13 @@ const params = new URLSearchParams(window.location.search)
const state = params.get('state')
const code = params.get('code')
if (state && code) {
window.history.replaceState({}, '', window.location.pathname);
}
const initApp = async () => {
debugger
const { default: router } = await import('./router/router.js')
const app = createApp(App)
const serverInformation = await axiosGet("/api/serverInformation", {})
app.use(createPinia())
@@ -31,12 +36,10 @@ if (state && code){
code: code,
redirect_uri: window.location.protocol + '//' + window.location.host + window.location.pathname
}).then(async (data) => {
let url = new URL(window.location.href);
url.search = '';
history.replaceState({}, document.title, url.toString());
if (data.status){
window.location.replace(window.location.protocol + '//' + window.location.host + window.location.pathname)
}else {
await initApp()
if (!data.status){
const store = clientStore()
store.newNotification(data.message, 'danger')
}
@@ -1 +0,0 @@
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
@@ -1 +0,0 @@
@media screen and (min-width: 576px){.backLink[data-v-f874264d]{display:none}}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
@media screen and (max-width: 576px){.clientListContainer.hide[data-v-a8650ee3],.clientViewerContainer.hide[data-v-a8650ee3]{display:none!important}.clientListContainer[data-v-a8650ee3]{border-right:none!important;animation:blurIn-a8650ee3 .2s ease-in-out forwards}.clientViewerContainer[data-v-a8650ee3]{animation:blurIn-a8650ee3 .2s ease-in-out forwards}}@keyframes blurIn-a8650ee3{0%{filter:blur(8px)}to{filter:blur(0px)}}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
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
@@ -1 +0,0 @@
.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}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
@media screen and (max-width: 992px){.apiKey-card-body{&[data-v-a76253c8]{flex-direction:column!important;align-items:start!important}div.ms-auto[data-v-a76253c8]{margin-left:0!important}div[data-v-a76253c8]{width:100%;align-items:start!important}small[data-v-a76253c8]{margin-right:auto}}}.apiKey-move[data-v-f7e62927],.apiKey-enter-active[data-v-f7e62927],.apiKey-leave-active[data-v-f7e62927]{transition:all .5s ease}.apiKey-enter-from[data-v-f7e62927],.apiKey-leave-to[data-v-f7e62927]{opacity:0;transform:translateY(30px) scale(.9)}.apiKey-leave-active[data-v-f7e62927]{position:absolute;width:100%}.dropdown-menu[data-v-4e34593e]{width:100%}
@@ -1 +0,0 @@
.list-group{&[data-v-4aa2aed9]:first-child{border-top-left-radius:var(--bs-border-radius-lg);border-top-right-radius:var(--bs-border-radius-lg)}&[data-v-4aa2aed9]:last-child{border-bottom-left-radius:var(--bs-border-radius-lg);border-bottom-right-radius:var(--bs-border-radius-lg)}}
@@ -1 +0,0 @@
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
@@ -1 +0,0 @@
.table[data-v-7b6e949e]>:not(caption)>*>*{padding-left:0!important;padding-right:1rem!important}.list-group-item[data-v-e0f0e683]{border-radius:0!important;border-left:0!important;border-right:0!important}.list-group-item[data-v-e0f0e683]:first-child{border-top:0!important}.url[data-v-e0f0e683]{text-overflow:ellipsis;overflow:hidden;white-space:nowrap;font-size:.9rem}
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
@@ -1 +0,0 @@
var n={},l;function u(){if(l)return n;l=1;const i=new Uint8Array(512),t=new Uint8Array(256);return(function(){let r=1;for(let e=0;e<255;e++)i[e]=r,t[r]=e,r<<=1,r&256&&(r^=285);for(let e=255;e<512;e++)i[e]=i[e-255]})(),n.log=function(r){if(r<1)throw new Error("log("+r+")");return t[r]},n.exp=function(r){return i[r]},n.mul=function(r,e){return r===0||e===0?0:i[t[r]+t[e]]},n}export{u as r};
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
function x(n){return n.includes(":")?6:n.includes(".")?4:0}function p(n){const t=x(n);if(!t)throw new Error(`Invalid IP address: ${n}`);let e=0n,r=0n;const s=Object.create(null);if(t===4)for(const i of n.split(".").map(BigInt).reverse())e+=i*2n**r,r+=8n;else{if(n.includes(".")&&(s.ipv4mapped=!0,n=n.split(":").map(o=>{if(o.includes(".")){const[u,l,f,a]=o.split(".").map(d=>Number(d).toString(16).padStart(2,"0"));return`${u}${l}:${f}${a}`}else return o}).join(":")),n.includes("%")){let o;[,n,o]=/(.+)%(.+)/.exec(n)||[],s.scopeid=o}const i=n.split(":"),c=i.indexOf("");if(c!==-1)for(;i.length<8;)i.splice(c,0,"");for(const o of i.map(u=>BigInt(parseInt(u||"0",16))).reverse())e+=o*2n**r,r+=16n}return s.number=e,s.version=t,s}function m({number:n,version:t,ipv4mapped:e,scopeid:r},{compress:s=!0,hexify:i=!1}={}){let c=t===4?24n:112n;const o=t===4?8n:16n;let u=n;const l=[];for(;c>0n;){const f=2n**c;l.push(u/f),u=n%f,c-=o}if(l.push(u),t===4)return l.join(".");{let f="";if(e&&!i){for(const[a,d]of l.entries())a<6?f+=`${d.toString(16)}:`:f+=`${String(d>>8n)}.${String(d&255n)}${a===6?".":""}`;s&&(f=y(f.split(":")))}else s?f=y(l.map(a=>a.toString(16))):f=l.map(a=>a.toString(16)).join(":");return r?`${f}%${r}`:f}}function B(n,{compress:t=!0,hexify:e=!1}={}){return m(p(n),{compress:t,hexify:e})}function y(n){let t=null,e=null;for(const[r,s]of n.entries())s==="0"?e?e.add(r):e=new Set([r]):e&&((!t||e.size>t.size)&&(t=e),e=null);(!t&&e||e&&t&&e.size>t.size)&&(t=e);for(const r of t||[])n[r]=":";return n.filter(Boolean).join(":").replace(/:{2,}/,"::")}const v={4:32,6:128};function b(n){return Array.from(new Set(n))}function N(n){return n.includes("/")?x(n):0}function I(n,t){const{number:e,version:r}=p(n.replace(/\/.+/,"")),{number:s,version:i}=p(t.replace(/\/.+/,""));return r===i?e-s>0n?1:e-s<0n?-1:0:r>i?1:0}function P(n,{compress:t=!0,hexify:e=!1}={}){const{start:r,end:s,prefix:i,version:c,prefixPresent:o}=g(n);return r!==s||o?`${B(m({number:r,version:c}),{compress:t,hexify:e})}/${i}`:B(n,{compress:t,hexify:e})}function h(n,t){return Array.isArray(n)?n.map(e=>h(e,t)):P(n,t)}function g(n){const t=N(n),e=Object.create(null);let r;if(t)r=n,e.version=t;else{const S=x(n);if(S)r=`${n}/${v[S]}`,e.version=S;else throw new Error(`Network is not a CIDR or IP: ${n}`)}const[s,i]=r.split("/");if(!/^[0-9]+$/.test(i))throw new Error(`Network is not a CIDR or IP: ${n}`);const{number:c,version:o,ipv4mapped:u,scopeid:l}=p(s);e.ip=m({number:c,version:o,ipv4mapped:u,scopeid:l}),e.cidr=`${e.ip}/${i}`,e.prefix=i,e.prefixPresent=!!t;const f=v[o],a=c.toString(2).padStart(f,"0"),d=Number(f-Number(i)),w=a.substring(0,f-d);return e.start=BigInt(`0b${w}${"0".repeat(d)}`),e.end=BigInt(`0b${w}${"1".repeat(d)}`),e}function z(n,t){return!(t.start<n.start||t.end>n.end)}function C(n){return n===0n?0n:2n**BigInt(String(n.toString(2).length-1))}function $(n){if(n.end-n.start===1n)return n.end%2n===0n?[{start:n.start,end:n.start},{start:n.end,end:n.end}]:[{start:n.start,end:n.end}];const t=A(n.end,n.start);let e=C(t),r,s;if(t===e&&n.start+t===n.end)return[n];if(n.start%e===0n)r=n.start,s=r+e-1n;else if(r=n.end/e*e,r+e-1n>n.end){for(r=(n.end/e-1n)*e;r<n.start;)e/=2n,r=(n.end/e-1n)*e;s=r+e-1n}else r=n.end/e*e,s=r+e-1n;let i=[{start:r,end:s}];return r!==n.start&&(i=i.concat($({start:n.start,end:r-1n}))),s!==n.end&&(i=i.concat($({start:s+1n,end:n.end}))),i}function A(n,t){return n+1n-t}function j(n,t){const e=h(m({number:BigInt(n.start.toString()),version:t})),r=A(n.end,n.start).toString(2),s=v[t]-(r.match(/0/g)||[]).length;return`${e}/${s}`}function O(n){const t={4:{},6:{}};for(const{start:e,end:r,version:s}of n)t[s][String(e)]||(t[s][String(e)]={start:0,end:0}),t[s][String(r)]||(t[s][String(r)]={start:0,end:0}),t[s][String(e)].start+=1,t[s][String(r)].end+=1;return t}function V(n){let t=null,e=null;const r=Object.keys(n);let s=0;const i=[];for(const[c,o]of r.entries()){const u=n[String(o)];if(t===null&&u.start&&(t=BigInt(o)),u.end&&(e=BigInt(o)),t===null)continue;u.start&&(s+=u.start),u.end&&(s-=u.end);const l=r[c+1];if(u.end&&s===0&&l&&BigInt(l)-BigInt(o)>1){for(const f of $({start:t,end:e}))i.push(f);t=null,e=null}else if(c===r.length-1)for(const f of $({start:t,end:e}))i.push(f)}return i}function k(n){const t=b((Array.isArray(n)?n:[n]).sort(I).map(g)),e=O(t),r={4:[],6:[]};for(const s of[4,6])r[s]=V(e[s]).map(i=>j(i,s));return[...r[4].sort(I),...r[6].sort(I)]}function*E(n){const t=b(Array.isArray(n)?n:[n]);for(const e of k(t)){const{start:r,end:s,version:i}=g(e);for(let c=r;c<=s;c++)yield h(m({number:c,version:i}))}}function R(n,t){const e=b(Array.isArray(n)?n:[n]),r=b(Array.isArray(t)?t:[t]),s=r.length;let i=0;for(const c of e){const o=g(c);for(const u of r){const l=g(u);if(o.version===l.version&&z(o,l)){i++;continue}}}return i===s}export{R as c,E as e,k as m,g as p};
@@ -1 +0,0 @@
import{H as I,q as w,P as S,J as k,Q as L,u as R}from"./index-DXzxfcZW.js";const W=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const X=Object.prototype.toString,Y=t=>X.call(t)==="[object Object]",$=()=>{};function C(t){return Array.isArray(t)?t:[t]}function q(t,a,r){return I(t,a,{...r,immediate:!0})}const O=W?window:void 0;function P(t){var a;const r=S(t);return(a=r?.$el)!==null&&a!==void 0?a:r}function T(...t){const a=(o,u,s,d)=>(o.addEventListener(u,s,d),()=>o.removeEventListener(u,s,d)),r=w(()=>{const o=C(S(t[0])).filter(u=>u!=null);return o.every(u=>typeof u!="string")?o:void 0});return q(()=>{var o,u;return[(o=(u=r.value)===null||u===void 0?void 0:u.map(s=>P(s)))!==null&&o!==void 0?o:[O].filter(s=>s!=null),C(S(r.value?t[1]:t[0])),C(R(r.value?t[2]:t[1])),S(r.value?t[3]:t[2])]},([o,u,s,d],p,c)=>{if(!o?.length||!u?.length||!s?.length)return;const f=Y(d)?{...d}:d,v=o.flatMap(b=>u.flatMap(h=>s.map(y=>a(b,h,y,f))));c(()=>{v.forEach(b=>b())})},{flush:"post"})}function B(t,a,r={}){const{window:o=O,ignore:u=[],capture:s=!0,detectIframe:d=!1,controls:p=!1}=r;if(!o)return p?{stop:$,cancel:$,trigger:$}:$;let c=!0;const f=e=>S(u).some(n=>{if(typeof n=="string")return Array.from(o.document.querySelectorAll(n)).some(i=>i===e.target||e.composedPath().includes(i));{const i=P(n);return i&&(e.target===i||e.composedPath().includes(i))}});function v(e){const n=S(e);return n&&n.$.subTree.shapeFlag===16}function b(e,n){const i=S(e),m=i.$.subTree&&i.$.subTree.children;return m==null||!Array.isArray(m)?!1:m.some(A=>A.el===n.target||n.composedPath().includes(A.el))}const h=e=>{const n=P(t);if(e.target!=null&&!(!(n instanceof Element)&&v(t)&&b(t,e))&&!(!n||n===e.target||e.composedPath().includes(n))){if("detail"in e&&e.detail===0&&(c=!f(e)),!c){c=!0;return}a(e)}};let y=!1;const E=[T(o,"click",e=>{y||(y=!0,setTimeout(()=>{y=!1},0),h(e))},{passive:!0,capture:s}),T(o,"pointerdown",e=>{const n=P(t);c=!f(e)&&!!(n&&!e.composedPath().includes(n))},{passive:!0}),d&&T(o,"blur",e=>{setTimeout(()=>{var n;const i=P(t);((n=o.document.activeElement)===null||n===void 0?void 0:n.tagName)==="IFRAME"&&!i?.contains(o.document.activeElement)&&a(e)},0)},{passive:!0})].filter(Boolean),x=()=>E.forEach(e=>e());return p?{stop:x,cancel:()=>{c=!1},trigger:e=>{c=!0,h(e),c=!1}}:x}function D(t,a={}){const{threshold:r=50,onSwipe:o,onSwipeEnd:u,onSwipeStart:s,passive:d=!0}=a,p=k({x:0,y:0}),c=k({x:0,y:0}),f=w(()=>p.x-c.x),v=w(()=>p.y-c.y),{max:b,abs:h}=Math,y=w(()=>b(h(f.value),h(v.value))>=r),E=L(!1),x=w(()=>y.value?h(f.value)>h(v.value)?f.value>0?"left":"right":v.value>0?"up":"down":"none"),e=l=>[l.touches[0].clientX,l.touches[0].clientY],n=(l,g)=>{p.x=l,p.y=g},i=(l,g)=>{c.x=l,c.y=g},m={passive:d,capture:!d},A=l=>{E.value&&u?.(l,x.value),E.value=!1},j=[T(t,"touchstart",l=>{if(l.touches.length!==1)return;const[g,M]=e(l);n(g,M),i(g,M),s?.(l)},m),T(t,"touchmove",l=>{if(l.touches.length!==1)return;const[g,M]=e(l);i(g,M),m.capture&&!m.passive&&Math.abs(f.value)>Math.abs(v.value)&&l.preventDefault(),!E.value&&y.value&&(E.value=!0),E.value&&o?.(l)},m),T(t,["touchend","touchcancel"],A,m)];return{isSwiping:E,direction:x,coordsStart:p,coordsEnd:c,lengthX:f,lengthY:v,stop:()=>j.forEach(l=>l())}}export{D as a,B as o,P as u};
@@ -1 +0,0 @@
.agentContainer[data-v-f37f608d]{--agentHeight: 100vh;position:absolute;z-index:9999;top:0;left:100%;width:450px;box-shadow:0 10px 30px #0000004d;backdrop-filter:blur(8px);background:linear-gradient(var(--degree),#009dff52 var(--distance2),#F9464752 100%)}.agentContainer.enabled[data-v-f37f608d]{height:calc(var(--agentHeight) - 1rem)}@media screen and (max-width: 768px){.agentContainer[data-v-f37f608d]{--agentHeight: 100vh !important;top:0;left:0;max-height:calc(var(--agentHeight) - 58px - 1rem);width:calc(100% - 1rem)}}.agentChatroomBody[data-v-f37f608d]{flex:1 1 auto;overflow-y:auto;max-height:calc(var(--agentHeight) - 70px - 244px)}@media screen and (max-width: 768px){.navbar-container[data-v-982f1a52]{position:absolute!important;z-index:1000;animation-duration:.4s;animation-fill-mode:both;display:none;animation-timing-function:cubic-bezier(.82,.58,.17,.9)}.navbar-container.active[data-v-982f1a52]{animation-direction:normal;display:block!important;animation-name:zoomInFade-982f1a52}}.navbar-container[data-v-982f1a52]{height:100vh;position:relative}@supports (height: 100dvh){@media screen and (max-width: 768px){.navbar-container[data-v-982f1a52]{height:calc(100dvh - 58px)}}}@keyframes zoomInFade-982f1a52{0%{opacity:0;transform:translateY(60px);filter:blur(3px)}to{opacity:1;transform:translateY(0);filter:blur(0px)}}.slideIn-enter-active[data-v-982f1a52],.slideIn-leave-active[data-v-982f1a52]{transition:all .3s cubic-bezier(.82,.58,.17,1)}.slideIn-enter-from[data-v-982f1a52],.slideIn-leave-to[data-v-982f1a52]{transform:translateY(30px);filter:blur(3px);opacity:0}main[data-v-0c6a5068]{height:100vh}@supports (height: 100dvh){@media screen and (max-width: 768px){main[data-v-0c6a5068]{height:calc(100dvh - 58px)}}}
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
@@ -1 +0,0 @@
import{_ as e,G as t,c as o,t as a,f as c}from"./index-DXzxfcZW.js";const s={name:"localeText",props:{t:""},computed:{getLocaleText(){return t(this.t)}}};function n(r,p,l,_,i,x){return c(),o("span",null,a(this.getLocaleText),1)}const m=e(s,[["render",n]]);export{m as L};
@@ -1 +0,0 @@
.message[data-v-94c76b54]{width:100%}@media screen and (min-width: 576px){.message[data-v-94c76b54]{width:400px}}
@@ -1 +0,0 @@
import{L as l}from"./localeText-Dmcj5qqx.js";import{d as c}from"./dayjs.min-C-brjxlJ.js";import{_ as h,c as o,a as e,b as a,w as u,e as p,h as g,t as i,k as f,n as _,f as n}from"./index-DXzxfcZW.js";const x={name:"message",methods:{dayjs:c,hide(){this.ct(),this.message.show=!1},show(){this.timeout=setTimeout(()=>{this.message.show=!1},5e3)},ct(){clearTimeout(this.timeout)}},components:{LocaleText:l},props:{message:Object},mounted(){this.show()},data(){return{dismiss:!1,timeout:null}}},v=["id"],b={key:0,class:"d-flex"},w={class:"fw-bold d-block",style:{"text-transform":"uppercase"}},y={class:"ms-auto"},k={key:1},T={class:"card-body d-flex align-items-center gap-3"};function M(C,s,L,j,t,m){const d=g("LocaleText");return n(),o("div",{onMouseenter:s[1]||(s[1]=r=>{t.dismiss=!0,this.ct()}),onMouseleave:s[2]||(s[2]=r=>{t.dismiss=!1,this.show()}),class:"card shadow rounded-3 position-relative message ms-auto",id:this.message.id},[e("div",{class:_([{"text-bg-danger":this.message.type==="danger","text-bg-success":this.message.type==="success","text-bg-warning":this.message.type==="warning"},"card-header pos"])},[a(f,{name:"zoom",mode:"out-in"},{default:u(()=>[t.dismiss?(n(),o("div",k,[e("small",{onClick:s[0]||(s[0]=r=>m.hide()),class:"d-block mx-auto w-100 text-center",style:{cursor:"pointer"}},[s[3]||(s[3]=e("i",{class:"bi bi-x-lg me-2"},null,-1)),a(d,{t:"Dismiss"})])])):(n(),o("div",b,[e("small",w,[a(d,{t:"FROM "}),p(" "+i(this.message.from),1)]),e("small",y,i(m.dayjs().format("hh:mm A")),1)]))]),_:1})],2),e("div",T,[e("div",null,i(this.message.content),1)])],40,v)}const z=h(x,[["render",M],["__scopeId","data-v-94c76b54"]]);export{z as M};
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
.protocolBtnGroup a[data-v-14fcf0ee]{transition:all .2s ease-in-out}
@@ -1 +0,0 @@
.ol-layer canvas{border-radius:var(--bs-border-radius-lg)!important}#map{height:300px}
@@ -1 +0,0 @@
import{S as C,e as y,c as w,m as _,a as L,f as S,l as v,i as M,b as k,d as x,g as A,h as F,j as R,M as D,V as P,T as b,k as l,O as E,n as O,F as h,P as f,o as T,p as c,C as V,q as u,r as X}from"./Vector-CuSZivra.js";import{_ as Y,D as G,c as $,d as j,f as q}from"./index-DXzxfcZW.js";class r extends C{constructor(t,e){super(),this.flatMidpoint_=null,this.flatMidpointRevision_=-1,this.maxDelta_=-1,this.maxDeltaRevision_=-1,e!==void 0&&!Array.isArray(t[0])?this.setFlatCoordinates(e,t):this.setCoordinates(t,e)}appendCoordinate(t){y(this.flatCoordinates,t),this.changed()}clone(){const t=new r(this.flatCoordinates.slice(),this.layout);return t.applyProperties(this),t}closestPointXY(t,e,o,n){return n<w(this.getExtent(),t,e)?n:(this.maxDeltaRevision_!=this.getRevision()&&(this.maxDelta_=Math.sqrt(_(this.flatCoordinates,0,this.flatCoordinates.length,this.stride,0)),this.maxDeltaRevision_=this.getRevision()),L(this.flatCoordinates,0,this.flatCoordinates.length,this.stride,this.maxDelta_,!1,t,e,o,n))}forEachSegment(t){return S(this.flatCoordinates,0,this.flatCoordinates.length,this.stride,t)}getCoordinateAtM(t,e){return this.layout!="XYM"&&this.layout!="XYZM"?null:(e=e!==void 0?e:!1,v(this.flatCoordinates,0,this.flatCoordinates.length,this.stride,t,e))}getCoordinates(){return M(this.flatCoordinates,0,this.flatCoordinates.length,this.stride)}getCoordinateAt(t,e){return k(this.flatCoordinates,0,this.flatCoordinates.length,this.stride,t,e,this.stride)}getLength(){return x(this.flatCoordinates,0,this.flatCoordinates.length,this.stride)}getFlatMidpoint(){return this.flatMidpointRevision_!=this.getRevision()&&(this.flatMidpoint_=this.getCoordinateAt(.5,this.flatMidpoint_??void 0),this.flatMidpointRevision_=this.getRevision()),this.flatMidpoint_}getSimplifiedGeometryInternal(t){const e=[];return e.length=A(this.flatCoordinates,0,this.flatCoordinates.length,this.stride,t,e,0),new r(e,"XY")}getType(){return"LineString"}intersectsExtent(t){return F(this.flatCoordinates,0,this.flatCoordinates.length,this.stride,t,this.getExtent())}setCoordinates(t,e){this.setLayout(e,t,1),this.flatCoordinates||(this.flatCoordinates=[]),this.flatCoordinates.length=R(this.flatCoordinates,0,t,this.stride),this.changed()}}const B={name:"osmap",props:{type:"",d:Object||Array},data(){return{osmAvailable:!0}},setup(){return{store:G()}},methods:{getLastLonLat(){if(this.type==="traceroute"){const i=this.d.findLast(t=>t.geo&&t.geo.lat&&t.geo.lon);return i?[i.geo.lon,i.geo.lat]:[0,0]}return[this.d.geo.lon,this.d.geo.lat]}},async mounted(){await fetch("https://tile.openstreetmap.org/",{signal:AbortSignal.timeout(1500)}).then(i=>{const t=new D({target:"map",layers:[new b({source:new E})],view:new P({center:l(this.getLastLonLat()),zoom:this.type==="traceroute"?3:10})}),e=[],o=new O;if(this.type==="traceroute")this.d.forEach(s=>{if(s.geo&&s.geo.lat&&s.geo.lon){const a=l([s.geo.lon,s.geo.lat]);e.push(a);const g=this.getLastLonLat(),m=new h({geometry:new f(a),last:s.geo.lon===g[0]&&s.geo.lat===g[1]});o.addFeature(m)}});else{const s=l([this.d.geo.lon,this.d.geo.lat]);e.push(s);const a=new h({geometry:new f(s)});o.addFeature(a)}const n=new r(e),d=new h({geometry:n});o.addFeature(d);const p=new T({source:o,style:function(s){if(s.getGeometry().getType()==="Point")return new c({image:new V({radius:10,fill:new X({color:s.get("last")?"#dc3545":"#0d6efd"}),stroke:new u({color:"white",width:5})})});if(s.getGeometry().getType()==="LineString")return new c({stroke:new u({color:"#0d6efd",width:2})})}});t.addLayer(p)}).catch(i=>{this.osmAvailable=!1})}},z={key:0,id:"map",class:"w-100 rounded-3"};function I(i,t,e,o,n,d){return this.osmAvailable?(q(),$("div",z)):j("",!0)}const H=Y(B,[["render",I]]);export{H as O};
@@ -1 +0,0 @@
.list-move[data-v-ed72944d],.list-enter-active[data-v-ed72944d],.list-leave-active[data-v-ed72944d]{transition:all .3s ease}.list-enter-from[data-v-ed72944d],.list-leave-to[data-v-ed72944d]{opacity:0;transform:translateY(10px)}.list-leave-active[data-v-ed72944d]{position:absolute}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
.list-move[data-v-99c0844e],.list-enter-active[data-v-99c0844e],.list-leave-active[data-v-99c0844e]{transition:all .5s ease}.list-enter-from[data-v-99c0844e],.list-leave-to[data-v-99c0844e]{opacity:0;transform:scale(.9)}.list-leave-active[data-v-99c0844e]{position:absolute;width:100%}.assignment[data-v-99c0844e]:last-child{margin-bottom:0!important}[data-v-b52659b4]:focus{outline:none}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{_ as v,D as g,r as o,o as h,L as x,g as y,c as i,f as n,a as s,b as c,d as w,n as C,w as k,k as F}from"./index-DXzxfcZW.js";import{L as T}from"./localeText-Dmcj5qqx.js";import"./browser-DFwZaPoQ.js";import"./galois-field-I2lBzzs-.js";const M={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0"},S={class:"container d-flex h-100 w-100"},D={class:"m-auto modal-dialog-centered dashboardModal justify-content-center"},L={class:"card rounded-3 shadow w-100"},P={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0"},B={class:"mb-0"},G={class:"card-body p-4 d-flex flex-column gap-3"},N={style:{height:"300px"},class:"d-flex"},V=["value"],j={key:0,class:"spinner-border m-auto",role:"status"},I={class:"d-flex"},W=["disabled"],$={key:0,class:"d-block"},q={key:1,class:"d-block",id:"check"},z={__name:"peerConfigurationFile",props:{selectedPeer:Object},emits:["close"],setup(u,{emit:p}){const m=p,f=u,r=g(),t=o(!1),l=o(""),a=o(!0);o({error:!1,message:void 0}),h(()=>{const d=x();y("/api/downloadPeer/"+d.params.id,{id:f.selectedPeer.id},e=>{e.status?(l.value=e.data.file,a.value=!1):this.dashboardStore.newMessage("Server",e.message,"danger")})});const b=async()=>{navigator.clipboard&&navigator.clipboard.writeText?navigator.clipboard.writeText(l.value).then(()=>{t.value=!0,setTimeout(()=>{t.value=!1},3e3)}).catch(()=>{r.newMessage("WGDashboard","Failed to copy","danger")}):(document.querySelector("#peerConfigurationFile").select(),document.execCommand("copy")?(t.value=!0,setTimeout(()=>{t.value=!1},3e3)):r.newMessage("WGDashboard","Failed to copy","danger"))};return(d,e)=>(n(),i("div",M,[s("div",S,[s("div",D,[s("div",L,[s("div",P,[s("h4",B,[c(T,{t:"Peer Configuration File"})]),s("button",{type:"button",class:"btn-close ms-auto",onClick:e[0]||(e[0]=_=>m("close"))})]),s("div",G,[s("div",N,[s("textarea",{style:{height:"300px"},class:C(["form-control w-100 rounded-3 animate__fadeIn animate__faster animate__animated",{"d-none":a.value}]),id:"peerConfigurationFile",value:l.value},null,10,V),a.value?(n(),i("div",j,[...e[2]||(e[2]=[s("span",{class:"visually-hidden"},"Loading...",-1)])])):w("",!0)]),s("div",I,[s("button",{onClick:e[1]||(e[1]=_=>b()),disabled:t.value||a.value,class:"ms-auto btn bg-primary-subtle border-primary-subtle text-primary-emphasis rounded-3 position-relative"},[c(F,{name:"slide-up",mode:"out-in"},{default:k(()=>[t.value?(n(),i("span",q,[...e[4]||(e[4]=[s("i",{class:"bi bi-check-circle-fill"},null,-1)])])):(n(),i("span",$,[...e[3]||(e[3]=[s("i",{class:"bi bi-clipboard-fill"},null,-1)])]))]),_:1})],8,W)])])])])])]))}},H=v(z,[["__scopeId","data-v-b0ea2d46"]]);export{H as default};
@@ -1 +0,0 @@
.slide-up-enter-active[data-v-b0ea2d46],.slide-up-leave-active[data-v-b0ea2d46]{transition:all .2s cubic-bezier(.42,0,.22,1)}.slide-up-enter-from[data-v-b0ea2d46],.slide-up-leave-to[data-v-b0ea2d46]{opacity:0;transform:scale(.9)}@keyframes spin-b0ea2d46{0%{transform:rotate(0)}to{transform:rotate(360deg)}}#check[data-v-b0ea2d46]{animation:cubic-bezier(.42,0,.22,1.3) .7s spin-b0ea2d46}
@@ -1 +0,0 @@
import{L as o}from"./localeText-Dmcj5qqx.js";import{P as t}from"./peersDefaultSettingsInput-KXSGcg6g.js";import{B as s,c as l,a,b as e,f as n}from"./index-DXzxfcZW.js";const r={class:"d-flex gap-3 flex-column"},i={class:"card rounded-3"},d={class:"card-header"},c={class:"my-2"},_={class:"card-body"},D=s({__name:"peerDefaultSettings",setup(p){return(g,m)=>(n(),l("div",r,[a("div",i,[a("div",d,[a("h6",c,[e(o,{t:"Peer Default Settings"})])]),a("div",_,[a("div",null,[e(t,{targetData:"peer_global_dns",title:"DNS"}),e(t,{targetData:"peer_endpoint_allowed_ip",title:"Endpoint Allowed IPs"}),e(t,{targetData:"peer_mtu",title:"MTU"}),e(t,{targetData:"peer_keep_alive",title:"Persistent Keepalive"}),e(t,{targetData:"remote_endpoint",title:"Peer Remote Endpoint",warning:!0,warningText:"This will be changed globally, and will be apply to all peer's QR code and configuration file."})])])])]))}});export{D as default};
@@ -1 +0,0 @@
import{a as p,S as b}from"./schedulePeerJob-DOBEE-kC.js";import{_ as h,h as i,c as a,f as s,a as e,b as r,w as u,d as m,F as _,i as f,j as v,T as J,A as x,W as g}from"./index-DXzxfcZW.js";import{L as w}from"./localeText-Dmcj5qqx.js";import"./vue-datepicker-vren6E8j.js";import"./index-CRsyV-e7.js";import"./dayjs.min-C-brjxlJ.js";const P={name:"peerJobs",setup(){return{store:g()}},props:{selectedPeer:Object},components:{LocaleText:w,SchedulePeerJob:b,ScheduleDropdown:p},data(){return{}},methods:{deleteJob(d){this.selectedPeer.jobs=this.selectedPeer.jobs.filter(t=>t.JobID!==d.JobID)},addJob(){this.selectedPeer.jobs.unshift(JSON.parse(JSON.stringify({JobID:x().toString(),Configuration:this.selectedPeer.configuration.Name,Peer:this.selectedPeer.id,Field:this.store.PeerScheduleJobs.dropdowns.Field[0].value,Operator:this.store.PeerScheduleJobs.dropdowns.Operator[0].value,Value:"",CreationDate:"",ExpireDate:"",Action:this.store.PeerScheduleJobs.dropdowns.Action[0].value})))}}},S={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll"},y={class:"container d-flex h-100 w-100"},$={class:"m-auto modal-dialog-centered dashboardModal"},C={class:"card rounded-3 shadow",style:{width:"700px"}},D={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2"},j={class:"mb-0 fw-normal"},k={class:"card-body px-4 pb-4 pt-2 position-relative"},T={class:"d-flex align-items-center mb-3"},N={class:"card shadow-sm",key:"none",style:{height:"153px"}},I={class:"card-body text-muted text-center d-flex"},L={class:"m-auto"};function O(d,t,B,F,V,A){const n=i("LocaleText"),l=i("SchedulePeerJob");return s(),a("div",S,[e("div",y,[e("div",$,[e("div",C,[e("div",D,[e("h4",j,[r(n,{t:"Schedule Jobs"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:t[0]||(t[0]=o=>this.$emit("close"))})]),e("div",k,[e("div",T,[e("button",{class:"btn bg-primary-subtle border-1 border-primary-subtle text-primary-emphasis rounded-3 shadow",onClick:t[1]||(t[1]=o=>this.addJob())},[t[3]||(t[3]=e("i",{class:"bi bi-plus-lg me-2"},null,-1)),r(n,{t:"Job"})])]),r(J,{name:"schedulePeerJobTransition",tag:"div",class:"position-relative"},{default:u(()=>[(s(!0),a(_,null,f(this.selectedPeer.jobs,(o,E)=>(s(),v(l,{onRefresh:t[2]||(t[2]=c=>this.$emit("refresh")),onDelete:c=>this.deleteJob(o),dropdowns:this.store.PeerScheduleJobs.dropdowns,key:o.JobID,pjob:o},null,8,["onDelete","dropdowns","pjob"]))),128)),this.selectedPeer.jobs.length===0?(s(),a("div",N,[e("div",I,[e("h6",L,[r(n,{t:"This peer does not have any job yet."})])])])):m("",!0)]),_:1})])])])])])}const H=h(P,[["render",O],["__scopeId","data-v-5bbdd42b"]]);export{H as default};
@@ -1 +0,0 @@
.schedulePeerJobTransition-move[data-v-5bbdd42b],.schedulePeerJobTransition-enter-active[data-v-5bbdd42b],.schedulePeerJobTransition-leave-active[data-v-5bbdd42b]{transition:all .4s cubic-bezier(.82,.58,.17,.9)}.schedulePeerJobTransition-enter-from[data-v-5bbdd42b],.schedulePeerJobTransition-leave-to[data-v-5bbdd42b]{opacity:0;transform:scale(.9)}.schedulePeerJobTransition-leave-active[data-v-5bbdd42b]{position:absolute;width:100%}
@@ -1 +0,0 @@
import{S as _}from"./schedulePeerJob-DOBEE-kC.js";import{_ as g,h as c,c as r,f as t,a as e,b as l,F as p,i as b,d as f,t as m,j as v,W as y}from"./index-DXzxfcZW.js";import{L as x}from"./localeText-Dmcj5qqx.js";import"./vue-datepicker-vren6E8j.js";import"./index-CRsyV-e7.js";import"./dayjs.min-C-brjxlJ.js";const J={name:"peerJobsAllModal",setup(){return{store:y()}},components:{LocaleText:x,SchedulePeerJob:_},props:{configurationPeers:Array[Object]},computed:{getAllJobs(){return this.configurationPeers.filter(a=>a.jobs.length>0)}}},w={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll"},$={class:"container d-flex h-100 w-100"},k={class:"m-auto modal-dialog-centered dashboardModal"},A={class:"card rounded-3 shadow",style:{width:"900px"}},L={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2"},S={class:"mb-0 fw-normal"},j={class:"card-body px-4 pb-4 pt-2"},C={key:0,class:"accordion",id:"peerJobsLogsModalAccordion"},P={class:"accordion-header"},M=["data-bs-target"],B={key:0},N={class:"text-muted"},D=["id"],T={class:"accordion-body"},V={key:1,class:"card shadow-sm",style:{height:"153px"}},F={class:"card-body text-muted text-center d-flex"},O={class:"m-auto"};function W(a,o,E,I,R,q){const n=c("LocaleText"),u=c("SchedulePeerJob");return t(),r("div",w,[e("div",$,[e("div",k,[e("div",A,[e("div",L,[e("h4",S,[l(n,{t:"All Active Jobs"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:o[0]||(o[0]=s=>this.$emit("close"))})]),e("div",j,[e("button",{class:"btn bg-primary-subtle border-1 border-primary-subtle text-primary-emphasis rounded-3 shadow mb-2",onClick:o[1]||(o[1]=s=>this.$emit("allLogs"))},[o[4]||(o[4]=e("i",{class:"bi bi-clock me-2"},null,-1)),l(n,{t:"Logs"})]),this.getAllJobs.length>0?(t(),r("div",C,[(t(!0),r(p,null,b(this.getAllJobs,(s,d)=>(t(),r("div",{class:"accordion-item",key:s.id},[e("h2",P,[e("button",{class:"accordion-button collapsed",type:"button","data-bs-toggle":"collapse","data-bs-target":"#collapse_"+d},[e("small",null,[e("strong",null,[s.name?(t(),r("span",B,m(s.name)+" • ",1)):f("",!0),e("samp",N,m(s.id),1)])])],8,M)]),e("div",{id:"collapse_"+d,class:"accordion-collapse collapse","data-bs-parent":"#peerJobsLogsModalAccordion"},[e("div",T,[(t(!0),r(p,null,b(s.jobs,i=>(t(),v(u,{onDelete:o[2]||(o[2]=h=>this.$emit("refresh")),onRefresh:o[3]||(o[3]=h=>this.$emit("refresh")),dropdowns:this.store.PeerScheduleJobs.dropdowns,viewOnly:!0,key:i.JobID,pjob:i},null,8,["dropdowns","pjob"]))),128))])],8,D)]))),128))])):(t(),r("div",V,[e("div",F,[e("span",O,[l(n,{t:"No active job at the moment."})])])]))])])])])])}const X=g(J,[["render",W]]);export{X as default};
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
.icon[data-v-3c48f50e]{flex:1;min-width:30px;max-width:30px;width:30px;aspect-ratio:1 / 1}.icon[data-v-accdf15e]{flex:1;aspect-ratio:1 / 1}#peerTag[data-v-ab3e5c4e]{width:300px;position:absolute;right:0;z-index:9999;margin-top:2px}.animation__fadeInDropdown[data-v-71502547]{animation-name:fadeInDropdown-71502547;animation-duration:.2s;animation-timing-function:cubic-bezier(.82,.58,.17,.9)}@keyframes fadeInDropdown-71502547{0%{opacity:0;filter:blur(3px);transform:translateY(-60px)}to{opacity:1;filter:blur(0px);transform:translateY(-40px)}}.displayModal .dashboardModal[data-v-71502547]{width:400px!important}@media screen and (max-width: 992px){.peerSearchContainer[data-v-71502547]{flex-direction:column}.peerSettingContainer .dashboardModal[data-v-71502547]{width:100%!important}}.peerSearchContainer>button[data-v-71502547],.peerSearchContainer .dropdown>button[data-v-71502547]{text-align:left;display:flex;align-items:center}span[data-v-d4e41a56]{top:-34px;left:0}.dropdown-menu[data-v-18549c26]{right:0;min-width:200px}.dropdown-menu.dropup[data-v-18549c26]{bottom:100%}.dropdown-item.disabled[data-v-18549c26],.dropdown-item[data-v-18549c26]:disabled{opacity:.7}.confirmDelete[data-v-18549c26]{padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x)}.subMenuBtn.active[data-v-f38d3291]{background-color:#ffffff20}.peerCard[data-v-f38d3291]{transition:box-shadow .1s cubic-bezier(.82,.58,.17,.9)}.peerCard[data-v-f38d3291]:hover{box-shadow:var(--bs-box-shadow)!important}@media screen and (max-width: 992px){.calendar-day .session-badge-list[data-v-5178a57b],.sessions-label[data-v-5178a57b]{display:none}}.session-list[data-v-5178a57b]{aspect-ratio:1 / 1}@media screen and (min-width: 992px){.session-dot[data-v-5178a57b]{display:none!important}.session-list[data-v-5178a57b]{height:12.5vh;overflow:scroll;aspect-ratio:auto!important}}.calendar-grid[data-v-3b03c7a5]{display:grid;grid-template-areas:"sun mon tue wed thu fri sat";grid-template-columns:repeat(7,1fr)}.calendar-day.day-6[data-v-3b03c7a5]{border-right:none!important}.calendar-day[data-v-3b03c7a5]{min-height:150px}@media screen and (max-width: 992px){.calendar-day[data-v-3b03c7a5]{min-height:100px!important}}@media screen and (min-width: 992px){.dayDetail[data-v-3b03c7a5]{display:none}}.extra-day .day-label[data-v-3b03c7a5]{opacity:.5}.peerNav .nav-link{&.active[data-v-b4fba9bc]{background-color:#efefef}}th[data-v-b4fba9bc],td[data-v-b4fba9bc]{background-color:transparent!important}@media screen and (max-width: 576px){.titleBtn[data-v-b4fba9bc]{flex-basis:100%}}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{Q as l}from"./browser-DFwZaPoQ.js";import{L as _}from"./localeText-Dmcj5qqx.js";import{_ as h,h as f,c,f as s,a as e,b as p,d as i,j as m,n as u,g,D as v}from"./index-DXzxfcZW.js";import"./galois-field-I2lBzzs-.js";const w={name:"peerQRCode",components:{LocaleText:_},props:{selectedPeer:Object},setup(){return{dashboardStore:v()}},data(){return{loading:!0}},mounted(){g("/api/downloadPeer/"+this.$route.params.id,{id:this.selectedPeer.id},o=>{if(this.loading=!1,o.status){let t="";if(this.selectedPeer.configuration.Protocol==="awg"){let a={containers:[{awg:{isThirdPartyConfig:!0,last_config:o.data.file,port:this.selectedPeer.configuration.ListenPort,transport_proto:"udp"},container:"amnezia-awg"}],defaultContainer:"amnezia-awg",description:this.selectedPeer.name,hostName:this.dashboardStore.Configuration.Peers.remote_endpoint};l.toCanvas(document.querySelector("#awg_vpn_qrcode"),btoa(JSON.stringify(a)),d=>{d&&console.error(d)})}t=o.data.file,l.toCanvas(document.querySelector("#qrcode"),t,a=>{a&&console.error(a)})}else this.dashboardStore.newMessage("Server",o.message,"danger")})}},b={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0"},x={class:"container d-flex h-100 w-100"},P={class:"m-auto modal-dialog-centered dashboardModal justify-content-center"},C={class:"card rounded-3 shadow"},y={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0"},S={class:"mb-0"},k={class:"card-body p-4"},q={class:"d-flex gap-2 flex-column"},L={class:"d-flex flex-column gap-2 align-items-center"},N={key:0,class:"d-flex flex-column gap-2 align-items-center"},Q={key:1,class:"spinner-border m-auto",role:"status"};function z(o,t,a,d,r,A){const n=f("LocaleText");return s(),c("div",b,[e("div",x,[e("div",P,[e("div",C,[e("div",y,[e("h4",S,[p(n,{t:"QR Code"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:t[0]||(t[0]=R=>this.$emit("close"))})]),e("div",k,[e("div",q,[e("div",L,[e("canvas",{id:"qrcode",style:{width:"200px !important",height:"200px !important"},class:u(["rounded-3 shadow animate__animated animate__fadeIn animate__faster qrcode",{"d-none":r.loading}])},null,2),this.selectedPeer.configuration.Protocol==="wg"?(s(),m(n,{key:0,t:"Scan with WireGuard App",class:"text-muted"})):i("",!0),this.selectedPeer.configuration.Protocol==="awg"?(s(),m(n,{key:1,t:"Scan with AmneziaWG App",class:"text-muted"})):i("",!0)]),this.selectedPeer.configuration.Protocol==="awg"?(s(),c("div",N,[e("canvas",{id:"awg_vpn_qrcode",class:u(["rounded-3 shadow animate__animated animate__fadeIn animate__faster qrcode",{"d-none":r.loading}])},null,2),p(n,{t:"Scan with AmneziaVPN App",class:"text-muted"})])):i("",!0),r.loading?(s(),c("div",Q,[...t[1]||(t[1]=[e("span",{class:"visually-hidden"},"Loading...",-1)])])):i("",!0)])])])])])])}const $=h(w,[["render",z],["__scopeId","data-v-02f2240d"]]);export{$ as default};
@@ -1 +0,0 @@
@media screen and (max-width: 768px){.qrcode[data-v-02f2240d]{width:100%!important;height:auto!important;aspect-ratio:1/1}}.qrcode[data-v-02f2240d]{width:200px!important;height:200px!important}
@@ -1 +0,0 @@
import{_ as p,q as m,G as f,W as h,r as u,a0 as _,L as v,K as g,o as x,a1 as S,c as y,d as b,f as B,a as s,m as w,y as T}from"./index-DXzxfcZW.js";const q={key:0,class:"fixed-bottom w-100 bottom-0 z-2 p-3",style:{"z-index":"1"}},C={class:"d-flex flex-column searchPeersContainer ms-auto p-2 rounded-5",style:{width:"300px"}},P={class:"rounded-5 border border-white p-2 d-flex align-items-center gap-1 w-100"},R=["placeholder"],k={__name:"peerSearchBar",props:["ConfigurationInfo"],emits:["close"],setup(V,{emit:z}){const l=m(()=>f("Search Peers..."));let r;const t=h(),e=u(t.searchString),d=()=>{r?(clearTimeout(r),r=setTimeout(()=>{t.searchString=e.value},300)):r=setTimeout(()=>{t.searchString=e.value},300)};_("searchBar");const a=v(),i=g();a.query.peer&&(e.value=a.query.peer,i.replace({query:null}));const n=u(!0);return x(()=>{document.querySelector("#searchPeers").focus()}),S(()=>{n.value=!1}),(G,o)=>n.value?(B(),y("div",q,[s("div",C,[s("div",P,[w(s("input",{ref:"searchBar",class:"flex-grow-1 form-control form-control-sm rounded-5 bg-transparent border-0 border-secondary-subtle",placeholder:l.value,id:"searchPeers",onKeyup:o[0]||(o[0]=c=>d()),"onUpdate:modelValue":o[1]||(o[1]=c=>e.value=c)},null,40,R),[[T,e.value]])])])])):b("",!0)}},K=p(k,[["__scopeId","data-v-576347d8"]]);export{K as default};
@@ -1 +0,0 @@
.searchPeersContainer[data-v-576347d8]{backdrop-filter:blur(8px);width:100%;background:linear-gradient(var(--degree),rgba(45,173,255,.4),rgba(255,108,109,.4),var(--brandColor2) 100%)}#searchPeers[data-v-576347d8]::placeholder{color:#fff}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
.toggleShowKey[data-v-f702a2b1]{position:absolute;top:35px;right:12px}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
.card[data-v-1a7765d4]{border-color:var(--bs-border-color)!important}textarea[data-v-01e380d2]:focus,input[data-v-01e380d2]:focus{box-shadow:none;border-color:var(--bs-border-color)!important}textarea[data-v-01e380d2]{padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x)}
@@ -1 +0,0 @@
import{_ as h,c as o,a as e,m as c,d as m,b as d,h as f,y as g,n as v,t as p,z as b,D as w,A as x,f as r}from"./index-DXzxfcZW.js";import{L as _}from"./localeText-Dmcj5qqx.js";const k={components:{LocaleText:_},props:{targetData:String,title:String,warning:!1,warningText:""},setup(){const s=w(),t=`input_${x()}`;return{store:s,uuid:t}},data(){return{value:"",invalidFeedback:"",showInvalidFeedback:!1,isValid:!1,timeout:void 0,changed:!1,updating:!1}},mounted(){this.value=this.store.Configuration.Peers[this.targetData]},methods:{async useValidation(){this.changed&&await b("/api/updateDashboardConfigurationItem",{section:"Peers",key:this.targetData,value:this.value},s=>{s.status?(this.isValid=!0,this.showInvalidFeedback=!1,this.store.Configuration.Peers[this.targetData]=this.value,clearTimeout(this.timeout),this.timeout=setTimeout(()=>this.isValid=!1,5e3)):(this.isValid=!1,this.showInvalidFeedback=!0,this.invalidFeedback=s.message),this.changed=!1,this.updating=!1})}}},V={class:"form-group mb-2"},D=["for"],y=["id","disabled"],T={class:"invalid-feedback"},C={key:0,class:"px-2 py-1 text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 d-inline-block mt-1"};function F(s,t,a,I,n,u){const l=f("LocaleText");return r(),o("div",V,[e("label",{for:this.uuid,class:"text-muted mb-1"},[e("strong",null,[e("small",null,[d(l,{t:this.title},null,8,["t"])])])],8,D),c(e("input",{type:"text",class:v(["form-control",{"is-invalid":n.showInvalidFeedback,"is-valid":n.isValid}]),id:this.uuid,"onUpdate:modelValue":t[0]||(t[0]=i=>this.value=i),onKeydown:t[1]||(t[1]=i=>this.changed=!0),onBlur:t[2]||(t[2]=i=>u.useValidation()),disabled:this.updating},null,42,y),[[g,this.value]]),e("div",T,p(this.invalidFeedback),1),a.warning?(r(),o("div",C,[e("small",null,[t[3]||(t[3]=e("i",{class:"bi bi-exclamation-triangle-fill me-2"},null,-1)),d(l,{t:a.warningText},null,8,["t"])])])):m("",!0)])}const B=h(k,[["render",F]]);export{B as P};
@@ -1 +0,0 @@
.pingPlaceholder[data-v-a08ce97e]{width:100%;height:79.98px}.ping-move[data-v-a08ce97e],.ping-enter-active[data-v-a08ce97e],.ping-leave-active[data-v-a08ce97e]{transition:all .4s cubic-bezier(.82,.58,.17,.9)}.ping-leave-active[data-v-a08ce97e]{position:absolute;width:100%}.ping-enter-from[data-v-a08ce97e],.ping-leave-to[data-v-a08ce97e]{opacity:0;filter:blur(3px)}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{L as n}from"./localeText-Dmcj5qqx.js";import{c as a,d as r,e as s,j as i,f as e}from"./index-DXzxfcZW.js";const d={class:"position-relative"},c={key:0,class:"badge wireguardBg rounded-3 shadow z-1"},l={key:1,class:"badge amneziawgBg rounded-3 shadow"},p={__name:"protocolBadge",props:{protocol:String,mini:!1},setup(o){return(m,t)=>(e(),a("div",d,[o.protocol==="wg"?(e(),a("span",c,[t[0]||(t[0]=s(" WireGuard ",-1)),o.mini?r("",!0):(e(),i(n,{key:0,t:"Configuration"}))])):o.protocol==="awg"?(e(),a("span",l,[t[1]||(t[1]=s(" AmneziaWG ",-1)),o.mini?r("",!0):(e(),i(n,{key:0,t:"Configuration"}))])):r("",!0)]))}};export{p as _};
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
.dropdownIcon[data-v-ccf48ac7]{transition:all .2s ease-in-out}.dropdownIcon.active[data-v-ccf48ac7]{transform:rotate(180deg)}.steps{&[data-v-324df2b1]{transition:all .3s ease-in-out;opacity:.3}&.active[data-v-324df2b1]{opacity:1}}

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