Skip to content

Commit 84b62fc

Browse files
committed
chore: add code update
1 parent b62b4cb commit 84b62fc

File tree

8 files changed

+263
-25
lines changed

8 files changed

+263
-25
lines changed

src/config.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ log_level = DEBUG
2525

2626
[account]
2727
username = dselen
28-
password = $2b$12$LId4p4WP3N0RLFi/TjNxseA1U.yybg2bJ8ufvcbpclUxckEX6OH.y
28+
password = $2b$12$0dvZY7mNbEcjfJI09AqxbeKYhBwoNwBrD4OQNhlJ3HqXoAbZmhNFq
2929
enable_totp = False
3030
totp_verified = False
31-
totp_key = NY7VFZFYIUM7FUZCDW7UMXQ7DMGGLSJU
31+
totp_key = ON75CRNI7MGTA33PTHTQEZVXB5JKCEYK
3232

3333
[other]
3434
welcome_session = False

src/modules/routes/routes.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
from ..database.functions import functions
1818
from ..config.config import config
1919

20+
from ..utilities.utilities import utilities
21+
from ..utilities.system_status import system_status
22+
2023
routes = flask.Blueprint("routes", __name__)
2124

2225
white_list = [
@@ -158,19 +161,9 @@ def api_authenticate():
158161
return make_resp_obj(False, error_msg, {"status": False}, 401)
159162

160163
if totp_enabled:
161-
return make_resp_obj(
162-
False,
163-
"Sorry, your username, password or OTP is incorrect.",
164-
{},
165-
401
166-
)
164+
return make_resp_obj(False, "Sorry, your username, password or OTP is incorrect.", {}, 401)
167165
else:
168-
return make_resp_obj(
169-
False,
170-
"Sorry, your username or password is incorrect.",
171-
{},
172-
401
173-
)
166+
return make_resp_obj(False, "Sorry, your username or password is incorrect.", {}, 401)
174167

175168
@routes.route('/')
176169
def index_handler():
@@ -216,6 +209,11 @@ def api_retrieve_dashboard_theme():
216209

217210
return make_resp_obj(True, "", config_server.get("wgdashboard_theme"), 200)
218211

212+
@routes.route('/api/getDashboardUpdate')
213+
def api_retrieve_dashboard_update():
214+
utilities.update_available()
215+
return make_resp_obj()
216+
219217
@routes.route('/api/getDashboardConfiguration')
220218
def api_retrieve_dashboard_config():
221219
return make_resp_obj(data=flask.current_app.wgd_config)
@@ -231,11 +229,31 @@ def api_totp_status():
231229

232230
return make_resp_obj(True, "", data, 200)
233231

232+
@routes.route('/api/getWireguardConfigurations')
233+
def api_retrieve_wireguard_configurations():
234+
ok, config_server = config.filter(flask.current_app.wgd_config, 'SERVER')
235+
if not ok:
236+
log.error("failed to filter the config in-memory")
237+
return make_resp_obj(False, 'Internal error', {}, 500)
238+
239+
if "wg_conf_path" not in config_server:
240+
return make_resp_obj(False, 'Internal error', {}, 500)
241+
242+
wireguard_path = config_server.get('wg_conf_path', '/etc/wireguard')
243+
if os.path.exists(wireguard_path):
244+
present_confs = os.listdir(wireguard_path)
245+
present_confs.sort()
246+
247+
log.info(present_confs)
248+
249+
return make_resp_obj(True, 'Wireguard', {}, 200)
250+
251+
@routes.route('/api/systemStatus')
252+
def api_system_status():
253+
status = system_status()
254+
return make_resp_obj(True, "", status.to_json(), 200)
255+
234256
@routes.route('/health', methods=["GET"])
235257
@routes.route('/healthz', methods=["GET"])
236258
def health_handler():
237-
return make_resp_obj(True,
238-
"Health Endpoint",
239-
{"status": "ok"},
240-
200
241-
)
259+
return make_resp_obj(True, "Health Endpoint", {"status": "ok"}, 200)

src/modules/routes/routes_welcome.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,42 @@ def api_welcome_get_totp():
7272
log.error("failed to update the key in the configuration file")
7373
return make_resp_obj(False, 'Internal error', {}, 500)
7474

75+
# Very important to also refresh the config in-memory
76+
ok, flask.current_app.wgd_config = config.read()
77+
if not ok:
78+
log.error("failed to refresh the in-memory configuration")
79+
return make_resp_obj(False, 'Internal error', {}, 500)
80+
7581
return make_resp_obj(True, '', pyotp.totp.TOTP(totp_key).provisioning_uri(issuer_name="WGDashboard Admin"))
7682

77-
return make_resp_obj(False, 'Internal error', {}, 500)
83+
return make_resp_obj(False, 'Internal error', {}, 500)
84+
85+
@routes_welcome.route('/api/Welcome_VerifyTotpLink', methods=["POST"])
86+
def api_welcome_verify_totp():
87+
ok, config_account = config.filter(flask.current_app.wgd_config, 'ACCOUNT')
88+
if not ok:
89+
log.error("failed to filter the config in-memory")
90+
return make_resp_obj(False, 'Internal error', {}, 500)
91+
92+
req_data = flask.request.get_json()
93+
totp_code = pyotp.TOTP(config_account['totp_key'], interval=30).now()
94+
95+
totp_match = totp_code == req_data['totp']
96+
if totp_match:
97+
ok = config.update('ACCOUNT', 'totp_verified', True)
98+
if not ok:
99+
log.error("failed to update the key in the configuration file")
100+
return make_resp_obj(False, 'Internal error', {}, 500)
101+
102+
ok = config.update('ACCOUNT', 'enable_totp', True)
103+
if not ok:
104+
log.error("failed to update the key in the configuration file")
105+
return make_resp_obj(False, 'Internal error', {}, 500)
106+
107+
# Very important to also refresh the config in-memory
108+
ok, flask.current_app.wgd_config = config.read()
109+
if not ok:
110+
log.error("failed to refresh the in-memory configuration")
111+
return make_resp_obj(False, 'Internal error', {}, 500)
112+
113+
return make_resp_obj(totp_match)
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/bin/env python3
2+
3+
import psutil
4+
import shutil
5+
import subprocess
6+
import time
7+
import flask
8+
9+
import psutil, shutil, subprocess, time
10+
from flask import current_app
11+
12+
13+
class system_status:
14+
def to_json(self):
15+
return {
16+
"CPU": self.get_cpu(),
17+
"Memory": self.get_memory(),
18+
"Disks": self.get_disks(),
19+
"NetworkInterfaces": self.get_network(),
20+
"NetworkInterfacesPriority": self.get_interface_priorities(),
21+
"Processes": self.get_processes()
22+
}
23+
24+
def get_cpu(self):
25+
try:
26+
return {
27+
"cpu_percent": psutil.cpu_percent(interval=1),
28+
"cpu_percent_per_cpu": psutil.cpu_percent(interval=1, percpu=True)
29+
}
30+
except Exception as e:
31+
current_app.logger.error("CPU error %s", e)
32+
return {}
33+
34+
def get_memory(self):
35+
try:
36+
v = psutil.virtual_memory()
37+
s = psutil.swap_memory()
38+
39+
return {
40+
"VirtualMemory": {
41+
"total": v.total,
42+
"available": v.available,
43+
"percent": v.percent
44+
},
45+
"SwapMemory": {
46+
"total": s.total,
47+
"available": s.free,
48+
"percent": s.percent
49+
}
50+
}
51+
except Exception as e:
52+
current_app.logger.error("Memory error %s", e)
53+
return {}
54+
55+
def get_disks(self):
56+
disks = []
57+
try:
58+
for p in psutil.disk_partitions():
59+
d = psutil.disk_usage(p.mountpoint)
60+
61+
disks.append({
62+
"mountPoint": p.mountpoint,
63+
"total": d.total,
64+
"used": d.used,
65+
"free": d.free,
66+
"percent": d.percent
67+
})
68+
except Exception as e:
69+
current_app.logger.error("Disk error %s", e)
70+
71+
return disks
72+
73+
def get_network(self):
74+
try:
75+
first = psutil.net_io_counters(pernic=True)
76+
time.sleep(1)
77+
second = psutil.net_io_counters(pernic=True)
78+
79+
result = {}
80+
81+
for iface in first:
82+
sent = (second[iface].bytes_sent - first[iface].bytes_sent) / 1024 / 1024
83+
recv = (second[iface].bytes_recv - first[iface].bytes_recv) / 1024 / 1024
84+
85+
result[iface] = {
86+
**first[iface]._asdict(),
87+
"realtime": {
88+
"sent": round(sent, 4),
89+
"recv": round(recv, 4)
90+
}
91+
}
92+
93+
return result
94+
95+
except Exception as e:
96+
current_app.logger.error("Network error %s", e)
97+
return {}
98+
99+
def get_interface_priorities(self):
100+
try:
101+
if not shutil.which("ip"):
102+
return {}
103+
104+
result = subprocess.check_output(["ip", "route", "show"]).decode()
105+
106+
priorities = {}
107+
108+
for line in result.splitlines():
109+
if "metric" in line and "dev" in line:
110+
parts = line.split()
111+
dev = parts[parts.index("dev") + 1]
112+
metric = int(parts[parts.index("metric") + 1])
113+
114+
priorities.setdefault(dev, metric)
115+
116+
return priorities
117+
118+
except Exception as e:
119+
current_app.logger.error("Interface priority error %s", e)
120+
return {}
121+
122+
def get_processes(self):
123+
cpu_list = []
124+
mem_list = []
125+
126+
try:
127+
for proc in psutil.process_iter():
128+
129+
try:
130+
name = proc.name()
131+
cmd = " ".join(proc.cmdline())
132+
pid = proc.pid
133+
134+
cpu = proc.cpu_percent()
135+
mem = proc.memory_percent()
136+
137+
cpu_list.append({
138+
"name": name,
139+
"command": cmd,
140+
"pid": pid,
141+
"percent": cpu
142+
})
143+
144+
mem_list.append({
145+
"name": name,
146+
"command": cmd,
147+
"pid": pid,
148+
"percent": mem
149+
})
150+
151+
except (psutil.NoSuchProcess, psutil.AccessDenied):
152+
continue
153+
154+
cpu_list.sort(key=lambda x: x["percent"], reverse=True)
155+
mem_list.sort(key=lambda x: x["percent"], reverse=True)
156+
157+
return {
158+
"cpu_top_10": cpu_list[:20],
159+
"memory_top_10": mem_list[:20]
160+
}
161+
162+
except Exception as e:
163+
current_app.logger.error("Process error %s", e)
164+
return {}

src/modules/utilities/utilities.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging as log
44

55
import os
6+
import urllib.request
67

78
class utilities():
89
@staticmethod
@@ -19,5 +20,22 @@ def ensure_directory(path: str) -> bool:
1920
return True
2021

2122
except Exception as err:
22-
log.critical('failed to create directory')
23-
return False
23+
log.error('failed to create directory')
24+
return False
25+
26+
@staticmethod
27+
def update_available() -> bool:
28+
request = urllib.request.urlopen("https://api.github.com/repos/WGDashboard/WGDashboard/releases/latest", timeout=5).read()
29+
30+
data = json.loads(request)
31+
log.info(data)
32+
33+
@staticmethod
34+
def ProtocolsEnabled() -> list[str]:
35+
from shutil import which
36+
protocols = []
37+
if which('awg') is not None and which('awg-quick') is not None:
38+
protocols.append("awg")
39+
if which('wg') is not None and which('wg-quick') is not None:
40+
protocols.append("wg")
41+
return protocols

src/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ gunicorn==25.1.0
44
sqlalchemy==2.0.48
55
Flask==3.1.3
66
Werkzeug==3.1.6
7-
pyotp==2.9.0
7+
pyotp==2.9.0
8+
psutil==7.2.2

src/static/build-app.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ rm ./dist -rf
66

77
echo "Compiling the new!"
88

9-
cd ./admin && npm run build && cd ..
10-
cd ./client && npm run build && cd ..
9+
cd ./admin && npm install && npm run build && cd ..
10+
cd ./client && npm install && npm run build && cd ..
1111

1212
echo "Done!"

src/static/client/proxy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const proxy = "http://wg.local:10086/"

0 commit comments

Comments
 (0)