mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 07:10:09 +01:00
Finished some building blocks on the new version
This commit is contained in:
parent
1e88491ca1
commit
ed3bb6429b
@ -344,7 +344,7 @@ def get_all_peers_data(config_name):
|
||||
"total_data": 0,
|
||||
"endpoint": "N/A",
|
||||
"status": "stopped",
|
||||
"latest_handshake": "N/A",
|
||||
"latest_handshake": "No Handshake",
|
||||
"allowed_ip": "N/A",
|
||||
"cumu_receive": 0,
|
||||
"cumu_sent": 0,
|
||||
|
@ -27,6 +27,8 @@ import pyotp
|
||||
from flask import Flask, request, render_template, redirect, url_for, session, jsonify, g
|
||||
from flask.json.provider import JSONProvider
|
||||
from flask_qrcode import QRcode
|
||||
from json import JSONEncoder
|
||||
|
||||
from icmplib import ping, traceroute
|
||||
|
||||
# Import other python files
|
||||
@ -35,6 +37,7 @@ import threading
|
||||
from sqlalchemy.orm import mapped_column, declarative_base, Session
|
||||
from sqlalchemy import FLOAT, INT, VARCHAR, select, MetaData, DATETIME
|
||||
from sqlalchemy import create_engine, inspect
|
||||
from flask.json.provider import DefaultJSONProvider
|
||||
|
||||
DASHBOARD_VERSION = 'v3.1'
|
||||
CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
|
||||
@ -55,20 +58,31 @@ app.secret_key = secrets.token_urlsafe(32)
|
||||
# Enable QR Code Generator
|
||||
QRcode(app)
|
||||
|
||||
|
||||
class ModelEncoder(JSONEncoder):
|
||||
def default(self, o: Any) -> Any:
|
||||
if hasattr(o, 'toJson'):
|
||||
return o.toJson()
|
||||
else:
|
||||
return super(ModelEncoder, self).default(o)
|
||||
|
||||
|
||||
'''
|
||||
Classes
|
||||
'''
|
||||
|
||||
|
||||
# Base = declarative_base(class_registry=dict())
|
||||
|
||||
|
||||
class CustomJsonEncoder(JSONProvider):
|
||||
def dumps(self, obj, **kwargs):
|
||||
if type(obj) == WireguardConfiguration:
|
||||
return obj.toJSON()
|
||||
return json.dumps(obj)
|
||||
class CustomJsonEncoder(DefaultJSONProvider):
|
||||
def __init__(self, app):
|
||||
super().__init__(app)
|
||||
|
||||
def loads(self, obj, **kwargs):
|
||||
return json.loads(obj, **kwargs)
|
||||
def default(self, o):
|
||||
if isinstance(o, WireguardConfiguration) or isinstance(o, Peer):
|
||||
return o.toJson()
|
||||
return super().default(self, o)
|
||||
|
||||
|
||||
app.json = CustomJsonEncoder(app)
|
||||
@ -100,11 +114,10 @@ class Peer:
|
||||
self.preshared_key = tableData["preshared_key"]
|
||||
|
||||
def toJson(self):
|
||||
return json.dumps(self, default=lambda o: o.__dict__,
|
||||
sort_keys=True, indent=4)
|
||||
return self.__dict__
|
||||
|
||||
def __repr__(self):
|
||||
return self.toJson()
|
||||
return str(self.toJson())
|
||||
|
||||
|
||||
class WireguardConfiguration:
|
||||
@ -178,7 +191,7 @@ class WireguardConfiguration:
|
||||
|
||||
with open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
||||
f"{self.Name}.conf"), "w+") as configFile:
|
||||
print(self.__parser.sections())
|
||||
# print(self.__parser.sections())
|
||||
self.__parser.write(configFile)
|
||||
|
||||
self.Peers = []
|
||||
@ -189,7 +202,7 @@ class WireguardConfiguration:
|
||||
|
||||
def __createDatabase(self):
|
||||
existingTables = cursor.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
|
||||
existingTables = itertools.chain(*existingTables)
|
||||
existingTables = [t['name'] for t in existingTables]
|
||||
if self.Name not in existingTables:
|
||||
cursor.execute(
|
||||
"""
|
||||
@ -221,18 +234,32 @@ class WireguardConfiguration:
|
||||
""" % self.Name
|
||||
)
|
||||
sqldb.commit()
|
||||
|
||||
if f'{self.Name}_transfer' not in existingTables:
|
||||
cursor.execute(
|
||||
"""
|
||||
CREATE TABLE %s_transfer (
|
||||
id VARCHAR NOT NULL, total_receive FLOAT NULL,
|
||||
id VARCHAR NOT NULL, total_receive FLOAT NULL,
|
||||
total_sent FLOAT NULL, total_data FLOAT NULL,
|
||||
cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, time DATETIME
|
||||
)
|
||||
""" % self.Name
|
||||
)
|
||||
sqldb.commit()
|
||||
if f'{self.Name}_deleted' not in existingTables:
|
||||
cursor.execute(
|
||||
"""
|
||||
CREATE TABLE %s_deleted (
|
||||
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)
|
||||
)
|
||||
""" % self.Name
|
||||
)
|
||||
sqldb.commit()
|
||||
|
||||
def __getPublicKey(self) -> str:
|
||||
return subprocess.check_output(['wg', 'pubkey'], input=self.PrivateKey.encode()).decode().strip('\n')
|
||||
@ -242,6 +269,7 @@ class WireguardConfiguration:
|
||||
return self.Status
|
||||
|
||||
def __getPeers(self):
|
||||
self.Peers = []
|
||||
with open(os.path.join(WG_CONF_PATH, f'{self.Name}.conf'), 'r') as configFile:
|
||||
p = []
|
||||
pCounter = -1
|
||||
@ -277,7 +305,7 @@ class WireguardConfiguration:
|
||||
"endpoint": "N/A",
|
||||
"status": "stopped",
|
||||
"latest_handshake": "N/A",
|
||||
"allowed_ip": "N/A",
|
||||
"allowed_ip": i.get("AllowedIPs", "N/A"),
|
||||
"cumu_receive": 0,
|
||||
"cumu_sent": 0,
|
||||
"cumu_data": 0,
|
||||
@ -296,11 +324,117 @@ class WireguardConfiguration:
|
||||
""" % self.Name
|
||||
, newPeer)
|
||||
sqldb.commit()
|
||||
self.Peers.append(Peer(newPeer))
|
||||
else:
|
||||
cursor.execute("UPDATE %s SET allowed_ip = ? WHERE id = ?" % self.Name,
|
||||
(i.get("AllowedIPs", "N/A"), i['PublicKey'],))
|
||||
sqldb.commit()
|
||||
self.Peers.append(Peer(checkIfExist))
|
||||
except ValueError:
|
||||
pass
|
||||
print(self.Peers)
|
||||
|
||||
def __savePeers(self):
|
||||
for i in self.Peers:
|
||||
d = i.toJson()
|
||||
sqldb.execute(
|
||||
'''
|
||||
UPDATE %s SET private_key = :private_key,
|
||||
DNS = :DNS, endpoint_allowed_ip = :endpoint_allowed_ip, name = :name,
|
||||
total_receive = :total_receive, total_sent = :total_sent, total_data = :total_data,
|
||||
endpoint = :endpoint, status = :status, latest_handshake = :latest_handshake,
|
||||
allowed_ip = :allowed_ip, cumu_receive = :cumu_receive, cumu_sent = :cumu_sent,
|
||||
cumu_data = :cumu_data, mtu = :mtu, keepalive = :keepalive,
|
||||
remote_endpoint = :remote_endpoint, preshared_key = :preshared_key WHERE id = :id
|
||||
''' % self.Name, d
|
||||
)
|
||||
sqldb.commit()
|
||||
|
||||
def getPeersLatestHandshake(self):
|
||||
try:
|
||||
latestHandshake = subprocess.check_output(f"wg show {self.Name} latest-handshakes",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError:
|
||||
return "stopped"
|
||||
latestHandshake = latestHandshake.decode("UTF-8").split()
|
||||
count = 0
|
||||
now = datetime.now()
|
||||
time_delta = timedelta(minutes=2)
|
||||
for _ in range(int(len(latestHandshake) / 2)):
|
||||
minus = now - datetime.fromtimestamp(int(latestHandshake[count + 1]))
|
||||
if minus < time_delta:
|
||||
status = "running"
|
||||
else:
|
||||
status = "stopped"
|
||||
if int(latestHandshake[count + 1]) > 0:
|
||||
sqldb.execute("UPDATE %s SET latest_handshake = ?, status = ? WHERE id= ?" % self.Name
|
||||
, (str(minus).split(".", maxsplit=1)[0], status, latestHandshake[count],))
|
||||
else:
|
||||
sqldb.execute("UPDATE %s SET latest_handshake = 'No Handshake', status = ? WHERE id= ?" % self.Name
|
||||
, (status, latestHandshake[count],))
|
||||
sqldb.commit()
|
||||
count += 2
|
||||
|
||||
def getPeersTransfer(self):
|
||||
try:
|
||||
data_usage = subprocess.check_output(f"wg show {self.Name} transfer",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
data_usage = data_usage.decode("UTF-8").split("\n")
|
||||
data_usage = [p.split("\t") for p in data_usage]
|
||||
for i in range(len(data_usage)):
|
||||
if len(data_usage[i]) == 3:
|
||||
cur_i = cursor.execute(
|
||||
"SELECT total_receive, total_sent, cumu_receive, cumu_sent, status FROM %s WHERE id= ? "
|
||||
% self.Name, (data_usage[i][0],)).fetchone()
|
||||
if cur_i is not None:
|
||||
total_sent = cur_i['total_sent']
|
||||
total_receive = cur_i['total_receive']
|
||||
cur_total_sent = round(int(data_usage[i][2]) / (1024 ** 3), 4)
|
||||
cur_total_receive = round(int(data_usage[i][1]) / (1024 ** 3), 4)
|
||||
cumulative_receive = cur_i['cumu_receive'] + total_receive
|
||||
cumulative_sent = cur_i['cumu_sent'] + total_sent
|
||||
if total_sent <= cur_total_sent and total_receive <= cur_total_receive:
|
||||
total_sent = cur_total_sent
|
||||
total_receive = cur_total_receive
|
||||
else:
|
||||
cursor.execute(
|
||||
"UPDATE %s SET cumu_receive = ?, cumu_sent = ?, cumu_data = ? WHERE id = ?" %
|
||||
self.Name, (round(cumulative_receive, 4), round(cumulative_sent, 4),
|
||||
round(cumulative_sent + cumulative_receive, 4),
|
||||
data_usage[i][0],))
|
||||
total_sent = 0
|
||||
total_receive = 0
|
||||
cursor.execute(
|
||||
"UPDATE %s SET total_receive = ?, total_sent = ?, total_data = ? WHERE id = ?"
|
||||
% self.Name, (round(total_receive, 4), round(total_sent, 4),
|
||||
round(total_receive + total_sent, 4), data_usage[i][0],))
|
||||
now = datetime.now()
|
||||
now_string = now.strftime("%d/%m/%Y %H:%M:%S")
|
||||
cursor.execute(f'''
|
||||
INSERT INTO %s_transfer
|
||||
(id, total_receive, total_sent, total_data,
|
||||
cumu_receive, cumu_sent, cumu_data, time)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''' % self.Name, (data_usage[i][0], round(total_receive, 4), round(total_sent, 4),
|
||||
round(total_receive + total_sent, 4), round(cumulative_receive, 4),
|
||||
round(cumulative_sent, 4),
|
||||
round(cumulative_sent + cumulative_receive, 4), now_string,))
|
||||
sqldb.commit()
|
||||
except Exception as e:
|
||||
print("Error" + str(e))
|
||||
|
||||
def getPeersEndpoint(self):
|
||||
try:
|
||||
data_usage = subprocess.check_output(f"wg show {self.Name} endpoints",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError:
|
||||
return "stopped"
|
||||
data_usage = data_usage.decode("UTF-8").split()
|
||||
count = 0
|
||||
for _ in range(int(len(data_usage) / 2)):
|
||||
sqldb.execute("UPDATE %s SET endpoint = ? WHERE id = ?" % self.Name
|
||||
, (data_usage[count + 1], data_usage[count],))
|
||||
sqldb.commit()
|
||||
count += 2
|
||||
|
||||
def toggleConfiguration(self) -> [bool, str]:
|
||||
self.getStatus()
|
||||
@ -320,7 +454,11 @@ class WireguardConfiguration:
|
||||
self.getStatus()
|
||||
return True, None
|
||||
|
||||
def toJSON(self):
|
||||
def getPeers(self):
|
||||
self.__getPeers()
|
||||
return self.Peers
|
||||
|
||||
def toJson(self):
|
||||
self.Status = self.getStatus()
|
||||
return {
|
||||
"Status": self.Status,
|
||||
@ -478,7 +616,7 @@ class DashboardConfig:
|
||||
|
||||
return True, self.__config[section][key]
|
||||
|
||||
def toJSON(self) -> dict[str, dict[Any, Any]]:
|
||||
def toJson(self) -> dict[str, dict[Any, Any]]:
|
||||
the_dict = {}
|
||||
|
||||
for section in self.__config.sections():
|
||||
@ -516,73 +654,6 @@ def _strToBool(value: str) -> bool:
|
||||
return value.lower() in ("yes", "true", "t", "1", 1)
|
||||
|
||||
|
||||
# def _createPeerModel(wgConfigName):
|
||||
# return type(wgConfigName, (Base,), {
|
||||
# "id": mapped_column(VARCHAR, primary_key=True),
|
||||
# "private_key": mapped_column(VARCHAR),
|
||||
# "DNS": mapped_column(VARCHAR),
|
||||
# "endpoint_allowed_ip": mapped_column(VARCHAR),
|
||||
# "name": mapped_column(VARCHAR),
|
||||
# "total_receive": mapped_column(FLOAT),
|
||||
# "total_sent": mapped_column(FLOAT),
|
||||
# "total_data": mapped_column(FLOAT),
|
||||
# "endpoint": mapped_column(VARCHAR),
|
||||
# "status": mapped_column(VARCHAR),
|
||||
# "latest_handshake": mapped_column(VARCHAR),
|
||||
# "allowed_ip": mapped_column(VARCHAR),
|
||||
# "cumu_receive": mapped_column(FLOAT),
|
||||
# "cumu_sent": mapped_column(FLOAT),
|
||||
# "cumu_data": mapped_column(FLOAT),
|
||||
# "mtu": mapped_column(INT),
|
||||
# "keepalive": mapped_column(INT),
|
||||
# "remote_endpoint": mapped_column(VARCHAR),
|
||||
# "preshared_key": mapped_column(VARCHAR),
|
||||
# "__tablename__": wgConfigName,
|
||||
# "__table_args__": {'extend_existing': True}
|
||||
# })
|
||||
#
|
||||
#
|
||||
# def _createRestrictedPeerModel(wgConfigName):
|
||||
# return type(wgConfigName + "_restrict_access", (Base,), {
|
||||
# "id": mapped_column(VARCHAR, primary_key=True),
|
||||
# "private_key": mapped_column(VARCHAR),
|
||||
# "DNS": mapped_column(VARCHAR),
|
||||
# "endpoint_allowed_ip": mapped_column(VARCHAR),
|
||||
# "name": mapped_column(VARCHAR),
|
||||
# "total_receive": mapped_column(FLOAT),
|
||||
# "total_sent": mapped_column(FLOAT),
|
||||
# "total_data": mapped_column(FLOAT),
|
||||
# "endpoint": mapped_column(VARCHAR),
|
||||
# "status": mapped_column(VARCHAR),
|
||||
# "latest_handshake": mapped_column(VARCHAR),
|
||||
# "allowed_ip": mapped_column(VARCHAR),
|
||||
# "cumu_receive": mapped_column(FLOAT),
|
||||
# "cumu_sent": mapped_column(FLOAT),
|
||||
# "cumu_data": mapped_column(FLOAT),
|
||||
# "mtu": mapped_column(INT),
|
||||
# "keepalive": mapped_column(INT),
|
||||
# "remote_endpoint": mapped_column(VARCHAR),
|
||||
# "preshared_key": mapped_column(VARCHAR),
|
||||
# "__tablename__": wgConfigName,
|
||||
# "__table_args__": {'extend_existing': True}
|
||||
# })
|
||||
#
|
||||
#
|
||||
# def _createPeerTransferModel(wgConfigName):
|
||||
# return type(wgConfigName + "_transfer", (Base,), {
|
||||
# "id": mapped_column(VARCHAR, primary_key=True),
|
||||
# "total_receive": mapped_column(FLOAT),
|
||||
# "total_sent": mapped_column(FLOAT),
|
||||
# "total_data": mapped_column(FLOAT),
|
||||
# "cumu_receive": mapped_column(FLOAT),
|
||||
# "cumu_sent": mapped_column(FLOAT),
|
||||
# "cumu_data": mapped_column(FLOAT),
|
||||
# "time": mapped_column(DATETIME),
|
||||
# "__tablename__": wgConfigName + "_transfer",
|
||||
# "__table_args__": {'extend_existing': True},
|
||||
# })
|
||||
|
||||
|
||||
def _regexMatch(regex, text):
|
||||
pattern = re.compile(regex)
|
||||
return pattern.search(text) is not None
|
||||
@ -667,7 +738,7 @@ def API_SignOut():
|
||||
@app.route('/api/getWireguardConfigurations', methods=["GET"])
|
||||
def API_getWireguardConfigurations():
|
||||
WireguardConfigurations = _getConfigurationList()
|
||||
return ResponseObject(data=[wc.toJSON() for wc in WireguardConfigurations.values()])
|
||||
return ResponseObject(data=[wc for wc in WireguardConfigurations.values()])
|
||||
|
||||
|
||||
@app.route('/api/addWireguardConfiguration', methods=["POST"])
|
||||
@ -728,7 +799,7 @@ def API_toggleWireguardConfiguration():
|
||||
|
||||
@app.route('/api/getDashboardConfiguration', methods=["GET"])
|
||||
def API_getDashboardConfiguration():
|
||||
return ResponseObject(data=DashboardConfig.toJSON())
|
||||
return ResponseObject(data=DashboardConfig.toJson())
|
||||
|
||||
|
||||
@app.route('/api/updateDashboardConfiguration', methods=["POST"])
|
||||
@ -756,6 +827,17 @@ def API_updateDashboardConfigurationItem():
|
||||
return ResponseObject()
|
||||
|
||||
|
||||
@app.route('/api/getWireguardConfigurationInfo', methods=["GET"])
|
||||
def API_getConfigurationInfo():
|
||||
configurationName = request.args.get("configurationName")
|
||||
if not configurationName or configurationName not in WireguardConfigurations.keys():
|
||||
return ResponseObject(False, "Please provide configuration name")
|
||||
return ResponseObject(data={
|
||||
"configurationInfo": WireguardConfigurations[configurationName],
|
||||
"configurationPeers": WireguardConfigurations[configurationName].getPeers()
|
||||
})
|
||||
|
||||
|
||||
@app.route('/api/getDashboardTheme')
|
||||
def API_getDashboardTheme():
|
||||
return ResponseObject(data=DashboardConfig.GetConfig("Server", "dashboard_theme")[1])
|
||||
@ -821,60 +903,20 @@ def index():
|
||||
|
||||
|
||||
def backGroundThread():
|
||||
print("Waiting 5 sec")
|
||||
time.sleep(5)
|
||||
while True:
|
||||
for c in WireguardConfigurations.values():
|
||||
if c.getStatus():
|
||||
try:
|
||||
data_usage = subprocess.check_output(f"wg show {c.Name} transfer",
|
||||
shell=True, stderr=subprocess.STDOUT)
|
||||
data_usage = data_usage.decode("UTF-8").split("\n")
|
||||
data_usage = [p.split("\t") for p in data_usage]
|
||||
for i in range(len(data_usage)):
|
||||
if len(data_usage[i]) == 3:
|
||||
cur_i = cursor.execute(
|
||||
"SELECT total_receive, total_sent, cumu_receive, cumu_sent, status FROM %s WHERE id= ? "
|
||||
% c.Name, (data_usage[i][0], )).fetchone()
|
||||
if cur_i is not None:
|
||||
total_sent = cur_i['total_sent']
|
||||
total_receive = cur_i['total_receive']
|
||||
cur_total_sent = round(int(data_usage[i][2]) / (1024 ** 3), 4)
|
||||
cur_total_receive = round(int(data_usage[i][1]) / (1024 ** 3), 4)
|
||||
cumulative_receive = cur_i['cumu_receive'] + total_receive
|
||||
cumulative_sent = cur_i['cumu_sent'] + total_sent
|
||||
if total_sent <= cur_total_sent and total_receive <= cur_total_receive:
|
||||
total_sent = cur_total_sent
|
||||
total_receive = cur_total_receive
|
||||
else:
|
||||
cursor.execute(
|
||||
"UPDATE %s SET cumu_receive = ?, cumu_sent = ?, cumu_data = ? WHERE id = ?" %
|
||||
c.Name, (round(cumulative_receive, 4), round(cumulative_sent, 4),
|
||||
round(cumulative_sent + cumulative_receive, 4),
|
||||
data_usage[i][0], ))
|
||||
total_sent = 0
|
||||
total_receive = 0
|
||||
cursor.execute(
|
||||
"UPDATE %s SET total_receive = ?, total_sent = ?, total_data = ? WHERE id = ?"
|
||||
% c.Name, (round(total_receive, 4), round(total_sent, 4),
|
||||
round(total_receive + total_sent, 4), data_usage[i][0], ))
|
||||
now = datetime.now()
|
||||
now_string = now.strftime("%d/%m/%Y %H:%M:%S")
|
||||
cursor.execute(f'''
|
||||
INSERT INTO %s_transfer
|
||||
(id, total_receive, total_sent, total_data,
|
||||
cumu_receive, cumu_sent, cumu_data, time)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''' % c.Name, (data_usage[i][0], round(total_receive, 4), round(total_sent, 4),
|
||||
round(total_receive + total_sent, 4), round(cumulative_receive, 4),
|
||||
round(cumulative_sent, 4),
|
||||
round(cumulative_sent + cumulative_receive, 4), now_string, ))
|
||||
sqldb.commit()
|
||||
print(data_usage)
|
||||
pass
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
time.sleep(30)
|
||||
with app.app_context():
|
||||
print("Waiting 5 sec")
|
||||
time.sleep(5)
|
||||
while True:
|
||||
for c in WireguardConfigurations.values():
|
||||
if c.getStatus():
|
||||
|
||||
try:
|
||||
c.getPeersTransfer()
|
||||
c.getPeersLatestHandshake()
|
||||
c.getPeersEndpoint()
|
||||
except Exception as e:
|
||||
print("Error: " + str(e))
|
||||
time.sleep(10)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@ -891,4 +933,4 @@ if __name__ == "__main__":
|
||||
bgThread.daemon = True
|
||||
bgThread.start()
|
||||
|
||||
app.run(host=app_ip, debug=False, port=app_port)
|
||||
app.run(host=app_ip, debug=True, port=app_port)
|
||||
|
34
src/static/app/package-lock.json
generated
34
src/static/app/package-lock.json
generated
@ -11,10 +11,12 @@
|
||||
"bootstrap": "^5.3.2",
|
||||
"bootstrap-icons": "^1.11.2",
|
||||
"cidr-tools": "^7.0.4",
|
||||
"dayjs": "^1.11.10",
|
||||
"pinia": "^2.1.7",
|
||||
"qrcode": "^1.5.3",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.3.11",
|
||||
"vue-chartjs": "^5.3.0",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -406,6 +408,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
||||
},
|
||||
"node_modules/@kurkle/color": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
|
||||
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
@ -756,6 +764,18 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz",
|
||||
"integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": ">=7"
|
||||
}
|
||||
},
|
||||
"node_modules/cidr-tools": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cidr-tools/-/cidr-tools-7.0.4.tgz",
|
||||
@ -798,6 +818,11 @@
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.10",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
|
||||
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
|
||||
},
|
||||
"node_modules/decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
@ -1278,6 +1303,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-chartjs": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.0.tgz",
|
||||
"integrity": "sha512-8XqX0JU8vFZ+WA2/knz4z3ThClduni2Nm0BMe2u0mXgTfd9pXrmJ07QBI+WAij5P/aPmPMX54HCE1seWL37ZdQ==",
|
||||
"peerDependencies": {
|
||||
"chart.js": "^4.1.1",
|
||||
"vue": "^3.0.0-0 || ^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz",
|
||||
|
@ -12,10 +12,12 @@
|
||||
"bootstrap": "^5.3.2",
|
||||
"bootstrap-icons": "^1.11.2",
|
||||
"cidr-tools": "^7.0.4",
|
||||
"dayjs": "^1.11.10",
|
||||
"pinia": "^2.1.7",
|
||||
"qrcode": "^1.5.3",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.3.11",
|
||||
"vue-chartjs": "^5.3.0",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -0,0 +1,47 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "peer",
|
||||
props: {
|
||||
Peer: Object
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card shadow rounded-3">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0">
|
||||
<div class="dot ms-0" :class="{active: Peer.status === 'running'}"></div>
|
||||
<div style="font-size: 0.8rem" class="ms-auto d-flex gap-2">
|
||||
<span class="text-primary">
|
||||
<i class="bi bi-arrow-down"></i><strong>
|
||||
{{(Peer.cumu_receive + Peer.total_receive).toFixed(4)}}</strong> GB
|
||||
</span>
|
||||
<span class="text-success">
|
||||
<i class="bi bi-arrow-up"></i><strong>
|
||||
{{(Peer.cumu_sent + Peer.total_sent).toFixed(4)}}</strong> GB
|
||||
</span>
|
||||
<span class="text-secondary" v-if="Peer.latest_handshake !== 'No Handshake'">
|
||||
<i class="bi bi-arrows-angle-contract"></i>
|
||||
{{Peer.latest_handshake}} ago
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body pt-1" style="font-size: 0.9rem">
|
||||
<h5>
|
||||
{{Peer.name ? Peer.name : 'Untitled Peer'}}
|
||||
</h5>
|
||||
<div class="mb-2">
|
||||
<small class="text-muted">Public Key</small>
|
||||
<p class="mb-0"><samp>{{Peer.id}}</samp></p>
|
||||
</div>
|
||||
<div>
|
||||
<small class="text-muted">Allowed IP</small>
|
||||
<p class="mb-0"><samp>{{Peer.allowed_ip}}</samp></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,12 +1,368 @@
|
||||
<script>
|
||||
import {fetchGet} from "@/utilities/fetch.js";
|
||||
import Peer from "@/components/configurationComponents/peer.vue";
|
||||
import { Line, Bar } from 'vue-chartjs'
|
||||
import {
|
||||
Chart,
|
||||
ArcElement,
|
||||
LineElement,
|
||||
BarElement,
|
||||
PointElement,
|
||||
BarController,
|
||||
BubbleController,
|
||||
DoughnutController,
|
||||
LineController,
|
||||
PieController,
|
||||
PolarAreaController,
|
||||
RadarController,
|
||||
ScatterController,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
LogarithmicScale,
|
||||
RadialLinearScale,
|
||||
TimeScale,
|
||||
TimeSeriesScale,
|
||||
Decimation,
|
||||
Filler,
|
||||
Legend,
|
||||
Title,
|
||||
Tooltip
|
||||
} from 'chart.js';
|
||||
|
||||
Chart.register(
|
||||
ArcElement,
|
||||
LineElement,
|
||||
BarElement,
|
||||
PointElement,
|
||||
BarController,
|
||||
BubbleController,
|
||||
DoughnutController,
|
||||
LineController,
|
||||
PieController,
|
||||
PolarAreaController,
|
||||
RadarController,
|
||||
ScatterController,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
LogarithmicScale,
|
||||
RadialLinearScale,
|
||||
TimeScale,
|
||||
TimeSeriesScale,
|
||||
Decimation,
|
||||
Filler,
|
||||
Legend,
|
||||
Title,
|
||||
Tooltip
|
||||
);
|
||||
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export default {
|
||||
name: "configuration"
|
||||
name: "configuration",
|
||||
components: {Peer, Line, Bar},
|
||||
data(){
|
||||
return {
|
||||
loading: false,
|
||||
error: null,
|
||||
configurationInfo: [],
|
||||
configurationPeers: [],
|
||||
historyDataSentDifference: [],
|
||||
historyDataReceivedDifference: [],
|
||||
|
||||
historySentData: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Data Sent',
|
||||
data: [],
|
||||
fill: false,
|
||||
borderColor: '#198754',
|
||||
tension: 0
|
||||
},
|
||||
],
|
||||
},
|
||||
historyReceiveData: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Data Received',
|
||||
data: [],
|
||||
fill: false,
|
||||
borderColor: '#0d6efd',
|
||||
tension: 0
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route.params': {
|
||||
immediate: true,
|
||||
handler(){
|
||||
clearInterval(this.interval)
|
||||
this.loading = true;
|
||||
let id = this.$route.params.id;
|
||||
this.configurationInfo = [];
|
||||
this.configurationPeers = [];
|
||||
if (id){
|
||||
this.getPeers(id)
|
||||
this.interval = setInterval(() => {
|
||||
this.getPeers(id)
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeRouteLeave(){
|
||||
clearInterval(this.interval)
|
||||
},
|
||||
methods:{
|
||||
getPeers(id){
|
||||
fetchGet("/api/getWireguardConfigurationInfo",
|
||||
{
|
||||
configurationName: id
|
||||
}, (res) => {
|
||||
this.configurationInfo = res.data.configurationInfo;
|
||||
this.configurationPeers = res.data.configurationPeers;
|
||||
this.loading = false;
|
||||
if (this.configurationPeers.length > 0){
|
||||
const sent = this.configurationPeers.map(x => x.total_sent + x.cumu_sent).reduce((x,y) => x + y).toFixed(4);
|
||||
const receive = this.configurationPeers.map(x => x.total_receive + x.cumu_receive).reduce((x,y) => x + y).toFixed(4);
|
||||
if (
|
||||
this.historyDataSentDifference[this.historyDataSentDifference.length - 1] !== sent
|
||||
){
|
||||
if (this.historyDataSentDifference.length > 0){
|
||||
this.historySentData = {
|
||||
labels: [...this.historySentData.labels, dayjs().format("HH:mm:ss A")],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Data Sent',
|
||||
data: [...this.historySentData.datasets[0].data,
|
||||
((sent - this.historyDataSentDifference[this.historyDataSentDifference.length - 1])*1000).toFixed(4)],
|
||||
fill: false,
|
||||
borderColor: ' #198754',
|
||||
tension: 0
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
this.historyDataSentDifference.push(sent)
|
||||
}
|
||||
if (
|
||||
this.historyDataReceivedDifference[this.historyDataReceivedDifference.length - 1] !== receive
|
||||
){
|
||||
if (this.historyDataReceivedDifference.length > 0){
|
||||
this.historyReceiveData = {
|
||||
labels: [...this.historyReceiveData.labels, dayjs().format("HH:mm:ss A")],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Data Received',
|
||||
data: [...this.historyReceiveData.datasets[0].data,
|
||||
((receive - this.historyDataReceivedDifference[this.historyDataReceivedDifference.length - 1])*1000).toFixed(4)],
|
||||
fill: false,
|
||||
borderColor: '#0d6efd',
|
||||
tension: 0
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
this.historyDataReceivedDifference.push(receive)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
configurationSummary(){
|
||||
return {
|
||||
connectedPeers: this.configurationPeers.filter(x => x.status === "running").length,
|
||||
totalUsage: this.configurationPeers.length > 0 ? this.configurationPeers.map(x => x.total_data + x.cumu_data).reduce((a, b) => a + b) : 0,
|
||||
totalReceive: this.configurationPeers.length > 0 ? this.configurationPeers.map(x => x.total_receive + x.cumu_receive).reduce((a, b) => a + b) : 0,
|
||||
totalSent: this.configurationPeers.length > 0 ? this.configurationPeers.map(x => x.total_sent + x.cumu_sent).reduce((a, b) => a + b) : 0
|
||||
}
|
||||
},
|
||||
receiveData(){
|
||||
return this.historyReceiveData
|
||||
},
|
||||
sentData(){
|
||||
return this.historySentData
|
||||
},
|
||||
individualDataUsage(){
|
||||
return {
|
||||
labels: this.configurationPeers.map(x => {
|
||||
if (x.name) return x.name
|
||||
return `Untitled Peer - ${x.id}`
|
||||
}),
|
||||
datasets: [{
|
||||
label: 'Total Data Usage',
|
||||
data: this.configurationPeers.map(x => x.cumu_data + x.total_data),
|
||||
backgroundColor: this.configurationPeers.map(x => `#0dcaf0`),
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (tooltipItem) => {
|
||||
console.log(tooltipItem)
|
||||
return `${tooltipItem.formattedValue} GB`
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
individualDataUsageChartOption(){
|
||||
return {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: {
|
||||
display: false,
|
||||
},
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
},
|
||||
y:{
|
||||
ticks: {
|
||||
callback: (val, index) => {
|
||||
return `${val} GB`
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
chartOptions() {
|
||||
return {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (tooltipItem) => {
|
||||
console.log(tooltipItem)
|
||||
return `${tooltipItem.formattedValue} MB/s`
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: {
|
||||
display: false,
|
||||
},
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
},
|
||||
y:{
|
||||
ticks: {
|
||||
callback: (val, index) => {
|
||||
return `${val} MB/s`
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-body">
|
||||
hiiii
|
||||
<div class="mt-5 text-body" v-if="!loading">
|
||||
<div>
|
||||
<small CLASS="text-muted">CONFIGURATION</small>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<h1 class="mb-0"><samp>{{this.configurationInfo.Name}}</samp></h1>
|
||||
<div class="dot active ms-0"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3 gy-2">
|
||||
<div class="col-sm-3">
|
||||
<p class="mb-0 text-muted"><small>Address</small></p>
|
||||
{{this.configurationInfo.Address}}
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<p class="mb-0 text-muted"><small>Listen Port</small></p>
|
||||
{{this.configurationInfo.ListenPort}}
|
||||
</div>
|
||||
<div style="word-break: break-all" class="col-sm-6">
|
||||
<p class="mb-0 text-muted"><small>Public Key</small></p>
|
||||
<samp>{{this.configurationInfo.PublicKey}}</samp>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<p class="mb-1 text-muted"><small>Connected Peers</small></p>
|
||||
<i class="bi bi-ethernet me-2"></i><strong>{{configurationSummary.connectedPeers}}</strong>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<p class="mb-0 text-muted"><small>Total Usage</small></p>
|
||||
<i class="bi bi-arrow-down-up me-2"></i><strong>{{configurationSummary.totalUsage.toFixed(4)}}</strong> GB
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<p class="mb-0 text-muted"><small>Total Received</small></p>
|
||||
<i class="bi bi-arrow-down me-2"></i><strong>{{configurationSummary.totalReceive.toFixed(4)}}</strong> GB
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<p class="mb-0 text-muted"><small>Total Sent</small></p>
|
||||
<i class="bi bi-arrow-up me-2"></i><strong>{{configurationSummary.totalSent.toFixed(4)}}</strong> GB
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3 gx-2 gy-2 mb-2">
|
||||
<div class="col-sm ">
|
||||
<div class="card rounded-3 bg-transparent">
|
||||
<div class="card-header bg-transparent border-0"><small>Peers Total Data Usage</small></div>
|
||||
<div class="card-body pt-1">
|
||||
<Bar
|
||||
:data="individualDataUsage"
|
||||
:options="individualDataUsageChartOption"
|
||||
style="height: 150px; width: 100%"></Bar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="card rounded-3 bg-transparent">
|
||||
<div class="card-header bg-transparent border-0"><small>Real Time Received Data Usage</small></div>
|
||||
<div class="card-body pt-1">
|
||||
<Line
|
||||
:options="chartOptions"
|
||||
:data="receiveData"
|
||||
style="width: 100%; height: 150px"
|
||||
></Line>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="card rounded-3 bg-transparent">
|
||||
<div class="card-header bg-transparent border-0"><small>Real Time Sent Data Usage</small></div>
|
||||
<div class="card-body pt-1">
|
||||
<Line
|
||||
:options="chartOptions"
|
||||
:data="sentData"
|
||||
style="width: 100%; height: 150px"
|
||||
></Line>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row gx-2 gy-2">
|
||||
<div class="col-12 col-lg-6 col-xl-4" v-for="peer in this.configurationPeers">
|
||||
<Peer :Peer="peer"></Peer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user