mirror of
https://github.com/requarks/wiki.git
synced 2026-08-26 13:17:10 -05:00
feat(admin): migrate webhooks to vue 3 composable
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
const graphHelper = require('../../helpers/graph')
|
||||
const _ = require('lodash')
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
module.exports = {
|
||||
Query: {
|
||||
async hooks () {
|
||||
return WIKI.models.hooks.query().orderBy('name')
|
||||
},
|
||||
async hookById (obj, args) {
|
||||
return WIKI.models.hooks.query().findById(args.id)
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
/**
|
||||
* CREATE HOOK
|
||||
*/
|
||||
async createHook (obj, args) {
|
||||
try {
|
||||
// -> Validate inputs
|
||||
if (!args.name || args.name.length < 1) {
|
||||
throw WIKI.ERROR(new Error('Invalid Hook Name'), 'HookCreateInvalidName')
|
||||
}
|
||||
if (!args.events || args.events.length < 1) {
|
||||
throw WIKI.ERROR(new Error('Invalid Hook Events'), 'HookCreateInvalidEvents')
|
||||
}
|
||||
if (!args.url || args.url.length < 8 || !args.url.startsWith('http')) {
|
||||
throw WIKI.ERROR(new Error('Invalid Hook URL'), 'HookCreateInvalidURL')
|
||||
}
|
||||
// -> Create hook
|
||||
const newHook = await WIKI.models.hooks.createHook(args)
|
||||
return {
|
||||
operation: graphHelper.generateSuccess('Hook created successfully'),
|
||||
hook: newHook
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* UPDATE HOOK
|
||||
*/
|
||||
async updateHook (obj, args) {
|
||||
try {
|
||||
// -> Load hook
|
||||
const hook = await WIKI.models.hooks.query().findById(args.id)
|
||||
if (!hook) {
|
||||
throw WIKI.ERROR(new Error('Invalid Hook ID'), 'HookInvalidId')
|
||||
}
|
||||
// -> Check for bad input
|
||||
if (_.has(args.patch, 'name') && args.patch.name.length < 1) {
|
||||
throw WIKI.ERROR(new Error('Invalid Hook Name'), 'HookCreateInvalidName')
|
||||
}
|
||||
if (_.has(args.patch, 'events') && args.patch.events.length < 1) {
|
||||
throw WIKI.ERROR(new Error('Invalid Hook Events'), 'HookCreateInvalidEvents')
|
||||
}
|
||||
if (_.has(args.patch, 'url') && (_.trim(args.patch.url).length < 8 || !args.patch.url.startsWith('http'))) {
|
||||
throw WIKI.ERROR(new Error('URL is invalid.'), 'HookInvalidURL')
|
||||
}
|
||||
// -> Update hook
|
||||
await WIKI.models.hooks.query().findById(args.id).patch(args.patch)
|
||||
|
||||
return {
|
||||
operation: graphHelper.generateSuccess('Hook updated successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* DELETE HOOK
|
||||
*/
|
||||
async deleteHook (obj, args) {
|
||||
try {
|
||||
await WIKI.models.hooks.deleteHook(args.id)
|
||||
return {
|
||||
operation: graphHelper.generateSuccess('Hook deleted successfully')
|
||||
}
|
||||
} catch (err) {
|
||||
return graphHelper.generateError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
# ===============================================
|
||||
# WEBHOOKS
|
||||
# ===============================================
|
||||
|
||||
extend type Query {
|
||||
hooks: [Hook]
|
||||
|
||||
hookById(
|
||||
id: UUID!
|
||||
): Hook
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createHook(
|
||||
name: String!
|
||||
events: [String]!
|
||||
url: String!
|
||||
includeMetadata: Boolean!
|
||||
includeContent: Boolean!
|
||||
acceptUntrusted: Boolean!
|
||||
authHeader: String
|
||||
): HookCreateResponse
|
||||
|
||||
updateHook(
|
||||
id: UUID!
|
||||
patch: HookUpdateInput!
|
||||
): DefaultResponse
|
||||
|
||||
deleteHook (
|
||||
id: UUID!
|
||||
): DefaultResponse
|
||||
}
|
||||
|
||||
# -----------------------------------------------
|
||||
# TYPES
|
||||
# -----------------------------------------------
|
||||
|
||||
type Hook {
|
||||
id: UUID
|
||||
name: String
|
||||
events: [String]
|
||||
url: String
|
||||
includeMetadata: Boolean
|
||||
includeContent: Boolean
|
||||
acceptUntrusted: Boolean
|
||||
authHeader: String
|
||||
state: HookState
|
||||
lastErrorMessage: String
|
||||
}
|
||||
|
||||
input HookUpdateInput {
|
||||
name: String
|
||||
events: [String]
|
||||
url: String
|
||||
includeMetadata: Boolean
|
||||
includeContent: Boolean
|
||||
acceptUntrusted: Boolean
|
||||
authHeader: String
|
||||
}
|
||||
|
||||
enum HookState {
|
||||
pending
|
||||
error
|
||||
success
|
||||
}
|
||||
|
||||
type HookCreateResponse {
|
||||
operation: Operation
|
||||
hook: Hook
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
const Model = require('objection').Model
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
/**
|
||||
* Hook model
|
||||
*/
|
||||
module.exports = class Hook extends Model {
|
||||
static get tableName () { return 'hooks' }
|
||||
|
||||
static get jsonAttributes () {
|
||||
return ['events']
|
||||
}
|
||||
|
||||
$beforeUpdate () {
|
||||
this.updatedAt = new Date()
|
||||
}
|
||||
|
||||
static async createHook (data) {
|
||||
return WIKI.models.hooks.query().insertAndFetch({
|
||||
name: data.name,
|
||||
events: data.events,
|
||||
url: data.url,
|
||||
includeMetadata: data.includeMetadata,
|
||||
includeContent: data.includeContent,
|
||||
acceptUntrusted: data.acceptUntrusted,
|
||||
authHeader: data.authHeader,
|
||||
state: 'pending',
|
||||
lastErrorMessage: null
|
||||
})
|
||||
}
|
||||
|
||||
static async updateHook (id, patch) {
|
||||
return WIKI.models.hooks.query().findById(id).patch({
|
||||
...patch,
|
||||
state: 'pending',
|
||||
lastErrorMessage: null
|
||||
})
|
||||
}
|
||||
|
||||
static async deleteHook (id) {
|
||||
return WIKI.models.hooks.query().deleteById(id)
|
||||
}
|
||||
}
|
||||
@@ -1,92 +1,106 @@
|
||||
<template lang="pug">
|
||||
q-dialog(ref='dialog', @hide='onDialogHide')
|
||||
q-dialog(ref='dialogRef', @hide='onDialogHide')
|
||||
q-card(style='min-width: 350px; max-width: 450px;')
|
||||
q-card-section.card-header
|
||||
q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
|
||||
span {{$t(`admin.webhooks.delete`)}}
|
||||
span {{t(`admin.webhooks.delete`)}}
|
||||
q-card-section
|
||||
.text-body2
|
||||
i18n-t(keypath='admin.webhooks.deleteConfirm')
|
||||
template(v-slot:name)
|
||||
strong {{hook.name}}
|
||||
.text-body2.q-mt-md
|
||||
strong.text-negative {{$t(`admin.webhooks.deleteConfirmWarn`)}}
|
||||
strong.text-negative {{t(`admin.webhooks.deleteConfirmWarn`)}}
|
||||
q-card-actions.card-actions
|
||||
q-space
|
||||
q-btn.acrylic-btn(
|
||||
flat
|
||||
:label='$t(`common.actions.cancel`)'
|
||||
:label='t(`common.actions.cancel`)'
|
||||
color='grey'
|
||||
padding='xs md'
|
||||
@click='hide'
|
||||
@click='onDialogCancel'
|
||||
)
|
||||
q-btn(
|
||||
unelevated
|
||||
:label='$t(`common.actions.delete`)'
|
||||
:label='t(`common.actions.delete`)'
|
||||
color='negative'
|
||||
padding='xs md'
|
||||
@click='confirm'
|
||||
:loading='state.isLoading'
|
||||
)
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import gql from 'graphql-tag'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDialogPluginComponent, useQuasar } from 'quasar'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
hook: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
emits: ['ok', 'hide'],
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show () {
|
||||
this.$refs.dialog.show()
|
||||
},
|
||||
hide () {
|
||||
this.$refs.dialog.hide()
|
||||
},
|
||||
onDialogHide () {
|
||||
this.$emit('hide')
|
||||
},
|
||||
async confirm () {
|
||||
try {
|
||||
const resp = await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation deleteHook ($id: UUID!) {
|
||||
deleteHook(id: $id) {
|
||||
status {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: this.hook.id
|
||||
}
|
||||
})
|
||||
if (resp?.data?.deleteHook?.status?.succeeded) {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: this.$t('admin.webhooks.deleteSuccess')
|
||||
})
|
||||
this.$emit('ok')
|
||||
this.hide()
|
||||
} else {
|
||||
throw new Error(resp?.data?.deleteHook?.status?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
}
|
||||
// PROPS
|
||||
|
||||
const props = defineProps({
|
||||
hook: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
// EMITS
|
||||
|
||||
defineEmits([
|
||||
...useDialogPluginComponent.emits
|
||||
])
|
||||
|
||||
// QUASAR
|
||||
|
||||
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
|
||||
const $q = useQuasar()
|
||||
|
||||
// I18N
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
// DATA
|
||||
|
||||
const state = reactive({
|
||||
isLoading: false
|
||||
})
|
||||
|
||||
// METHODS
|
||||
|
||||
async function confirm () {
|
||||
state.isLoading = true
|
||||
try {
|
||||
const resp = await APOLLO_CLIENT.mutate({
|
||||
mutation: gql`
|
||||
mutation deleteHook ($id: UUID!) {
|
||||
deleteHook(id: $id) {
|
||||
operation {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: props.hook.id
|
||||
}
|
||||
})
|
||||
if (resp?.data?.deleteHook?.operation?.succeeded) {
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: t('admin.webhooks.deleteSuccess')
|
||||
})
|
||||
onDialogOK()
|
||||
} else {
|
||||
throw new Error(resp?.data?.deleteHook?.operation?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
state.isLoading = false
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<template lang="pug">
|
||||
q-dialog(ref='dialog', @hide='onDialogHide')
|
||||
q-dialog(ref='dialogRef', @hide='onDialogHide')
|
||||
q-card(style='min-width: 850px;')
|
||||
q-card-section.card-header
|
||||
template(v-if='hookId')
|
||||
template(v-if='props.hookId')
|
||||
q-icon(name='img:/_assets/icons/fluent-pencil-drawing.svg', left, size='sm')
|
||||
span {{$t(`admin.webhooks.edit`)}}
|
||||
span {{t(`admin.webhooks.edit`)}}
|
||||
template(v-else)
|
||||
q-icon(name='img:/_assets/icons/fluent-plus-plus.svg', left, size='sm')
|
||||
span {{$t(`admin.webhooks.new`)}}
|
||||
span {{t(`admin.webhooks.new`)}}
|
||||
//- STATE INFO BAR
|
||||
q-card-section.flex.items-center.bg-indigo.text-white(v-if='hookId && hook.state === `pending`')
|
||||
q-card-section.flex.items-center.bg-indigo.text-white(v-if='props.hookId && state.hook.state === `pending`')
|
||||
q-spinner-clock.q-mr-sm(
|
||||
color='white'
|
||||
size='xs'
|
||||
)
|
||||
.text-caption {{$t('admin.webhooks.statePendingHint')}}
|
||||
q-card-section.flex.items-center.bg-positive.text-white(v-if='hookId && hook.state === `success`')
|
||||
.text-caption {{t('admin.webhooks.statePendingHint')}}
|
||||
q-card-section.flex.items-center.bg-positive.text-white(v-if='props.hookId && state.hook.state === `success`')
|
||||
q-spinner-infinity.q-mr-sm(
|
||||
color='white'
|
||||
size='xs'
|
||||
)
|
||||
.text-caption {{$t('admin.webhooks.stateSuccessHint')}}
|
||||
q-card-section.bg-negative.text-white(v-if='hookId && hook.state === `error`')
|
||||
.text-caption {{t('admin.webhooks.stateSuccessHint')}}
|
||||
q-card-section.bg-negative.text-white(v-if='props.hookId && state.hook.state === `error`')
|
||||
.flex.items-center
|
||||
q-icon.q-mr-sm(
|
||||
color='white'
|
||||
size='xs'
|
||||
name='las la-exclamation-triangle'
|
||||
)
|
||||
.text-caption {{$t('admin.webhooks.stateErrorExplain')}}
|
||||
.text-caption.q-pl-lg.q-ml-xs.text-red-2 {{hook.lastErrorMessage}}
|
||||
.text-caption {{t('admin.webhooks.stateErrorExplain')}}
|
||||
.text-caption.q-pl-lg.q-ml-xs.text-red-2 {{state.hook.lastErrorMessage}}
|
||||
//- FORM
|
||||
q-form.q-py-sm(ref='editWebhookForm')
|
||||
q-item
|
||||
@@ -37,15 +37,12 @@ q-dialog(ref='dialog', @hide='onDialogHide')
|
||||
q-item-section
|
||||
q-input(
|
||||
outlined
|
||||
v-model='hook.name'
|
||||
v-model='state.hook.name'
|
||||
dense
|
||||
:rules=`[
|
||||
val => val.length > 0 || $t('admin.webhooks.nameMissing'),
|
||||
val => /^[^<>"]+$/.test(val) || $t('admin.webhooks.nameInvalidChars')
|
||||
]`
|
||||
:rules='hookNameValidation'
|
||||
hide-bottom-space
|
||||
:label='$t(`common.field.name`)'
|
||||
:aria-label='$t(`common.field.name`)'
|
||||
:label='t(`common.field.name`)'
|
||||
:aria-label='t(`common.field.name`)'
|
||||
lazy-rules='ondemand'
|
||||
autofocus
|
||||
)
|
||||
@@ -55,7 +52,7 @@ q-dialog(ref='dialog', @hide='onDialogHide')
|
||||
q-select(
|
||||
outlined
|
||||
:options='events'
|
||||
v-model='hook.events'
|
||||
v-model='state.hook.events'
|
||||
multiple
|
||||
map-options
|
||||
emit-value
|
||||
@@ -63,21 +60,18 @@ q-dialog(ref='dialog', @hide='onDialogHide')
|
||||
option-label='name'
|
||||
options-dense
|
||||
dense
|
||||
:rules=`[
|
||||
val => val.length > 0 || $t('admin.webhooks.eventsMissing')
|
||||
]`
|
||||
:rules='hookEventsValidation'
|
||||
hide-bottom-space
|
||||
:label='$t(`admin.webhooks.events`)'
|
||||
:aria-label='$t(`admin.webhooks.events`)'
|
||||
:label='t(`admin.webhooks.events`)'
|
||||
:aria-label='t(`admin.webhooks.events`)'
|
||||
lazy-rules='ondemand'
|
||||
)
|
||||
template(v-slot:selected)
|
||||
.text-caption(v-if='hook.events.length > 0') {{$tc(`admin.webhooks.eventsSelected`, hook.events.length, { count: hook.events.length })}}
|
||||
span(v-else)
|
||||
template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
|
||||
.text-caption(v-if='state.hook.events.length > 0') {{t(`admin.webhooks.eventsSelected`, state.hook.events.length, { count: state.hook.events.length })}}
|
||||
span(v-else)
|
||||
template(v-slot:option='{ itemProps, opt, selected, toggleOption }')
|
||||
q-item(
|
||||
v-bind='itemProps'
|
||||
v-on='itemEvents'
|
||||
)
|
||||
q-item-section(side)
|
||||
q-checkbox(
|
||||
@@ -97,19 +91,16 @@ q-dialog(ref='dialog', @hide='onDialogHide')
|
||||
q-item
|
||||
blueprint-icon.self-start(icon='unknown-status')
|
||||
q-item-section
|
||||
q-item-label {{$t(`admin.webhooks.url`)}}
|
||||
q-item-label(caption) {{$t(`admin.webhooks.urlHint`)}}
|
||||
q-item-label {{t(`admin.webhooks.url`)}}
|
||||
q-item-label(caption) {{t(`admin.webhooks.urlHint`)}}
|
||||
q-input.q-mt-sm(
|
||||
outlined
|
||||
v-model='hook.url'
|
||||
v-model='state.hook.url'
|
||||
dense
|
||||
:rules=`[
|
||||
val => (val.length > 0 && val.startsWith('http')) || $t('admin.webhooks.urlMissing'),
|
||||
val => /^[^<>"]+$/.test(val) || $t('admin.webhooks.urlInvalidChars')
|
||||
]`
|
||||
:rules='hookUrlValidation'
|
||||
hide-bottom-space
|
||||
placeholder='https://'
|
||||
:aria-label='$t(`admin.webhooks.url`)'
|
||||
:aria-label='t(`admin.webhooks.url`)'
|
||||
lazy-rules='ondemand'
|
||||
)
|
||||
template(v-slot:prepend)
|
||||
@@ -122,301 +113,328 @@ q-dialog(ref='dialog', @hide='onDialogHide')
|
||||
q-item(tag='label', v-ripple)
|
||||
blueprint-icon(icon='rescan-document')
|
||||
q-item-section
|
||||
q-item-label {{$t(`admin.webhooks.includeMetadata`)}}
|
||||
q-item-label(caption) {{$t(`admin.webhooks.includeMetadataHint`)}}
|
||||
q-item-label {{t(`admin.webhooks.includeMetadata`)}}
|
||||
q-item-label(caption) {{t(`admin.webhooks.includeMetadataHint`)}}
|
||||
q-item-section(avatar)
|
||||
q-toggle(
|
||||
v-model='hook.includeMetadata'
|
||||
v-model='state.hook.includeMetadata'
|
||||
color='primary'
|
||||
checked-icon='las la-check'
|
||||
unchecked-icon='las la-times'
|
||||
:aria-label='$t(`admin.webhooks.includeMetadata`)'
|
||||
:aria-label='t(`admin.webhooks.includeMetadata`)'
|
||||
)
|
||||
q-item(tag='label', v-ripple)
|
||||
blueprint-icon(icon='select-all')
|
||||
q-item-section
|
||||
q-item-label {{$t(`admin.webhooks.includeContent`)}}
|
||||
q-item-label(caption) {{$t(`admin.webhooks.includeContentHint`)}}
|
||||
q-item-label {{t(`admin.webhooks.includeContent`)}}
|
||||
q-item-label(caption) {{t(`admin.webhooks.includeContentHint`)}}
|
||||
q-item-section(avatar)
|
||||
q-toggle(
|
||||
v-model='hook.includeContent'
|
||||
v-model='state.hook.includeContent'
|
||||
color='primary'
|
||||
checked-icon='las la-check'
|
||||
unchecked-icon='las la-times'
|
||||
:aria-label='$t(`admin.webhooks.includeContent`)'
|
||||
:aria-label='t(`admin.webhooks.includeContent`)'
|
||||
)
|
||||
q-item(tag='label', v-ripple)
|
||||
blueprint-icon(icon='security-ssl')
|
||||
q-item-section
|
||||
q-item-label {{$t(`admin.webhooks.acceptUntrusted`)}}
|
||||
q-item-label(caption) {{$t(`admin.webhooks.acceptUntrustedHint`)}}
|
||||
q-item-label {{t(`admin.webhooks.acceptUntrusted`)}}
|
||||
q-item-label(caption) {{t(`admin.webhooks.acceptUntrustedHint`)}}
|
||||
q-item-section(avatar)
|
||||
q-toggle(
|
||||
v-model='hook.acceptUntrusted'
|
||||
v-model='state.hook.acceptUntrusted'
|
||||
color='primary'
|
||||
checked-icon='las la-check'
|
||||
unchecked-icon='las la-times'
|
||||
:aria-label='$t(`admin.webhooks.acceptUntrusted`)'
|
||||
:aria-label='t(`admin.webhooks.acceptUntrusted`)'
|
||||
)
|
||||
q-item
|
||||
blueprint-icon.self-start(icon='fingerprint-scan')
|
||||
q-item-section
|
||||
q-item-label {{$t(`admin.webhooks.authHeader`)}}
|
||||
q-item-label(caption) {{$t(`admin.webhooks.authHeaderHint`)}}
|
||||
q-item-label {{t(`admin.webhooks.authHeader`)}}
|
||||
q-item-label(caption) {{t(`admin.webhooks.authHeaderHint`)}}
|
||||
q-input.q-mt-sm(
|
||||
outlined
|
||||
v-model='hook.authHeader'
|
||||
v-model='state.hook.authHeader'
|
||||
dense
|
||||
:aria-label='$t(`admin.webhooks.authHeader`)'
|
||||
:aria-label='t(`admin.webhooks.authHeader`)'
|
||||
)
|
||||
q-card-actions.card-actions
|
||||
q-space
|
||||
q-btn.acrylic-btn(
|
||||
flat
|
||||
:label='$t(`common.actions.cancel`)'
|
||||
:label='t(`common.actions.cancel`)'
|
||||
color='grey'
|
||||
padding='xs md'
|
||||
@click='hide'
|
||||
@click='onDialogCancel'
|
||||
)
|
||||
q-btn(
|
||||
v-if='hookId'
|
||||
v-if='props.hookId'
|
||||
unelevated
|
||||
:label='$t(`common.actions.save`)'
|
||||
:label='t(`common.actions.save`)'
|
||||
color='primary'
|
||||
padding='xs md'
|
||||
@click='save'
|
||||
:loading='loading'
|
||||
:loading='state.isLoading'
|
||||
)
|
||||
q-btn(
|
||||
v-else
|
||||
unelevated
|
||||
:label='$t(`common.actions.create`)'
|
||||
:label='t(`common.actions.create`)'
|
||||
color='primary'
|
||||
padding='xs md'
|
||||
@click='create'
|
||||
:loading='loading'
|
||||
:loading='state.isLoading'
|
||||
)
|
||||
|
||||
q-inner-loading(:showing='loading')
|
||||
q-inner-loading(:showing='state.isLoading')
|
||||
q-spinner(color='accent', size='lg')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import gql from 'graphql-tag'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDialogPluginComponent, useQuasar } from 'quasar'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
|
||||
import { QSpinnerClock, QSpinnerInfinity } from 'quasar'
|
||||
// PROPS
|
||||
|
||||
export default {
|
||||
components: {
|
||||
QSpinnerClock,
|
||||
QSpinnerInfinity
|
||||
},
|
||||
props: {
|
||||
hookId: {
|
||||
type: String,
|
||||
default: null
|
||||
const props = defineProps({
|
||||
hookId: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
// EMITS
|
||||
|
||||
defineEmits([
|
||||
...useDialogPluginComponent.emits
|
||||
])
|
||||
|
||||
// QUASAR
|
||||
|
||||
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
|
||||
const $q = useQuasar()
|
||||
|
||||
// I18N
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
// DATA
|
||||
|
||||
const state = reactive({
|
||||
isLoading: false,
|
||||
hook: {
|
||||
name: '',
|
||||
events: [],
|
||||
url: '',
|
||||
acceptUntrusted: false,
|
||||
authHeader: '',
|
||||
includeMetadata: true,
|
||||
includeContent: false,
|
||||
state: 'pending',
|
||||
lastErrorMessage: ''
|
||||
}
|
||||
})
|
||||
|
||||
// COMPUTED
|
||||
|
||||
const events = computed(() => ([
|
||||
{ key: 'page:create', name: t('admin.webhooks.eventCreatePage'), type: t('admin.webhooks.typePage') },
|
||||
{ key: 'page:edit', name: t('admin.webhooks.eventEditPage'), type: t('admin.webhooks.typePage') },
|
||||
{ key: 'page:rename', name: t('admin.webhooks.eventRenamePage'), type: t('admin.webhooks.typePage') },
|
||||
{ key: 'page:delete', name: t('admin.webhooks.eventDeletePage'), type: t('admin.webhooks.typePage') },
|
||||
{ key: 'asset:upload', name: t('admin.webhooks.eventUploadAsset'), type: t('admin.webhooks.typeAsset') },
|
||||
{ key: 'asset:edit', name: t('admin.webhooks.eventEditAsset'), type: t('admin.webhooks.typeAsset') },
|
||||
{ key: 'asset:rename', name: t('admin.webhooks.eventRenameAsset'), type: t('admin.webhooks.typeAsset') },
|
||||
{ key: 'asset:delete', name: t('admin.webhooks.eventDeleteAsset'), type: t('admin.webhooks.typeAsset') },
|
||||
{ key: 'comment:new', name: t('admin.webhooks.eventNewComment'), type: t('admin.webhooks.typeComment') },
|
||||
{ key: 'comment:edit', name: t('admin.webhooks.eventEditComment'), type: t('admin.webhooks.typeComment') },
|
||||
{ key: 'comment:delete', name: t('admin.webhooks.eventDeleteComment'), type: t('admin.webhooks.typeComment') },
|
||||
{ key: 'user:join', name: t('admin.webhooks.eventUserJoin'), type: t('admin.webhooks.typeUser') },
|
||||
{ key: 'user:login', name: t('admin.webhooks.eventUserLogin'), type: t('admin.webhooks.typeUser') },
|
||||
{ key: 'user:logout', name: t('admin.webhooks.eventUserLogout'), type: t('admin.webhooks.typeUser') }
|
||||
]))
|
||||
|
||||
// REFS
|
||||
|
||||
const editWebhookForm = ref(null)
|
||||
|
||||
// VALIDATION RULES
|
||||
|
||||
const hookNameValidation = [
|
||||
val => val.length > 0 || t('admin.webhooks.nameMissing'),
|
||||
val => /^[^<>"]+$/.test(val) || t('admin.webhooks.nameInvalidChars')
|
||||
]
|
||||
const hookEventsValidation = [
|
||||
val => val.length > 0 || t('admin.webhooks.eventsMissing')
|
||||
]
|
||||
const hookUrlValidation = [
|
||||
val => (val.length > 0 && val.startsWith('http')) || t('admin.webhooks.urlMissing'),
|
||||
val => /^[^<>"]+$/.test(val) || t('admin.webhooks.urlInvalidChars')
|
||||
]
|
||||
|
||||
// METHODS
|
||||
|
||||
async function fetchHook (id) {
|
||||
state.isLoading = true
|
||||
try {
|
||||
const resp = await APOLLO_CLIENT.query({
|
||||
query: gql`
|
||||
query getHook (
|
||||
$id: UUID!
|
||||
) {
|
||||
hookById (
|
||||
id: $id
|
||||
) {
|
||||
name
|
||||
events
|
||||
url
|
||||
includeMetadata
|
||||
includeContent
|
||||
acceptUntrusted
|
||||
authHeader
|
||||
state
|
||||
lastErrorMessage
|
||||
}
|
||||
}
|
||||
`,
|
||||
fetchPolicy: 'no-cache',
|
||||
variables: { id }
|
||||
})
|
||||
if (resp?.data?.hookById) {
|
||||
state.hook = cloneDeep(resp.data.hookById)
|
||||
} else {
|
||||
throw new Error('Failed to fetch webhook configuration.')
|
||||
}
|
||||
},
|
||||
emits: ['ok', 'hide'],
|
||||
data () {
|
||||
return {
|
||||
hook: {
|
||||
name: '',
|
||||
events: [],
|
||||
url: '',
|
||||
acceptUntrusted: false,
|
||||
authHeader: '',
|
||||
includeMetadata: true,
|
||||
includeContent: false,
|
||||
state: 'pending',
|
||||
lastErrorMessage: ''
|
||||
},
|
||||
loading: false
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
onDialogHide()
|
||||
}
|
||||
state.isLoading = false
|
||||
}
|
||||
|
||||
async function create () {
|
||||
state.isLoading = true
|
||||
try {
|
||||
const isFormValid = await editWebhookForm.value.validate(true)
|
||||
if (!isFormValid) {
|
||||
throw new Error(t('admin.webhooks.createInvalidData'))
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
events () {
|
||||
return [
|
||||
{ key: 'page:create', name: this.$t('admin.webhooks.eventCreatePage'), type: this.$t('admin.webhooks.typePage') },
|
||||
{ key: 'page:edit', name: this.$t('admin.webhooks.eventEditPage'), type: this.$t('admin.webhooks.typePage') },
|
||||
{ key: 'page:rename', name: this.$t('admin.webhooks.eventRenamePage'), type: this.$t('admin.webhooks.typePage') },
|
||||
{ key: 'page:delete', name: this.$t('admin.webhooks.eventDeletePage'), type: this.$t('admin.webhooks.typePage') },
|
||||
{ key: 'asset:upload', name: this.$t('admin.webhooks.eventUploadAsset'), type: this.$t('admin.webhooks.typeAsset') },
|
||||
{ key: 'asset:edit', name: this.$t('admin.webhooks.eventEditAsset'), type: this.$t('admin.webhooks.typeAsset') },
|
||||
{ key: 'asset:rename', name: this.$t('admin.webhooks.eventRenameAsset'), type: this.$t('admin.webhooks.typeAsset') },
|
||||
{ key: 'asset:delete', name: this.$t('admin.webhooks.eventDeleteAsset'), type: this.$t('admin.webhooks.typeAsset') },
|
||||
{ key: 'comment:new', name: this.$t('admin.webhooks.eventNewComment'), type: this.$t('admin.webhooks.typeComment') },
|
||||
{ key: 'comment:edit', name: this.$t('admin.webhooks.eventEditComment'), type: this.$t('admin.webhooks.typeComment') },
|
||||
{ key: 'comment:delete', name: this.$t('admin.webhooks.eventDeleteComment'), type: this.$t('admin.webhooks.typeComment') },
|
||||
{ key: 'user:join', name: this.$t('admin.webhooks.eventUserJoin'), type: this.$t('admin.webhooks.typeUser') },
|
||||
{ key: 'user:login', name: this.$t('admin.webhooks.eventUserLogin'), type: this.$t('admin.webhooks.typeUser') },
|
||||
{ key: 'user:logout', name: this.$t('admin.webhooks.eventUserLogout'), type: this.$t('admin.webhooks.typeUser') }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show () {
|
||||
this.$refs.dialog.show()
|
||||
if (this.hookId) {
|
||||
this.fetchHook(this.hookId)
|
||||
}
|
||||
},
|
||||
hide () {
|
||||
this.$refs.dialog.hide()
|
||||
},
|
||||
onDialogHide () {
|
||||
this.$emit('hide')
|
||||
},
|
||||
async fetchHook (id) {
|
||||
this.loading = true
|
||||
try {
|
||||
const resp = await this.$apollo.query({
|
||||
query: gql`
|
||||
query getHook (
|
||||
$id: UUID!
|
||||
) {
|
||||
hookById (
|
||||
id: $id
|
||||
) {
|
||||
name
|
||||
events
|
||||
url
|
||||
includeMetadata
|
||||
includeContent
|
||||
acceptUntrusted
|
||||
authHeader
|
||||
state
|
||||
lastErrorMessage
|
||||
}
|
||||
}
|
||||
`,
|
||||
fetchPolicy: 'no-cache',
|
||||
variables: { id }
|
||||
})
|
||||
if (resp?.data?.hookById) {
|
||||
this.hook = cloneDeep(resp.data.hookById)
|
||||
} else {
|
||||
throw new Error('Failed to fetch webhook configuration.')
|
||||
}
|
||||
} catch (err) {
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
this.hide()
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
async create () {
|
||||
this.loading = true
|
||||
try {
|
||||
const isFormValid = await this.$refs.editWebhookForm.validate(true)
|
||||
if (!isFormValid) {
|
||||
throw new Error(this.$t('admin.webhooks.createInvalidData'))
|
||||
}
|
||||
const resp = await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation createHook (
|
||||
$name: String!
|
||||
$events: [String]!
|
||||
$url: String!
|
||||
$includeMetadata: Boolean!
|
||||
$includeContent: Boolean!
|
||||
$acceptUntrusted: Boolean!
|
||||
$authHeader: String
|
||||
) {
|
||||
createHook (
|
||||
name: $name
|
||||
events: $events
|
||||
url: $url
|
||||
includeMetadata: $includeMetadata
|
||||
includeContent: $includeContent
|
||||
acceptUntrusted: $acceptUntrusted
|
||||
authHeader: $authHeader
|
||||
) {
|
||||
status {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: this.hook
|
||||
})
|
||||
if (resp?.data?.createHook?.status?.succeeded) {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: this.$t('admin.webhooks.createSuccess')
|
||||
})
|
||||
this.$emit('ok')
|
||||
this.hide()
|
||||
} else {
|
||||
throw new Error(resp?.data?.createHook?.status?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
async save () {
|
||||
this.loading = true
|
||||
try {
|
||||
const isFormValid = await this.$refs.editWebhookForm.validate(true)
|
||||
if (!isFormValid) {
|
||||
throw new Error(this.$t('admin.webhooks.createInvalidData'))
|
||||
}
|
||||
const resp = await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation saveHook (
|
||||
$id: UUID!
|
||||
$patch: HookUpdateInput!
|
||||
) {
|
||||
updateHook (
|
||||
id: $id
|
||||
patch: $patch
|
||||
) {
|
||||
status {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: this.hookId,
|
||||
patch: {
|
||||
name: this.hook.name,
|
||||
events: this.hook.events,
|
||||
url: this.hook.url,
|
||||
acceptUntrusted: this.hook.acceptUntrusted,
|
||||
authHeader: this.hook.authHeader,
|
||||
includeMetadata: this.hook.includeMetadata,
|
||||
includeContent: this.hook.includeContent
|
||||
const resp = await APOLLO_CLIENT.mutate({
|
||||
mutation: gql`
|
||||
mutation createHook (
|
||||
$name: String!
|
||||
$events: [String]!
|
||||
$url: String!
|
||||
$includeMetadata: Boolean!
|
||||
$includeContent: Boolean!
|
||||
$acceptUntrusted: Boolean!
|
||||
$authHeader: String
|
||||
) {
|
||||
createHook (
|
||||
name: $name
|
||||
events: $events
|
||||
url: $url
|
||||
includeMetadata: $includeMetadata
|
||||
includeContent: $includeContent
|
||||
acceptUntrusted: $acceptUntrusted
|
||||
authHeader: $authHeader
|
||||
) {
|
||||
operation {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
})
|
||||
if (resp?.data?.updateHook?.status?.succeeded) {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: this.$t('admin.webhooks.updateSuccess')
|
||||
})
|
||||
this.$emit('ok')
|
||||
this.hide()
|
||||
} else {
|
||||
throw new Error(resp?.data?.updateHook?.status?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
this.loading = false
|
||||
`,
|
||||
variables: state.hook
|
||||
})
|
||||
if (resp?.data?.createHook?.operation?.succeeded) {
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: t('admin.webhooks.createSuccess')
|
||||
})
|
||||
onDialogOK()
|
||||
} else {
|
||||
throw new Error(resp?.data?.createHook?.operation?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
state.isLoading = false
|
||||
}
|
||||
|
||||
async function save () {
|
||||
state.isLoading = true
|
||||
try {
|
||||
const isFormValid = await editWebhookForm.value.validate(true)
|
||||
if (!isFormValid) {
|
||||
throw new Error(t('admin.webhooks.createInvalidData'))
|
||||
}
|
||||
const resp = await APOLLO_CLIENT.mutate({
|
||||
mutation: gql`
|
||||
mutation saveHook (
|
||||
$id: UUID!
|
||||
$patch: HookUpdateInput!
|
||||
) {
|
||||
updateHook (
|
||||
id: $id
|
||||
patch: $patch
|
||||
) {
|
||||
operation {
|
||||
succeeded
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: props.hookId,
|
||||
patch: {
|
||||
name: state.hook.name,
|
||||
events: state.hook.events,
|
||||
url: state.hook.url,
|
||||
acceptUntrusted: state.hook.acceptUntrusted,
|
||||
authHeader: state.hook.authHeader,
|
||||
includeMetadata: state.hook.includeMetadata,
|
||||
includeContent: state.hook.includeContent
|
||||
}
|
||||
}
|
||||
})
|
||||
if (resp?.data?.updateHook?.operation?.succeeded) {
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: t('admin.webhooks.updateSuccess')
|
||||
})
|
||||
onDialogOK()
|
||||
} else {
|
||||
throw new Error(resp?.data?.updateHook?.operation?.message || 'An unexpected error occured.')
|
||||
}
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
state.isLoading = false
|
||||
}
|
||||
|
||||
// MOUNTED
|
||||
|
||||
onMounted(() => {
|
||||
if (props.hookId) {
|
||||
fetchHook(props.hookId)
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
@@ -41,7 +41,7 @@ q-page.admin-locale
|
||||
)
|
||||
q-separator(inset)
|
||||
.row.q-pa-md.q-col-gutter-md
|
||||
.col-7
|
||||
.col-12.col-lg-7
|
||||
//- -----------------------
|
||||
//- Locale Options
|
||||
//- -----------------------
|
||||
@@ -89,7 +89,7 @@ q-page.admin-locale
|
||||
span {{ t('admin.locale.namespacingPrefixWarning.title', { langCode: state.selectedLocale }) }}
|
||||
.text-caption.text-yellow-1 {{ t('admin.locale.namespacingPrefixWarning.subtitle') }}
|
||||
|
||||
.col-5
|
||||
.col-12.col-lg-5
|
||||
//- -----------------------
|
||||
//- Namespacing
|
||||
//- -----------------------
|
||||
|
||||
+101
-94
@@ -4,14 +4,9 @@ q-page.admin-webhooks
|
||||
.col-auto
|
||||
img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-lightning-bolt.svg')
|
||||
.col.q-pl-md
|
||||
.text-h5.text-primary.animated.fadeInLeft {{ $t('admin.webhooks.title') }}
|
||||
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.webhooks.subtitle') }}
|
||||
.text-h5.text-primary.animated.fadeInLeft {{ t('admin.webhooks.title') }}
|
||||
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.webhooks.subtitle') }}
|
||||
.col-auto
|
||||
q-spinner-tail.q-mr-md(
|
||||
v-show='loading'
|
||||
color='accent'
|
||||
size='sm'
|
||||
)
|
||||
q-btn.q-mr-sm.acrylic-btn(
|
||||
icon='las la-question-circle'
|
||||
flat
|
||||
@@ -24,19 +19,19 @@ q-page.admin-webhooks
|
||||
icon='las la-redo-alt'
|
||||
flat
|
||||
color='secondary'
|
||||
:loading='loading > 0'
|
||||
:loading='state.loading > 0'
|
||||
@click='load'
|
||||
)
|
||||
q-btn(
|
||||
unelevated
|
||||
icon='las la-plus'
|
||||
:label='$t(`admin.webhooks.new`)'
|
||||
:label='t(`admin.webhooks.new`)'
|
||||
color='primary'
|
||||
@click='createHook'
|
||||
)
|
||||
q-separator(inset)
|
||||
.row.q-pa-md.q-col-gutter-md
|
||||
.col-12(v-if='hooks.length < 1')
|
||||
.col-12(v-if='state.hooks.length < 1')
|
||||
q-card.rounded-borders(
|
||||
flat
|
||||
:class='$q.dark.isActive ? `bg-dark-5 text-white` : `bg-grey-3 text-dark`'
|
||||
@@ -44,11 +39,11 @@ q-page.admin-webhooks
|
||||
q-card-section.items-center(horizontal)
|
||||
q-card-section.col-auto.q-pr-none
|
||||
q-icon(name='las la-info-circle', size='sm')
|
||||
q-card-section.text-caption {{ $t('admin.webhooks.none') }}
|
||||
q-card-section.text-caption {{ t('admin.webhooks.none') }}
|
||||
.col-12(v-else)
|
||||
q-card
|
||||
q-list(separator)
|
||||
q-item(v-for='hook of hooks', :key='hook.id')
|
||||
q-item(v-for='hook of state.hooks', :key='hook.id')
|
||||
q-item-section(side)
|
||||
q-icon(name='las la-bolt', color='primary')
|
||||
q-item-section
|
||||
@@ -60,23 +55,23 @@ q-page.admin-webhooks
|
||||
color='indigo'
|
||||
size='xs'
|
||||
)
|
||||
.text-caption.text-indigo {{$t('admin.webhooks.statePending')}}
|
||||
q-tooltip(anchor='center left', self='center right') {{$t('admin.webhooks.statePendingHint')}}
|
||||
.text-caption.text-indigo {{t('admin.webhooks.statePending')}}
|
||||
q-tooltip(anchor='center left', self='center right') {{t('admin.webhooks.statePendingHint')}}
|
||||
template(v-else-if='hook.state === `success`')
|
||||
q-spinner-infinity.q-mr-sm(
|
||||
color='positive'
|
||||
size='xs'
|
||||
)
|
||||
.text-caption.text-positive {{$t('admin.webhooks.stateSuccess')}}
|
||||
q-tooltip(anchor='center left', self='center right') {{$t('admin.webhooks.stateSuccessHint')}}
|
||||
.text-caption.text-positive {{t('admin.webhooks.stateSuccess')}}
|
||||
q-tooltip(anchor='center left', self='center right') {{t('admin.webhooks.stateSuccessHint')}}
|
||||
template(v-else-if='hook.state === `error`')
|
||||
q-icon.q-mr-sm(
|
||||
color='negative'
|
||||
size='xs'
|
||||
name='las la-exclamation-triangle'
|
||||
)
|
||||
.text-caption.text-negative {{$t('admin.webhooks.stateError')}}
|
||||
q-tooltip(anchor='center left', self='center right') {{$t('admin.webhooks.stateErrorHint')}}
|
||||
.text-caption.text-negative {{t('admin.webhooks.stateError')}}
|
||||
q-tooltip(anchor='center left', self='center right') {{t('admin.webhooks.stateErrorHint')}}
|
||||
q-separator.q-ml-md(vertical)
|
||||
q-item-section(side, style='flex-direction: row; align-items: center;')
|
||||
q-btn.acrylic-btn.q-mr-sm(
|
||||
@@ -96,88 +91,100 @@ q-page.admin-webhooks
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
import { createMetaMixin, QSpinnerClock, QSpinnerInfinity } from 'quasar'
|
||||
import WebhookDeleteDialog from '../components/WebhookDeleteDialog.vue'
|
||||
import WebhookEditDialog from '../components/WebhookEditDialog.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useMeta, useQuasar } from 'quasar'
|
||||
import { onMounted, reactive } from 'vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
QSpinnerClock,
|
||||
QSpinnerInfinity
|
||||
},
|
||||
mixins: [
|
||||
createMetaMixin(function () {
|
||||
return {
|
||||
title: this.$t('admin.webhooks.title')
|
||||
import WebhookEditDialog from 'src/components/WebhookEditDialog.vue'
|
||||
import WebhookDeleteDialog from 'src/components/WebhookDeleteDialog.vue'
|
||||
|
||||
// QUASAR
|
||||
|
||||
const $q = useQuasar()
|
||||
|
||||
// I18N
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
// META
|
||||
|
||||
useMeta({
|
||||
title: t('admin.webhooks.title')
|
||||
})
|
||||
|
||||
// DATA
|
||||
|
||||
const state = reactive({
|
||||
hooks: [],
|
||||
loading: 0
|
||||
})
|
||||
|
||||
// METHODS
|
||||
|
||||
async function load () {
|
||||
state.loading++
|
||||
$q.loading.show()
|
||||
const resp = await APOLLO_CLIENT.query({
|
||||
query: gql`
|
||||
query getHooks {
|
||||
hooks {
|
||||
id
|
||||
name
|
||||
url
|
||||
state
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
hooks: [],
|
||||
loading: 0
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.load()
|
||||
},
|
||||
methods: {
|
||||
async load () {
|
||||
this.loading++
|
||||
this.$q.loading.show()
|
||||
const resp = await this.$apollo.query({
|
||||
query: gql`
|
||||
query getHooks {
|
||||
hooks {
|
||||
id
|
||||
name
|
||||
url
|
||||
state
|
||||
}
|
||||
}
|
||||
`,
|
||||
fetchPolicy: 'network-only'
|
||||
})
|
||||
this.config = cloneDeep(resp?.data?.hooks) ?? []
|
||||
this.$q.loading.hide()
|
||||
this.loading--
|
||||
},
|
||||
createHook () {
|
||||
this.$q.dialog({
|
||||
component: WebhookEditDialog,
|
||||
componentProps: {
|
||||
hookId: null
|
||||
}
|
||||
}).onOk(() => {
|
||||
this.load()
|
||||
})
|
||||
},
|
||||
editHook (id) {
|
||||
this.$q.dialog({
|
||||
component: WebhookEditDialog,
|
||||
componentProps: {
|
||||
hookId: id
|
||||
}
|
||||
}).onOk(() => {
|
||||
this.load()
|
||||
})
|
||||
},
|
||||
deleteHook (hook) {
|
||||
this.$q.dialog({
|
||||
component: WebhookDeleteDialog,
|
||||
componentProps: {
|
||||
hook
|
||||
}
|
||||
}).onOk(() => {
|
||||
this.load()
|
||||
})
|
||||
}
|
||||
}
|
||||
`,
|
||||
fetchPolicy: 'network-only'
|
||||
})
|
||||
state.hooks = cloneDeep(resp?.data?.hooks) ?? []
|
||||
$q.loading.hide()
|
||||
state.loading--
|
||||
}
|
||||
|
||||
function createHook () {
|
||||
$q.dialog({
|
||||
component: WebhookEditDialog,
|
||||
componentProps: {
|
||||
hookId: null
|
||||
}
|
||||
}).onOk(() => {
|
||||
load()
|
||||
})
|
||||
}
|
||||
|
||||
function editHook (id) {
|
||||
$q.dialog({
|
||||
component: WebhookEditDialog,
|
||||
componentProps: {
|
||||
hookId: id
|
||||
}
|
||||
}).onOk(() => {
|
||||
load()
|
||||
})
|
||||
}
|
||||
|
||||
function deleteHook (hook) {
|
||||
$q.dialog({
|
||||
component: WebhookDeleteDialog,
|
||||
componentProps: {
|
||||
hook
|
||||
}
|
||||
}).onOk(() => {
|
||||
load()
|
||||
})
|
||||
}
|
||||
|
||||
// MOUNTED
|
||||
|
||||
onMounted(() => {
|
||||
load()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
|
||||
@@ -50,7 +50,7 @@ const routes = [
|
||||
{ path: 'security', component: () => import('../pages/AdminSecurity.vue') },
|
||||
{ path: 'system', component: () => import('../pages/AdminSystem.vue') },
|
||||
// { path: 'utilities', component: () => import('../pages/AdminUtilities.vue') },
|
||||
// { path: 'webhooks', component: () => import('../pages/AdminWebhooks.vue') },
|
||||
{ path: 'webhooks', component: () => import('../pages/AdminWebhooks.vue') },
|
||||
{ path: 'flags', component: () => import('../pages/AdminFlags.vue') }
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user