1
0
mirror of https://github.com/donaldzou/WGDashboard.git synced 2024-11-06 16:00:28 +01:00

Added line graph using chart.js & Improving websocket.

Added line graph to show total receive & total sent changes per refresh interval, using chart.js line chart to show the changes. Switching configuration don't need to refresh anymore, by using websocket.
This commit is contained in:
Donald Cheng Hong Zou 2022-03-03 08:46:23 -05:00
parent 264a050360
commit 8fe8209580
8 changed files with 342 additions and 142 deletions

View File

@ -120,7 +120,7 @@ def get_conf_running_peer_number(config_name):
data_usage = subprocess.check_output(f"wg show {config_name} latest-handshakes", data_usage = subprocess.check_output(f"wg show {config_name} latest-handshakes",
shell=True, stderr=subprocess.STDOUT) shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
return "stopped" return 0
data_usage = data_usage.decode("UTF-8").split() data_usage = data_usage.decode("UTF-8").split()
count = 0 count = 0
now = datetime.now() now = datetime.now()
@ -143,6 +143,7 @@ def read_conf_file_interface(config_name):
""" """
conf_location = WG_CONF_PATH + "/" + config_name + ".conf" conf_location = WG_CONF_PATH + "/" + config_name + ".conf"
try:
with open(conf_location, 'r', encoding='utf-8') as file_object: with open(conf_location, 'r', encoding='utf-8') as file_object:
file = file_object.read().split("\n") file = file_object.read().split("\n")
data = {} data = {}
@ -153,6 +154,8 @@ def read_conf_file_interface(config_name):
tmp = re.split(r'\s*=\s*', i, 1) tmp = re.split(r'\s*=\s*', i, 1)
if len(tmp) == 2: if len(tmp) == 2:
data[tmp[0]] = tmp[1] data[tmp[0]] = tmp[1]
except FileNotFoundError as e:
return {}
return data return data
@ -1013,17 +1016,25 @@ def get_conf(data):
@type config_name: str @type config_name: str
@return: TODO @return: TODO
""" """
result = {
"status": True,
"message": "",
"data": {}
}
if not session:
result["status"] = False
result["message"] = "Oops! <br> You're not signed in. Please refresh your page."
socketio.emit("get_config", result, json=True)
return
if getattr(g, 'db', None) is None: if getattr(g, 'db', None) is None:
g.db = connect_db() g.db = connect_db()
g.cur = g.db.cursor() g.cur = g.db.cursor()
print(data)
config_name = data['config'] config_name = data['config']
print(config_name)
config_interface = read_conf_file_interface(config_name) config_interface = read_conf_file_interface(config_name)
if config_interface != {}:
search = data['search'] search = data['search']
print(search)
# search = request.args.get('search')
if len(search) == 0: if len(search) == 0:
search = "" search = ""
search = urllib.parse.unquote(search) search = urllib.parse.unquote(search)
@ -1035,7 +1046,7 @@ def get_conf(data):
conf_address = "N/A" conf_address = "N/A"
else: else:
conf_address = config_interface['Address'] conf_address = config_interface['Address']
conf_data = { result['data'] = {
"peer_data": get_peers(config_name, search, sort), "peer_data": get_peers(config_name, search, sort),
"name": config_name, "name": config_name,
"status": get_conf_status(config_name), "status": get_conf_status(config_name),
@ -1049,13 +1060,15 @@ def get_conf(data):
"dashboard_refresh_interval": int(config.get("Server", "dashboard_refresh_interval")), "dashboard_refresh_interval": int(config.get("Server", "dashboard_refresh_interval")),
"peer_display_mode": peer_display_mode "peer_display_mode": peer_display_mode
} }
if conf_data['status'] == "stopped": if result['data']['status'] == "stopped":
conf_data['checked'] = "nope" result['data']['checked'] = "nope"
else: else:
conf_data['checked'] = "checked" result['data']['checked'] = "checked"
config.clear() config.clear()
socketio.emit("get_config", conf_data, json=True) else:
# return jsonify(conf_data) result['status'] = False
result['message'] = "I cannot find this configuration. <br> Please refresh and try again"
socketio.emit("get_config", result, json=True)
# Turn on / off a configuration # Turn on / off a configuration
@ -1334,7 +1347,11 @@ def get_peer_name(config_name):
# Return available IPs # Return available IPs
@app.route('/available_ips/<config_name>', methods=['GET']) @app.route('/available_ips/<config_name>', methods=['GET'])
def available_ips(config_name): def available_ips(config_name):
return jsonify(f_available_ips(config_name)) result = {"status": True, "message":"", "data": f_available_ips(config_name)}
if len(result["data"]) == 0:
result['status'] = False
result['message'] = f"No more available IP for {config_name}."
return jsonify(result)
# Check if both key match # Check if both key match

View File

@ -9,6 +9,10 @@ body {
vertical-align: text-bottom; vertical-align: text-bottom;
} }
.btn-primary{
font-weight: bold;
}
/* /*
* Sidebar * Sidebar
*/ */
@ -23,12 +27,6 @@ body {
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
} }
/*@media (max-width: 767.98px) {*/
/* .sidebar {*/
/* top: 5rem;*/
/* }*/
/*}*/
.sidebar-sticky { .sidebar-sticky {
position: relative; position: relative;
top: 0; top: 0;
@ -369,6 +367,7 @@ main{
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
.rotating::before { .rotating::before {
-webkit-animation: rotating 0.75s linear infinite; -webkit-animation: rotating 0.75s linear infinite;
-moz-animation: rotating 0.75s linear infinite; -moz-animation: rotating 0.75s linear infinite;
@ -385,7 +384,7 @@ main{
cursor: pointer; cursor: pointer;
} }
#peer_private_key_textbox, #private_key, #public_key{ #peer_private_key_textbox, #private_key, #public_key, #peer_preshared_key_textbox{
font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace; font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
} }
@ -571,3 +570,31 @@ pre.index-alert{
.peerLightContainer{ .peerLightContainer{
text-transform: uppercase; margin: 0; margin-left: auto !important; text-transform: uppercase; margin: 0; margin-left: auto !important;
} }
.conf_card .dot, .info .dot {
transform: translateX(10px);
}
#config_body{
transition: 0.3s ease-in-out;
}
#config_body.firstLoading{
opacity: 0.2;
}
.chartTitle{
display: flex;
}
.chartControl{
margin-bottom: 1rem;
display: flex;
align-items: center;
}
.chartTitle h6{
margin-bottom: 0;
line-height: 1;
margin-right: 0.5rem;
}

File diff suppressed because one or more lines are too long

View File

@ -50,6 +50,29 @@
} }
} }
let firstLoading = true;
$(".nav-conf-link").on("click", function(e){
if(socket.connected){
e.preventDefault();
firstLoading = true;
$("#config_body").addClass("firstLoading");
conf_name = $(this).data("conf-id");
configurations.loadPeers($('#search_peer_textbox').val());
$(".nav-conf-link").removeClass("active");
$(`.sb-${conf_name}-url`).addClass("active");
window.history.pushState(null,null,`/configuration/${conf_name}`);
$("title").text(`${conf_name} | WGDashboard`);
totalDataUsageChartObj.data.labels = [];
totalDataUsageChartObj.data.datasets[0].data = [];
totalDataUsageChartObj.data.datasets[1].data = [];
totalDataUsageChartObj.update();
}
});
/** /**
* Parse all responded information onto the configuration header * Parse all responded information onto the configuration header
* @param response * @param response
@ -61,6 +84,45 @@
}else{ }else{
$conf_status_btn.innerHTML = `<a href="#" id="${response.name}" ${response.checked} class="switch text-primary"><i class="bi bi-toggle2-off"></i> OFF</a>`; $conf_status_btn.innerHTML = `<a href="#" id="${response.name}" ${response.checked} class="switch text-primary"><i class="bi bi-toggle2-off"></i> OFF</a>`;
} }
if (response.running_peer > 0){
let d = new Date();
let time = d.toLocaleString("en-us",
{hour: '2-digit', minute: '2-digit', second: "2-digit", hourCycle: 'h23'});
totalDataUsageChartObj.data.labels.push(`${time}`);
if (totalDataUsageChartObj.data.datasets[0].data.length === 0){
totalDataUsageChartObj.data.datasets[1].lastData = response.total_data_usage[2];
totalDataUsageChartObj.data.datasets[0].lastData = response.total_data_usage[1];
totalDataUsageChartObj.data.datasets[0].data.push(0);
totalDataUsageChartObj.data.datasets[1].data.push(0);
}else{
if (totalDataUsageChartObj.data.datasets[0].data.length === 50 && totalDataUsageChartObj.data.datasets[1].data.length === 50){
totalDataUsageChartObj.data.labels.shift();
totalDataUsageChartObj.data.datasets[0].data.shift();
totalDataUsageChartObj.data.datasets[1].data.shift();
}
let newTotalReceive = response.total_data_usage[2] - totalDataUsageChartObj.data.datasets[1].lastData;
let newTotalSent = response.total_data_usage[1] - totalDataUsageChartObj.data.datasets[0].lastData;
let k = 0;
if (chartUnit === "MB"){
k = 1024;
}
else if (chartUnit === "KB"){
k = 1048576;
}else{
k = 1;
}
totalDataUsageChartObj.data.datasets[1].data.push(newTotalReceive*k);
totalDataUsageChartObj.data.datasets[0].data.push(newTotalSent*k);
totalDataUsageChartObj.data.datasets[0].lastData = response.total_data_usage[1];
totalDataUsageChartObj.data.datasets[1].lastData = response.total_data_usage[2];
}
totalDataUsageChartObj.update();
}
$conf_status_btn.classList.remove("info_loading"); $conf_status_btn.classList.remove("info_loading");
document.querySelectorAll("#sort_by_dropdown option").forEach(ele => ele.removeAttribute("selected")); document.querySelectorAll("#sort_by_dropdown option").forEach(ele => ele.removeAttribute("selected"));
document.querySelector(`#sort_by_dropdown option[value="${response.sort_tag}"]`).setAttribute("selected", "selected"); document.querySelector(`#sort_by_dropdown option[value="${response.sort_tag}"]`).setAttribute("selected", "selected");
@ -164,7 +226,7 @@
let data_list = [$new_add_DNS, $new_add_endpoint_allowed_ip,$new_add_MTU, $new_add_keep_alive]; let data_list = [$new_add_DNS, $new_add_endpoint_allowed_ip,$new_add_MTU, $new_add_keep_alive];
if ($new_add_amount.val() > 0 && !$new_add_amount.hasClass("is-invalid")){ if ($new_add_amount.val() > 0 && !$new_add_amount.hasClass("is-invalid")){
if ($new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){ if ($new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
let conf = $add_peer.getAttribute('conf_id'); let conf = conf_name;
let keys = []; let keys = [];
for (let i = 0; i < $new_add_amount.val(); i++) { for (let i = 0; i < $new_add_amount.val(); i++) {
keys.push(window.wireguard.generateKeypair()); keys.push(window.wireguard.generateKeypair());
@ -262,12 +324,13 @@
/** /**
* Handle when the server is not responding * Handle when the server is not responding
*/ */
function noResponding(){ function noResponding(message = "Opps! <br> I can't connect to the server."){
document.querySelectorAll(".no-response").forEach(ele => ele.classList.add("active")); document.querySelectorAll(".no-response").forEach(ele => ele.classList.add("active"));
setTimeout(function (){ setTimeout(function (){
document.querySelectorAll(".no-response").forEach(ele => ele.classList.add("show")); document.querySelectorAll(".no-response").forEach(ele => ele.classList.add("show"));
document.querySelector("#right_body").classList.add("no-responding"); document.querySelector("#right_body").classList.add("no-responding");
document.querySelector(".navbar").classList.add("no-responding"); document.querySelector(".navbar").classList.add("no-responding");
document.querySelector(".no-response .container h4").innerHTML = message;
},10); },10);
} }
@ -349,53 +412,38 @@
let time = 0; let time = 0;
let count = 0; let count = 0;
let d1 = new Date(); let d1 = new Date();
function loadPeers(searchString){ function loadPeers(searchString){
d1 = new Date(); d1 = new Date();
startProgressBar();
socket.emit('get_config', {"config": conf_name, "search": searchString}); socket.emit('get_config', {"config": conf_name, "search": searchString});
// let d1 = new Date();
// $.ajax({
// method: "GET",
// url: `/get_config/${conf_name}?search=${encodeURIComponent(searchString)}`,
// headers:{"Content-Type": "application/json"}
// }).done(function(response){
// removeNoResponding();
// peers = response.peer_data;
// configurationAlert(response);
// configurationHeader(response);
// configurationPeers(response);
// $(".dot.dot-running").attr("title","Peer Connected").tooltip();
// $(".dot.dot-stopped").attr("title","Peer Disconnected").tooltip();
// $("i[data-toggle='tooltip']").tooltip();
// endProgressBar();
// let d2 = new Date();
// let seconds = (d2 - d1);
// $("#peer_loading_time").html(`Peer Loading Time: ${seconds}ms`);
// }).fail(function(){
// noResponding();
// });
} }
function parsePeers(response){ function parsePeers(response){
if (response.status){
let d2 = new Date(); let d2 = new Date();
let seconds = (d2 - d1); let seconds = (d2 - d1);
time += seconds time += seconds;
count += 1 count += 1;
window.console.log(`Average time: ${time/count}ms`); window.console.log(`Average time: ${time/count}ms`);
$("#peer_loading_time").html(`Peer Loading Time: ${seconds}ms`); $("#peer_loading_time").html(`Peer Loading Time: ${seconds}ms`);
removeNoResponding(); removeNoResponding();
peers = response.peer_data; peers = response.data.peer_data;
configurationAlert(response); configurationAlert(response.data);
configurationHeader(response); configurationHeader(response.data);
configurationPeers(response); configurationPeers(response.data);
$(".dot.dot-running").attr("title","Peer Connected").tooltip(); $(".dot.dot-running").attr("title","Peer Connected").tooltip();
$(".dot.dot-stopped").attr("title","Peer Disconnected").tooltip(); $(".dot.dot-stopped").attr("title","Peer Disconnected").tooltip();
$("i[data-toggle='tooltip']").tooltip(); $("i[data-toggle='tooltip']").tooltip();
endProgressBar(); $("#conf_name").text(conf_name);
if (firstLoading){
firstLoading = false;
$("#config_body").removeClass("firstLoading");
}
}else{
noResponding(response.message);
removeConfigurationInterval();
}
} }
/** /**
@ -513,16 +561,21 @@
*/ */
function getAvailableIps(){ function getAvailableIps(){
$.ajax({ $.ajax({
"url": `/available_ips/${$add_peer.getAttribute("conf_id")}`, "url": `/available_ips/${conf_name}`,
"method": "GET", "method": "GET",
}).done(function (res) { }).done(function (res) {
available_ips = res; if (res.status === true){
available_ips = res.data;
let $list_group = document.querySelector("#available_ip_modal .modal-body .list-group"); let $list_group = document.querySelector("#available_ip_modal .modal-body .list-group");
$list_group.innerHTML = ""; $list_group.innerHTML = "";
document.querySelector("#allowed_ips").value = available_ips[0]; document.querySelector("#allowed_ips").value = available_ips[0];
available_ips.forEach((ip) => available_ips.forEach((ip) =>
$list_group.innerHTML += $list_group.innerHTML +=
`<a class="list-group-item list-group-item-action available-ip-item" style="cursor: pointer" data-ip="${ip}">${ip}</a>`); `<a class="list-group-item list-group-item-action available-ip-item" style="cursor: pointer" data-ip="${ip}">${ip}</a>`);
}else{
document.querySelector("#allowed_ips").value = res.message;
document.querySelector("#search_available_ip").setAttribute("disabled", "disabled");
}
}); });
} }
@ -533,6 +586,7 @@
ipModal: () => { return ipModal; }, ipModal: () => { return ipModal; },
qrcodeModal: () => { return qrcodeModal; }, qrcodeModal: () => { return qrcodeModal; },
settingModal: () => { return settingModal; }, settingModal: () => { return settingModal; },
configurationTimeout: () => { return configuration_timeout;},
loadPeers: (searchString) => { loadPeers(searchString); }, loadPeers: (searchString) => { loadPeers(searchString); },
addPeersByBulk: () => { addPeersByBulk(); }, addPeersByBulk: () => { addPeersByBulk(); },
@ -663,7 +717,7 @@ $add_peer.addEventListener("click",function(){
$add_peer.setAttribute("disabled","disabled"); $add_peer.setAttribute("disabled","disabled");
$add_peer.innerHTML = "Adding..."; $add_peer.innerHTML = "Adding...";
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() !== ""){
let conf = $add_peer.getAttribute('conf_id'); let conf = conf_name;
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]; 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];
data_list.forEach((ele) => ele.attr("disabled", "disabled")); data_list.forEach((ele) => ele.attr("disabled", "disabled"));
$.ajax({ $.ajax({
@ -739,7 +793,7 @@ $("#new_add_amount").on("keyup", function(){
* Handle when user toggled add peers by bulk * Handle when user toggled add peers by bulk
*/ */
$("#bulk_add").on("change", function (){ $("#bulk_add").on("change", function (){
let hide = $(".non-bulk").find("input"); let hide = $(".non-bulk");
let amount = $("#new_add_amount"); let amount = $("#new_add_amount");
if ($(this).prop("checked") === true){ if ($(this).prop("checked") === true){
for(let i = 0; i < hide.length; i++){ for(let i = 0; i < hide.length; i++){
@ -854,7 +908,7 @@ $body.on("click", ".btn-qrcode-peer", function (){
*/ */
$body.on("click", ".btn-delete-peer", function(){ $body.on("click", ".btn-delete-peer", function(){
let peer_id = $(this).attr("id"); let peer_id = $(this).attr("id");
$("#delete_peer").attr("peer_id", peer_id); $("#delete_peer").data("peer-id", peer_id);
window.configurations.deleteModal().toggle(); window.configurations.deleteModal().toggle();
}); });
@ -864,9 +918,8 @@ $body.on("click", ".btn-delete-peer", function(){
$("#delete_peer").on("click",function(){ $("#delete_peer").on("click",function(){
$(this).attr("disabled","disabled"); $(this).attr("disabled","disabled");
$(this).html("Deleting..."); $(this).html("Deleting...");
let peer_id = $(this).attr("peer_id"); let config = conf_name;
let config = $(this).attr("conf_id"); let peer_ids = [$(this).data("peer-id")];
let peer_ids = [peer_id];
window.configurations.deletePeers(config, peer_ids); window.configurations.deletePeers(config, peer_ids);
}); });
@ -880,12 +933,12 @@ $("#delete_peer").on("click",function(){
* Handle when setting button got clicked for each peer * Handle when setting button got clicked for each peer
*/ */
$body.on("click", ".btn-setting-peer", function(){ $body.on("click", ".btn-setting-peer", function(){
window.configurations.startProgressBar(); // window.configurations.startProgressBar();
let peer_id = $(this).attr("id"); let peer_id = $(this).attr("id");
$("#save_peer_setting").attr("peer_id", peer_id); $("#save_peer_setting").attr("peer_id", peer_id);
$.ajax({ $.ajax({
method: "POST", method: "POST",
url: "/get_peer_data/"+$("#setting_modal").attr("conf_id"), url: "/get_peer_data/"+conf_name,
headers:{ headers:{
"Content-Type": "application/json" "Content-Type": "application/json"
}, },
@ -921,7 +974,7 @@ $("#peer_private_key_textbox").on("change",function(){
let $save_peer_setting = $("#save_peer_setting"); let $save_peer_setting = $("#save_peer_setting");
if ($(this).val().length > 0){ if ($(this).val().length > 0){
$.ajax({ $.ajax({
"url": "/check_key_match/"+$save_peer_setting.attr("conf_id"), "url": "/check_key_match/"+conf_name,
"method": "POST", "method": "POST",
"headers":{"Content-Type": "application/json"}, "headers":{"Content-Type": "application/json"},
"data": JSON.stringify({ "data": JSON.stringify({
@ -1233,7 +1286,7 @@ $("#confirm_delete_bulk_peers").on("click", function(){
btn.attr("disabled", "disabled"); btn.attr("disabled", "disabled");
let ips = []; let ips = [];
$selected_peer_list.childNodes.forEach((ele) => ips.push(ele.dataset.id)); $selected_peer_list.childNodes.forEach((ele) => ips.push(ele.dataset.id));
window.configurations.deletePeers(btn.data("conf"), ips); window.configurations.deletePeers(conf_name, ips);
clearInterval(confirm_delete_bulk_peers_interval); clearInterval(confirm_delete_bulk_peers_interval);
confirm_delete_bulk_peers_interval = undefined; confirm_delete_bulk_peers_interval = undefined;
} }
@ -1289,7 +1342,7 @@ $body.on("click", ".btn-download-peer", function(e){
*/ */
$("#download_all_peers").on("click", function(){ $("#download_all_peers").on("click", function(){
$.ajax({ $.ajax({
"url": $(this).data("url"), "url": `/download_all/${conf_name}`,
"method": "GET", "method": "GET",
success: function(res){ success: function(res){
if (res.peers.length > 0){ if (res.peers.length > 0){

File diff suppressed because one or more lines are too long

View File

@ -74,6 +74,24 @@
</div> </div>
</div> </div>
<hr> <hr>
<div class="row">
<div class="col-sm">
<div class="chartTitle">
<h6>Data Usage / Refresh Interval</h6>
<div class="chartControl" style="margin-left: auto">
<div class="btn-group" role="group">
<button class="btn btn-outline-primary btn-sm switchUnit active" data-unit="GB">GB</button>
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="MB">MB</button>
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="KB">KB</button>
</div>
</div>
</div>
<div style="width: 100%; max-height: 300px">
<canvas id="myChart" width="100" height="100"></canvas>
</div>
</div>
</div>
<hr>
<div class="button-div mb-3"> <div class="button-div mb-3">
<div class="row"> <div class="row">
<div class="col-sm"> <div class="col-sm">
@ -154,35 +172,35 @@
</button> </button>
</div> </div>
<form id="add_peer_form"> <form id="add_peer_form">
<div class="form-group non-bulk"> <div class="form-group">
<div> <div>
<label for="private_key">Private Key</label> <label for="private_key">Private Key</label>
</div> </div>
<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 non-bulk" 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" data-toggle="tooltip" data-placement="top" title="Regenerate Key"><i class="bi bi-arrow-repeat"></i></button> <button type="button" class="btn btn-danger non-bulk" 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>
<div class="form-group non-bulk"> <div class="form-group">
<label for="public_key">Public Key <code>(Required)</code></label> <label for="public_key">Public Key <code>(Required)</code></label>
<input type="text" class="form-control" id="public_key" aria-describedby="public_key" disabled> <input type="text" class="form-control non-bulk" id="public_key" aria-describedby="public_key" disabled>
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-6 non-bulk"> <div class="col-sm-6">
<div class="form-group"> <div class="form-group">
<label for="new_add_name">Name</label> <label for="new_add_name">Name</label>
<input type="text" class="form-control" id="new_add_name"> <input type="text" class="form-control non-bulk" id="new_add_name">
</div> </div>
</div> </div>
<div class="col-sm-6 non-bulk"> <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>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" id="allowed_ips"> <input type="text" class="form-control non-bulk" id="allowed_ips">
<div class="input-group-append"> <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"> <button type="button" class="btn btn-primary non-bulk" id="search_available_ip" data-toggle="tooltip" data-placement="top" title="Search Available IPs">
<i class="bi bi-search"></i> <i class="bi bi-search"></i>
</button> </button>
</div> </div>
@ -281,7 +299,6 @@
<input type="text" class="form-control" id="peer_preshared_key_textbox"> <input type="text" class="form-control" id="peer_preshared_key_textbox">
</div> </div>
<hr> <hr>
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<div class="mb-3"> <div class="mb-3">
@ -418,9 +435,8 @@
let load_interval = 0; let load_interval = 0;
let conf_name = "{{ conf_data['name'] }}" let conf_name = "{{ conf_data['name'] }}"
let peers = []; let peers = [];
$(".sb-"+conf_name+"-url").addClass("active"); $(`.sb-${conf_name}-url`).addClass("active");
let socket = io(); let socket = io();
$(function(){
socket.on('connect', function() { socket.on('connect', function() {
configurations.loadPeers($('#search_peer_textbox').val()); configurations.loadPeers($('#search_peer_textbox').val());
}); });
@ -429,6 +445,92 @@
configurations.parsePeers(res); configurations.parsePeers(res);
}); });
let chartUnit = $(".switchUnit.active").data('unit');
const totalDataUsageChart = document.getElementById('myChart').getContext('2d');
const totalDataUsageChartObj = new Chart(totalDataUsageChart, {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'Total Sent',
data: [],
fill: false,
borderColor: '#28a745',
tension: 0.1,
borderWidth: 1
},{
label: 'Total Received',
data: [],
fill: false,
borderColor: '#007bff',
tension: 0.1,
borderWidth: 1
}]
},
options: {
responsive:false,
scales: {
y: {
ticks: {
callback: function(value, index, ticks) {
return `${value} ${chartUnit}`
}
}
}
}
}
}); });
$("#myChart").css("width", "100%");
totalDataUsageChartObj.width = $("#myChart").parent().width();
totalDataUsageChartObj.resize();
$(window).on("resize", function() {
totalDataUsageChartObj.width = $("#myChart").parent().width();
totalDataUsageChartObj.resize();
});
let mul = 1;
$(".switchUnit").on("click", function(){
$(".switchUnit").removeClass("active");
$(this).addClass("active");
if ($(this).data('unit') !== chartUnit){
switch ($(this).data('unit')) {
case "GB":
if (chartUnit === "MB"){
mul = 1/1024;
}
if (chartUnit === "KB"){
mul = 1/1048576;
}
break;
case "MB":
if (chartUnit === "GB"){
mul = 1024
}
if (chartUnit === "KB"){
mul = 1/1024
}
break;
case "KB":
if (chartUnit === "GB"){
mul = 1048576
}
if (chartUnit === "MB"){
mul = 1024
}
break;
default:
break;
}
chartUnit = $(this).data('unit');
totalDataUsageChartObj.data.datasets[0].data = totalDataUsageChartObj.data.datasets[0].data.map(x => x * mul);
totalDataUsageChartObj.data.datasets[1].data = totalDataUsageChartObj.data.datasets[1].data.map(x => x * mul);
totalDataUsageChartObj.update();
}
})
</script> </script>
</html> </html>

View File

@ -15,4 +15,5 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='css/dashboard.css') }}"> <link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='css/dashboard.css') }}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head> </head>

View File

@ -19,7 +19,7 @@
</h6> </h6>
<ul class="nav flex-column"> <ul class="nav flex-column">
{% for i in conf%} {% for i in conf%}
<li class="nav-item"><a class="nav-link sb-{{i['conf']}}-url" href="/configuration/{{i['conf']}}"><samp>{{i['conf']}}</samp></a></li> <li class="nav-item"><a class="nav-link nav-conf-link sb-{{i['conf']}}-url" href="/configuration/{{i['conf']}}" data-conf-id="{{i['conf']}}"><samp>{{i['conf']}}</samp></a></li>
{%endfor%} {%endfor%}
</ul> </ul>
<hr> <hr>