mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-10-03 07:46:18 +00:00
Testing webhooks
This commit is contained in:
@@ -749,6 +749,10 @@ def API_addPeers(configName):
|
|||||||
if len(keyPairs) == 0 or (bulkAdd and len(keyPairs) != bulkAddAmount):
|
if len(keyPairs) == 0 or (bulkAdd and len(keyPairs) != bulkAddAmount):
|
||||||
return ResponseObject(False, "Generating key pairs by bulk failed")
|
return ResponseObject(False, "Generating key pairs by bulk failed")
|
||||||
status, result = config.addPeers(keyPairs)
|
status, result = config.addPeers(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'])
|
return ResponseObject(status=status, message=result['message'], data=result['peers'])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -813,6 +817,10 @@ def API_addPeers(configName):
|
|||||||
"advanced_security": "off"
|
"advanced_security": "off"
|
||||||
}]
|
}]
|
||||||
)
|
)
|
||||||
|
DashboardWebHooks.RunWebHook('peer_created', {
|
||||||
|
"configuration": config.Name,
|
||||||
|
"peers": [{"id": public_key}]
|
||||||
|
})
|
||||||
return ResponseObject(status=status, message=result['message'], data=result['peers'])
|
return ResponseObject(status=status, message=result['message'], data=result['peers'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
app.logger.error("Add peers failed", data, exc_info=e)
|
app.logger.error("Add peers failed", data, exc_info=e)
|
||||||
@@ -1386,7 +1394,14 @@ def API_WebHooks_UpdateWebHook():
|
|||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
status, msg = DashboardWebHooks.UpdateWebHook(data)
|
status, msg = DashboardWebHooks.UpdateWebHook(data)
|
||||||
return ResponseObject(status, msg)
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
|
@app.post(f'{APP_PREFIX}/api/webHooks/deleteWebHook')
|
||||||
|
def API_WebHooks_DeleteWebHook():
|
||||||
|
data = request.get_json()
|
||||||
|
status, msg = DashboardWebHooks.DeleteWebHook(data)
|
||||||
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Index Page
|
Index Page
|
||||||
'''
|
'''
|
||||||
|
@@ -1,7 +1,12 @@
|
|||||||
|
import json
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import urllib.parse
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from pydantic import BaseModel
|
import requests
|
||||||
|
from pydantic import BaseModel, field_serializer
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from .ConnectionString import ConnectionString
|
from .ConnectionString import ConnectionString
|
||||||
|
|
||||||
@@ -17,6 +22,21 @@ class WebHook(BaseModel):
|
|||||||
CreationDate: datetime = ''
|
CreationDate: datetime = ''
|
||||||
Notes: str = ''
|
Notes: str = ''
|
||||||
|
|
||||||
|
class WebHookSessionLog(BaseModel):
|
||||||
|
LogTime: datetime
|
||||||
|
Status: int
|
||||||
|
Message: str = ''
|
||||||
|
|
||||||
|
@field_serializer('LogTime')
|
||||||
|
def logTimeSerializer(self, LogTime: datetime):
|
||||||
|
return LogTime.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
class WebHookSessionLogs(BaseModel):
|
||||||
|
Logs: list[WebHookSessionLog] = []
|
||||||
|
|
||||||
|
def addLog(self, status: int, message: str):
|
||||||
|
self.Logs.append(WebHookSessionLog(LogTime=datetime.now(), Status=status, Message=message))
|
||||||
|
|
||||||
class DashboardWebHooks:
|
class DashboardWebHooks:
|
||||||
def __init__(self, DashboardConfig):
|
def __init__(self, DashboardConfig):
|
||||||
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
@@ -37,6 +57,22 @@ class DashboardWebHooks:
|
|||||||
db.Column('Notes', db.Text),
|
db.Column('Notes', db.Text),
|
||||||
extend_existing=True
|
extend_existing=True
|
||||||
)
|
)
|
||||||
|
self.webHookSessionsTable = db.Table(
|
||||||
|
'DashboardWebHookSessions', self.metadata,
|
||||||
|
db.Column('WebHookSessionID', db.String(255), nullable=False, primary_key=True),
|
||||||
|
db.Column('WebHookID', db.String(255), nullable=False),
|
||||||
|
db.Column('StartDate',
|
||||||
|
(db.DATETIME if DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite' else db.TIMESTAMP),
|
||||||
|
server_default=db.func.now(),
|
||||||
|
nullable=False
|
||||||
|
),
|
||||||
|
db.Column('EndDate',
|
||||||
|
(db.DATETIME if DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite' else db.TIMESTAMP),
|
||||||
|
),
|
||||||
|
db.Column('Status', db.INTEGER),
|
||||||
|
db.Column('Logs', db.JSON)
|
||||||
|
)
|
||||||
|
|
||||||
self.metadata.create_all(self.engine)
|
self.metadata.create_all(self.engine)
|
||||||
self.WebHooks: list[WebHook] = []
|
self.WebHooks: list[WebHook] = []
|
||||||
self.__getWebHooks()
|
self.__getWebHooks()
|
||||||
@@ -72,7 +108,9 @@ class DashboardWebHooks:
|
|||||||
if len(webHook.PayloadURL) == 0:
|
if len(webHook.PayloadURL) == 0:
|
||||||
return False, "Payload URL cannot be empty"
|
return False, "Payload URL cannot be empty"
|
||||||
|
|
||||||
if len(webHook.ContentType) == 0 or webHook.ContentType not in ['application/json', 'application/x-www-form-urlencoded']:
|
if len(webHook.ContentType) == 0 or webHook.ContentType not in [
|
||||||
|
'application/json', 'application/x-www-form-urlencoded'
|
||||||
|
]:
|
||||||
return False, "Content Type is invalid"
|
return False, "Content Type is invalid"
|
||||||
|
|
||||||
|
|
||||||
@@ -99,13 +137,115 @@ class DashboardWebHooks:
|
|||||||
|
|
||||||
def DeleteWebHook(self, webHook) -> tuple[bool, str] | tuple[bool, None]:
|
def DeleteWebHook(self, webHook) -> tuple[bool, str] | tuple[bool, None]:
|
||||||
try:
|
try:
|
||||||
webHook = WebHook.model_validate(webHook)
|
webHook = WebHook(**webHook)
|
||||||
with self.engine.begin() as conn:
|
with self.engine.begin() as conn:
|
||||||
conn.execute(
|
conn.execute(
|
||||||
self.webHooksTable.delete().where(
|
self.webHooksTable.delete().where(
|
||||||
self.webHooksTable.c.WebHookID == webHook.WebHookID
|
self.webHooksTable.c.WebHookID == webHook.WebHookID
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.__getWebHooks()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, str(e)
|
return False, str(e)
|
||||||
return True, None
|
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)
|
||||||
|
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
|
||||||
|
return True
|
||||||
|
|
||||||
|
class WebHookSession:
|
||||||
|
def __init__(self, webHook: WebHook, data: dict[str, str]):
|
||||||
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
|
self.metadata = db.MetaData()
|
||||||
|
self.webHookSessionsTable = db.Table('DashboardWebHookSessions', self.metadata, autoload_with=self.engine)
|
||||||
|
self.webHook = webHook
|
||||||
|
self.sessionID = str(uuid.uuid4())
|
||||||
|
self.webHookSessionLogs: WebHookSessionLogs = WebHookSessionLogs()
|
||||||
|
|
||||||
|
data['time'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
data['webhook_id'] = webHook.WebHookID
|
||||||
|
data['webhook_session'] = self.sessionID
|
||||||
|
self.Prepare()
|
||||||
|
self.Execute(data)
|
||||||
|
|
||||||
|
def Prepare(self):
|
||||||
|
with self.engine.begin() as conn:
|
||||||
|
conn.execute(
|
||||||
|
self.webHookSessionsTable.insert().values({
|
||||||
|
"WebHookSessionID": self.sessionID,
|
||||||
|
"WebHookID": self.webHook.WebHookID,
|
||||||
|
"Status": -1,
|
||||||
|
"Logs": self.webHookSessionLogs.model_dump()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
self.UpdateSessionLog(-1, "Preparing webhook session")
|
||||||
|
|
||||||
|
def UpdateSessionLog(self, status, message):
|
||||||
|
self.webHookSessionLogs.addLog(status, message)
|
||||||
|
with self.engine.begin() as conn:
|
||||||
|
conn.execute(
|
||||||
|
self.webHookSessionsTable.update().values({
|
||||||
|
"Logs": self.webHookSessionLogs.model_dump()
|
||||||
|
}).where(
|
||||||
|
self.webHookSessionsTable.c.WebHookSessionID == self.sessionID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def UpdateStatus(self, status: int):
|
||||||
|
with self.engine.begin() as conn:
|
||||||
|
conn.execute(
|
||||||
|
self.webHookSessionsTable.update().values({
|
||||||
|
"Status": status,
|
||||||
|
"EndDate": datetime.now()
|
||||||
|
}).where(
|
||||||
|
self.webHookSessionsTable.c.WebHookSessionID == self.sessionID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def Execute(self, data: dict[str, str]):
|
||||||
|
success = False
|
||||||
|
|
||||||
|
for i in range(5):
|
||||||
|
headerDictionary = {
|
||||||
|
'Content-Type': self.webHook.ContentType
|
||||||
|
}
|
||||||
|
for header in self.webHook.Headers.values():
|
||||||
|
if header['key'] not in ['Content-Type']:
|
||||||
|
headerDictionary[header['key']] = header['value']
|
||||||
|
|
||||||
|
if self.webHook.ContentType == "application/json":
|
||||||
|
reqData = json.dumps(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)
|
||||||
|
try:
|
||||||
|
req = requests.post(
|
||||||
|
self.webHook.PayloadURL, headers=headerDictionary, timeout=10, data=reqData
|
||||||
|
)
|
||||||
|
req.raise_for_status()
|
||||||
|
success = True
|
||||||
|
self.UpdateSessionLog(0, "Webhook request finished")
|
||||||
|
self.UpdateSessionLog(0, json.dumps({"returned_data": req.text}))
|
||||||
|
self.UpdateStatus(0)
|
||||||
|
break
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
self.UpdateSessionLog(1, f"Attempt #{i + 1}/5. Request errored. Reason: " + str(e))
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
self.UpdateSessionLog(1, "Webhook request failed & terminated.")
|
||||||
|
self.UpdateStatus(1)
|
||||||
|
|
||||||
|
|
@@ -444,7 +444,6 @@ class WireguardConfiguration:
|
|||||||
existingPeers = conn.execute(self.peersTable.select()).mappings().fetchall()
|
existingPeers = conn.execute(self.peersTable.select()).mappings().fetchall()
|
||||||
for i in existingPeers:
|
for i in existingPeers:
|
||||||
tmpList.append(Peer(i, self))
|
tmpList.append(Peer(i, self))
|
||||||
self.Peers = []
|
|
||||||
self.Peers = tmpList
|
self.Peers = tmpList
|
||||||
|
|
||||||
def addPeers(self, peers: list) -> tuple[bool, dict]:
|
def addPeers(self, peers: list) -> tuple[bool, dict]:
|
||||||
|
@@ -57,18 +57,22 @@ const selectedWebHook = ref(undefined)
|
|||||||
<p class="mb-0 fw-bold text-body url" >
|
<p class="mb-0 fw-bold text-body url" >
|
||||||
{{ webHook.PayloadURL }}
|
{{ webHook.PayloadURL }}
|
||||||
</p>
|
</p>
|
||||||
<small>
|
<p class="url mb-0">
|
||||||
<LocaleText t="Subscribed Actions"></LocaleText>:
|
<LocaleText t="Subscribed Actions"></LocaleText>:
|
||||||
{{ webHook.SubscribedActions.join(", ")}}
|
{{ webHook.SubscribedActions.join(", ")}}
|
||||||
</small>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
<div class="flex-grow-1 d-flex text-muted" v-else>
|
<div class="flex-grow-1 d-flex text-muted" v-else>
|
||||||
<LocaleText t="No Webhooks" class="m-auto"></LocaleText>
|
<LocaleText t="No Webhooks" class="m-auto"></LocaleText>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-8 overflow-scroll h-100" v-if="selectedWebHook">
|
<div class="col-sm-8 overflow-scroll h-100" >
|
||||||
<AddWebHook :webHook="selectedWebHook" @refresh="getWebHooks()" :key="selectedWebHook.WebHookID"></AddWebHook>
|
<AddWebHook
|
||||||
|
:key="selectedWebHook"
|
||||||
|
v-if="selectedWebHook"
|
||||||
|
@delete="getWebHooks(); selectedWebHook = undefined;"
|
||||||
|
:webHook="selectedWebHook" @refresh="getWebHooks()" ></AddWebHook>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<suspense v-else>
|
<suspense v-else>
|
||||||
|
@@ -30,7 +30,7 @@ const Actions = ref({
|
|||||||
'peer_deleted': "Peer Deleted",
|
'peer_deleted': "Peer Deleted",
|
||||||
'peer_updated': "Peer Updated"
|
'peer_updated': "Peer Updated"
|
||||||
})
|
})
|
||||||
const emits = defineEmits(['refresh'])
|
const emits = defineEmits(['refresh', 'delete'])
|
||||||
|
|
||||||
const alert = ref(false)
|
const alert = ref(false)
|
||||||
const alertMsg = ref("")
|
const alertMsg = ref("")
|
||||||
@@ -48,6 +48,19 @@ const submitWebHook = async (e) => {
|
|||||||
submitting.value = false
|
submitting.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteWebHook = async () => {
|
||||||
|
submitting.value = true;
|
||||||
|
await fetchPost("/api/webHooks/deleteWebHook", newWebHook.value, (res) => {
|
||||||
|
if (res.status){
|
||||||
|
emits('delete')
|
||||||
|
}else{
|
||||||
|
alert.value = true
|
||||||
|
alertMsg.value = res.message
|
||||||
|
}
|
||||||
|
submitting.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -208,7 +221,7 @@ const submitWebHook = async (e) => {
|
|||||||
<h6 class="mb-0">
|
<h6 class="mb-0">
|
||||||
<LocaleText t="Danger Zone"></LocaleText></h6>
|
<LocaleText t="Danger Zone"></LocaleText></h6>
|
||||||
<button
|
<button
|
||||||
@click="confirmDelete = true"
|
@click="deleteWebHook()"
|
||||||
type="button"
|
type="button"
|
||||||
:class="{disabled: submitting}"
|
:class="{disabled: submitting}"
|
||||||
class="btn bg-danger-subtle text-danger-emphasis border-danger-subtle rounded-3 ms-auto">
|
class="btn bg-danger-subtle text-danger-emphasis border-danger-subtle rounded-3 ms-auto">
|
||||||
|
Reference in New Issue
Block a user