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

v2.2beta almost done!!!!!

This commit is contained in:
Donald Cheng Hong Zou 2021-08-14 23:30:05 -04:00
parent 0d380672f3
commit 9d476af384
9 changed files with 128 additions and 59 deletions

View File

@ -20,15 +20,26 @@
## 📣 What's New: Version v2.2
- 🎉 **New Features**
- **Add new peers**: Now you can add peers directly on dashboard, it will generate a pair of private key and public key. You can also set its DNS, endpoint allowed IPs. Both can set a default value in the setting page. [❤️ in [#44](https://github.com/donaldzou/wireguard-dashboard/issues/44)]
- **QR Code:** You can add the private key in peer setting of your existed peer to create a QR code. Or just create a new one, dashboard will now be able to auto generate a private key and public key ;) Don't worry, all keys will be generated on your machine, and **will delete all key files after they got generated**. [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)]
- **Peer configuration file download:** Same as QR code, you now can download the peer configuration file, so you don't need to manually input all the details on the peer machine! [❤️ in [#40](https://github.com/donaldzou/wireguard-dashboard/issues/40)]
- **Search peers**: You can now search peers by their name.
- **Autostart on boot:** Added a tutorial on how to start the dashboard to on boot! Please read the [tutorial below](#autostart-wireguard-dashboard-on-boot). [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)]
- **Click to copy**: You can now click and copy all peer's public key and configuration's public key.
- And many more...
- 🪚 **Bug Fixed**
- When there are comments in the wireguard config file, will cause the dashboard to crash.
- Used regex to search for config files.
- **🧐 Other Changes**
- Moved all external CSS and JavaScript file to local hosting (Except Bootstrap Icon, due to large amount of SVG files).
- Updated `Flask` from`v1.1.2` to `v2.0.1`, and `Jinja` from `v2.10.1` to `v3.0.1`
- Updated Python dependencies
- Flask: `v1.1.2 => v2.0.1`
- Jinja: `v2.10.1 => v3.0.1`
- icmplib: `v2.1.1 => v3.0.1`
- Updated CSS/JS dependencies
- Bootstrap: `v4.5.3 => v4.6.0`
- UI adjustment
- Adjusted how peers will display in larger screens, used to be 1 row per peer, now is 3 peers in 1 row.
<hr>

View File

@ -66,6 +66,23 @@ def is_match(regex, text):
return pattern.search(text) is not None
def read_conf_file_interface(config_name):
conf_location = wg_conf_path + "/" + config_name + ".conf"
f = open(conf_location, 'r')
file = f.read().split("\n")
data = {}
peers_start = 0
for i in range(len(file)):
if not is_match("#(.*)", file[i]):
if len(file[i]) > 0:
if file[i] != "[Interface]":
tmp = re.split(r'\s*=\s*', file[i], 1)
if len(tmp) == 2:
data[tmp[0]] = tmp[1]
f.close()
return data
def read_conf_file(config_name):
# Read Configuration File Start
conf_location = wg_conf_path + "/" + config_name + ".conf"
@ -195,7 +212,7 @@ def get_all_peers_data(config_name):
"id": i['PublicKey'],
"private_key": "",
"DNS": "1.1.1.1",
"endpoint_allowed_ip":"0.0.0.0/0",
"endpoint_allowed_ip": "0.0.0.0/0",
"name": "",
"total_receive": 0,
"total_sent": 0,
@ -357,14 +374,23 @@ def checkAllowedIP(public_key, ip, config_name):
else:
return {'status': 'success'}
def checkIp(ip):
return is_match("((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}",ip)
return is_match("((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}", ip)
def cleanIp(ip):
return ip.replace(' ','')
return ip.replace(' ', '')
def cleanIpWithRange(ip):
return cleanIp(ip).split(',')
def checkIpWithRange(ip):
return is_match("((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|\/)){4}(0|8|16|24|32)(,|$)", ip)
def checkAllowedIPs(ip):
ip = cleanIpWithRange(ip)
for i in ip:
@ -372,8 +398,6 @@ def checkAllowedIPs(ip):
return True
@app.before_request
def auth_req():
conf = configparser.ConfigParser(strict=False)
@ -431,8 +455,8 @@ def settings():
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"),
peer_global_DNS=config.get("Peers","peer_global_DNS"),
peer_endpoint_allowed_ip=config.get("Peers","peer_endpoint_allowed_ip"))
peer_global_DNS=config.get("Peers", "peer_global_DNS"),
peer_endpoint_allowed_ip=config.get("Peers", "peer_endpoint_allowed_ip"))
@app.route('/auth', methods=['POST'])
@ -473,6 +497,7 @@ def update_acct():
config.clear()
return redirect(url_for("settings"))
@app.route('/update_peer_default_config', methods=['POST'])
def update_peer_default_config():
config = configparser.ConfigParser(strict=False)
@ -492,13 +517,11 @@ def update_peer_default_config():
# Check Endpoint Allowed IPs
ip = request.form['peer_endpoint_allowed_ip']
if not checkAllowedIPs(ip):
session['message'] = "Peer Endpoint Allowed IPs Format Incorrect. Example: 192.168.1.1/32 or 192.168.1.1/32,192.168.1.2/32"
session[
'message'] = "Peer Endpoint Allowed IPs Format Incorrect. Example: 192.168.1.1/32 or 192.168.1.1/32,192.168.1.2/32"
session['message_status'] = "danger"
return redirect(url_for("settings"))
config.set("Peers", "peer_endpoint_allowed_ip", ','.join(cleanIpWithRange(ip)))
config.set("Peers", "peer_global_DNS", request.form['peer_global_DNS'])
try:
@ -679,6 +702,8 @@ def conf(config_name):
@app.route('/get_config/<config_name>', methods=['GET'])
def get_conf(config_name):
config_interface = read_conf_file_interface(config_name)
search = request.args.get('search')
if len(search) == 0: search = ""
search = urllib.parse.unquote(search)
@ -693,12 +718,14 @@ def get_conf(config_name):
"public_key": get_conf_pub_key(config_name),
"listen_port": get_conf_listen_port(config_name),
"running_peer": get_conf_running_peer_number(config_name),
"conf_address": config_interface['Address']
}
if conf_data['status'] == "stopped":
conf_data['checked'] = "nope"
else:
conf_data['checked'] = "checked"
return render_template('get_conf.html', conf_data=conf_data, wg_ip=wg_ip, sort_tag=sort, dashboard_refresh_interval=int(config.get("Server", "dashboard_refresh_interval")))
return render_template('get_conf.html', conf_data=conf_data, wg_ip=wg_ip, sort_tag=sort,
dashboard_refresh_interval=int(config.get("Server", "dashboard_refresh_interval")))
@app.route('/switch/<config_name>', methods=['GET'])
@ -731,6 +758,9 @@ def add_peer(config_name):
endpoint_allowed_ip = data['endpoint_allowed_ip']
DNS = data['DNS']
keys = get_conf_peer_key(config_name)
if len(public_key) == 0 or len(DNS) == 0 or len(allowed_ips) == 0 or len(endpoint_allowed_ip) == 0:
return "Please fill in all required box."
if type(keys) != list:
return config_name + " is not running."
if public_key in keys:
@ -750,7 +780,8 @@ def add_peer(config_name):
stderr=subprocess.STDOUT)
status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT)
get_all_peers_data(config_name)
db.update({"name": data['name'], "private_key": data['private_key'], "DNS": data['DNS'], "endpoint_allowed_ip": endpoint_allowed_ip},
db.update({"name": data['name'], "private_key": data['private_key'], "DNS": data['DNS'],
"endpoint_allowed_ip": endpoint_allowed_ip},
peers.id == public_key)
db.close()
return "true"
@ -804,7 +835,6 @@ def save_peer_setting(config_name):
return jsonify(check_key)
if check_ip['status'] == "failed":
return jsonify(check_ip)
try:
if allowed_ip == "":
allowed_ip = '""'
@ -815,16 +845,13 @@ def save_peer_setting(config_name):
if change_ip.decode("UTF-8") != "":
return jsonify({"status": "failed", "msg": change_ip.decode("UTF-8")})
db.update({"name": name, "private_key": private_key, "DNS": DNS, "endpoint_allowed_ip":endpoint_allowed_ip}, peers.id == id)
db.update(
{"name": name, "private_key": private_key, "DNS": DNS, "endpoint_allowed_ip": endpoint_allowed_ip},
peers.id == id)
db.close()
return jsonify({"status": "success", "msg": ""})
except subprocess.CalledProcessError as exc:
return jsonify({"status": "failed", "msg": str(exc.output.decode("UTF-8").strip())})
else:
return jsonify({"status": "failed", "msg": "This peer does not exist."})
@ -881,7 +908,6 @@ def download(config_name):
private_key = peer['private_key']
allowed_ip = peer['allowed_ip']
DNS = peer['DNS']
filename = peer['name']
if len(filename) == 0:
filename = "Untitled_Peers"
@ -922,7 +948,6 @@ def init_dashboard():
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']:
@ -939,7 +964,6 @@ def init_dashboard():
config['Server']['dashboard_refresh_interval'] = '15000'
if 'dashboard_sort' not in config['Server']:
config['Server']['dashboard_sort'] = 'status'
if "Peers" not in config:
config['Peers'] = {}
if 'peer_global_DNS' not in config['Peers']:

View File

@ -1,5 +1,5 @@
Flask==2.0.1
tinydb==4.3.0
ifcfg==0.21
icmplib==2.1.1
icmplib==3.0.1
flask-qrcode==3.0.0

View File

@ -58,8 +58,17 @@ $("#re_generate_key").click(function (){
})
$("#save_peer").click(function(){
if ($("#allowed_ips") !== "" && $("#public_key") !== ""){
$(this).attr("disabled","disabled")
$(this).html("Saving...")
if ($("#allowed_ips").val() !== "" && $("#public_key").val() !== "" && $("#new_add_DNS").val() !== "" && $("#new_add_endpoint_allowed_ip").val() != ""){
var conf = $(this).attr('conf_id')
var data_list = [$("#private_key"), $("#allowed_ips"), $("#new_add_name"), $("#new_add_DNS"), $("#new_add_endpoint_allowed_ip")]
for (var i = 0; i < data_list.length; i++){
data_list[i].attr("disabled", "disabled")
}
$.ajax({
method: "POST",
url: "/add_peer/"+conf,
@ -84,6 +93,9 @@ $("#save_peer").click(function(){
}
}
})
}else{
$("#add_peer_alert").html("Please fill in all required box.");
$("#add_peer_alert").removeClass("d-none");
}
})
var qrcodeModal = new bootstrap.Modal(document.getElementById('qrcode_modal'), {
@ -108,6 +120,8 @@ $("body").on("click", ".btn-delete-peer", function(){
})
$("#delete_peer").click(function(){
$(this).attr("disabled","disabled")
$(this).html("Deleting...")
var peer_id = $(this).attr("peer_id");
var config = $(this).attr("conf_id");
$.ajax({
@ -127,6 +141,8 @@ $("#delete_peer").click(function(){
load_data($('#search_peer_textbox').val());
$('#alertToast').toast('show');
$('#alertToast .toast-body').html("Peer deleted!");
$("#delete_peer").removeAttr("disabled")
$("#delete_peer").html("Delete")
}
}
})
@ -189,9 +205,16 @@ $("#peer_private_key_textbox").change(function(){
$("#save_peer_setting").click(function (){
$(this).attr("disabled","disabled")
$(this).html("Saving...")
if ($("#peer_DNS_textbox").val() !== "" && $("#peer_allowed_ip_textbox").val() !== ""){
if ($("#peer_DNS_textbox").val() !== "" &&
$("#peer_allowed_ip_textbox").val() !== "" &&
$("#peer_endpoint_allowed_ips").val() != ""
){
var peer_id = $(this).attr("peer_id");
var conf_id = $(this).attr("conf_id");
var data_list = [$("#peer_name_textbox"), $("#peer_DNS_textbox"), $("#peer_private_key_textbox"), $("#peer_allowed_ip_textbox"), $("#peer_endpoint_allowed_ips")]
for (var i = 0; i < data_list.length; i++){
data_list[i].attr("disabled", "disabled")
}
$.ajax({
method: "POST",
url: "/save_peer_setting/"+conf_id,
@ -218,8 +241,14 @@ $("#save_peer_setting").click(function (){
}
$("#save_peer_setting").removeAttr("disabled")
$("#save_peer_setting").html("Save")
for (var i = 0; i < data_list.length; i++){
data_list[i].removeAttr("disabled")
}
}
})
}else{
$("#setting_peer_alert").html("Please fill in all required box.");
$("#setting_peer_alert").removeClass("d-none");
}
@ -288,7 +317,16 @@ function copyToClipboard(element) {
$temp.remove();
}
// $(".key").mouseenter(function(){
//
// })
$("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($('#search_peer_textbox').val());
});

View File

@ -69,14 +69,14 @@
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_DNS">DNS</label>
<label for="new_add_DNS">DNS <code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_DNS" value="{{ DNS }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_endpoint_allowed_ip">Endpoint Allowed IPs</label>
<label for="new_add_endpoint_allowed_ip">Endpoint Allowed IPs <code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_endpoint_allowed_ip" value="{{ endpoint_allowed_ip }}">
</div>
</div>
@ -228,7 +228,6 @@
function load_data(search){
startProgressBar()
$.ajax({
method: "GET",
url: "/get_config/"+conf_name+"?search="+encodeURIComponent(search),
@ -237,12 +236,10 @@
},
success: function (response){
$("#config_body").html(response);
{#$("[refresh-interval={{ dashboard_refresh_interval }}]").addClass("active")#}
$("#search_peer_textbox").css("display", "block")
if (bar.css("width") !== "0%"){
endProgressBar()
}
}
})
}
@ -253,19 +250,7 @@
}, {{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($('#search_peer_textbox').val());
});
</script>
<script src="{{ url_for('static',filename='js/configuration.js') }}"></script>
</html>

View File

@ -49,6 +49,10 @@
<small class="text-muted"><strong>LISTEN PORT</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{conf_data['listen_port']}}</samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>ADDRESS</strong></small>
<h6 style="text-transform: uppercase;"><samp>{{conf_data['conf_address']}}</samp></h6>
</div>
</div>
<hr>
<div class="button-div mb-3">
@ -66,12 +70,12 @@
<div class="col-sm">
<div class="form-group">
<label><small class="text-muted">Refresh Interval</small></label><br>
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-primary btn-group-label refresh"><i class="bi bi-arrow-repeat"></i></button>
<button type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 5000 %} {{ "active" }} {% endif %}" refresh-interval="5000">5s</button>
<button type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 10000 %} {{ "active" }} {% endif %}" refresh-interval="10000">10s</button>
<button type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 30000 %} {{ "active" }} {% endif %}" refresh-interval="30000">30s</button>
<button type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 60000 %} {{ "active" }} {% endif %}" refresh-interval="60000">1m</button>
<div class="btn-group" role="group" style="width: 100%">
<button style="width: 20%" type="button" class="btn btn-outline-primary btn-group-label refresh"><i class="bi bi-arrow-repeat"></i></button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 5000 %} {{ "active" }} {% endif %}" refresh-interval="5000">5s</button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 10000 %} {{ "active" }} {% endif %}" refresh-interval="10000">10s</button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 30000 %} {{ "active" }} {% endif %}" refresh-interval="30000">30s</button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval {% if dashboard_refresh_interval == 60000 %} {{ "active" }} {% endif %}" refresh-interval="60000">1m</button>
</div>
</div>
</div>
@ -151,7 +155,7 @@
<button type="button" class="btn btn-outline-success btn-qrcode-peer btn-control" img_src="{{ qrcode("[Interface]\nPrivateKey = "+i['private_key']+"\nAddress = "+i['allowed_ip']+"\nDNS = 1.1.1.1\n\n[Peer]\nPublicKey = "+conf_data['public_key']+"\nAllowedIPs = 0.0.0.0/0\nEndpoint = "+wg_ip+":"+conf_data['listen_port']) }}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="width: 19px;" fill="#28a745"><path d="M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zM13 3v8h8V3h-8zm6 6h-4V5h4v4zM13 13h2v2h-2zM15 15h2v2h-2zM13 17h2v2h-2zM17 17h2v2h-2zM19 19h2v2h-2zM15 19h2v2h-2zM17 13h2v2h-2zM19 15h2v2h-2z"/></svg>
</button>
<a href="/download/{{ conf_data['name'] }}?id={{ i['id']|urlencode }}" type="button" class="btn btn-outline-info btn-download-peer btn-control">
<a href="/download/{{ conf_data['name'] }}?id={{ i['id']|urlencode }}" class="btn btn-outline-info btn-download-peer btn-control">
<i class="bi bi-download"></i>
</a>
</div>

View File

@ -1,7 +1,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Wireguard Dashboard - {{ title }}</title>
<title>Wireguard Dashboard | {{ title }}</title>
<link rel="icon" href="{{ url_for('static',filename='img/logo.png') }}"/>
<link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='css/dashboard.css') }}">

View File

@ -1,5 +1,9 @@
<html>
{% include "header.html" %}
{% with %}
{% set title="Home" %}
{% include "header.html"%}
{% endwith %}
<body>
{% include "navbar.html" %}
<div class="container-fluid">

View File

@ -1,5 +1,8 @@
<html>
{% include "header.html" %}
{% with %}
{% set title="Settings" %}
{% include "header.html"%}
{% endwith %}
<body>
{% include "navbar.html" %}
<div class="container-fluid">