1
0
mirror of https://github.com/donaldzou/WGDashboard.git synced 2024-06-30 22:50:14 +02:00

Merge branch 'main' into refactoring

This commit is contained in:
Donald Zou 2021-12-29 13:57:11 -05:00 committed by GitHub
commit c22e61add2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 473 additions and 201 deletions

View File

@ -260,7 +260,7 @@ def get_all_peers_data(config_name):
for i in conf_peer_data['Peers']:
search = db.search(peers.id == i['PublicKey'])
if not search:
db.insert({
new_data = {
"id": i['PublicKey'],
"private_key": "",
"DNS": config.get("Peers", "peer_global_DNS"),
@ -276,8 +276,12 @@ def get_all_peers_data(config_name):
"traffic": [],
"mtu": config.get("Peers", "peer_mtu"),
"keepalive": config.get("Peers", "peer_keep_alive"),
"remote_endpoint": config.get("Peers", "remote_endpoint")
})
"remote_endpoint": config.get("Peers", "remote_endpoint"),
"preshared_key": ""
}
if "PresharedKey" in i.keys():
new_data["preshared_key"] = i["PresharedKey"]
db.insert(new_data)
else:
# Update database since V2.2
update_db = {}
@ -294,7 +298,12 @@ def get_all_peers_data(config_name):
if "keepalive" not in search[0]:
update_db['keepalive'] = config.get("Peers", "peer_keep_alive")
if "remote_endpoint" not in search[0]:
update_db['remote_endpoint'] = config.get("Peers", "remote_endpoint")
update_db['remote_endpoint'] = config.get("Peers","remote_endpoint")
if "preshared_key" not in search[0]:
if "PresharedKey" in i.keys():
update_db['preshared_key'] = i["PresharedKey"]
else:
update_db['preshared_key'] = ""
db.update(update_db, peers.id == i['PublicKey'])
# Remove peers no longer exist in WireGuard configuration file
db_key = list(map(lambda a: a['id'], db.all()))
@ -313,7 +322,7 @@ def get_all_peers_data(config_name):
try:
sem.release()
except RuntimeError as e:
pass
print("RuntimeError: cannot release un-acquired lock")
# Search for peers
@ -335,7 +344,10 @@ def get_peers(config_name, search, sort_t):
else:
result = sorted(result, key=lambda d: d[sort_t])
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return result
@ -381,7 +393,10 @@ def get_conf_total_data(config_name):
upload_total = round(upload_total, 4)
download_total = round(download_total, 4)
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return [total, upload_total, download_total]
@ -411,13 +426,17 @@ def get_conf_list():
# Generate private key
def gen_private_key():
gen = subprocess.check_output('wg genkey > private_key.txt && wg pubkey < private_key.txt > public_key.txt',
shell=True)
gen_psk = subprocess.check_output('wg genpsk', shell=True)
preshare_key = gen_psk.decode("UTF-8").strip()
with open('private_key.txt', encoding='utf-8') as file_object:
private_key = file_object.readline().strip()
with open('public_key.txt', encoding='utf-8') as file_object:
public_key = file_object.readline().strip()
data = {"private_key": private_key, "public_key": public_key}
os.remove('private_key.txt')
os.remove('public_key.txt')
data = {"private_key": private_key, "public_key": public_key, "preshared_key": preshare_key}
private.close()
public.close()
return data
@ -441,20 +460,25 @@ def f_check_key_match(private_key, public_key, config_name):
result = gen_public_key(private_key)
if result['status'] == 'failed':
return result
sem.acquire(timeout=1)
db = TinyDB(os.path.join(db_path, config_name + ".json"))
peers = Query()
match = db.search(peers.id == result['data'])
if len(match) != 1 or result['data'] != public_key:
db.close()
sem.release()
return {'status': 'failed', 'msg': 'Please check your private key, it does not match with the public key.'}
db.close()
sem.release()
return {'status': 'success'}
else:
sem.acquire(timeout=1)
db = TinyDB(os.path.join(db_path, config_name + ".json"))
peers = Query()
match = db.search(peers.id == result['data'])
if len(match) != 1 or result['data'] != public_key:
db.close()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return {'status': 'failed', 'msg': 'Please check your private key, it does not match with the public key.'}
else:
db.close()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return {'status': 'success'}
# Check if there is repeated allowed IP
def check_repeat_allowed_ip(public_key, ip, config_name):
@ -464,16 +488,22 @@ def check_repeat_allowed_ip(public_key, ip, config_name):
peer = db.search(peers.id == public_key)
if len(peer) != 1:
return {'status': 'failed', 'msg': 'Peer does not exist'}
existed_ip = db.search((peers.id != public_key) & (peers.allowed_ip == ip))
if len(existed_ip) != 0:
db.close()
sem.release()
return {'status': 'failed', 'msg': "Allowed IP already taken by another peer."}
db.close()
sem.release()
return {'status': 'success'}
else:
existed_ip = db.search((peers.id != public_key) & (peers.allowed_ip == ip))
if len(existed_ip) != 0:
db.close()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return {'status': 'failed', 'msg': "Allowed IP already taken by another peer."}
else:
db.close()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return {'status': 'success'}
"""
@ -759,13 +789,17 @@ def update_dashbaord_sort():
# Update configuration refresh interval
@app.route('/update_dashboard_refresh_interval', methods=['POST'])
def update_dashboard_refresh_interval():
config = configparser.ConfigParser(strict=False)
config.read(DASHBOARD_CONF)
config.set("Server", "dashboard_refresh_interval", str(request.form['interval']))
with open(DASHBOARD_CONF, "w", encoding='utf-8') as config_object:
config.write(config_object)
config.clear()
return "true"
preset_interval = ["5000", "10000", "30000", "60000"]
if request.form["interval"] in preset_interval:
config = configparser.ConfigParser(strict=False)
config.read(dashboard_conf)
config.set("Server", "dashboard_refresh_interval", str(request.form['interval']))
with open(DASHBOARD_CONF, "w", encoding='utf-8') as config_object:
config.write(config_object)
config.clear()
return "true"
else:
return "false"
# Configuration Page
@ -868,55 +902,92 @@ def add_peer(config_name):
public_key = data['public_key']
allowed_ips = data['allowed_ips']
endpoint_allowed_ip = data['endpoint_allowed_ip']
dns_addresses = data['DNS']
DNS = data['DNS']
enable_preshared_key = data["enable_preshared_key"]
keys = get_conf_peer_key(config_name)
if len(public_key) == 0 or len(dns_addresses) == 0 or len(allowed_ips) == 0 or len(endpoint_allowed_ip) == 0:
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return "Please fill in all required box."
if not isinstance(keys, list):
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return config_name + " is not running."
if public_key in keys:
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return "Public key already exist."
if len(db.search(peers.allowed_ip.matches(allowed_ips))) != 0:
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return "Allowed IP already taken by another peer."
if not check_DNS(dns_addresses):
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return "DNS formate is incorrect. Example: 1.1.1.1"
if not check_Allowed_IPs(endpoint_allowed_ip):
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return "Endpoint Allowed IPs format is incorrect."
if len(data['MTU']) == 0 or not data['MTU'].isdigit():
db.close()
sem.release()
return "MTU format is not correct."
db.close()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return "MTU format is not correct."
if len(data['keep_alive']) == 0 or not data['keep_alive'].isdigit():
db.close()
sem.release()
return "Persistent Keepalive format is not correct."
db.close()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return "Persistent Keepalive format is not correct."
try:
subprocess.run(f"wg set {config_name} peer {public_key} allowed-ips {allowed_ips}",
check=True, shell=True, capture_output=True, stderr=subprocess.STDOUT)
subprocess.run("wg-quick save " + config_name,
check=True, shell=True, capture_output=True, stderr=subprocess.STDOUT)
if enable_preshared_key == True:
key = subprocess.check_output("wg genpsk > tmp_psk.txt", shell=True)
status = subprocess.check_output(f"wg set {config_name} peer {public_key} allowed-ips {allowed_ips} preshared-key tmp_psk.txt",
shell=True, stderr=subprocess.STDOUT)
os.remove("tmp_psk.txt")
elif enable_preshared_key == False:
status = subprocess.check_output(f"wg set {config_name} peer {public_key} allowed-ips {allowed_ips}",
shell=True, stderr=subprocess.STDOUT)
status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT)
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},
peers.id == public_key)
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return "true"
except subprocess.CalledProcessError as exc:
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return exc.output.strip()
@ -936,21 +1007,26 @@ def remove_peer(config_name):
if delete_key not in keys:
db.close()
return "This key does not exist"
try:
subprocess.run(f"wg set {config_name} peer {delete_key} remove",
check=True, shell=True, capture_output=True, stderr=subprocess.STDOUT)
subprocess.run("wg-quick save " + config_name,
check=True, shell=True, capture_output=True, stderr=subprocess.STDOUT)
db.remove(peers.id == delete_key)
db.close()
sem.release()
return "true"
except subprocess.CalledProcessError as exc:
db.close()
sem.release()
return exc.output.strip()
else:
try:
remove_wg = subprocess.check_output(f"wg set {config_name} peer {delete_key} remove",
shell=True, stderr=subprocess.STDOUT)
save_wg = subprocess.check_output(f"wg-quick save {config_name}",
shell=True, stderr=subprocess.STDOUT)
db.remove(peers.id == delete_key)
db.close()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return "true"
except subprocess.CalledProcessError as exc:
db.close()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return exc.output.strip()
# Save peer settings
@app.route('/save_peer_setting/<config_name>', methods=['POST'])
@ -962,6 +1038,7 @@ def save_peer_setting(config_name):
dns_addresses = data['DNS']
allowed_ip = data['allowed_ip']
endpoint_allowed_ip = data['endpoint_allowed_ip']
preshared_key = data['preshared_key']
sem.acquire(timeout=1)
db = TinyDB(os.path.join(db_path, config_name + ".json"))
peers = Query()
@ -969,31 +1046,60 @@ def save_peer_setting(config_name):
check_ip = check_repeat_allowed_ip(id, allowed_ip, config_name)
if not check_IP_with_range(endpoint_allowed_ip):
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify({"status": "failed", "msg": "Endpoint Allowed IPs format is incorrect."})
if not check_DNS(dns_addresses):
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify({"status": "failed", "msg": "DNS format is incorrect."})
if len(data['MTU']) == 0 or not data['MTU'].isdigit():
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify({"status": "failed", "msg": "MTU format is not correct."})
if len(data['keep_alive']) == 0 or not data['keep_alive'].isdigit():
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify({"status": "failed", "msg": "Persistent Keepalive format is not correct."})
if private_key != "":
check_key = f_check_key_match(private_key, id, config_name)
if check_key['status'] == "failed":
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify(check_key)
if check_ip['status'] == "failed":
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify(check_ip)
try:
tmp_psk = open("tmp_edit_psk.txt", "w+")
tmp_psk.write(preshared_key)
tmp_psk.close()
change_psk = subprocess.check_output("wg set " + config_name + " peer " + id + " preshared-key tmp_edit_psk.txt", shell=True, stderr=subprocess.STDOUT)
if change_psk.decode("UTF-8") != "":
db.close()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify({"status": "failed", "msg": change_psk.decode("UTF-8")})
if allowed_ip == "":
allowed_ip = '""'
allowed_ip = allowed_ip.replace(" ", "")
@ -1003,24 +1109,39 @@ def save_peer_setting(config_name):
check=True, shell=True, capture_output=True, stderr=subprocess.STDOUT)
if change_ip.decode("UTF-8") != "":
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify({"status": "failed", "msg": change_ip.decode("UTF-8")})
db.update(
{"name": name, "private_key": private_key,
"DNS": dns_addresses, "endpoint_allowed_ip": endpoint_allowed_ip,
"mtu": data['MTU'],
"keepalive": data['keep_alive']},
peers.id == id)
{
"name": name,
"private_key": private_key,
"DNS": dns_addresses,
"endpoint_allowed_ip": endpoint_allowed_ip,
"mtu": data['MTU'],
"keepalive":data['keep_alive'], "preshared_key": preshared_key
}, peers.id == id)
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify({"status": "success", "msg": ""})
except subprocess.CalledProcessError as exc:
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify({"status": "failed", "msg": str(exc.output.decode("UTF-8").strip())})
else:
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify({"status": "failed", "msg": "This peer does not exist."})
@ -1036,8 +1157,12 @@ def get_peer_name(config_name):
db.close()
data = {"name": result[0]['name'], "allowed_ip": result[0]['allowed_ip'], "DNS": result[0]['DNS'],
"private_key": result[0]['private_key'], "endpoint_allowed_ip": result[0]['endpoint_allowed_ip'],
"mtu": result[0]['mtu'], "keep_alive": result[0]['keepalive']}
sem.release()
"mtu": result[0]['mtu'], "keep_alive": result[0]['keepalive'], "preshared_key": result[0]["preshared_key"]}
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return jsonify(data)
@ -1084,6 +1209,7 @@ def generate_qrcode(config_name):
mtu_value = peer['mtu']
endpoint_allowed_ip = peer['endpoint_allowed_ip']
keepalive = peer['keepalive']
preshared_key = peer["preshared_key"]
conf = {
"public_key": public_key,
"listen_port": listen_port,
@ -1094,18 +1220,31 @@ def generate_qrcode(config_name):
"mtu": mtu_value,
"endpoint_allowed_ip": endpoint_allowed_ip,
"keepalive": keepalive,
"preshared_key": preshared_key
}
db.close()
sem.release()
return render_template("qrcode.html", i=conf)
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
result = "[Interface]\nPrivateKey = "+conf['private_key']+"\nAddress = "+conf['allowed_ip']+"\nMTU = "+conf['mtu']+"\nDNS = "+conf['DNS']\
+"\n\n[Peer]\nPublicKey = "+conf['public_key']+"\nAllowedIPs = "+conf['endpoint_allowed_ip']+"\nPersistentKeepalive = "+conf['keepalive']+"\nEndpoint = "+conf['endpoint']
if preshared_key != "":
result += "\nPresharedKey = "+preshared_key
return render_template("qrcode.html", i=result)
else:
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return redirect("/configuration/" + config_name)
# Download configuration file
@app.route('/<config_name>', methods=['GET'])
@app.route('/download/<config_name>', methods=['GET'])
def download(config_name):
print(request.headers.get('User-Agent'))
id = request.args.get('id')
@ -1127,6 +1266,7 @@ def download(config_name):
endpoint_allowed_ip = peer['endpoint_allowed_ip']
keepalive = peer['keepalive']
filename = peer['name']
preshared_key = peer["preshared_key"]
if len(filename) == 0:
filename = "Untitled_Peers"
else:
@ -1141,23 +1281,24 @@ def download(config_name):
filename = "Untitled_Peer"
filename = "".join(filename.split(' '))
filename = filename + "_" + config_name
psk = ""
if preshared_key != "":
psk = "\nPresharedKey = "+preshared_key
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
result = "[Interface]\nPrivateKey = " + private_key + "\nAddress = " + allowed_ip + "\nDNS = " + \
dns_addresses + "\nMTU = " + mtu_value + "\n\n[Peer]\nPublicKey = " + \
public_key + "\nAllowedIPs = " + endpoint_allowed_ip + "\nEndpoint = " + \
endpoint + "\nPersistentKeepalive = " + keepalive
return app.response_class((yield result),
mimetype='text/conf',
headers={"Content-Disposition": "attachment;filename=" + filename + ".conf"})
endpoint + "\nPersistentKeepalive = " + keepalive + psk
return app.response_class((yield result), mimetype='text/conf', headers={"Content-Disposition": "attachment;filename=" + filename + ".conf"})
db.close()
return redirect("/configuration/" + config_name)
# Switch peer displate mode
# Switch peer display mode
@app.route('/switch_display_mode/<mode>', methods=['GET'])
def switch_display_mode(mode):
if mode in ['list', 'grid']:
@ -1181,7 +1322,7 @@ Dashboard Tools Related
def get_ping_ip():
config_name = request.form['config']
sem.acquire(timeout=1)
db = TinyDB(os.path.join(db_path, config_name + ".json"))
db = TinyDB(os.path.join(db_path, config + ".json"))
html = ""
for i in db.all():
@ -1196,7 +1337,10 @@ def get_ping_ip():
html += "<option value=" + endpoint[0] + ">" + endpoint[0] + "</option>"
html += "</optgroup>"
db.close()
sem.release()
try:
sem.release()
except RuntimeError as e:
print("RuntimeError: cannot release un-acquired lock")
return html
@ -1326,13 +1470,4 @@ if __name__ == "__main__":
app_port = configuration_settings.get("Server", "app_port")
wg_conf_path = configuration_settings.get("Server", "wg_conf_path")
configuration_settings.clear()
app.run(host=app_ip, debug=False, port=app_port)
else:
init_dashboard()
UPDATE = check_update()
configuration_settings = configparser.ConfigParser(strict=False)
configuration_settings.read(DASHBOARD_CONF)
app_ip = configuration_settings.get("Server", "app_ip")
app_port = configuration_settings.get("Server", "app_port")
wg_conf_path = configuration_settings.get("Server", "wg_conf_path")
configuration_settings.clear()
app.run(host=app_ip, debug=False, port=app_port)

View File

@ -134,6 +134,7 @@ body {
.info h6{
line-break: anywhere;
transition: 0.2s ease-in-out;
}
.info .row .col-sm{
@ -291,6 +292,10 @@ main{
cursor: pointer;
}
#peer_private_key_textbox, #private_key, #public_key{
font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
}
.progress-bar{
transition: 0.3s ease-in-out;
}
@ -322,4 +327,57 @@ main{
.modal-content{
border-radius: 10px;
}
.tooltip-inner{
font-size: 0.8rem;
}
@-webkit-keyframes loading {
0%{
background-color: #dfdfdf;
}
50%{
background-color: #adadad;
}
100%{
background-color: #dfdfdf;
}
}
@-moz-keyframes loading {
0%{
background-color: #dfdfdf;
}
50%{
background-color: #adadad;
}
100%{
background-color: #dfdfdf;
}
}
.conf_card{
transition: 0.2s ease-in-out;
}
.conf_card:hover{
border-color: #007bff;
cursor: pointer;
}
.info_loading{
animation: loading 2s infinite ease-in-out;
border-radius: 5px;
height: 19px;
transition: 0.3s ease-in-out;
}
#conf_status_btn{
transition: 0.2s ease-in-out;
}
#conf_status_btn.info_loading{
height: 38px;
border-radius: 5px;
animation: loading 2s infinite ease-in-out;
}

View File

@ -1,5 +1,38 @@
let $body = $("body");
// Progress Bar
let $progress_bar = $(".progress-bar")
function startProgressBar(){
$progress_bar.css("width","0%")
$progress_bar.css("opacity", "100")
$progress_bar.css("background", "rgb(255,69,69)")
$progress_bar.css("background", "linear-gradient(145deg, rgba(255,69,69,1) 0%, rgba(0,115,186,1) 100%)")
$progress_bar.css("width","25%")
setTimeout(function(){
stillLoadingProgressBar();
},300)
}
function stillLoadingProgressBar(){
$progress_bar.css("transition", "3s ease-in-out")
$progress_bar.css("width", "75%")
}
function endProgressBar(){
$progress_bar.css("transition", "0.3s ease-in-out")
$progress_bar.css("width","100%")
setTimeout(function(){
$progress_bar.css("opacity", "0")
},250)
}
function showToast(msg) {
$('#alertToast').toast('show');
$('#alertToast .toast-body').html(msg);
}
// Config Toggle
$("body").on("click", ".switch", function (){
$body.on("click", ".switch", function (){
$(this).siblings($(".spinner-border")).css("display", "inline-block");
$(this).remove()
location.replace("/switch/"+$(this).attr('id'));
@ -13,6 +46,7 @@ function generate_key(){
}).done(function(res){
$("#private_key").val(res.private_key)
$("#public_key").val(res.public_key)
$("#preshare_key").val(res.preshared_key)
$("#add_peer_alert").addClass("d-none");
$("#re_generate_key i").removeClass("rotating")
})
@ -36,7 +70,7 @@ function generate_public_key(){
}
// Add Peer
$("#private_key").change(function(){
$("#private_key").on("change",function(){
if ($("#private_key").val().length > 0){
$("#re_generate_key i").addClass("rotating")
generate_public_key()
@ -53,7 +87,15 @@ $("#re_generate_key").click(function (){
$("#re_generate_key i").addClass("rotating")
generate_key()
})
$("#save_peer").click(function(){
let addModal = new bootstrap.Modal(document.getElementById('add_modal'), {
keyboard: false
})
$(".add_btn").on("click", function(){
addModal.toggle();
})
$("#save_peer").on("click",function(){
$(this).attr("disabled","disabled")
$(this).html("Saving...")
@ -77,7 +119,8 @@ $("#save_peer").click(function(){
"DNS": $("#new_add_DNS").val(),
"endpoint_allowed_ip": $("#new_add_endpoint_allowed_ip").val(),
"MTU": $("#new_add_MTU").val(),
"keep_alive": $("#new_add_keep_alive").val()
"keep_alive": $("#new_add_keep_alive").val(),
"enable_preshared_key": $("#enable_preshare_key").prop("checked"),
}),
success: function (response){
if(response != "true"){
@ -90,7 +133,8 @@ $("#save_peer").click(function(){
$("#save_peer").html("Save")
}
else{
location.reload();
load_data("");
addModal.hide();
}
}
})
@ -106,7 +150,7 @@ var qrcodeModal = new bootstrap.Modal(document.getElementById('qrcode_modal'), {
})
// QR Code
$("body").on("click", ".btn-qrcode-peer", function (){
$body.on("click", ".btn-qrcode-peer", function (){
var src = $(this).attr('img_src');
$.ajax({
"url": src,
@ -122,7 +166,7 @@ var deleteModal = new bootstrap.Modal(document.getElementById('delete_modal'), {
keyboard: false
});
$("body").on("click", ".btn-delete-peer", function(){
$body.on("click", ".btn-delete-peer", function(){
var peer_id = $(this).attr("id");
$("#delete_peer").attr("peer_id", peer_id);
deleteModal.toggle();
@ -161,7 +205,7 @@ $("#delete_peer").click(function(){
var settingModal = new bootstrap.Modal(document.getElementById('setting_modal'), {
keyboard: false
})
$("body").on("click", ".btn-setting-peer", function(){
$body.on("click", ".btn-setting-peer", function(){
startProgressBar()
var peer_id = $(this).attr("id");
$("#save_peer_setting").attr("peer_id", peer_id);
@ -182,6 +226,7 @@ $("body").on("click", ".btn-setting-peer", function(){
$("#setting_modal #peer_endpoint_allowed_ips").val(response['endpoint_allowed_ip'])
$("#setting_modal #peer_mtu").val(response['mtu'])
$("#setting_modal #peer_keep_alive").val(response['keep_alive'])
$("#setting_modal #peer_preshared_key_textbox").val(response["preshared_key"])
settingModal.toggle();
endProgressBar()
}
@ -223,7 +268,7 @@ $("#save_peer_setting").click(function (){
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_name_textbox"), $("#peer_DNS_textbox"), $("#peer_private_key_textbox"), $("#peer_preshared_key_textbox"),
$("#peer_allowed_ip_textbox"), $("#peer_endpoint_allowed_ips"), $("#peer_mtu"), $("#peer_keep_alive")
]
for (var i = 0; i < data_list.length; i++){
@ -243,7 +288,8 @@ $("#save_peer_setting").click(function (){
allowed_ip: $("#peer_allowed_ip_textbox").val(),
endpoint_allowed_ip: $("#peer_endpoint_allowed_ips").val(),
MTU: $("#peer_mtu").val(),
keep_alive: $("#peer_keep_alive").val()
keep_alive: $("#peer_keep_alive").val(),
preshared_key: $("#peer_preshared_key_textbox").val()
}),
success: function (response){
if (response['status'] === "failed"){
@ -296,7 +342,7 @@ function doneTyping () {
}
// Sorting
$("body").on("change", "#sort_by_dropdown", function (){
$body.on("change", "#sort_by_dropdown", function (){
$.ajax({
method:"POST",
data: JSON.stringify({'sort':$("#sort_by_dropdown option:selected").val()}),
@ -309,11 +355,11 @@ $("body").on("change", "#sort_by_dropdown", function (){
})
// Click key to copy
$("body").on("mouseenter", ".key", function(){
$body.on("mouseenter", ".key", function(){
var label = $(this).parent().siblings().children()[1]
label.style.opacity = "100"
})
$("body").on("mouseout", ".key", function(){
$body.on("mouseout", ".key", function(){
var label = $(this).parent().siblings().children()[1]
label.style.opacity = "0"
setTimeout(function (){
@ -321,39 +367,54 @@ $("body").on("mouseout", ".key", function(){
},200)
});
$("body").on("click", ".key", function(){
$body.on("click", ".key", function(){
var label = $(this).parent().siblings().children()[1]
copyToClipboard($(this))
label.innerHTML = "COPIED!"
})
function copyToClipboard(element) {
var $temp = $("<input>");
$("body").append($temp);
$body.append($temp);
$temp.val($(element).text()).select();
document.execCommand("copy");
$temp.remove();
}
// Update Interval
$("body").on("click", ".update_interval", function(){
$body.on("click", ".update_interval", function(){
let prev = $(".interval-btn-group.active button");
$(".interval-btn-group button").removeClass("active");
$(this).addClass("active");
let _new = $(this)
_new.addClass("active");
let interval = $(this).data("refresh-interval");
$.ajax({
method:"POST",
data: "interval="+$(this).data("refresh-interval"),
url: "/update_dashboard_refresh_interval",
success: function (res){
load_data($('#search_peer_textbox').val())
if (res === "true"){
load_interval = interval;
clearInterval(load_timeout);
load_timeout = setInterval(function (){
load_data($('#search_peer_textbox').val());
}, interval);
showToast("Refresh Interval set to "+Math.round(interval/1000)+" seconds");
}else{
$(".interval-btn-group button").removeClass("active");
$('.interval-btn-group button[data-refresh-interval="'+load_interval+'"]').addClass("active");
showToast("Refresh Interval set unsuccessful");
}
}
})
});
$("body").on("click", ".refresh", function (){
$body.on("click", ".refresh", function (){
load_data($('#search_peer_textbox').val());
});
// Switch display mode
$("body").on("click", ".display_mode", function(){
$body.on("click", ".display_mode", function(){
$(".display-btn-group button").removeClass("active");
$(this).addClass("active");
let display_mode = $(this).data("display-mode");
@ -361,16 +422,25 @@ $("body").on("click", ".display_mode", function(){
method:"GET",
url: "/switch_display_mode/"+$(this).data("display-mode"),
success: function (res){
// load_data($('#search_peer_textbox').val())
if (display_mode === "list"){
if (res === "true"){
if (display_mode === "list"){
Array($(".peer_list").children()).forEach(function(child){
$(child).removeClass().addClass("col-12");
})
showToast("Displaying as List");
}else{
Array($(".peer_list").children()).forEach(function(child){
$(child).removeClass().addClass("col-sm-6 col-lg-4");
})
});
showToast("Displaying as Grid");
}
}
}
})
})
})
// Pre-share key
// $("#enable_preshare_key").on("change", function(){
// $(".preshare_key_container").css("display", $(".preshare_key_container").css("display") === "none" ? "block":"none");
//
// })

View File

@ -8,7 +8,7 @@
{% include "sidebar.html" %}
<div class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4 mb-4">
<div class="form-group">
<input type="text" class="form-control" id="search_peer_textbox" placeholder="Search Peer..." value="" style="display: none">
<input type="text" class="form-control" id="search_peer_textbox" placeholder="Search Peer..." value="">
</div>
</div>
<div id="config_body">
@ -21,8 +21,8 @@
<h1 class="mb-3"><samp id="conf_name">{{ title }}</samp></h1>
</div>
<div class="col">
<small class="text-muted"><strong>ACTION</strong></small><br>
<div id="conf_status_btn"></div>
<small class="text-muted"><strong>SWITCH</strong></small><br>
<div id="conf_status_btn" class="info_loading"></div>
<div class="spinner-border text-primary" role="status" style="display: none; margin-top: 10px">
<span class="sr-only">Loading...</span>
</div>
@ -30,23 +30,23 @@
<div class="w-100"></div>
<div class="col">
<small class="text-muted"><strong>STATUS</strong></small>
<h6 style="text-transform: uppercase;" id="conf_status"></h6>
<h6 style="text-transform: uppercase;" id="conf_status" class="info_loading"></h6>
</div>
<div class="col">
<small class="text-muted"><strong>CONNECTED PEERS</strong></small>
<h6 style="text-transform: uppercase;" id="conf_connected_peers"></h6>
<h6 style="text-transform: uppercase;" id="conf_connected_peers" class="info_loading"></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL DATA USAGE</strong></small>
<h6 style="text-transform: uppercase;" id="conf_total_data_usage"></h6>
<h6 style="text-transform: uppercase;" id="conf_total_data_usage" class="info_loading"></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL RECEIVED</strong></small>
<h6 style="text-transform: uppercase;" id="conf_total_data_received"></h6>
<h6 style="text-transform: uppercase;" id="conf_total_data_received" class="info_loading"></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>TOTAL SENT</strong></small>
<h6 style="text-transform: uppercase;" id="conf_total_data_sent"></h6>
<h6 style="text-transform: uppercase;" id="conf_total_data_sent" class="info_loading"></h6>
</div>
<div class="w-100"></div>
<div class="col-sm">
@ -54,15 +54,15 @@
<strong>PUBLIC KEY</strong>
<strong style="margin-left: auto!important; opacity: 0; transition: 0.2s ease-in-out" class="text-primary">CLICK TO COPY</strong>
</small>
<h6><samp class="key" id="conf_public_key"></samp></h6>
<h6 class="info_loading"><samp class="key" id="conf_public_key"></samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>LISTEN PORT</strong></small>
<h6 style="text-transform: uppercase;"><samp id="conf_listen_port"></samp></h6>
<h6 style="text-transform: uppercase;" class="info_loading"><samp id="conf_listen_port"></samp></h6>
</div>
<div class="col-sm">
<small class="text-muted"><strong>ADDRESS</strong></small>
<h6 style="text-transform: uppercase;"><samp id="conf_address"></samp></h6>
<h6 style="text-transform: uppercase;" class="info_loading"><samp id="conf_address"></samp></h6>
</div>
</div>
</div>
@ -100,7 +100,7 @@
</div>
</div>
</div>
<button type="button" class="btn btn-primary add_btn" data-toggle="modal" data-target="#add_modal">
<button type="button" class="btn btn-primary add_btn">
<i class="bi bi-plus-circle-fill" style=""></i> Add Peer
</button>
</div>
@ -134,7 +134,7 @@
<label for="private_key">Private Key</label>
</div>
<div class="input-group">
<input type="text" class="form-control" id="private_key" aria-describedby="public_key">
<input type="text" class="form-control" id="private_key" aria-describedby="private_key">
<div class="input-group-append">
<button type="button" class="btn btn-danger" id="re_generate_key"><i class="bi bi-arrow-repeat"></i></button>
</div>
@ -179,6 +179,12 @@
<div class="form-group">
<label for="new_add_keep_alive">Persistent keepalive</label>
<input type="text" class="form-control" id="new_add_keep_alive" value="{{ keep_alive }}">
</div>
</div>
<div class="col-sm">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enable_preshare_key" name="enable_preshare_key" value="enable_psk">
<label class="form-check-label" for="enable_preshare_key">Use Pre-shared Key</label>
</div>
</div>
</div>
@ -233,10 +239,14 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="mb-3">
<label for="peer_private_key_textbox" class="form-label">Private Key <code>(Required for QR Code and download)</code></label>
<input type="password" class="form-control" id="peer_private_key_textbox" style="padding-right: 40px">
<a class="peer_private_key_textbox_switch"><i class="bi bi-eye-fill"></i></a>
</div>
<div>
<label for="peer_private_key_textbox" class="form-label">Private Key <code>(Required for QR Code and download)</code></label>
<input type="password" class="form-control" id="peer_private_key_textbox" style="padding-right: 40px">
<a class="peer_private_key_textbox_switch"><i class="bi bi-eye-fill"></i></a>
<label for="peer_preshared_key_textbox" class="form-label">Pre-Shared Key</label>
<input type="text" class="form-control" id="peer_preshared_key_textbox">
</div>
<hr>
@ -307,7 +317,7 @@
<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>
<strong class="mr-auto">WGDashboard</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
@ -319,35 +329,16 @@
{% include "tools.html" %}
</body>
{% include "footer.html" %}
<script src="{{ url_for('static',filename='js/configuration.js') }}"></script>
<script>
function roundN(value, digits) {
var tenToN = 10 ** digits;
return /*Math.trunc*/(Math.round(value * tenToN)) / tenToN;
}
let load_timeout;
let load_interval = 0;
var conf_name = "{{ conf_data['name'] }}"
let conf_name = "{{ conf_data['name'] }}"
$(".sb-"+conf_name+"-url").addClass("active");
// Progress Bar
let bar = $(".progress-bar")
function startProgressBar(){
bar.css("width","0%")
bar.css("opacity", "100")
bar.css("background", "rgb(255,69,69)")
bar.css("background", "linear-gradient(145deg, rgba(255,69,69,1) 0%, rgba(0,115,186,1) 100%)")
bar.css("width","25%")
setTimeout(function(){
stillLoadingProgressBar();
},300)
}
function stillLoadingProgressBar(){
bar.css("transition", "3s ease-in-out")
bar.css("width", "75%")
}
function endProgressBar(){
bar.css("transition", "0.3s ease-in-out")
bar.css("width","100%")
setTimeout(function(){
bar.css("opacity", "0")
},250)
}
function load_data(search){
startProgressBar()
let result = '';
@ -382,22 +373,35 @@
$("#conf_status").html(response["status"]+'<span class="dot dot-'+response["status"]+'"></span>');
$("#conf_connected_peers").html(response["running_peer"]);
$("#conf_total_data_usage").html(response["total_data_usage"][0]+" GB");
$("#conf_total_data_received").html(response["total_data_usage"][1]+" GB");
$("#conf_total_data_sent").html(response["total_data_usage"][2]+" GB");
$("#conf_total_data_usage").html(response["total_data_usage"][0] +" GB");
$("#conf_total_data_received").html(response["total_data_usage"][2] +" GB");
$("#conf_total_data_sent").html(response["total_data_usage"][1]+" GB");
$("#conf_public_key").html(response["public_key"]);
$("#conf_listen_port").html(response["listen_port"] === "" ? "N/A":response["listen_port"]);
$("#conf_address").html(response["conf_address"]);
$(".info h6").removeClass("info_loading");
$("#conf_status_btn").removeClass("info_loading")
if (response["peer_data"].length === 0){
$(".peer_list").html('<div class="col-12" style="text-align: center; margin-top: 1.5rem"><h3 class="text-muted">Oops! No peers found ‘︿’</h3></div>');
}else{
let display_mode = response["peer_display_mode"] === "list" ? "col-12" : "col-sm-6 col-lg-4";
response["peer_data"].forEach(function(peer){
let total_r = 0;
let total_s = 0;
for (let i = 0; i < peer["traffic"].length; i++){
total_r += peer["traffic"][i]["total_receive"];
total_s += peer["traffic"][i]["total_sent"];
}
let spliter = '<div class="w-100"></div>';
let peer_name = '<div class="col-sm"><h4>'+ (peer["name"] === "" ? "Untitled" : peer["name"]) +'</h4></div>';
let peer_status = '<div class="col-6"><small class="text-muted"><strong>STATUS</strong></small> <h6 style="text-transform: uppercase;" class="mb-2 h6-dot-'+peer["status"]+'"><span class="dot dot-'+peer["status"]+'" style="margin-left: 0 !important;margin-top: 5px"></span></h6></div>'
let peer_transfer = '<div class="col-6 peer_data_group" style="text-align: right"> <small class="text-muted"><strong>TRANSFER</strong></small> <p class="text-primary" style="text-transform: uppercase; margin-bottom: 0;"><small><i class="bi bi-arrow-down-right"></i>'+peer["total_receive"]+' GB</small></p> <p class="text-success" style="text-transform: uppercase; margin-bottom: 0"><small><i class="bi bi-arrow-up-right"></i> '+peer["total_sent"]+' GB</small></p> </div>'
let peer_name =
'<div class="col-sm display" style="display: flex; align-items: center; margin-bottom: 0.2rem">' +
'<h5 style="margin: 0;">'+ (peer["name"] === "" ? "Untitled" : peer["name"]) +'</h5>' +
'<h6 style="text-transform: uppercase; margin: 0; margin-left: auto !important;"><span class="dot dot-'+peer["status"]+'" style="margin-left: auto !important;" data-toggle="tooltip" data-placement="left" title="Peer Running"></span></h6>' +
'</div>';
let peer_transfer = '<div class="col-12 peer_data_group" style="text-align: right; display: flex; margin-bottom: 0.5rem"><p class="text-primary" style="text-transform: uppercase; margin-bottom: 0; margin-right: 1rem"><small><i class="bi bi-arrow-down-right"></i> '+ roundN(peer["total_receive"] + total_r, 4) +' GB</small></p> <p class="text-success" style="text-transform: uppercase; margin-bottom: 0"><small><i class="bi bi-arrow-up-right"></i> '+ roundN(peer["total_sent"] + total_s, 4) +' GB</small></p> </div>'
let peer_key = '<div class="col-sm"><small class="text-muted" style="display: flex"><strong>PEER</strong><strong style="margin-left: auto!important; opacity: 0; transition: 0.2s ease-in-out" class="text-primary">CLICK TO COPY</strong></small> <h6><samp class="ml-auto key">'+peer["id"]+'</samp></h6></div>';
let peer_allowed_ip = '<div class="col-sm"><small class="text-muted"><strong>ALLOWED IP</strong></small><h6 style="text-transform: uppercase;">'+peer["allowed_ip"]+'</h6></div>';
let peer_latest_handshake = '<div class="col-sm"> <small class="text-muted"><strong>LATEST HANDSHAKE</strong></small> <h6 style="text-transform: uppercase;">'+peer['latest_handshake']+'</h6> </div>';
@ -413,7 +417,6 @@
'<div class="row">'
+ peer_name
+ spliter
+ peer_status
+ peer_transfer
+ peer_key
+ peer_allowed_ip
@ -437,17 +440,16 @@
}
}
{#$("#config_body").html(response);#}
$("#search_peer_textbox").css("display", "block")
$(".dot.dot-running").attr("title","Peer Running").tooltip();
$(".dot.dot-stopped").attr("title","Peer Stopped").tooltip();
{#$("#search_peer_textbox").css("display", "block")#}
endProgressBar()
}
})
}
$(function(){
load_data($('#search_peer_textbox').val());
{#setInterval(function(){#}
{# load_data($('#search_peer_textbox').val());#}
{#}, {{dashboard_refresh_interval}})#}
});
</script>
<script src="{{ url_for('static',filename='js/configuration.js') }}"></script>
</html>

View File

@ -14,12 +14,12 @@
<p class="text-muted">You don't have any WireGuard configurations yet. Please check the configuration folder or change it in "Settings". By default the folder is "/etc/wireguard".</p>
{% endif %}
{% for i in conf%}
<div class="card mt-3">
<div class="card mt-3 conf_card">
<div class="card-body">
<div class="row">
<div class="col card-col">
<small class="text-muted"><strong>CONFIGURATION</strong></small>
<a href="/configuration/{{i['conf']}}">
<a href="/configuration/{{i['conf']}}" class="conf_link">
<h6 class="card-title" style="margin:0 !important;">{{i['conf']}}</h6>
</a>
</div>
@ -51,11 +51,18 @@
</body>
{% include "footer.html" %}
<script>
$('.switch').click(function() {
$('.switch').on("click", function() {
$(this).siblings($(".spinner-border")).css("display", "inline-block")
$(this).remove()
location.replace("/switch/"+$(this).attr('id'))
});
$(".sb-home-url").addClass("active")
$(".sb-home-url").addClass("active");
$(".card-body").on("click", function(handle){
if ($(handle.target).attr("class") !== "bi bi-toggle2-off" && $(handle.target).attr("class") !== "bi bi-toggle2-on") {
window.open($(this).find("a").attr("href"), "_self");
}
})
</script>
</html>

View File

@ -1 +1 @@
{{ qrcode("[Interface]\nPrivateKey = "+i['private_key']+"\nAddress = "+i['allowed_ip']+"\nMTU = "+i['mtu']+"\nDNS = "+i['DNS']+"\n\n[Peer]\nPublicKey = "+i['public_key']+"\nAllowedIPs = "+i['endpoint_allowed_ip']+"\nPersistentKeepalive = "+i['keepalive']+"\nEndpoint = "+i['endpoint']) }}
{{ qrcode(i) }}