diff --git a/docker/Dockerfile b/docker/Dockerfile index b5e22585..c9deca46 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,6 +2,7 @@ # AWG GOLANG BUILDING STAGE # Base: Alpine # + FROM golang:1.25-alpine AS awg-go RUN apk add --no-cache \ @@ -44,6 +45,7 @@ RUN make && chmod +x wg* # FROM python:3.13-alpine AS pip-builder + RUN apk add --no-cache \ build-base \ pkgconfig \ diff --git a/docker/Dockerfile-Debian-Slim b/docker/Dockerfile-Debian-Slim new file mode 100644 index 00000000..e36868e3 --- /dev/null +++ b/docker/Dockerfile-Debian-Slim @@ -0,0 +1,3 @@ +FROM ubuntu:24.04 + +RUN apt-get update && apt-get full-upgrade -y \ No newline at end of file diff --git a/src/dashboard.py b/src/dashboard.py index d4dcc4b3..48f47641 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -303,7 +303,7 @@ def API_addWireguardConfiguration(): else: WireguardConfigurations[data['ConfigurationName']] = ( WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data)) if data.get('Protocol') == 'wg' else ( - AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, DashboardWebHooks, data=data)) + AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data)) return ResponseObject() @app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration') @@ -585,6 +585,7 @@ def API_updatePeerSettings(configName): else: status, msg = peer.updatePeer(name, private_key, preshared_key, dns_addresses, allowed_ip, endpoint_allowed_ip, mtu, keepalive, "off") + wireguardConfig.getPeers() DashboardWebHooks.RunWebHook('peer_updated', { "configuration": wireguardConfig.Name, "peers": [id] @@ -1555,7 +1556,6 @@ Index Page @app.get(f'{APP_PREFIX}/') def index(): - app.logger.info('hi') return render_template('index.html') def peerInformationBackgroundThread(): @@ -1581,7 +1581,7 @@ def peerInformationBackgroundThread(): c.logPeersHistoryEndpoint() c.getRestrictedPeersList() except Exception as e: - print(f"[WGDashboard] Background Thread #1 Error: {str(e)}", flush=True) + app.logger.error(f"[WGDashboard] Background Thread #1 Error", e) if delay == 6: delay = 1 @@ -1595,8 +1595,11 @@ def peerJobScheduleBackgroundThread(): app.logger.info(f"Background Thread #2 PID:" + str(threading.get_native_id())) time.sleep(10) while True: - AllPeerJobs.runJob() - time.sleep(180) + try: + AllPeerJobs.runJob() + time.sleep(180) + except Exception as e: + app.logger.error("Background Thread #2 Error", e) def gunicornConfig(): _, app_ip = DashboardConfig.GetConfig("Server", "app_ip") @@ -1622,11 +1625,13 @@ def InitWireguardConfigurationsList(startup: bool = False): try: if i in WireguardConfigurations.keys(): if WireguardConfigurations[i].configurationFileChanged(): - WireguardConfigurations[i] = WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i) + with app.app_context(): + WireguardConfigurations[i] = WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i) else: - WireguardConfigurations[i] = WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i, startup=startup) + with app.app_context(): + WireguardConfigurations[i] = WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i, startup=startup) except WireguardConfiguration.InvalidConfigurationFileException as e: - print(f"{i} have an invalid configuration file.") + app.logger.error(f"{i} have an invalid configuration file.") if "awg" in ProtocolsEnabled(): confs = os.listdir(DashboardConfig.GetConfig("Server", "awg_conf_path")[1]) @@ -1637,11 +1642,13 @@ def InitWireguardConfigurationsList(startup: bool = False): try: if i in WireguardConfigurations.keys(): if WireguardConfigurations[i].configurationFileChanged(): - WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i) + with app.app_context(): + WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i) else: - WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i, startup=startup) + with app.app_context(): + WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i, startup=startup) except WireguardConfiguration.InvalidConfigurationFileException as e: - print(f"{i} have an invalid configuration file.") + app.logger.error(f"{i} have an invalid configuration file.") _, app_ip = DashboardConfig.GetConfig("Server", "app_ip") @@ -1650,17 +1657,14 @@ _, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path") WireguardConfigurations: dict[str, WireguardConfiguration] = {} -AllPeerShareLinks: PeerShareLinks = PeerShareLinks(DashboardConfig, WireguardConfigurations) -AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations) -DashboardLogger: DashboardLogger = DashboardLogger() -DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations) -DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig) -NewConfigurationTemplates: NewConfigurationTemplates = NewConfigurationTemplates() - -InitWireguardConfigurationsList(startup=True) - - with app.app_context(): + AllPeerShareLinks: PeerShareLinks = PeerShareLinks(DashboardConfig, WireguardConfigurations) + AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations) + DashboardLogger: DashboardLogger = DashboardLogger() + DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations) + DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig) + NewConfigurationTemplates: NewConfigurationTemplates = NewConfigurationTemplates() + InitWireguardConfigurationsList(startup=True) DashboardClients: DashboardClients = DashboardClients(WireguardConfigurations) app.register_blueprint(createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients)) @@ -1673,5 +1677,5 @@ def startThreads(): if __name__ == "__main__": startThreads() DashboardPlugins.startThreads() - app.logger.addHandler(logging.StreamHandler()) + # app.logger.addHandler(logging.StreamHandler()) app.run(host=app_ip, debug=False, port=app_port) \ No newline at end of file diff --git a/src/gunicorn.conf.py b/src/gunicorn.conf.py index ed5fc3dc..8d226842 100644 --- a/src/gunicorn.conf.py +++ b/src/gunicorn.conf.py @@ -1,5 +1,4 @@ -import os.path -import dashboard, configparser +import dashboard from datetime import datetime global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger, Dash app_host, app_port = dashboard.gunicornConfig() diff --git a/src/modules/AmneziaWGPeer.py b/src/modules/AmneziaWGPeer.py index 30370ef2..11989989 100644 --- a/src/modules/AmneziaWGPeer.py +++ b/src/modules/AmneziaWGPeer.py @@ -149,12 +149,6 @@ class AmneziaWGPeer(Peer): shell=True, stderr=subprocess.STDOUT) if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'): return False, "Update peer failed when saving the configuration" - # sqlUpdate( - # '''UPDATE '%s' SET name = ?, private_key = ?, DNS = ?, endpoint_allowed_ip = ?, mtu = ?, - # keepalive = ?, preshared_key = ?, advanced_security = ? WHERE id = ?''' % self.configuration.Name, - # (name, private_key, dns_addresses, endpoint_allowed_ip, mtu, - # keepalive, preshared_key, advanced_security, self.id,) - # ) with self.configuration.engine.begin() as conn: conn.execute( @@ -171,7 +165,7 @@ class AmneziaWGPeer(Peer): self.configuration.peersTable.c.id == self.id ) ) - + self.configuration.getPeers() return True, None except subprocess.CalledProcessError as exc: return False, exc.output.decode("UTF-8").strip() \ No newline at end of file diff --git a/src/modules/AmneziaWireguardConfiguration.py b/src/modules/AmneziaWireguardConfiguration.py index 8f7de89e..8223d01f 100644 --- a/src/modules/AmneziaWireguardConfiguration.py +++ b/src/modules/AmneziaWireguardConfiguration.py @@ -2,7 +2,7 @@ AmneziaWG Configuration """ import random, sqlalchemy, os, subprocess, re, uuid - +from flask import current_app from .PeerJobs import PeerJobs from .AmneziaWGPeer import AmneziaWGPeer from .PeerShareLinks import PeerShareLinks @@ -52,6 +52,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): "ConnectedPeers": len(list(filter(lambda x: x.status == "running", self.Peers))), "TotalPeers": len(self.Peers), "Protocol": self.Protocol, + "Table": self.Table, "Jc": self.Jc, "Jmin": self.Jmin, "Jmax": self.Jmax, @@ -227,20 +228,10 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): "remote_endpoint": self.DashboardConfig.GetConfig("Peers", "remote_endpoint")[1], "preshared_key": i["PresharedKey"] if "PresharedKey" in i.keys() else "" } - # sqlUpdate( - # """ - # INSERT INTO '%s' - # VALUES (:id, :private_key, :DNS, :advanced_security, :endpoint_allowed_ip, :name, :total_receive, :total_sent, - # :total_data, :endpoint, :status, :latest_handshake, :allowed_ip, :cumu_receive, :cumu_sent, - # :cumu_data, :mtu, :keepalive, :remote_endpoint, :preshared_key); - # """ % self.Name - # , newPeer) conn.execute( self.peersTable.insert().values(tempPeer) ) else: - # sqlUpdate("UPDATE '%s' SET allowed_ip = ? WHERE id = ?" % self.Name, - # (i.get("AllowedIPs", "N/A"), i['PublicKey'],)) conn.execute( self.peersTable.update().values({ "allowed_ip": i.get("AllowedIPs", "N/A") @@ -250,10 +241,8 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): ) self.Peers.append(AmneziaWGPeer(tempPeer, self)) except Exception as e: - if __name__ == '__main__': - print(f"[WGDashboard] {self.Name} getPeers() Error: {str(e)}") + current_app.logger.error(f"{self.Name} getPeers() Error", e) else: - # checkIfExist = sqlSelect("SELECT * FROM '%s'" % self.Name).fetchall() with self.engine.connect() as conn: existingPeers = conn.execute(self.peersTable.select()).mappings().fetchall() for i in existingPeers: diff --git a/src/modules/ConnectionString.py b/src/modules/ConnectionString.py index c56fca4b..77f69644 100644 --- a/src/modules/ConnectionString.py +++ b/src/modules/ConnectionString.py @@ -1,7 +1,9 @@ import configparser import os from sqlalchemy_utils import database_exists, create_database -def ConnectionString(database) -> str or None: +from flask import current_app + +def ConnectionString(database) -> str: parser = configparser.ConfigParser(strict=False) parser.read_file(open('wg-dashboard.ini', "r+")) sqlitePath = os.path.join("db") @@ -10,14 +12,14 @@ def ConnectionString(database) -> str or None: if parser.get("Database", "type") == "postgresql": cn = f'postgresql+psycopg://{parser.get("Database", "username")}:{parser.get("Database", "password")}@{parser.get("Database", "host")}/{database}' elif parser.get("Database", "type") == "mysql": - cn = f'mysql+mysqldb://{parser.get("Database", "username")}:{parser.get("Database", "password")}@{parser.get("Database", "host")}/{database}' + cn = f'mysql+pymysql://{parser.get("Database", "username")}:{parser.get("Database", "password")}@{parser.get("Database", "host")}/{database}' else: cn = f'sqlite:///{os.path.join(sqlitePath, f"{database}.db")}' try: if not database_exists(cn): create_database(cn) except Exception as e: - print("[WGDashboard] Database error: " + str(e)) + current_app.logger.error("Database error. Terminating...", e) exit(1) return cn \ No newline at end of file diff --git a/src/modules/DashboardClients.py b/src/modules/DashboardClients.py index 231d0991..231141b1 100644 --- a/src/modules/DashboardClients.py +++ b/src/modules/DashboardClients.py @@ -210,7 +210,6 @@ class DashboardClients: if not status: return False, "Sign in failed. Reason: " + data existingClient = self.SignIn_OIDC_UserExistence(data) - print(data) if not existingClient: status, newClientUUID = self.SignUp_OIDC(data) session['ClientID'] = newClientUUID diff --git a/src/modules/DashboardConfig.py b/src/modules/DashboardConfig.py index 8ca8fd8f..efddb100 100644 --- a/src/modules/DashboardConfig.py +++ b/src/modules/DashboardConfig.py @@ -6,7 +6,7 @@ from sqlalchemy_utils import database_exists, create_database import sqlalchemy as db from datetime import datetime from typing import Any - +from flask import current_app from .ConnectionString import ConnectionString from .Utilities import ( GetRemoteEndpoint, ValidateDNSAddress @@ -141,7 +141,7 @@ class DashboardConfig: fKeys.append(DashboardAPIKey(k[0], k[1].strftime("%Y-%m-%d %H:%M:%S"), (k[2].strftime("%Y-%m-%d %H:%M:%S") if k[2] else None))) return fKeys except Exception as e: - print(e) + current_app.logger.error("API Keys error", e) return [] def createAPIKeys(self, ExpiredAt = None): diff --git a/src/modules/DashboardLogger.py b/src/modules/DashboardLogger.py index afa4a029..9b4e1f24 100644 --- a/src/modules/DashboardLogger.py +++ b/src/modules/DashboardLogger.py @@ -3,7 +3,7 @@ Dashboard Logger Class """ import uuid import sqlalchemy as db - +from flask import current_app from .ConnectionString import ConnectionString @@ -40,5 +40,5 @@ class DashboardLogger: ) return True except Exception as e: - print(f"[WGDashboard] Access Log Error: {str(e)}") + current_app.logger.error(f"Access Log Error", e) return False \ No newline at end of file diff --git a/src/modules/DashboardOIDC.py b/src/modules/DashboardOIDC.py index 45029d88..1fa5e73a 100644 --- a/src/modules/DashboardOIDC.py +++ b/src/modules/DashboardOIDC.py @@ -69,10 +69,9 @@ class DashboardOIDC: try: tokens = requests.post(oidc_config.get('token_endpoint'), data=data).json() if not all([tokens.get('access_token'), tokens.get('id_token')]): - print(oidc_config.get('token_endpoint'), data) return False, tokens.get('error_description', None) except Exception as e: - print(str(e)) + current_app.logger.error("Verify token failed", e) return False, str(e) access_token = tokens.get('access_token') diff --git a/src/modules/DashboardWebHooks.py b/src/modules/DashboardWebHooks.py index 892fb066..a598444b 100644 --- a/src/modules/DashboardWebHooks.py +++ b/src/modules/DashboardWebHooks.py @@ -3,12 +3,13 @@ import threading import time import urllib.parse import uuid -from datetime import datetime +from datetime import datetime, timedelta import requests from pydantic import BaseModel, field_serializer import sqlalchemy as db from .ConnectionString import ConnectionString +from flask import current_app WebHookActions = ['peer_created', 'peer_deleted', 'peer_updated'] class WebHook(BaseModel): @@ -76,6 +77,17 @@ class DashboardWebHooks: self.metadata.create_all(self.engine) self.WebHooks: list[WebHook] = [] + + with self.engine.begin() as conn: + conn.execute( + self.webHookSessionsTable.update().values({ + "EndDate": datetime.now(), + "Status": 2 + }).where( + self.webHookSessionsTable.c.Status == -1 + ) + ) + self.__getWebHooks() def __getWebHooks(self): @@ -173,19 +185,19 @@ class DashboardWebHooks: if action not in WebHookActions: return False self.__getWebHooks() - subscribedWebHooks = filter(lambda webhook: action in webhook.SubscribedActions and webhook.IsActive, self.WebHooks) + subscribedWebHooks = filter(lambda webhook: action in webhook.SubscribedActions and webhook.IsActive, + self.WebHooks) data['action'] = action for i in subscribedWebHooks: try: ws = WebHookSession(i, data) t = threading.Thread(target=ws.Execute, daemon=True) t.start() - print("Spinning threads...") + current_app.logger.info(f"Requesting {i.PayloadURL}") except Exception as e: - print(e) + current_app.logger.error(f"Requesting {i.PayloadURL} error", e) except Exception as e: - print(e) - return True + current_app.logger.error("Error when running WebHook") class WebHookSession: def __init__(self, webHook: WebHook, data: dict[str, str]): diff --git a/src/modules/Peer.py b/src/modules/Peer.py index 7f234caf..4724fdf7 100644 --- a/src/modules/Peer.py +++ b/src/modules/Peer.py @@ -68,13 +68,13 @@ class Peer: if len(dns_addresses) > 0 and not ValidateDNSAddress(dns_addresses): return False, f"DNS format is incorrect" - if type(mtu) is str: + if type(mtu) is str or mtu is None: mtu = 0 if mtu < 0 or mtu > 1460: return False, "MTU format is not correct" - if type(keepalive) is str: + if type(keepalive) is str or keepalive is None: keepalive = 0 if keepalive < 0: diff --git a/src/modules/PeerJobLogger.py b/src/modules/PeerJobLogger.py index a64347dd..b047f3c4 100644 --- a/src/modules/PeerJobLogger.py +++ b/src/modules/PeerJobLogger.py @@ -3,7 +3,7 @@ Peer Job Logger """ import uuid import sqlalchemy as db - +from flask import current_app from .ConnectionString import ConnectionString from .Log import Log @@ -36,7 +36,7 @@ class PeerJobLogger: ) ) except Exception as e: - print(f"[WGDashboard] Peer Job Log Error: {str(e)}") + current_app.logger.error(f"Peer Job Log Error", e) return False return True @@ -54,6 +54,6 @@ class PeerJobLogger: logs.append( Log(l.LogID, l.JobID, l.LogDate.strftime("%Y-%m-%d %H:%M:%S"), l.Status, l.Message)) except Exception as e: - print(e) + current_app.logger.error(f"Getting Peer Job Log Error", e) return logs return logs \ No newline at end of file diff --git a/src/modules/PeerJobs.py b/src/modules/PeerJobs.py index 618d10a2..5d069f66 100644 --- a/src/modules/PeerJobs.py +++ b/src/modules/PeerJobs.py @@ -6,6 +6,7 @@ from .PeerJob import PeerJob from .PeerJobLogger import PeerJobLogger import sqlalchemy as db from datetime import datetime +from flask import current_app class PeerJobs: def __init__(self, DashboardConfig, WireguardConfigurations): @@ -140,7 +141,7 @@ class PeerJobs: def runJob(self): - print("[WGDashboard] Running scheduled jobs") + current_app.logger.info("Running scheduled jobs") needToDelete = [] self.__getJobs() for job in self.Jobs: @@ -166,25 +167,24 @@ class PeerJobs: s = fp.resetDataUsage("total") c.restrictPeers([fp.id]) c.allowAccessPeers([fp.id]) - if s is True: self.JobLogger.log(job.JobID, s, f"Peer {fp.id} from {c.Name} is successfully {job.Action}ed." ) - print(f"[WGDashboard] Peer {fp.id} from {c.Name} is successfully {job.Action}ed.") + current_app.logger.info(f"Peer {fp.id} from {c.Name} is successfully {job.Action}ed.") needToDelete.append(job) else: - print(f"[WGDashboard] Peer {fp.id} from {c.Name} is failed {job.Action}ed.") + current_app.logger.info(f"Peer {fp.id} from {c.Name} is failed {job.Action}ed.") self.JobLogger.log(job.JobID, s, f"Peer {fp.id} from {c.Name} failed {job.Action}ed." ) else: - print(f"[WGDashboard] Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed.") + current_app.logger.warning(f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed.") self.JobLogger.log(job.JobID, False, f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed." ) else: - print(f"[WGDashboard] Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed.") + current_app.logger.warning(f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed.") self.JobLogger.log(job.JobID, False, f"Somehow can't find this peer {job.Peer} from {job.Configuration} failed {job.Action}ed." ) diff --git a/src/modules/SystemStatus.py b/src/modules/SystemStatus.py index dba3ea2f..46dd553c 100644 --- a/src/modules/SystemStatus.py +++ b/src/modules/SystemStatus.py @@ -1,8 +1,6 @@ -import shutil -import subprocess -import time -import threading -import psutil +import shutil, subprocess, time, threading, psutil +from flask import current_app + class SystemStatus: def __init__(self): self.CPU = CPU() @@ -40,28 +38,20 @@ class CPU: def __init__(self): self.cpu_percent: float = 0 self.cpu_percent_per_cpu: list[float] = [] - def getData(self): - pass - # try: - # self.cpu_percent_per_cpu = psutil.cpu_percent(interval=1, percpu=True) - # - # except Exception as e: - # pass + def getCPUPercent(self): try: self.cpu_percent = psutil.cpu_percent(interval=1) except Exception as e: - pass + current_app.logger.error("Get CPU Percent error", e) def getPerCPUPercent(self): try: self.cpu_percent_per_cpu = psutil.cpu_percent(interval=1, percpu=True) - except Exception as e: - pass + current_app.logger.error("Get Per CPU Percent error", e) def toJson(self): - self.getData() return self.__dict__ class Memory: @@ -74,13 +64,15 @@ class Memory: try: if self.__memoryType__ == "virtual": memory = psutil.virtual_memory() + self.available = memory.available else: memory = psutil.swap_memory() + self.available = memory.free self.total = memory.total - self.available = memory.available + self.percent = memory.percent except Exception as e: - pass + current_app.logger.error("Get Memory percent error", e) def toJson(self): self.getData() return self.__dict__ @@ -92,7 +84,7 @@ class Disks: try: self.disks = list(map(lambda x : Disk(x.mountpoint), psutil.disk_partitions())) except Exception as e: - pass + current_app.logger.error("Get Disk percent error", e) def toJson(self): self.getData() return self.disks @@ -112,7 +104,7 @@ class Disk: self.used = disk.used self.percent = disk.percent except Exception as e: - pass + current_app.logger.error("Get Disk percent error", e) def toJson(self): self.getData() return self.__dict__ @@ -149,7 +141,8 @@ class NetworkInterfaces: 'recv': round((network[i].bytes_recv - self.interfaces[i]['bytes_recv']) / 1024 / 1024, 4) } except Exception as e: - print(str(e)) + current_app.logger.error("Get network error", e) + def toJson(self): return self.interfaces @@ -178,7 +171,8 @@ class Processes: key=lambda x : x.percent, reverse=True)[:20] break except Exception as e: - break + current_app.logger.error("Get processes error", e) + def toJson(self): self.getData() return { diff --git a/src/modules/WireguardConfiguration.py b/src/modules/WireguardConfiguration.py index bf9f2fee..a58829ed 100644 --- a/src/modules/WireguardConfiguration.py +++ b/src/modules/WireguardConfiguration.py @@ -8,6 +8,7 @@ import sqlalchemy, random, shutil, configparser, ipaddress, os, subprocess, time from zipfile import ZipFile from datetime import datetime, timedelta from itertools import islice +from flask import current_app from .ConnectionString import ConnectionString from .DashboardConfig import DashboardConfig @@ -120,17 +121,17 @@ class WireguardConfiguration: self.createDatabase() with open(self.configPath, "w+") as configFile: self.__parser.write(configFile) - print(f"[WGDashboard] Configuration file {self.configPath} created") + current_app.logger.info(f"Configuration file {self.configPath} created") self.__initPeersList() if not os.path.exists(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup')): os.mkdir(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup')) - print(f"[WGDashboard] Initialized Configuration: {name}") + current_app.logger.info(f"Initialized Configuration: {name}") self.__dumpDatabase() if self.getAutostartStatus() and not self.getStatus() and startup: self.toggleConfiguration() - print(f"[WGDashboard] Autostart Configuration: {name}") + current_app.logger.info(f"Autostart Configuration: {name}") self.configurationInfo: WireguardConfigurationInfo | None = None configurationInfoJson = self.readConfigurationInfo() @@ -170,7 +171,7 @@ class WireguardConfiguration: status, err = self.toggleConfiguration() if not status: restoreStatus = self.restoreBackup(backup['filename']) - print(f"Restore status: {restoreStatus}") + current_app.logger.error(f"Backup restore status: {restoreStatus}") self.toggleConfiguration() return False, err return True, None @@ -228,7 +229,7 @@ class WireguardConfiguration: ) ) except Exception as e: - print("[WGDashboard] Error: Drop table failed - " + str(e)) + current_app.logger.error("Dropping table failed") return False return True @@ -397,79 +398,83 @@ class WireguardConfiguration: def getPeers(self): tmpList = [] + current_app.logger.info(f"Refreshing {self.Name} peer list") if self.configurationFileChanged(): with open(self.configPath, 'r') as configFile: p = [] pCounter = -1 content = configFile.read().split('\n') try: - peerStarts = content.index("[Peer]") - content = content[peerStarts:] - for i in content: - if not RegexMatch("#(.*)", i) and not RegexMatch(";(.*)", i): - if i == "[Peer]": - pCounter += 1 - p.append({}) - p[pCounter]["name"] = "" - else: - if len(i) > 0: - split = re.split(r'\s*=\s*', i, 1) - if len(split) == 2: - p[pCounter][split[0]] = split[1] - - if RegexMatch("#Name# = (.*)", i): - split = re.split(r'\s*=\s*', i, 1) - if len(split) == 2: - p[pCounter]["name"] = split[1] - - for i in p: - if "PublicKey" in i.keys(): - with self.engine.connect() as conn: - tempPeer = conn.execute( - self.peersTable.select().where( - self.peersTable.columns.id == i['PublicKey'] - ) - ).mappings().fetchone() - - if tempPeer is None: - tempPeer = { - "id": i['PublicKey'], - "private_key": "", - "DNS": self.DashboardConfig.GetConfig("Peers", "peer_global_DNS")[1], - "endpoint_allowed_ip": self.DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[ - 1], - "name": i.get("name"), - "total_receive": 0, - "total_sent": 0, - "total_data": 0, - "endpoint": "N/A", - "status": "stopped", - "latest_handshake": "N/A", - "allowed_ip": i.get("AllowedIPs", "N/A"), - "cumu_receive": 0, - "cumu_sent": 0, - "cumu_data": 0, - "mtu": self.DashboardConfig.GetConfig("Peers", "peer_mtu")[1] if len(self.DashboardConfig.GetConfig("Peers", "peer_mtu")[1]) > 0 else None, - "keepalive": self.DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1] if len(self.DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1]) > 0 else None, - "remote_endpoint": self.DashboardConfig.GetConfig("Peers", "remote_endpoint")[1], - "preshared_key": i["PresharedKey"] if "PresharedKey" in i.keys() else "" - } - with self.engine.begin() as conn: - conn.execute( - self.peersTable.insert().values(tempPeer) - ) - else: - with self.engine.begin() as conn: - conn.execute( - self.peersTable.update().values({ - "allowed_ip": i.get("AllowedIPs", "N/A") - }).where( + if "[Peer]" in content: + peerStarts = content.index("[Peer]") + content = content[peerStarts:] + for i in content: + if not RegexMatch("#(.*)", i) and not RegexMatch(";(.*)", i): + if i == "[Peer]": + pCounter += 1 + p.append({}) + p[pCounter]["name"] = "" + else: + if len(i) > 0: + split = re.split(r'\s*=\s*', i, 1) + if len(split) == 2: + p[pCounter][split[0]] = split[1] + + if RegexMatch("#Name# = (.*)", i): + split = re.split(r'\s*=\s*', i, 1) + if len(split) == 2: + p[pCounter]["name"] = split[1] + + for i in p: + if "PublicKey" in i.keys(): + with self.engine.connect() as conn: + tempPeer = conn.execute( + self.peersTable.select().where( self.peersTable.columns.id == i['PublicKey'] ) - ) - tmpList.append(Peer(tempPeer, self)) + ).mappings().fetchone() + + if tempPeer is None: + tempPeer = { + "id": i['PublicKey'], + "private_key": "", + "DNS": self.DashboardConfig.GetConfig("Peers", "peer_global_DNS")[1], + "endpoint_allowed_ip": self.DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[ + 1], + "name": i.get("name"), + "total_receive": 0, + "total_sent": 0, + "total_data": 0, + "endpoint": "N/A", + "status": "stopped", + "latest_handshake": "N/A", + "allowed_ip": i.get("AllowedIPs", "N/A"), + "cumu_receive": 0, + "cumu_sent": 0, + "cumu_data": 0, + "mtu": self.DashboardConfig.GetConfig("Peers", "peer_mtu")[1] if len(self.DashboardConfig.GetConfig("Peers", "peer_mtu")[1]) > 0 else None, + "keepalive": self.DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1] if len(self.DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1]) > 0 else None, + "remote_endpoint": self.DashboardConfig.GetConfig("Peers", "remote_endpoint")[1], + "preshared_key": i["PresharedKey"] if "PresharedKey" in i.keys() else "" + } + with self.engine.begin() as conn: + conn.execute( + self.peersTable.insert().values(tempPeer) + ) + else: + with self.engine.begin() as conn: + conn.execute( + self.peersTable.update().values({ + "allowed_ip": i.get("AllowedIPs", "N/A") + }).where( + self.peersTable.columns.id == i['PublicKey'] + ) + ) + tmpList.append(Peer(tempPeer, self)) + else: + current_app.logger.warning(f"{self.Name} is an empty configuration") except Exception as e: - print(f"[WGDashboard] {self.Name} getPeers() Error: {str(e)}") + current_app.logger.error(f"{self.Name} getPeers() Error", e) else: with self.engine.connect() as conn: existingPeers = conn.execute(self.peersTable.select()).mappings().fetchall() @@ -1074,7 +1079,7 @@ class WireguardConfiguration: check = ipaddress.ip_network(ppip[0]) existedAddress.add(check) except Exception as e: - print(f"[WGDashboard] Error: {self.Name} peer {p.id} have invalid ip") + current_app.logger.error(f"{self.Name} peer {p.id} have invalid ip", e) configurationAddresses = self.Address.split(',') for ca in configurationAddresses: ca = ca.strip() @@ -1088,8 +1093,7 @@ class WireguardConfiguration: if p.version == network.version and p.subnet_of(network): availableAddress[ca] -= 1 except Exception as e: - print(e) - print(f"[WGDashboard] Error: Failed to parse IP address {ca} from {self.Name}") + current_app.logger.error(f"Error: Failed to parse IP address {ca} from {self.Name}", e) return True, availableAddress def getAvailableIP(self, threshold = 255): @@ -1106,7 +1110,7 @@ class WireguardConfiguration: check = ipaddress.ip_network(ppip[0]) existedAddress.add(check.compressed) except Exception as e: - print(f"[WGDashboard] Error: {self.Name} peer {p.id} have invalid ip") + current_app.logger.error(f"{self.Name} peer {p.id} have invalid ip", e) configurationAddresses = self.Address.split(',') for ca in configurationAddresses: ca = ca.strip() @@ -1122,8 +1126,7 @@ class WireguardConfiguration: availableAddress[ca] = list(islice(filter(lambda ip : ip not in existedAddress, map(lambda iph : ipaddress.ip_network(iph).compressed, network.hosts())), threshold)) except Exception as e: - print(e) - print(f"[WGDashboard] Error: Failed to parse IP address {ca} from {self.Name}") + current_app.logger.error(f"Failed to parse IP address {ca} from {self.Name}", e) return True, availableAddress def getRealtimeTrafficUsage(self): diff --git a/src/modules/WireguardConfigurationInfo.py b/src/modules/WireguardConfigurationInfo.py index beebcdba..03d05ca2 100644 --- a/src/modules/WireguardConfigurationInfo.py +++ b/src/modules/WireguardConfigurationInfo.py @@ -18,9 +18,4 @@ class PeerGroupsClass(BaseModel): class WireguardConfigurationInfo(BaseModel): Description: str = '' OverridePeerSettings: OverridePeerSettingsClass = OverridePeerSettingsClass(**{}) - PeerGroups: dict[str, PeerGroupsClass] = {} - - -if __name__ == '__main__': - d = WireguardConfigurationInfo.model_validate_json("") - print(d.model_dump()) \ No newline at end of file + PeerGroups: dict[str, PeerGroupsClass] = {} \ No newline at end of file diff --git a/src/requirements.txt b/src/requirements.txt index bac3fa86..0fcb82dc 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -11,7 +11,7 @@ tcconfig sqlalchemy sqlalchemy_utils psycopg -mysqlclient +PyMySQL tzlocal python-jose pydantic \ No newline at end of file diff --git a/src/static/app/package.json b/src/static/app/package.json index 95e90eb8..deb7bb9f 100644 --- a/src/static/app/package.json +++ b/src/static/app/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "4.3", + "version": "4.3.0", "private": true, "type": "module", "module": "es2022", @@ -8,7 +8,7 @@ "dev": "vite", "build": "vite build --emptyOutDir", "buildcommitpush": "./build.sh", - "build electron": "vite build && vite build --mode electron && cd ../../../../WGDashboard-Desktop && /opt/homebrew/bin/npm run \"electron dist\"", + "build electron": "vite build --emptyOutDir && vite build --mode electron && cd ../../../../WGDashboard-Desktop && /opt/homebrew/bin/npm run \"electron dist\"", "preview": "vite preview" }, "dependencies": { diff --git a/src/static/app/src/App.vue b/src/static/app/src/App.vue index 13711b0f..9bc02a73 100644 --- a/src/static/app/src/App.vue +++ b/src/static/app/src/App.vue @@ -9,6 +9,15 @@ store.initCrossServerConfiguration(); if (window.IS_WGDASHBOARD_DESKTOP){ store.IsElectronApp = true; store.CrossServerConfiguration.Enable = true; + if (store.ActiveServerConfiguration){ + fetchGet("/api/locale", {}, (res) => { + store.Locale = res.data + }) + } +}else{ + fetchGet("/api/locale", {}, (res) => { + store.Locale = res.data + }) } watch(store.CrossServerConfiguration, () => { store.syncCrossServerConfiguration() @@ -16,9 +25,7 @@ watch(store.CrossServerConfiguration, () => { deep: true }); const route = useRoute() -fetchGet("/api/locale", {}, (res) => { - store.Locale = res.data -}) +