mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-04 15:00:23 +01:00
Feature Added
Moved key generating to front-end, by using wireguard.js from WireGuard's official repository. Added "Add Peers by bulk" feature.
This commit is contained in:
parent
77a82cb84b
commit
584118805a
130
src/dashboard.py
130
src/dashboard.py
@ -464,13 +464,41 @@ def check_repeat_allowed_ip(public_key, ip, config_name):
|
||||
return {'status': 'failed', 'msg': 'Peer does not exist'}
|
||||
else:
|
||||
existed_ip = g.cur.execute("SELECT COUNT(*) FROM " +
|
||||
config_name + " WHERE id != ? AND allowed_ip = ?", (public_key, ip)).fetchone()
|
||||
config_name + " WHERE id != ? AND allowed_ip LIKE '" + ip + "/%'", (public_key,))\
|
||||
.fetchone()
|
||||
if existed_ip[0] != 0:
|
||||
return {'status': 'failed', 'msg': "Allowed IP already taken by another peer."}
|
||||
else:
|
||||
return {'status': 'success'}
|
||||
|
||||
|
||||
def f_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 available
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
"""
|
||||
Flask Functions
|
||||
"""
|
||||
@ -830,8 +858,6 @@ def get_conf(config_name):
|
||||
conf_data['checked'] = "checked"
|
||||
config.clear()
|
||||
return jsonify(conf_data)
|
||||
# return render_template('get_conf.html', conf_data=conf_data, wg_ip=config.get("Peers","remote_endpoint"), sort_tag=sort,
|
||||
# dashboard_refresh_interval=int(config.get("Server", "dashboard_refresh_interval")), peer_display_mode=peer_display_mode)
|
||||
|
||||
|
||||
# Turn on / off a configuration
|
||||
@ -856,6 +882,64 @@ def switch(config_name):
|
||||
return redirect(request.referrer)
|
||||
|
||||
|
||||
@app.route('/add_peer_bulk/<config_name>', methods=['POST'])
|
||||
def add_peer_bulk(config_name):
|
||||
data = request.get_json()
|
||||
keys = data['keys']
|
||||
endpoint_allowed_ip = data['endpoint_allowed_ip']
|
||||
dns_addresses = data['DNS']
|
||||
enable_preshared_key = data["enable_preshared_key"]
|
||||
amount = data['amount']
|
||||
if not amount.isdigit() or int(amount) < 1:
|
||||
return "Amount must be integer larger than 0"
|
||||
amount = int(amount)
|
||||
if not check_DNS(dns_addresses):
|
||||
return "DNS formate is incorrect. Example: 1.1.1.1"
|
||||
if not check_Allowed_IPs(endpoint_allowed_ip):
|
||||
return "Endpoint Allowed IPs format is incorrect."
|
||||
if len(data['MTU']) == 0 or not data['MTU'].isdigit():
|
||||
return "MTU format is not correct."
|
||||
if len(data['keep_alive']) == 0 or not data['keep_alive'].isdigit():
|
||||
return "Persistent Keepalive format is not correct."
|
||||
ips = f_available_ips(config_name)
|
||||
wg_command = ["wg", "set", config_name]
|
||||
sql_command = []
|
||||
for i in range(amount):
|
||||
keys[i]['name'] = f"{config_name}_{datetime.now().strftime('%m%d%Y%H%M%S')}_Peer_#_{(i + 1)}"
|
||||
wg_command.append("peer")
|
||||
wg_command.append(keys[i]['publicKey'])
|
||||
keys[i]['allowed_ips'] = ips.pop(0)
|
||||
if enable_preshared_key:
|
||||
keys[i]['psk_file'] = f"{keys[i]['name']}.txt"
|
||||
f = open(keys[i]['psk_file'], "w+")
|
||||
f.write(keys[i]['presharedKey'])
|
||||
f.close()
|
||||
wg_command.append("preshared-key")
|
||||
wg_command.append(keys[i]['psk_file'])
|
||||
else:
|
||||
keys[i]['psk_file'] = ""
|
||||
wg_command.append("allowed-ips")
|
||||
wg_command.append(keys[i]['allowed_ips'])
|
||||
update = ["UPDATE ", config_name, " SET name = '", keys[i]['name'],
|
||||
"', private_key = '", keys[i]['privateKey'], "', DNS = '", dns_addresses,
|
||||
"', endpoint_allowed_ip = '", endpoint_allowed_ip, "' WHERE id = '", keys[i]['publicKey'], "'"]
|
||||
sql_command.append(update)
|
||||
try:
|
||||
status = subprocess.check_output(" ".join(wg_command), 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)
|
||||
if enable_preshared_key:
|
||||
for i in keys:
|
||||
os.remove(i['psk_file'])
|
||||
for i in range(len(sql_command)):
|
||||
sql_command[i] = "".join(sql_command[i])
|
||||
g.cur.executescript("; ".join(sql_command))
|
||||
return "true"
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return exc.output.strip()
|
||||
|
||||
|
||||
|
||||
# Add peer
|
||||
@app.route('/add_peer/<config_name>', methods=['POST'])
|
||||
def add_peer(config_name):
|
||||
@ -865,6 +949,7 @@ def add_peer(config_name):
|
||||
endpoint_allowed_ip = data['endpoint_allowed_ip']
|
||||
dns_addresses = data['DNS']
|
||||
enable_preshared_key = data["enable_preshared_key"]
|
||||
preshared_key = data['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:
|
||||
return "Please fill in all required box."
|
||||
@ -873,7 +958,7 @@ def add_peer(config_name):
|
||||
if public_key in keys:
|
||||
return "Public key already exist."
|
||||
check_dup_ip = g.cur.execute(
|
||||
"SELECT COUNT(*) FROM " + config_name + " WHERE allowed_ip LIKE '" + allowed_ips + "%'", ) \
|
||||
"SELECT COUNT(*) FROM " + config_name + " WHERE allowed_ip LIKE '" + allowed_ips + "/%'", ) \
|
||||
.fetchone()
|
||||
if check_dup_ip[0] != 0:
|
||||
return "Allowed IP already taken by another peer."
|
||||
@ -887,11 +972,16 @@ def add_peer(config_name):
|
||||
return "Persistent Keepalive format is not correct."
|
||||
try:
|
||||
if enable_preshared_key:
|
||||
key = subprocess.check_output("wg genpsk > tmp_psk.txt", shell=True)
|
||||
now = str(datetime.now().strftime("%m%d%Y%H%M%S"))
|
||||
f_name = now + "_tmp_psk.txt"
|
||||
print(f_name)
|
||||
f = open(f_name, "w+")
|
||||
f.write(preshared_key)
|
||||
f.close()
|
||||
status = subprocess.check_output(
|
||||
f"wg set {config_name} peer {public_key} allowed-ips {allowed_ips} preshared-key tmp_psk.txt",
|
||||
f"wg set {config_name} peer {public_key} allowed-ips {allowed_ips} preshared-key {f_name}",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
os.remove("tmp_psk.txt")
|
||||
os.remove(f_name)
|
||||
elif not enable_preshared_key:
|
||||
status = subprocess.check_output(f"wg set {config_name} peer {public_key} allowed-ips {allowed_ips}",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
@ -924,6 +1014,7 @@ def remove_peer(config_name):
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
sql = "DELETE FROM " + config_name + " WHERE id = ?"
|
||||
g.cur.execute(sql, (delete_key,))
|
||||
g.db.commit()
|
||||
return "true"
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return exc.output.strip()
|
||||
@ -1000,30 +1091,7 @@ def get_peer_name(config_name):
|
||||
# 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([])
|
||||
return jsonify(f_available_ips(config_name))
|
||||
|
||||
|
||||
# Generate a private key
|
||||
|
BIN
src/static/.DS_Store
vendored
BIN
src/static/.DS_Store
vendored
Binary file not shown.
@ -90,6 +90,14 @@ body {
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.form-control{
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.form-control:disabled{
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.navbar .form-control {
|
||||
padding: .75rem 1rem;
|
||||
border-width: 0;
|
||||
|
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}#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)}
|
||||
@-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}.form-control{transition:all .2s ease-in-out}.form-control:disabled{cursor:not-allowed}.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,4 +1,5 @@
|
||||
$("[data-toggle='tooltip']").tooltip()
|
||||
$("[data-toggle='popover']").popover()
|
||||
let $body = $("body");
|
||||
let $progress_bar = $(".progress-bar");
|
||||
let available_ips = [];
|
||||
@ -74,44 +75,21 @@ $body.on("click", ".switch", function (){
|
||||
* Generate Private and Public key for a new peer
|
||||
*/
|
||||
function generate_key(){
|
||||
$.ajax({
|
||||
"url": "/generate_peer",
|
||||
"method": "GET",
|
||||
}).done(function(res){
|
||||
$("#private_key").val(res.private_key);
|
||||
$("#public_key").val(res.public_key);
|
||||
let keys = wireguard.generateKeypair();
|
||||
$("#private_key").val(keys.privateKey);
|
||||
$("#public_key").val(keys.publicKey);
|
||||
$("#add_peer_alert").addClass("d-none");
|
||||
$("#re_generate_key i").removeClass("rotating");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate public for existing peer
|
||||
*/
|
||||
function generate_public_key(){
|
||||
$.ajax({
|
||||
"url": "/generate_public_key",
|
||||
"method": "POST",
|
||||
"headers":{"Content-Type": "application/json"},
|
||||
"data": JSON.stringify({"private_key": $("#private_key").val()})
|
||||
}).done(function(res){
|
||||
if(res.status === "failed"){
|
||||
$("#add_peer_alert").html(res.msg).removeClass("d-none");
|
||||
}else{
|
||||
$("#add_peer_alert").addClass("d-none");
|
||||
}
|
||||
$("#public_key").val(res.data);
|
||||
$("#re_generate_key i").removeClass("rotating");
|
||||
});
|
||||
$("#enable_preshare_key").val(keys.presharedKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Public key when private got change
|
||||
*/
|
||||
$("#private_key").on("change",function(){
|
||||
if ($(this).val().length > 0){
|
||||
if ($(this).val().length === 44){
|
||||
$("#re_generate_key i").addClass("rotating");
|
||||
generate_public_key();
|
||||
$("#public_key").val(wireguard.generatePublicKey($("#private_key").val()));
|
||||
}else{
|
||||
$("#public_key").removeAttr("disabled").val("");
|
||||
}
|
||||
@ -241,7 +219,73 @@ function clean_ip(val){
|
||||
return clean_ip.filter(Boolean).join(",");
|
||||
}
|
||||
|
||||
let bulk_add_peers = () => {
|
||||
$save_peer.attr("disabled","disabled");
|
||||
$save_peer.html("Adding...");
|
||||
|
||||
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");
|
||||
$new_add_endpoint_allowed_ip.val(clean_ip($new_add_endpoint_allowed_ip.val()));
|
||||
let $new_add_MTU = $("#new_add_MTU");
|
||||
let $new_add_keep_alive = $("#new_add_keep_alive");
|
||||
let $enable_preshare_key = $("#enable_preshare_key");
|
||||
let $new_add_amount = $("#new_add_amount");
|
||||
let data_list = [$new_add_DNS, $new_add_endpoint_allowed_ip,$new_add_MTU, $new_add_keep_alive];
|
||||
if ($new_add_amount.val() > 0){
|
||||
if ($new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
|
||||
let conf = $save_peer.attr('conf_id');
|
||||
let keys = [];
|
||||
for (let i = 0; i < $new_add_amount.val(); i++) keys.push(wireguard.generateKeypair());
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/add_peer_bulk/"+conf,
|
||||
headers:{
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({
|
||||
"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(),
|
||||
"enable_preshared_key": $enable_preshare_key.prop("checked"),
|
||||
"keys": keys,
|
||||
"amount": $new_add_amount.val()
|
||||
}),
|
||||
success: function (response){
|
||||
if(response !== "true"){
|
||||
$("#add_peer_alert").html(response).removeClass("d-none");
|
||||
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
||||
$save_peer.removeAttr("disabled").html("Save");
|
||||
}
|
||||
else{
|
||||
load_data("");
|
||||
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
||||
$("#add_peer_form").trigger("reset");
|
||||
$save_peer.removeAttr("disabled").html("Save");
|
||||
showToast($new_add_amount.val()+"peers added successful!");
|
||||
addModal.toggle();
|
||||
}
|
||||
}
|
||||
})
|
||||
}else{
|
||||
$("#add_peer_alert").html("Please fill in all required box.").removeClass("d-none");
|
||||
$save_peer.removeAttr("disabled");
|
||||
$save_peer.html("Add");
|
||||
}
|
||||
}else{
|
||||
$("#add_peer_alert").html("Please enter 1 or more amount.").removeClass("d-none");
|
||||
$save_peer.removeAttr("disabled");
|
||||
$save_peer.html("Add");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$save_peer.on("click",function(){
|
||||
let $bulk_add = $("#bulk_add");
|
||||
if ($bulk_add.prop("checked")){
|
||||
bulk_add_peers()
|
||||
}else {
|
||||
let $public_key = $("#public_key");
|
||||
let $private_key = $("#private_key");
|
||||
let $allowed_ips = $("#allowed_ips");
|
||||
@ -254,9 +298,8 @@ $save_peer.on("click",function(){
|
||||
let $new_add_MTU = $("#new_add_MTU");
|
||||
let $new_add_keep_alive = $("#new_add_keep_alive");
|
||||
let $enable_preshare_key = $("#enable_preshare_key");
|
||||
let p_key = $public_key.val()
|
||||
$(this).attr("disabled","disabled");
|
||||
$(this).html("Saving...");
|
||||
$(this).html("Adding...");
|
||||
if ($allowed_ips.val() !== "" && $public_key.val() !== "" && $new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
|
||||
let conf = $(this).attr('conf_id');
|
||||
let data_list = [$private_key, $allowed_ips, $new_add_name, $new_add_DNS, $new_add_endpoint_allowed_ip,$new_add_MTU, $new_add_keep_alive];
|
||||
@ -277,6 +320,7 @@ $save_peer.on("click",function(){
|
||||
"MTU": $new_add_MTU.val(),
|
||||
"keep_alive": $new_add_keep_alive.val(),
|
||||
"enable_preshared_key": $enable_preshare_key.prop("checked"),
|
||||
"preshared_key": $enable_preshare_key.val()
|
||||
}),
|
||||
success: function (response){
|
||||
if(response !== "true"){
|
||||
@ -297,7 +341,8 @@ $save_peer.on("click",function(){
|
||||
}else{
|
||||
$("#add_peer_alert").html("Please fill in all required box.").removeClass("d-none");
|
||||
$(this).removeAttr("disabled");
|
||||
$(this).html("Save");
|
||||
$(this).html("Add");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -592,3 +637,22 @@ $body.on("click", ".display_mode", function(){
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#bulk_add").on("change", function (){
|
||||
let hide = $(".non-bulk").find("input");
|
||||
let amount = $("#new_add_amount");
|
||||
if ($(this).prop("checked") === true){
|
||||
for(let i = 0; i < hide.length; i++){
|
||||
$(hide[i]).attr("disabled", "disabled");
|
||||
}
|
||||
amount.removeAttr("disabled");
|
||||
}
|
||||
else{
|
||||
for(let i = 0; i < hide.length; i++){
|
||||
if ($(hide[i]).attr('id') !== "public_key"){
|
||||
$(hide[i]).removeAttr("disabled");
|
||||
}
|
||||
}
|
||||
amount.attr("disabled", "disabled");
|
||||
}
|
||||
})
|
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
189
src/static/js/wireguard.js
Normal file
189
src/static/js/wireguard.js
Normal file
@ -0,0 +1,189 @@
|
||||
/*! SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
function gf(init) {
|
||||
var r = new Float64Array(16);
|
||||
if (init) {
|
||||
for (var i = 0; i < init.length; ++i)
|
||||
r[i] = init[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function pack(o, n) {
|
||||
var b, m = gf(), t = gf();
|
||||
for (var i = 0; i < 16; ++i)
|
||||
t[i] = n[i];
|
||||
carry(t);
|
||||
carry(t);
|
||||
carry(t);
|
||||
for (var j = 0; j < 2; ++j) {
|
||||
m[0] = t[0] - 0xffed;
|
||||
for (var i = 1; i < 15; ++i) {
|
||||
m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
|
||||
m[i - 1] &= 0xffff;
|
||||
}
|
||||
m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
|
||||
b = (m[15] >> 16) & 1;
|
||||
m[14] &= 0xffff;
|
||||
cswap(t, m, 1 - b);
|
||||
}
|
||||
for (var i = 0; i < 16; ++i) {
|
||||
o[2 * i] = t[i] & 0xff;
|
||||
o[2 * i + 1] = t[i] >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
function carry(o) {
|
||||
var c;
|
||||
for (var i = 0; i < 16; ++i) {
|
||||
o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536);
|
||||
o[i] &= 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
function cswap(p, q, b) {
|
||||
var t, c = ~(b - 1);
|
||||
for (var i = 0; i < 16; ++i) {
|
||||
t = c & (p[i] ^ q[i]);
|
||||
p[i] ^= t;
|
||||
q[i] ^= t;
|
||||
}
|
||||
}
|
||||
|
||||
function add(o, a, b) {
|
||||
for (var i = 0; i < 16; ++i)
|
||||
o[i] = (a[i] + b[i]) | 0;
|
||||
}
|
||||
|
||||
function subtract(o, a, b) {
|
||||
for (var i = 0; i < 16; ++i)
|
||||
o[i] = (a[i] - b[i]) | 0;
|
||||
}
|
||||
|
||||
function multmod(o, a, b) {
|
||||
var t = new Float64Array(31);
|
||||
for (var i = 0; i < 16; ++i) {
|
||||
for (var j = 0; j < 16; ++j)
|
||||
t[i + j] += a[i] * b[j];
|
||||
}
|
||||
for (var i = 0; i < 15; ++i)
|
||||
t[i] += 38 * t[i + 16];
|
||||
for (var i = 0; i < 16; ++i)
|
||||
o[i] = t[i];
|
||||
carry(o);
|
||||
carry(o);
|
||||
}
|
||||
|
||||
function invert(o, i) {
|
||||
var c = gf();
|
||||
for (var a = 0; a < 16; ++a)
|
||||
c[a] = i[a];
|
||||
for (var a = 253; a >= 0; --a) {
|
||||
multmod(c, c, c);
|
||||
if (a !== 2 && a !== 4)
|
||||
multmod(c, c, i);
|
||||
}
|
||||
for (var a = 0; a < 16; ++a)
|
||||
o[a] = c[a];
|
||||
}
|
||||
|
||||
function clamp(z) {
|
||||
z[31] = (z[31] & 127) | 64;
|
||||
z[0] &= 248;
|
||||
}
|
||||
|
||||
function generatePublicKey(privateKey) {
|
||||
var r, z = new Uint8Array(32);
|
||||
var a = gf([1]),
|
||||
b = gf([9]),
|
||||
c = gf(),
|
||||
d = gf([1]),
|
||||
e = gf(),
|
||||
f = gf(),
|
||||
_121665 = gf([0xdb41, 1]),
|
||||
_9 = gf([9]);
|
||||
for (var i = 0; i < 32; ++i)
|
||||
z[i] = privateKey[i];
|
||||
clamp(z);
|
||||
for (var i = 254; i >= 0; --i) {
|
||||
r = (z[i >>> 3] >>> (i & 7)) & 1;
|
||||
cswap(a, b, r);
|
||||
cswap(c, d, r);
|
||||
add(e, a, c);
|
||||
subtract(a, a, c);
|
||||
add(c, b, d);
|
||||
subtract(b, b, d);
|
||||
multmod(d, e, e);
|
||||
multmod(f, a, a);
|
||||
multmod(a, c, a);
|
||||
multmod(c, b, e);
|
||||
add(e, a, c);
|
||||
subtract(a, a, c);
|
||||
multmod(b, a, a);
|
||||
subtract(c, d, f);
|
||||
multmod(a, c, _121665);
|
||||
add(a, a, d);
|
||||
multmod(c, c, a);
|
||||
multmod(a, d, f);
|
||||
multmod(d, b, _9);
|
||||
multmod(b, e, e);
|
||||
cswap(a, b, r);
|
||||
cswap(c, d, r);
|
||||
}
|
||||
invert(c, c);
|
||||
multmod(a, a, c);
|
||||
pack(z, a);
|
||||
return z;
|
||||
}
|
||||
|
||||
function generatePresharedKey() {
|
||||
var privateKey = new Uint8Array(32);
|
||||
window.crypto.getRandomValues(privateKey);
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
function generatePrivateKey() {
|
||||
var privateKey = generatePresharedKey();
|
||||
clamp(privateKey);
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
function encodeBase64(dest, src) {
|
||||
var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]);
|
||||
for (var i = 0; i < 4; ++i)
|
||||
dest[i] = input[i] + 65 +
|
||||
(((25 - input[i]) >> 8) & 6) -
|
||||
(((51 - input[i]) >> 8) & 75) -
|
||||
(((61 - input[i]) >> 8) & 15) +
|
||||
(((62 - input[i]) >> 8) & 3);
|
||||
}
|
||||
|
||||
function keyToBase64(key) {
|
||||
var i, base64 = new Uint8Array(44);
|
||||
for (i = 0; i < 32 / 3; ++i)
|
||||
encodeBase64(base64.subarray(i * 4), key.subarray(i * 3));
|
||||
encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0]));
|
||||
base64[43] = 61;
|
||||
return String.fromCharCode.apply(null, base64);
|
||||
}
|
||||
|
||||
window.wireguard = {
|
||||
generateKeypair: function() {
|
||||
var privateKey = generatePrivateKey();
|
||||
var publicKey = generatePublicKey(privateKey);
|
||||
var presharedKey = generatePresharedKey();
|
||||
return {
|
||||
publicKey: keyToBase64(publicKey),
|
||||
privateKey: keyToBase64(privateKey),
|
||||
presharedKey: keyToBase64(presharedKey)
|
||||
};
|
||||
},
|
||||
generatePublicKey: function (privateKey){
|
||||
return keyToBase64(generatePublicKey(privateKey))
|
||||
}
|
||||
};
|
||||
})();
|
1
src/static/js/wireguard.min.js
vendored
Normal file
1
src/static/js/wireguard.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
(function(){function gf(init){var r=new Float64Array(16);if(init){for(var i=0;i<init.length;++i)r[i]=init[i]}return r}function pack(o,n){var b,m=gf(),t=gf();for(var i=0;i<16;++i)t[i]=n[i];carry(t);carry(t);carry(t);for(var j=0;j<2;++j){m[0]=t[0]-65517;for(var i=1;i<15;++i){m[i]=t[i]-65535-(m[i-1]>>16&1);m[i-1]&=65535}m[15]=t[15]-32767-(m[14]>>16&1);b=m[15]>>16&1;m[14]&=65535;cswap(t,m,1-b)}for(var i=0;i<16;++i){o[2*i]=t[i]&255;o[2*i+1]=t[i]>>8}}function carry(o){var c;for(var i=0;i<16;++i){o[(i+1)%16]+=(i<15?1:38)*Math.floor(o[i]/65536);o[i]&=65535}}function cswap(p,q,b){var t,c=~(b-1);for(var i=0;i<16;++i){t=c&(p[i]^q[i]);p[i]^=t;q[i]^=t}}function add(o,a,b){for(var i=0;i<16;++i)o[i]=a[i]+b[i]|0}function subtract(o,a,b){for(var i=0;i<16;++i)o[i]=a[i]-b[i]|0}function multmod(o,a,b){var t=new Float64Array(31);for(var i=0;i<16;++i){for(var j=0;j<16;++j)t[i+j]+=a[i]*b[j]}for(var i=0;i<15;++i)t[i]+=38*t[i+16];for(var i=0;i<16;++i)o[i]=t[i];carry(o);carry(o)}function invert(o,i){var c=gf();for(var a=0;a<16;++a)c[a]=i[a];for(var a=253;a>=0;--a){multmod(c,c,c);if(a!==2&&a!==4)multmod(c,c,i)}for(var a=0;a<16;++a)o[a]=c[a]}function clamp(z){z[31]=z[31]&127|64;z[0]&=248}function generatePublicKey(privateKey){var r,z=new Uint8Array(32);var a=gf([1]),b=gf([9]),c=gf(),d=gf([1]),e=gf(),f=gf(),_121665=gf([56129,1]),_9=gf([9]);for(var i=0;i<32;++i)z[i]=privateKey[i];clamp(z);for(var i=254;i>=0;--i){r=z[i>>>3]>>>(i&7)&1;cswap(a,b,r);cswap(c,d,r);add(e,a,c);subtract(a,a,c);add(c,b,d);subtract(b,b,d);multmod(d,e,e);multmod(f,a,a);multmod(a,c,a);multmod(c,b,e);add(e,a,c);subtract(a,a,c);multmod(b,a,a);subtract(c,d,f);multmod(a,c,_121665);add(a,a,d);multmod(c,c,a);multmod(a,d,f);multmod(d,b,_9);multmod(b,e,e);cswap(a,b,r);cswap(c,d,r)}invert(c,c);multmod(a,a,c);pack(z,a);return z}function generatePresharedKey(){var privateKey=new Uint8Array(32);window.crypto.getRandomValues(privateKey);return privateKey}function generatePrivateKey(){var privateKey=generatePresharedKey();clamp(privateKey);return privateKey}function encodeBase64(dest,src){var input=Uint8Array.from([src[0]>>2&63,(src[0]<<4|src[1]>>4)&63,(src[1]<<2|src[2]>>6)&63,src[2]&63]);for(var i=0;i<4;++i)dest[i]=input[i]+65+(25-input[i]>>8&6)-(51-input[i]>>8&75)-(61-input[i]>>8&15)+(62-input[i]>>8&3)}function keyToBase64(key){var i,base64=new Uint8Array(44);for(i=0;i<32/3;++i)encodeBase64(base64.subarray(i*4),key.subarray(i*3));encodeBase64(base64.subarray(i*4),Uint8Array.from([key[i*3+0],key[i*3+1],0]));base64[43]=61;return String.fromCharCode.apply(null,base64)}window.wireguard={generateKeypair:function(){var privateKey=generatePrivateKey();var publicKey=generatePublicKey(privateKey);var presharedKey=generatePresharedKey();return{publicKey:keyToBase64(publicKey),privateKey:keyToBase64(privateKey),presharedKey:keyToBase64(presharedKey)}},generatePublicKey:function(privateKey){return keyToBase64(generatePublicKey(privateKey))}}})();
|
@ -100,9 +100,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary add_btn">
|
||||
<i class="bi bi-plus-circle-fill" style=""></i> Add Peer
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary add_btn"><i class="bi bi-plus-circle-fill" style=""></i> Add Peer</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row peer_list"></div>
|
||||
@ -121,16 +119,29 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-sm" style="display: flex; align-items: center;">
|
||||
<div class="custom-control custom-switch">
|
||||
<input class="custom-control-input" type="checkbox" id="bulk_add">
|
||||
<label class="custom-control-label" for="bulk_add"><strong>Add Peers by bulk</strong></label>
|
||||
<i class="bi bi-question-circle-fill" style="cursor: pointer" data-container="body" data-toggle="popover" data-placement="right" data-trigger="click" data-content="By adding peers by bulk, each peer's name will be auto generated, and Allowed IP will be assign to the next available IP."></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="form-group" style="margin: 0">
|
||||
{# <label for="new_add_amount">Amount</label>#}
|
||||
<input type="number" class="form-control" id="new_add_amount" min="1" placeholder="Amount" disabled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div id="add_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="add_peer_form">
|
||||
<div class="alert alert-warning" role="alert" style="font-size: 0.8rem">
|
||||
To generate QR code for this new peer, you need to provide the private key, or use the generated key. If you don't need the QR code, simply remove the private key and insert your existed public key.
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group non-bulk">
|
||||
<div>
|
||||
<label for="private_key">Private Key</label>
|
||||
</div>
|
||||
@ -141,18 +152,18 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group non-bulk">
|
||||
<label for="public_key">Public Key <code>(Required)</code></label>
|
||||
<input type="text" class="form-control" id="public_key" aria-describedby="public_key" disabled>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="col-sm-6 non-bulk">
|
||||
<div class="form-group">
|
||||
<label for="new_add_name">Name</label>
|
||||
<input type="text" class="form-control" id="new_add_name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="col-sm-6 non-bulk">
|
||||
<div class="form-group">
|
||||
<label for="allowed_ips">Allowed IPs <code>(Required)</code></label>
|
||||
<div class="input-group">
|
||||
@ -201,7 +212,7 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="save_peer" conf_id={{conf_data['name']}}>Save</button>
|
||||
<button type="button" class="btn btn-primary" id="save_peer" conf_id={{conf_data['name']}}>Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -363,6 +374,7 @@
|
||||
{% include "tools.html" %}
|
||||
</body>
|
||||
{% include "footer.html" %}
|
||||
<script src="{{ url_for('static',filename='js/wireguard.min.js') }}"></script>
|
||||
<script src="{{ url_for('static',filename='js/configuration.min.js') }}"></script>
|
||||
|
||||
<script>
|
||||
|
@ -99,8 +99,8 @@ update_wgd() {
|
||||
mv wgd.sh wgd.sh.old
|
||||
printf "| Downloading %s from GitHub... |\n" "$new_ver"
|
||||
git stash > /dev/null 2>&1
|
||||
git pull
|
||||
# git pull https://github.com/donaldzou/wireguard-dashboard.git $new_ver --force > /dev/null 2>&1
|
||||
# git pull
|
||||
git pull https://github.com/donaldzou/wireguard-dashboard.git $new_ver --force > /dev/null 2>&1
|
||||
printf "| Upgrading pip |\n"
|
||||
python3 -m pip install -U pip > /dev/null 2>&1
|
||||
printf "| Installing latest Python dependencies |\n"
|
||||
|
Loading…
Reference in New Issue
Block a user