mirror of
https://github.com/WGDashboard/WGDashboard.git
synced 2026-08-04 15:03:02 +00:00
Compare commits
192 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b15bbce9bc | |||
| ddaada8e4f | |||
| 920f832297 | |||
| 22df8a1b1f | |||
| 468d1c7642 | |||
| 2cdc0265b8 | |||
| df295bf470 | |||
| dfb75d77b8 | |||
| 18ef023817 | |||
| 12da167b8c | |||
| db78f42a04 | |||
| 78a7211680 | |||
| 1bcb48a50e | |||
| 2e01ecc455 | |||
| ebd13b4df9 | |||
| e4d441388d | |||
| a8bf35b0c0 | |||
| 6beda22f39 | |||
| 6b1d2b4b50 | |||
| 3f23c0f949 | |||
| abaaff809a | |||
| eebb2e39cc | |||
| fd2daa2934 | |||
| cc253a6a11 | |||
| 06ce640f56 | |||
| 8c8746cb9b | |||
| 97d92d5310 | |||
| a19ab85ba9 | |||
| 6705705e3d | |||
| 8f973021fc | |||
| 23256a873c | |||
| 20847a0954 | |||
| 0f98bb2537 | |||
| d5026a4081 | |||
| aeed7cf322 | |||
| 29ebe65587 | |||
| ce553de03b | |||
| 601ccb355d | |||
| 2347c76812 | |||
| 9ffba5e33a | |||
| 18018d3079 | |||
| 2b703d6ab8 | |||
| d506fdf2c1 | |||
| e5dc6086fa | |||
| febc8818b0 | |||
| 8b7d372dcb | |||
| 66dd5c4f9f | |||
| 682587183f | |||
| a3da48be8c | |||
| e398a07f32 | |||
| 0745d39a50 | |||
| 87bdc5dde9 | |||
| 126c200fa2 | |||
| 174fd527cc | |||
| 92651729eb | |||
| d51c543346 | |||
| 3adca64624 | |||
| 306e17eb4d | |||
| d75c7ad418 | |||
| 82e10659b6 | |||
| b6f25ac817 | |||
| 48481ab992 | |||
| d56a3cb5d6 | |||
| d6a930c04b | |||
| 6fe257fa4a | |||
| e5d1a64d7d | |||
| da4cec60e5 | |||
| a4d471df4f | |||
| f00940b73c | |||
| cb9cb4a0b6 | |||
| b695ccc5f9 | |||
| 16cd4da5fc | |||
| 7be830a34e | |||
| eb65d6fb2a | |||
| 6fee7da633 | |||
| 2e8d2fe400 | |||
| da3c34c250 | |||
| d864d12793 | |||
| 36390a4d71 | |||
| 5db2baf1dc | |||
| dc10da607d | |||
| 9b4abf19fa | |||
| bf823e6cf8 | |||
| 880c94b8a3 | |||
| 656eba3a57 | |||
| 4154a27f4d | |||
| 785d0d6b06 | |||
| 9af2983a7b | |||
| 83b4753d5f | |||
| f4f0ab27ef | |||
| d51815e0f7 | |||
| 413b65e976 | |||
| b1ea6d3c1d | |||
| c7bed91626 | |||
| b9e09bf91b | |||
| 839f686d73 | |||
| 1ead86dacc | |||
| c4265b31a9 | |||
| 63e6c2040f | |||
| 35de3eb45a | |||
| 7c35f81af1 | |||
| 1a653bfe32 | |||
| 024072688f | |||
| fa59de4a09 | |||
| 4756556c82 | |||
| 66ffc12864 | |||
| 9f609b01de | |||
| a40df0481f | |||
| 2de46d97cf | |||
| bd6d27f282 | |||
| 61c9f5aab7 | |||
| aba3c5da64 | |||
| 2ee1b3b138 | |||
| 876a079be3 | |||
| 1fe03e1664 | |||
| d55770c385 | |||
| cd11e0495a | |||
| 24367db1f5 | |||
| bfc3e285db | |||
| 58dc5a70dc | |||
| c94b8a17bc | |||
| b18ceca439 | |||
| b794c342b1 | |||
| 230fa166b8 | |||
| 0ed5640499 | |||
| 931d547046 | |||
| 30dd9f3e54 | |||
| c98a83e5b1 | |||
| 10c9b5635f | |||
| 9dd5341fd9 | |||
| d763867566 | |||
| 18531b71c7 | |||
| bbeae93c05 | |||
| 3c297cbb96 | |||
| 954f99b354 | |||
| 3f75c0aa3b | |||
| 33d8a1f991 | |||
| 616238735a | |||
| 649c1ad292 | |||
| 145b18c28c | |||
| 34e43de1a1 | |||
| b7ed934331 | |||
| d5a7632bb6 | |||
| 6621c5f910 | |||
| 09a3fddd87 | |||
| 45510165a2 | |||
| 508dbf8dc2 | |||
| eb1d52ffba | |||
| 5db7351f8c | |||
| 5ae3a56337 | |||
| e7068b472e | |||
| 3dc94a35a1 | |||
| f82abd71a3 | |||
| 602238d794 | |||
| 4d4a15740b | |||
| 524d50ee07 | |||
| fc591b7fe8 | |||
| c2f06193d0 | |||
| f2ead12315 | |||
| ca8700ac2a | |||
| 10a8d22efd | |||
| fc3ec61373 | |||
| 094d1c0718 | |||
| 0d814ec03c | |||
| 5ccfe07e12 | |||
| 101ac5e985 | |||
| 113a780eec | |||
| cf77610a56 | |||
| 84675fe521 | |||
| 5db5b35311 | |||
| ff345c9609 | |||
| 6cccfec923 | |||
| 8231dd1463 | |||
| d8ff020d8c | |||
| 238fb91360 | |||
| 9ecc16fcc1 | |||
| 7d9f60cf9b | |||
| 8ee16b4173 | |||
| 74861fa96b | |||
| 442a658487 | |||
| 3706b91b26 | |||
| 48cb54156b | |||
| f3104c29ea | |||
| 689aee34ec | |||
| 3862ea4d28 | |||
| a7e0eb52c2 | |||
| 537a88f618 | |||
| 93a5624294 | |||
| 37539990dd | |||
| 5cd8a80118 | |||
| c74ecc3d75 | |||
| 404bccfb64 |
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: [donaldzou]
|
github: [WGDashboard]
|
||||||
patreon: DonaldDonnyZou
|
open_collective: wgdashboard
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve the project!
|
||||||
title: ''
|
title: ''
|
||||||
labels: bug
|
labels: bug
|
||||||
assignees: ''
|
assignees: ''
|
||||||
@@ -11,6 +11,7 @@ assignees: ''
|
|||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
**Expected Error / Traceback**
|
**Expected Error / Traceback**
|
||||||
|
|
||||||
```
|
```
|
||||||
Please provide the error traceback here
|
Please provide the error traceback here
|
||||||
```
|
```
|
||||||
@@ -20,9 +21,22 @@ Please provide how you run the dashboard
|
|||||||
|
|
||||||
**OS Information:**
|
**OS Information:**
|
||||||
- OS: [e.g. Ubuntu 18.02]
|
- OS: [e.g. Ubuntu 18.02]
|
||||||
- Python Version: [e.g v3.7]
|
- Docker: [e.g. Yes or No]
|
||||||
|
- Python Version: [e.g v37]
|
||||||
|
|
||||||
|
**Docker Details**
|
||||||
|
If using docker, provide the following:
|
||||||
|
- Image used: [e.g. ghcr.io/wgdashboard/wgdashboard:latest]
|
||||||
|
- Compose: [e.g. Yes or No]
|
||||||
|
If Docker Compose is used:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
**Your Docker Compose**
|
||||||
|
```
|
||||||
|
|
||||||
**Sample of your `.conf` file**
|
**Sample of your `.conf` file**
|
||||||
```
|
|
||||||
Please provide a sample of your configuration file that you are having problem with. You can replace your public key and private key to ABCD...
|
```ini
|
||||||
|
Please provide a sample of your configuration file that you are having problem with.
|
||||||
|
You should replace your public key and private key to ABCD...
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: WGDashboard Community Support (Github)
|
||||||
|
url: https://github.com/orgs/WGDashboard/discussions
|
||||||
|
about: Please ask and answer questions here.
|
||||||
|
|
||||||
|
- name: WGDashboard Community Support (Discord)
|
||||||
|
url: https://discord.gg/72TwzjeuWm
|
||||||
|
about: Discord Server about WGDashboard
|
||||||
|
|
||||||
|
- name: WGDashboard Issue reporting (Bugs and Feature Requests (FR))
|
||||||
|
url: https://github.com/WGDashboard/WGDashboard/issues
|
||||||
|
about: Please report bugs or feature requests here so they can be tracked
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: Feature Request (FR)
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea to extend or improve the usage of the project
|
||||||
title: ''
|
title: ''
|
||||||
labels: enhancement
|
labels: enhancement
|
||||||
assignees: ''
|
assignees: ''
|
||||||
@@ -10,5 +10,11 @@ assignees: ''
|
|||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Related/Adjacent Issues**
|
||||||
|
Paste a Github link to possible issues that could be solved if they exist.
|
||||||
|
|
||||||
|
**What have you done yourself?**
|
||||||
|
What have you already tried to do or considered to fix this issue?
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
A clear and concise description of what you want to happen.
|
A clear and concise description of what you want to happen.
|
||||||
|
|||||||
@@ -8,24 +8,24 @@ updates:
|
|||||||
- package-ecosystem: "pip"
|
- package-ecosystem: "pip"
|
||||||
directory: "/src"
|
directory: "/src"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
|
||||||
- package-ecosystem: "npm"
|
- package-ecosystem: "npm"
|
||||||
directory: "/"
|
directory: "/src/static/app"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/.github"
|
directory: "/.github"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/docker"
|
directory: "/docker"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
|
||||||
- package-ecosystem: "docker-compose"
|
- package-ecosystem: "docker-compose"
|
||||||
directory: "/docker"
|
directory: "/docker"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
|||||||
@@ -39,11 +39,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@v4
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v3
|
uses: github/codeql-action/autobuild@v4
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -68,4 +68,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v4
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
name: Clone of Docker Build and Push
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKERHUB_PREFIX: docker.io
|
||||||
|
GITHUB_CONTAINER_PREFIX: ghcr.io
|
||||||
|
DOCKER_IMAGE: WGDashboard
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker_build_debug:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.DOCKERHUB_PREFIX }}
|
||||||
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.GITHUB_CONTAINER_PREFIX }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Extract Docker metadata from environment
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ env.DOCKERHUB_PREFIX }}/donaldzou/${{ env.DOCKER_IMAGE }}
|
||||||
|
${{ env.GITHUB_CONTAINER_PREFIX }}/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=tag
|
||||||
|
type=sha,format=short,prefix=
|
||||||
|
|
||||||
|
- name: Build and export Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
platforms: linux/amd64
|
||||||
|
|
||||||
|
docker_scan_debug:
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: docker_build_debug
|
||||||
|
steps:
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.DOCKERHUB_PREFIX }}
|
||||||
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.GITHUB_CONTAINER_PREFIX }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Docker Scout CVEs
|
||||||
|
uses: docker/scout-action@v1
|
||||||
|
with:
|
||||||
|
command: cves
|
||||||
|
image: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE }}:main
|
||||||
|
only-severities: critical,high
|
||||||
|
only-fixed: true
|
||||||
|
write-comment: true
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
exit-code: true
|
||||||
|
|
||||||
|
- name: Docker Scout Compare
|
||||||
|
uses: docker/scout-action@v1
|
||||||
|
with:
|
||||||
|
command: compare
|
||||||
|
# Set to Github for maximum compat
|
||||||
|
image: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE }}:main
|
||||||
|
to: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE }}:latest
|
||||||
|
only-severities: critical,high
|
||||||
|
ignore-unchanged: true
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -5,8 +5,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
|
- '**dev'
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- 'docker/**'
|
||||||
release:
|
release:
|
||||||
types: [ published ]
|
types: [ published ]
|
||||||
|
|
||||||
@@ -44,15 +48,12 @@ jobs:
|
|||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
with:
|
with:
|
||||||
platforms: |
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
- linux/amd64
|
|
||||||
- linux/arm64
|
|
||||||
- linux/arm/v7
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Docker meta by docs https://github.com/docker/metadata-action
|
- name: Extract Docker metadata from environment
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
@@ -64,7 +65,7 @@ jobs:
|
|||||||
type=ref,event=tag
|
type=ref,event=tag
|
||||||
type=sha,format=short,prefix=
|
type=sha,format=short,prefix=
|
||||||
|
|
||||||
- name: Build and export (multi-arch)
|
- name: Build and export Docker image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
@@ -86,11 +87,18 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.GITHUB_CONTAINER_PREFIX }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Docker Scout CVEs
|
- name: Docker Scout CVEs
|
||||||
uses: docker/scout-action@v1
|
uses: docker/scout-action@v1
|
||||||
with:
|
with:
|
||||||
command: cves
|
command: cves
|
||||||
image: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ env.DOCKER_IMAGE }}:main
|
image: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE }}:main
|
||||||
only-severities: critical,high
|
only-severities: critical,high
|
||||||
only-fixed: true
|
only-fixed: true
|
||||||
write-comment: true
|
write-comment: true
|
||||||
@@ -102,8 +110,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: compare
|
command: compare
|
||||||
# Set to Github for maximum compat
|
# Set to Github for maximum compat
|
||||||
image: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ env.DOCKER_IMAGE }}:main
|
image: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE }}:main
|
||||||
to: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ env.DOCKER_IMAGE }}:latest
|
to: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ github.repository_owner }}/${{ env.DOCKER_IMAGE }}:latest
|
||||||
only-severities: critical,high
|
only-severities: critical,high
|
||||||
ignore-unchanged: true
|
ignore-unchanged: true
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
+4
-1
@@ -1,3 +1,4 @@
|
|||||||
|
*.tar
|
||||||
.vscode
|
.vscode
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
@@ -10,7 +11,7 @@ src/static/pic.xd
|
|||||||
*.conf
|
*.conf
|
||||||
private_key.txt
|
private_key.txt
|
||||||
public_key.txt
|
public_key.txt
|
||||||
venv/**
|
*venv*
|
||||||
log/**
|
log/**
|
||||||
release/*
|
release/*
|
||||||
src/db/wgdashboard.db
|
src/db/wgdashboard.db
|
||||||
@@ -19,6 +20,8 @@ node_modules/**
|
|||||||
*/proxy.js
|
*/proxy.js
|
||||||
src/static/app/proxy.js
|
src/static/app/proxy.js
|
||||||
.secrets
|
.secrets
|
||||||
|
*.ini
|
||||||
|
*.pid
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
> [!TIP]
|
> [!TIP]
|
||||||
> 🎉 I'm excited to announce that WGDashboard is officially listed on DigitalOcean's Marketplace! For more information, please visit [Host WGDashboard & WireGuard with DigitalOcean](https://docs.wgdashboard.dev/host-wgdashboard-wireguard-with-digitalocean.html) for more information!
|
> 🎉 To help us better understand and improve WGDashboard’s performance, we’re launching the **WGDashboard Testing Program**. As part of this program, participants will receive free WireGuard VPN access to our server in Toronto, Canada, valid for **24 hours** or up to **1GB of total traffic**—whichever comes first. If you’d like to join, visit [https://wg.wgdashboard.dev/](https://wg.wgdashboard.dev/) for more details!
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> **Help Wanted 🎉**: Localizing WGDashboard to other languages! If you're willing to help, please visit https://github.com/WGDashboard/WGDashboard/issues/397. Many thanks!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -23,9 +20,11 @@
|
|||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/WGDashboard/WGDashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard?style=for-the-badge"></a>
|
<a href="https://github.com/WGDashboard/WGDashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard?style=for-the-badge"></a>
|
||||||
<a href="https://wakatime.com/badge/github/donaldzou/WGDashboard"><img src="https://wakatime.com/badge/github/donaldzou/WGDashboard.svg?style=for-the-badge" alt="wakatime"></a>
|
<a href="https://wakatime.com/badge/github/donaldzou/WGDashboard"><img src="https://wakatime.com/badge/user/45f53c7c-9da9-4cb0-85d6-17bd38cc748b/project/5334ae20-e9a6-4c55-9fea-52d4eb9dfba6.svg?style=for-the-badge" alt="wakatime"></a>
|
||||||
<a href="https://hitscounter.dev"><img src="https://hitscounter.dev/api/hit?url=https%3A%2F%2Fgithub.com%2Fdonaldzou%2FWGDashboard&label=Visitor&icon=github&color=%230a58ca&style=for-the-badge"></a>
|
<a href="https://hitscounter.dev"><img src="https://hitscounter.dev/api/hit?url=https%3A%2F%2Fgithub.com%2Fdonaldzou%2FWGDashboard&label=Visitor&icon=github&color=%230a58ca&style=for-the-badge"></a>
|
||||||
<img src="https://img.shields.io/docker/pulls/donaldzou/wgdashboard?logo=docker&label=Docker%20Image%20Pulls&labelColor=ffffff&style=for-the-badge">
|
<img src="https://img.shields.io/docker/pulls/donaldzou/wgdashboard?logo=docker&label=Docker%20Image%20Pulls&labelColor=ffffff&style=for-the-badge">
|
||||||
|
<img src="https://github.com/WGDashboard/WGDashboard/actions/workflows/docker.yml/badge.svg?style=for-the-badge">
|
||||||
|
<img src="https://github.com/WGDashboard/WGDashboard/actions/workflows/codeql-analyze.yaml/badge.svg">
|
||||||
</p>
|
</p>
|
||||||
<p align="center"><b>This project is supported by</b></p>
|
<p align="center"><b>This project is supported by</b></p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -33,10 +32,10 @@
|
|||||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p align="center">Monitoring WireGuard is not convenient, in most case, you'll need to login to your server and type <code>wg show</code>. That's why this project is being created, to view and manage all WireGuard configurations in a easy way.</p>
|
<p align="center">Monitoring WireGuard is not convenient, in most case, you'll need to login to your server and type <code>wg show</code>. That's why this project is being created, to view and manage all WireGuard configurations in an easy way.</p>
|
||||||
<p align="center">With all these awesome features, while keeping it <b>easy to install and use</b></p>
|
<p align="center">Though all these awesome features are present, we are still striving to make it <b>easy to install and use</b></p>
|
||||||
|
|
||||||
<p align="center"><b><i>This project is not affiliate to the official WireGuard Project</i></b></p>
|
<p align="center"><b><i>This project is not affiliated to the official WireGuard Project</i></b></p>
|
||||||
|
|
||||||
<h3 align="center">Looking for help or want to chat about this project?</h4>
|
<h3 align="center">Looking for help or want to chat about this project?</h4>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -52,7 +51,7 @@
|
|||||||
You can support via <br>
|
You can support via <br>
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a align="center" href="https://github.com/sponsors/donaldzou" target="_blank"><img src="https://img.shields.io/badge/GitHub%20Sponsor-2e9a40?style=for-the-badge&logo=github"></a>
|
<a align="center" href="https://github.com/sponsors/WGDashboard" target="_blank"><img src="https://img.shields.io/badge/GitHub%20Sponsor-2e9a40?style=for-the-badge&logo=github"></a>
|
||||||
<a align="center" href="https://buymeacoffee.com/donaldzou" target="_blank"><img src="https://img.shields.io/badge/Buy%20me%20a%20coffee-ffdd00?style=for-the-badge&logo=buymeacoffee&logoColor=000000"></a>
|
<a align="center" href="https://buymeacoffee.com/donaldzou" target="_blank"><img src="https://img.shields.io/badge/Buy%20me%20a%20coffee-ffdd00?style=for-the-badge&logo=buymeacoffee&logoColor=000000"></a>
|
||||||
<a align="center" href="https://patreon.com/c/DonaldDonnyZou/membership" target="_blank"><img src="https://img.shields.io/badge/Patreon-000000?style=for-the-badge&logo=patreon&logoColor=ffffff"></a>
|
<a align="center" href="https://patreon.com/c/DonaldDonnyZou/membership" target="_blank"><img src="https://img.shields.io/badge/Patreon-000000?style=for-the-badge&logo=patreon&logoColor=ffffff"></a>
|
||||||
</p>
|
</p>
|
||||||
@@ -74,7 +73,6 @@
|
|||||||
|
|
||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
|
|
||||||
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/sign-in.png" alt=""/>
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/sign-in.png" alt=""/>
|
||||||
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/cross-server.png" alt=""/>
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/cross-server.png" alt=""/>
|
||||||
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/index.png" alt=""/>
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/index.png" alt=""/>
|
||||||
|
|||||||
+2
-4
@@ -4,7 +4,5 @@
|
|||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | ------------------ |
|
| ------- | ------------------ |
|
||||||
| 5.1.x | :white_check_mark: |
|
| 4.3 | :white_check_mark: |
|
||||||
| 5.0.x | :x: |
|
| < 4.3 | :x: |
|
||||||
| 4.0.x | :white_check_mark: |
|
|
||||||
| < 4.0 | :x: |
|
|
||||||
|
|||||||
+46
-28
@@ -3,8 +3,10 @@
|
|||||||
# Base: Alpine
|
# Base: Alpine
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Pull the current golang-alpine image.
|
||||||
FROM golang:1.25-alpine AS awg-go
|
FROM golang:1.25-alpine AS awg-go
|
||||||
|
|
||||||
|
# Install build-dependencies.
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
git \
|
git \
|
||||||
gcc \
|
gcc \
|
||||||
@@ -14,18 +16,23 @@ RUN apk add --no-cache \
|
|||||||
RUN mkdir -p /workspace && \
|
RUN mkdir -p /workspace && \
|
||||||
git clone https://github.com/WGDashboard/amneziawg-go /workspace/awg
|
git clone https://github.com/WGDashboard/amneziawg-go /workspace/awg
|
||||||
|
|
||||||
|
# Enable CGO compilation for AmneziaWG
|
||||||
ENV CGO_ENABLED=1
|
ENV CGO_ENABLED=1
|
||||||
|
|
||||||
|
# Change directory
|
||||||
WORKDIR /workspace/awg
|
WORKDIR /workspace/awg
|
||||||
RUN go mod download && \
|
# Compile the binaries
|
||||||
|
RUN go version && \
|
||||||
|
go mod download && \
|
||||||
go mod verify && \
|
go mod verify && \
|
||||||
go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"' -v -o /usr/bin
|
go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"' -v -o /usr/bin
|
||||||
#
|
#
|
||||||
# AWG TOOLS BUILDING STAGE
|
# AWG TOOLS BUILDING STAGE
|
||||||
# Base: Debian
|
# Base: Alpine
|
||||||
#
|
#
|
||||||
FROM alpine:latest AS awg-tools
|
FROM alpine:latest AS awg-tools
|
||||||
|
|
||||||
|
# Install needed dependencies.
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
make \
|
make \
|
||||||
git \
|
git \
|
||||||
@@ -33,31 +40,47 @@ RUN apk add --no-cache \
|
|||||||
linux-headers \
|
linux-headers \
|
||||||
ca-certificates
|
ca-certificates
|
||||||
|
|
||||||
|
# Get the workspace ready
|
||||||
RUN mkdir -p /workspace && \
|
RUN mkdir -p /workspace && \
|
||||||
git clone https://github.com/WGDashboard/amneziawg-tools /workspace/awg-tools
|
git clone https://github.com/WGDashboard/amneziawg-tools /workspace/awg-tools
|
||||||
|
|
||||||
|
# Change directory
|
||||||
WORKDIR /workspace/awg-tools/src
|
WORKDIR /workspace/awg-tools/src
|
||||||
|
# Compile and change permissions
|
||||||
RUN make && chmod +x wg*
|
RUN make && chmod +x wg*
|
||||||
|
|
||||||
#
|
#
|
||||||
# PIP DEPENDENCY BUILDING
|
# PIP DEPENDENCY BUILDING
|
||||||
# Base: Alpine
|
# Base: Alpine
|
||||||
#
|
#
|
||||||
FROM python:3.13-alpine AS pip-builder
|
|
||||||
|
|
||||||
|
# Use the python-alpine image for building pip dependencies
|
||||||
|
FROM python:3.14-alpine AS pip-builder
|
||||||
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
|
# Add the build dependencies and create a Python virtual environment.
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
build-base \
|
build-base \
|
||||||
pkgconfig \
|
pkgconfig \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
|
postgresql-dev \
|
||||||
libffi-dev \
|
libffi-dev \
|
||||||
|
libpq \
|
||||||
linux-headers \
|
linux-headers \
|
||||||
rust \
|
rust \
|
||||||
cargo \
|
cargo \
|
||||||
&& mkdir -p /opt/wgdashboard/src \
|
&& mkdir -p /opt/wgdashboard/src \
|
||||||
&& python3 -m venv /opt/wgdashboard/src/venv
|
&& python3 -m venv /opt/wgdashboard/src/venv
|
||||||
|
|
||||||
|
# Copy the requirements file into the build layer.
|
||||||
COPY ./src/requirements.txt /opt/wgdashboard/src
|
COPY ./src/requirements.txt /opt/wgdashboard/src
|
||||||
|
RUN if [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then \
|
||||||
|
sed -i 's|psycopg\[binary\]|psycopg[c]|' /opt/wgdashboard/src/requirements.txt; \
|
||||||
|
fi; \
|
||||||
|
cat /opt/wgdashboard/src/requirements.txt
|
||||||
|
|
||||||
|
# Install the pip packages
|
||||||
RUN . /opt/wgdashboard/src/venv/bin/activate && \
|
RUN . /opt/wgdashboard/src/venv/bin/activate && \
|
||||||
pip3 install --upgrade pip && \
|
pip3 install --upgrade pip && \
|
||||||
pip3 install -r /opt/wgdashboard/src/requirements.txt
|
pip3 install -r /opt/wgdashboard/src/requirements.txt
|
||||||
@@ -66,25 +89,26 @@ RUN . /opt/wgdashboard/src/venv/bin/activate && \
|
|||||||
# WGDashboard RUNNING STAGE
|
# WGDashboard RUNNING STAGE
|
||||||
# Base: Alpine
|
# Base: Alpine
|
||||||
#
|
#
|
||||||
FROM python:3.13-alpine AS final
|
|
||||||
|
# Running with the python-alpine image.
|
||||||
|
FROM python:3.14-alpine AS final
|
||||||
LABEL maintainer="dselen@nerthus.nl"
|
LABEL maintainer="dselen@nerthus.nl"
|
||||||
|
|
||||||
# Install only the runtime dependencies
|
# Install only the runtime dependencies
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
iproute2 iptables \
|
iproute2 iptables \
|
||||||
bash curl \
|
bash curl procps openrc \
|
||||||
wget unzip \
|
tzdata wireguard-tools envsubst
|
||||||
procps sudo \
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
tzdata wireguard-tools \
|
|
||||||
openresolv openrc
|
|
||||||
|
|
||||||
# Copy only the final binaries from the builders
|
# Copy only the final binaries from the AWG builder stages
|
||||||
COPY --from=awg-go /usr/bin/amneziawg-go /usr/bin/amneziawg-go
|
COPY --from=awg-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 /usr/bin/awg
|
||||||
COPY --from=awg-tools /workspace/awg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick
|
COPY --from=awg-tools /workspace/awg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick
|
||||||
|
|
||||||
# Environment variables
|
# Environment variables
|
||||||
ARG wg_net="10.0.0.1"
|
ARG wg_net="10.0.0.1"
|
||||||
|
ARG wg_subn="24"
|
||||||
ARG wg_port="51820"
|
ARG wg_port="51820"
|
||||||
ENV TZ="Europe/Amsterdam" \
|
ENV TZ="Europe/Amsterdam" \
|
||||||
global_dns="9.9.9.9" \
|
global_dns="9.9.9.9" \
|
||||||
@@ -92,33 +116,27 @@ ENV TZ="Europe/Amsterdam" \
|
|||||||
public_ip="" \
|
public_ip="" \
|
||||||
WGDASH=/opt/wgdashboard
|
WGDASH=/opt/wgdashboard
|
||||||
|
|
||||||
# Create directories
|
# Create directories needed for operation
|
||||||
RUN mkdir /data /configs -p ${WGDASH}/src /etc/amnezia/amneziawg
|
RUN mkdir /data /configs -p ${WGDASH}/src /etc/amnezia/amneziawg
|
||||||
|
|
||||||
# Copy app source and prebuilt venv only (no pip cache)
|
# Copy the venv and source files from local compiled locations or repos
|
||||||
COPY ./src ${WGDASH}/src
|
COPY ./src ${WGDASH}/src
|
||||||
COPY --from=pip-builder /opt/wgdashboard/src/venv /opt/wgdashboard/src/venv
|
COPY --from=pip-builder /opt/wgdashboard/src/venv /opt/wgdashboard/src/venv
|
||||||
|
COPY ./docker/wg0.conf.template /tmp/wg0.conf.template
|
||||||
|
# Copy in the runtime script, essential.
|
||||||
|
COPY ./docker/entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
# WireGuard interface template
|
# First WireGuard interface template
|
||||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
RUN export out_adapt=$(ip -o -4 route show to default | awk '{print $NF}') \
|
||||||
RUN out_adapt=$(ip -o -4 route show to default | awk '{print $NF}') \
|
&& envsubst < /tmp/wg0.conf.template > /configs/wg0.conf.template \
|
||||||
&& echo -e "[Interface]\n\
|
&& chmod 600 /configs/wg0.conf.template \
|
||||||
Address = ${wg_net}/24\n\
|
&& cat /configs/wg0.conf.template
|
||||||
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
|
|
||||||
|
|
||||||
|
# Set a healthcheck to determine the container its health
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
|
CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
|
||||||
|
|
||||||
COPY ./docker/entrypoint.sh /entrypoint.sh
|
# Expose ports on the container
|
||||||
|
|
||||||
EXPOSE 10086
|
EXPOSE 10086
|
||||||
WORKDIR $WGDASH/src
|
WORKDIR $WGDASH/src
|
||||||
|
|
||||||
|
|||||||
+26
-22
@@ -3,8 +3,8 @@ Author: @DaanSelen<br>
|
|||||||
|
|
||||||
This document delves into how the WGDashboard Docker container has been built.<br>
|
This document delves into how the WGDashboard Docker container has been built.<br>
|
||||||
Of course there are two stages (simply said), one before run-time and one at/after run-time.<br>
|
Of course there are two stages (simply said), one before run-time and one at/after run-time.<br>
|
||||||
The `Dockerfile` describes how the container image is made, and the `entrypoint.sh` is executed after running the container. <br>
|
The `Dockerfile` describes how the container image is made, and the `entrypoint.sh` is executed after the container is started. <br>
|
||||||
In this example, WireGuard is integrated into the container itself, so it should be a run-and-go(/out-of-the-box).<br>
|
In this example, [WireGuard](https://www.wireguard.com/) is integrated into the container itself, so it should be a run-and-go(/out-of-the-box) experience.<br>
|
||||||
For more details on the source-code specific to this Docker image, refer to the source files, they have lots of comments.
|
For more details on the source-code specific to this Docker image, refer to the source files, they have lots of comments.
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
@@ -18,20 +18,24 @@ For more details on the source-code specific to this Docker image, refer to the
|
|||||||
/>
|
/>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
To get the container running you either pull the image from the repository, (docker.io)`donaldzou/wgdashboard:latest`.<br>
|
To get the container running you either pull the pre-made image from a remote repository, there are 2 official options.<br>
|
||||||
From there either use the environment variables describe below as parameters or use the Docker Compose file: `compose.yaml`.<br>
|
|
||||||
Be careful, the default generated WireGuard configuration file uses port 51820/udp. So use this port if you want to use it out of the box.<br>
|
- ghcr.io/wgdashboard/wgdashboard:<tag>
|
||||||
Otherwise edit the configuration file in `/etc/wireguard/wg0.conf`.
|
- docker.io/donaldzou/wgdashboard:<tag>
|
||||||
|
|
||||||
|
> tags should be either: latest, main, <version> or <commit-sha>.
|
||||||
|
|
||||||
|
From there either use the environment variables described below as parameters or use the Docker Compose file: `compose.yaml`.<br>
|
||||||
|
Be careful, the default generated WireGuard configuration file uses port 51820/udp. So make sure to use this port if you want to use it out of the box.<br>
|
||||||
|
Otherwise edit the configuration file in WGDashboard under `Configuration Settings` -> `Edit Raw Configuration File`.
|
||||||
|
|
||||||
|
> Otherwise you need to enter the container and edit: `/etc/wireguard/wg0.conf`.
|
||||||
|
|
||||||
# WGDashboard: 🐳 Docker Deployment Guide
|
# WGDashboard: 🐳 Docker Deployment Guide
|
||||||
|
|
||||||
To run the container, you can either pull the image from Docker Hub or build it yourself. The image is available at:
|
To run the container, you can either pull the image from the Github Container Registry (ghcr.io), Docker Hub (docker.io) or build it yourself. The image is available at:
|
||||||
|
|
||||||
```
|
> `docker.io` is in most cases automatically resolved by the Docker application. Therefor you can ofter specify: `donaldzou/wgdashboard:latest`
|
||||||
docker.io/donaldzou/wgdashboard:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
> `docker.io` is in most cases automatically resolved by the Docker application.
|
|
||||||
|
|
||||||
### 🔧 Quick Docker Run Command
|
### 🔧 Quick Docker Run Command
|
||||||
|
|
||||||
@@ -44,37 +48,37 @@ docker run -d \
|
|||||||
-p 10086:10086/tcp \
|
-p 10086:10086/tcp \
|
||||||
-p 51820:51820/udp \
|
-p 51820:51820/udp \
|
||||||
--cap-add NET_ADMIN \
|
--cap-add NET_ADMIN \
|
||||||
donaldzou/wgdashboard:latest
|
ghcr.io/wgdashboard/wgdashboard:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
> ⚠️ The default WireGuard port is `51820/udp`. If you change this, update the `/etc/wireguard/wg0.conf` accordingly.
|
> ⚠️ The default WireGuard port is `51820/udp`. If you change this, update the `/etc/wireguard/wg0.conf` accordingly.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 📦 Docker Compose Alternative
|
### 📦 Docker Compose Alternative (see the [compose file](./compose.yaml))
|
||||||
|
|
||||||
You can also use Docker Compose for easier configuration:
|
You can also use Docker Compose for easier configuration:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
wgdashboard:
|
wgdashboard:
|
||||||
image: donaldzou/wgdashboard:latest
|
image: ghcr.io/wgdashboard/wgdashboard:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
container_name: wgdashboard
|
container_name: wgdashboard
|
||||||
environment:
|
|
||||||
# - tz=Europe/Amsterdam
|
|
||||||
# - global_dns=1.1.1.1
|
|
||||||
# - public_ip=YOUR_PUBLIC_IP
|
|
||||||
ports:
|
ports:
|
||||||
- 10086:10086/tcp
|
- 10086:10086/tcp
|
||||||
- 51820:51820/udp
|
- 51820:51820/udp
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
- aconf:/etc/amnezia/amneziawg
|
||||||
- conf:/etc/wireguard
|
- conf:/etc/wireguard
|
||||||
- data:/data
|
- data:/data
|
||||||
|
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
aconf:
|
||||||
conf:
|
conf:
|
||||||
data:
|
data:
|
||||||
```
|
```
|
||||||
@@ -85,14 +89,14 @@ volumes:
|
|||||||
|
|
||||||
## 🔄 Updating the Container
|
## 🔄 Updating the Container
|
||||||
|
|
||||||
Updating WGDashboard is currently in **alpha** stage. While the update process may work, it's still under testing.
|
Updating the WGDashboard container should be through 'The Docker Way' - by pulling the newest/newer image and replacing this old one.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚙️ Environment Variables
|
## ⚙️ Environment Variables
|
||||||
|
|
||||||
| Variable | Accepted Values | Default | Example | Description |
|
| Variable | Accepted Values | Default | Example | Description |
|
||||||
| ------------------ | ---------------------------------------- | ----------------------- | --------------------- | ----------------------------------------------------------------------- |
|
| ------------------ | ---------------------------------------- | ----------------------- | ------------------------ | ----------------------------------------------------------------------- |
|
||||||
| `tz` | Timezone | `Europe/Amsterdam` | `America/New_York` | Sets the container's timezone. Useful for accurate logs and scheduling. |
|
| `tz` | Timezone | `Europe/Amsterdam` | `America/New_York` | Sets the container's timezone. Useful for accurate logs and scheduling. |
|
||||||
| `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. |
|
| `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. |
|
||||||
| `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NAT’d. |
|
| `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NAT’d. |
|
||||||
@@ -100,7 +104,7 @@ Updating WGDashboard is currently in **alpha** stage. While the update process m
|
|||||||
| `username` | Any non‐empty string | `-` | `admin` | Username for the WGDashboard web interface account. |
|
| `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). |
|
| `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. |
|
| `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` or `wg0\|\|wg1\|\|wg2` | Auto‐start the WireGuard interface when the container launches. |
|
||||||
| `email_server` | SMTP server address | `-` | `smtp.gmail.com` | SMTP server for sending email notifications. |
|
| `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_port` | SMTP port number | `-` | `587` | Port for connecting to the SMTP server. |
|
||||||
| `email_encryption` | `TLS`, `SSL`, etc. | `-` | `TLS` | Encryption method for email communication. |
|
| `email_encryption` | `TLS`, `SSL`, etc. | `-` | `TLS` | Encryption method for email communication. |
|
||||||
|
|||||||
+21
-2
@@ -1,22 +1,41 @@
|
|||||||
services:
|
services:
|
||||||
wireguard-dashboard:
|
wgdashboard:
|
||||||
image: donaldzou/wgdashboard:latest
|
# Since the github organisation we recommend the ghcr.io.
|
||||||
|
# Alternatively we also still push to docker.io under donaldzou/wgdashboard.
|
||||||
|
# Both share the exact same tags. So they should be interchangable.
|
||||||
|
image: ghcr.io/wgdashboard/wgdashboard:latest
|
||||||
|
|
||||||
|
# Make sure to set the restart policy. Because for a VPN its important to come back IF it crashes.
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
container_name: wgdashboard
|
container_name: wgdashboard
|
||||||
|
|
||||||
|
# Environment variables can be used to configure certain values at startup. Without having to configure it from the dashboard.
|
||||||
|
# By default its all disabled, but uncomment the following lines to apply these. (uncommenting is removing the # character)
|
||||||
|
# Refer to the documentation on https://wgdashboard.dev/ for more info on what everything means.
|
||||||
#environment:
|
#environment:
|
||||||
#- tz= # <--- Set container timezone, default: Europe/Amsterdam.
|
#- tz= # <--- Set container timezone, default: Europe/Amsterdam.
|
||||||
#- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
|
#- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
|
||||||
#- wgd_port= # <--- Set the port WGDashboard will use for its web-server.
|
#- wgd_port= # <--- Set the port WGDashboard will use for its web-server.
|
||||||
|
|
||||||
|
# The following section, ports is very important for exposing more than one Wireguard/AmneziaWireguard interfaces.
|
||||||
|
# Once you create a new configuration and assign a port in the dashboard, don't forget to add it to the ports as well.
|
||||||
|
# Quick-tip: most Wireguard VPN tunnels use UDP. WGDashboard uses HTTP, so tcp.
|
||||||
ports:
|
ports:
|
||||||
- 10086:10086/tcp
|
- 10086:10086/tcp
|
||||||
- 51820:51820/udp
|
- 51820:51820/udp
|
||||||
|
|
||||||
|
# Volumes can be configured however you'd like. The default is using docker volumes.
|
||||||
|
# If you want to use local paths, replace the path before the : with your path.
|
||||||
volumes:
|
volumes:
|
||||||
- aconf:/etc/amnezia/amneziawg
|
- aconf:/etc/amnezia/amneziawg
|
||||||
- conf:/etc/wireguard
|
- conf:/etc/wireguard
|
||||||
- data:/data
|
- data:/data
|
||||||
|
|
||||||
|
# Needed for network administration.
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
|
|
||||||
|
# The following configuration is linked to the above default volumes.
|
||||||
volumes:
|
volumes:
|
||||||
aconf:
|
aconf:
|
||||||
conf:
|
conf:
|
||||||
|
|||||||
+74
-24
@@ -1,12 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
config_file="/data/wg-dashboard.ini"
|
config_file="/data/wg-dashboard.ini"
|
||||||
|
runtime_pid=""
|
||||||
|
|
||||||
trap 'stop_service' SIGTERM
|
trap 'stop_service' SIGTERM
|
||||||
|
|
||||||
# Hash password with bcrypt
|
# Hash password with bcrypt
|
||||||
hash_password() {
|
hash_password() {
|
||||||
python3 -c "import bcrypt; print(bcrypt.hashpw('$1'.encode(), bcrypt.gensalt(12)).decode())"
|
${WGDASH}/src/venv/bin/python3 -c "import bcrypt; print(bcrypt.hashpw('$1'.encode(), bcrypt.gensalt(12)).decode())"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to set or update section/key/value in the INI file
|
# Function to set or update section/key/value in the INI file
|
||||||
@@ -22,7 +23,7 @@ set_ini() {
|
|||||||
if grep -q "^[[:space:]]*${key}[[:space:]]*=" "$config_file"; then
|
if grep -q "^[[:space:]]*${key}[[:space:]]*=" "$config_file"; then
|
||||||
current_value=$(grep "^[[:space:]]*${key}[[:space:]]*=" "$config_file" | cut -d= -f2- | xargs)
|
current_value=$(grep "^[[:space:]]*${key}[[:space:]]*=" "$config_file" | cut -d= -f2- | xargs)
|
||||||
|
|
||||||
# Don't display actual value if it's a password field
|
# Dont display actual value if it's a password field
|
||||||
if [[ "$key" == *"password"* ]]; then
|
if [[ "$key" == *"password"* ]]; then
|
||||||
if [ "$current_value" = "$value" ]; then
|
if [ "$current_value" = "$value" ]; then
|
||||||
echo "- $key is already set correctly (value hidden)"
|
echo "- $key is already set correctly (value hidden)"
|
||||||
@@ -52,7 +53,31 @@ set_ini() {
|
|||||||
|
|
||||||
stop_service() {
|
stop_service() {
|
||||||
echo "[WGDashboard] Stopping WGDashboard..."
|
echo "[WGDashboard] Stopping WGDashboard..."
|
||||||
/bin/bash ./wgd.sh stop
|
|
||||||
|
local max_rounds="10"
|
||||||
|
local round="0"
|
||||||
|
local runtime_pid=""
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
round=$((round + 1))
|
||||||
|
|
||||||
|
if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
|
||||||
|
runtime_pid=$(cat ${WGDASH}/src/gunicorn.pid)
|
||||||
|
|
||||||
|
echo "Running as PID: ${runtime_pid}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $round -eq $max_rounds ]]; then
|
||||||
|
echo "Reached breaking point!"
|
||||||
|
return 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 0.5s
|
||||||
|
done
|
||||||
|
|
||||||
|
kill $runtime_pid
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,16 +121,8 @@ ensure_installation() {
|
|||||||
ln -s "${config_file}" "${WGDASH}/src/wg-dashboard.ini"
|
ln -s "${config_file}" "${WGDASH}/src/wg-dashboard.ini"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create the Python virtual environment.
|
|
||||||
. "${WGDASH}/src/venv/bin/activate"
|
|
||||||
|
|
||||||
# Use the bash interpreter to install WGDashboard according to the wgd.sh script.
|
|
||||||
/bin/bash ./wgd.sh install
|
|
||||||
|
|
||||||
echo "Looks like the installation succeeded. Moving on."
|
|
||||||
|
|
||||||
# Setup WireGuard if needed
|
# Setup WireGuard if needed
|
||||||
if [ ! -f "/etc/wireguard/wg0.conf" ]; then
|
if [ -z "$(ls -A /etc/wireguard)" ]; then
|
||||||
cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
|
cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
|
||||||
|
|
||||||
echo "Setting a secure private key."
|
echo "Setting a secure private key."
|
||||||
@@ -196,25 +213,58 @@ start_and_monitor() {
|
|||||||
chmod 600 /dev/net/tun
|
chmod 600 /dev/net/tun
|
||||||
|
|
||||||
# Actually starting WGDashboard
|
# Actually starting WGDashboard
|
||||||
echo "Activating Python venv and executing the WireGuard Dashboard service."
|
echo "Starting WGDashboard directly with Gunicorn..."
|
||||||
bash ./wgd.sh start
|
|
||||||
|
[[ ! -d ${WGDASH}/src/log ]] && mkdir ${WGDASH}/src/log
|
||||||
|
[[ ! -d ${WGDASH}/src/download ]] && mkdir ${WGDASH}/src/download
|
||||||
|
|
||||||
|
${WGDASH}/src/venv/bin/gunicorn --config ${WGDASH}/src/gunicorn.conf.py
|
||||||
|
|
||||||
|
/usr/sbin/resolvconf -u
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Loading WGDashboard failed... Look above for details."
|
||||||
|
fi
|
||||||
|
|
||||||
# Wait a second before continuing, to give the python program some time to get ready.
|
# Wait a second before continuing, to give the python program some time to get ready.
|
||||||
sleep 1
|
|
||||||
echo -e "\nEnsuring container continuation."
|
echo -e "\nEnsuring container continuation."
|
||||||
|
|
||||||
# Find and monitor log file
|
local max_rounds="10"
|
||||||
local logdir="${WGDASH}/src/log"
|
local round="0"
|
||||||
latestErrLog=$(find "$logdir" -name "error_*.log" -type f -print | sort -r | head -n 1)
|
|
||||||
|
# Hang in there for 10s for Gunicorn to get ready
|
||||||
|
while true; do
|
||||||
|
round=$((round + 1))
|
||||||
|
|
||||||
|
local latest_error=$(ls -t ${WGDASH}/src/log/error_*.log 2> /dev/null | head -n 1)
|
||||||
|
|
||||||
|
if [[ $round -eq $max_rounds ]]; then
|
||||||
|
echo "Reached breaking point!"
|
||||||
|
break
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $latest_error ]]; then
|
||||||
|
echo -e "Logs not yet present! Retrying in 1 second!"
|
||||||
|
sleep 1s
|
||||||
|
|
||||||
# Only tail the logs if they are found
|
|
||||||
if [ -n "$latestErrLog" ]; then
|
|
||||||
tail -f "$latestErrLog" &
|
|
||||||
# Wait for the tail process to end.
|
|
||||||
wait $!
|
|
||||||
else
|
else
|
||||||
echo "No log files found to tail. Something went wrong, exiting..."
|
break
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z $latest_error ]]; then
|
||||||
|
echo -e "No error logs founds... Please investigate.\nExiting in 3 minutes..."
|
||||||
|
sleep 180s
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
else
|
||||||
|
tail -f "$latest_error" &
|
||||||
|
tail_pid=$!
|
||||||
|
|
||||||
|
wait $tail_pid
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[Interface]
|
||||||
|
Address = ${wg_net}/24
|
||||||
|
PrivateKey =
|
||||||
|
PostUp = iptables -t nat -I POSTROUTING 1 -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE; iptables -I FORWARD -i wg0 -o wg0 -j DROP
|
||||||
|
PreDown = iptables -t nat -D POSTROUTING -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE; iptables -D FORWARD -i wg0 -o wg0 -j DROP
|
||||||
|
ListenPort = ${wg_port}
|
||||||
|
SaveConfig = true
|
||||||
|
DNS = ${global_dns}
|
||||||
Generated
-1407
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^1.9.0",
|
|
||||||
"marked": "^15.0.7",
|
|
||||||
"openai": "^4.89.0",
|
|
||||||
"pinia-plugin-persistedstate": "^4.2.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
version: "1.0"
|
|
||||||
linter: jetbrains/qodana-python:2024.3
|
|
||||||
profile:
|
|
||||||
name: qodana.recommended
|
|
||||||
include:
|
|
||||||
- name: CheckDependencyLicenses
|
|
||||||
+80
-41
@@ -93,7 +93,9 @@ def peerInformationBackgroundThread():
|
|||||||
c.getPeersEndpoint()
|
c.getPeersEndpoint()
|
||||||
c.getPeers()
|
c.getPeers()
|
||||||
if delay == 6:
|
if delay == 6:
|
||||||
|
if c.configurationInfo.PeerTrafficTracking:
|
||||||
c.logPeersTraffic()
|
c.logPeersTraffic()
|
||||||
|
if c.configurationInfo.PeerHistoricalEndpointTracking:
|
||||||
c.logPeersHistoryEndpoint()
|
c.logPeersHistoryEndpoint()
|
||||||
c.getRestrictedPeersList()
|
c.getRestrictedPeersList()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -194,7 +196,7 @@ with app.app_context():
|
|||||||
DashboardConfig = DashboardConfig()
|
DashboardConfig = DashboardConfig()
|
||||||
EmailSender = EmailSender(DashboardConfig)
|
EmailSender = EmailSender(DashboardConfig)
|
||||||
AllPeerShareLinks: PeerShareLinks = PeerShareLinks(DashboardConfig, WireguardConfigurations)
|
AllPeerShareLinks: PeerShareLinks = PeerShareLinks(DashboardConfig, WireguardConfigurations)
|
||||||
AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations)
|
AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations, AllPeerShareLinks)
|
||||||
DashboardLogger: DashboardLogger = DashboardLogger()
|
DashboardLogger: DashboardLogger = DashboardLogger()
|
||||||
DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
|
DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
|
||||||
DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig)
|
DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig)
|
||||||
@@ -223,13 +225,6 @@ def auth_req():
|
|||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
DashboardConfig.APIAccessed = False
|
DashboardConfig.APIAccessed = False
|
||||||
if "api" in request.path:
|
|
||||||
if str(request.method) == "GET":
|
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=str(request.args))
|
|
||||||
elif str(request.method) == "POST":
|
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Request Args: {str(request.args)} Body:{str(request.get_json())}")
|
|
||||||
|
|
||||||
|
|
||||||
authenticationRequired = DashboardConfig.GetConfig("Server", "auth_req")[1]
|
authenticationRequired = DashboardConfig.GetConfig("Server", "auth_req")[1]
|
||||||
d = request.headers
|
d = request.headers
|
||||||
if authenticationRequired:
|
if authenticationRequired:
|
||||||
@@ -251,17 +246,26 @@ def auth_req():
|
|||||||
DashboardConfig.APIAccessed = True
|
DashboardConfig.APIAccessed = True
|
||||||
else:
|
else:
|
||||||
DashboardConfig.APIAccessed = False
|
DashboardConfig.APIAccessed = False
|
||||||
|
appPrefix = APP_PREFIX if len(APP_PREFIX) > 0 else ''
|
||||||
whiteList = [
|
whiteList = [
|
||||||
'/static/', 'validateAuthentication', 'authenticate', 'getDashboardConfiguration',
|
# f'/static/',
|
||||||
'getDashboardTheme', 'getDashboardVersion', 'sharePeer/get', 'isTotpEnabled', 'locale',
|
f'{appPrefix}/api/validateAuthentication',
|
||||||
'/fileDownload',
|
f'{appPrefix}/api/authenticate',
|
||||||
'/client'
|
# f'{appPrefix}/api/getDashboardConfiguration',
|
||||||
|
f'{appPrefix}/api/getDashboardTheme',
|
||||||
|
f'{appPrefix}/api/getDashboardVersion',
|
||||||
|
f'{appPrefix}/api/sharePeer/get',
|
||||||
|
f'{appPrefix}/api/isTotpEnabled',
|
||||||
|
f'{appPrefix}/api/locale',
|
||||||
]
|
]
|
||||||
|
|
||||||
if (("username" not in session or session.get("role") != "admin")
|
|
||||||
and (f"{(APP_PREFIX if len(APP_PREFIX) > 0 else '')}/" != request.path
|
if (
|
||||||
and f"{(APP_PREFIX if len(APP_PREFIX) > 0 else '')}" != request.path)
|
("username" not in session or session.get("role") != "admin")
|
||||||
and len(list(filter(lambda x : x not in request.path, whiteList))) == len(whiteList)
|
and (f"{appPrefix}/" != request.path and f"{appPrefix}" != request.path)
|
||||||
|
and not request.path.startswith(f'{appPrefix}/client')
|
||||||
|
and not request.path.startswith(f'{appPrefix}/static')
|
||||||
|
and request.path not in whiteList
|
||||||
):
|
):
|
||||||
response = Flask.make_response(app, {
|
response = Flask.make_response(app, {
|
||||||
"status": False,
|
"status": False,
|
||||||
@@ -606,12 +610,19 @@ def API_deleteWireguardConfigurationBackup():
|
|||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/downloadWireguardConfigurationBackup')
|
@app.get(f'{APP_PREFIX}/api/downloadWireguardConfigurationBackup')
|
||||||
def API_downloadWireguardConfigurationBackup():
|
def API_downloadWireguardConfigurationBackup():
|
||||||
configurationName = request.args.get('configurationName')
|
configurationName = os.path.basename(request.args.get('configurationName'))
|
||||||
backupFileName = request.args.get('backupFileName')
|
backupFileName = os.path.basename(request.args.get('backupFileName'))
|
||||||
|
|
||||||
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
|
||||||
status, zip = WireguardConfigurations[configurationName].downloadBackup(backupFileName)
|
status, zip = WireguardConfigurations[configurationName].downloadBackup(backupFileName)
|
||||||
return ResponseObject(status, data=zip, status_code=(200 if status else 404))
|
|
||||||
|
if not status:
|
||||||
|
current_app.logger.error(f"Failed to download a requested backup.\nConfiguration Name: {configurationName}\nBackup File Name: {backupFileName}")
|
||||||
|
return ResponseObject(False, "Internal server error", status_code=500)
|
||||||
|
|
||||||
|
return send_file(os.path.join('download', zip), as_attachment=True)
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
|
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
|
||||||
def API_restoreWireguardConfigurationBackup():
|
def API_restoreWireguardConfigurationBackup():
|
||||||
@@ -856,15 +867,21 @@ def API_addPeers(configName):
|
|||||||
preshared_key: str = data.get('preshared_key', "")
|
preshared_key: str = data.get('preshared_key', "")
|
||||||
|
|
||||||
if type(mtu) is not int or mtu < 0 or mtu > 1460:
|
if type(mtu) is not int or mtu < 0 or mtu > 1460:
|
||||||
default: str = DashboardConfig.GetConfig("Peers", "peer_MTU")[1]
|
default: str = DashboardConfig.GetConfig("Peers", "peer_mtu")[1]
|
||||||
if default.isnumeric():
|
if default.isnumeric():
|
||||||
mtu = default
|
try:
|
||||||
|
mtu = int(default)
|
||||||
|
except Exception as e:
|
||||||
|
mtu = 0
|
||||||
else:
|
else:
|
||||||
mtu = 0
|
mtu = 0
|
||||||
if type(keep_alive) is not int or keep_alive < 0:
|
if type(keep_alive) is not int or keep_alive < 0:
|
||||||
default = DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1]
|
default = DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1]
|
||||||
if default.isnumeric():
|
if default.isnumeric():
|
||||||
keep_alive = default
|
try:
|
||||||
|
keep_alive = int(default)
|
||||||
|
except Exception as e:
|
||||||
|
keep_alive = 0
|
||||||
else:
|
else:
|
||||||
keep_alive = 0
|
keep_alive = 0
|
||||||
|
|
||||||
@@ -1084,12 +1101,10 @@ def API_GetPeerTraffics():
|
|||||||
interval = request.args.get('interval', 30)
|
interval = request.args.get('interval', 30)
|
||||||
startDate = request.args.get('startDate', None)
|
startDate = request.args.get('startDate', None)
|
||||||
endDate = request.args.get('endDate', None)
|
endDate = request.args.get('endDate', None)
|
||||||
|
|
||||||
if type(interval) is str:
|
if type(interval) is str:
|
||||||
if not interval.isdigit():
|
if not interval.isdigit():
|
||||||
return ResponseObject(False, "Interval must be integers in minutes")
|
return ResponseObject(False, "Interval must be integers in minutes")
|
||||||
interval = int(interval)
|
interval = int(interval)
|
||||||
|
|
||||||
if startDate is None:
|
if startDate is None:
|
||||||
endDate = None
|
endDate = None
|
||||||
else:
|
else:
|
||||||
@@ -1107,6 +1122,44 @@ def API_GetPeerTraffics():
|
|||||||
return ResponseObject(data=p.getTraffics(interval, startDate, endDate))
|
return ResponseObject(data=p.getTraffics(interval, startDate, endDate))
|
||||||
return ResponseObject(False, "Peer does not exist")
|
return ResponseObject(False, "Peer does not exist")
|
||||||
|
|
||||||
|
@app.get(f'{APP_PREFIX}/api/getPeerTrackingTableCounts')
|
||||||
|
def API_GetPeerTrackingTableCounts():
|
||||||
|
configurationName = request.args.get("configurationName")
|
||||||
|
if configurationName not in WireguardConfigurations.keys():
|
||||||
|
return ResponseObject(False, "Configuration does not exist")
|
||||||
|
c = WireguardConfigurations.get(configurationName)
|
||||||
|
return ResponseObject(data={
|
||||||
|
"TrafficTrackingTableSize": c.getTransferTableSize(),
|
||||||
|
"HistoricalTrackingTableSize": c.getHistoricalEndpointTableSize()
|
||||||
|
})
|
||||||
|
|
||||||
|
@app.get(f'{APP_PREFIX}/api/downloadPeerTrackingTable')
|
||||||
|
def API_DownloadPeerTackingTable():
|
||||||
|
configurationName = request.args.get("configurationName")
|
||||||
|
table = request.args.get('table')
|
||||||
|
if configurationName not in WireguardConfigurations.keys():
|
||||||
|
return ResponseObject(False, "Configuration does not exist")
|
||||||
|
if table not in ['TrafficTrackingTable', 'HistoricalTrackingTable']:
|
||||||
|
return ResponseObject(False, "Table does not exist")
|
||||||
|
c = WireguardConfigurations.get(configurationName)
|
||||||
|
return ResponseObject(
|
||||||
|
data=c.downloadTransferTable() if table == 'TrafficTrackingTable'
|
||||||
|
else c.downloadHistoricalEndpointTable())
|
||||||
|
|
||||||
|
@app.post(f'{APP_PREFIX}/api/deletePeerTrackingTable')
|
||||||
|
def API_DeletePeerTrackingTable():
|
||||||
|
data = request.get_json()
|
||||||
|
configurationName = data.get('configurationName')
|
||||||
|
table = data.get('table')
|
||||||
|
if not configurationName or configurationName not in WireguardConfigurations.keys():
|
||||||
|
return ResponseObject(False, "Configuration does not exist")
|
||||||
|
if not table or table not in ['TrafficTrackingTable', 'HistoricalTrackingTable']:
|
||||||
|
return ResponseObject(False, "Table does not exist")
|
||||||
|
c = WireguardConfigurations.get(configurationName)
|
||||||
|
return ResponseObject(
|
||||||
|
status=c.deleteTransferTable() if table == 'TrafficTrackingTable'
|
||||||
|
else c.deleteHistoryEndpointTable())
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getDashboardTheme')
|
@app.get(f'{APP_PREFIX}/api/getDashboardTheme')
|
||||||
def API_getDashboardTheme():
|
def API_getDashboardTheme():
|
||||||
return ResponseObject(data=DashboardConfig.GetConfig("Server", "dashboard_theme")[1])
|
return ResponseObject(data=DashboardConfig.GetConfig("Server", "dashboard_theme")[1])
|
||||||
@@ -1170,20 +1223,6 @@ def API_getPeerScheduleJobLogs(configName):
|
|||||||
requestAll = True
|
requestAll = True
|
||||||
return ResponseObject(data=AllPeerJobs.getPeerJobLogs(configName))
|
return ResponseObject(data=AllPeerJobs.getPeerJobLogs(configName))
|
||||||
|
|
||||||
'''
|
|
||||||
File Download
|
|
||||||
'''
|
|
||||||
@app.get(f'{APP_PREFIX}/fileDownload')
|
|
||||||
def API_download():
|
|
||||||
file = request.args.get('file')
|
|
||||||
if file is None or len(file) == 0:
|
|
||||||
return ResponseObject(False, "Please specify a file")
|
|
||||||
if os.path.exists(os.path.join('download', file)):
|
|
||||||
return send_file(os.path.join('download', file), as_attachment=True)
|
|
||||||
else:
|
|
||||||
return ResponseObject(False, "File does not exist")
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Tools
|
Tools
|
||||||
'''
|
'''
|
||||||
@@ -1201,8 +1240,9 @@ def API_ping_getAllPeersIpAddress():
|
|||||||
ip = ipaddress.ip_network(x, strict=False)
|
ip = ipaddress.ip_network(x, strict=False)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
app.logger.error(f"Failed to parse IP address of {p.id} - {c.Name}")
|
app.logger.error(f"Failed to parse IP address of {p.id} - {c.Name}")
|
||||||
if len(list(ip.hosts())) == 1:
|
host = list(ip.hosts())
|
||||||
parsed.append(str(ip.hosts()[0]))
|
if len(host) == 1:
|
||||||
|
parsed.append(str(host[0]))
|
||||||
endpoint = p.endpoint.replace(" ", "").replace("(none)", "")
|
endpoint = p.endpoint.replace(" ", "").replace("(none)", "")
|
||||||
if len(p.name) > 0:
|
if len(p.name) > 0:
|
||||||
cips[f"{p.name} - {p.id}"] = {
|
cips[f"{p.name} - {p.id}"] = {
|
||||||
@@ -1457,7 +1497,6 @@ def API_Email_PreviewBody():
|
|||||||
try:
|
try:
|
||||||
template = Template(body)
|
template = Template(body)
|
||||||
download = p.downloadPeer()
|
download = p.downloadPeer()
|
||||||
# body = template.render(peer=p.toJson(), configurationFile=download)
|
|
||||||
return ResponseObject(data={
|
return ResponseObject(data={
|
||||||
"Body": Template(body).render(peer=p.toJson(), configurationFile=download),
|
"Body": Template(body).render(peer=p.toJson(), configurationFile=download),
|
||||||
"Subject": Template(subject).render(peer=p.toJson(), configurationFile=download)
|
"Subject": Template(subject).render(peer=p.toJson(), configurationFile=download)
|
||||||
|
|||||||
@@ -174,8 +174,6 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
|
|||||||
|
|
||||||
def getPeers(self):
|
def getPeers(self):
|
||||||
self.Peers.clear()
|
self.Peers.clear()
|
||||||
current_app.logger.info(f"Refreshing {self.Name} peer list")
|
|
||||||
|
|
||||||
if self.configurationFileChanged():
|
if self.configurationFileChanged():
|
||||||
with open(self.configPath, 'r') as configFile:
|
with open(self.configPath, 'r') as configFile:
|
||||||
p = []
|
p = []
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from .DashboardAPIKey import DashboardAPIKey
|
|||||||
|
|
||||||
|
|
||||||
class DashboardConfig:
|
class DashboardConfig:
|
||||||
DashboardVersion = 'v4.3'
|
DashboardVersion = 'v4.3.2'
|
||||||
ConfigurationPath = os.getenv('CONFIGURATION_PATH', '.')
|
ConfigurationPath = os.getenv('CONFIGURATION_PATH', '.')
|
||||||
ConfigurationFilePath = os.path.join(ConfigurationPath, 'wg-dashboard.ini')
|
ConfigurationFilePath = os.path.join(ConfigurationPath, 'wg-dashboard.ini')
|
||||||
|
|
||||||
|
|||||||
@@ -192,6 +192,10 @@ class Peer:
|
|||||||
for (key, val) in combine[s]:
|
for (key, val) in combine[s]:
|
||||||
if val is not None and ((type(val) is str and len(val) > 0) or (type(val) is int and val > 0)):
|
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"] += f"{key} = {val}\n"
|
||||||
|
|
||||||
|
final["file"] = jinja2.Template(final["file"]).render(configuration=self.configuration)
|
||||||
|
|
||||||
|
|
||||||
if self.configuration.Protocol == "awg":
|
if self.configuration.Protocol == "awg":
|
||||||
final["amneziaVPN"] = json.dumps({
|
final["amneziaVPN"] = json.dumps({
|
||||||
"containers": [{
|
"containers": [{
|
||||||
|
|||||||
@@ -2,8 +2,12 @@
|
|||||||
Peer Job Logger
|
Peer Job Logger
|
||||||
"""
|
"""
|
||||||
import uuid
|
import uuid
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
from sqlalchemy import RowMapping
|
||||||
|
|
||||||
from .ConnectionString import ConnectionString
|
from .ConnectionString import ConnectionString
|
||||||
from .Log import Log
|
from .Log import Log
|
||||||
|
|
||||||
@@ -57,3 +61,41 @@ class PeerJobLogger:
|
|||||||
current_app.logger.error(f"Getting Peer Job Log Error", e)
|
current_app.logger.error(f"Getting Peer Job Log Error", e)
|
||||||
return logs
|
return logs
|
||||||
return logs
|
return logs
|
||||||
|
|
||||||
|
def getFailingJobs(self) -> Sequence[RowMapping]:
|
||||||
|
with self.engine.connect() as conn:
|
||||||
|
table = conn.execute(
|
||||||
|
db.select(
|
||||||
|
self.jobLogTable.c.JobID
|
||||||
|
).where(
|
||||||
|
(db.or_(
|
||||||
|
self.jobLogTable.c.Status == 'false',
|
||||||
|
self.jobLogTable.c.Status == 0
|
||||||
|
) if conn.dialect.name == 'sqlite' else self.jobLogTable.c.Status == 'false')
|
||||||
|
).group_by(
|
||||||
|
self.jobLogTable.c.JobID
|
||||||
|
).having(
|
||||||
|
db.func.count(
|
||||||
|
self.jobLogTable.c.JobID
|
||||||
|
) > 10
|
||||||
|
)
|
||||||
|
).mappings().fetchall()
|
||||||
|
return table
|
||||||
|
|
||||||
|
def deleteLogs(self, LogID = None, JobID = None):
|
||||||
|
with self.engine.begin() as conn:
|
||||||
|
print(f"[WGDashboard] Deleted stale logs of JobID: {JobID}")
|
||||||
|
conn.execute(
|
||||||
|
self.jobLogTable.delete().where(
|
||||||
|
db.and_(
|
||||||
|
(self.jobLogTable.c.LogID == LogID if LogID is not None else True),
|
||||||
|
(self.jobLogTable.c.JobID == JobID if JobID is not None else True),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def vacuum(self):
|
||||||
|
with self.engine.begin() as conn:
|
||||||
|
if conn.dialect.name == 'sqlite':
|
||||||
|
print("[WGDashboard] SQLite Vacuuming PeerJobLogs Database")
|
||||||
|
conn.execute(db.text('VACUUM;'))
|
||||||
+28
-8
@@ -1,6 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
Peer Jobs
|
Peer Jobs
|
||||||
"""
|
"""
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
from .ConnectionString import ConnectionString
|
from .ConnectionString import ConnectionString
|
||||||
from .PeerJob import PeerJob
|
from .PeerJob import PeerJob
|
||||||
from .PeerJobLogger import PeerJobLogger
|
from .PeerJobLogger import PeerJobLogger
|
||||||
@@ -9,7 +11,7 @@ from datetime import datetime
|
|||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
class PeerJobs:
|
class PeerJobs:
|
||||||
def __init__(self, DashboardConfig, WireguardConfigurations):
|
def __init__(self, DashboardConfig, WireguardConfigurations, AllPeerShareLinks):
|
||||||
self.Jobs: list[PeerJob] = []
|
self.Jobs: list[PeerJob] = []
|
||||||
self.engine = db.create_engine(ConnectionString('wgdashboard_job'))
|
self.engine = db.create_engine(ConnectionString('wgdashboard_job'))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
@@ -28,6 +30,8 @@ class PeerJobs:
|
|||||||
self.__getJobs()
|
self.__getJobs()
|
||||||
self.JobLogger: PeerJobLogger = PeerJobLogger(self, DashboardConfig)
|
self.JobLogger: PeerJobLogger = PeerJobLogger(self, DashboardConfig)
|
||||||
self.WireguardConfigurations = WireguardConfigurations
|
self.WireguardConfigurations = WireguardConfigurations
|
||||||
|
self.AllPeerShareLinks = AllPeerShareLinks
|
||||||
|
self.cleanJob(init=True)
|
||||||
|
|
||||||
def __getJobs(self):
|
def __getJobs(self):
|
||||||
self.Jobs.clear()
|
self.Jobs.clear()
|
||||||
@@ -116,7 +120,7 @@ class PeerJobs:
|
|||||||
}
|
}
|
||||||
).where(self.peerJobTable.columns.JobID == Job.JobID)
|
).where(self.peerJobTable.columns.JobID == Job.JobID)
|
||||||
)
|
)
|
||||||
self.JobLogger.log(Job.JobID, Message=f"Job is removed due to being deleted or finshed.")
|
self.JobLogger.log(Job.JobID, Message=f"Job is removed due to being deleted or finished.")
|
||||||
self.__getJobs()
|
self.__getJobs()
|
||||||
self.WireguardConfigurations.get(Job.Configuration).searchPeer(Job.Peer)[1].getJobs()
|
self.WireguardConfigurations.get(Job.Configuration).searchPeer(Job.Peer)[1].getJobs()
|
||||||
return True, None
|
return True, None
|
||||||
@@ -141,7 +145,7 @@ class PeerJobs:
|
|||||||
|
|
||||||
|
|
||||||
def runJob(self):
|
def runJob(self):
|
||||||
current_app.logger.info("Running scheduled jobs")
|
self.cleanJob()
|
||||||
needToDelete = []
|
needToDelete = []
|
||||||
self.__getJobs()
|
self.__getJobs()
|
||||||
for job in self.Jobs:
|
for job in self.Jobs:
|
||||||
@@ -162,7 +166,7 @@ class PeerJobs:
|
|||||||
if job.Action == "restrict":
|
if job.Action == "restrict":
|
||||||
s, msg = c.restrictPeers([fp.id])
|
s, msg = c.restrictPeers([fp.id])
|
||||||
elif job.Action == "delete":
|
elif job.Action == "delete":
|
||||||
s, msg = c.deletePeers([fp.id])
|
s, msg = c.deletePeers([fp.id], self, self.AllPeerShareLinks)
|
||||||
elif job.Action == "reset_total_data_usage":
|
elif job.Action == "reset_total_data_usage":
|
||||||
s = fp.resetDataUsage("total")
|
s = fp.resetDataUsage("total")
|
||||||
c.restrictPeers([fp.id])
|
c.restrictPeers([fp.id])
|
||||||
@@ -171,26 +175,42 @@ class PeerJobs:
|
|||||||
self.JobLogger.log(job.JobID, s,
|
self.JobLogger.log(job.JobID, s,
|
||||||
f"Peer {fp.id} from {c.Name} is successfully {job.Action}ed."
|
f"Peer {fp.id} from {c.Name} is successfully {job.Action}ed."
|
||||||
)
|
)
|
||||||
current_app.logger.info(f"Peer {fp.id} from {c.Name} is successfully {job.Action}ed.")
|
|
||||||
needToDelete.append(job)
|
needToDelete.append(job)
|
||||||
else:
|
else:
|
||||||
current_app.logger.info(f"Peer {fp.id} from {c.Name} is failed {job.Action}ed.")
|
|
||||||
self.JobLogger.log(job.JobID, s,
|
self.JobLogger.log(job.JobID, s,
|
||||||
f"Peer {fp.id} from {c.Name} failed {job.Action}ed."
|
f"Peer {fp.id} from {c.Name} failed {job.Action}ed."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
current_app.logger.warning(f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed.")
|
|
||||||
self.JobLogger.log(job.JobID, False,
|
self.JobLogger.log(job.JobID, False,
|
||||||
f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed."
|
f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
current_app.logger.warning(f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed.")
|
|
||||||
self.JobLogger.log(job.JobID, False,
|
self.JobLogger.log(job.JobID, False,
|
||||||
f"Somehow can't find this peer {job.Peer} from {job.Configuration} failed {job.Action}ed."
|
f"Somehow can't find this peer {job.Peer} from {job.Configuration} failed {job.Action}ed."
|
||||||
)
|
)
|
||||||
for j in needToDelete:
|
for j in needToDelete:
|
||||||
self.deleteJob(j)
|
self.deleteJob(j)
|
||||||
|
|
||||||
|
def cleanJob(self, init = False):
|
||||||
|
failingJobs = self.JobLogger.getFailingJobs()
|
||||||
|
with self.engine.begin() as conn:
|
||||||
|
for job in failingJobs:
|
||||||
|
conn.execute(
|
||||||
|
self.peerJobTable.update().values(
|
||||||
|
{
|
||||||
|
"ExpireDate": datetime.now()
|
||||||
|
}
|
||||||
|
).where(self.peerJobTable.columns.JobID == job.get('JobID'))
|
||||||
|
)
|
||||||
|
self.JobLogger.deleteLogs(JobID=job.get('JobID'))
|
||||||
|
self.JobLogger.log(job.get('JobID'), Message=f"Job is removed due to being stale.")
|
||||||
|
|
||||||
|
with self.engine.connect() as conn:
|
||||||
|
if init and conn.dialect.name == 'sqlite':
|
||||||
|
print("[WGDashboard] SQLite Vacuuming PeerJobs Database")
|
||||||
|
self.JobLogger.vacuum()
|
||||||
|
conn.execute(sqlalchemy.text('VACUUM;'))
|
||||||
|
|
||||||
def __runJob_Compare(self, x: float | datetime, y: float | datetime, operator: str):
|
def __runJob_Compare(self, x: float | datetime, y: float | datetime, operator: str):
|
||||||
if operator == "eq":
|
if operator == "eq":
|
||||||
return x == y
|
return x == y
|
||||||
|
|||||||
@@ -160,16 +160,39 @@ class Processes:
|
|||||||
self.CPU_Top_10_Processes: list[Process] = []
|
self.CPU_Top_10_Processes: list[Process] = []
|
||||||
self.Memory_Top_10_Processes: list[Process] = []
|
self.Memory_Top_10_Processes: list[Process] = []
|
||||||
def getData(self):
|
def getData(self):
|
||||||
while True:
|
|
||||||
try:
|
try:
|
||||||
processes = list(psutil.process_iter())
|
processes = list(psutil.process_iter())
|
||||||
self.CPU_Top_10_Processes = sorted(
|
|
||||||
list(map(lambda x : Process(x.name(), " ".join(x.cmdline()), x.pid, x.cpu_percent()), processes)),
|
cpu_processes = []
|
||||||
key=lambda x : x.percent, reverse=True)[:20]
|
memory_processes = []
|
||||||
self.Memory_Top_10_Processes = sorted(
|
|
||||||
list(map(lambda x : Process(x.name(), " ".join(x.cmdline()), x.pid, x.memory_percent()), processes)),
|
for proc in processes:
|
||||||
key=lambda x : x.percent, reverse=True)[:20]
|
try:
|
||||||
break
|
name = proc.name()
|
||||||
|
cmdline = " ".join(proc.cmdline())
|
||||||
|
pid = proc.pid
|
||||||
|
cpu_percent = proc.cpu_percent()
|
||||||
|
mem_percent = proc.memory_percent()
|
||||||
|
|
||||||
|
# Create Process object for CPU and memory tracking
|
||||||
|
cpu_process = Process(name, cmdline, pid, cpu_percent)
|
||||||
|
mem_process = Process(name, cmdline, pid, mem_percent)
|
||||||
|
|
||||||
|
cpu_processes.append(cpu_process)
|
||||||
|
memory_processes.append(mem_process)
|
||||||
|
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||||
|
# Skip processes we can’t access or that no longer exist
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Sort by CPU and memory usage (descending order)
|
||||||
|
cpu_sorted = sorted(cpu_processes, key=lambda p: p.percent, reverse=True)
|
||||||
|
mem_sorted = sorted(memory_processes, key=lambda p: p.percent, reverse=True)
|
||||||
|
|
||||||
|
# Get top 20 processes for each
|
||||||
|
self.CPU_Top_10_Processes = cpu_sorted[:20]
|
||||||
|
self.Memory_Top_10_Processes = mem_sorted[:20]
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error("Get processes error", e)
|
current_app.logger.error("Get processes error", e)
|
||||||
|
|
||||||
|
|||||||
@@ -396,8 +396,6 @@ class WireguardConfiguration:
|
|||||||
|
|
||||||
def getPeers(self):
|
def getPeers(self):
|
||||||
tmpList = []
|
tmpList = []
|
||||||
current_app.logger.info(f"Refreshing {self.Name} peer list")
|
|
||||||
|
|
||||||
if self.configurationFileChanged():
|
if self.configurationFileChanged():
|
||||||
with open(self.configPath, 'r') as configFile:
|
with open(self.configPath, 'r') as configFile:
|
||||||
p = []
|
p = []
|
||||||
@@ -505,6 +503,15 @@ class WireguardConfiguration:
|
|||||||
if tempPeer.status == "running":
|
if tempPeer.status == "running":
|
||||||
endpoint = tempPeer.endpoint.rsplit(":", 1)
|
endpoint = tempPeer.endpoint.rsplit(":", 1)
|
||||||
if len(endpoint) == 2 and len(endpoint[0]) > 0:
|
if len(endpoint) == 2 and len(endpoint[0]) > 0:
|
||||||
|
exist = conn.execute(
|
||||||
|
self.peersHistoryEndpointTable.select().where(
|
||||||
|
sqlalchemy.and_(
|
||||||
|
self.peersHistoryEndpointTable.c.id == tempPeer.id,
|
||||||
|
self.peersHistoryEndpointTable.c.endpoint == endpoint[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).mappings().fetchone()
|
||||||
|
if not exist:
|
||||||
conn.execute(
|
conn.execute(
|
||||||
self.peersHistoryEndpointTable.insert().values({
|
self.peersHistoryEndpointTable.insert().values({
|
||||||
"id": tempPeer.id,
|
"id": tempPeer.id,
|
||||||
@@ -1188,7 +1195,7 @@ class WireguardConfiguration:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def updateConfigurationInfo(self, key: str, value: str | dict[str, str] | dict[str, dict]) -> tuple[bool, Any, str] | tuple[
|
def updateConfigurationInfo(self, key: str, value: str | dict[str, str] | dict[str, dict] | bool) -> tuple[bool, Any, str] | tuple[
|
||||||
bool, str, None] | tuple[bool, None, None]:
|
bool, str, None] | tuple[bool, None, None]:
|
||||||
if key == "Description":
|
if key == "Description":
|
||||||
self.configurationInfo.Description = value
|
self.configurationInfo.Description = value
|
||||||
@@ -1207,9 +1214,12 @@ class WireguardConfiguration:
|
|||||||
for name, data in value.items():
|
for name, data in value.items():
|
||||||
peerGroups[name] = PeerGroupsClass(**data)
|
peerGroups[name] = PeerGroupsClass(**data)
|
||||||
self.configurationInfo.PeerGroups = peerGroups
|
self.configurationInfo.PeerGroups = peerGroups
|
||||||
|
elif key == "PeerTrafficTracking":
|
||||||
|
self.configurationInfo.PeerTrafficTracking = value
|
||||||
|
elif key == "PeerHistoricalEndpointTracking":
|
||||||
|
self.configurationInfo.PeerHistoricalEndpointTracking = value
|
||||||
else:
|
else:
|
||||||
return False, "Key does not exist", None
|
return False, "Key does not exist", None
|
||||||
|
|
||||||
self.storeConfigurationInfo()
|
self.storeConfigurationInfo()
|
||||||
return True, None, None
|
return True, None, None
|
||||||
|
|
||||||
@@ -1227,3 +1237,58 @@ class WireguardConfiguration:
|
|||||||
msg = "Listen Port must be >= 1 and <= 65535"
|
msg = "Listen Port must be >= 1 and <= 65535"
|
||||||
return status, msg
|
return status, msg
|
||||||
|
|
||||||
|
def getTransferTableSize(self):
|
||||||
|
with self.engine.connect() as db:
|
||||||
|
row_count = db.execute(
|
||||||
|
sqlalchemy.select(sqlalchemy.func.count()).select_from(self.peersTransferTable)
|
||||||
|
).scalar()
|
||||||
|
return int(row_count)
|
||||||
|
|
||||||
|
def getHistoricalEndpointTableSize(self):
|
||||||
|
with self.engine.connect() as db:
|
||||||
|
row_count = db.execute(
|
||||||
|
sqlalchemy.select(sqlalchemy.func.count()).select_from(self.peersHistoryEndpointTable)
|
||||||
|
).scalar()
|
||||||
|
return int(row_count)
|
||||||
|
|
||||||
|
def downloadTransferTable(self):
|
||||||
|
with self.engine.connect() as db:
|
||||||
|
data = db.execute(
|
||||||
|
self.peersTransferTable.select()
|
||||||
|
).mappings().fetchall()
|
||||||
|
return data
|
||||||
|
|
||||||
|
def downloadHistoricalEndpointTable(self):
|
||||||
|
with self.engine.connect() as db:
|
||||||
|
data = db.execute(
|
||||||
|
self.peersHistoryEndpointTable.select()
|
||||||
|
).mappings().fetchall()
|
||||||
|
return data
|
||||||
|
|
||||||
|
def deleteTransferTable(self):
|
||||||
|
try:
|
||||||
|
with self.engine.begin() as db:
|
||||||
|
db.execute(
|
||||||
|
self.peersTransferTable.delete()
|
||||||
|
)
|
||||||
|
with self.engine.connect() as conn:
|
||||||
|
if conn.dialect.name == 'sqlite':
|
||||||
|
print("[WGDashboard] SQLite Vacuuming Database")
|
||||||
|
conn.execute(sqlalchemy.text('VACUUM;'))
|
||||||
|
except Exception as e:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def deleteHistoryEndpointTable(self):
|
||||||
|
try:
|
||||||
|
with self.engine.begin() as db:
|
||||||
|
db.execute(
|
||||||
|
self.peersHistoryEndpointTable.delete()
|
||||||
|
)
|
||||||
|
with self.engine.connect() as conn:
|
||||||
|
if conn.dialect.name == 'sqlite':
|
||||||
|
print("[WGDashboard] SQLite Vacuuming Database")
|
||||||
|
conn.execute(sqlalchemy.text('VACUUM;'))
|
||||||
|
except Exception as e:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
@@ -19,3 +19,5 @@ class WireguardConfigurationInfo(BaseModel):
|
|||||||
Description: str = ''
|
Description: str = ''
|
||||||
OverridePeerSettings: OverridePeerSettingsClass = OverridePeerSettingsClass(**{})
|
OverridePeerSettings: OverridePeerSettingsClass = OverridePeerSettingsClass(**{})
|
||||||
PeerGroups: dict[str, PeerGroupsClass] = {}
|
PeerGroups: dict[str, PeerGroupsClass] = {}
|
||||||
|
PeerTrafficTracking: bool = True
|
||||||
|
PeerHistoricalEndpointTracking: bool = True
|
||||||
+17
-17
@@ -1,17 +1,17 @@
|
|||||||
bcrypt
|
bcrypt==5.0.0
|
||||||
ifcfg
|
ifcfg==0.24
|
||||||
psutil
|
psutil==7.2.2
|
||||||
pyotp
|
pyotp==2.9.0
|
||||||
Flask
|
Flask==3.1.2
|
||||||
flask-cors
|
flask-cors==6.0.2
|
||||||
icmplib
|
icmplib==3.0.4
|
||||||
gunicorn
|
gunicorn==25.0.3
|
||||||
requests
|
requests==2.32.5
|
||||||
tcconfig
|
tcconfig==0.30.1
|
||||||
sqlalchemy
|
sqlalchemy==2.0.46
|
||||||
sqlalchemy_utils
|
sqlalchemy_utils==0.42.1
|
||||||
psycopg
|
psycopg[binary]==3.3.2
|
||||||
PyMySQL
|
PyMySQL==1.1.2
|
||||||
tzlocal
|
tzlocal==5.3.1
|
||||||
python-jose
|
python-jose==3.5.0
|
||||||
pydantic
|
pydantic==2.12.5
|
||||||
|
|||||||
Generated
+2588
-2664
File diff suppressed because it is too large
Load Diff
+23
-20
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"version": "4.3.0",
|
"version": "4.3.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "es2022",
|
"module": "es2022",
|
||||||
@@ -12,35 +12,38 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@volar/language-server": "2.4.23",
|
"@volar/language-server": "2.4.28",
|
||||||
"@vue/language-server": "3.0.5",
|
"@vue/language-server": "3.2.4",
|
||||||
"@vuepic/vue-datepicker": "^11.0.2",
|
"@vuepic/vue-datepicker": "^12.1.0",
|
||||||
"@vueuse/core": "^13.5.0",
|
"@vueuse/core": "^14.2.0",
|
||||||
"@vueuse/shared": "^13.5.0",
|
"@vueuse/shared": "^14.1.0",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"bootstrap": "^5.3.2",
|
"bootstrap": "^5.3.2",
|
||||||
"bootstrap-icons": "^1.11.3",
|
"bootstrap-icons": "^1.11.3",
|
||||||
"cidr-tools": "^11.0.3",
|
"cidr-tools": "^11.0.8",
|
||||||
"css-color-converter": "^2.0.0",
|
"css-color-converter": "^2.0.0",
|
||||||
"dayjs": "^1.11.12",
|
"dayjs": "^1.11.19",
|
||||||
"electron-builder": "^26.0.12",
|
"electron-builder": "^26.7.0",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"is-cidr": "^5.0.3",
|
"is-cidr": "^6.0.3",
|
||||||
"npm": "^10.5.0",
|
"npm": "^11.8.0",
|
||||||
"ol": "^10.2.1",
|
"ol": "^10.7.0",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.4",
|
||||||
"pinia-plugin-persistedstate": "^4.5.0",
|
"pinia-plugin-persistedstate": "^4.7.1",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"qrcodejs": "^1.0.0",
|
"qrcodejs": "^1.0.0",
|
||||||
"simple-code-editor": "^2.0.9",
|
"simple-code-editor": "^2.0.9",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^13.0.0",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.28",
|
||||||
"vue-chartjs": "^5.3.0",
|
"vue-chartjs": "^5.3.3",
|
||||||
"vue-router": "^4.2.5"
|
"vue-router": "^5.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
"@vitejs/plugin-vue": "^6.0.4",
|
||||||
"vite": "^7.0.5"
|
"vite": "^7.3.1"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"tar": "^7.5.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const collapse = ref(false)
|
|||||||
<div
|
<div
|
||||||
@click="collapse = !collapse"
|
@click="collapse = !collapse"
|
||||||
role="button"
|
role="button"
|
||||||
class="card-header rounded-0 sticky-top z-5 bg-body-secondary border-0 border-bottom text-white d-flex">
|
class="card-header rounded-0 sticky-top bg-body-secondary border-0 border-bottom text-white d-flex">
|
||||||
<small><samp>{{ configuration }}</samp></small>
|
<small><samp>{{ configuration }}</samp></small>
|
||||||
<a role="button" class="ms-auto text-white" >
|
<a role="button" class="ms-auto text-white" >
|
||||||
<i class="bi bi-chevron-compact-down" v-if="collapse"></i>
|
<i class="bi bi-chevron-compact-down" v-if="collapse"></i>
|
||||||
|
|||||||
+1
-8
@@ -46,14 +46,7 @@ const restoreBackup = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const downloadBackup = () => {
|
const downloadBackup = () => {
|
||||||
fetchGet("/api/downloadWireguardConfigurationBackup", {
|
window.location.href = getUrl(`/api/downloadWireguardConfigurationBackup?configurationName=${route.params.id}&backupFileName=${props.b.filename}`);
|
||||||
configurationName: route.params.id,
|
|
||||||
backupFileName: props.b.filename
|
|
||||||
}, (res) => {
|
|
||||||
if (res.status){
|
|
||||||
window.open(getUrl(`/fileDownload?file=${res.data}`), '_blank')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const delaySeconds = computed(() => {
|
const delaySeconds = computed(() => {
|
||||||
|
|||||||
@@ -77,11 +77,11 @@ const emits = defineEmits(["backup", "close"])
|
|||||||
</div>
|
</div>
|
||||||
<div v-else-if="backups.length > 0">
|
<div v-else-if="backups.length > 0">
|
||||||
<i class="bi bi-check-circle-fill me-2"></i>
|
<i class="bi bi-check-circle-fill me-2"></i>
|
||||||
<LocaleText :t="'This configuration have ' + backups.length + ' backups'"></LocaleText>
|
<LocaleText :t="'This configuration has ' + backups.length + ' backups'"></LocaleText>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="d-flex align-items-center gap-2">
|
<div v-else class="d-flex align-items-center gap-2">
|
||||||
<i class="bi bi-x-circle-fill me-2"></i>
|
<i class="bi bi-x-circle-fill me-2"></i>
|
||||||
<LocaleText t="This configuration have no backup"></LocaleText>
|
<LocaleText t="This configuration has no backup"></LocaleText>
|
||||||
<a role="button"
|
<a role="button"
|
||||||
@click="emits('backup')"
|
@click="emits('backup')"
|
||||||
class="ms-auto btn btn-sm btn-primary rounded-3">
|
class="ms-auto btn btn-sm btn-primary rounded-3">
|
||||||
|
|||||||
@@ -146,8 +146,8 @@ export default {
|
|||||||
<td v-if="showLogID"><samp class="text-muted">{{log.LogID}}</samp></td>
|
<td v-if="showLogID"><samp class="text-muted">{{log.LogID}}</samp></td>
|
||||||
<td v-if="showJobID"><samp class="text-muted">{{log.JobID}}</samp></td>
|
<td v-if="showJobID"><samp class="text-muted">{{log.JobID}}</samp></td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge" :class="[log.Status === '1' ? 'text-success-emphasis bg-success-subtle':'text-danger-emphasis bg-danger-subtle']">
|
<span class="badge" :class="[(log.Status === '1' || log.Status === 'true') ? 'text-success-emphasis bg-success-subtle':'text-danger-emphasis bg-danger-subtle']">
|
||||||
{{log.Status === "1" ? 'Success': 'Failed'}}
|
{{(log.Status === "1" || log.Status === "true") ? 'Success': 'Failed'}}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>{{log.Message}}</td>
|
<td>{{log.Message}}</td>
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import ScheduleDropdown from "@/components/configurationComponents/peerScheduleJ
|
|||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
import {fetchPost} from "@/utilities/fetch.js";
|
import {fetchPost} from "@/utilities/fetch.js";
|
||||||
import VueDatePicker from "@vuepic/vue-datepicker";
|
import { VueDatePicker } from "@vuepic/vue-datepicker";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import LocaleText from "@/components/text/localeText.vue";
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStor
|
|||||||
import {fetchPost} from "@/utilities/fetch.js";
|
import {fetchPost} from "@/utilities/fetch.js";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
import VueDatePicker from '@vuepic/vue-datepicker';
|
import { VueDatePicker } from '@vuepic/vue-datepicker';
|
||||||
import LocaleText from "@/components/text/localeText.vue";
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
import PeerShareWithEmail from "@/components/configurationComponents/peerShareLinkComponents/peerShareWithEmail.vue";
|
import PeerShareWithEmail from "@/components/configurationComponents/peerShareLinkComponents/peerShareWithEmail.vue";
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import {fetchPost} from "@/utilities/fetch.js";
|
import {fetchPost} from "@/utilities/fetch.js";
|
||||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
import VueDatePicker from "@vuepic/vue-datepicker";
|
import { VueDatePicker } from "@vuepic/vue-datepicker";
|
||||||
import LocaleText from "@/components/text/localeText.vue";
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import ConfigurationTracking
|
||||||
|
from "@/components/settingsComponent/dashboardWireguardConfigurationTrackingComponents/configurationTracking.vue";
|
||||||
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||||
|
|
||||||
|
const store = WireguardConfigurationsStore()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h6 class="my-2">
|
||||||
|
<LocaleText t="Peer Tracking"></LocaleText>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body d-flex flex-column gap-3">
|
||||||
|
<ConfigurationTracking :configuration="configuration" v-for="configuration in store.Configurations"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
+192
@@ -0,0 +1,192 @@
|
|||||||
|
<script setup>
|
||||||
|
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||||
|
import {onMounted, ref, watch} from "vue";
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
|
||||||
|
const props = defineProps(['configuration'])
|
||||||
|
const sizes = ref({
|
||||||
|
HistoricalTrackingTableSize: 0,
|
||||||
|
TrafficTrackingTableSize: 0
|
||||||
|
})
|
||||||
|
const sizeDataLoaded = ref(false)
|
||||||
|
const toggling = ref(false)
|
||||||
|
|
||||||
|
await onMounted(async () => {
|
||||||
|
await loadSizeData();
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadSizeData = async () => {
|
||||||
|
sizeDataLoaded.value = false;
|
||||||
|
await fetchGet("/api/getPeerTrackingTableCounts", {
|
||||||
|
configurationName: props.configuration.Name
|
||||||
|
}, (res) => {
|
||||||
|
sizes.value = res.data;
|
||||||
|
sizeDataLoaded.value = true;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateToggle = async (key) => {
|
||||||
|
toggling.value = true;
|
||||||
|
await fetchPost('/api/updateWireguardConfigurationInfo', {
|
||||||
|
Name: props.configuration.Name,
|
||||||
|
Key: key,
|
||||||
|
Value: props.configuration.Info[key]
|
||||||
|
}, (res) => {
|
||||||
|
console.log(res)
|
||||||
|
toggling.value = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const downloading = ref(undefined);
|
||||||
|
const download = async (key) => {
|
||||||
|
downloading.value = key;
|
||||||
|
await fetchGet("/api/downloadPeerTrackingTable", {
|
||||||
|
configurationName: props.configuration.Name,
|
||||||
|
table: key
|
||||||
|
}, (res) => {
|
||||||
|
if (res.status){
|
||||||
|
const s = JSON.stringify(res.data, null, 2)
|
||||||
|
const b = new Blob([s], {
|
||||||
|
type: "application/json"
|
||||||
|
})
|
||||||
|
const url = URL.createObjectURL(b)
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = url;
|
||||||
|
a.download = `${props.configuration.Name}_${key}.json`
|
||||||
|
a.click()
|
||||||
|
downloading.value = undefined;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmDelete = ref("")
|
||||||
|
const deleting = ref(undefined)
|
||||||
|
const dashboardStore = DashboardConfigurationStore()
|
||||||
|
const deleteRecord = async (key) => {
|
||||||
|
deleting.value = true;
|
||||||
|
await fetchPost('/api/deletePeerTrackingTable', {
|
||||||
|
configurationName: props.configuration.Name,
|
||||||
|
table: key
|
||||||
|
}, async(res) => {
|
||||||
|
if (res.status){
|
||||||
|
dashboardStore.newMessage('Server', 'Record deleted', 'success')
|
||||||
|
}else{
|
||||||
|
dashboardStore.newMessage('Server', 'Record delete failed', 'danger')
|
||||||
|
}
|
||||||
|
await loadSizeData()
|
||||||
|
deleting.value = false;
|
||||||
|
confirmDelete.value = ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
{{ configuration.Name }}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row gy-2">
|
||||||
|
<div class="col-sm">
|
||||||
|
<small class="text-muted fw-bold">Peer Traffic Tracking</small>
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox"
|
||||||
|
:disabled="toggling"
|
||||||
|
@change="updateToggle('PeerTrafficTracking')"
|
||||||
|
v-model="configuration.Info.PeerTrafficTracking" :id="configuration.Name + '_traffic_tracking'">
|
||||||
|
<label class="form-check-label" :for="configuration.Name + '_traffic_tracking'">
|
||||||
|
{{ configuration.Info.PeerTrafficTracking ? 'On':'Off'}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="d-flex align-items-start align-items-md-center flex-column flex-md-row gap-2">
|
||||||
|
<h6 class="mb-0">
|
||||||
|
{{ sizes.TrafficTrackingTableSize }} <span class="text-muted fw-normal"><LocaleText t="Records"></LocaleText></span>
|
||||||
|
</h6>
|
||||||
|
<div class="ms-md-auto d-flex gap-2" v-if="confirmDelete !== 'TrafficTrackingTable'">
|
||||||
|
<button class="btn btn-sm bg-primary-subtle text-primary-emphasis rounded-3"
|
||||||
|
:class="{disabled: downloading === 'TrafficTrackingTable'}"
|
||||||
|
@click="download('TrafficTrackingTable')"
|
||||||
|
>
|
||||||
|
<i class="bi bi-download me-2 "></i>
|
||||||
|
<LocaleText :t="downloading === 'TrafficTrackingTable' ? 'Downloading...':'Download'"></LocaleText>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm bg-danger-subtle text-danger-emphasis rounded-3" @click="confirmDelete = 'TrafficTrackingTable'">
|
||||||
|
<i class="bi bi-trash me-2 "></i>Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="ms-md-auto d-flex gap-2 align-items-center" v-else-if="confirmDelete === 'TrafficTrackingTable'">
|
||||||
|
<small>
|
||||||
|
<LocaleText t="Are you sure to delete?"></LocaleText>
|
||||||
|
</small>
|
||||||
|
<button class="btn btn-sm bg-danger-subtle text-danger-emphasis rounded-3"
|
||||||
|
:class="{disabled: deleting}"
|
||||||
|
@click="deleteRecord('TrafficTrackingTable')">
|
||||||
|
<i class="bi bi-check me-2 "></i>Yes
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:class="{disabled: deleting}"
|
||||||
|
class="btn btn-sm bg-secondary-subtle text-secondary-emphasis rounded-3" @click="confirmDelete = ''">
|
||||||
|
<i class="bi bi-x me-2 "></i>No
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<small class="text-muted fw-bold">Peer Historical Endpoint Tracking</small>
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input"
|
||||||
|
:disabled="toggling"
|
||||||
|
@change="updateToggle('PeerHistoricalEndpointTracking')"
|
||||||
|
type="checkbox" v-model="configuration.Info.PeerHistoricalEndpointTracking" :id="configuration.Name + '_historicalEndpoint_tracking'">
|
||||||
|
<label class="form-check-label" :for="configuration.Name + '_historicalEndpoint_tracking'">
|
||||||
|
{{ configuration.Info.PeerHistoricalEndpointTracking ? 'On':'Off'}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="d-flex align-items-start align-items-md-center flex-column flex-md-row gap-2">
|
||||||
|
<div>
|
||||||
|
<h6 class="placeholder animate__animated animate__flash animate__infinite animate__slower w-100 mb-0" v-if="!sizeDataLoaded"></h6>
|
||||||
|
<h6 v-else class="mb-0">
|
||||||
|
{{ sizes.HistoricalTrackingTableSize }} <span class="text-muted fw-normal"><LocaleText t="Records"></LocaleText></span>
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div class="ms-md-auto d-flex gap-2" v-if="confirmDelete !== 'HistoricalTrackingTable'">
|
||||||
|
<button
|
||||||
|
@click="download('HistoricalTrackingTable')"
|
||||||
|
:class="{disabled: downloading === 'HistoricalTrackingTable'}"
|
||||||
|
class="btn btn-sm bg-primary-subtle text-primary-emphasis rounded-3">
|
||||||
|
<i class="bi bi-download me-2 "></i>
|
||||||
|
<LocaleText :t="downloading === 'HistoricalTrackingTable' ? 'Downloading...':'Download'"></LocaleText>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm bg-danger-subtle text-danger-emphasis rounded-3" @click="confirmDelete = 'HistoricalTrackingTable'">
|
||||||
|
<i class="bi bi-trash me-2 "></i>Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="ms-md-auto d-flex gap-2 align-items-center" v-else-if="confirmDelete === 'HistoricalTrackingTable'">
|
||||||
|
<small>
|
||||||
|
<LocaleText t="Are you sure to delete?"></LocaleText>
|
||||||
|
</small>
|
||||||
|
<button class="btn btn-sm bg-danger-subtle text-danger-emphasis rounded-3"
|
||||||
|
:class="{disabled: deleting}"
|
||||||
|
@click="deleteRecord('HistoricalTrackingTable')">
|
||||||
|
<i class="bi bi-check me-2 "></i>Yes
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:class="{disabled: deleting}"
|
||||||
|
class="btn btn-sm bg-secondary-subtle text-secondary-emphasis rounded-3" @click="confirmDelete = ''">
|
||||||
|
<i class="bi bi-x me-2 "></i>No
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -4,6 +4,8 @@ import DashboardSettingsInputWireguardConfigurationPath
|
|||||||
from "@/components/settingsComponent/dashboardSettingsInputWireguardConfigurationPath.vue";
|
from "@/components/settingsComponent/dashboardSettingsInputWireguardConfigurationPath.vue";
|
||||||
import DashboardSettingsWireguardConfigurationAutostart
|
import DashboardSettingsWireguardConfigurationAutostart
|
||||||
from "@/components/settingsComponent/dashboardSettingsWireguardConfigurationAutostart.vue";
|
from "@/components/settingsComponent/dashboardSettingsWireguardConfigurationAutostart.vue";
|
||||||
|
import DashboardWireguardConfigurationTracking
|
||||||
|
from "@/components/settingsComponent/dashboardWireguardConfigurationTracking.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -16,6 +18,7 @@ import DashboardSettingsWireguardConfigurationAutostart
|
|||||||
>
|
>
|
||||||
</DashboardSettingsInputWireguardConfigurationPath>
|
</DashboardSettingsInputWireguardConfigurationPath>
|
||||||
<DashboardSettingsWireguardConfigurationAutostart></DashboardSettingsWireguardConfigurationAutostart>
|
<DashboardSettingsWireguardConfigurationAutostart></DashboardSettingsWireguardConfigurationAutostart>
|
||||||
|
<DashboardWireguardConfigurationTracking/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ const deleteSuccess = async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="text-body w-100 h-100 pb-2 position-relative">
|
<div class="text-body w-100 h-100 pb-2 position-relative">
|
||||||
|
|
||||||
<div class="w-100 h-100 card rounded-3">
|
<div class="w-100 h-100 card rounded-3">
|
||||||
<Transition name="zoom">
|
<Transition name="zoom">
|
||||||
<ClientSettings v-if="settings" @close="settings = false"></ClientSettings>
|
<ClientSettings v-if="settings" @close="settings = false" class="z-5"></ClientSettings>
|
||||||
</Transition>
|
</Transition>
|
||||||
<div class="border-bottom z-0">
|
<div class="border-bottom z-0">
|
||||||
<div class="d-flex text-body align-items-center sticky-top p-3 bg-body-tertiary rounded-top-3" style="border-top-right-radius: 0 !important;">
|
<div class="d-flex text-body align-items-center sticky-top p-3 bg-body-tertiary rounded-top-3" style="border-top-right-radius: 0 !important;">
|
||||||
@@ -64,7 +65,7 @@ const deleteSuccess = async () => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
:class="{'hide': !route.params.id}"
|
:class="{'hide': !route.params.id}"
|
||||||
class="col-sm-8 clientViewerContainer">
|
class="col-sm-8 clientViewerContainer z-0">
|
||||||
<RouterView></RouterView>
|
<RouterView></RouterView>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
import{a7 as A,r as n,D as S,g as l,z as v}from"./index-B_FN5WmC.js";const b=A("DashboardClientAssignmentStore",()=>{const f=n({}),d=n([]),o=n({}),c=n([]),g=n(!1),r=n(""),i=S(),w=async()=>{await l("/api/clients/allClients",{},s=>{o.value=s.data})},y=async()=>{await l("/api/clients/allClientsRaw",{},s=>{c.value=s.data,console.log(c.value)})},m=s=>Object.values(o.value).flat().find(e=>e.ClientID===s),u=async(s,e)=>{await l("/api/clients/assignedClients",{ConfigurationName:s,Peer:e},a=>{d.value=a.data})};return{assignments:d,getAssignedClients:u,getClients:w,getClientsRaw:y,clients:o,unassignClient:async(s,e,a)=>{g.value=!0,await v("/api/clients/unassignClient",{AssignmentID:a},async t=>{t.status?(i.newMessage("Server","Unassign successfully!","success"),s&&e&&await u(s,e)):(i.newMessage("Server","Unassign Failed. Reason: "+t.message,"success"),console.error("Unassign Failed. Reason: "+t.message)),g.value=!1})},assignClient:async(s,e,a,t=!0)=>{r.value=a,await v("/api/clients/assignClient",{ConfigurationName:s,Peer:e,ClientID:a},async C=>{C.status?(i.newMessage("Server","Assign successfully!","success"),t&&await u(s,e)):(i.newMessage("Server","Assign Failed. Reason: "+C.message,"success"),console.error("Assign Failed. Reason: "+C.message)),r.value=""})},getClientById:m,unassigning:g,assigning:r,clientsRaw:c,allConfigurationsPeers:f,getAllConfigurationsPeers:async()=>{await l("/api/clients/allConfigurationsPeers",{},s=>{f.value=s.data})}}});export{b as D};
|
import{a5 as A,r as n,D as S,g as l,z as v}from"./index-CmClDcBF.js";const b=A("DashboardClientAssignmentStore",()=>{const f=n({}),d=n([]),o=n({}),c=n([]),g=n(!1),r=n(""),i=S(),w=async()=>{await l("/api/clients/allClients",{},s=>{o.value=s.data})},y=async()=>{await l("/api/clients/allClientsRaw",{},s=>{c.value=s.data,console.log(c.value)})},m=s=>Object.values(o.value).flat().find(e=>e.ClientID===s),u=async(s,e)=>{await l("/api/clients/assignedClients",{ConfigurationName:s,Peer:e},a=>{d.value=a.data})};return{assignments:d,getAssignedClients:u,getClients:w,getClientsRaw:y,clients:o,unassignClient:async(s,e,a)=>{g.value=!0,await v("/api/clients/unassignClient",{AssignmentID:a},async t=>{t.status?(i.newMessage("Server","Unassign successfully!","success"),s&&e&&await u(s,e)):(i.newMessage("Server","Unassign Failed. Reason: "+t.message,"success"),console.error("Unassign Failed. Reason: "+t.message)),g.value=!1})},assignClient:async(s,e,a,t=!0)=>{r.value=a,await v("/api/clients/assignClient",{ConfigurationName:s,Peer:e,ClientID:a},async C=>{C.status?(i.newMessage("Server","Assign successfully!","success"),t&&await u(s,e)):(i.newMessage("Server","Assign Failed. Reason: "+C.message,"success"),console.error("Assign Failed. Reason: "+C.message)),r.value=""})},getClientById:m,unassigning:g,assigning:r,clientsRaw:c,allConfigurationsPeers:f,getAllConfigurationsPeers:async()=>{await l("/api/clients/allConfigurationsPeers",{},s=>{f.value=s.data})}}});export{b as D};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
@media screen and (max-width: 576px){.clientListContainer.hide[data-v-e441f3fe],.clientViewerContainer.hide[data-v-e441f3fe]{display:none!important}.clientListContainer[data-v-e441f3fe]{border-right:none!important;animation:blurIn-e441f3fe .2s ease-in-out forwards}.clientViewerContainer[data-v-e441f3fe]{animation:blurIn-e441f3fe .2s ease-in-out forwards}}@keyframes blurIn-e441f3fe{0%{filter:blur(8px)}to{filter:blur(0px)}}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@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
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
import{_ as r,c as i,b as o,w as e,k as l,j as a,l as _,S as u,h as d,f as t}from"./index-B_FN5WmC.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};
|
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-CmClDcBF.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};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
.fade-enter-active[data-v-9f596f5e]{transition-delay:var(--v0d365bfc)!important}.progress-bar[data-v-851170e4]{width:0;transition:all 1s cubic-bezier(.42,0,.22,1)}.filter a[data-v-7ed053f0]{text-decoration:none}
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
.fade-enter-active[data-v-9f596f5e]{transition-delay:var(--0d365bfc)!important}.progress-bar[data-v-851170e4]{width:0;transition:all 1s cubic-bezier(.42,0,.22,1)}.filter a[data-v-7ed053f0]{text-decoration:none}
|
|
||||||
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
-1
@@ -1 +1 @@
|
|||||||
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-B_FN5WmC.js";import{L as C}from"./localeText-B3I6AP1L.js";const F={name:"dashboardSettingsInputWireguardConfigurationPath",components:{LocaleText:C},props:{targetData:String,title:String,warning:!1,warningText:""},setup(){const o=m(),s=b(),r=`input_${S()}`;return{store:o,uuid:r,WireguardConfigurationStore:s}},data(){return{value:"",invalidFeedback:"",showInvalidFeedback:!1,isValid:!1,timeout:void 0,changed:!1,updating:!1}},mounted(){this.value=this.store.Configuration.Server[this.targetData]},methods:{async useValidation(){this.changed&&(this.updating=!0,await _("/api/updateDashboardConfigurationItem",{section:"Server",key:this.targetData,value:this.value},o=>{o.status?(this.isValid=!0,this.showInvalidFeedback=!1,this.store.Configuration.Account[this.targetData]=this.value,clearTimeout(this.timeout),this.timeout=setTimeout(()=>this.isValid=!1,5e3),this.WireguardConfigurationStore.getConfigurations(),this.store.newMessage("Server","WireGuard configuration path saved","success")):(this.isValid=!1,this.showInvalidFeedback=!0,this.invalidFeedback=o.message),this.changed=!1,this.updating=!1}))}}},I={class:"card"},T={class:"card-header"},A={class:"my-2"},L={class:"card-body"},M={class:"form-group"},N=["for"],P={class:"d-flex gap-2 align-items-start"},B={class:"flex-grow-1"},G=["id","disabled"],z={class:"invalid-feedback fw-bold"},U=["disabled"],q={key:0,class:"bi bi-save2-fill"},E={key:1,class:"spinner-border spinner-border-sm"},K={key:0,class:"px-2 py-1 text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 d-inline-block mt-1 mb-2"};function j(o,s,r,a,c,g){const d=w("LocaleText");return n(),i("div",I,[t("div",T,[t("h6",A,[u(d,{t:"Path"})])]),t("div",L,[t("div",M,[t("label",{for:this.uuid,class:"text-muted mb-1"},[t("strong",null,[t("small",null,[u(d,{t:this.title},null,8,["t"])])])],8,N),t("div",P,[t("div",B,[x(t("input",{type:"text",class:p(["form-control rounded-3",{"is-invalid":this.showInvalidFeedback,"is-valid":this.isValid}]),id:this.uuid,"onUpdate:modelValue":s[0]||(s[0]=e=>this.value=e),onKeydown:s[1]||(s[1]=e=>this.changed=!0),disabled:this.updating},null,42,G),[[y,this.value]]),t("div",z,v(this.invalidFeedback),1)]),t("button",{onClick:s[2]||(s[2]=e=>this.useValidation()),disabled:!this.changed,class:"ms-auto btn rounded-3 border-success-subtle bg-success-subtle text-success-emphasis"},[this.updating?(n(),i("span",E)):(n(),i("i",q))],8,U)]),r.warning?(n(),i("div",K,[t("small",null,[s[3]||(s[3]=t("i",{class:"bi bi-exclamation-triangle-fill me-2"},null,-1)),u(d,{t:r.warningText},null,8,["t"])])])):k("",!0)])])])}const et=f(F,[["render",j]]),H={class:"card rounded-3"},J={class:"card-header"},O={class:"my-2"},Q={class:"card-body d-flex gap-2"},R={class:"list-group w-100"},X=["onClick"],Y={__name:"dashboardSettingsWireguardConfigurationAutostart",setup(o){const s=m(),r=b(),a=D(s.Configuration.WireGuardConfiguration.autostart),c=$(()=>r.Configurations.map(e=>e.Name)),g=async()=>{await _("/api/updateDashboardConfigurationItem",{section:"WireGuardConfiguration",key:"autostart",value:a.value},async e=>{e.status?(s.newMessage("Server","Start up configurations saved","success"),a.value=e.data):s.newMessage("Server","Start up configurations failed to save","danger")})},d=e=>{a.value.includes(e)?a.value=a.value.filter(h=>h!==e):a.value.push(e),g()};return(e,h)=>(n(),i("div",H,[t("div",J,[t("h6",O,[u(C,{t:"Toggle When Start Up"})])]),t("div",Q,[t("div",R,[(n(!0),i(W,null,V(c.value,l=>(n(),i("button",{type:"button",key:l,onClick:Z=>d(l),class:"list-group-item list-group-item-action py-2 w-100 d-flex align-items-center"},[t("samp",null,v(l),1),t("i",{class:p(["ms-auto",[a.value.includes(l)?"bi-check-circle-fill":"bi-circle"]])},null,2)],8,X))),128))])])]))}},at=f(Y,[["__scopeId","data-v-4aa2aed9"]]);export{et as D,at as a};
|
import{_ as f,c as i,a as t,b as u,h as w,d as k,m as x,y,n as p,t as v,z as _,D as m,W as b,A as S,f as n,r as D,q as $,F as W,i as V}from"./index-CmClDcBF.js";import{L as C}from"./localeText-TzABauzQ.js";const F={name:"dashboardSettingsInputWireguardConfigurationPath",components:{LocaleText:C},props:{targetData:String,title:String,warning:!1,warningText:""},setup(){const o=m(),s=b(),r=`input_${S()}`;return{store:o,uuid:r,WireguardConfigurationStore:s}},data(){return{value:"",invalidFeedback:"",showInvalidFeedback:!1,isValid:!1,timeout:void 0,changed:!1,updating:!1}},mounted(){this.value=this.store.Configuration.Server[this.targetData]},methods:{async useValidation(){this.changed&&(this.updating=!0,await _("/api/updateDashboardConfigurationItem",{section:"Server",key:this.targetData,value:this.value},o=>{o.status?(this.isValid=!0,this.showInvalidFeedback=!1,this.store.Configuration.Account[this.targetData]=this.value,clearTimeout(this.timeout),this.timeout=setTimeout(()=>this.isValid=!1,5e3),this.WireguardConfigurationStore.getConfigurations(),this.store.newMessage("Server","WireGuard configuration path saved","success")):(this.isValid=!1,this.showInvalidFeedback=!0,this.invalidFeedback=o.message),this.changed=!1,this.updating=!1}))}}},I={class:"card"},T={class:"card-header"},A={class:"my-2"},L={class:"card-body"},M={class:"form-group"},N=["for"],P={class:"d-flex gap-2 align-items-start"},B={class:"flex-grow-1"},G=["id","disabled"],z={class:"invalid-feedback fw-bold"},U=["disabled"],q={key:0,class:"bi bi-save2-fill"},E={key:1,class:"spinner-border spinner-border-sm"},K={key:0,class:"px-2 py-1 text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 d-inline-block mt-1 mb-2"};function j(o,s,r,a,c,g){const d=w("LocaleText");return n(),i("div",I,[t("div",T,[t("h6",A,[u(d,{t:"Path"})])]),t("div",L,[t("div",M,[t("label",{for:this.uuid,class:"text-muted mb-1"},[t("strong",null,[t("small",null,[u(d,{t:this.title},null,8,["t"])])])],8,N),t("div",P,[t("div",B,[x(t("input",{type:"text",class:p(["form-control rounded-3",{"is-invalid":this.showInvalidFeedback,"is-valid":this.isValid}]),id:this.uuid,"onUpdate:modelValue":s[0]||(s[0]=e=>this.value=e),onKeydown:s[1]||(s[1]=e=>this.changed=!0),disabled:this.updating},null,42,G),[[y,this.value]]),t("div",z,v(this.invalidFeedback),1)]),t("button",{onClick:s[2]||(s[2]=e=>this.useValidation()),disabled:!this.changed,class:"ms-auto btn rounded-3 border-success-subtle bg-success-subtle text-success-emphasis"},[this.updating?(n(),i("span",E)):(n(),i("i",q))],8,U)]),r.warning?(n(),i("div",K,[t("small",null,[s[3]||(s[3]=t("i",{class:"bi bi-exclamation-triangle-fill me-2"},null,-1)),u(d,{t:r.warningText},null,8,["t"])])])):k("",!0)])])])}const et=f(F,[["render",j]]),H={class:"card rounded-3"},J={class:"card-header"},O={class:"my-2"},Q={class:"card-body d-flex gap-2"},R={class:"list-group w-100"},X=["onClick"],Y={__name:"dashboardSettingsWireguardConfigurationAutostart",setup(o){const s=m(),r=b(),a=D(s.Configuration.WireGuardConfiguration.autostart),c=$(()=>r.Configurations.map(e=>e.Name)),g=async()=>{await _("/api/updateDashboardConfigurationItem",{section:"WireGuardConfiguration",key:"autostart",value:a.value},async e=>{e.status?(s.newMessage("Server","Start up configurations saved","success"),a.value=e.data):s.newMessage("Server","Start up configurations failed to save","danger")})},d=e=>{a.value.includes(e)?a.value=a.value.filter(h=>h!==e):a.value.push(e),g()};return(e,h)=>(n(),i("div",H,[t("div",J,[t("h6",O,[u(C,{t:"Toggle When Start Up"})])]),t("div",Q,[t("div",R,[(n(!0),i(W,null,V(c.value,l=>(n(),i("button",{type:"button",key:l,onClick:Z=>d(l),class:"list-group-item list-group-item-action py-2 w-100 d-flex align-items-center"},[t("samp",null,v(l),1),t("i",{class:p(["ms-auto",[a.value.includes(l)?"bi-check-circle-fill":"bi-circle"]])},null,2)],8,X))),128))])])]))}},at=f(Y,[["__scopeId","data-v-4aa2aed9"]]);export{et as D,at as a};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
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};
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
import{I as j,q as w,P as S,J as k,Q as L,u as R}from"./index-CmClDcBF.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 j(t,a,{...r,immediate:!0})}const I=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:[I].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=I,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},O=[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:()=>O.forEach(l=>l())}}export{D as a,B as o,P as u};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
import{_ as e,G as t,c as o,t as a,f as c}from"./index-B_FN5WmC.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};
|
import{_ as e,G as t,c as o,t as a,f as c}from"./index-CmClDcBF.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
-1
@@ -1 +1 @@
|
|||||||
import{L as l}from"./localeText-B3I6AP1L.js";import{d as c}from"./dayjs.min-DBMCopRq.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-B_FN5WmC.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};
|
import{L as l}from"./localeText-TzABauzQ.js";import{d as c}from"./dayjs.min-DFlK6ZAc.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-CmClDcBF.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};
|
||||||
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
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-C8V_x6Dm.js";import{_ as Y,D as G,c as $,d as j,f as q}from"./index-B_FN5WmC.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};
|
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-CmClDcBF.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};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
|||||||
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-B_FN5WmC.js";import{L as T}from"./localeText-B3I6AP1L.js";import"./browser-Dh9QwgON.js";import"./galois-field-CkUIqpA6.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};
|
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-CmClDcBF.js";import{L as T}from"./localeText-TzABauzQ.js";import"./browser-Bjk3Qpx-.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
-1
@@ -1 +1 @@
|
|||||||
import{L as o}from"./localeText-B3I6AP1L.js";import{P as t}from"./peersDefaultSettingsInput-DstQ1-i-.js";import{B as s,c as l,a,b as e,f as n}from"./index-B_FN5WmC.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};
|
import{L as o}from"./localeText-TzABauzQ.js";import{P as t}from"./peersDefaultSettingsInput-B_ngK8i0.js";import{B as s,c as l,a,b as e,f as n}from"./index-CmClDcBF.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};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import{a as p,S as b}from"./schedulePeerJob-u-FqcbUM.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-CmClDcBF.js";import{L as w}from"./localeText-TzABauzQ.js";import"./vue-datepicker-BYHO-v3J.js";import"./index-DZpKhbG8.js";import"./dayjs.min-DFlK6ZAc.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 @@
|
|||||||
import{a as p,S as b}from"./schedulePeerJob-CaaaMxTG.js";import{_ as h,h as i,c as a,f as o,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-B_FN5WmC.js";import{L as w}from"./localeText-B3I6AP1L.js";import"./vue-datepicker-Bsrbo1KS.js";import"./dayjs.min-DBMCopRq.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 o(),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]=s=>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]=s=>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(()=>[(o(!0),a(_,null,f(this.selectedPeer.jobs,(s,E)=>(o(),v(l,{onRefresh:t[2]||(t[2]=c=>this.$emit("refresh")),onDelete:c=>this.deleteJob(s),dropdowns:this.store.PeerScheduleJobs.dropdowns,key:s.JobID,pjob:s},null,8,["onDelete","dropdowns","pjob"]))),128)),this.selectedPeer.jobs.length===0?(o(),a("div",N,[e("div",I,[e("h6",L,[r(n,{t:"This peer does not have any job yet."})])])])):m("",!0)]),_:1})])])])])])}const z=h(P,[["render",O],["__scopeId","data-v-5bbdd42b"]]);export{z as default};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
import{S as _}from"./schedulePeerJob-CaaaMxTG.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-B_FN5WmC.js";import{L as x}from"./localeText-B3I6AP1L.js";import"./vue-datepicker-Bsrbo1KS.js";import"./dayjs.min-DBMCopRq.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 U=g(J,[["render",W]]);export{U as default};
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import{S as _}from"./schedulePeerJob-u-FqcbUM.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-CmClDcBF.js";import{L as x}from"./localeText-TzABauzQ.js";import"./vue-datepicker-BYHO-v3J.js";import"./index-DZpKhbG8.js";import"./dayjs.min-DFlK6ZAc.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
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
-1
@@ -1 +1 @@
|
|||||||
import{Q as l}from"./browser-Dh9QwgON.js";import{L as _}from"./localeText-B3I6AP1L.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-B_FN5WmC.js";import"./galois-field-CkUIqpA6.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};
|
import{Q as l}from"./browser-Bjk3Qpx-.js";import{L as _}from"./localeText-TzABauzQ.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-CmClDcBF.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
-1
@@ -1 +1 @@
|
|||||||
import{_ as p,q as m,G as f,W as h,r as u,a2 as _,L as v,K as g,o as x,a3 as S,c as y,d as b,f as B,a as s,m as w,y as T}from"./index-B_FN5WmC.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};
|
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-CmClDcBF.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};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user