mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 15:20:09 +01:00
Finished adding available IP and related UI adjustment
This commit is contained in:
parent
b8b721f2bd
commit
77a82cb84b
@ -13,20 +13,17 @@ import json
|
|||||||
import os
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import urllib.request
|
import urllib.request
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
import signal
|
|
||||||
# PIP installed library
|
# PIP installed library
|
||||||
import ifcfg
|
import ifcfg
|
||||||
from flask import Flask, request, render_template, redirect, url_for, session, jsonify
|
from flask import Flask, request, render_template, redirect, url_for, session, jsonify
|
||||||
from flask_qrcode import QRcode
|
from flask_qrcode import QRcode
|
||||||
from icmplib import ping, traceroute
|
from icmplib import ping, traceroute
|
||||||
from tinydb import TinyDB, Query
|
|
||||||
|
|
||||||
# Import other python files
|
# Import other python files
|
||||||
from util import regex_match, check_DNS, check_Allowed_IPs, check_remote_endpoint, \
|
from util import regex_match, check_DNS, check_Allowed_IPs, check_remote_endpoint, \
|
||||||
@ -325,7 +322,7 @@ def get_peers(config_name, search, sort_t):
|
|||||||
data = g.cur.execute("SELECT * FROM " + config_name).fetchall()
|
data = g.cur.execute("SELECT * FROM " + config_name).fetchall()
|
||||||
result = [{col[i]: data[k][i] for i in range(len(col))} for k in range(len(data))]
|
result = [{col[i]: data[k][i] for i in range(len(col))} for k in range(len(data))]
|
||||||
else:
|
else:
|
||||||
sql = "SELECT * FROM " + config_name + " WHERE name LIKE '%"+search+"%'"
|
sql = "SELECT * FROM " + config_name + " WHERE name LIKE '%" + search + "%'"
|
||||||
data = g.cur.execute(sql).fetchall()
|
data = g.cur.execute(sql).fetchall()
|
||||||
result = [{col[i]: data[k][i] for i in range(len(col))} for k in range(len(data))]
|
result = [{col[i]: data[k][i] for i in range(len(col))} for k in range(len(data))]
|
||||||
if sort_t == "allowed_ip":
|
if sort_t == "allowed_ip":
|
||||||
@ -395,8 +392,18 @@ def get_conf_list():
|
|||||||
for i in os.listdir(wg_conf_path):
|
for i in os.listdir(wg_conf_path):
|
||||||
if regex_match("^(.{1,}).(conf)$", i):
|
if regex_match("^(.{1,}).(conf)$", i):
|
||||||
i = i.replace('.conf', '')
|
i = i.replace('.conf', '')
|
||||||
g.cur.execute(
|
create_table = f"""
|
||||||
"CREATE TABLE IF NOT EXISTS " + i + " (id VARCHAR NULL, private_key VARCHAR NULL, DNS VARCHAR NULL, endpoint_allowed_ip VARCHAR NULL, name VARCHAR NULL, total_receive FLOAT NULL, total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL, status VARCHAR NULL, latest_handshake VARCHAR NULL, allowed_ip VARCHAR NULL, cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, mtu INT NULL, keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL)")
|
CREATE TABLE IF NOT EXISTS {i} (
|
||||||
|
id VARCHAR NOT NULL, private_key VARCHAR NULL, DNS VARCHAR NULL,
|
||||||
|
endpoint_allowed_ip VARCHAR NULL, name VARCHAR NULL, total_receive FLOAT NULL,
|
||||||
|
total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL,
|
||||||
|
status VARCHAR NULL, latest_handshake VARCHAR NULL, allowed_ip VARCHAR NULL,
|
||||||
|
cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, mtu INT NULL,
|
||||||
|
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
g.cur.execute(create_table)
|
||||||
temp = {"conf": i, "status": get_conf_status(i), "public_key": get_conf_pub_key(i)}
|
temp = {"conf": i, "status": get_conf_status(i), "public_key": get_conf_pub_key(i)}
|
||||||
if temp['status'] == "running":
|
if temp['status'] == "running":
|
||||||
temp['checked'] = 'checked'
|
temp['checked'] = 'checked'
|
||||||
@ -412,13 +419,11 @@ def get_conf_list():
|
|||||||
def gen_private_key():
|
def gen_private_key():
|
||||||
gen = subprocess.check_output('wg genkey > private_key.txt && wg pubkey < private_key.txt > public_key.txt',
|
gen = subprocess.check_output('wg genkey > private_key.txt && wg pubkey < private_key.txt > public_key.txt',
|
||||||
shell=True)
|
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:
|
with open('private_key.txt', encoding='utf-8') as file_object:
|
||||||
private_key = file_object.readline().strip()
|
private_key = file_object.readline().strip()
|
||||||
with open('public_key.txt', encoding='utf-8') as file_object:
|
with open('public_key.txt', encoding='utf-8') as file_object:
|
||||||
public_key = file_object.readline().strip()
|
public_key = file_object.readline().strip()
|
||||||
data = {"private_key": private_key, "public_key": public_key, "preshared_key": preshare_key}
|
data = {"private_key": private_key, "public_key": public_key}
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@ -867,8 +872,9 @@ def add_peer(config_name):
|
|||||||
return config_name + " is not running."
|
return config_name + " is not running."
|
||||||
if public_key in keys:
|
if public_key in keys:
|
||||||
return "Public key already exist."
|
return "Public key already exist."
|
||||||
check_dup_ip = g.cur.execute("SELECT COUNT(*) FROM " + config_name + " WHERE allowed_ip = ?",
|
check_dup_ip = g.cur.execute(
|
||||||
(allowed_ips,)).fetchone()
|
"SELECT COUNT(*) FROM " + config_name + " WHERE allowed_ip LIKE '" + allowed_ips + "%'", ) \
|
||||||
|
.fetchone()
|
||||||
if check_dup_ip[0] != 0:
|
if check_dup_ip[0] != 0:
|
||||||
return "Allowed IP already taken by another peer."
|
return "Allowed IP already taken by another peer."
|
||||||
if not check_DNS(dns_addresses):
|
if not check_DNS(dns_addresses):
|
||||||
@ -890,7 +896,6 @@ def add_peer(config_name):
|
|||||||
status = subprocess.check_output(f"wg set {config_name} peer {public_key} allowed-ips {allowed_ips}",
|
status = subprocess.check_output(f"wg set {config_name} peer {public_key} allowed-ips {allowed_ips}",
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
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)
|
||||||
|
|
||||||
get_all_peers_data(config_name)
|
get_all_peers_data(config_name)
|
||||||
sql = "UPDATE " + config_name + " SET name = ?, private_key = ?, DNS = ?, endpoint_allowed_ip = ? WHERE id = ?"
|
sql = "UPDATE " + config_name + " SET name = ?, private_key = ?, DNS = ?, endpoint_allowed_ip = ? WHERE id = ?"
|
||||||
g.cur.execute(sql, (data['name'], data['private_key'], data['DNS'], endpoint_allowed_ip, public_key))
|
g.cur.execute(sql, (data['name'], data['private_key'], data['DNS'], endpoint_allowed_ip, public_key))
|
||||||
@ -992,6 +997,35 @@ def get_peer_name(config_name):
|
|||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
|
# Return available IPs
|
||||||
|
@app.route('/available_ips/<config_name>', methods=['GET'])
|
||||||
|
def available_ips(config_name):
|
||||||
|
config_interface = read_conf_file_interface(config_name)
|
||||||
|
if "Address" in config_interface:
|
||||||
|
existed = []
|
||||||
|
conf_address = config_interface['Address']
|
||||||
|
address = conf_address.split(',')
|
||||||
|
for i in address:
|
||||||
|
add, sub = i.split("/")
|
||||||
|
existed.append(ipaddress.ip_address(add))
|
||||||
|
peers = g.cur.execute("SELECT allowed_ip FROM " + config_name).fetchall()
|
||||||
|
for i in peers:
|
||||||
|
add = i[0].split(",")
|
||||||
|
for k in add:
|
||||||
|
a, s = k.split("/")
|
||||||
|
existed.append(ipaddress.ip_address(a))
|
||||||
|
available = list(ipaddress.ip_network(address[0], False).hosts())
|
||||||
|
for i in existed:
|
||||||
|
try:
|
||||||
|
available.remove(i)
|
||||||
|
except ValueError as e:
|
||||||
|
pass
|
||||||
|
available = [str(i) for i in available]
|
||||||
|
return jsonify(available)
|
||||||
|
else:
|
||||||
|
return jsonify([])
|
||||||
|
|
||||||
|
|
||||||
# Generate a private key
|
# Generate a private key
|
||||||
@app.route('/generate_peer', methods=['GET'])
|
@app.route('/generate_peer', methods=['GET'])
|
||||||
def generate_peer():
|
def generate_peer():
|
||||||
@ -1018,8 +1052,9 @@ def check_key_match(config_name):
|
|||||||
@app.route("/qrcode/<config_name>", methods=['GET'])
|
@app.route("/qrcode/<config_name>", methods=['GET'])
|
||||||
def generate_qrcode(config_name):
|
def generate_qrcode(config_name):
|
||||||
peer_id = request.args.get('id')
|
peer_id = request.args.get('id')
|
||||||
get_peer = g.cur.execute("SELECT private_key, allowed_ip, DNS, mtu, endpoint_allowed_ip, keepalive, preshared_key FROM "
|
get_peer = g.cur.execute(
|
||||||
+ config_name + " WHERE id = ?", (peer_id,)).fetchall()
|
"SELECT private_key, allowed_ip, DNS, mtu, endpoint_allowed_ip, keepalive, preshared_key FROM "
|
||||||
|
+ config_name + " WHERE id = ?", (peer_id,)).fetchall()
|
||||||
config = get_dashboard_conf()
|
config = get_dashboard_conf()
|
||||||
if len(get_peer) == 1:
|
if len(get_peer) == 1:
|
||||||
peer = get_peer[0]
|
peer = get_peer[0]
|
||||||
@ -1041,7 +1076,6 @@ def generate_qrcode(config_name):
|
|||||||
+ str(keepalive) + "\nEndpoint = " + endpoint
|
+ str(keepalive) + "\nEndpoint = " + endpoint
|
||||||
if preshared_key != "":
|
if preshared_key != "":
|
||||||
result += "\nPresharedKey = " + preshared_key
|
result += "\nPresharedKey = " + preshared_key
|
||||||
|
|
||||||
return render_template("qrcode.html", i=result)
|
return render_template("qrcode.html", i=result)
|
||||||
else:
|
else:
|
||||||
return redirect("/configuration/" + config_name)
|
return redirect("/configuration/" + config_name)
|
||||||
@ -1054,7 +1088,7 @@ def download(config_name):
|
|||||||
peer_id = request.args.get('id')
|
peer_id = request.args.get('id')
|
||||||
get_peer = g.cur.execute(
|
get_peer = g.cur.execute(
|
||||||
"SELECT private_key, allowed_ip, DNS, mtu, endpoint_allowed_ip, keepalive, preshared_key, name FROM "
|
"SELECT private_key, allowed_ip, DNS, mtu, endpoint_allowed_ip, keepalive, preshared_key, name FROM "
|
||||||
+ config_name + " WHERE id = ?", (peer_id,)).fetchall()
|
+ config_name + " WHERE id = ?", (peer_id,)).fetchall()
|
||||||
config = get_dashboard_conf()
|
config = get_dashboard_conf()
|
||||||
if len(get_peer) == 1:
|
if len(get_peer) == 1:
|
||||||
peer = get_peer[0]
|
peer = get_peer[0]
|
||||||
@ -1090,9 +1124,10 @@ def download(config_name):
|
|||||||
|
|
||||||
def generate():
|
def generate():
|
||||||
yield "[Interface]\nPrivateKey = " + private_key + "\nAddress = " + allowed_ip + "\nDNS = " + \
|
yield "[Interface]\nPrivateKey = " + private_key + "\nAddress = " + allowed_ip + "\nDNS = " + \
|
||||||
dns_addresses + "\nMTU = " + str(mtu_value) + "\n\n[Peer]\nPublicKey = " + \
|
dns_addresses + "\nMTU = " + str(mtu_value) + "\n\n[Peer]\nPublicKey = " + \
|
||||||
public_key + "\nAllowedIPs = " + endpoint_allowed_ip + "\nEndpoint = " + \
|
public_key + "\nAllowedIPs = " + endpoint_allowed_ip + "\nEndpoint = " + \
|
||||||
endpoint + "\nPersistentKeepalive = " + str(keepalive) + psk
|
endpoint + "\nPersistentKeepalive = " + str(keepalive) + psk
|
||||||
|
|
||||||
return app.response_class(generate(), mimetype='text/conf',
|
return app.response_class(generate(), mimetype='text/conf',
|
||||||
headers={"Content-Disposition": "attachment;filename=" + filename + ".conf"})
|
headers={"Content-Disposition": "attachment;filename=" + filename + ".conf"})
|
||||||
return redirect("/configuration/" + config_name)
|
return redirect("/configuration/" + config_name)
|
||||||
|
@ -382,3 +382,15 @@ main{
|
|||||||
animation: loading 3s infinite ease-in-out;
|
animation: loading 3s infinite ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#qrcode_img img{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#selected_ip_list .badge{
|
||||||
|
margin: 0.1rem
|
||||||
|
}
|
||||||
|
|
||||||
|
#add_modal.ip_modal_open{
|
||||||
|
transition: filter 0.2s ease-in-out;
|
||||||
|
filter: brightness(0.5);
|
||||||
|
}
|
2
src/static/css/dashboard.min.css
vendored
2
src/static/css/dashboard.min.css
vendored
@ -1 +1 @@
|
|||||||
@-webkit-keyframes rotating{0%{-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotating{0%{-ms-transform:rotate(0deg);-moz-transform:rotate(0deg);-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}to{-ms-transform:rotate(360deg);-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes loading{0%,to{background-color:#dfdfdf}50%{background-color:#adadad}}@-moz-keyframes loading{0%,to{background-color:#dfdfdf}50%{background-color:#adadad}}body{font-size:.875rem}.feather{width:16px;height:16px;vertical-align:text-bottom}.sidebar{position:fixed;top:0;bottom:0;left:0;z-index:100;padding:48px 0 0;box-shadow:inset -1px 0 0 rgba(0,0,0,.1)}.sidebar-sticky{position:relative;top:0;height:calc(100vh - 48px);padding-top:.5rem;overflow-x:hidden;overflow-y:auto}@supports ((position:-webkit-sticky) or (position:sticky)){.sidebar-sticky{position:-webkit-sticky;position:sticky}}.sidebar .nav-link{font-weight:500;color:#333;transition:.2s cubic-bezier(.82,-.07,0,1.01)}.nav-link:hover{padding-left:30px}.sidebar .nav-link .feather{margin-right:4px;color:#999}.sidebar .nav-link.active{color:#007bff}.sidebar .nav-link.active .feather,.sidebar .nav-link:hover .feather{color:inherit}.sidebar-heading{font-size:.75rem;text-transform:uppercase}.navbar-brand{padding-top:.75rem;padding-bottom:.75rem;font-size:1rem;background-color:rgba(0,0,0,.25);box-shadow:inset -1px 0 0 rgba(0,0,0,.25)}.navbar .navbar-toggler{top:.25rem;right:1rem}.navbar .form-control{padding:.75rem 1rem;border-width:0;border-radius:0}.form-control-dark{color:#fff;background-color:rgba(255,255,255,.1);border-color:rgba(255,255,255,.1)}.form-control-dark:focus{border-color:transparent;box-shadow:0 0 0 3px rgba(255,255,255,.25)}.dot{width:10px;height:10px;border-radius:50px;display:inline-block;margin-left:10px}.dot-running{background-color:#28a745!important;box-shadow:0 0 0 .2rem #28a74545}.h6-dot-running{margin-left:.3rem}.dot-stopped{background-color:#6c757d!important}.card-running{border-color:#28a745}.info h6{line-break:anywhere;transition:.2s ease-in-out}.info .row .col-sm{display:flex;flex-direction:column}.info .row .col-sm small{display:flex}.info .row .col-sm small strong:last-child(1){margin-left:auto!important}.btn-control{border:0!important;padding:0 1rem 0 0}.btn-control:active,.btn-control:focus{background-color:transparent!important;border:0!important;box-shadow:none}.share_peer_btn_group .btn-control{padding:0 0 0 1rem}.btn-control:hover{background:#fff}.btn-delete-peer:hover{color:#dc3545}.btn-setting-peer:hover{color:#007bff}.btn-download-peer:hover{color:#17a2b8}.login-container{padding:2rem}@media (max-width:992px){.card-col{margin-bottom:1rem}}.switch{font-size:2rem}.switch:hover{text-decoration:none}.btn-group-label:hover{color:#007bff;border-color:#007bff;background:#fff}@media (max-width:768px){.peer_data_group{text-align:left}}.index-switch{text-align:right}main{margin-bottom:3rem}.peer_list{margin-bottom:7rem}@media (max-width:768px){.add_btn{bottom:1.5rem!important}.peer_list{margin-bottom:4rem!important}}.add_btn{position:fixed;bottom:3rem;right:2rem;z-index:99;border-radius:100px!important;padding:10px 20px;box-shadow:0 10px 20px rgba(0,0,0,.19),0 6px 6px rgba(0,0,0,.23)}.rotating::before{-webkit-animation:rotating .75s linear infinite;-moz-animation:rotating .75s linear infinite;-ms-animation:rotating .75s linear infinite;-o-animation:rotating .75s linear infinite;animation:rotating .75s linear infinite}.peer_private_key_textbox_switch{position:absolute;right:2rem;transform:translateY(-28px);font-size:1.2rem;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:.3s ease-in-out}.key{transition:.2s ease-in-out;cursor:pointer}.key:hover{color:#007bff}.card,.form-control{border-radius:10px}.peer_list .card .button-group{height:22px}.btn{border-radius:8px}.modal-content{border-radius:10px}.tooltip-inner{font-size:.8rem}#conf_status_btn,.conf_card{transition:.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:.3s ease-in-out}#conf_status_btn.info_loading{height:38px;border-radius:5px;animation:loading 3s infinite ease-in-out}
|
@-webkit-keyframes rotating{0%{-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotating{0%{-ms-transform:rotate(0deg);-moz-transform:rotate(0deg);-webkit-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}to{-ms-transform:rotate(360deg);-moz-transform:rotate(360deg);-webkit-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes loading{0%,to{background-color:#dfdfdf}50%{background-color:#adadad}}@-moz-keyframes loading{0%,to{background-color:#dfdfdf}50%{background-color:#adadad}}body{font-size:.875rem}.feather{width:16px;height:16px;vertical-align:text-bottom}.sidebar{position:fixed;top:0;bottom:0;left:0;z-index:100;padding:48px 0 0;box-shadow:inset -1px 0 0 rgba(0,0,0,.1)}.sidebar-sticky{position:relative;top:0;height:calc(100vh - 48px);padding-top:.5rem;overflow-x:hidden;overflow-y:auto}@supports ((position:-webkit-sticky) or (position:sticky)){.sidebar-sticky{position:-webkit-sticky;position:sticky}}.sidebar .nav-link{font-weight:500;color:#333;transition:.2s cubic-bezier(.82,-.07,0,1.01)}.nav-link:hover{padding-left:30px}.sidebar .nav-link .feather{margin-right:4px;color:#999}.sidebar .nav-link.active{color:#007bff}.sidebar .nav-link.active .feather,.sidebar .nav-link:hover .feather{color:inherit}.sidebar-heading{font-size:.75rem;text-transform:uppercase}.navbar-brand{padding-top:.75rem;padding-bottom:.75rem;font-size:1rem;background-color:rgba(0,0,0,.25);box-shadow:inset -1px 0 0 rgba(0,0,0,.25)}.navbar .navbar-toggler{top:.25rem;right:1rem}.navbar .form-control{padding:.75rem 1rem;border-width:0;border-radius:0}.form-control-dark{color:#fff;background-color:rgba(255,255,255,.1);border-color:rgba(255,255,255,.1)}.form-control-dark:focus{border-color:transparent;box-shadow:0 0 0 3px rgba(255,255,255,.25)}.dot{width:10px;height:10px;border-radius:50px;display:inline-block;margin-left:10px}.dot-running{background-color:#28a745!important;box-shadow:0 0 0 .2rem #28a74545}.h6-dot-running{margin-left:.3rem}.dot-stopped{background-color:#6c757d!important}.card-running{border-color:#28a745}.info h6{line-break:anywhere;transition:.2s ease-in-out}.info .row .col-sm{display:flex;flex-direction:column}.info .row .col-sm small{display:flex}.info .row .col-sm small strong:last-child(1){margin-left:auto!important}.btn-control{border:0!important;padding:0 1rem 0 0}.btn-control:active,.btn-control:focus{background-color:transparent!important;border:0!important;box-shadow:none}.share_peer_btn_group .btn-control{padding:0 0 0 1rem}.btn-control:hover{background:#fff}.btn-delete-peer:hover{color:#dc3545}.btn-setting-peer:hover{color:#007bff}.btn-download-peer:hover{color:#17a2b8}.login-container{padding:2rem}@media (max-width:992px){.card-col{margin-bottom:1rem}}.switch{font-size:2rem}.switch:hover{text-decoration:none}.btn-group-label:hover{color:#007bff;border-color:#007bff;background:#fff}@media (max-width:768px){.peer_data_group{text-align:left}}.index-switch{text-align:right}main{margin-bottom:3rem}.peer_list{margin-bottom:7rem}@media (max-width:768px){.add_btn{bottom:1.5rem!important}.peer_list{margin-bottom:4rem!important}}.add_btn{position:fixed;bottom:3rem;right:2rem;z-index:99;border-radius:100px!important;padding:10px 20px;box-shadow:0 10px 20px rgba(0,0,0,.19),0 6px 6px rgba(0,0,0,.23)}.rotating::before{-webkit-animation:rotating .75s linear infinite;-moz-animation:rotating .75s linear infinite;-ms-animation:rotating .75s linear infinite;-o-animation:rotating .75s linear infinite;animation:rotating .75s linear infinite}.peer_private_key_textbox_switch{position:absolute;right:2rem;transform:translateY(-28px);font-size:1.2rem;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:.3s ease-in-out}.key{transition:.2s ease-in-out;cursor:pointer}.key:hover{color:#007bff}.card,.form-control{border-radius:10px}.peer_list .card .button-group{height:22px}.btn{border-radius:8px}.modal-content{border-radius:10px}.tooltip-inner{font-size:.8rem}#conf_status_btn,.conf_card{transition:.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:.3s ease-in-out}#conf_status_btn.info_loading{height:38px;border-radius:5px;animation:loading 3s infinite ease-in-out}#qrcode_img img{width:100%}#selected_ip_list .badge{margin:.1rem}#add_modal.ip_modal_open{transition:filter .2s ease-in-out;filter:brightness(.5)}
|
@ -1,12 +1,27 @@
|
|||||||
|
$("[data-toggle='tooltip']").tooltip()
|
||||||
let $body = $("body");
|
let $body = $("body");
|
||||||
|
let $progress_bar = $(".progress-bar");
|
||||||
|
let available_ips = [];
|
||||||
|
let $save_peer = $("#save_peer");
|
||||||
|
|
||||||
|
$(".add_btn").on("click", function(){
|
||||||
|
addModal.toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Round Transfer number into 4 digits
|
||||||
|
* @param value
|
||||||
|
* @param digits
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
function roundN(value, digits) {
|
function roundN(value, digits) {
|
||||||
let tenToN = 10 ** digits;
|
let tenToN = 10 ** digits;
|
||||||
return (Math.round(value * tenToN)) / tenToN;
|
return (Math.round(value * tenToN)) / tenToN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Progress Bar
|
/**
|
||||||
let $progress_bar = $(".progress-bar");
|
* Start Progress Bar
|
||||||
|
*/
|
||||||
function startProgressBar(){
|
function startProgressBar(){
|
||||||
$progress_bar.css("width","0%")
|
$progress_bar.css("width","0%")
|
||||||
.css("opacity", "100")
|
.css("opacity", "100")
|
||||||
@ -19,10 +34,16 @@ function startProgressBar(){
|
|||||||
},300);
|
},300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Still Loading Progress Bar
|
||||||
|
*/
|
||||||
function stillLoadingProgressBar(){
|
function stillLoadingProgressBar(){
|
||||||
$progress_bar.css("transition", "3s ease-in-out").css("width", "75%");
|
$progress_bar.css("transition", "3s ease-in-out").css("width", "75%");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End Progress Bae
|
||||||
|
*/
|
||||||
function endProgressBar(){
|
function endProgressBar(){
|
||||||
$progress_bar.css("transition", "0.3s ease-in-out").css("width","100%");
|
$progress_bar.css("transition", "0.3s ease-in-out").css("width","100%");
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
@ -31,20 +52,27 @@ function endProgressBar(){
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show toast
|
||||||
|
* @param msg
|
||||||
|
*/
|
||||||
function showToast(msg) {
|
function showToast(msg) {
|
||||||
$('#alertToast').toast('show');
|
$('#alertToast').toast('show');
|
||||||
$('#alertToast .toast-body').html(msg);
|
$('#alertToast .toast-body').html(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
// Config Toggle
|
* When configuration switch got click
|
||||||
|
*/
|
||||||
$body.on("click", ".switch", function (){
|
$body.on("click", ".switch", function (){
|
||||||
$(this).siblings($(".spinner-border")).css("display", "inline-block");
|
$(this).siblings($(".spinner-border")).css("display", "inline-block");
|
||||||
$(this).remove();
|
$(this).remove();
|
||||||
location.replace("/switch/"+$(this).attr('id'));
|
location.replace("/switch/"+$(this).attr('id'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generating Keys
|
/**
|
||||||
|
* Generate Private and Public key for a new peer
|
||||||
|
*/
|
||||||
function generate_key(){
|
function generate_key(){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
"url": "/generate_peer",
|
"url": "/generate_peer",
|
||||||
@ -52,11 +80,14 @@ function generate_key(){
|
|||||||
}).done(function(res){
|
}).done(function(res){
|
||||||
$("#private_key").val(res.private_key);
|
$("#private_key").val(res.private_key);
|
||||||
$("#public_key").val(res.public_key);
|
$("#public_key").val(res.public_key);
|
||||||
$("#preshare_key").val(res.preshared_key);
|
|
||||||
$("#add_peer_alert").addClass("d-none");
|
$("#add_peer_alert").addClass("d-none");
|
||||||
$("#re_generate_key i").removeClass("rotating");
|
$("#re_generate_key i").removeClass("rotating");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate public for existing peer
|
||||||
|
*/
|
||||||
function generate_public_key(){
|
function generate_public_key(){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
"url": "/generate_public_key",
|
"url": "/generate_public_key",
|
||||||
@ -70,11 +101,13 @@ function generate_public_key(){
|
|||||||
$("#add_peer_alert").addClass("d-none");
|
$("#add_peer_alert").addClass("d-none");
|
||||||
}
|
}
|
||||||
$("#public_key").val(res.data);
|
$("#public_key").val(res.data);
|
||||||
$("#re_generate_key i").removeClass("rotating");
|
$("#re_generate_key i").removeClass("rotating");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Peer
|
/**
|
||||||
|
* Generate Public key when private got change
|
||||||
|
*/
|
||||||
$("#private_key").on("change",function(){
|
$("#private_key").on("change",function(){
|
||||||
if ($(this).val().length > 0){
|
if ($(this).val().length > 0){
|
||||||
$("#re_generate_key i").addClass("rotating");
|
$("#re_generate_key i").addClass("rotating");
|
||||||
@ -84,33 +117,144 @@ $("#private_key").on("change",function(){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger IP badge and item
|
||||||
|
* @param ip
|
||||||
|
*/
|
||||||
|
function trigger_ip(ip){
|
||||||
|
let $ip_ele = $(".available-ip-item[data-ip='"+ip+"']");
|
||||||
|
if ($ip_ele.html()){
|
||||||
|
if ($ip_ele.hasClass("active")){
|
||||||
|
$ip_ele.removeClass("active");
|
||||||
|
$("#selected_ip_list .badge[data-ip='"+ip+"']").remove();
|
||||||
|
}else{
|
||||||
|
$ip_ele.addClass("active");
|
||||||
|
$("#selected_ip_list").append('<span class="badge badge-primary available-ip-badge" style="cursor: pointer" data-ip="'+ip+'">'+ip+'</span>')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available IP for this configuration
|
||||||
|
*/
|
||||||
|
function get_available_ip(){
|
||||||
|
$.ajax({
|
||||||
|
"url": "/available_ips/"+$save_peer.attr("conf_id"),
|
||||||
|
"method": "GET",
|
||||||
|
}).done(function (res) {
|
||||||
|
available_ips = res;
|
||||||
|
let $list_group = $("#available_ip_modal .modal-body .list-group");
|
||||||
|
$list_group.html("");
|
||||||
|
$("#allowed_ips").val(available_ips[0]);
|
||||||
|
available_ips.forEach((ip) =>
|
||||||
|
$list_group.append('<a class="list-group-item list-group-item-action available-ip-item" style="cursor: pointer" data-ip="'+ip+'">'+ip+'</a>'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#available_ip_modal").on("show.bs.modal", () => {
|
||||||
|
$('#add_modal').addClass("ip_modal_open");
|
||||||
|
}).on("hidden.bs.modal", function () {
|
||||||
|
$('#add_modal').removeClass("ip_modal_open");
|
||||||
|
let ips = [];
|
||||||
|
let $selected_ip_list = $("#selected_ip_list");
|
||||||
|
$selected_ip_list.children().each(function(){
|
||||||
|
ips.push($(this).data("ip"));
|
||||||
|
});
|
||||||
|
ips.forEach((ele) => trigger_ip(ele));
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When IP Badge got click
|
||||||
|
*/
|
||||||
|
$body.on("click", ".available-ip-badge", function(){
|
||||||
|
$(".available-ip-item[data-ip='"+$(this).data("ip")+"']").removeClass("active");
|
||||||
|
$(this).remove();
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When available ip item got click
|
||||||
|
*/
|
||||||
|
$body.on("click", ".available-ip-item", function () {
|
||||||
|
trigger_ip($(this).data("ip"));
|
||||||
|
});
|
||||||
|
|
||||||
|
let $ipModal = new bootstrap.Modal(document.getElementById('available_ip_modal'), {
|
||||||
|
keyboard: false
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#search_available_ip").on("click", function () {
|
||||||
|
$ipModal.toggle();
|
||||||
|
let $allowed_ips = $("#allowed_ips");
|
||||||
|
if ($allowed_ips.val().length > 0){
|
||||||
|
let s = $allowed_ips.val().split(",");
|
||||||
|
for (let i = 0; i < s.length; i++){
|
||||||
|
s[i] = s[i].trim();
|
||||||
|
trigger_ip(s[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).tooltip();
|
||||||
|
|
||||||
|
$("#confirm_ip").on("click", () => {
|
||||||
|
$ipModal.toggle();
|
||||||
|
let ips = [];
|
||||||
|
let $selected_ip_list = $("#selected_ip_list");
|
||||||
|
$selected_ip_list.children().each(function(){
|
||||||
|
ips.push($(this).data("ip"));
|
||||||
|
});
|
||||||
|
$("#allowed_ips").val(ips.join(", "));
|
||||||
|
ips.forEach((ele) => trigger_ip(ele));
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#allowed_ips").on("keyup", function(){
|
||||||
|
let s = clean_ip($(this).val());
|
||||||
|
s = s.split(",");
|
||||||
|
if (available_ips.includes(s[s.length - 1])){
|
||||||
|
$("#allowed_ips_indicator").removeClass().addClass("text-success")
|
||||||
|
.html('<i class="bi bi-check-circle-fill"></i>');
|
||||||
|
}else{
|
||||||
|
$("#allowed_ips_indicator").removeClass().addClass("text-warning")
|
||||||
|
.html('<i class="bi bi-exclamation-circle-fill"></i>');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
$('#add_modal').on('show.bs.modal', function (event) {
|
$('#add_modal').on('show.bs.modal', function (event) {
|
||||||
generate_key();
|
generate_key();
|
||||||
|
get_available_ip();
|
||||||
|
}).on('hide.bs.modal', function(){
|
||||||
|
$("#allowed_ips_indicator").html('');
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#re_generate_key").on("click",function (){
|
$("#re_generate_key").on("click",function (){
|
||||||
$("#public_key").attr("disabled","disabled");
|
$("#public_key").attr("disabled","disabled");
|
||||||
$("#re_generate_key i").addClass("rotating");
|
$("#re_generate_key i").addClass("rotating");
|
||||||
generate_key();
|
generate_key();
|
||||||
});
|
});
|
||||||
|
|
||||||
let addModal = new bootstrap.Modal(document.getElementById('add_modal'), {
|
let addModal = new bootstrap.Modal(document.getElementById('add_modal'), {
|
||||||
keyboard: false
|
keyboard: false
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".add_btn").on("click", function(){
|
function clean_ip(val){
|
||||||
addModal.toggle();
|
let clean_ip = val.split(',');
|
||||||
});
|
for (let i = 0; i < clean_ip.length; i++) clean_ip[i] = clean_ip[i].trim(' ');
|
||||||
|
return clean_ip.filter(Boolean).join(",");
|
||||||
|
}
|
||||||
|
|
||||||
$("#save_peer").on("click",function(){
|
$save_peer.on("click",function(){
|
||||||
let $public_key = $("#public_key");
|
let $public_key = $("#public_key");
|
||||||
let $private_key = $("#private_key");
|
let $private_key = $("#private_key");
|
||||||
let $allowed_ips = $("#allowed_ips");
|
let $allowed_ips = $("#allowed_ips");
|
||||||
|
$allowed_ips.val(clean_ip($allowed_ips.val()));
|
||||||
let $new_add_DNS = $("#new_add_DNS");
|
let $new_add_DNS = $("#new_add_DNS");
|
||||||
|
$new_add_DNS.val(clean_ip($new_add_DNS.val()));
|
||||||
let $new_add_endpoint_allowed_ip = $("#new_add_endpoint_allowed_ip");
|
let $new_add_endpoint_allowed_ip = $("#new_add_endpoint_allowed_ip");
|
||||||
|
$new_add_endpoint_allowed_ip.val(clean_ip($new_add_endpoint_allowed_ip.val()));
|
||||||
let $new_add_name = $("#new_add_name");
|
let $new_add_name = $("#new_add_name");
|
||||||
let $new_add_MTU = $("#new_add_MTU");
|
let $new_add_MTU = $("#new_add_MTU");
|
||||||
let $new_add_keep_alive = $("#new_add_keep_alive");
|
let $new_add_keep_alive = $("#new_add_keep_alive");
|
||||||
let $enable_preshare_key = $("#enable_preshare_key");
|
let $enable_preshare_key = $("#enable_preshare_key");
|
||||||
|
let p_key = $public_key.val()
|
||||||
$(this).attr("disabled","disabled");
|
$(this).attr("disabled","disabled");
|
||||||
$(this).html("Saving...");
|
$(this).html("Saving...");
|
||||||
if ($allowed_ips.val() !== "" && $public_key.val() !== "" && $new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
|
if ($allowed_ips.val() !== "" && $public_key.val() !== "" && $new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
|
||||||
@ -138,10 +282,14 @@ $("#save_peer").on("click",function(){
|
|||||||
if(response !== "true"){
|
if(response !== "true"){
|
||||||
$("#add_peer_alert").html(response).removeClass("d-none");
|
$("#add_peer_alert").html(response).removeClass("d-none");
|
||||||
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
||||||
$("#save_peer").removeAttr("disabled").html("Save");
|
$save_peer.removeAttr("disabled").html("Save");
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
load_data("");
|
load_data("");
|
||||||
|
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
||||||
|
$("#add_peer_form").trigger("reset");
|
||||||
|
$save_peer.removeAttr("disabled").html("Save");
|
||||||
|
showToast("Add peer successful!");
|
||||||
addModal.toggle();
|
addModal.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,6 +307,8 @@ let qrcodeModal = new bootstrap.Modal(document.getElementById('qrcode_modal'), {
|
|||||||
});
|
});
|
||||||
// QR Code
|
// QR Code
|
||||||
$body.on("click", ".btn-qrcode-peer", function (){
|
$body.on("click", ".btn-qrcode-peer", function (){
|
||||||
|
|
||||||
|
|
||||||
let src = $(this).attr('img_src');
|
let src = $(this).attr('img_src');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
"url": src,
|
"url": src,
|
||||||
@ -326,7 +476,6 @@ $(".peer_private_key_textbox_switch").on("click",function (){
|
|||||||
$(".peer_private_key_textbox_switch i").removeClass().addClass(icon);
|
$(".peer_private_key_textbox_switch i").removeClass().addClass(icon);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Search Peer
|
// Search Peer
|
||||||
let typingTimer;
|
let typingTimer;
|
||||||
let doneTypingInterval = 200;
|
let doneTypingInterval = 200;
|
||||||
@ -335,14 +484,15 @@ $input.on('keyup', function () {
|
|||||||
clearTimeout(typingTimer);
|
clearTimeout(typingTimer);
|
||||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||||
});
|
});
|
||||||
|
|
||||||
$input.on('keydown', function () {
|
$input.on('keydown', function () {
|
||||||
clearTimeout(typingTimer);
|
clearTimeout(typingTimer);
|
||||||
});
|
});
|
||||||
|
|
||||||
function doneTyping () {
|
function doneTyping () {
|
||||||
load_data($input.val());
|
load_data($input.val());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Sorting
|
// Sorting
|
||||||
$body.on("change", "#sort_by_dropdown", function (){
|
$body.on("change", "#sort_by_dropdown", function (){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -360,16 +510,13 @@ $body.on("change", "#sort_by_dropdown", function (){
|
|||||||
$body.on("mouseenter", ".key", function(){
|
$body.on("mouseenter", ".key", function(){
|
||||||
let label = $(this).parent().siblings().children()[1];
|
let label = $(this).parent().siblings().children()[1];
|
||||||
label.style.opacity = "100";
|
label.style.opacity = "100";
|
||||||
})
|
}).on("mouseout", ".key", function(){
|
||||||
$body.on("mouseout", ".key", function(){
|
|
||||||
let label = $(this).parent().siblings().children()[1];
|
let label = $(this).parent().siblings().children()[1];
|
||||||
label.style.opacity = "0";
|
label.style.opacity = "0";
|
||||||
setTimeout(function (){
|
setTimeout(function (){
|
||||||
label.innerHTML = "CLICK TO COPY";
|
label.innerHTML = "CLICK TO COPY";
|
||||||
},200);
|
},200);
|
||||||
});
|
}).on("click", ".key", function(){
|
||||||
|
|
||||||
$body.on("click", ".key", function(){
|
|
||||||
var label = $(this).parent().siblings().children()[1];
|
var label = $(this).parent().siblings().children()[1];
|
||||||
copyToClipboard($(this));
|
copyToClipboard($(this));
|
||||||
label.innerHTML = "COPIED!";
|
label.innerHTML = "COPIED!";
|
||||||
@ -420,7 +567,6 @@ $body.on("click", ".refresh", function (){
|
|||||||
load_data($('#search_peer_textbox').val());
|
load_data($('#search_peer_textbox').val());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Switch display mode
|
// Switch display mode
|
||||||
$body.on("click", ".display_mode", function(){
|
$body.on("click", ".display_mode", function(){
|
||||||
$(".display-btn-group button").removeClass("active");
|
$(".display-btn-group button").removeClass("active");
|
||||||
|
2
src/static/js/configuration.min.js
vendored
2
src/static/js/configuration.min.js
vendored
File diff suppressed because one or more lines are too long
@ -83,7 +83,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label><small class="text-muted">Refresh Interval</small></label><br>
|
<label><small class="text-muted">Refresh Interval</small></label><br>
|
||||||
<div class="btn-group interval-btn-group" role="group" style="width: 100%">
|
<div class="btn-group interval-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 btn-group-label refresh" data-toggle="tooltip" data-placement="bottom" title="Refresh Peers"><i class="bi bi-arrow-repeat"></i></button>
|
||||||
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="5000">5s</button>
|
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="5000">5s</button>
|
||||||
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="10000">10s</button>
|
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="10000">10s</button>
|
||||||
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="30000">30s</button>
|
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="30000">30s</button>
|
||||||
@ -109,6 +109,7 @@
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" id="add_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
|
<div class="modal fade" id="add_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
|
||||||
aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||||
@ -136,7 +137,7 @@
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" id="private_key" aria-describedby="private_key">
|
<input type="text" class="form-control" id="private_key" aria-describedby="private_key">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button type="button" class="btn btn-danger" id="re_generate_key"><i class="bi bi-arrow-repeat"></i></button>
|
<button type="button" class="btn btn-danger" id="re_generate_key" data-toggle="tooltip" data-placement="top" title="Regenerate Key"><i class="bi bi-arrow-repeat"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -154,7 +155,15 @@
|
|||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="allowed_ips">Allowed IPs <code>(Required)</code></label>
|
<label for="allowed_ips">Allowed IPs <code>(Required)</code></label>
|
||||||
<input type="text" class="form-control" id="allowed_ips">
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" id="allowed_ips">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button type="button" class="btn btn-primary" id="search_available_ip" data-toggle="tooltip" data-placement="top" title="Search Available IPs">
|
||||||
|
<i class="bi bi-search"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p style="position: absolute; top: 4px; right: 1rem;" class="text-success" id="allowed_ips_indicator"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
@ -296,6 +305,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal fade" id="available_ip_modal" data-backdrop="static" data-keyboard="false" tabindex="1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="staticBackdropLabel">Select available IP</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="selected_ip" style="padding: 1rem; border-bottom: 1px solid #dee2e6;">
|
||||||
|
<small class="text-muted"><strong>SELECTED IP (CLICK TO REMOVE)</strong></small>
|
||||||
|
<div id="selected_ip_list">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" style="max-height: 400px; overflow-y: scroll;">
|
||||||
|
<div class="list-group"></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="confirm_ip">Confirm</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" id="qrcode_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
|
<div class="modal fade" id="qrcode_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
|
||||||
aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||||
@ -308,7 +342,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<img src="" id="qrcode_img" style="width: 100%">
|
<img id="qrcode_img" style="width: 100%">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -330,6 +364,7 @@
|
|||||||
</body>
|
</body>
|
||||||
{% include "footer.html" %}
|
{% include "footer.html" %}
|
||||||
<script src="{{ url_for('static',filename='js/configuration.min.js') }}"></script>
|
<script src="{{ url_for('static',filename='js/configuration.min.js') }}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let load_timeout;
|
let load_timeout;
|
||||||
let load_interval = 0;
|
let load_interval = 0;
|
||||||
@ -400,12 +435,12 @@
|
|||||||
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_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>';
|
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>';
|
||||||
let peer_endpoint = '<div class="col-sm"><small class="text-muted"><strong>END POINT</strong></small><h6 style="text-transform: uppercase;">'+peer["endpoint"]+'</h6></div>';
|
let peer_endpoint = '<div class="col-sm"><small class="text-muted"><strong>END POINT</strong></small><h6 style="text-transform: uppercase;">'+peer["endpoint"]+'</h6></div>';
|
||||||
let peer_control = '<div class="col-sm"><hr><div class="button-group" style="display:flex"> <button type="button" class="btn btn-outline-primary btn-setting-peer btn-control" id="'+peer["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="'+peer["id"]+'" data-toggle="modal"><i class="bi bi-x-circle-fill"></i></button>';
|
let peer_control = '<div class="col-sm"><hr><div class="button-group" style="display:flex"><button type="button" class="btn btn-outline-primary btn-setting-peer btn-control" id="'+peer["id"]+'" data-toggle="modal"><i class="bi bi-gear-fill" data-toggle="tooltip" data-placement="bottom" title="Peer Settings"></i></button> <button type="button" class="btn btn-outline-danger btn-delete-peer btn-control" id="'+peer["id"]+'" data-toggle="modal"><i class="bi bi-x-circle-fill" data-toggle="tooltip" data-placement="bottom" title="Delete Peer"></i></button>';
|
||||||
if (peer["private_key"] !== ""){
|
if (peer["private_key"] !== ""){
|
||||||
peer_control += '<div class="share_peer_btn_group" style="margin-left: auto !important; display: inline"><button type="button" class="btn btn-outline-success btn-qrcode-peer btn-control" img_src="/qrcode/'+response['name']+'?id='+encodeURIComponent(peer["id"])+'"><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/'+response["name"]+'?id='+encodeURIComponent(peer["id"])+'" class="btn btn-outline-info btn-download-peer btn-control"><i class="bi bi-download"></i></a></div>';
|
peer_control += '<div class="share_peer_btn_group" style="margin-left: auto !important; display: inline"><button type="button" class="btn btn-outline-success btn-qrcode-peer btn-control" img_src="/qrcode/'+response['name']+'?id='+encodeURIComponent(peer["id"])+'"><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/'+response["name"]+'?id='+encodeURIComponent(peer["id"])+'" class="btn btn-outline-info btn-download-peer btn-control"><i class="bi bi-download"></i></a></div>';
|
||||||
}
|
}
|
||||||
peer_control += '</div>';
|
peer_control += '</div>';
|
||||||
let html = '<div class="'+display_mode+'">' +
|
let html = '<div class="'+display_mode+'" data-id="'+peer["id"]+'">' +
|
||||||
'<div class="card mb-3 card-'+peer["status"]+'">' +
|
'<div class="card mb-3 card-'+peer["status"]+'">' +
|
||||||
'<div class="card-body">' +
|
'<div class="card-body">' +
|
||||||
'<div class="row">'
|
'<div class="row">'
|
||||||
@ -436,6 +471,7 @@
|
|||||||
{#$("#config_body").html(response);#}
|
{#$("#config_body").html(response);#}
|
||||||
$(".dot.dot-running").attr("title","Peer Running").tooltip();
|
$(".dot.dot-running").attr("title","Peer Running").tooltip();
|
||||||
$(".dot.dot-stopped").attr("title","Peer Stopped").tooltip();
|
$(".dot.dot-stopped").attr("title","Peer Stopped").tooltip();
|
||||||
|
$("i[data-toggle='tooltip']").tooltip();
|
||||||
{#$("#search_peer_textbox").css("display", "block")#}
|
{#$("#search_peer_textbox").css("display", "block")#}
|
||||||
endProgressBar()
|
endProgressBar()
|
||||||
}
|
}
|
||||||
|
@ -78,3 +78,4 @@ def check_DNS(dns):
|
|||||||
def check_remote_endpoint(address):
|
def check_remote_endpoint(address):
|
||||||
return (check_IP(address) or regex_match("(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]",
|
return (check_IP(address) or regex_match("(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]",
|
||||||
address))
|
address))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user