Compare commits

...

59 Commits
v1.1.1 ... v2.0

Author SHA1 Message Date
Donald Zou
ae3b8f6494 Merge pull request #19 from donaldzou/v2.0-beta-6
v2.0 merge to main
2021-05-05 15:29:41 -04:00
Donald Cheng Hong Zou
33487ee03f v2.0-beta-6 Commit 2021-05-05 15:16:32 -04:00
Donald Cheng Hong Zou
58ecae1162 v2.0-beta-6 Commit 2021-05-05 15:09:34 -04:00
Donald Cheng Hong Zou
8777da10e4 v2.0-beta-1 Commit 2021-05-05 14:59:00 -04:00
Donald Zou
4ea971c1b9 v2.0-beta-1 Commit 2021-05-05 14:52:28 -04:00
Donald Cheng Hong Zou
0fa4759c3a Update 2021-05-05 14:38:10 -04:00
Donald Cheng Hong Zou
fe7b9730d3 Update README.md 2021-05-04 21:41:13 -04:00
Donald Cheng Hong Zou
040f6a8393 Commit 2021-05-04 21:26:40 -04:00
Donald Cheng Hong Zou
b17575b197 Update requirements.txt 2021-05-04 15:55:52 -04:00
Donald Cheng Hong Zou
dee4f757e7 Upload README pictures 2021-05-04 15:42:04 -04:00
Donald Cheng Hong Zou
5ae08d02ab Merge branch 'main' of https://github.com/donaldzou/Wireguard-Dashboard into main 2021-05-04 02:10:32 -04:00
Donald Cheng Hong Zou
b83aec4965 v2.0-beta-2 Commit 2021-05-04 02:10:06 -04:00
Donald Zou
68c4837780 Merge pull request #18 from donaldzou/testing
Update .gitignore
2021-05-04 01:39:51 -04:00
Donald Cheng Hong Zou
46fe5e99fe Update .gitignore 2021-05-04 01:39:32 -04:00
Donald Zou
9ffb537cfb Delete wg-dashboard.ini 2021-05-04 01:39:14 -04:00
Donald Zou
cc7e6f852d Merge pull request #17 from donaldzou/testing
v2.0-beta1 Merge
2021-05-04 01:37:27 -04:00
Donald Cheng Hong Zou
f83e99c62c Merge branch 'testing' of https://github.com/donaldzou/Wireguard-Dashboard into testing 2021-05-04 01:36:32 -04:00
Donald Zou
bc8b8e7982 v2.0-beta-1 Commit 2021-05-04 01:32:34 -04:00
Donald Zou
3b761ca2a8 Update README.md 2021-05-04 01:20:20 -04:00
Donald Cheng Hong Zou
edc21f9830 Update README.md 2021-04-12 23:18:57 -04:00
Donald Cheng Hong Zou
d025ec4dff Push Logo 2021-04-12 23:16:12 -04:00
Donald Cheng Hong Zou
ab334a393f Logo 2021-04-12 23:15:53 -04:00
Donald Cheng Hong Zou
2a9044086f Push Logo 2021-04-12 23:14:53 -04:00
Donald Zou
c3038ec60c Merge pull request #14 from donaldzou/all-contributors/add-tonjo
docs: add tonjo as a contributor
2021-04-12 22:30:23 -04:00
allcontributors[bot]
0fa827ba00 docs: update .all-contributorsrc [skip ci] 2021-04-13 02:29:48 +00:00
allcontributors[bot]
26002fc372 docs: update README.md [skip ci] 2021-04-13 02:29:47 +00:00
Donald Zou
59acba8c08 Update README.md 2021-04-12 22:29:00 -04:00
Donald Zou
4c5ee45fdb Update README.md 2021-04-12 22:28:30 -04:00
Donald Zou
6062120d0d Merge pull request #13 from donaldzou/all-contributors/add-antonioag95
docs: add antonioag95 as a contributor
2021-04-12 22:27:54 -04:00
allcontributors[bot]
cd3ffb2126 docs: create .all-contributorsrc [skip ci] 2021-04-13 02:26:56 +00:00
allcontributors[bot]
a8c11cc445 docs: update README.md [skip ci] 2021-04-13 02:26:55 +00:00
Donald Zou
fcb89aac6b Update README.md 2021-04-12 11:05:19 -04:00
Donald Zou
3704d8f6e7 Merge pull request #11 from tonjo/patch-1
Avoid error when `AllowedIPs` is not defined for a peer.
2021-04-12 11:01:38 -04:00
Donald Zou
8c75d172c5 Update issue templates 2021-04-10 12:49:34 -04:00
Donald Zou
5687302081 Update issue templates 2021-04-10 12:47:18 -04:00
Donald Zou
9020b16a9a Update README.md 2021-04-10 00:49:32 -04:00
tonjo
d5462df4da Update dashboard.py 2021-04-09 11:15:25 +02:00
tonjo
9021ca31af Avoid error when AllowedIps is not defined 2021-04-09 11:08:41 +02:00
Donald Zou
83cb8b7f03 Update README.md 2021-04-09 00:42:42 -04:00
Donald Cheng Hong Zou
baf2658eb8 v20210409 2021-04-09 00:07:37 -04:00
Donald Zou
3f19b05d28 Update README.md 2021-04-07 17:25:21 -04:00
Donald Zou
5e6be1f830 Update README.md 2021-04-07 15:58:00 -04:00
Donald Zou
14d1a3038f Update README.md 2021-04-07 12:19:23 -04:00
Donald Zou
6a15e2e587 Update README.md 2021-04-07 12:15:45 -04:00
Donald Zou
0679805728 Update README.md 2021-04-07 12:01:35 -04:00
Donald Zou
62a03419a5 Update README.md 2021-04-07 11:52:24 -04:00
Donald Zou
81f6b0767e Update README.md 2021-04-07 11:52:03 -04:00
Donald Zou
c7ca5c749b Update README.md 2021-04-06 19:52:33 -04:00
Donald Zou
1daac09b7c Update README.md 2021-04-06 19:51:50 -04:00
Donald Zou
4d3dd68ac0 Merge pull request #5 from antonioag95/main
- Fixed a bug where a peer without last handshake date was showing wrong date in UI
- Hidden and temp `.conf` files will be excluded from interfaces list
2021-04-06 19:43:52 -04:00
Donald Zou
de9e4a6270 Update README.md 2021-04-06 16:51:37 -04:00
Donald Zou
bf8f347245 Update README.md 2021-04-06 15:03:49 -04:00
Donald Zou
a70d09ac7a Update README.md 2021-04-06 10:14:11 -04:00
Donald Zou
919b344da4 Update README.md 2021-04-05 22:41:55 -04:00
antonioag95
44f013a678 Update dashboard.py
- Fixed a bug where a peer without last handshake date was showing wrong date in webui
- Hidden and temp conf files will be excluded from interfaces list
2021-04-03 23:48:07 +02:00
Donald Zou
742191111b Merge pull request #4 from donaldzou/v20210403-alpha
v20210403 Bug Fixed
2021-04-03 14:31:11 -04:00
Donald Cheng Hong Zou
8dc0031475 Update dashboard.py 2021-04-03 14:30:02 -04:00
Donald Cheng Hong Zou
19fb90a300 Update dashboard.py 2021-04-03 14:10:07 -04:00
Donald Cheng Hong Zou
0c3960d8d9 Commit 2021-04-03 14:06:21 -04:00
31 changed files with 1110 additions and 334 deletions

34
.all-contributorsrc Normal file
View File

@@ -0,0 +1,34 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "antonioag95",
"name": "antonioag95",
"avatar_url": "https://avatars.githubusercontent.com/u/30556866?v=4",
"profile": "https://github.com/antonioag95",
"contributions": [
"test",
"code"
]
},
{
"login": "tonjo",
"name": "tonjo",
"avatar_url": "https://avatars.githubusercontent.com/u/4726289?v=4",
"profile": "https://github.com/tonjo",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"projectName": "wireguard-dashboard",
"projectOwner": "donaldzou",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true
}

28
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe The Problem**
A clear and concise description of what the bug is.
**Expected Error / Traceback**
```
Please provide the error traceback here
```
**To Reproduce**
Please provide how you run the dashboard
**OS Information:**
- OS: [e.g. Ubuntu 18.02]
- Python Version: [e.g v3.7]
**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...
```

View File

@@ -0,0 +1,14 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.

5
.gitignore vendored
View File

@@ -5,3 +5,8 @@ wg.db
*.json *.json
.idea .idea
src/test.py src/test.py
tmp
__pycache__
src/wg-dashboard.ini
src/wg-dashboard.ini
src/static/pic.xd

203
README.md
View File

@@ -1,49 +1,192 @@
# Wireguard Dashboard <p align="center">
Monitoring Wireguard is not convinient, need to login into server and type `wg show`. That's why this platform is being created, to view all configurations in a more straight forward way. <img src="https://raw.githubusercontent.com/donaldzou/wireguard-dashboard/main/img/Group%202.png" width="128">
</p>
<h1 align="center"> Wireguard Dashboard</h1>
## 💡Features
- Add peers in configuration <p align="center">
- Manage peer names <img src="http://ForTheBadge.com/images/badges/made-with-python.svg">
</p>
<p align="center">
<a href="https://github.com/donaldzou/wireguard-dashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard"></a>
</p>
<p align="center">Monitoring Wireguard is not convinient, need to login into server and type <code>wg show</code>. That's why this platform is being created, to view all configurations and manage them in a easier way.</p>
## 📣 What's New: Version 2.0
### ⚠️ **Update from v1.x.x**
1. Stop the dashboard if it is running.
2. You can use `git pull https://github.com/donaldzou/Wireguard-Dashboard.git v2.0` to get the new update inside `Wireguard-Dashboard` directory.
3. Proceed **Step 2 & 3** in the Install step down below.
<hr>
- Added login function to dashboard
- ***I'm not using the most ideal way to store the username and password, feel free to provide a better way to do this if you any good idea!***
- Added a config file to the dashboard
- Dashboard config can be change within the **Setting** tab on the side bar
- Adjusted UI
- And much more!
## 💡 Features
- Add peers for each WireGuard configuration
- Manage peer
- Delete peers - Delete peers
- And many more coming up! Welcome to contribute to this project! - And many more coming up! Welcome to contribute to this project!
## 📝Requirement
- Ubuntu 18.04.1 LTS, other OS might work, but haven't test yet. ## 📝 Requirement
- **Wireguard**
- Ubuntu or Debian based OS, other might work, but haven't test yet. Tested on the following OS:
- [x] Ubuntu 18.04.1 LTS
- [ ] If you have tested on other OS and it works perfectly please provide it to me!
- ‼️ Make sure you have **Wireguard** and **Wireguard-Tools (`wg-quick`)** installed.‼️ <a href="https://www.wireguard.com/install/">How to install?</a>
- Configuration files under **/etc/wireguard** - Configuration files under **/etc/wireguard**
- Python 3.7
## 🛠Install - **Note: For peers, `PublicKey` & `AllowedIPs` is required.**
- Python 3.7+ & Pip3
```
$ sudo apt-get install python3 python3-pip
```
**1. Install Python Dependencies**
## 🛠 Install
1. Download Wireguard Dashboard
``` ```
$ python3 -m pip install flask tinydb $ git clone -b v2.0 https://github.com/donaldzou/Wireguard-Dashboard.git
``` ```
**2. Install Wireguard Dashboard** **2. Install Python Dependencies**
```
$ git clone https://github.com/donaldzou/Wireguard-Dashboard.git
$ cd Wireguard-Dashboard/src
$ python3 dashboard.py
```
Access your server with port `10086` ! e.g (http://your_server_ip:10086)
**3. Install with Production Mode (Optional)**
``` ```
$ cd Wireguard-Dashboard/src $ cd Wireguard-Dashboard/src
$ export FLASK_APP=dashboard.py $ python3 -m pip install -r requirements.txt
$ export FLASK_RUN_HOST=0.0.0.0
$ export FLASK_ENV=development
$ export FLASK_DEBUG=0
$ flask run
``` ```
## 🔍Example **3. Install & run Wireguard Dashboard**
```
$ sudo sh wgd.sh start
```
Access your server with port `10086` ! e.g (http://your_server_ip:10086), continue to read to on how to change port and ip that dashboard is running with.
## 🪜 Usage
**1. Start/Stop/Restart Wireguard Dashboard**
```
$ cd Wireguard-Dashboard/src
$ sudo sh wgd.sh start # Start the dashboard in background
$ sudo sh wgd.sh debug # Start the dashboard in foreground (debug mode)
$ sudo sh wgd.sh stop # Stop the dashboard
$ sudo sh wgd.sh restart # Restart the dasboard
$ sudo sh wgd.sh update # Update the dashboard
```
⚠️ **For first time user please also read the next section.**
## ✂️ Dashboard Configuration
Since version 2.0, Wireguard Dashboard will be using a configuration file called `wg-dashboard.ini`, (It will generate automatically after first time running the dashboard). More options will include in future versions, and for now it included the following config:
### `[Account]`
`username` - Username (Default: `admin`)
`password` - Password, will be hash with SHA256 (Default: `admin`).
### `[Server]`
`wg_conf_path` - The path of all the Wireguard configurations (Default: `/etc/wireguard`)
`app_ip` - IP address the flask will run with (Default: `0.0.0.0`)
`app_port` - Port the flask will run with (Default: `10086`)
`auth_req` - Does the dashboard need authentication (Default: `true`)
- If `auth_req = false` , user will not be access the **Setting** tab due to security consideration. **User can only change the file directly in system**.
`version` - Dashboard Version
All these settings will be able to configure within the dashboard in **Settings** on the sidebar, without changing the actual file. **Except `version` and `auth_req` due to security consideration.**
## ❓ How to update the dashboard?
```
$ cd wireguard-dashboard
$ sudo sh wgd.sh update # Perform update
$ sudo sh wgd.sh start # Start dashboard
```
## 🔍 Screenshot
![Index Image](https://github.com/donaldzou/Wireguard-Dashboard/raw/main/src/static/index.png) ![Index Image](https://github.com/donaldzou/Wireguard-Dashboard/raw/main/src/static/index.png)
![Conf Image](https://github.com/donaldzou/Wireguard-Dashboard/raw/main/src/static/configuration.png)
<p align=center>Index Page</p>
![Signin Image](https://github.com/donaldzou/Wireguard-Dashboard/raw/main/src/static/signin.png)
<p align=center>Signin Page</p>
![Configuration Image](https://github.com/donaldzou/Wireguard-Dashboard/raw/main/src/static/configuration.png)
<p align=center>Configuration Page</p>
![Settings Image](https://github.com/donaldzou/Wireguard-Dashboard/raw/main/src/static/settings.png)
<p align=center>Settings Page</p>
## Contributors ✨
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://github.com/antonioag95"><img src="https://avatars.githubusercontent.com/u/30556866?v=4?s=100" width="100px;" alt=""/><br /><sub><b>antonioag95</b></sub></a><br /><a href="https://github.com/donaldzou/wireguard-dashboard/commits?author=antonioag95" title="Tests">⚠️</a> <a href="https://github.com/donaldzou/wireguard-dashboard/commits?author=antonioag95" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tonjo"><img src="https://avatars.githubusercontent.com/u/4726289?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tonjo</b></sub></a><br /><a href="https://github.com/donaldzou/wireguard-dashboard/commits?author=tonjo" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

BIN
img/Group 2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
img/Group 2@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

BIN
img/Group 3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
img/Group 3@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

BIN
img/Wg-dashboard-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

View File

@@ -1,2 +0,0 @@
Flask==1.1.2
tinydb==4.3.0

View File

@@ -1,19 +1,34 @@
dashboard_version = 'v2.0'
# Python Built-in Library
import os import os
from flask import Flask, request, render_template, redirect, url_for from flask import Flask, request, render_template, redirect, url_for, session, abort
import subprocess import subprocess
from datetime import datetime, date, time, timedelta from datetime import datetime, date, time, timedelta
from operator import itemgetter from operator import itemgetter
import secrets
import hashlib
import json, urllib.request
import configparser
# PIP installed library
import ifcfg
from tinydb import TinyDB, Query from tinydb import TinyDB, Query
conf_location = "/etc/wireguard" dashboard_conf = 'wg-dashboard.ini'
update = ""
app = Flask("Wireguard Dashboard") app = Flask("Wireguard Dashboard")
app.secret_key = secrets.token_urlsafe(16)
app.config['TEMPLATES_AUTO_RELOAD'] = True app.config['TEMPLATES_AUTO_RELOAD'] = True
css = ""
conf_data = {} conf_data = {}
def get_conf_peer_key(config_name): def get_conf_peer_key(config_name):
keys = [] keys = []
try: peer_key = subprocess.check_output("wg show "+config_name+" peers", shell=True) try:
except Exception: return "stopped" peer_key = subprocess.check_output("wg show " + config_name + " peers", shell=True)
except Exception:
return "stopped"
peer_key = peer_key.decode("UTF-8").split() peer_key = peer_key.decode("UTF-8").split()
for i in peer_key: keys.append(i) for i in peer_key: keys.append(i)
return keys return keys
@@ -21,33 +36,67 @@ def get_conf_peer_key(config_name):
def get_conf_running_peer_number(config_name): def get_conf_running_peer_number(config_name):
running = 0 running = 0
#Get latest handshakes # Get latest handshakes
try: data_usage = subprocess.check_output("wg show "+config_name+" latest-handshakes", shell=True) try:
except Exception: return "stopped" data_usage = subprocess.check_output("wg show " + config_name + " latest-handshakes", shell=True)
except Exception:
return "stopped"
data_usage = data_usage.decode("UTF-8").split() data_usage = data_usage.decode("UTF-8").split()
count = 0 count = 0
now = datetime.now() now = datetime.now()
b = timedelta(minutes=2) b = timedelta(minutes=2)
for i in range(int(len(data_usage)/2)): for i in range(int(len(data_usage) / 2)):
minus = now - datetime.fromtimestamp(int(data_usage[count+1])) minus = now - datetime.fromtimestamp(int(data_usage[count + 1]))
if minus < b: if minus < b:
running += 1 running += 1
count += 2 count += 2
return running return running
def read_conf_file(config_name):
# Read Configuration File Start
conf_location = wg_conf_path+"/" + config_name + ".conf"
f = open(conf_location, 'r')
file = f.read().split("\n")
conf_peer_data = {
"Interface": {},
"Peers": []
}
peers_start = 0
for i in range(len(file)):
if file[i] == "[Peer]":
peers_start = i
break
else:
if len(file[i]) > 0:
if file[i] != "[Interface]":
tmp = file[i].replace(" ", "").split("=", 1)
if len(tmp) == 2:
conf_peer_data['Interface'][tmp[0]] = tmp[1]
conf_peers = file[peers_start:]
peer = -1
for i in conf_peers:
if i == "[Peer]":
peer += 1
conf_peer_data["Peers"].append({})
else:
if len(i) > 0:
tmp = i.replace(" ", "").split("=", 1)
if len(tmp) == 2:
conf_peer_data["Peers"][peer][tmp[0]] = tmp[1]
# Read Configuration File End
return conf_peer_data
def get_conf_peers_data(config_name): def get_conf_peers_data(config_name):
db = TinyDB('db/'+config_name+'.json') db = TinyDB('db/' + config_name + '.json')
peers = Query() peers = Query()
peer_data = {} conf_peer_data = read_conf_file(config_name)
# Get key
try: peer_key = subprocess.check_output("wg show "+config_name+" peers", shell=True) for i in conf_peer_data['Peers']:
except Exception: return "stopped" if not db.search(peers.id == i['PublicKey']):
peer_key = peer_key.decode("UTF-8").split()
for i in peer_key:
peer_data[i] = {}
if not db.search(peers.id == i):
db.insert({ db.insert({
"id": i, "id": i['PublicKey'],
"name": "", "name": "",
"total_receive": 0, "total_receive": 0,
"total_sent": 0, "total_sent": 0,
@@ -59,131 +108,283 @@ def get_conf_peers_data(config_name):
"traffic": [] "traffic": []
}) })
# Get latest handshakes
try:
data_usage = subprocess.check_output("wg show " + config_name + " latest-handshakes", shell=True)
except Exception:
return "stopped"
data_usage = data_usage.decode("UTF-8").split()
count = 0
now = datetime.now()
b = timedelta(minutes=2)
for i in range(int(len(data_usage) / 2)):
minus = now - datetime.fromtimestamp(int(data_usage[count + 1]))
if minus < b:
status = "running"
else:
status = "stopped"
if int(data_usage[count + 1]) > 0:
db.update({"latest_handshake": str(minus).split(".")[0], "status": status},
peers.id == data_usage[count])
else:
db.update({"latest_handshake": "(None)", "status": status}, peers.id == data_usage[count])
count += 2
# Get transfer
try:
#Get transfer data_usage = subprocess.check_output("wg show " + config_name + " transfer", shell=True)
try: data_usage = subprocess.check_output("wg show "+config_name+" transfer", shell=True) except Exception:
except Exception: return "stopped" return "stopped"
data_usage = data_usage.decode("UTF-8").split() data_usage = data_usage.decode("UTF-8").split()
count = 0 count = 0
upload_total = 0 for i in range(int(len(data_usage) / 3)):
download_total = 0 cur_i = db.search(peers.id == data_usage[count])
total = 0 total_sent = cur_i[0]['total_sent']
for i in range(int(len(data_usage)/3)): total_receive = cur_i[0]['total_receive']
db.update({"total_receive": round(int(data_usage[count+1])/(1024**3),4), cur_total_sent = round(int(data_usage[count + 2]) / (1024 ** 3), 4)
"total_sent": round(int(data_usage[count+2])/(1024**3),4), cur_total_receive = round(int(data_usage[count + 1]) / (1024 ** 3), 4)
"total_data": round((int(data_usage[count+2])+int(data_usage[count+1]))/(1024**3),4)}, peers.id == data_usage[count]) if cur_i[0]["status"] == "running":
if total_sent <= cur_total_sent:
total_sent = cur_total_sent
else: total_sent += cur_total_sent
if total_receive <= cur_total_receive:
total_receive = cur_total_receive
else: total_receive += cur_total_receive
db.update({"total_receive": round(total_receive,4),
"total_sent": round(total_sent,4),
"total_data": round(total_receive + total_sent, 4)}, peers.id == data_usage[count])
# Will get implement in the future
# traffic = db.search(peers.id == data_usage[count])[0]['traffic']
# traffic.append({"time": current_time, "total_receive": round(int(data_usage[count + 1]) / (1024 ** 3), 4),
# "total_sent": round(int(data_usage[count + 2]) / (1024 ** 3), 4)})
# db.update({"traffic": traffic}, peers.id == data_usage[count])
peer_data[data_usage[count]]['total_receive'] = round(int(data_usage[count+1])/(1024**3),4)
peer_data[data_usage[count]]['total_sent'] = round(int(data_usage[count+2])/(1024**3),4)
peer_data[data_usage[count]]['total_data'] = round((int(data_usage[count+2])+int(data_usage[count+1]))/(1024**3),4)
count += 3 count += 3
#Get endpoint # Get endpoint
try: data_usage = subprocess.check_output("wg show "+config_name+" endpoints", shell=True) try:
except Exception: return "stopped" data_usage = subprocess.check_output("wg show " + config_name + " endpoints", shell=True)
except Exception:
return "stopped"
data_usage = data_usage.decode("UTF-8").split() data_usage = data_usage.decode("UTF-8").split()
count = 0 count = 0
for i in range(int(len(data_usage)/2)): for i in range(int(len(data_usage) / 2)):
db.update({"endpoint": data_usage[count+1]}, peers.id == data_usage[count]) db.update({"endpoint": data_usage[count + 1]}, peers.id == data_usage[count])
peer_data[data_usage[count]]['endpoint'] = data_usage[count+1]
count += 2 count += 2
#Get latest handshakes # Get allowed ip
try: data_usage = subprocess.check_output("wg show "+config_name+" latest-handshakes", shell=True) for i in conf_peer_data["Peers"]:
except Exception: return "stopped" db.update({"allowed_ip":i.get('AllowedIPs', '(None)')}, peers.id == i["PublicKey"])
data_usage = data_usage.decode("UTF-8").split()
count = 0
now = datetime.now()
b = timedelta(minutes=2)
for i in range(int(len(data_usage)/2)):
minus = now - datetime.fromtimestamp(int(data_usage[count+1]))
status = ""
if minus < b:
peer_data[data_usage[count]]['status'] = "running"
status = "running"
else:
peer_data[data_usage[count]]['status'] = "stopped"
status = "stopped"
db.update({"latest_handshake": str(minus).split(".")[0], "status": status}, peers.id == data_usage[count])
peer_data[data_usage[count]]['latest_handshake'] = str(minus).split(".")[0] def get_peers(config_name):
count += 2 get_conf_peers_data(config_name)
db = TinyDB('db/' + config_name + '.json')
#Get allowed ip
try: data_usage = subprocess.check_output("wg show "+config_name+" allowed-ips", shell=True)
except Exception: return "stopped"
data_usage = data_usage.decode("UTF-8").split()
count = 0
for i in range(int(len(data_usage)/2)):
db.update({"allowed_ip": data_usage[count+1]}, peers.id == data_usage[count])
peer_data[data_usage[count]]['allowed_ip'] = data_usage[count+1]
count += 2
result = db.all() result = db.all()
result = sorted(result, key=lambda d: d['status']) result = sorted(result, key=lambda d: d['status'])
return result return result
def get_conf_pub_key(config_name): def get_conf_pub_key(config_name):
try: pub_key = subprocess.check_output("wg show "+config_name+" public-key", shell=True, stderr=subprocess.STDOUT) conf = configparser.ConfigParser(strict=False)
except Exception: return "stopped" conf.read(wg_conf_path + "/" + config_name + ".conf")
return pub_key.decode("UTF-8") pri = conf.get("Interface", "PrivateKey")
pub = subprocess.check_output("echo '" + pri + "' | wg pubkey", shell=True)
conf.clear()
return pub.decode().strip("\n")
def get_conf_listen_port(config_name): def get_conf_listen_port(config_name):
try: pub_key = subprocess.check_output("wg show "+config_name+" listen-port", shell=True, stderr=subprocess.STDOUT) conf = configparser.ConfigParser(strict=False)
except Exception: return "stopped" conf.read(wg_conf_path + "/" + config_name + ".conf")
return pub_key.decode("UTF-8") port = conf.get("Interface", "ListenPort")
conf.clear()
return port
def get_conf_total_data(config_name): def get_conf_total_data(config_name):
try: data_usage = subprocess.check_output("wg show "+config_name+" transfer", shell=True, stderr=subprocess.STDOUT) db = TinyDB('db/' + config_name + '.json')
except Exception: return "stopped"
data_usage = data_usage.decode("UTF-8").split()
count = 0
upload_total = 0 upload_total = 0
download_total = 0 download_total = 0
total = 0 for i in db.all():
for i in range(int(len(data_usage)/3)): upload_total += round(i['total_sent'],4)
upload_total += int(data_usage[count+1]) download_total += round(i['total_receive'],4)
download_total += int(data_usage[count+2]) total = round(upload_total + download_total, 4)
count += 3
total = round(((((upload_total+download_total)/1024)/1024)/1024),4)
upload_total = round(((((upload_total)/1024)/1024)/1024),4)
download_total = round(((((download_total)/1024)/1024)/1024),4)
return [total, upload_total, download_total] return [total, upload_total, download_total]
def get_conf_status(config_name): def get_conf_status(config_name):
try: status = subprocess.check_output("wg show "+config_name, shell=True, stderr=subprocess.STDOUT) ifconfig = dict(ifcfg.interfaces().items())
except Exception: return "stopped" if config_name in ifconfig.keys():
else: return "running" return "running"
else:
return "stopped"
def get_conf_list(): def get_conf_list():
conf = [] conf = []
for i in os.listdir(conf_location): for i in os.listdir(wg_conf_path):
if ".conf" in i: if not i.startswith('.'):
i = i.replace('.conf','') if ".conf" in i:
temp = {"conf":i, "status":get_conf_status(i), "public_key": get_conf_pub_key(i)} i = i.replace('.conf', '')
if temp['status'] == "running": temp = {"conf": i, "status": get_conf_status(i), "public_key": get_conf_pub_key(i)}
temp['checked'] = 'checked' # get_conf_peers_data(i)
else: temp['checked'] = "" if temp['status'] == "running":
conf.append(temp) temp['checked'] = 'checked'
conf = sorted(conf, key=itemgetter('status')) else:
temp['checked'] = ""
conf.append(temp)
conf = sorted(conf, key=itemgetter('conf'))
return conf return conf
@app.route('/',methods=['GET'])
def index():
return render_template('index.html', conf=get_conf_list())
@app.before_request
def auth_req():
conf = configparser.ConfigParser(strict=False)
conf.read(dashboard_conf)
req = conf.get("Server", "auth_req")
session['update'] = update
session['dashboard_version'] = dashboard_version
if req == "true":
if '/static/' not in request.path and \
request.endpoint != "signin" and \
request.endpoint != "signout" and \
request.endpoint != "auth" and \
"username" not in session:
print("not loggedin")
session['message'] = "You need to sign in first!"
return redirect(url_for("signin"))
else:
if request.endpoint in ['signin', 'signout', 'auth', 'settings', 'update_acct', 'update_pwd', 'update_app_ip_port', 'update_wg_conf_path']:
return redirect(url_for("index"))
@app.route('/signin', methods=['GET'])
def signin():
message = ""
if "message" in session:
message = session['message']
session.pop("message")
return render_template('signin.html', message=message)
@app.route('/signout', methods=['GET'])
def signout():
if "username" in session:
session.pop("username")
message = "Sign out successfully!"
return render_template('signin.html', message=message)
@app.route('/settings', methods=['GET'])
def settings():
message = ""
status = ""
config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
if "message" in session and "message_status" in session:
message = session['message']
status = session['message_status']
session.pop("message")
session.pop("message_status")
required_auth = config.get("Server", "auth_req")
return render_template('settings.html',conf=get_conf_list(),message=message, status=status, app_ip=config.get("Server", "app_ip"), app_port=config.get("Server", "app_port"), required_auth=required_auth, wg_conf_path=config.get("Server", "wg_conf_path"))
@app.route('/auth', methods=['POST'])
def auth():
config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
password = hashlib.sha256(request.form['password'].encode())
if password.hexdigest() == config["Account"]["password"] and request.form['username'] == config["Account"]["username"]:
session['username'] = request.form['username']
config.clear()
return redirect(url_for("index"))
else:
session['message'] = "Username or Password is correct."
config.clear()
return redirect(url_for("signin"))
@app.route('/update_acct', methods=['POST'])
def update_acct():
config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
config.set("Account", "username", request.form['username'])
try:
config.write(open(dashboard_conf, "w"))
session['message'] = "Username update successfully!"
session['message_status'] = "success"
session['username'] = request.form['username']
config.clear()
return redirect(url_for("settings"))
except Exception:
session['message'] = "Username update failed."
session['message_status'] = "danger"
config.clear()
return redirect(url_for("settings"))
@app.route('/update_pwd', methods=['POST'])
def update_pwd():
config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
if hashlib.sha256(request.form['currentpass'].encode()).hexdigest() == config.get("Account", "password"):
if hashlib.sha256(request.form['newpass'].encode()).hexdigest() == hashlib.sha256(request.form['repnewpass'].encode()).hexdigest():
config.set("Account", "password", hashlib.sha256(request.form['repnewpass'].encode()).hexdigest())
try:
config.write(open(dashboard_conf, "w"))
session['message'] = "Password update successfully!"
session['message_status'] = "success"
config.clear()
return redirect(url_for("settings"))
except Exception:
session['message'] = "Password update failed"
session['message_status'] = "danger"
config.clear()
return redirect(url_for("settings"))
else:
session['message'] = "Your New Password does not match."
session['message_status'] = "danger"
config.clear()
return redirect(url_for("settings"))
else:
session['message'] = "Your Password does not match."
session['message_status'] = "danger"
config.clear()
return redirect(url_for("settings"))
@app.route('/update_app_ip_port', methods=['POST'])
def update_app_ip_port():
config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
config.set("Server", "app_ip", request.form['app_ip'])
config.set("Server", "app_port", request.form['app_port'])
config.write(open(dashboard_conf, "w"))
config.clear()
os.system('bash wgd.sh restart')
@app.route('/update_wg_conf_path', methods=['POST'])
def update_wg_conf_path():
config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
config.set("Server", "wg_conf_path", request.form['wg_conf_path'])
config.write(open(dashboard_conf, "w"))
session['message'] = "WireGuard Configuration Path Update Successfully!"
session['message_status'] = "success"
config.clear()
os.system('bash wgd.sh restart')
# @app.route('/check_update_dashboard', methods=['GET'])
# def check_update_dashboard():
# return have_update
@app.route('/', methods=['GET'])
def index():
print(request.referrer)
return render_template('index.html', conf=get_conf_list())
@app.route('/configuration/<config_name>', methods=['GET']) @app.route('/configuration/<config_name>', methods=['GET'])
def conf(config_name): def conf(config_name):
conf_data = { conf_data = {
@@ -192,41 +393,50 @@ def conf(config_name):
"checked": "" "checked": ""
} }
if conf_data['status'] == "stopped": if conf_data['status'] == "stopped":
return redirect('/') conf_data['checked'] = "nope"
else: else:
conf_data['checked'] = "checked" conf_data['checked'] = "checked"
return render_template('configuration.html', conf=get_conf_list(), conf_data=conf_data) return render_template('configuration.html', conf=get_conf_list(), conf_data=conf_data)
@app.route('/get_config/<config_name>', methods=['GET']) @app.route('/get_config/<config_name>', methods=['GET'])
def get_conf(config_name): def get_conf(config_name):
db = TinyDB('db/'+config_name+'.json') db = TinyDB('db/' + config_name + '.json')
conf_data = { conf_data = {
"peer_data": get_peers(config_name),
"name": config_name, "name": config_name,
"status": get_conf_status(config_name), "status": get_conf_status(config_name),
"total_data_usage": get_conf_total_data(config_name), "total_data_usage": get_conf_total_data(config_name),
"public_key": get_conf_pub_key(config_name), "public_key": get_conf_pub_key(config_name),
"listen_port": get_conf_listen_port(config_name), "listen_port": get_conf_listen_port(config_name),
"peer_data":get_conf_peers_data(config_name),
"running_peer": get_conf_running_peer_number(config_name), "running_peer": get_conf_running_peer_number(config_name),
} }
if conf_data['status'] == "stopped": if conf_data['status'] == "stopped":
return redirect('/') # return redirect('/')
conf_data['checked'] = "nope"
else: else:
conf_data['checked'] = "checked" conf_data['checked'] = "checked"
return render_template('get_conf.html', conf=get_conf_list(), conf_data=conf_data) return render_template('get_conf.html', conf=get_conf_list(), conf_data=conf_data)
@app.route('/switch/<config_name>', methods=['GET']) @app.route('/switch/<config_name>', methods=['GET'])
def switch(config_name): def switch(config_name):
if "username" not in session:
print("not loggedin")
return redirect(url_for("signin"))
status = get_conf_status(config_name) status = get_conf_status(config_name)
if status == "running": if status == "running":
try: status = subprocess.check_output("wg-quick down "+config_name, shell=True) try:
except Exception: return redirect('/') status = subprocess.check_output("wg-quick down " + config_name, shell=True)
except Exception:
return redirect('/')
elif status == "stopped": elif status == "stopped":
try: status = subprocess.check_output("wg-quick up "+config_name, shell=True) try:
except Exception: return redirect('/') status = subprocess.check_output("wg-quick up " + config_name, shell=True)
return redirect('/') except Exception:
return redirect('/')
return redirect(request.referrer)
@app.route('/add_peer/<config_name>', methods=['POST']) @app.route('/add_peer/<config_name>', methods=['POST'])
@@ -239,18 +449,23 @@ def add_peer(config_name):
return "Key already exist." return "Key already exist."
else: else:
status = "" status = ""
try: try:
status = subprocess.check_output("wg set "+config_name+" peer "+public_key+" allowed-ips "+allowed_ips, shell=True, stderr=subprocess.STDOUT) status = subprocess.check_output(
status = subprocess.check_output("wg-quick save "+config_name, shell=True, stderr=subprocess.STDOUT) "wg set " + config_name + " peer " + public_key + " allowed-ips " + allowed_ips, shell=True,
stderr=subprocess.STDOUT)
status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT)
return "true" return "true"
except subprocess.CalledProcessError as exc: except subprocess.CalledProcessError as exc:
return exc.output.strip() return exc.output.strip()
# return redirect('/configuration/'+config_name) # return redirect('/configuration/'+config_name)
@app.route('/remove_peer/<config_name>', methods=['POST']) @app.route('/remove_peer/<config_name>', methods=['POST'])
def remove_peer(config_name): def remove_peer(config_name):
db = TinyDB("db/"+config_name+".json") if get_conf_status(config_name) == "stopped":
return "Your need to turn on "+config_name+" first."
db = TinyDB("db/" + config_name + ".json")
peers = Query() peers = Query()
data = request.get_json() data = request.get_json()
delete_key = data['peer_id'] delete_key = data['peer_id']
@@ -259,37 +474,84 @@ def remove_peer(config_name):
return "This key does not exist" return "This key does not exist"
else: else:
try: try:
status = subprocess.check_output("wg set "+config_name+" peer "+delete_key+" remove", shell=True, stderr=subprocess.STDOUT) status = subprocess.check_output("wg set " + config_name + " peer " + delete_key + " remove", shell=True,
status = subprocess.check_output("wg-quick save "+config_name, shell=True, stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT)
db.remove(peers.id == delete_key) db.remove(peers.id == delete_key)
return "true" return "true"
except subprocess.CalledProcessError as exc: except subprocess.CalledProcessError as exc:
return exc.output.strip() return exc.output.strip()
@app.route('/save_peer_name/<config_name>', methods=['POST']) @app.route('/save_peer_name/<config_name>', methods=['POST'])
def save_peer_name(config_name): def save_peer_name(config_name):
data = request.get_json() data = request.get_json()
id = data['id'] id = data['id']
name = data['name'] name = data['name']
db = TinyDB("db/"+config_name+".json") db = TinyDB("db/" + config_name + ".json")
peers = Query() peers = Query()
db.update({"name": name}, peers.id == id) db.update({"name": name}, peers.id == id)
return id + " " + name return id + " " + name
@app.route('/get_peer_name/<config_name>', methods=['POST']) @app.route('/get_peer_name/<config_name>', methods=['POST'])
def get_peer_name(config_name): def get_peer_name(config_name):
data = request.get_json() data = request.get_json()
id = data['id'] id = data['id']
db = TinyDB("db/"+config_name+".json") db = TinyDB("db/" + config_name + ".json")
peers = Query() peers = Query()
result = db.search(peers.id == id) result = db.search(peers.id == id)
return result[0]['name'] return result[0]['name']
# db.update({"name": name}, peers.id == id)
def init_dashboard():
# Set Default INI File
if not os.path.isfile("wg-dashboard.ini"):
conf_file = open("wg-dashboard.ini", "w+")
config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
if "Account" not in config:
config['Account'] = {}
if "username" not in config['Account']:
config['Account']['username'] = 'admin'
if "password" not in config['Account']:
config['Account']['password'] = '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918'
if "Server" not in config:
config['Server'] = {}
if 'wg_conf_path' not in config['Server']:
config['Server']['wg_conf_path'] = '/etc/wireguard'
if 'app_ip' not in config['Server']:
config['Server']['app_ip'] = '0.0.0.0'
if 'app_port' not in config['Server']:
config['Server']['app_port'] = '10086'
if 'auth_req' not in config['Server']:
config['Server']['auth_req'] = 'true'
if 'version' not in config['Server'] or config['Server']['version'] != dashboard_version:
config['Server']['version'] = dashboard_version
config.write(open(dashboard_conf, "w"))
config.clear()
def check_update():
conf = configparser.ConfigParser(strict=False)
conf.read(dashboard_conf)
data = urllib.request.urlopen("https://api.github.com/repos/donaldzou/wireguard-dashboard/releases").read()
output = json.loads(data)
if conf.get("Server", "version") == output[0]["tag_name"]:
return "false"
else:
return "true"
if __name__ == "__main__":
init_dashboard()
update = check_update()
config = configparser.ConfigParser(strict=False)
config.read('wg-dashboard.ini')
app_ip = config.get("Server", "app_ip")
app_port = config.get("Server", "app_port")
wg_conf_path = config.get("Server", "wg_conf_path")
config.clear()
app.run(host=app_ip, debug=False, port=app_port)
app.run(host='0.0.0.0',debug=False, port=10086)

View File

3
src/requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
Flask==1.1.2
tinydb==4.3.0
ifcfg==0.21

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 145 KiB

View File

@@ -22,11 +22,11 @@ body {
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
} }
@media (max-width: 767.98px) { /*@media (max-width: 767.98px) {*/
.sidebar { /* .sidebar {*/
top: 5rem; /* top: 5rem;*/
} /* }*/
} /*}*/
.sidebar-sticky { .sidebar-sticky {
position: relative; position: relative;
@@ -47,6 +47,11 @@ body {
.sidebar .nav-link { .sidebar .nav-link {
font-weight: 500; font-weight: 500;
color: #333; color: #333;
transition: 0.2s cubic-bezier(0.82, -0.07, 0, 1.01);
}
.nav-link:hover {
padding-left: 30px;
} }
.sidebar .nav-link .feather { .sidebar .nav-link .feather {
@@ -139,4 +144,21 @@ body {
.btn-setting-peer:hover{ .btn-setting-peer:hover{
color:#007bff color:#007bff
}
.login-container{
padding: 2rem;
}
@media (max-width: 992px){
.card-col{
margin-bottom: 1rem;
}
}
.switch{
font-size: 2rem;
}
.switch:hover{
text-decoration: none
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 49 KiB

BIN
src/static/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
src/static/signin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -1,44 +1,11 @@
<html> <html>
<head> {% include "header.html" %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Wireguard Dashboard</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='dashboard.css') }}">
<link href="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css">
</head>
<body> <body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow"> {% include "navbar.html" %}
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="/">Wireguard Dashboard</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse"
data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</nav>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> {% include "sidebar.html" %}
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" href="/">Home</a></li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Configurations</span>
</h6>
<ul class="nav flex-column">
{% for i in conf%}
<li class="nav-item"><a class="nav-link" href="/configuration/{{i['conf']}}">{{i['conf']}}</a></li>
{%endfor%}
</ul>
</div>
</nav>
</div>
</div>
<div id="config_body"> <div id="config_body">
</div> </div>
@@ -141,14 +108,11 @@
</div> </div>
</div> </div>
</body> </body>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script> {% include "footer.html" %}
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js"
integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s"
crossorigin="anonymous"></script>
<script> <script>
$(".sb-{{conf_data['name']}}-url").addClass("active");
function load_data(){ function load_data(){
$.ajax({ $.ajax({
method: "GET", method: "GET",
@@ -159,11 +123,6 @@
async:false, async:false,
success: function (response){ success: function (response){
$("#config_body").html(response); $("#config_body").html(response);
$.ajax({
url: "{{ url_for('static',filename='bootstrap4-toggle.min.js') }}",
dataType: "script",
cache: true
});
} }
}) })
} }
@@ -171,24 +130,19 @@
load_data(); load_data();
setInterval(function(){ setInterval(function(){
load_data(); load_data();
}, 10000) }, 15000)
}); });
</script> </script>
<script> <script>
$('.switch').change(function() { $("body").on("click", ".switch", function (){
if ($(this).prop('checked') == false){ $(this).siblings($(".spinner-border")).css("display", "inline-block");
if (confirm('Are you sure you want to turn off this connection?')){ $(this).remove()
location.replace("/switch/"+$(this).attr('id')); location.replace("/switch/"+$(this).attr('id'));
} })
}
else{
location.replace("/switch/"+$(this).attr('id'));
}
});
$("#save_peer").click(function(){ $("#save_peer").click(function(){
if ($("#allowed_ips") != "" && $("#public_key") != ""){ if ($("#allowed_ips") != "" && $("#public_key") != ""){
var conf = $(this).attr('conf_id') var conf = $(this).attr('conf_id')
$.ajax({ $.ajax({
method: "POST", method: "POST",
url: "/add_peer/"+conf, url: "/add_peer/"+conf,
@@ -286,5 +240,7 @@
} }
}) })
}) })
</script> </script>
</html> </html>

View File

@@ -0,0 +1,8 @@
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js"
integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s"
crossorigin="anonymous"></script>

View File

@@ -1,20 +1,27 @@
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4"> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4">
<div class="info mt-4"> <div class="info mt-4">
<div class="row"> <div class="row">
<div class="col-sm"> <div class="col">
<small class="text-muted"><strong>CONFIGURATION</strong></small> <small class="text-muted"><strong>CONFIGURATION</strong></small>
<h1 class="mb-3">{{conf_data['name']}}</h1> <h1 class="mb-3">{{conf_data['name']}}</h1>
</div> </div>
<div class="col-sm"> <div class="col">
<small class="text-muted"><strong>ACTION</strong></small><br> <small class="text-muted"><strong>ACTION</strong></small><br>
<input class="mt-2 switch" id="{{conf_data['name']}}" type="checkbox" data-toggle="toggle" {{conf_data['checked']}} data-size="sm"> {% if conf_data['checked'] == "checked" %}
<a href="#" id="{{conf_data['name']}}" {{conf_data['checked']}} class="switch text-primary"><i class="bi bi-toggle2-on"></i> ON</a>
{% else %}
<a href="#" id="{{conf_data['name']}}" {{conf_data['checked']}} class="switch text-secondary"><i class="bi bi-toggle2-off"></i> OFF</a>
{% endif %}
<div class="spinner-border text-primary" role="status" style="display: none; margin-top: 10px">
<span class="sr-only">Loading...</span>
</div>
</div> </div>
<div class="w-100"></div> <div class="w-100"></div>
<div class="col-sm"> <div class="col">
<small class="text-muted"><strong>STATUS</strong></small> <small class="text-muted"><strong>STATUS</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['status']}}<span class="dot dot-{{conf_data['status']}}"></span></h6> <h6 style="text-transform: uppercase;">{{conf_data['status']}}<span class="dot dot-{{conf_data['status']}}"></span></h6>
</div> </div>
<div class="col-sm"> <div class="col">
<small class="text-muted"><strong>CONNECTED PEERS</strong></small> <small class="text-muted"><strong>CONNECTED PEERS</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['running_peer']}}</h6> <h6 style="text-transform: uppercase;">{{conf_data['running_peer']}}</h6>
</div> </div>
@@ -52,29 +59,42 @@
{% for i in conf_data['peer_data']%} {% for i in conf_data['peer_data']%}
<div class="card mb-3"> <div class="card mb-3">
<div class="card-header"> <div class="card-header">
{% if not i['name']%} <div class="row">
{{ "Untitled Peer" }} <div class="col">
{% else %} <div class="card-header-body ">
{{i['name']}} {% if not i['name']%}
{% endif %} {{ "Untitled Peer" }}
{% else %}
{{i['name']}}
{% endif %}
<span class="dot dot-{{i['status']}}"></span>
</div>
</div>
<div class="col" style="text-align: right">
<p class="text-primary" style="text-transform: uppercase; display: inline-block; margin-bottom: 0; margin-right: 1rem"><i class="bi bi-arrow-down-right"></i> {{i['total_receive']}} GB</p>
<p class="text-success" style="text-transform: uppercase; display: inline-block; margin-bottom: 0"><i class="bi bi-arrow-up-right"></i> {{i['total_sent']}} GB</p>
</div>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-sm"> {# <div class="col-sm">#}
<small class="text-muted"><strong>STATUS</strong></small> {# <small class="text-muted"><strong>STATUS</strong></small>#}
<h6 style="text-transform: uppercase;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6> {# <h6 style="text-transform: uppercase;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6>#}
</div> {# </div>#}
<div class="col-sm"> <div class="col-sm">
<small class="text-muted"><strong>PEER</strong></small> <small class="text-muted"><strong>PEER</strong></small>
<h6><samp>{{i['id']}}</samp></h6> <h6><samp class="ml-auto">{{i['id']}}</samp></h6>
</div> </div>
<div class="w-100"></div>
<div class="col-sm"> <div class="col-sm">
<small class="text-muted"><strong>ALLOWED IP</strong></small> <small class="text-muted"><strong>ALLOWED IP</strong></small>
<h6 style="text-transform: uppercase;">{{i['allowed_ip']}}</h6> <h6 style="text-transform: uppercase;">{{i['allowed_ip']}}</h6>
</div> </div>
<div class="w-100"></div>
<div class="col-sm"> <div class="col-sm">
<small class="text-muted"><strong>LATEST HANDSHAKE</strong></small> <small class="text-muted"><strong>LATEST HANDSHAKE</strong></small>
<h6 style="text-transform: uppercase;">{{i['latest_handshake']}}</h6> <h6 style="text-transform: uppercase;">{{i['latest_handshake']}}</h6>
@@ -83,20 +103,7 @@
<small class="text-muted"><strong>END POINT</strong></small> <small class="text-muted"><strong>END POINT</strong></small>
<h6 style="text-transform: uppercase;">{{i['endpoint']}}</h6> <h6 style="text-transform: uppercase;">{{i['endpoint']}}</h6>
</div> </div>
<div class="w-100"></div> <div class="w-100"></div>
{# <div class="col-sm">#}
{# <small class="text-muted"><strong>TOTAL DATA USAGE</strong></small>#}
{# <h6 style="text-transform: uppercase;">{{i['total_data']}} GB</h6>#}
{# </div>#}
<div class="col-sm">
<small class="text-muted"><strong>TOTAL RECEIVED</strong></small>
<h6 style="text-transform: uppercase;"><i class="bi bi-arrow-down-right"></i> {{i['total_receive']}} GB</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL SENT</strong></small>
<h6 style="text-transform: uppercase;"><i class="bi bi-arrow-up-right"></i> {{i['total_sent']}} GB</h6>
</div>
<div class="w-100"></div>
<div class="col-sm"> <div class="col-sm">
<!-- <small class="text-muted"><strong>ACTION</strong></small> --> <!-- <small class="text-muted"><strong>ACTION</strong></small> -->
<div class="button-group"> <div class="button-group">

13
src/templates/header.html Normal file
View File

@@ -0,0 +1,13 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Wireguard Dashboard</title>
<link rel="icon" href="{{ url_for('static',filename='logo.png') }}"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='dashboard.css') }}">
<link href="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.0.1/dist/chart.min.js"></script>
</head>

View File

@@ -1,70 +1,39 @@
<html> <html>
{% include "header.html" %}
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Wireguard Dashboard</title>
<link rel="icon" href="{{ url_for('static',filename='logo.png') }}"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='dashboard.css') }}">
<link href="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
</head>
<body> <body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow"> {% include "navbar.html" %}
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#">Wireguard Dashboard</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse"
data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</nav>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> {% include "sidebar.html" %}
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" href="/">Home</a></li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Configurations</span>
</h6>
<ul class="nav flex-column">
{% for i in conf%}
<li class="nav-item"><a class="nav-link" href="/configuration/{{i['conf']}}">{{i['conf']}}</a></li>
{%endfor%}
</ul>
</div>
</nav>
</div>
</div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4"> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<h1 class="pb-4 mt-4">Home</h1>
{% for i in conf%} {% for i in conf%}
<div class="card mt-3"> <div class="card mt-3">
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col"> <div class="col card-col">
<small class="text-muted"><strong>CONFIGURATION</strong></small> <small class="text-muted"><strong>CONFIGURATION</strong></small>
<a href="/configuration/{{i['conf']}}"> <a href="/configuration/{{i['conf']}}">
<h5 class="card-title">{{i['conf']}}</h5> <h6 class="card-title" style="margin:0 !important;">{{i['conf']}}</h6>
</a> </a>
</div> </div>
<div class="col"> <div class="col card-col">
<input class="mt-2 switch" id="{{i['conf']}}" type="checkbox" data-toggle="toggle" {{i['checked']}} data-size="sm">
</div>
<div class="w-100"></div>
<div class="col-sm">
<small class="text-muted"><strong>STATUS</strong></small> <small class="text-muted"><strong>STATUS</strong></small>
<h6 style="text-transform: uppercase;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6> <h6 style="text-transform: uppercase; margin:0 !important;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6>
</div> </div>
<div class="col-sm"> <div class="col-md card-col">
<small class="text-muted"><strong>PUBLIC KEY</strong></small> <small class="text-muted"><strong>PUBLIC KEY</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{i['public_key']}}</samp></h6> <h6 style="text-transform: uppercase; margin:0 !important;"><samp>{{i['public_key']}}</samp></h6>
</div>
<div class="col-md">
{% if i['checked'] == "checked" %}
<a href="#" id="{{i['conf']}}" {{i['checked']}} class="switch text-primary tt"><i class="bi bi-toggle2-on"></i></a>
{% else %}
<a href="#" id="{{i['conf']}}" {{i['checked']}} class="switch text-secondary"><i class="bi bi-toggle2-off"></i></a>
{% endif %}
<div class="spinner-border text-primary" role="status" style="display: none">
<span class="sr-only">Loading...</span>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@@ -72,25 +41,13 @@
</main> </main>
</div> </div>
</body> </body>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" {% include "footer.html" %}
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js"
integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/js/bootstrap4-toggle.min.js"></script>
<script> <script>
$('.switch').change(function() { $('.switch').click(function() {
if ($(this).prop('checked') == false){ $(this).siblings($(".spinner-border")).css("display", "inline-block")
if (confirm('Are you sure you want to turn off this connection?')){ $(this).remove()
location.replace("/switch/"+$(this).attr('id')) location.replace("/switch/"+$(this).attr('id'))
}
}
else{
location.replace("/switch/"+$(this).attr('id'))
}
}); });
$(".sb-home-url").addClass("active")
</script> </script>
</html> </html>

View File

@@ -0,0 +1,7 @@
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="/">Wireguard Dashboard</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse"
data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</nav>

131
src/templates/settings.html Normal file
View File

@@ -0,0 +1,131 @@
<html>
{% include "header.html" %}
<body>
{% include "navbar.html" %}
<div class="container-fluid">
{% include "sidebar.html" %}
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div class="setting-container mt-4">
{% if message != ""%}
<div class="alert alert-{{ status }}" role="alert">
{{ message }}
</div>
{% endif %}
<h1 class="pb-4">Settings</h1>
{% if required_auth == "true" %}
<h3>Account</h3>
<form action="/update_acct" method="post">
<div class="form-group">
<label for="username">Username</label>
<input type="text" class="form-control mb-4" id="username" name="username" value="{{ session['username'] }}">
<button type="submit" class="btn btn-success" >Update Account</button>
</div>
</form>
<hr>
<h3>WireGuard Configuration Path</h3>
<form action="/update_wg_conf_path" method="post" class="update_wg_conf_path">
<div class="form-group">
<label for="username">Path</label>
<input type="text" class="form-control mb-4" id="wg_conf_path" name="wg_conf_path" value="{{ wg_conf_path }}">
<button class="btn btn-danger change_path">Update Path & Restart Dashboard</button>
</div>
</form>
<hr>
<h3>Security</h3>
<form action="/update_pwd", method="post">
<div class="form-group">
<label for="currentpass">Current Password</label>
<input type="password" class="form-control mb-2" id="currentpass" name="currentpass">
<label for="newpass">New Password</label>
<input type="password" class="form-control mb-2" id="newpass" name="newpass">
<label for="repnewpass">Repeat New Password</label>
<input type="password" class="form-control mb-4" id="repnewpass" name="repnewpass">
<button type="submit" class="btn btn-danger">Update Password</button>
</div>
</form>
<hr>
{% endif %}
<h3>Dashboard Configuration</h3>
<form action="/update_app_ip_port" method="post" class="update_app_ip_port">
<div class="form-group">
<div class="row">
<div class="col-sm">
<label for="app_ip" >Dashboard IP</label>
<input type="text" class="form-control mb-2" id="app_ip" name="app_ip" value="{{ app_ip }}">
<p><small class="text-danger mb-4">0.0.0.0 means it can be access by anyone with your server IP Address.</small></p>
</div>
<div class="col-sm">
<label for="app_port">Dashboard Port</label>
<input type="text" class="form-control mb-4" id="app_port" name="app_port" value="{{ app_port }}">
</div>
</div>
<button type="button" class="btn btn-danger confirm_modal" data-toggle="modal" data-target="#confirmModal">Update Configuration & Restart</button>
</div>
</form>
</div>
</main>
<!-- Modal -->
<div class="modal fade" id="confirmModal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Confirm Dashboard Configuration</h5>
</div>
<div class="modal-body">
<small>Dashboard Original IP</small>
<p>{{ app_ip }}</p>
<small style="font-weight: bold" class="text-bold">Dashboard New IP</small>
<p class="app_new_ip text-bold text-danger" style="font-weight: bold"></p>
<small>Dashboard Original Port</small>
<p>{{ app_port }}</p>
<small style="font-weight: bold" class="text-bold">Dashboard New Port</small>
<p class="app_new_port text-bold text-danger" style="font-weight: bold"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary cancel_restart" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger confirm_restart">Confirm & Restart Dashboard</button>
</div>
</div>
</div>
</div>
</div>
</body>
{% include "footer.html" %}
<script>
$(".sb-settings-url").addClass("active")
$(".confirm_modal").click(function (){
$(".app_new_ip").html($("#app_ip")[0].value)
$(".app_new_port").html($("#app_port")[0].value)
})
$(".confirm_restart").click(function (){
$(".cancel_restart").remove()
countdown = 7;
$.post('/update_app_ip_port', $('.update_app_ip_port').serialize())
url = $("#app_ip")[0].value+":"+$("#app_port")[0].value;
$(".confirm_restart").attr("disabled", "disabled")
setInterval(function (){
if (countdown === 0){
window.location.replace("http://"+url);
}
$(".confirm_restart").html("Redirecting you in "+countdown+" seconds.")
countdown--;
},1000)
});
$(".change_path").click(function (){
$(this).attr("disabled", "disabled");
countdown = 5;
setInterval(function (){
if (countdown === 0){
location.reload()
}
$(".change_path").html("Redirecting you in "+countdown+" seconds.")
countdown--;
},1000)
$.post('/update_wg_conf_path', $('.update_wg_conf_path').serialize())
});
</script>
</html>

View File

@@ -0,0 +1,35 @@
<div class="row">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link sb-home-url" href="/">Home</a></li>
{% if "username" in session %}
<li class="nav-item"><a class="nav-link sb-settings-url" href="/settings">Settings</a></li>
{% endif %}
{% if session['update'] == "true" %}
<li class="nav-item sb-update-li"><a class="nav-link sb-update-url" href="https://github.com/donaldzou/wireguard-dashboard/#-how-to-update-the-dashboard">New Update Available!<span class="dot dot-running"></span></a></li>
{% endif %}
</ul>
<hr>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Configurations</span>
</h6>
<ul class="nav flex-column">
{% for i in conf%}
<li class="nav-item"><a class="nav-link sb-{{i['conf']}}-url" href="/configuration/{{i['conf']}}">{{i['conf']}}</a></li>
{%endfor%}
</ul>
<hr>
{% if "username" in session %}
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link text-danger" href="/signout" style="font-weight: bold">Sign Out</a></li>
</ul>
{% endif %}
<ul class="nav flex-column">
<li class="nav-item"><a href="https://github.com/donaldzou/wireguard-dashboard"><small class="nav-link text-muted">{{ session['dashboard_version'] }}</small></a></li>
</ul>
</div>
</nav>
</div>
</div>

43
src/templates/signin.html Normal file
View File

@@ -0,0 +1,43 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Wireguard Dashboard | Login</title>
<link rel="icon" href="{{ url_for('static',filename='logo.png') }}"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='dashboard.css') }}">
</head>
<body>
{% include "navbar.html" %}
<div class="container-fluid">
<main role="main" class="container login-container">
<div class="login-box" style="margin: auto !important;">
<h3 class="text-center">Sign In</h3>
<form style="margin-left: auto !important; margin-right: auto !important; max-width: 500px;" action="/auth" method="post">
{% if message != ""%}
<div class="alert alert-danger" role="alert">
{{ message }}
</div>
{% endif %}
<div class="form-group">
<label for="username" class="text-left">User Name</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password" class="text-left">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Keep you login</label>
</div>
<button type="submit" class="btn btn-dark" style="width: 100%;">Sign In</button>
</form>
</div>
</main>
</div>
</body>
{% include "footer.html" %}
</html>

110
src/wgd.sh Normal file
View File

@@ -0,0 +1,110 @@
#!/bin/bash
app_name="dashboard.py"
dashes='------------------------------------------------------------'
help () {
printf "<Wireguard Dashboard> by Donald Zou - https://github.com/donaldzou \n"
printf "Usage: sh wg-dashboard.sh <option>"
printf "\n \n"
printf "Available options: \n"
printf " start: To start Wireguard Dashboard.\n"
printf " stop: To stop Wireguard Dashboard.\n"
printf " debug: To start Wireguard Dashboard in debug mode (i.e run in foreground).\n"
printf " update: To update Wireguard Dashboard to the newest version from GitHub.\n"
printf "Thank you for using this dashboard! Your support is my motivation ;) \n"
printf "\n"
}
check_wgd_status(){
if ps aux | grep '[p]ython3 '$app_name > /dev/null;
then
return 0
else
return 1
fi
}
start_wgd () {
printf "Starting Wireguard Dashboard in the background. \n"
if [ ! -d "log" ]
then mkdir "log"
fi
d=$(date '+%Y%m%d%H%M%S')
python3 "$app_name" > log/"$d".txt 2>&1 &
printf "Log file: log/%s""$d"".txt\n"
}
stop_wgd() {
kill "$(ps aux | grep "[p]ython3 $app_name" | awk '{print $2}')"
}
start_wgd_debug() {
printf "Starting Wireguard Dashboard in the foreground. \n"
python3 "$app_name"
}
update_wgd() {
new_ver=$(python3 -c "import json; import urllib.request; data = urllib.request.urlopen('https://api.github.com/repos/donaldzou/wireguard-dashboard/releases').read(); output = json.loads(data);print(output[0]['tag_name'])")
printf "%s\n" "$dashes"
printf "Are you sure you want to update to the %s? (Y/N): " "$new_ver"
read up
if [ "$up" = "Y" ]; then
printf "%s\n" "$dashes"
printf "| Shutting down Wireguard Dashboard... |\n"
printf "%s\n" "$dashes"
printf "| Downloading %s from GitHub... |\n" "$new_ver"
printf "%s\n" "$dashes"
git pull https://github.com/donaldzou/wireguard-dashboard.git $new_ver --force > /dev/null 2>&1
printf "| Update Successfully! |\n"
printf "%s\n" "$dashes"
printf "| Now you can start the dashboard with >> sh wgd.sh start |\n"
printf "%s\n" "$dashes"
exit 1
else
printf "%s\n" "$dashes"
printf "CANCEL update. \n"
printf "%s\n" "$dashes"
fi
}
if [ "$#" != 1 ];
then
help
else
if [ "$1" = "start" ]; then
if check_wgd_status; then
printf "Wireguard Dashboard is already running. \n"
else
start_wgd
fi
elif [ "$1" = "stop" ]; then
if check_wgd_status; then
stop_wgd
printf "Wireguard Dashboard is stopped. \n"
else
printf "Wireguard Dashboard is not running. \n"
fi
elif [ "$1" = "update" ]; then
update_wgd
elif [ "$1" = "restart" ]; then
if check_wgd_status; then
stop_wgd
sleep 2
printf "Wireguard Dashboard is stopped. \n"
start_wgd_debug
else
start_wgd_debug
fi
elif [ "$1" = "debug" ]; then
if check_wgd_status; then
printf "Wireguard Dashboard is already running. \n"
else
start_wgd_debug
fi
else
help
fi
fi