mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-10-03 15:56:17 +00:00
Webhook UI
This commit is contained in:
@@ -66,6 +66,8 @@ class CustomJsonEncoder(DefaultJSONProvider):
|
|||||||
return o.toJson()
|
return o.toJson()
|
||||||
if type(o) is RowMapping:
|
if type(o) is RowMapping:
|
||||||
return dict(o)
|
return dict(o)
|
||||||
|
if type(o) is datetime:
|
||||||
|
return o.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
return super().default(self)
|
return super().default(self)
|
||||||
app.json = CustomJsonEncoder(app)
|
app.json = CustomJsonEncoder(app)
|
||||||
|
|
||||||
@@ -1368,6 +1370,23 @@ def API_Clients_DeleteClient():
|
|||||||
if not DashboardClients.GetClient(clientId):
|
if not DashboardClients.GetClient(clientId):
|
||||||
return ResponseObject(False, "Client does not exist")
|
return ResponseObject(False, "Client does not exist")
|
||||||
return ResponseObject(status=DashboardClients.DeleteClient(clientId))
|
return ResponseObject(status=DashboardClients.DeleteClient(clientId))
|
||||||
|
|
||||||
|
@app.get(f'{APP_PREFIX}/api/webHooks/getWebHooks')
|
||||||
|
def API_WebHooks_GetWebHooks():
|
||||||
|
return ResponseObject(data=DashboardWebHooks.GetWebHooks())
|
||||||
|
|
||||||
|
@app.get(f'{APP_PREFIX}/api/webHooks/createWebHook')
|
||||||
|
def API_WebHooks_createWebHook():
|
||||||
|
return ResponseObject(data=DashboardWebHooks.CreateWebHook().model_dump(
|
||||||
|
exclude={'CreationDate'}
|
||||||
|
))
|
||||||
|
|
||||||
|
@app.post(f'{APP_PREFIX}/api/webHooks/updateWebHook')
|
||||||
|
def API_WebHooks_UpdateWebHook():
|
||||||
|
data = request.get_json()
|
||||||
|
status, msg = DashboardWebHooks.UpdateWebHook(data)
|
||||||
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Index Page
|
Index Page
|
||||||
'''
|
'''
|
||||||
|
@@ -7,14 +7,14 @@ from .ConnectionString import ConnectionString
|
|||||||
|
|
||||||
WebHookActions = ['peer_created', 'peer_deleted', 'peer_updated']
|
WebHookActions = ['peer_created', 'peer_deleted', 'peer_updated']
|
||||||
class WebHook(BaseModel):
|
class WebHook(BaseModel):
|
||||||
WebHookID: str = uuid.uuid4()
|
WebHookID: str = ''
|
||||||
PayloadURL: str = ''
|
PayloadURL: str = ''
|
||||||
ContentType: str = ''
|
ContentType: str = 'application/json'
|
||||||
Headers: dict[str, str] = {}
|
Headers: dict[str, dict[str, str]] = {}
|
||||||
VerifySSL: bool = True
|
VerifySSL: bool = True
|
||||||
SubscribedActions: list[str] = WebHookActions
|
SubscribedActions: list[str] = WebHookActions
|
||||||
IsActive: bool = True
|
IsActive: bool = True
|
||||||
CreationDate: datetime = datetime.now()
|
CreationDate: datetime = ''
|
||||||
Notes: str = ''
|
Notes: str = ''
|
||||||
|
|
||||||
class DashboardWebHooks:
|
class DashboardWebHooks:
|
||||||
@@ -26,11 +26,14 @@ class DashboardWebHooks:
|
|||||||
db.Column('WebHookID', db.String(255), nullable=False, primary_key=True),
|
db.Column('WebHookID', db.String(255), nullable=False, primary_key=True),
|
||||||
db.Column('PayloadURL', db.Text, nullable=False),
|
db.Column('PayloadURL', db.Text, nullable=False),
|
||||||
db.Column('ContentType', db.String(255), nullable=False),
|
db.Column('ContentType', db.String(255), nullable=False),
|
||||||
db.Column('Headers', db.Text),
|
db.Column('Headers', db.JSON),
|
||||||
db.Column('VerifySSL', db.Boolean, nullable=False),
|
db.Column('VerifySSL', db.Boolean, nullable=False),
|
||||||
db.Column('SubscribedActions', db.Text),
|
db.Column('SubscribedActions', db.JSON),
|
||||||
db.Column('IsActive', db.Boolean, nullable=False),
|
db.Column('IsActive', db.Boolean, nullable=False),
|
||||||
db.Column('CreationDate', (db.DATETIME if DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite' else db.TIMESTAMP), nullable=False),
|
db.Column('CreationDate',
|
||||||
|
(db.DATETIME if DashboardConfig.GetConfig("Database", "type")[1] == 'sqlite' else db.TIMESTAMP),
|
||||||
|
server_default=db.func.now(),
|
||||||
|
nullable=False),
|
||||||
db.Column('Notes', db.Text),
|
db.Column('Notes', db.Text),
|
||||||
extend_existing=True
|
extend_existing=True
|
||||||
)
|
)
|
||||||
@@ -41,13 +44,19 @@ class DashboardWebHooks:
|
|||||||
def __getWebHooks(self):
|
def __getWebHooks(self):
|
||||||
with self.engine.connect() as conn:
|
with self.engine.connect() as conn:
|
||||||
webhooks = conn.execute(
|
webhooks = conn.execute(
|
||||||
self.webHooksTable.select()
|
self.webHooksTable.select().order_by(
|
||||||
|
self.webHooksTable.c.CreationDate
|
||||||
|
)
|
||||||
).mappings().fetchall()
|
).mappings().fetchall()
|
||||||
self.WebHooks.clear()
|
self.WebHooks.clear()
|
||||||
self.WebHooks = [WebHook(**webhook) for webhook in webhooks]
|
self.WebHooks = [WebHook(**webhook) for webhook in webhooks]
|
||||||
|
|
||||||
|
def GetWebHooks(self):
|
||||||
|
self.__getWebHooks()
|
||||||
|
return list(map(lambda x : x.model_dump(), self.WebHooks))
|
||||||
|
|
||||||
def CreateWebHook(self):
|
def CreateWebHook(self) -> WebHook:
|
||||||
return WebHook().model_dump()
|
return WebHook(WebHookID=str(uuid.uuid4()))
|
||||||
|
|
||||||
def SearchWebHook(self, webHook: WebHook) -> WebHook | None:
|
def SearchWebHook(self, webHook: WebHook) -> WebHook | None:
|
||||||
try:
|
try:
|
||||||
@@ -58,7 +67,15 @@ class DashboardWebHooks:
|
|||||||
|
|
||||||
def UpdateWebHook(self, webHook: dict[str, str]) -> tuple[bool, str] | tuple[bool, None]:
|
def UpdateWebHook(self, webHook: dict[str, str]) -> tuple[bool, str] | tuple[bool, None]:
|
||||||
try:
|
try:
|
||||||
webHook = WebHook.model_validate(webHook)
|
webHook = WebHook(**webHook)
|
||||||
|
|
||||||
|
if len(webHook.PayloadURL) == 0:
|
||||||
|
return False, "Payload URL cannot be empty"
|
||||||
|
|
||||||
|
if len(webHook.ContentType) == 0 or webHook.ContentType not in ['application/json', 'application/x-www-form-urlencoded']:
|
||||||
|
return False, "Content Type is invalid"
|
||||||
|
|
||||||
|
|
||||||
with self.engine.begin() as conn:
|
with self.engine.begin() as conn:
|
||||||
if self.SearchWebHook(webHook):
|
if self.SearchWebHook(webHook):
|
||||||
conn.execute(
|
conn.execute(
|
||||||
@@ -69,6 +86,7 @@ class DashboardWebHooks:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
webHook.CreationDate = datetime.now()
|
||||||
conn.execute(
|
conn.execute(
|
||||||
self.webHooksTable.insert().values(
|
self.webHooksTable.insert().values(
|
||||||
webHook.model_dump()
|
webHook.model_dump()
|
||||||
|
@@ -22,7 +22,7 @@ fetchGet("/api/locale", {}, (res) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-100">
|
<div class="h-100 bg-body" :data-bs-theme="store.Configuration?.Server.dashboard_theme">
|
||||||
<div style="z-index: 9999; height: 5px" class="position-absolute loadingBar top-0 start-0"></div>
|
<div style="z-index: 9999; height: 5px" class="position-absolute loadingBar top-0 start-0"></div>
|
||||||
<nav class="navbar bg-dark sticky-top" data-bs-theme="dark" v-if="!route.meta.hideTopNav">
|
<nav class="navbar bg-dark sticky-top" data-bs-theme="dark" v-if="!route.meta.hideTopNav">
|
||||||
<div class="container-fluid d-flex text-body align-items-center">
|
<div class="container-fluid d-flex text-body align-items-center">
|
||||||
@@ -57,7 +57,8 @@ fetchGet("/api/locale", {}, (res) => {
|
|||||||
.app-enter-from,
|
.app-enter-from,
|
||||||
.app-leave-to{
|
.app-leave-to{
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(1.1);
|
transform: scale(1.05);
|
||||||
|
filter: blur(8px);
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.navbar{
|
.navbar{
|
||||||
|
@@ -78,60 +78,65 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<form class="d-flex flex-column gap-2">
|
<div>
|
||||||
<div class="row g-2">
|
<h6>
|
||||||
<div class="col-sm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label :for="'currentPassword_' + this.uuid" class="text-muted mb-1">
|
|
||||||
<strong><small>
|
|
||||||
<LocaleText t="Current Password"></LocaleText>
|
|
||||||
</small></strong>
|
|
||||||
</label>
|
|
||||||
<input type="password" class="form-control"
|
|
||||||
autocomplete="current-password"
|
|
||||||
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
|
||||||
v-model="this.value.currentPassword"
|
|
||||||
:id="'currentPassword_' + this.uuid">
|
|
||||||
<div class="invalid-feedback d-block" v-if="showInvalidFeedback">{{this.invalidFeedback}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label :for="'newPassword_' + this.uuid" class="text-muted mb-1">
|
|
||||||
<strong><small>
|
|
||||||
<LocaleText t="New Password"></LocaleText>
|
|
||||||
</small></strong>
|
|
||||||
</label>
|
|
||||||
<input type="password" class="form-control"
|
|
||||||
autocomplete="new-password"
|
|
||||||
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
|
||||||
v-model="this.value.newPassword"
|
|
||||||
:id="'newPassword_' + this.uuid">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label :for="'repeatNewPassword_' + this.uuid" class="text-muted mb-1">
|
|
||||||
<strong><small>
|
|
||||||
<LocaleText t="Repeat New Password"></LocaleText>
|
|
||||||
</small></strong>
|
|
||||||
</label>
|
|
||||||
<input type="password" class="form-control"
|
|
||||||
autocomplete="new-password"
|
|
||||||
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
|
||||||
v-model="this.value.repeatNewPassword"
|
|
||||||
:id="'repeatNewPassword_' + this.uuid">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
:disabled="!this.passwordValid"
|
|
||||||
class="ms-auto btn bg-success-subtle text-success-emphasis border-1 border-success-subtle rounded-3 shadow-sm" @click="this.useValidation()">
|
|
||||||
<i class="bi bi-save2-fill me-2"></i>
|
|
||||||
<LocaleText t="Update Password"></LocaleText>
|
<LocaleText t="Update Password"></LocaleText>
|
||||||
</button>
|
</h6>
|
||||||
</form>
|
<form class="d-flex flex-column gap-2">
|
||||||
|
<div class="row g-2">
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label :for="'currentPassword_' + this.uuid" class="text-muted mb-1">
|
||||||
|
<strong><small>
|
||||||
|
<LocaleText t="Current Password"></LocaleText>
|
||||||
|
</small></strong>
|
||||||
|
</label>
|
||||||
|
<input type="password" class="form-control"
|
||||||
|
autocomplete="current-password"
|
||||||
|
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
||||||
|
v-model="this.value.currentPassword"
|
||||||
|
:id="'currentPassword_' + this.uuid">
|
||||||
|
<div class="invalid-feedback d-block" v-if="showInvalidFeedback">{{this.invalidFeedback}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label :for="'newPassword_' + this.uuid" class="text-muted mb-1">
|
||||||
|
<strong><small>
|
||||||
|
<LocaleText t="New Password"></LocaleText>
|
||||||
|
</small></strong>
|
||||||
|
</label>
|
||||||
|
<input type="password" class="form-control"
|
||||||
|
autocomplete="new-password"
|
||||||
|
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
||||||
|
v-model="this.value.newPassword"
|
||||||
|
:id="'newPassword_' + this.uuid">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label :for="'repeatNewPassword_' + this.uuid" class="text-muted mb-1">
|
||||||
|
<strong><small>
|
||||||
|
<LocaleText t="Repeat New Password"></LocaleText>
|
||||||
|
</small></strong>
|
||||||
|
</label>
|
||||||
|
<input type="password" class="form-control"
|
||||||
|
autocomplete="new-password"
|
||||||
|
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
||||||
|
v-model="this.value.repeatNewPassword"
|
||||||
|
:id="'repeatNewPassword_' + this.uuid">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
:disabled="!this.passwordValid"
|
||||||
|
class="ms-auto btn bg-success-subtle text-success-emphasis border-1 border-success-subtle rounded-3 shadow-sm" @click="this.useValidation()">
|
||||||
|
<i class="bi bi-save2-fill me-2"></i>
|
||||||
|
<LocaleText t="Save"></LocaleText>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@@ -65,6 +65,7 @@ export default {
|
|||||||
<div class="card rounded-3" >
|
<div class="card rounded-3" >
|
||||||
<div class="card-header d-flex align-items-center" :class="{'border-bottom-0 rounded-3': !this.value}">
|
<div class="card-header d-flex align-items-center" :class="{'border-bottom-0 rounded-3': !this.value}">
|
||||||
<h6 class="my-2">
|
<h6 class="my-2">
|
||||||
|
<i class="bi bi-key-fill me-2"></i>
|
||||||
<LocaleText t="API Keys"></LocaleText>
|
<LocaleText t="API Keys"></LocaleText>
|
||||||
</h6>
|
</h6>
|
||||||
<div class="form-check form-switch ms-auto" v-if="!this.store.getActiveCrossServer()" >
|
<div class="form-check form-switch ms-auto" v-if="!this.store.getActiveCrossServer()" >
|
||||||
|
@@ -60,6 +60,7 @@ const sendTestEmail = async () => {
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h6 class="my-2 d-flex">
|
<h6 class="my-2 d-flex">
|
||||||
|
<i class="bi bi-envelope-fill me-2"></i>
|
||||||
<LocaleText t="Email Account"></LocaleText>
|
<LocaleText t="Email Account"></LocaleText>
|
||||||
<span class="text-success ms-auto" v-if="emailIsReady">
|
<span class="text-success ms-auto" v-if="emailIsReady">
|
||||||
<i class="bi bi-check-circle-fill me-2"></i>
|
<i class="bi bi-check-circle-fill me-2"></i>
|
||||||
|
@@ -1,11 +1,100 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import { fetchGet } from "@/utilities/fetch.js"
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import AddWebHook from "@/components/settingsComponent/dashboardWebHooksComponents/addWebHook.vue";
|
||||||
|
const webHooks = ref([])
|
||||||
|
const webHooksLoaded = ref(false)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getWebHooks()
|
||||||
|
webHooksLoaded.value = true
|
||||||
|
})
|
||||||
|
const getWebHooks = async () => {
|
||||||
|
await fetchGet("/api/webHooks/getWebHooks", {}, (res) => {
|
||||||
|
webHooks.value = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const addWebHook = ref(false)
|
||||||
|
const selectedWebHook = ref(undefined)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div class="card rounded-3">
|
||||||
|
<div class="card-header d-flex align-items-center">
|
||||||
|
<h6 class="my-2">
|
||||||
|
<i class="bi bi-plug-fill me-2"></i>
|
||||||
|
<LocaleText t="Webhooks"></LocaleText>
|
||||||
|
</h6>
|
||||||
|
<button class="btn bg-primary-subtle text-primary-emphasis border-1 border-primary-subtle rounded-3 shadow-sm ms-auto"
|
||||||
|
@click="addWebHook = true; selectedWebHook = undefined"
|
||||||
|
v-if="!addWebHook"
|
||||||
|
>
|
||||||
|
<i class="bi bi-plus-circle-fill me-2"></i>
|
||||||
|
<LocaleText t="Webhook"></LocaleText>
|
||||||
|
</button>
|
||||||
|
<button class="btn bg-secondary-subtle text-secondary-emphasis border-1 border-secondary-subtle rounded-3 shadow-sm ms-auto"
|
||||||
|
@click="addWebHook = false"
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
<i class="bi bi-chevron-left me-2"></i>
|
||||||
|
<LocaleText t="Back"></LocaleText>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div style="height: 600px" class="overflow-scroll">
|
||||||
|
<div class="row g-0 h-100" v-if="!addWebHook">
|
||||||
|
<div class="col-sm-4 border-end h-100" style="overflow-y: scroll">
|
||||||
|
<div class="list-group d-flex flex-column d-flex h-100">
|
||||||
|
<a role="button"
|
||||||
|
@click="selectedWebHook = webHook"
|
||||||
|
:class="{active: selectedWebHook?.WebHookID === webHook.WebHookID}"
|
||||||
|
v-if="webHooks.length > 0"
|
||||||
|
v-for="webHook in webHooks"
|
||||||
|
class="list-group-item list-group-item-action " aria-current="true">
|
||||||
|
<p class="mb-0 fw-bold text-body url" >
|
||||||
|
{{ webHook.PayloadURL }}
|
||||||
|
</p>
|
||||||
|
<small>
|
||||||
|
<LocaleText t="Subscribed Actions"></LocaleText>:
|
||||||
|
{{ webHook.SubscribedActions.join(", ")}}
|
||||||
|
</small>
|
||||||
|
</a>
|
||||||
|
<div class="flex-grow-1 d-flex text-muted" v-else>
|
||||||
|
<LocaleText t="No Webhooks" class="m-auto"></LocaleText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8 overflow-scroll h-100" v-if="selectedWebHook">
|
||||||
|
<AddWebHook :webHook="selectedWebHook" @refresh="getWebHooks()" :key="selectedWebHook.WebHookID"></AddWebHook>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<suspense v-else>
|
||||||
|
<AddWebHook @refresh="selectedWebHook = undefined; addWebHook = false; getWebHooks(); "></AddWebHook>
|
||||||
|
</suspense>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.list-group-item{
|
||||||
|
border-radius: 0 !important;
|
||||||
|
border-left: 0 !important;
|
||||||
|
border-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item:first-child{
|
||||||
|
border-top: 0 !important;
|
||||||
|
}
|
||||||
|
.url{
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@@ -0,0 +1,225 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {fetchGet, fetchPost} from "@/utilities/fetch.js"
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import { v4 } from "uuid";
|
||||||
|
|
||||||
|
const newWebHook = ref({
|
||||||
|
ContentType: String,
|
||||||
|
Headers: Object,
|
||||||
|
IsActive: Boolean,
|
||||||
|
Notes: String,
|
||||||
|
PayloadURL: String,
|
||||||
|
SubscribedActions: Array,
|
||||||
|
VerifySSL: Boolean,
|
||||||
|
WebHookID: String
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps(['webHook'])
|
||||||
|
|
||||||
|
if (!props.webHook){
|
||||||
|
await fetchGet("/api/webHooks/createWebHook", {}, (res) => {
|
||||||
|
newWebHook.value = res.data
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
newWebHook.value = {...props.webHook}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Actions = ref({
|
||||||
|
'peer_created': "Peer Created",
|
||||||
|
'peer_deleted': "Peer Deleted",
|
||||||
|
'peer_updated': "Peer Updated"
|
||||||
|
})
|
||||||
|
const emits = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
const alert = ref(false)
|
||||||
|
const alertMsg = ref("")
|
||||||
|
const submitting = ref(false)
|
||||||
|
const submitWebHook = async (e) => {
|
||||||
|
if (e) e.preventDefault()
|
||||||
|
submitting.value = true
|
||||||
|
await fetchPost("/api/webHooks/updateWebHook", newWebHook.value, (res) => {
|
||||||
|
if (res.status){
|
||||||
|
emits('refresh')
|
||||||
|
}else{
|
||||||
|
alert.value = true
|
||||||
|
alertMsg.value = res.message
|
||||||
|
}
|
||||||
|
submitting.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="p-3">
|
||||||
|
<div v-if="!webHook">
|
||||||
|
<h6>
|
||||||
|
<LocaleText t="Add Webhook"></LocaleText>
|
||||||
|
</h6>
|
||||||
|
<p>
|
||||||
|
<LocaleText t="WGDashboard will sent a POST Request to the URL below with details of any subscribed events."></LocaleText>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<form
|
||||||
|
@submit="(e) => submitWebHook(e)"
|
||||||
|
class="d-flex flex-column gap-2">
|
||||||
|
<div>
|
||||||
|
<label for="PayloadURL" class="form-label fw-bold text-muted">
|
||||||
|
<small>
|
||||||
|
<LocaleText t="Payload URL"></LocaleText>*
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
:disabled="submitting"
|
||||||
|
id="PayloadURL" v-model="newWebHook.PayloadURL"
|
||||||
|
class="form-control rounded-3" type="url">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="ContentType" class="form-label fw-bold text-muted">
|
||||||
|
<small>
|
||||||
|
<LocaleText t="Content Type"></LocaleText>*
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
:disabled="submitting"
|
||||||
|
id="ContentType" v-model="newWebHook.ContentType"
|
||||||
|
class="form-select rounded-3" required>
|
||||||
|
<option value="application/json">
|
||||||
|
application/json
|
||||||
|
</option>
|
||||||
|
<option value="application/x-www-form-urlencoded">
|
||||||
|
application/x-www-form-urlencoded
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label fw-bold text-muted">
|
||||||
|
<small>
|
||||||
|
<LocaleText t="Verify SSL"></LocaleText>
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<div class="form-check form-switch mb-2">
|
||||||
|
<input
|
||||||
|
:disabled="submitting"
|
||||||
|
v-model="newWebHook.VerifySSL"
|
||||||
|
class="form-check-input" type="checkbox" role="switch" id="VerifySSL" >
|
||||||
|
<label class="form-check-label" for="VerifySSL">
|
||||||
|
<LocaleText :t="newWebHook.VerifySSL ? 'Enabled':'Disabled'"></LocaleText>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="alert-danger alert rounded-3" v-if="!newWebHook.VerifySSL">
|
||||||
|
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||||
|
<LocaleText t="We highly suggest to enable SSL verification"></LocaleText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label fw-bold text-muted">
|
||||||
|
<small>
|
||||||
|
<LocaleText t="Custom Headers"></LocaleText>
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
<div class="card rounded-3">
|
||||||
|
<div class="card-body d-flex gap-2 flex-column">
|
||||||
|
<div class="d-flex gap-2" v-for="(header, headerKey) in newWebHook.Headers">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<input class="form-control rounded-3 form-control-sm"
|
||||||
|
:disabled="submitting"
|
||||||
|
v-model="header.key"
|
||||||
|
placeholder="Key">
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<input class="form-control rounded-3 form-control-sm"
|
||||||
|
:disabled="submitting"
|
||||||
|
v-model="header.value"
|
||||||
|
placeholder="Value">
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
:class="{disabled: submitting}"
|
||||||
|
type="button"
|
||||||
|
@click="delete newWebHook.Headers[headerKey]"
|
||||||
|
class="btn btn-sm bg-danger-subtle text-danger-emphasis border-danger-subtle rounded-3">
|
||||||
|
<i class="bi bi-trash-fill"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
:class="{disabled: submitting}"
|
||||||
|
@click="newWebHook.Headers[v4().toString()] = {key: '', value: ''}"
|
||||||
|
class="btn btn-sm bg-primary-subtle text-primary-emphasis border-primary-subtle rounded-3">
|
||||||
|
<i class="bi bi-plus-lg me-2"></i><LocaleText t="Header"></LocaleText>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<label class="form-label fw-bold text-muted">
|
||||||
|
<small>
|
||||||
|
<LocaleText t="Subscribed Actions"></LocaleText>
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<div v-for="(action, key) in Actions" class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input"
|
||||||
|
:disabled="newWebHook.SubscribedActions.length === 1 && newWebHook.SubscribedActions.includes(key) || submitting"
|
||||||
|
type="checkbox" :id="key"
|
||||||
|
:value="key"
|
||||||
|
v-model="newWebHook.SubscribedActions">
|
||||||
|
<label class="form-check-label" :for="key">{{ action }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<label class="form-label fw-bold text-muted">
|
||||||
|
<small>
|
||||||
|
<LocaleText t="Enable Webhook"></LocaleText>
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<div class="form-check form-switch mb-2">
|
||||||
|
<input
|
||||||
|
:disabled="submitting"
|
||||||
|
v-model="newWebHook.IsActive"
|
||||||
|
class="form-check-input" type="checkbox" role="switch" id="IsActive" >
|
||||||
|
<label class="form-check-label" for="IsActive">
|
||||||
|
<LocaleText :t="newWebHook.IsActive ? 'Yes':'No'"></LocaleText>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-danger rounded-3" v-if="alert">
|
||||||
|
{{ alertMsg }}
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
:class="{disabled: submitting}"
|
||||||
|
class="ms-auto btn bg-success-subtle text-success-emphasis border-success-subtle rounded-3">
|
||||||
|
<LocaleText t="Save"></LocaleText>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<template v-if="webHook">
|
||||||
|
<hr>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<h6 class="mb-0">
|
||||||
|
<LocaleText t="Danger Zone"></LocaleText></h6>
|
||||||
|
<button
|
||||||
|
@click="confirmDelete = true"
|
||||||
|
type="button"
|
||||||
|
:class="{disabled: submitting}"
|
||||||
|
class="btn bg-danger-subtle text-danger-emphasis border-danger-subtle rounded-3 ms-auto">
|
||||||
|
<LocaleText t="Delete"></LocaleText>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@@ -21,6 +21,7 @@ const dashboardConfigurationStore = DashboardConfigurationStore()
|
|||||||
<div class="card rounded-3">
|
<div class="card rounded-3">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h6 class="my-2">
|
<h6 class="my-2">
|
||||||
|
<i class="bi bi-magic me-2"></i>
|
||||||
<LocaleText t="Appearance"></LocaleText>
|
<LocaleText t="Appearance"></LocaleText>
|
||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,9 +36,10 @@ const dashboardConfigurationStore = DashboardConfigurationStore()
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card rounded-3">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h6 class="my-2">
|
<h6 class="my-2">
|
||||||
|
<i class="bi bi-ethernet me-2"></i>
|
||||||
<LocaleText t="Dashboard IP Address & Listen Port"></LocaleText>
|
<LocaleText t="Dashboard IP Address & Listen Port"></LocaleText>
|
||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
@@ -45,9 +47,10 @@ const dashboardConfigurationStore = DashboardConfigurationStore()
|
|||||||
<DashboardIPPortInput></DashboardIPPortInput>
|
<DashboardIPPortInput></DashboardIPPortInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card rounded-3">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h6 class="my-2">
|
<h6 class="my-2">
|
||||||
|
<i class="bi bi-people-fill me-2"></i>
|
||||||
<LocaleText t="Account Settings"></LocaleText>
|
<LocaleText t="Account Settings"></LocaleText>
|
||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,18 +66,16 @@ const dashboardConfigurationStore = DashboardConfigurationStore()
|
|||||||
targetData="password">
|
targetData="password">
|
||||||
</AccountSettingsInputPassword>
|
</AccountSettingsInputPassword>
|
||||||
</div>
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<h6 >
|
||||||
|
<LocaleText t="Multi-Factor Authentication (MFA)"></LocaleText>
|
||||||
|
</h6>
|
||||||
|
<AccountSettingsMFA v-if="!dashboardConfigurationStore.getActiveCrossServer()"></AccountSettingsMFA>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h6 class="my-2">
|
|
||||||
<LocaleText t="Multi-Factor Authentication (MFA)"></LocaleText>
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<AccountSettingsMFA v-if="!dashboardConfigurationStore.getActiveCrossServer()"></AccountSettingsMFA>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DashboardAPIKeys></DashboardAPIKeys>
|
<DashboardAPIKeys></DashboardAPIKeys>
|
||||||
<DashboardEmailSettings></DashboardEmailSettings>
|
<DashboardEmailSettings></DashboardEmailSettings>
|
||||||
<DashboardWebHooks></DashboardWebHooks>
|
<DashboardWebHooks></DashboardWebHooks>
|
||||||
|
Reference in New Issue
Block a user