From 85fa4271343f1c61efda3046d1a7b33b279a1720 Mon Sep 17 00:00:00 2001 From: Donald Zou Date: Thu, 28 Aug 2025 16:11:01 +0800 Subject: [PATCH] Webhooks feature is done #669 --- src/dashboard.py | 42 +++++++++-------- src/modules/AmneziaWireguardConfiguration.py | 8 +++- src/modules/DashboardWebHooks.py | 45 ++++++++++--------- src/modules/WireguardConfiguration.py | 19 +++++++- .../settingsComponent/dashboardWebHooks.vue | 7 +-- .../addWebHook.vue | 7 ++- .../webHookSession.vue | 7 ++- .../webHookSessions.vue | 10 +---- 8 files changed, 86 insertions(+), 59 deletions(-) diff --git a/src/dashboard.py b/src/dashboard.py index 6b7e2bda..abc8e3b1 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -270,11 +270,11 @@ def API_addWireguardConfiguration(): ) WireguardConfigurations[data['ConfigurationName']] = ( WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data=data, name=data['ConfigurationName'])) if protocol == 'wg' else ( - AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data=data, name=data['ConfigurationName'])) + AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data, name=data['ConfigurationName'])) else: WireguardConfigurations[data['ConfigurationName']] = ( - WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data=data)) if data.get('Protocol') == 'wg' else ( - AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data=data)) + WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data)) if data.get('Protocol') == 'wg' else ( + AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, DashboardWebHooks, data=data)) return ResponseObject() @app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration') @@ -371,7 +371,7 @@ def API_renameWireguardConfiguration(): status, message = rc.renameConfiguration(data.get("NewConfigurationName")) if status: - WireguardConfigurations[data.get("NewConfigurationName")] = (WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data.get("NewConfigurationName")) if rc.Protocol == 'wg' else AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data.get("NewConfigurationName"))) + WireguardConfigurations[data.get("NewConfigurationName")] = (WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName")) if rc.Protocol == 'wg' else AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName"))) else: WireguardConfigurations[data.get("ConfigurationName")] = rc return ResponseObject(status, message) @@ -552,9 +552,13 @@ def API_updatePeerSettings(configName): if wireguardConfig.Protocol == 'wg': status, msg = peer.updatePeer(name, private_key, preshared_key, dns_addresses, allowed_ip, endpoint_allowed_ip, mtu, keepalive) - return ResponseObject(status, msg) - status, msg = peer.updatePeer(name, private_key, preshared_key, dns_addresses, - allowed_ip, endpoint_allowed_ip, mtu, keepalive, "off") + else: + status, msg = peer.updatePeer(name, private_key, preshared_key, dns_addresses, + allowed_ip, endpoint_allowed_ip, mtu, keepalive, "off") + DashboardWebHooks.RunWebHook('peer_updated', { + "configuration": wireguardConfig.Name, + "peers": [id] + }) return ResponseObject(status, msg) return ResponseObject(False, "Peer does not exist") @@ -749,10 +753,10 @@ def API_addPeers(configName): if len(keyPairs) == 0 or (bulkAdd and len(keyPairs) != bulkAddAmount): return ResponseObject(False, "Generating key pairs by bulk failed") status, result = config.addPeers(keyPairs) - DashboardWebHooks.RunWebHook('peer_created', { - "configuration": config.Name, - "peers": list(map(lambda p : p['id'], keyPairs)) - }) + # DashboardWebHooks.RunWebHook('peer_created', { + # "configuration": config.Name, + # "peers": list(map(lambda p : p['id'], keyPairs)) + # }) return ResponseObject(status=status, message=result['message'], data=result['peers']) else: @@ -817,10 +821,10 @@ def API_addPeers(configName): "advanced_security": "off" }] ) - DashboardWebHooks.RunWebHook('peer_created', { - "configuration": config.Name, - "peers": [{"id": public_key}] - }) + # DashboardWebHooks.RunWebHook('peer_created', { + # "configuration": config.Name, + # "peers": [{"id": public_key}] + # }) return ResponseObject(status=status, message=result['message'], data=result['peers']) except Exception as e: app.logger.error("Add peers failed", data, exc_info=e) @@ -1473,9 +1477,9 @@ def InitWireguardConfigurationsList(startup: bool = False): try: if i in WireguardConfigurations.keys(): if WireguardConfigurations[i].configurationFileChanged(): - WireguardConfigurations[i] = WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, i) + WireguardConfigurations[i] = WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i) else: - WireguardConfigurations[i] = WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, i, startup=startup) + WireguardConfigurations[i] = WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i, startup=startup) except WireguardConfiguration.InvalidConfigurationFileException as e: print(f"{i} have an invalid configuration file.") @@ -1488,9 +1492,9 @@ def InitWireguardConfigurationsList(startup: bool = False): try: if i in WireguardConfigurations.keys(): if WireguardConfigurations[i].configurationFileChanged(): - WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, i) + WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i) else: - WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, i, startup=startup) + WireguardConfigurations[i] = AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, i, startup=startup) except WireguardConfiguration.InvalidConfigurationFileException as e: print(f"{i} have an invalid configuration file.") diff --git a/src/modules/AmneziaWireguardConfiguration.py b/src/modules/AmneziaWireguardConfiguration.py index cfdefe1a..b4c1824c 100644 --- a/src/modules/AmneziaWireguardConfiguration.py +++ b/src/modules/AmneziaWireguardConfiguration.py @@ -8,12 +8,14 @@ from .AmneziaWGPeer import AmneziaWGPeer from .PeerShareLinks import PeerShareLinks from .Utilities import RegexMatch from .WireguardConfiguration import WireguardConfiguration +from .DashboardWebHooks import DashboardWebHooks class AmneziaWireguardConfiguration(WireguardConfiguration): def __init__(self, DashboardConfig, AllPeerJobs: PeerJobs, AllPeerShareLinks: PeerShareLinks, + DashboardWebHooks: DashboardWebHooks, name: str = None, data: dict = None, backup: dict = None, startup: bool = False): self.Jc = 0 self.Jmin = 0 @@ -25,7 +27,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): self.H3 = 3 self.H4 = 4 - super().__init__(DashboardConfig, AllPeerJobs, AllPeerShareLinks, name, data, backup, startup, wg=False) + super().__init__(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, name, data, backup, startup, wg=False) def toJson(self): self.Status = self.getStatus() @@ -301,6 +303,10 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): p = self.searchPeer(p['id']) if p[0]: result['peers'].append(p[1]) + self.DashboardWebHooks.RunWebHook("peer_created", { + "configuration": self.Name, + "peers": list(map(lambda k : k['id'], peers)) + }) return True, result except Exception as e: result['message'] = str(e) diff --git a/src/modules/DashboardWebHooks.py b/src/modules/DashboardWebHooks.py index 7bba8a71..191855dc 100644 --- a/src/modules/DashboardWebHooks.py +++ b/src/modules/DashboardWebHooks.py @@ -168,19 +168,23 @@ class DashboardWebHooks: return False, str(e) return True, None - def RunWebHook(self, action: str, data: dict[str, str]): - if action not in WebHookActions: - return False - self.__getWebHooks() - subscribedWebHooks = filter(lambda webhook: action in webhook.SubscribedActions, self.WebHooks) - data['action'] = action - for i in subscribedWebHooks: - try: - t = threading.Thread(target=WebHookSession, args=(i,data), daemon=True) - t.start() - print("Spinning threads...") - except Exception as e: - pass + def RunWebHook(self, action: str, data): + try: + if action not in WebHookActions: + return False + self.__getWebHooks() + subscribedWebHooks = filter(lambda webhook: action in webhook.SubscribedActions, 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...") + except Exception as e: + print(e) + except Exception as e: + print(e) return True class WebHookSession: @@ -197,7 +201,6 @@ class WebHookSession: data['webhook_session'] = self.sessionID self.data = data self.Prepare() - self.Execute(data) def Prepare(self): with self.engine.begin() as conn: @@ -235,7 +238,7 @@ class WebHookSession: ) ) - def Execute(self, data: dict[str, str]): + def Execute(self): success = False for i in range(5): @@ -247,15 +250,15 @@ class WebHookSession: headerDictionary[header['key']] = header['value'] if self.webHook.ContentType == "application/json": - reqData = json.dumps(data) + reqData = json.dumps(self.data) else: - for (key, val) in data.items(): - if type(data[key]) not in [str, int]: - data[key] = json.dumps(data[key]) - reqData = urllib.parse.urlencode(data) + for (key, val) in self.data.items(): + if type(self.data[key]) not in [str, int]: + self.data[key] = json.dumps(self.data[key]) + reqData = urllib.parse.urlencode(self.data) try: req = requests.post( - self.webHook.PayloadURL, headers=headerDictionary, timeout=10, data=reqData + self.webHook.PayloadURL, headers=headerDictionary, timeout=10, data=reqData, verify=self.webHook.VerifySSL ) req.raise_for_status() success = True diff --git a/src/modules/WireguardConfiguration.py b/src/modules/WireguardConfiguration.py index 10949b56..8b42ba09 100644 --- a/src/modules/WireguardConfiguration.py +++ b/src/modules/WireguardConfiguration.py @@ -11,12 +11,14 @@ from itertools import islice from .ConnectionString import ConnectionString from .DashboardConfig import DashboardConfig +from .DashboardWebHooks import DashboardWebHooks from .Peer import Peer from .PeerJobs import PeerJobs from .PeerShareLinks import PeerShareLinks from .Utilities import StringToBoolean, GenerateWireguardPublicKey, RegexMatch, ValidateDNSAddress, \ ValidateEndpointAllowedIPs from .WireguardConfigurationInfo import WireguardConfigurationInfo, PeerGroupsClass +from .DashboardWebHooks import DashboardWebHooks class WireguardConfiguration: @@ -30,6 +32,7 @@ class WireguardConfiguration: def __init__(self, DashboardConfig: DashboardConfig, AllPeerJobs: PeerJobs, AllPeerShareLinks: PeerShareLinks, + DashboardWebHooks: DashboardWebHooks, name: str = None, data: dict = None, backup: dict = None, @@ -61,6 +64,7 @@ class WireguardConfiguration: self.AllPeerJobs = AllPeerJobs self.DashboardConfig = DashboardConfig self.AllPeerShareLinks = AllPeerShareLinks + self.DashboardWebHooks = DashboardWebHooks self.configPath = os.path.join(self.__getProtocolPath(), f'{self.Name}.conf') self.engine: sqlalchemy.engine = sqlalchemy.create_engine(ConnectionString("wgdashboard")) self.metadata: sqlalchemy.MetaData = sqlalchemy.MetaData() @@ -497,6 +501,10 @@ class WireguardConfiguration: p = self.searchPeer(p['id']) if p[0]: result['peers'].append(p[1]) + self.DashboardWebHooks.RunWebHook("peer_created", { + "configuration": self.Name, + "peers": list(map(lambda k : k['id'], peers)) + }) return True, result except Exception as e: result['message'] = str(e) @@ -598,6 +606,7 @@ class WireguardConfiguration: def deletePeers(self, listOfPublicKeys, AllPeerJobs: PeerJobs, AllPeerShareLinks: PeerShareLinks) -> tuple[bool, str]: numOfDeletedPeers = 0 numOfFailedToDeletePeers = 0 + deleted = [] if not self.getStatus(): self.toggleConfiguration() with self.engine.begin() as conn: @@ -616,6 +625,7 @@ class WireguardConfiguration: self.peersTable.columns.id == pf.id ) ) + deleted.append(pf.id) numOfDeletedPeers += 1 except Exception as e: numOfFailedToDeletePeers += 1 @@ -624,12 +634,17 @@ class WireguardConfiguration: return False, "Failed to save configuration through WireGuard" self.getPeers() - + if numOfDeletedPeers == 0 and numOfFailedToDeletePeers == 0: return False, "No peer(s) to delete found" - + if numOfDeletedPeers == len(listOfPublicKeys): + self.DashboardWebHooks.RunWebHook("peer_deleted", { + "configuration": self.Name, + "peers": deleted + }) return True, f"Deleted {numOfDeletedPeers} peer(s)" + return False, f"Deleted {numOfDeletedPeers} peer(s) successfully. Failed to delete {numOfFailedToDeletePeers} peer(s)" def __wgSave(self) -> tuple[bool, str] | tuple[bool, None]: diff --git a/src/static/app/src/components/settingsComponent/dashboardWebHooks.vue b/src/static/app/src/components/settingsComponent/dashboardWebHooks.vue index e24ef9ed..9fde52d4 100644 --- a/src/static/app/src/components/settingsComponent/dashboardWebHooks.vue +++ b/src/static/app/src/components/settingsComponent/dashboardWebHooks.vue @@ -4,8 +4,6 @@ import { fetchGet } from "@/utilities/fetch.js" import {onMounted, ref} from "vue"; import AddWebHook from "@/components/settingsComponent/dashboardWebHooksComponents/addWebHook.vue"; import WebHookSessions from "@/components/settingsComponent/dashboardWebHooksComponents/webHookSessions.vue"; -import ClientGroup from "@/components/clientComponents/clientGroup.vue"; -import ClientSettings from "@/components/clientComponents/clientSettings.vue"; const webHooks = ref([]) const webHooksLoaded = ref(false) @@ -34,14 +32,14 @@ const view = ref("edit") - -