Compare commits

...

11 Commits
v1.0 ... v1.1.1

Author SHA1 Message Date
Donald Cheng Hong Zou
7e770b9b5d v20210402.2 2021-04-02 22:22:41 -04:00
Donald Zou
b95f391f02 Update README.md 2021-04-02 21:15:50 -04:00
Donald Zou
4ed861ffce Update README.md 2021-04-02 21:15:28 -04:00
Donald Zou
2c0b3be900 Update README.md 2021-04-02 21:14:51 -04:00
Donald Cheng Hong Zou
145e163670 Merge branch 'main' of https://github.com/donaldzou/Wireguard-Dashboard into main 2021-04-02 21:14:06 -04:00
Donald Cheng Hong Zou
d5497843eb v20210403.2 2021-04-02 21:12:46 -04:00
Donald Zou
3799f6fe49 Update README.md 2021-04-02 20:49:41 -04:00
Donald Cheng Hong Zou
07678300da v20210402
Added more features
2021-04-02 20:48:00 -04:00
Donald Zou
881c8f30ef Update README.md 2021-03-24 02:07:01 -04:00
Donald Zou
5d02fbbdea Update README.md 2021-01-12 13:36:13 -05:00
donaldzou
4c969f5524 Update 2020-12-27 00:02:36 -05:00
14 changed files with 389 additions and 146 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,7 @@
.vscode/sftp.json .vscode/sftp.json
src/.vscode/sftp.json src/.vscode/sftp.json
.DS_Store .DS_Store
wg.db
*.json
.idea
src/test.py

View File

@@ -1,20 +1,49 @@
# Wireguard Dashboard # Wireguard Dashboard
## Intro 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.
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.
## Installation ## 💡Features
**Requirement:**
- Add peers in configuration
- Manage peer names
- Delete peers
- 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. - Ubuntu 18.04.1 LTS, other OS might work, but haven't test yet.
- Wireguard - **Wireguard**
- Configuration files under **/etc/wireguard** - Configuration files under **/etc/wireguard**
- Python 3.7 - Python 3.7
- Flask
**Install:** ## 🛠Install
1. `git clone https://github.com/donaldzou/Wireguard-Dashboard.git`
2. `cd Wireguard-Dashboard\src`
3. `python3 dashboard.py`
4. Access your server! e.g (http://your_server_ip:10086)
## Example **1. Install Python Dependencies**
```
$ python3 -m pip install flask tinydb
```
**2. Install Wireguard Dashboard**
```
$ 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
$ export FLASK_APP=dashboard.py
$ export FLASK_RUN_HOST=0.0.0.0
$ export FLASK_ENV=development
$ export FLASK_DEBUG=0
$ flask run
```
## 🔍Example
![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) ![Conf Image](https://github.com/donaldzou/Wireguard-Dashboard/raw/main/src/static/configuration.png)

2
requirements.txt Normal file
View File

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

View File

@@ -2,19 +2,14 @@ import os
from flask import Flask, request, render_template, redirect, url_for from flask import Flask, request, render_template, redirect, url_for
import subprocess import subprocess
from datetime import datetime, date, time, timedelta from datetime import datetime, date, time, timedelta
from operator import itemgetter
from tinydb import TinyDB, Query from tinydb import TinyDB, Query
import time
import requests
conf_location = "/etc/wireguard" conf_location = "/etc/wireguard"
app = Flask("Wireguard Dashboard") app = Flask("Wireguard Dashboard")
app.config['TEMPLATES_AUTO_RELOAD'] = True app.config['TEMPLATES_AUTO_RELOAD'] = True
css = "" 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: peer_key = subprocess.check_output("wg show "+config_name+" peers", shell=True)
@@ -41,12 +36,31 @@ def get_conf_running_peer_number(config_name):
return running return running
def get_conf_peers_data(config_name): def get_conf_peers_data(config_name):
db = TinyDB('db/'+config_name+'.json')
peers = Query()
peer_data = {} peer_data = {}
# Get key # Get key
try: peer_key = subprocess.check_output("wg show "+config_name+" peers", shell=True) try: peer_key = subprocess.check_output("wg show "+config_name+" peers", shell=True)
except Exception: return "stopped" 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: peer_data[i] = {} for i in peer_key:
peer_data[i] = {}
if not db.search(peers.id == i):
db.insert({
"id": i,
"name": "",
"total_receive": 0,
"total_sent": 0,
"total_data": 0,
"endpoint": 0,
"status": 0,
"latest_handshake": 0,
"allowed_ip": 0,
"traffic": []
})
#Get transfer #Get transfer
try: 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)
@@ -57,7 +71,11 @@ def get_conf_peers_data(config_name):
download_total = 0 download_total = 0
total = 0 total = 0
for i in range(int(len(data_usage)/3)): for i in range(int(len(data_usage)/3)):
peer_data[data_usage[count]]['total_recive'] = round(int(data_usage[count+1])/(1024**3),4) db.update({"total_receive": round(int(data_usage[count+1])/(1024**3),4),
"total_sent": round(int(data_usage[count+2])/(1024**3),4),
"total_data": round((int(data_usage[count+2])+int(data_usage[count+1]))/(1024**3),4)}, 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_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) 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
@@ -68,6 +86,8 @@ def get_conf_peers_data(config_name):
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])
peer_data[data_usage[count]]['endpoint'] = data_usage[count+1] peer_data[data_usage[count]]['endpoint'] = data_usage[count+1]
count += 2 count += 2
@@ -80,11 +100,16 @@ def get_conf_peers_data(config_name):
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]))
status = ""
if minus < b: if minus < b:
peer_data[data_usage[count]]['status'] = "running" peer_data[data_usage[count]]['status'] = "running"
status = "running"
else: else:
peer_data[data_usage[count]]['status'] = "stopped" peer_data[data_usage[count]]['status'] = "stopped"
peer_data[data_usage[count]]['latest_handshake'] = minus 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]
count += 2 count += 2
#Get allowed ip #Get allowed ip
@@ -93,27 +118,30 @@ def get_conf_peers_data(config_name):
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({"allowed_ip": data_usage[count+1]}, peers.id == data_usage[count])
peer_data[data_usage[count]]['allowed_ip'] = data_usage[count+1] peer_data[data_usage[count]]['allowed_ip'] = data_usage[count+1]
count += 2 count += 2
return peer_data result = db.all()
result = sorted(result, key=lambda d: d['status'])
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) try: pub_key = subprocess.check_output("wg show "+config_name+" public-key", shell=True, stderr=subprocess.STDOUT)
except Exception: return "stopped" except Exception: return "stopped"
return pub_key.decode("UTF-8") return pub_key.decode("UTF-8")
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) try: pub_key = subprocess.check_output("wg show "+config_name+" listen-port", shell=True, stderr=subprocess.STDOUT)
except Exception: return "stopped" except Exception: return "stopped"
return pub_key.decode("UTF-8") return pub_key.decode("UTF-8")
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) try: data_usage = subprocess.check_output("wg show "+config_name+" transfer", shell=True, stderr=subprocess.STDOUT)
except Exception: return "stopped" except Exception: return "stopped"
data_usage = data_usage.decode("UTF-8").split() data_usage = data_usage.decode("UTF-8").split()
count = 0 count = 0
@@ -133,7 +161,7 @@ def get_conf_total_data(config_name):
def get_conf_status(config_name): def get_conf_status(config_name):
try: status = subprocess.check_output("wg show "+config_name, shell=True) try: status = subprocess.check_output("wg show "+config_name, shell=True, stderr=subprocess.STDOUT)
except Exception: return "stopped" except Exception: return "stopped"
else: return "running" else: return "running"
@@ -148,6 +176,7 @@ def get_conf_list():
temp['checked'] = 'checked' temp['checked'] = 'checked'
else: temp['checked'] = "" else: temp['checked'] = ""
conf.append(temp) conf.append(temp)
conf = sorted(conf, key=itemgetter('status'))
return conf return conf
@app.route('/',methods=['GET']) @app.route('/',methods=['GET'])
@@ -157,6 +186,21 @@ def index():
@app.route('/configuration/<config_name>', methods=['GET']) @app.route('/configuration/<config_name>', methods=['GET'])
def conf(config_name): def conf(config_name):
conf_data = {
"name": config_name,
"status": get_conf_status(config_name),
"checked": ""
}
if conf_data['status'] == "stopped":
return redirect('/')
else:
conf_data['checked'] = "checked"
return render_template('configuration.html', conf=get_conf_list(), conf_data=conf_data)
@app.route('/get_config/<config_name>', methods=['GET'])
def get_conf(config_name):
db = TinyDB('db/'+config_name+'.json')
conf_data = { conf_data = {
"name": config_name, "name": config_name,
"status": get_conf_status(config_name), "status": get_conf_status(config_name),
@@ -165,14 +209,12 @@ def conf(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), "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),
"checked": ""
} }
if conf_data['status'] == "stopped": if conf_data['status'] == "stopped":
print(conf_data)
return redirect('/') return redirect('/')
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('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'])
@@ -208,6 +250,8 @@ def add_peer(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")
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)
@@ -217,8 +261,35 @@ def remove_peer(config_name):
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, 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)
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'])
def save_peer_name(config_name):
data = request.get_json()
id = data['id']
name = data['name']
db = TinyDB("db/"+config_name+".json")
peers = Query()
db.update({"name": name}, peers.id == id)
return id + " " + name
@app.route('/get_peer_name/<config_name>', methods=['POST'])
def get_peer_name(config_name):
data = request.get_json()
id = data['id']
db = TinyDB("db/"+config_name+".json")
peers = Query()
result = db.search(peers.id == id)
return result[0]['name']
# db.update({"name": name}, peers.id == id)
app.run(host='0.0.0.0',debug=False, port=10086) app.run(host='0.0.0.0',debug=False, port=10086)

1
src/db/hi.txt Normal file
View File

@@ -0,0 +1 @@
You can delete this later ;)

1
src/static/bootstrap4-toggle.min.js vendored Normal file
View File

@@ -0,0 +1 @@
!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);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 455 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -122,3 +122,21 @@ body {
.info h6{ .info h6{
line-break: anywhere; line-break: anywhere;
} }
.btn-control{
border: none !important;
padding: 0;
padding-right: 0.5rem;
}
.btn-control:hover{
background: white;
}
.btn-delete-peer:hover{
color: #dc3545;
}
.btn-setting-peer:hover{
color:#007bff
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 50 KiB

BIN
src/static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -8,11 +8,12 @@
<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://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 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> </head>
<body> <body>
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow"> <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> <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" <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"> data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
@@ -38,110 +39,10 @@
</nav> </nav>
</div> </div>
</div> </div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4"> <div id="config_body">
<div class="info mt-4">
<div class="row">
<div class="col-sm">
<small class="text-muted"><strong>CONFIGURATION</strong></small>
<h1 class="mb-3">{{conf_data['name']}}</h1>
</div>
<div class="col-sm">
<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">
</div>
<div class="w-100"></div>
<div class="col-sm">
<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>
</div>
<div class="col-sm">
<small class="text-muted"><strong>CONNECTED PEERS</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['running_peer']}}</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL DATA USAGE</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][0]}} GB</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL RECIEVED</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][1]}} GB</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL SENT</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][2]}} GB</h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
<small class="text-muted"><strong>PUBLIC KEY</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{conf_data['public_key']}}</samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>LISTEN PORT</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{conf_data['listen_port']}}</samp></h6>
</div>
</div>
<hr>
<div class="button-div" style="text-align: right;">
<button type="button" class="btn btn-outline-primary btn-sm mb-3" data-toggle="modal" data-target="#add_modal">
<strong>ADD PEER</strong>
</button>
</div> </div>
</div> </div>
{% for i in conf_data['peer_data']%}
<div class="card mb-3">
<div class="card-body">
<div class="row">
<div class="col-sm">
<small class="text-muted"><strong>STATUS</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['peer_data'][i]['status']}}<span class="dot dot-{{conf_data['peer_data'][i]['status']}}"></span></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>PEER</strong></small>
<h6><samp>{{i}}</samp></h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
<small class="text-muted"><strong>ALLOWED IP</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['peer_data'][i]['allowed_ip']}}</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>LATEST HANDSHAKE</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['peer_data'][i]['latest_handshake']}}</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>END POINT</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['peer_data'][i]['endpoint']}}</h6>
</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;">{{conf_data['peer_data'][i]['total_data']}} GB</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL RECIEVED</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['peer_data'][i]['total_recive']}} GB</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL SENT</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['peer_data'][i]['total_sent']}} GB</h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
<!-- <small class="text-muted"><strong>ACTION</strong></small> -->
<div class="button-group">
<button type="button" class="btn btn-outline-danger btn-sm btn-delete-peer" id="{{i}}" data-toggle="modal" data-target="#delete_modal"><span class="material-icons">delete_forever</span></button>
</div>
</div>
</div>
</div>
</div>
{%endfor%}
</main>
</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">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
@@ -201,6 +102,44 @@
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" id="setting_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true" conf_id={{conf_data['name']}} peer_id="">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="peer_name"></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="mb-3">
<label for="peer_name" class="form-label">Name</label>
<input type="text" class="form-control" id="peer_name_textbox" placeholder="">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="save_peer_name" conf_id={{conf_data['name']}} peer_id="">Save</button>
</div>
</div>
</div>
</div>
<div class="position-fixed top-0 right-0 p-3" style="z-index: 5; right: 0; top: 50px;">
<div id="alertToast" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true" data-delay="5000">
<div class="toast-header">
<strong class="mr-auto">Wireguard Dashboard</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="toast-body">
</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> <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" <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
@@ -209,7 +148,32 @@
<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="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/js/bootstrap4-toggle.min.js"></script> <script>
function load_data(){
$.ajax({
method: "GET",
url: "/get_config/"+"{{conf_data['name']}}",
headers:{
"Content-Type": "application/json"
},
async:false,
success: function (response){
$("#config_body").html(response);
$.ajax({
url: "{{ url_for('static',filename='bootstrap4-toggle.min.js') }}",
dataType: "script",
cache: true
});
}
})
}
$(document).ready(function(){
load_data();
setInterval(function(){
load_data();
}, 10000)
});
</script>
<script> <script>
$('.switch').change(function() { $('.switch').change(function() {
if ($(this).prop('checked') == false){ if ($(this).prop('checked') == false){
@@ -221,7 +185,6 @@
location.replace("/switch/"+$(this).attr('id')); 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')
@@ -246,9 +209,13 @@
} }
}) })
$(".btn-delete-peer").click(function(){ $("body").on("click", ".btn-delete-peer", function(){
var peer_id = $(this).attr("id"); var peer_id = $(this).attr("id");
$("#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(){
@@ -275,8 +242,49 @@
// setInterval(function(){ var myModal = new bootstrap.Modal(document.getElementById('setting_modal'), {
// location.reload(); keyboard: false
// }, 10000) })
$("body").on("click", ".btn-setting-peer", function(){
myModal.toggle();
var peer_id = $(this).attr("id");
$("#save_peer_name").attr("peer_id", peer_id);
$.ajax({
method: "POST",
url: "/get_peer_name/"+$("#setting_modal").attr("conf_id"),
headers:{
"Content-Type": "application/json"
},
data: JSON.stringify({"id": peer_id}),
success: function(response){
if (response == ""){
$("#setting_modal .peer_name").html("Untitled Peer");
$("#peer_name_textbox").val("")
}else{
$("#setting_modal .peer_name").html(response);
$("#peer_name_textbox").val(response)
}
}
})
});
$("#save_peer_name").click(function (){
var peer_id = $(this).attr("peer_id");
$.ajax({
method: "POST",
url: "/save_peer_name/"+"{{conf_data['name']}}",
headers:{
"Content-Type": "application/json"
},
data: JSON.stringify({id: peer_id, name: $("#peer_name_textbox").val()}),
success: function (response){
myModal.toggle();
load_data();
$('#alertToast').toast('show');
$('#alertToast .toast-body').html("Name Saved!");
}
})
})
</script> </script>
</html> </html>

112
src/templates/get_conf.html Normal file
View File

@@ -0,0 +1,112 @@
<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="row">
<div class="col-sm">
<small class="text-muted"><strong>CONFIGURATION</strong></small>
<h1 class="mb-3">{{conf_data['name']}}</h1>
</div>
<div class="col-sm">
<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">
</div>
<div class="w-100"></div>
<div class="col-sm">
<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>
</div>
<div class="col-sm">
<small class="text-muted"><strong>CONNECTED PEERS</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['running_peer']}}</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL DATA USAGE</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][0]}} GB</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL RECIEVED</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][1]}} GB</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL SENT</strong></small>
<h6 style="text-transform: uppercase;">{{conf_data['total_data_usage'][2]}} GB</h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
<small class="text-muted"><strong>PUBLIC KEY</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{conf_data['public_key']}}</samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>LISTEN PORT</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{conf_data['listen_port']}}</samp></h6>
</div>
</div>
<hr>
<div class="button-div" style="text-align: right;">
<button type="button" class="btn btn-outline-primary btn-sm mb-3" data-toggle="modal" data-target="#add_modal">
<strong>ADD PEER</strong>
</button>
</div>
</div>
{% for i in conf_data['peer_data']%}
<div class="card mb-3">
<div class="card-header">
{% if not i['name']%}
{{ "Untitled Peer" }}
{% else %}
{{i['name']}}
{% endif %}
</div>
<div class="card-body">
<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">
<small class="text-muted"><strong>PEER</strong></small>
<h6><samp>{{i['id']}}</samp></h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
<small class="text-muted"><strong>ALLOWED IP</strong></small>
<h6 style="text-transform: uppercase;">{{i['allowed_ip']}}</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>LATEST HANDSHAKE</strong></small>
<h6 style="text-transform: uppercase;">{{i['latest_handshake']}}</h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>END POINT</strong></small>
<h6 style="text-transform: uppercase;">{{i['endpoint']}}</h6>
</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">
<!-- <small class="text-muted"><strong>ACTION</strong></small> -->
<div class="button-group">
<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-danger btn-delete-peer btn-control" id="{{i['id']}}" data-toggle="modal" data-target="#delete_modal"><i class="bi bi-x-circle-fill"></i></button>
</div>
</div>
</div>
</div>
</div>
{%endfor%}
</main>

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Wireguard Dashboard</title> <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" 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://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">

View File

@@ -1,4 +0,0 @@
from tinydb import TinyDB, Query
conf_db = TinyDB("json/conf.json")
print(conf_db.all())