mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 15:20:09 +01:00
Merge pull request #4 from donaldzou/v20210403-alpha
v20210403 Bug Fixed
This commit is contained in:
commit
742191111b
268
src/dashboard.py
268
src/dashboard.py
@ -4,16 +4,20 @@ import subprocess
|
||||
from datetime import datetime, date, time, timedelta
|
||||
from operator import itemgetter
|
||||
from tinydb import TinyDB, Query
|
||||
|
||||
conf_location = "/etc/wireguard"
|
||||
app = Flask("Wireguard Dashboard")
|
||||
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
||||
css = ""
|
||||
conf_data = {}
|
||||
|
||||
|
||||
def get_conf_peer_key(config_name):
|
||||
keys = []
|
||||
try: peer_key = subprocess.check_output("wg show "+config_name+" peers", shell=True)
|
||||
except Exception: return "stopped"
|
||||
try:
|
||||
peer_key = subprocess.check_output("wg show " + config_name + " peers", shell=True)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
peer_key = peer_key.decode("UTF-8").split()
|
||||
for i in peer_key: keys.append(i)
|
||||
return keys
|
||||
@ -21,28 +25,69 @@ def get_conf_peer_key(config_name):
|
||||
|
||||
def get_conf_running_peer_number(config_name):
|
||||
running = 0
|
||||
#Get latest handshakes
|
||||
try: data_usage = subprocess.check_output("wg show "+config_name+" latest-handshakes", shell=True)
|
||||
except Exception: return "stopped"
|
||||
# Get latest handshakes
|
||||
try:
|
||||
data_usage = subprocess.check_output("wg show " + config_name + " latest-handshakes", shell=True)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
data_usage = data_usage.decode("UTF-8").split()
|
||||
count = 0
|
||||
now = datetime.now()
|
||||
b = timedelta(minutes=2)
|
||||
for i in range(int(len(data_usage)/2)):
|
||||
minus = now - datetime.fromtimestamp(int(data_usage[count+1]))
|
||||
for i in range(int(len(data_usage) / 2)):
|
||||
minus = now - datetime.fromtimestamp(int(data_usage[count + 1]))
|
||||
if minus < b:
|
||||
running += 1
|
||||
count += 2
|
||||
return running
|
||||
|
||||
|
||||
def get_conf_peers_data(config_name):
|
||||
db = TinyDB('db/'+config_name+'.json')
|
||||
db = TinyDB('db/' + config_name + '.json')
|
||||
peers = Query()
|
||||
peer_data = {}
|
||||
|
||||
# Read Configuration File Start
|
||||
conf_location = "/etc/wireguard/"+config_name+".conf"
|
||||
f = open(conf_location, 'r')
|
||||
file = f.read().split("\n")
|
||||
conf_peer_data = {
|
||||
"Interface": {},
|
||||
"Peers": []
|
||||
}
|
||||
interface = []
|
||||
peers_start = 0
|
||||
for i in range(len(file)):
|
||||
if file[i] == "[Peer]":
|
||||
peers_start = i
|
||||
break
|
||||
else:
|
||||
if len(file[i]) > 0:
|
||||
if file[i] != "[Interface]":
|
||||
tmp = file[i].replace(" ", "").split("=", 1)
|
||||
if len(tmp) == 2:
|
||||
conf_peer_data['Interface'][tmp[0]] = tmp[1]
|
||||
conf_peers = file[peers_start:]
|
||||
peer = -1
|
||||
for i in conf_peers:
|
||||
if i == "[Peer]":
|
||||
peer += 1
|
||||
conf_peer_data["Peers"].append({})
|
||||
else:
|
||||
if len(i) > 0:
|
||||
tmp = i.replace(" ", "").split("=", 1)
|
||||
if len(tmp) == 2:
|
||||
conf_peer_data["Peers"][peer][tmp[0]] = tmp[1]
|
||||
# Read Configuration File End
|
||||
|
||||
# Get key
|
||||
try: peer_key = subprocess.check_output("wg show "+config_name+" peers", shell=True)
|
||||
except Exception: return "stopped"
|
||||
try:
|
||||
peer_key = subprocess.check_output("wg show " + config_name + " peers", shell=True)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
peer_key = peer_key.decode("UTF-8").split()
|
||||
now = datetime.now()
|
||||
current_time = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
for i in peer_key:
|
||||
peer_data[i] = {}
|
||||
if not db.search(peers.id == i):
|
||||
@ -59,47 +104,51 @@ def get_conf_peers_data(config_name):
|
||||
"traffic": []
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
#Get transfer
|
||||
try: data_usage = subprocess.check_output("wg show "+config_name+" transfer", shell=True)
|
||||
except Exception: return "stopped"
|
||||
# Get transfer
|
||||
try:
|
||||
data_usage = subprocess.check_output("wg show " + config_name + " transfer", shell=True)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
data_usage = data_usage.decode("UTF-8").split()
|
||||
count = 0
|
||||
upload_total = 0
|
||||
download_total = 0
|
||||
total = 0
|
||||
for i in range(int(len(data_usage)/3)):
|
||||
db.update({"total_receive": round(int(data_usage[count+1])/(1024**3),4),
|
||||
"total_sent": round(int(data_usage[count+2])/(1024**3),4),
|
||||
"total_data": round((int(data_usage[count+2])+int(data_usage[count+1]))/(1024**3),4)}, peers.id == data_usage[count])
|
||||
|
||||
peer_data[data_usage[count]]['total_receive'] = round(int(data_usage[count+1])/(1024**3),4)
|
||||
peer_data[data_usage[count]]['total_sent'] = round(int(data_usage[count+2])/(1024**3),4)
|
||||
peer_data[data_usage[count]]['total_data'] = round((int(data_usage[count+2])+int(data_usage[count+1]))/(1024**3),4)
|
||||
for i in range(int(len(data_usage) / 3)):
|
||||
db.update({"total_receive": round(int(data_usage[count + 1]) / (1024 ** 3), 4),
|
||||
"total_sent": round(int(data_usage[count + 2]) / (1024 ** 3), 4),
|
||||
"total_data": round((int(data_usage[count + 2]) + int(data_usage[count + 1])) / (1024 ** 3), 4)},
|
||||
peers.id == data_usage[count])
|
||||
peer_data[data_usage[count]]['total_receive'] = round(int(data_usage[count + 1]) / (1024 ** 3), 4)
|
||||
peer_data[data_usage[count]]['total_sent'] = round(int(data_usage[count + 2]) / (1024 ** 3), 4)
|
||||
peer_data[data_usage[count]]['total_data'] = round(
|
||||
(int(data_usage[count + 2]) + int(data_usage[count + 1])) / (1024 ** 3), 4)
|
||||
traffic = db.search(peers.id == data_usage[count])[0]['traffic']
|
||||
traffic.append({"time": current_time, "total_receive": round(int(data_usage[count + 1]) / (1024 ** 3), 4),
|
||||
"total_sent": round(int(data_usage[count + 2]) / (1024 ** 3), 4)})
|
||||
db.update({"traffic": traffic}, peers.id == data_usage[count])
|
||||
count += 3
|
||||
|
||||
#Get endpoint
|
||||
try: data_usage = subprocess.check_output("wg show "+config_name+" endpoints", shell=True)
|
||||
except Exception: return "stopped"
|
||||
# Get endpoint
|
||||
try:
|
||||
data_usage = subprocess.check_output("wg show " + config_name + " endpoints", shell=True)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
data_usage = data_usage.decode("UTF-8").split()
|
||||
count = 0
|
||||
for i in range(int(len(data_usage)/2)):
|
||||
db.update({"endpoint": data_usage[count+1]}, peers.id == data_usage[count])
|
||||
for i in range(int(len(data_usage) / 2)):
|
||||
db.update({"endpoint": data_usage[count + 1]}, peers.id == data_usage[count])
|
||||
|
||||
peer_data[data_usage[count]]['endpoint'] = data_usage[count+1]
|
||||
peer_data[data_usage[count]]['endpoint'] = data_usage[count + 1]
|
||||
count += 2
|
||||
|
||||
#Get latest handshakes
|
||||
try: data_usage = subprocess.check_output("wg show "+config_name+" latest-handshakes", shell=True)
|
||||
except Exception: return "stopped"
|
||||
# Get latest handshakes
|
||||
try:
|
||||
data_usage = subprocess.check_output("wg show " + config_name + " latest-handshakes", shell=True)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
data_usage = data_usage.decode("UTF-8").split()
|
||||
count = 0
|
||||
now = datetime.now()
|
||||
b = timedelta(minutes=2)
|
||||
for i in range(int(len(data_usage)/2)):
|
||||
minus = now - datetime.fromtimestamp(int(data_usage[count+1]))
|
||||
for i in range(int(len(data_usage) / 2)):
|
||||
minus = now - datetime.fromtimestamp(int(data_usage[count + 1]))
|
||||
status = ""
|
||||
if minus < b:
|
||||
peer_data[data_usage[count]]['status'] = "running"
|
||||
@ -111,75 +160,96 @@ def get_conf_peers_data(config_name):
|
||||
db.update({"latest_handshake": str(minus).split(".")[0], "status": status}, peers.id == data_usage[count])
|
||||
peer_data[data_usage[count]]['latest_handshake'] = str(minus).split(".")[0]
|
||||
count += 2
|
||||
|
||||
#Get allowed ip
|
||||
try: data_usage = subprocess.check_output("wg show "+config_name+" allowed-ips", shell=True)
|
||||
except Exception: return "stopped"
|
||||
data_usage = data_usage.decode("UTF-8").split()
|
||||
count = 0
|
||||
for i in range(int(len(data_usage)/2)):
|
||||
db.update({"allowed_ip": data_usage[count+1]}, peers.id == data_usage[count])
|
||||
peer_data[data_usage[count]]['allowed_ip'] = data_usage[count+1]
|
||||
count += 2
|
||||
|
||||
|
||||
# Get allowed ip
|
||||
for i in conf_peer_data["Peers"]:
|
||||
db.update({"allowed_ip":i['AllowedIPs']}, peers.id == i["PublicKey"])
|
||||
|
||||
def getdb(config_name):
|
||||
get_conf_peers_data(config_name)
|
||||
db = TinyDB('db/' + config_name + '.json')
|
||||
result = db.all()
|
||||
result = sorted(result, key=lambda d: d['status'])
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
def get_conf_pub_key(config_name):
|
||||
try: pub_key = subprocess.check_output("wg show "+config_name+" public-key", shell=True, stderr=subprocess.STDOUT)
|
||||
except Exception: return "stopped"
|
||||
try:
|
||||
pub_key = subprocess.check_output("wg show " + config_name + " public-key", shell=True,
|
||||
stderr=subprocess.STDOUT)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
return pub_key.decode("UTF-8")
|
||||
|
||||
|
||||
def get_conf_listen_port(config_name):
|
||||
try: pub_key = subprocess.check_output("wg show "+config_name+" listen-port", shell=True, stderr=subprocess.STDOUT)
|
||||
except Exception: return "stopped"
|
||||
try:
|
||||
pub_key = subprocess.check_output("wg show " + config_name + " listen-port", shell=True,
|
||||
stderr=subprocess.STDOUT)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
return pub_key.decode("UTF-8")
|
||||
|
||||
|
||||
|
||||
def get_conf_total_data(config_name):
|
||||
try: data_usage = subprocess.check_output("wg show "+config_name+" transfer", shell=True, stderr=subprocess.STDOUT)
|
||||
except Exception: return "stopped"
|
||||
try:
|
||||
data_usage = subprocess.check_output("wg show " + config_name + " transfer", shell=True,
|
||||
stderr=subprocess.STDOUT)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
data_usage = data_usage.decode("UTF-8").split()
|
||||
count = 0
|
||||
upload_total = 0
|
||||
download_total = 0
|
||||
total = 0
|
||||
for i in range(int(len(data_usage)/3)):
|
||||
upload_total += int(data_usage[count+1])
|
||||
download_total += int(data_usage[count+2])
|
||||
for i in range(int(len(data_usage) / 3)):
|
||||
upload_total += int(data_usage[count + 1])
|
||||
download_total += int(data_usage[count + 2])
|
||||
count += 3
|
||||
|
||||
total = round(((((upload_total+download_total)/1024)/1024)/1024),4)
|
||||
upload_total = round(((((upload_total)/1024)/1024)/1024),4)
|
||||
download_total = round(((((download_total)/1024)/1024)/1024),4)
|
||||
|
||||
|
||||
total = round(((((upload_total + download_total) / 1024) / 1024) / 1024), 4)
|
||||
upload_total = round(((((upload_total) / 1024) / 1024) / 1024), 4)
|
||||
download_total = round(((((download_total) / 1024) / 1024) / 1024), 4)
|
||||
|
||||
return [total, upload_total, download_total]
|
||||
|
||||
|
||||
def get_conf_status(config_name):
|
||||
try: status = subprocess.check_output("wg show "+config_name, shell=True, stderr=subprocess.STDOUT)
|
||||
except Exception: return "stopped"
|
||||
else: return "running"
|
||||
try:
|
||||
status = subprocess.check_output("wg show " + config_name, shell=True, stderr=subprocess.STDOUT)
|
||||
except Exception:
|
||||
return "stopped"
|
||||
else:
|
||||
return "running"
|
||||
|
||||
|
||||
def get_conf_list():
|
||||
conf = []
|
||||
for i in os.listdir(conf_location):
|
||||
if ".conf" in i:
|
||||
i = i.replace('.conf','')
|
||||
temp = {"conf":i, "status":get_conf_status(i), "public_key": get_conf_pub_key(i)}
|
||||
i = i.replace('.conf', '')
|
||||
temp = {"conf": i, "status": get_conf_status(i), "public_key": get_conf_pub_key(i)}
|
||||
if temp['status'] == "running":
|
||||
temp['checked'] = 'checked'
|
||||
else: temp['checked'] = ""
|
||||
else:
|
||||
temp['checked'] = ""
|
||||
conf.append(temp)
|
||||
conf = sorted(conf, key=itemgetter('status'))
|
||||
return conf
|
||||
|
||||
@app.route('/',methods=['GET'])
|
||||
|
||||
def get_running_conf_list():
|
||||
conf = []
|
||||
for i in os.listdir(conf_location):
|
||||
if ".conf" in i:
|
||||
i = i.replace('.conf', '')
|
||||
if get_conf_status(i) == "running":
|
||||
conf.append(i)
|
||||
|
||||
return conf
|
||||
|
||||
|
||||
@app.route('/', methods=['GET'])
|
||||
def index():
|
||||
return render_template('index.html', conf=get_conf_list())
|
||||
|
||||
@ -197,9 +267,10 @@ def conf(config_name):
|
||||
conf_data['checked'] = "checked"
|
||||
return render_template('configuration.html', conf=get_conf_list(), conf_data=conf_data)
|
||||
|
||||
|
||||
@app.route('/get_config/<config_name>', methods=['GET'])
|
||||
def get_conf(config_name):
|
||||
db = TinyDB('db/'+config_name+'.json')
|
||||
db = TinyDB('db/' + config_name + '.json')
|
||||
|
||||
conf_data = {
|
||||
"name": config_name,
|
||||
@ -207,7 +278,7 @@ def get_conf(config_name):
|
||||
"total_data_usage": get_conf_total_data(config_name),
|
||||
"public_key": get_conf_pub_key(config_name),
|
||||
"listen_port": get_conf_listen_port(config_name),
|
||||
"peer_data":get_conf_peers_data(config_name),
|
||||
"peer_data": getdb(config_name),
|
||||
"running_peer": get_conf_running_peer_number(config_name),
|
||||
}
|
||||
if conf_data['status'] == "stopped":
|
||||
@ -221,11 +292,15 @@ def get_conf(config_name):
|
||||
def switch(config_name):
|
||||
status = get_conf_status(config_name)
|
||||
if status == "running":
|
||||
try: status = subprocess.check_output("wg-quick down "+config_name, shell=True)
|
||||
except Exception: return redirect('/')
|
||||
try:
|
||||
status = subprocess.check_output("wg-quick down " + config_name, shell=True)
|
||||
except Exception:
|
||||
return redirect('/')
|
||||
elif status == "stopped":
|
||||
try: status = subprocess.check_output("wg-quick up "+config_name, shell=True)
|
||||
except Exception: return redirect('/')
|
||||
try:
|
||||
status = subprocess.check_output("wg-quick up " + config_name, shell=True)
|
||||
except Exception:
|
||||
return redirect('/')
|
||||
return redirect('/')
|
||||
|
||||
|
||||
@ -239,18 +314,21 @@ def add_peer(config_name):
|
||||
return "Key already exist."
|
||||
else:
|
||||
status = ""
|
||||
try:
|
||||
status = subprocess.check_output("wg set "+config_name+" peer "+public_key+" allowed-ips "+allowed_ips, shell=True, stderr=subprocess.STDOUT)
|
||||
status = subprocess.check_output("wg-quick save "+config_name, shell=True, stderr=subprocess.STDOUT)
|
||||
try:
|
||||
status = subprocess.check_output(
|
||||
"wg set " + config_name + " peer " + public_key + " allowed-ips " + allowed_ips, shell=True,
|
||||
stderr=subprocess.STDOUT)
|
||||
status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT)
|
||||
return "true"
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return exc.output.strip()
|
||||
|
||||
# return redirect('/configuration/'+config_name)
|
||||
|
||||
|
||||
@app.route('/remove_peer/<config_name>', methods=['POST'])
|
||||
def remove_peer(config_name):
|
||||
db = TinyDB("db/"+config_name+".json")
|
||||
db = TinyDB("db/" + config_name + ".json")
|
||||
peers = Query()
|
||||
data = request.get_json()
|
||||
delete_key = data['peer_id']
|
||||
@ -259,37 +337,39 @@ def remove_peer(config_name):
|
||||
return "This key does not exist"
|
||||
else:
|
||||
try:
|
||||
status = subprocess.check_output("wg set "+config_name+" peer "+delete_key+" remove", shell=True, stderr=subprocess.STDOUT)
|
||||
status = subprocess.check_output("wg-quick save "+config_name, shell=True, stderr=subprocess.STDOUT)
|
||||
status = subprocess.check_output("wg set " + config_name + " peer " + delete_key + " remove", shell=True,
|
||||
stderr=subprocess.STDOUT)
|
||||
status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT)
|
||||
db.remove(peers.id == delete_key)
|
||||
return "true"
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return exc.output.strip()
|
||||
|
||||
|
||||
@app.route('/save_peer_name/<config_name>', methods=['POST'])
|
||||
def save_peer_name(config_name):
|
||||
data = request.get_json()
|
||||
id = data['id']
|
||||
name = data['name']
|
||||
db = TinyDB("db/"+config_name+".json")
|
||||
db = TinyDB("db/" + config_name + ".json")
|
||||
peers = Query()
|
||||
|
||||
db.update({"name": name}, peers.id == id)
|
||||
return id + " " + name
|
||||
|
||||
|
||||
@app.route('/get_peer_name/<config_name>', methods=['POST'])
|
||||
def get_peer_name(config_name):
|
||||
data = request.get_json()
|
||||
id = data['id']
|
||||
db = TinyDB("db/"+config_name+".json")
|
||||
db = TinyDB("db/" + config_name + ".json")
|
||||
peers = Query()
|
||||
result = db.search(peers.id == id)
|
||||
return result[0]['name']
|
||||
# db.update({"name": name}, peers.id == id)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
app.run(host='0.0.0.0',debug=False, port=10086)
|
||||
if __name__ == "__main__":
|
||||
app.run(host='0.0.0.0', debug=False, port=10086)
|
||||
# for i in get_running_conf_list():
|
||||
# p = Process(target=get_conf_peers_data, args=(i,))
|
||||
# p.start()
|
@ -9,6 +9,7 @@
|
||||
<link href="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.0.1/dist/chart.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -171,7 +172,7 @@
|
||||
load_data();
|
||||
setInterval(function(){
|
||||
load_data();
|
||||
}, 10000)
|
||||
}, 15000)
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
|
@ -52,29 +52,42 @@
|
||||
{% for i in conf_data['peer_data']%}
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
{% if not i['name']%}
|
||||
{{ "Untitled Peer" }}
|
||||
{% else %}
|
||||
{{i['name']}}
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="card-header-body ">
|
||||
{% if not i['name']%}
|
||||
{{ "Untitled Peer" }}
|
||||
{% else %}
|
||||
{{i['name']}}
|
||||
{% endif %}
|
||||
<span class="dot dot-{{i['status']}}"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" style="text-align: right">
|
||||
<p class="text-primary" style="text-transform: uppercase; display: inline-block; margin-bottom: 0; margin-right: 1rem"><i class="bi bi-arrow-down-right"></i> {{i['total_receive']}} GB</p>
|
||||
<p class="text-success" style="text-transform: uppercase; display: inline-block; margin-bottom: 0"><i class="bi bi-arrow-up-right"></i> {{i['total_sent']}} GB</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<small class="text-muted"><strong>STATUS</strong></small>
|
||||
<h6 style="text-transform: uppercase;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6>
|
||||
</div>
|
||||
{# <div class="col-sm">#}
|
||||
{# <small class="text-muted"><strong>STATUS</strong></small>#}
|
||||
{# <h6 style="text-transform: uppercase;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6>#}
|
||||
{# </div>#}
|
||||
<div class="col-sm">
|
||||
<small class="text-muted"><strong>PEER</strong></small>
|
||||
<h6><samp>{{i['id']}}</samp></h6>
|
||||
<h6><samp class="ml-auto">{{i['id']}}</samp></h6>
|
||||
</div>
|
||||
|
||||
<div class="w-100"></div>
|
||||
<div class="col-sm">
|
||||
<small class="text-muted"><strong>ALLOWED IP</strong></small>
|
||||
<h6 style="text-transform: uppercase;">{{i['allowed_ip']}}</h6>
|
||||
</div>
|
||||
<div class="w-100"></div>
|
||||
|
||||
<div class="col-sm">
|
||||
<small class="text-muted"><strong>LATEST HANDSHAKE</strong></small>
|
||||
<h6 style="text-transform: uppercase;">{{i['latest_handshake']}}</h6>
|
||||
@ -83,20 +96,51 @@
|
||||
<small class="text-muted"><strong>END POINT</strong></small>
|
||||
<h6 style="text-transform: uppercase;">{{i['endpoint']}}</h6>
|
||||
</div>
|
||||
<div class="w-100"></div>
|
||||
{# <div class="col-sm">#}
|
||||
{# <small class="text-muted"><strong>TOTAL DATA USAGE</strong></small>#}
|
||||
{# <h6 style="text-transform: uppercase;">{{i['total_data']}} GB</h6>#}
|
||||
{# </div>#}
|
||||
<div class="col-sm">
|
||||
<small class="text-muted"><strong>TOTAL RECEIVED</strong></small>
|
||||
<h6 style="text-transform: uppercase;"><i class="bi bi-arrow-down-right"></i> {{i['total_receive']}} GB</h6>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<small class="text-muted"><strong>TOTAL SENT</strong></small>
|
||||
<h6 style="text-transform: uppercase;"><i class="bi bi-arrow-up-right"></i> {{i['total_sent']}} GB</h6>
|
||||
</div>
|
||||
<div class="w-100"></div>
|
||||
{# <div class="w-100"></div>#}
|
||||
{# <div class="col-sm">#}
|
||||
{# <canvas id="{{ i['id'] }}_chart" style="width: 100%" height="200"></canvas>#}
|
||||
{# </div>#}
|
||||
{# <script>#}
|
||||
{# var data = "{{ i['traffic'] }}";#}
|
||||
{# data = data.replaceAll("'", "\"")#}
|
||||
{# data = JSON.parse(data)#}
|
||||
{# y_label = [];#}
|
||||
{# data_point = [];#}
|
||||
{# for (var i = 1; i < data.length; i++){#}
|
||||
{# y_label.push(data[i]['time'])#}
|
||||
{# data_point.push(data[i]['total_sent'] - data[i-1]['total_sent']);#}
|
||||
{# }#}
|
||||
{# var ctx = document.getElementById('{{ i['id'] }}_chart');#}
|
||||
{# var plot_data =#}
|
||||
{# {#}
|
||||
{# labels: y_label,#}
|
||||
{# datasets:[{#}
|
||||
{# label: "Traffic",#}
|
||||
{# data: data_point,#}
|
||||
{# fill: false,#}
|
||||
{# borderColor: 'rgb(75, 192, 192)',#}
|
||||
{# tension: 0.1#}
|
||||
{# }],#}
|
||||
{# options: {#}
|
||||
{# scales: {#}
|
||||
{# xAxes: [{#}
|
||||
{# type: 'time',#}
|
||||
{# ticks: {#}
|
||||
{# autoSkip: true,#}
|
||||
{# maxTicksLimit: 12#}
|
||||
{# }#}
|
||||
{# }]#}
|
||||
{# },#}
|
||||
{# responsive: true#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
{##}
|
||||
{# var myLineChart = new Chart(ctx, {#}
|
||||
{# type: 'line',#}
|
||||
{# data: plot_data#}
|
||||
{# });#}
|
||||
{# </script>#}
|
||||
<div class="w-100"></div>
|
||||
<div class="col-sm">
|
||||
<!-- <small class="text-muted"><strong>ACTION</strong></small> -->
|
||||
<div class="button-group">
|
||||
|
Loading…
Reference in New Issue
Block a user