1
0
mirror of https://github.com/donaldzou/WGDashboard.git synced 2024-11-22 15:20:09 +01:00

v2.0-beta-6 Commit

This commit is contained in:
Donald Cheng Hong Zou 2021-05-13 18:00:40 -04:00
parent a05d6979ec
commit f76579fe5e
17 changed files with 518 additions and 153 deletions

View File

@ -1,9 +1,12 @@
<hr>
<p align=center>I'm looking for someone that have experiences on migrating this project to a Docker app, with a complete solution ;) If you know how please <a href="https://github.com/donaldzou/wireguard-dashboard/issues/20">comment in here</a>.</p>
<hr>
<p align="center"> <p align="center">
<img src="https://raw.githubusercontent.com/donaldzou/wireguard-dashboard/main/img/Group%202.png" width="128"> <img src="https://raw.githubusercontent.com/donaldzou/wireguard-dashboard/main/img/Group%202.png" width="128">
</p> </p>
<h1 align="center"> Wireguard Dashboard</h1> <h1 align="center"> Wireguard Dashboard</h1>
<p align="center"> <p align="center">
<img src="http://ForTheBadge.com/images/badges/made-with-python.svg"> <img src="http://ForTheBadge.com/images/badges/made-with-python.svg">
</p> </p>
@ -11,34 +14,25 @@
<a href="https://github.com/donaldzou/wireguard-dashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard"></a> <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>
<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> <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>
<hr>
<p align=center>I'm looking for someone that have experiences on migrating this project to a Docker app, with a complete solution ;) If you know how please <a href="https://github.com/donaldzou/wireguard-dashboard/issues/20">comment in here</a>.</p>
<hr>
## 📣 What's New: Version 2.0
## 📣 What's New: Version 2.0.1
- Added **Ping** and **Traceroute** tool
- Adjusted the calculation of data usage on each peers
- Added refresh interval of the dashboard
<hr>
### ⚠️ **Update from v1.x.x** ### ⚠️ **Update from v1.x.x**
1. Stop the dashboard if it is running. 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. 2. You can use `git pull https://github.com/donaldzou/Wireguard-Dashboard.git v2.0.1` to get the new update inside `Wireguard-Dashboard` directory.
3. Proceed **Step 2 & 3** in the [Install](#-install) step down below. 3. Proceed **Step 2 & 3** in the [Install](#-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 ## 💡 Features
@ -63,9 +57,6 @@
- **Note: For peers, `PublicKey` & `AllowedIPs` is required.** - **Note: For peers, `PublicKey` & `AllowedIPs` is required.**
- Python 3.7+ & Pip3 - Python 3.7+ & Pip3
```
$ sudo apt-get install python3 python3-pip
```
@ -73,21 +64,22 @@
**1. Download Wireguard Dashboard** **1. Download Wireguard Dashboard**
``` ```shell
$ git clone -b v2.0 https://github.com/donaldzou/Wireguard-Dashboard.git git clone -b v2.0.1 https://github.com/donaldzou/Wireguard-Dashboard.git
``` ```
**2. Install Python Dependencies** **2. Install Python Dependencies**
``` ```shell
$ cd Wireguard-Dashboard/src cd Wireguard-Dashboard/src
$ python3 -m pip install -r requirements.txt python3 -m pip install -r requirements.txt
``` ```
**3. Install & run Wireguard Dashboard** **3. Install & run Wireguard Dashboard**
``` ```shell
$ sudo sh wgd.sh start chmod u+x wgd.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. 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.
@ -98,13 +90,13 @@ Access your server with port `10086` ! e.g (http://your_server_ip:10086), contin
**1. Start/Stop/Restart Wireguard Dashboard** **1. Start/Stop/Restart Wireguard Dashboard**
``` ```shell
$ cd Wireguard-Dashboard/src cd Wireguard-Dashboard/src
$ sudo sh wgd.sh start # Start the dashboard in background ./wgd.sh start # Start the dashboard in background
$ sudo sh wgd.sh debug # Start the dashboard in foreground (debug mode) ./wgd.sh debug # Start the dashboard in foreground (debug mode)
$ sudo sh wgd.sh stop # Stop the dashboard ./wgd.sh stop # Stop the dashboard
$ sudo sh wgd.sh restart # Restart the dasboard ./wgd.sh restart # Restart the dasboard
$ sudo sh wgd.sh update # Update the dashboard ./wgd.sh update # Update the dashboard
``` ```
⚠️ **For first time user please also read the next section.** ⚠️ **For first time user please also read the next section.**
@ -141,10 +133,10 @@ All these settings will be able to configure within the dashboard in **Settings*
## ❓ How to update the dashboard? ## ❓ How to update the dashboard?
``` ```shell
$ cd wireguard-dashboard cd wireguard-dashboard
$ sudo sh wgd.sh update # Perform update sudo sh wgd.sh update # Perform update
$ sudo sh wgd.sh start # Start dashboard sudo sh wgd.sh start # Start dashboard
``` ```
@ -167,6 +159,22 @@ $ sudo sh wgd.sh start # Start dashboard
<p align=center>Settings Page</p> <p align=center>Settings Page</p>
## 🛒 Dependencies
- CSS/JS
- [Bootstrap](https://getbootstrap.com/docs/4.6/getting-started/introduction/) `v4.6.0`
- [Bootstrap Icon](https://icons.getbootstrap.com) `v1.4.0`
- [jQuery](https://jquery.com) `v3.5.1`
- Python
- [Flask](https://pypi.org/project/Flask/) `v1.1.2`
- [TinyDB](https://pypi.org/project/tinydb/) `v4.3.0`
- [ifcfg](https://pypi.org/project/ifcfg/) `v0.21`
- [icmplib](https://pypi.org/project/icmplib/) `v2.1.1`
## Contributors ✨ ## Contributors ✨
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

View File

@ -1,9 +1,7 @@
dashboard_version = 'v2.0'
# Python Built-in Library # Python Built-in Library
import os import os
from flask import Flask, request, render_template, redirect, url_for, session, abort from flask import Flask, request, render_template, redirect, url_for, session, abort, jsonify
from icmplib import ping, multiping, traceroute, resolve, Host, Hop
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
@ -11,28 +9,30 @@ import secrets
import hashlib import hashlib
import json, urllib.request import json, urllib.request
import configparser import configparser
import re
# PIP installed library # PIP installed library
import ifcfg import ifcfg
from tinydb import TinyDB, Query from tinydb import TinyDB, Query
# Dashboard Version
dashboard_version = 'v2.0.1'
# Dashboard Config Name
dashboard_conf = 'wg-dashboard.ini' dashboard_conf = 'wg-dashboard.ini'
# Upgrade Required
update = "" update = ""
# Flask App Configuration
app = Flask("Wireguard Dashboard") app = Flask("Wireguard Dashboard")
app.secret_key = secrets.token_urlsafe(16) app.secret_key = secrets.token_urlsafe(16)
app.config['TEMPLATES_AUTO_RELOAD'] = True app.config['TEMPLATES_AUTO_RELOAD'] = True
conf_data = {}
def get_conf_peer_key(config_name): def get_conf_peer_key(config_name):
keys = []
try: try:
peer_key = subprocess.check_output("wg show " + config_name + " peers", shell=True) peer_key = subprocess.check_output("wg show " + config_name + " peers", shell=True)
peer_key = peer_key.decode("UTF-8").split()
return peer_key
except Exception: except Exception:
return "stopped" return config_name+" is not running."
peer_key = peer_key.decode("UTF-8").split()
for i in peer_key: keys.append(i)
return keys
def get_conf_running_peer_number(config_name): def get_conf_running_peer_number(config_name):
running = 0 running = 0
@ -52,9 +52,14 @@ def get_conf_running_peer_number(config_name):
count += 2 count += 2
return running return running
def is_match(regex, text):
pattern = re.compile(regex)
return pattern.search(text) is not None
def read_conf_file(config_name): def read_conf_file(config_name):
# Read Configuration File Start # Read Configuration File Start
conf_location = wg_conf_path+"/" + config_name + ".conf" conf_location = wg_conf_path + "/" + config_name + ".conf"
f = open(conf_location, 'r') f = open(conf_location, 'r')
file = f.read().split("\n") file = f.read().split("\n")
conf_peer_data = { conf_peer_data = {
@ -63,31 +68,32 @@ def read_conf_file(config_name):
} }
peers_start = 0 peers_start = 0
for i in range(len(file)): for i in range(len(file)):
if file[i] == "[Peer]": if not is_match("^#(.*)",file[i]):
peers_start = i if file[i] == "[Peer]":
break peers_start = i
else: break
if len(file[i]) > 0: else:
if file[i] != "[Interface]": if len(file[i]) > 0:
tmp = file[i].replace(" ", "").split("=", 1) if file[i] != "[Interface]":
if len(tmp) == 2: tmp = re.split(r'\s*=\s*', file[i], 1)
conf_peer_data['Interface'][tmp[0]] = tmp[1] if len(tmp) == 2:
conf_peer_data['Interface'][tmp[0]] = tmp[1]
conf_peers = file[peers_start:] conf_peers = file[peers_start:]
peer = -1 peer = -1
for i in conf_peers: for i in conf_peers:
if i == "[Peer]": if not is_match("^#(.*)", i):
peer += 1 if i == "[Peer]":
conf_peer_data["Peers"].append({}) peer += 1
else: conf_peer_data["Peers"].append({})
if len(i) > 0: else:
tmp = i.replace(" ", "").split("=", 1) if len(i) > 0:
if len(tmp) == 2: tmp = re.split('\s*=\s*', i,1)
conf_peer_data["Peers"][peer][tmp[0]] = tmp[1] if len(tmp) == 2:
conf_peer_data["Peers"][peer][tmp[0]] = tmp[1]
# Read Configuration File End # Read Configuration File End
return conf_peer_data 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()
@ -141,26 +147,25 @@ def get_conf_peers_data(config_name):
cur_i = db.search(peers.id == data_usage[count]) cur_i = db.search(peers.id == data_usage[count])
total_sent = cur_i[0]['total_sent'] total_sent = cur_i[0]['total_sent']
total_receive = cur_i[0]['total_receive'] total_receive = cur_i[0]['total_receive']
traffic = cur_i[0]['traffic']
cur_total_sent = round(int(data_usage[count + 2]) / (1024 ** 3), 4) cur_total_sent = round(int(data_usage[count + 2]) / (1024 ** 3), 4)
cur_total_receive = round(int(data_usage[count + 1]) / (1024 ** 3), 4) cur_total_receive = round(int(data_usage[count + 1]) / (1024 ** 3), 4)
if cur_i[0]["status"] == "running": if cur_i[0]["status"] == "running":
if total_sent <= cur_total_sent: if total_sent <= cur_total_sent and total_receive <= cur_total_receive:
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 total_receive = cur_total_receive
else: total_receive += cur_total_receive else:
db.update({"total_receive": round(total_receive,4), now = datetime.now()
"total_sent": round(total_sent,4), ctime = now.strftime("%d/%m/%Y %H:%M:%S")
traffic.append({"time": ctime, "total_receive": round(total_receive, 4),"total_sent": round(total_sent, 4),
"total_data": round(total_receive + total_sent, 4)})
total_sent = 0
total_receive = 0
db.update({"traffic": traffic}, peers.id == data_usage[count])
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]) "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])
count += 3 count += 3
# Get endpoint # Get endpoint
@ -176,7 +181,8 @@ def get_conf_peers_data(config_name):
# Get allowed ip # Get allowed ip
for i in conf_peer_data["Peers"]: for i in conf_peer_data["Peers"]:
db.update({"allowed_ip":i.get('AllowedIPs', '(None)')}, peers.id == i["PublicKey"]) db.update({"allowed_ip": i.get('AllowedIPs', '(None)')}, peers.id == i["PublicKey"])
db.close()
def get_peers(config_name): def get_peers(config_name):
@ -184,6 +190,7 @@ def get_peers(config_name):
db = TinyDB('db/' + config_name + '.json') db = TinyDB('db/' + config_name + '.json')
result = db.all() result = db.all()
result = sorted(result, key=lambda d: d['status']) result = sorted(result, key=lambda d: d['status'])
db.close()
return result return result
@ -209,9 +216,15 @@ def get_conf_total_data(config_name):
upload_total = 0 upload_total = 0
download_total = 0 download_total = 0
for i in db.all(): for i in db.all():
upload_total += round(i['total_sent'],4) upload_total += i['total_sent']
download_total += round(i['total_receive'],4) download_total += i['total_receive']
for k in i['traffic']:
upload_total += k['total_sent']
download_total += k['total_receive']
total = round(upload_total + download_total, 4) total = round(upload_total + download_total, 4)
upload_total = round(upload_total, 4)
download_total = round(download_total, 4)
db.close()
return [total, upload_total, download_total] return [total, upload_total, download_total]
@ -240,8 +253,6 @@ def get_conf_list():
return conf return conf
@app.before_request @app.before_request
def auth_req(): def auth_req():
conf = configparser.ConfigParser(strict=False) conf = configparser.ConfigParser(strict=False)
@ -259,9 +270,11 @@ def auth_req():
session['message'] = "You need to sign in first!" session['message'] = "You need to sign in first!"
return redirect(url_for("signin")) return redirect(url_for("signin"))
else: else:
if request.endpoint in ['signin', 'signout', 'auth', 'settings', 'update_acct', 'update_pwd', 'update_app_ip_port', 'update_wg_conf_path']: 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")) return redirect(url_for("index"))
@app.route('/signin', methods=['GET']) @app.route('/signin', methods=['GET'])
def signin(): def signin():
message = "" message = ""
@ -279,7 +292,6 @@ def signout():
return render_template('signin.html', message=message) return render_template('signin.html', message=message)
@app.route('/settings', methods=['GET']) @app.route('/settings', methods=['GET'])
def settings(): def settings():
message = "" message = ""
@ -292,14 +304,18 @@ def settings():
session.pop("message") session.pop("message")
session.pop("message_status") session.pop("message_status")
required_auth = config.get("Server", "auth_req") 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")) 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']) @app.route('/auth', methods=['POST'])
def auth(): def auth():
config = configparser.ConfigParser(strict=False) config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf) config.read(dashboard_conf)
password = hashlib.sha256(request.form['password'].encode()) password = hashlib.sha256(request.form['password'].encode())
if password.hexdigest() == config["Account"]["password"] and request.form['username'] == config["Account"]["username"]: if password.hexdigest() == config["Account"]["password"] and request.form['username'] == config["Account"][
"username"]:
session['username'] = request.form['username'] session['username'] = request.form['username']
config.clear() config.clear()
return redirect(url_for("index")) return redirect(url_for("index"))
@ -308,6 +324,7 @@ def auth():
config.clear() config.clear()
return redirect(url_for("signin")) return redirect(url_for("signin"))
@app.route('/update_acct', methods=['POST']) @app.route('/update_acct', methods=['POST'])
def update_acct(): def update_acct():
config = configparser.ConfigParser(strict=False) config = configparser.ConfigParser(strict=False)
@ -326,12 +343,14 @@ def update_acct():
config.clear() config.clear()
return redirect(url_for("settings")) return redirect(url_for("settings"))
@app.route('/update_pwd', methods=['POST']) @app.route('/update_pwd', methods=['POST'])
def update_pwd(): def update_pwd():
config = configparser.ConfigParser(strict=False) config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf) config.read(dashboard_conf)
if hashlib.sha256(request.form['currentpass'].encode()).hexdigest() == config.get("Account", "password"): 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(): 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()) config.set("Account", "password", hashlib.sha256(request.form['repnewpass'].encode()).hexdigest())
try: try:
config.write(open(dashboard_conf, "w")) config.write(open(dashboard_conf, "w"))
@ -355,6 +374,7 @@ def update_pwd():
config.clear() config.clear()
return redirect(url_for("settings")) return redirect(url_for("settings"))
@app.route('/update_app_ip_port', methods=['POST']) @app.route('/update_app_ip_port', methods=['POST'])
def update_app_ip_port(): def update_app_ip_port():
config = configparser.ConfigParser(strict=False) config = configparser.ConfigParser(strict=False)
@ -365,6 +385,7 @@ def update_app_ip_port():
config.clear() config.clear()
os.system('bash wgd.sh restart') os.system('bash wgd.sh restart')
@app.route('/update_wg_conf_path', methods=['POST']) @app.route('/update_wg_conf_path', methods=['POST'])
def update_wg_conf_path(): def update_wg_conf_path():
config = configparser.ConfigParser(strict=False) config = configparser.ConfigParser(strict=False)
@ -376,15 +397,71 @@ def update_wg_conf_path():
config.clear() config.clear()
os.system('bash wgd.sh restart') os.system('bash wgd.sh restart')
# @app.route('/check_update_dashboard', methods=['GET']) @app.route('/update_dashboard_refresh_interval', methods=['POST'])
# def check_update_dashboard(): def update_dashboard_refresh_interval():
# return have_update config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
config.set("Server", "dashboard_refresh_interval", str(request.form['interval']))
config.write(open(dashboard_conf, "w"))
config.clear()
return "true"
@app.route('/get_ping_ip', methods=['POST'])
def get_ping_ip():
config = request.form['config']
db = TinyDB('db/' + config + '.json')
html = ""
for i in db.all():
html += '<optgroup label="'+i['name']+' - '+i['id']+'">'
allowed_ip = str(i['allowed_ip']).split(",")
for k in allowed_ip:
k = k.split("/")
if len(k) == 2:
html += "<option value="+k[0]+">"+k[0]+"</option>"
endpoint = str(i['endpoint']).split(":")
if len(endpoint) == 2:
html += "<option value=" + endpoint[0] + ">" + endpoint[0] + "</option>"
html += "</optgroup>"
return html
@app.route('/ping_ip', methods=['POST'])
def ping_ip():
try:
result = ping(''+request.form['ip']+'', count=int(request.form['count']),privileged=True, source=None)
returnjson = {
"address": result.address,
"is_alive": result.is_alive,
"min_rtt": result.min_rtt,
"avg_rtt": result.avg_rtt,
"max_rtt": result.max_rtt,
"package_sent": result.packets_sent,
"package_received": result.packets_received,
"package_loss": result.packet_loss
}
return jsonify(returnjson)
except Exception:
return "Error"
@app.route('/traceroute_ip', methods=['POST'])
def traceroute_ip():
try:
result = traceroute(''+request.form['ip']+'', first_hop=1, max_hops=30, count=1, fast=True)
returnjson = []
last_distance = 0
for hop in result:
if last_distance + 1 != hop.distance:
returnjson.append({"hop":"*", "ip":"*", "avg_rtt":"", "min_rtt":"", "max_rtt":""})
returnjson.append({"hop": hop.distance, "ip": hop.address, "avg_rtt": hop.avg_rtt, "min_rtt": hop.min_rtt, "max_rtt": hop.max_rtt})
last_distance = hop.distance
return jsonify(returnjson)
except Exception:
return "Error"
@app.route('/', methods=['GET']) @app.route('/', methods=['GET'])
def index(): def index():
print(request.referrer)
return render_template('index.html', conf=get_conf_list()) 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 = {
@ -396,12 +473,13 @@ def conf(config_name):
conf_data['checked'] = "nope" 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) config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
return render_template('configuration.html', conf=get_conf_list(), conf_data=conf_data, dashboard_refresh_interval=int(config.get("Server","dashboard_refresh_interval")))
@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')
conf_data = { conf_data = {
"peer_data": get_peers(config_name), "peer_data": get_peers(config_name),
"name": config_name, "name": config_name,
@ -445,6 +523,8 @@ def add_peer(config_name):
public_key = data['public_key'] public_key = data['public_key']
allowed_ips = data['allowed_ips'] allowed_ips = data['allowed_ips']
keys = get_conf_peer_key(config_name) keys = get_conf_peer_key(config_name)
if public_key is not list:
return config_name+" is not running."
if public_key in keys: if public_key in keys:
return "Key already exist." return "Key already exist."
else: else:
@ -454,23 +534,30 @@ def add_peer(config_name):
"wg set " + config_name + " peer " + public_key + " allowed-ips " + allowed_ips, shell=True, "wg set " + config_name + " peer " + public_key + " allowed-ips " + allowed_ips, shell=True,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT) status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT)
get_conf_peers_data(config_name)
db = TinyDB("db/" + config_name + ".json")
peers = Query()
db.update({"name": data['name']}, peers.id == public_key)
db.close()
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)
@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):
if get_conf_status(config_name) == "stopped": if get_conf_status(config_name) == "stopped":
return "Your need to turn on "+config_name+" first." return "Your need to turn on " + config_name + " first."
db = TinyDB("db/" + config_name + ".json") 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']
keys = get_conf_peer_key(config_name) keys = get_conf_peer_key(config_name)
if keys is not list:
return config_name+" is not running."
if delete_key not in keys: if delete_key not in keys:
db.close()
return "This key does not exist" return "This key does not exist"
else: else:
try: try:
@ -478,6 +565,7 @@ def remove_peer(config_name):
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
status = subprocess.check_output("wg-quick save " + config_name, shell=True, 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)
db.close()
return "true" return "true"
except subprocess.CalledProcessError as exc: except subprocess.CalledProcessError as exc:
return exc.output.strip() return exc.output.strip()
@ -490,8 +578,8 @@ def save_peer_name(config_name):
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)
db.close()
return id + " " + name return id + " " + name
@ -502,8 +590,12 @@ def get_peer_name(config_name):
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)
db.close()
return result[0]['name'] return result[0]['name']
def init_dashboard(): def init_dashboard():
# Set Default INI File # Set Default INI File
if not os.path.isfile("wg-dashboard.ini"): if not os.path.isfile("wg-dashboard.ini"):
@ -530,9 +622,12 @@ def init_dashboard():
config['Server']['auth_req'] = 'true' config['Server']['auth_req'] = 'true'
if 'version' not in config['Server'] or config['Server']['version'] != dashboard_version: if 'version' not in config['Server'] or config['Server']['version'] != dashboard_version:
config['Server']['version'] = dashboard_version config['Server']['version'] = dashboard_version
if 'dashboard_refresh_interval' not in config['Server']:
config['Server']['dashboard_refresh_interval'] = '15000'
config.write(open(dashboard_conf, "w")) config.write(open(dashboard_conf, "w"))
config.clear() config.clear()
def check_update(): def check_update():
conf = configparser.ConfigParser(strict=False) conf = configparser.ConfigParser(strict=False)
conf.read(dashboard_conf) conf.read(dashboard_conf)
@ -554,4 +649,3 @@ if __name__ == "__main__":
wg_conf_path = config.get("Server", "wg_conf_path") wg_conf_path = config.get("Server", "wg_conf_path")
config.clear() config.clear()
app.run(host=app_ip, debug=False, port=app_port) app.run(host=app_ip, debug=False, port=app_port)

34
src/db/wg0.conf Normal file
View File

@ -0,0 +1,34 @@
[Interface]
Address = 10.200.200.1/24
SaveConfig = true
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820
PrivateKey = 8DsSMli3okgUx5frKbFQ0fMW5ZMyqyxOdOW7+g21L18=
[Peer]
#Name = 10
PublicKey = Hw2sISygeuT4eQyubDdP0CA2RJHYcpHhBOnIn6riEA0=
AllowedIPs = 10.200.200.9/32, 10.200.200.2/32
Endpoint = 142.114.220.189:58403
[Peer]
PublicKey = 0pCjuGUyXIxTmCZCoFeVtMOgZFVkU9WPGilJxLpYVAI=
AllowedIPs = 10.200.200.3/32
Endpoint = 76.67.102.20:55349
[Peer]
PublicKey = sJrgj14BfToasB5EhQfUjw2xTj3FoU/lSca9bDx+2Ww=
AllowedIPs = 10.200.200.4/32
Endpoint = 175.0.140.3:59637
[Peer]
PublicKey = IxM4gsOWugRtQ0WmFnaAYUCVquTsSqxHiE7oqisKsRQ=
AllowedIPs = 10.200.200.5/32
Endpoint = 180.152.230.148:61618
[Peer]
PublicKey = TSsTB1NPTOHfqNkcNZbJQiz+XcJ32AudypDwa5ItmBM=
AllowedIPs = 10.200.200.6/32
Endpoint = 118.250.38.191:51820

View File

@ -1,3 +1,4 @@
Flask==1.1.2 Flask==1.1.2
tinydb==4.3.0 tinydb==4.3.0
ifcfg==0.21 ifcfg==0.21
icmplib==2.1.1

View File

@ -1 +0,0 @@
!function(a){"use strict";function l(t,e){this.$element=a(t),this.options=a.extend({},this.defaults(),e),this.render()}l.VERSION="3.6.0",l.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"light",size:"normal",style:"",width:null,height:null},l.prototype.defaults=function(){return{on:this.$element.attr("data-on")||l.DEFAULTS.on,off:this.$element.attr("data-off")||l.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||l.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||l.DEFAULTS.offstyle,size:this.$element.attr("data-size")||l.DEFAULTS.size,style:this.$element.attr("data-style")||l.DEFAULTS.style,width:this.$element.attr("data-width")||l.DEFAULTS.width,height:this.$element.attr("data-height")||l.DEFAULTS.height}},l.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var t="large"===this.options.size||"lg"===this.options.size?"btn-lg":"small"===this.options.size||"sm"===this.options.size?"btn-sm":"mini"===this.options.size||"xs"===this.options.size?"btn-xs":"",e=a('<label for="'+this.$element.prop("id")+'" class="btn">').html(this.options.on).addClass(this._onstyle+" "+t),s=a('<label for="'+this.$element.prop("id")+'" class="btn">').html(this.options.off).addClass(this._offstyle+" "+t),o=a('<span class="toggle-handle btn btn-light">').addClass(t),i=a('<div class="toggle-group">').append(e,s,o),l=a('<div class="toggle btn" data-toggle="toggle" role="button">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(t).addClass(this.options.style);this.$element.wrap(l),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:e,$toggleOff:s,$toggleGroup:i}),this.$toggle.append(i);var n=this.options.width||Math.max(e.outerWidth(),s.outerWidth())+o.outerWidth()/2,h=this.options.height||Math.max(e.outerHeight(),s.outerHeight());e.addClass("toggle-on"),s.addClass("toggle-off"),this.$toggle.css({width:n,height:h}),this.options.height&&(e.css("line-height",e.height()+"px"),s.css("line-height",s.height()+"px")),this.update(!0),this.trigger(!0)},l.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},l.prototype.on=function(t){if(this.$element.prop("disabled"))return!1;this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),t||this.trigger()},l.prototype.off=function(t){if(this.$element.prop("disabled"))return!1;this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),t||this.trigger()},l.prototype.enable=function(){this.$toggle.removeClass("disabled"),this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},l.prototype.disable=function(){this.$toggle.addClass("disabled"),this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},l.prototype.update=function(t){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(t):this.off(t)},l.prototype.trigger=function(t){this.$element.off("change.bs.toggle"),t||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},l.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var t=a.fn.bootstrapToggle;a.fn.bootstrapToggle=function(o){var i=Array.prototype.slice.call(arguments,1)[0];return this.each(function(){var t=a(this),e=t.data("bs.toggle"),s="object"==typeof o&&o;e||t.data("bs.toggle",e=new l(this,s)),"string"==typeof o&&e[o]&&"boolean"==typeof i?e[o](i):"string"==typeof o&&e[o]&&e[o]()})},a.fn.bootstrapToggle.Constructor=l,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=t,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(t){a(this).find("input[type=checkbox]").bootstrapToggle("toggle"),t.preventDefault()})}(jQuery);

View File

@ -161,4 +161,28 @@ body {
} }
.switch:hover{ .switch:hover{
text-decoration: none text-decoration: none
}
.btn-group-label:hover{
color: #007bff;
border-color: #007bff;
background: white;
}
.peer_data_group{
text-align: right;
}
@media (max-width: 768px) {
.peer_data_group{
text-align: left;
}
}
.index-switch{
text-align: right;
}
main{
margin-bottom: 3rem;
} }

68
src/static/tools.js Normal file
View File

@ -0,0 +1,68 @@
$(".ip_dropdown").change(function (){
$(".modal.show .btn").removeAttr("disabled")
});
$(".conf_dropdown").change(function (){
$(".modal.show .ip_dropdown").html('<option value="none" selected="selected" disabled>Loading...')
$.ajax({
url: "/get_ping_ip",
method: "POST",
data: "config="+$(this).children("option:selected").val(),
success: function (res){
$(".modal.show .ip_dropdown").html("")
$(".modal.show .ip_dropdown").append('<option value="none" selected="selected" disabled>Choose an IP')
$(".modal.show .ip_dropdown").append(res)
}
})
});
// Ping Tools
$(".send_ping").click(function (){
$(this).attr("disabled","disabled")
$(this).html("Pinging...")
$("#ping_modal .form-control").attr("disabled","disabled")
$.ajax({
method:"POST",
data: "ip="+$(':selected', $("#ping_modal .ip_dropdown")).val()+"&count="+$("#ping_modal .ping_count").val(),
url: "/ping_ip",
success: function (res){
$(".ping_result tbody").html("")
html = '<tr><th scope="row">Address</th><td>'+res['address']+'</td></tr>' +
'<tr><th scope="row">Is Alive</th><td>'+res['is_alive']+'</td></tr>' +
'<tr><th scope="row">Min RTT</th><td>'+res['min_rtt']+'ms</td></tr>' +
'<tr><th scope="row">Average RTT </th><td>'+res['avg_rtt']+'ms</td></tr>' +
'<tr><th scope="row">Max RTT</th><td>'+res['max_rtt']+'ms</td></tr>' +
'<tr><th scope="row">Package Sent</th><td>'+res['package_sent']+'</td></tr>' +
'<tr><th scope="row">Package Received</th><td>'+res['package_received']+'</td></tr>' +
'<tr><th scope="row">Package Loss</th><td>'+res['package_loss']+'</td></tr>'
$(".ping_result tbody").html(html)
$(".send_ping").removeAttr("disabled")
$(".send_ping").html("Ping")
$("#ping_modal .form-control").removeAttr("disabled")
}
})
});
// Traceroute Tools
$(".send_traceroute").click(function (){
$(this).attr("disabled","disabled")
$(this).html("Tracing...");
$("#traceroute_modal .form-control").attr("disabled","disabled")
$.ajax({
url: "/traceroute_ip",
method: "POST",
data: "ip="+$(':selected', $("#traceroute_modal .ip_dropdown")).val(),
success: function (res){
$(".traceroute_result tbody").html("");
for (i in res){
$(".traceroute_result tbody").append('<tr><th scope="row">'+res[i]['hop']+'</th><td>'+res[i]['ip']+'</td><td>'+res[i]['avg_rtt']+'</td><td>'+res[i]['min_rtt']+'</td><td>'+res[i]['max_rtt']+'</td></tr>')
}
$(".send_traceroute").removeAttr("disabled");
$(".send_traceroute").html("Traceroute");
$("#traceroute_modal .form-control").removeAttr("disabled")
}
})
})

View File

@ -1,14 +1,10 @@
<html> <html>
{% include "header.html" %} {% include "header.html" %}
<body> <body>
{% include "navbar.html" %} {% include "navbar.html" %}
<div class="container-fluid"> <div class="container-fluid">
{% include "sidebar.html" %} {% include "sidebar.html" %}
<div id="config_body"> <div id="config_body"></div>
</div>
</div> </div>
<div class="modal fade" id="add_modal" data-backdrop="static" data-keyboard="false" tabindex="-1" <div class="modal fade" id="add_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true"> aria-labelledby="staticBackdropLabel" aria-hidden="true">
@ -35,6 +31,10 @@
<label for="allowed_ips">Allowed IPs</label> <label for="allowed_ips">Allowed IPs</label>
<input type="text" class="form-control" id="allowed_ips"> <input type="text" class="form-control" id="allowed_ips">
</div> </div>
<div class="form-group">
<label for="allowed_ips">Name</label>
<input type="text" class="form-control" id="new_add_name">
</div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@ -107,12 +107,11 @@
</div> </div>
</div> </div>
</div> </div>
{% include "tools.html" %}
</body> </body>
{% include "footer.html" %} {% include "footer.html" %}
<script> <script>
$(".sb-{{conf_data['name']}}-url").addClass("active"); $(".sb-{{conf_data['name']}}-url").addClass("active");
function load_data(){ function load_data(){
$.ajax({ $.ajax({
method: "GET", method: "GET",
@ -123,6 +122,7 @@
async:false, async:false,
success: function (response){ success: function (response){
$("#config_body").html(response); $("#config_body").html(response);
$("[refresh-interval={{ dashboard_refresh_interval }}]").addClass("active")
} }
}) })
} }
@ -130,8 +130,22 @@
load_data(); load_data();
setInterval(function(){ setInterval(function(){
load_data(); load_data();
}, 15000) }, {{dashboard_refresh_interval}})
}); });
$("body").on("click", ".update_interval", function(){
$.ajax({
method:"POST",
data: "interval="+$(this).attr("refresh-interval"),
url: "/update_dashboard_refresh_interval",
success: function (res){
location.reload()
}
})
});
$("body").on("click", ".refresh", function (){
load_data();
});
</script> </script>
<script> <script>
$("body").on("click", ".switch", function (){ $("body").on("click", ".switch", function (){
@ -149,7 +163,7 @@
headers:{ headers:{
"Content-Type": "application/json" "Content-Type": "application/json"
}, },
data: JSON.stringify({"public_key":$("#public_key").val(), "allowed_ips": $("#allowed_ips").val()}), data: JSON.stringify({"public_key":$("#public_key").val(), "allowed_ips": $("#allowed_ips").val(), "name":$("#new_add_name").val()}),
success: function (response){ success: function (response){
if(response != "true"){ if(response != "true"){
$("#add_peer_alert").html(response+$("#add_peer_alert").html()); $("#add_peer_alert").html(response+$("#add_peer_alert").html());
@ -168,9 +182,6 @@
$("#delete_peer").attr("peer_id", peer_id); $("#delete_peer").attr("peer_id", peer_id);
}) })
$(".btn-delete-peer").click(function(){
});
$("#delete_peer").click(function(){ $("#delete_peer").click(function(){
var peer_id = $(this).attr("peer_id"); var peer_id = $(this).attr("peer_id");
@ -183,7 +194,7 @@
}, },
data: JSON.stringify({"action": "delete", "peer_id": peer_id}), data: JSON.stringify({"action": "delete", "peer_id": peer_id}),
success: function (response){ success: function (response){
if(response != "true"){ if(response !== "true"){
$("#remove_peer_alert").html(response+$("#add_peer_alert").html()); $("#remove_peer_alert").html(response+$("#add_peer_alert").html());
$("#remove_peer_alert").removeClass("d-none"); $("#remove_peer_alert").removeClass("d-none");
} }
@ -222,7 +233,6 @@
}) })
}); });
$("#save_peer_name").click(function (){ $("#save_peer_name").click(function (){
var peer_id = $(this).attr("peer_id"); var peer_id = $(this).attr("peer_id");
$.ajax({ $.ajax({

View File

@ -5,4 +5,5 @@
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js"
integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<script src="{{ url_for('static',filename='tools.js') }}"></script>

View File

@ -1,4 +1,4 @@
<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 mb-4">
<div class="info mt-4"> <div class="info mt-4">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@ -48,9 +48,16 @@
</div> </div>
</div> </div>
<hr> <hr>
<div class="button-div" style="text-align: right;"> <div class="button-div mb-3" style="text-align: right;">
<button type="button" class="btn btn-outline-primary btn-sm mb-3" data-toggle="modal" data-target="#add_modal"> <div class="btn-group" role="group" aria-label="Basic example">
<strong>ADD PEER</strong> <button type="button" class="btn btn-outline-primary btn-sm btn-group-label refresh"><i class="bi bi-arrow-repeat"></i></button>
<button type="button" class="btn btn-outline-primary btn-sm update_interval" refresh-interval="5000">5s</button>
<button type="button" class="btn btn-outline-primary btn-sm update_interval" refresh-interval="10000">10s</button>
<button type="button" class="btn btn-outline-primary btn-sm update_interval" refresh-interval="30000">30s</button>
<button type="button" class="btn btn-outline-primary btn-sm update_interval" refresh-interval="60000">1m</button>
</div>
<button type="button" class="btn btn-outline-primary btn-sm" data-toggle="modal" data-target="#add_modal">
<i class="bi bi-plus-circle-fill"></i> Peer
</button> </button>
</div> </div>
@ -60,7 +67,7 @@
<div class="card mb-3"> <div class="card mb-3">
<div class="card-header"> <div class="card-header">
<div class="row"> <div class="row">
<div class="col"> <div class="col-sm">
<div class="card-header-body "> <div class="card-header-body ">
{% if not i['name']%} {% if not i['name']%}
{{ "Untitled Peer" }} {{ "Untitled Peer" }}
@ -70,7 +77,7 @@
<span class="dot dot-{{i['status']}}"></span> <span class="dot dot-{{i['status']}}"></span>
</div> </div>
</div> </div>
<div class="col" style="text-align: right"> <div class="col-sm peer_data_group">
<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-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> <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>
@ -81,10 +88,6 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
{# <div class="col-sm">#}
{# <small class="text-muted"><strong>STATUS</strong></small>#}
{# <h6 style="text-transform: uppercase;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6>#}
{# </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 class="ml-auto">{{i['id']}}</samp></h6> <h6><samp class="ml-auto">{{i['id']}}</samp></h6>
@ -105,7 +108,6 @@
</div> </div>
<div class="w-100"></div> <div class="w-100"></div>
<div class="col-sm"> <div class="col-sm">
<!-- <small class="text-muted"><strong>ACTION</strong></small> -->
<div class="button-group"> <div class="button-group">
<hr> <hr>
<button type="button" class="btn btn-outline-primary btn-setting-peer btn-control" id="{{i['id']}}" data-toggle="modal"><i class="bi bi-gear-fill"></i></button> <button type="button" class="btn btn-outline-primary btn-setting-peer btn-control" id="{{i['id']}}" data-toggle="modal"><i class="bi bi-gear-fill"></i></button>

View File

@ -5,9 +5,6 @@
<link rel="icon" href="{{ url_for('static',filename='logo.png') }}"/> <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" 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 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"> <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> </head>

View File

@ -4,7 +4,7 @@
{% include "navbar.html" %} {% include "navbar.html" %}
<div class="container-fluid"> <div class="container-fluid">
{% include "sidebar.html" %} {% include "sidebar.html" %}
<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 mb-4">
<h1 class="pb-4 mt-4">Home</h1> <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">
@ -24,7 +24,7 @@
<small class="text-muted"><strong>PUBLIC KEY</strong></small> <small class="text-muted"><strong>PUBLIC KEY</strong></small>
<h6 style="text-transform: uppercase; margin:0 !important;"><samp>{{i['public_key']}}</samp></h6> <h6 style="text-transform: uppercase; margin:0 !important;"><samp>{{i['public_key']}}</samp></h6>
</div> </div>
<div class="col-md"> <div class="col-md index-switch">
{% if i['checked'] == "checked" %} {% if i['checked'] == "checked" %}
<a href="#" id="{{i['conf']}}" {{i['checked']}} class="switch text-primary tt"><i class="bi bi-toggle2-on"></i></a> <a href="#" id="{{i['conf']}}" {{i['checked']}} class="switch text-primary tt"><i class="bi bi-toggle2-on"></i></a>
{% else %} {% else %}
@ -40,6 +40,7 @@
{%endfor%} {%endfor%}
</main> </main>
</div> </div>
{% include "tools.html" %}
</body> </body>
{% include "footer.html" %} {% include "footer.html" %}
<script> <script>

View File

@ -26,7 +26,9 @@
<form action="/update_wg_conf_path" method="post" class="update_wg_conf_path"> <form action="/update_wg_conf_path" method="post" class="update_wg_conf_path">
<div class="form-group"> <div class="form-group">
<label for="username">Path</label> <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 }}"> <input type="text" class="form-control mb-4" id="wg_conf_path" name="wg_conf_path" value="{{ wg_conf_path }}">
<p>Remember to remove <code>/</code> at the end of your path. e.g <code>/etc/wireguard</code></p>
<button class="btn btn-danger change_path">Update Path & Restart Dashboard</button> <button class="btn btn-danger change_path">Update Path & Restart Dashboard</button>
</div> </div>
</form> </form>

View File

@ -8,7 +8,9 @@
<li class="nav-item"><a class="nav-link sb-settings-url" href="/settings">Settings</a></li> <li class="nav-item"><a class="nav-link sb-settings-url" href="/settings">Settings</a></li>
{% endif %} {% endif %}
{% if session['update'] == "true" %} {% 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> <li class="nav-item sb-update-li">
<a class="nav-link sb-update-url" data-toggle="modal" data-target="#update_modal" href="#">New Update Available!<span class="dot dot-running"></span></a>
</li>
{% endif %} {% endif %}
</ul> </ul>
<hr> <hr>
@ -21,6 +23,16 @@
{%endfor%} {%endfor%}
</ul> </ul>
<hr> <hr>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Tools</span>
</h6>
<ul class="nav flex-column">
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" data-toggle="modal" data-target="#ping_modal" href="#">Ping</a></li>
<li class="nav-item"><a class="nav-link" data-toggle="modal" data-target="#traceroute_modal" href="#">Traceroute</a></li>
</ul>
</ul>
<hr>
{% if "username" in session %} {% if "username" in session %}
<ul class="nav flex-column"> <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> <li class="nav-item"><a class="nav-link text-danger" href="/signout" style="font-weight: bold">Sign Out</a></li>

View File

@ -29,10 +29,6 @@
<label for="password" class="text-left">Password</label> <label for="password" class="text-left">Password</label>
<input type="password" class="form-control" id="password" name="password" required> <input type="password" class="form-control" id="password" name="password" required>
</div> </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> <button type="submit" class="btn btn-dark" style="width: 100%;">Sign In</button>
</form> </form>
</div> </div>

121
src/templates/tools.html Normal file
View File

@ -0,0 +1,121 @@
<div class="modal fade" id="ping_modal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Ping</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm">
<div class="mb-3">
<small>Configuration</small>
<select class="form-control mt-2 conf_dropdown">
<option value="none" selected="selected" disabled>Select Configuration</option>
{% for i in conf%}
<option value="{{ i['conf'] }}">{{ i['conf'] }}</option>
{%endfor%}
</select>
</div>
</div>
<div class="col-sm">
<div class="mb-3">
<small>IP</small>
<select class="form-control mt-2 ip_dropdown">
<option value="none" selected="selected" disabled>Choose an IP</option>
</select>
</div>
</div>
<div class="col-sm">
<div class="mb-3">
<small>Ping Count</small>
<input type="number" class="form-control mt-2 ping_count" min=1 value=4>
</div>
</div>
</div>
<hr>
<div class="ping_result">
<table class="table">
<tbody></tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary send_ping" disabled>Ping</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="traceroute_modal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Traceroute</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm">
<div class="mb-3">
<small>Configuration</small>
<select class="form-control mt-2 conf_dropdown">
<option value="none" selected="selected" disabled>Select Configuration</option>
{% for i in conf%}
<option value="{{ i['conf'] }}">{{ i['conf'] }}</option>
{%endfor%}
</select>
</div>
</div>
<div class="col-sm">
<div class="mb-3">
<small>IP</small>
<select class="form-control mt-2 ip_dropdown">
<option value="none" selected="selected" disabled>Choose an IP</option>
</select>
</div>
</div>
</div>
<button class="btn btn-primary send_traceroute" disabled>Traceroute</button>
<hr>
<div class="traceroute_result">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Hop</th>
<th scope="col">IP</th>
<th scope="col">Avg RTT</th>
<th scope="col">Min RTT</th>
<th scope="col">Max RTT</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="update_modal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">How to update dashboard</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<pre><code>$ sudo sh wgd.sh stop</code><br><code>$ sudo sh wgd.sh update</code><br><code>$ sudo sh wgd.sh start</code></pre>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

View File

@ -4,7 +4,7 @@ app_name="dashboard.py"
dashes='------------------------------------------------------------' dashes='------------------------------------------------------------'
help () { help () {
printf "<Wireguard Dashboard> by Donald Zou - https://github.com/donaldzou \n" printf "<Wireguard Dashboard> by Donald Zou - https://github.com/donaldzou \n"
printf "Usage: sh wg-dashboard.sh <option>" printf "Usage: sh wgd.sh <option>"
printf "\n \n" printf "\n \n"
printf "Available options: \n" printf "Available options: \n"
printf " start: To start Wireguard Dashboard.\n" printf " start: To start Wireguard Dashboard.\n"
@ -25,6 +25,7 @@ check_wgd_status(){
} }
start_wgd () { start_wgd () {
printf "%s" "$PLATFORM"
printf "Starting Wireguard Dashboard in the background. \n" printf "Starting Wireguard Dashboard in the background. \n"
if [ ! -d "log" ] if [ ! -d "log" ]
then mkdir "log" then mkdir "log"
@ -44,27 +45,21 @@ start_wgd_debug() {
} }
update_wgd() { 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'])") 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 "%s\n" "$dashes"
printf "Are you sure you want to update to the %s? (Y/N): " "$new_ver" printf "Are you sure you want to update to the %s? (Y/N): " "$new_ver"
read up read up
if [ "$up" = "Y" ]; then if [ "$up" = "Y" ]; then
printf "%s\n" "$dashes"
printf "| Shutting down Wireguard Dashboard... |\n" printf "| Shutting down Wireguard Dashboard... |\n"
printf "%s\n" "$dashes"
printf "| Downloading %s from GitHub... |\n" "$new_ver" 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 git pull https://github.com/donaldzou/wireguard-dashboard.git $new_ver --force > /dev/null 2>&1
printf "| Installing all required python package |\n"
python3 -m pip install -r requirements.txt
printf "| Update Successfully! |\n" printf "| Update Successfully! |\n"
printf "%s\n" "$dashes"
printf "| Now you can start the dashboard with >> sh wgd.sh start |\n" printf "| Now you can start the dashboard with >> sh wgd.sh start |\n"
printf "%s\n" "$dashes"
exit 1 exit 1
else else
printf "%s\n" "$dashes" printf "Cancel update. \n"
printf "CANCEL update. \n"
printf "%s\n" "$dashes"
fi fi
} }