After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 969 B |
After Width: | Height: | Size: 1005 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 25 KiB |
@ -0,0 +1,63 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
|
||||||
|
q-card(style='min-width: 600px;')
|
||||||
|
q-card-section.card-header
|
||||||
|
q-icon(name='img:/_assets/icons/fluent-key-2.svg', left, size='sm')
|
||||||
|
span {{t(`admin.api.copyKeyTitle`)}}
|
||||||
|
q-card-section.card-negative
|
||||||
|
i18n-t(tag='span', keypath='admin.api.newKeyCopyWarn')
|
||||||
|
template(#bold)
|
||||||
|
strong {{t('admin.api.newKeyCopyWarnBold')}}
|
||||||
|
q-form.q-py-sm
|
||||||
|
q-item
|
||||||
|
blueprint-icon.self-start(icon='binary-file')
|
||||||
|
q-item-section
|
||||||
|
q-input(
|
||||||
|
type='textarea'
|
||||||
|
outlined
|
||||||
|
v-model='props.keyValue'
|
||||||
|
dense
|
||||||
|
hide-bottom-space
|
||||||
|
:label='t(`admin.api.key`)'
|
||||||
|
:aria-label='t(`admin.api.key`)'
|
||||||
|
autofocus
|
||||||
|
)
|
||||||
|
q-card-actions.card-actions
|
||||||
|
q-space
|
||||||
|
q-btn(
|
||||||
|
unelevated
|
||||||
|
:label='t(`common.actions.close`)'
|
||||||
|
color='primary'
|
||||||
|
padding='xs md'
|
||||||
|
@click='onDialogOK'
|
||||||
|
)
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useDialogPluginComponent, useQuasar } from 'quasar'
|
||||||
|
|
||||||
|
// PROPS
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
keyValue: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// EMITS
|
||||||
|
|
||||||
|
defineEmits([
|
||||||
|
...useDialogPluginComponent.emits
|
||||||
|
])
|
||||||
|
|
||||||
|
// QUASAR
|
||||||
|
|
||||||
|
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent()
|
||||||
|
const $q = useQuasar()
|
||||||
|
|
||||||
|
// I18N
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
</script>
|
@ -0,0 +1,253 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
q-dialog(ref='dialogRef', @hide='onDialogHide')
|
||||||
|
q-card(style='min-width: 650px;')
|
||||||
|
q-card-section.card-header
|
||||||
|
q-icon(name='img:/_assets/icons/fluent-plus-plus.svg', left, size='sm')
|
||||||
|
span {{t(`admin.api.newKeyTitle`)}}
|
||||||
|
q-form.q-py-sm(ref='createKeyForm', @submit='create')
|
||||||
|
q-item
|
||||||
|
blueprint-icon.self-start(icon='grand-master-key')
|
||||||
|
q-item-section
|
||||||
|
q-input(
|
||||||
|
outlined
|
||||||
|
v-model='state.keyName'
|
||||||
|
dense
|
||||||
|
:rules='keyNameValidation'
|
||||||
|
hide-bottom-space
|
||||||
|
:label='t(`admin.api.newKeyName`)'
|
||||||
|
:aria-label='t(`admin.api.newKeyName`)'
|
||||||
|
:hint='t(`admin.api.newKeyNameHint`)'
|
||||||
|
lazy-rules='ondemand'
|
||||||
|
autofocus
|
||||||
|
ref='iptName'
|
||||||
|
)
|
||||||
|
q-item
|
||||||
|
blueprint-icon.self-start(icon='schedule')
|
||||||
|
q-item-section
|
||||||
|
q-select(
|
||||||
|
outlined
|
||||||
|
:options='expirations'
|
||||||
|
v-model='state.keyExpiration'
|
||||||
|
multiple
|
||||||
|
map-options
|
||||||
|
option-value='value'
|
||||||
|
option-label='text'
|
||||||
|
emit-value
|
||||||
|
options-dense
|
||||||
|
dense
|
||||||
|
hide-bottom-space
|
||||||
|
:label='t(`admin.api.newKeyExpiration`)'
|
||||||
|
:aria-label='t(`admin.api.newKeyExpiration`)'
|
||||||
|
:hint='t(`admin.api.newKeyExpirationHint`)'
|
||||||
|
)
|
||||||
|
q-item
|
||||||
|
blueprint-icon.self-start(icon='access')
|
||||||
|
q-item-section
|
||||||
|
q-select(
|
||||||
|
outlined
|
||||||
|
:options='state.groups'
|
||||||
|
v-model='state.keyGroups'
|
||||||
|
multiple
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
option-value='id'
|
||||||
|
option-label='name'
|
||||||
|
options-dense
|
||||||
|
dense
|
||||||
|
:rules='keyGroupsValidation'
|
||||||
|
hide-bottom-space
|
||||||
|
:label='t(`admin.api.permissionGroups`)'
|
||||||
|
:aria-label='t(`admin.api.permissionGroups`)'
|
||||||
|
:hint='t(`admin.api.newKeyGroupHint`)'
|
||||||
|
lazy-rules='ondemand'
|
||||||
|
:loading='state.loadingGroups'
|
||||||
|
)
|
||||||
|
template(v-slot:selected)
|
||||||
|
.text-caption(v-if='state.keyGroups.length > 1')
|
||||||
|
i18n-t(keypath='admin.api.groupsSelected')
|
||||||
|
template(#count)
|
||||||
|
strong {{ state.keyGroups.length }}
|
||||||
|
.text-caption(v-else-if='state.keyGroups.length === 1')
|
||||||
|
i18n-t(keypath='admin.api.groupSelected')
|
||||||
|
template(#group)
|
||||||
|
strong {{ selectedGroupName }}
|
||||||
|
span(v-else)
|
||||||
|
template(v-slot:option='{ itemProps, opt, selected, toggleOption }')
|
||||||
|
q-item(
|
||||||
|
v-bind='itemProps'
|
||||||
|
)
|
||||||
|
q-item-section(side)
|
||||||
|
q-checkbox(
|
||||||
|
size='sm'
|
||||||
|
:model-value='selected'
|
||||||
|
@update:model-value='toggleOption(opt)'
|
||||||
|
)
|
||||||
|
q-item-section
|
||||||
|
q-item-label {{opt.name}}
|
||||||
|
q-card-actions.card-actions
|
||||||
|
q-space
|
||||||
|
q-btn.acrylic-btn(
|
||||||
|
flat
|
||||||
|
:label='t(`common.actions.cancel`)'
|
||||||
|
color='grey'
|
||||||
|
padding='xs md'
|
||||||
|
@click='onDialogCancel'
|
||||||
|
)
|
||||||
|
q-btn(
|
||||||
|
unelevated
|
||||||
|
:label='t(`common.actions.create`)'
|
||||||
|
color='primary'
|
||||||
|
padding='xs md'
|
||||||
|
@click='create'
|
||||||
|
:loading='state.loading > 0'
|
||||||
|
)
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import { cloneDeep, sampleSize } from 'lodash-es'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useDialogPluginComponent, useQuasar } from 'quasar'
|
||||||
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
|
|
||||||
|
import ApiKeyCopyDialog from './ApiKeyCopyDialog.vue'
|
||||||
|
|
||||||
|
// EMITS
|
||||||
|
|
||||||
|
defineEmits([
|
||||||
|
...useDialogPluginComponent.emits
|
||||||
|
])
|
||||||
|
|
||||||
|
// QUASAR
|
||||||
|
|
||||||
|
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
|
||||||
|
const $q = useQuasar()
|
||||||
|
|
||||||
|
// I18N
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
// DATA
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
keyName: '',
|
||||||
|
keyExpiration: '90d',
|
||||||
|
keyGroups: [],
|
||||||
|
groups: [],
|
||||||
|
loadingGroups: false,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const expirations = [
|
||||||
|
{ value: '30d', text: t('admin.api.expiration30d') },
|
||||||
|
{ value: '90d', text: t('admin.api.expiration90d') },
|
||||||
|
{ value: '180d', text: t('admin.api.expiration180d') },
|
||||||
|
{ value: '1y', text: t('admin.api.expiration1y') },
|
||||||
|
{ value: '3y', text: t('admin.api.expiration3y') }
|
||||||
|
]
|
||||||
|
|
||||||
|
// REFS
|
||||||
|
|
||||||
|
const createKeyForm = ref(null)
|
||||||
|
const iptName = ref(null)
|
||||||
|
|
||||||
|
// COMPUTED
|
||||||
|
|
||||||
|
const selectedGroupName = computed(() => {
|
||||||
|
return state.groups.filter(g => g.id === state.keyGroups[0])[0]?.name
|
||||||
|
})
|
||||||
|
|
||||||
|
// VALIDATION RULES
|
||||||
|
|
||||||
|
const keyNameValidation = [
|
||||||
|
val => val.length > 0 || t('admin.api.nameMissing'),
|
||||||
|
val => /^[^<>"]+$/.test(val) || t('admin.api.nameInvalidChars')
|
||||||
|
]
|
||||||
|
|
||||||
|
const keyGroupsValidation = [
|
||||||
|
val => val.length > 0 || t('admin.api.groupsMissing')
|
||||||
|
]
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
|
async function loadGroups () {
|
||||||
|
state.loading++
|
||||||
|
state.loadingGroups = true
|
||||||
|
const resp = await APOLLO_CLIENT.query({
|
||||||
|
query: gql`
|
||||||
|
query getGroupsForCreateApiKey {
|
||||||
|
groups {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fetchPolicy: 'network-only'
|
||||||
|
})
|
||||||
|
state.groups = cloneDeep(resp?.data?.groups?.filter(g => g.id !== '10000000-0000-4000-8000-000000000001') ?? [])
|
||||||
|
state.loadingGroups = false
|
||||||
|
state.loading--
|
||||||
|
}
|
||||||
|
|
||||||
|
async function create () {
|
||||||
|
state.loading++
|
||||||
|
try {
|
||||||
|
const isFormValid = await createKeyForm.value.validate(true)
|
||||||
|
if (!isFormValid) {
|
||||||
|
throw new Error(t('admin.api.createInvalidData'))
|
||||||
|
}
|
||||||
|
const resp = await APOLLO_CLIENT.mutate({
|
||||||
|
mutation: gql`
|
||||||
|
mutation createApiKey (
|
||||||
|
$name: String!
|
||||||
|
$expiration: String!
|
||||||
|
$groups: [UUID]!
|
||||||
|
) {
|
||||||
|
createApiKey (
|
||||||
|
name: $name
|
||||||
|
expiration: $expiration
|
||||||
|
groups: $groups
|
||||||
|
) {
|
||||||
|
operation {
|
||||||
|
succeeded
|
||||||
|
message
|
||||||
|
}
|
||||||
|
key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
name: state.keyName,
|
||||||
|
expiration: state.keyExpiration,
|
||||||
|
groups: state.keyGroups
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (resp?.data?.createApiKey?.operation?.succeeded) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'positive',
|
||||||
|
message: t('admin.api.createSuccess')
|
||||||
|
})
|
||||||
|
$q.dialog({
|
||||||
|
component: ApiKeyCopyDialog,
|
||||||
|
componentProps: {
|
||||||
|
keyValue: resp?.data?.createApiKey?.key || 'ERROR'
|
||||||
|
}
|
||||||
|
}).onDismiss(() => {
|
||||||
|
onDialogOK()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error(resp?.data?.createApiKey?.operation?.message || 'An unexpected error occured.')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: err.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
state.loading--
|
||||||
|
}
|
||||||
|
|
||||||
|
// MOUNTED
|
||||||
|
|
||||||
|
onMounted(loadGroups)
|
||||||
|
</script>
|
@ -0,0 +1,104 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
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-unavailable.svg', left, size='sm')
|
||||||
|
span {{t(`admin.api.revokeConfirm`)}}
|
||||||
|
q-card-section
|
||||||
|
.text-body2
|
||||||
|
i18n-t(keypath='admin.api.revokeConfirmText')
|
||||||
|
template(#name)
|
||||||
|
strong {{apiKey.name}}
|
||||||
|
q-card-actions.card-actions
|
||||||
|
q-space
|
||||||
|
q-btn.acrylic-btn(
|
||||||
|
flat
|
||||||
|
:label='t(`common.actions.cancel`)'
|
||||||
|
color='grey'
|
||||||
|
padding='xs md'
|
||||||
|
@click='onDialogCancel'
|
||||||
|
)
|
||||||
|
q-btn(
|
||||||
|
unelevated
|
||||||
|
:label='t(`admin.api.revoke`)'
|
||||||
|
color='negative'
|
||||||
|
padding='xs md'
|
||||||
|
@click='confirm'
|
||||||
|
:loading='state.isLoading'
|
||||||
|
)
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useDialogPluginComponent, useQuasar } from 'quasar'
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
|
// PROPS
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
apiKey: {
|
||||||
|
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 revokeApiKey ($id: UUID!) {
|
||||||
|
revokeApiKey (id: $id) {
|
||||||
|
operation {
|
||||||
|
succeeded
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
id: props.apiKey.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (resp?.data?.revokeApiKey?.operation?.succeeded) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'positive',
|
||||||
|
message: t('admin.api.revokeSuccess')
|
||||||
|
})
|
||||||
|
onDialogOK()
|
||||||
|
} else {
|
||||||
|
throw new Error(resp?.data?.revokeApiKey?.operation?.message || 'An unexpected error occured.')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: err.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
state.isLoading = false
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,101 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
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-downloading-updates.svg', left, size='sm')
|
||||||
|
span {{t(`admin.system.checkingForUpdates`)}}
|
||||||
|
q-card-section
|
||||||
|
.q-pa-md.text-center
|
||||||
|
img(src='/_assets/illustrations/undraw_going_up.svg', style='width: 150px;')
|
||||||
|
q-linear-progress(
|
||||||
|
indeterminate
|
||||||
|
size='lg'
|
||||||
|
rounded
|
||||||
|
)
|
||||||
|
.q-mt-sm.text-center.text-caption Fetching latest version info...
|
||||||
|
q-card-actions.card-actions
|
||||||
|
q-space
|
||||||
|
q-btn.acrylic-btn(
|
||||||
|
flat
|
||||||
|
:label='t(`common.actions.cancel`)'
|
||||||
|
color='grey'
|
||||||
|
padding='xs md'
|
||||||
|
@click='onDialogCancel'
|
||||||
|
)
|
||||||
|
q-btn(
|
||||||
|
v-if='state.canUpgrade'
|
||||||
|
unelevated
|
||||||
|
:label='t(`admin.system.upgrade`)'
|
||||||
|
color='primary'
|
||||||
|
padding='xs md'
|
||||||
|
@click='upgrade'
|
||||||
|
:loading='state.isLoading'
|
||||||
|
)
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useDialogPluginComponent, useQuasar } from 'quasar'
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
|
// EMITS
|
||||||
|
|
||||||
|
defineEmits([
|
||||||
|
...useDialogPluginComponent.emits
|
||||||
|
])
|
||||||
|
|
||||||
|
// QUASAR
|
||||||
|
|
||||||
|
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
|
||||||
|
const $q = useQuasar()
|
||||||
|
|
||||||
|
// I18N
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
// DATA
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
isLoading: false,
|
||||||
|
canUpgrade: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
|
async function upgrade () {
|
||||||
|
state.isLoading = true
|
||||||
|
try {
|
||||||
|
const resp = await APOLLO_CLIENT.mutate({
|
||||||
|
mutation: gql`
|
||||||
|
mutation deleteHook ($id: UUID!) {
|
||||||
|
deleteHook(id: $id) {
|
||||||
|
operation {
|
||||||
|
succeeded
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
id: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
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,236 +0,0 @@
|
|||||||
<template lang="pug">
|
|
||||||
div
|
|
||||||
v-dialog(v-model='isShown', max-width='650', persistent)
|
|
||||||
v-card
|
|
||||||
.dialog-header.is-short
|
|
||||||
v-icon.mr-3(color='white') mdi-plus
|
|
||||||
span {{$t('admin.api.newKeyTitle')}}
|
|
||||||
v-card-text.pt-5
|
|
||||||
v-text-field(
|
|
||||||
outlined
|
|
||||||
prepend-icon='mdi-format-title'
|
|
||||||
v-model='name'
|
|
||||||
:label='$t(`admin.api.newKeyName`)'
|
|
||||||
persistent-hint
|
|
||||||
ref='keyNameInput'
|
|
||||||
:hint='$t(`admin.api.newKeyNameHint`)'
|
|
||||||
counter='255'
|
|
||||||
)
|
|
||||||
v-select.mt-3(
|
|
||||||
:items='expirations'
|
|
||||||
outlined
|
|
||||||
prepend-icon='mdi-clock'
|
|
||||||
v-model='expiration'
|
|
||||||
:label='$t(`admin.api.newKeyExpiration`)'
|
|
||||||
:hint='$t(`admin.api.newKeyExpirationHint`)'
|
|
||||||
persistent-hint
|
|
||||||
)
|
|
||||||
v-divider.mt-4
|
|
||||||
v-subheader.pl-2: strong.indigo--text {{$t('admin.api.newKeyPermissionScopes')}}
|
|
||||||
v-list.pl-8(nav)
|
|
||||||
v-list-item-group(v-model='fullAccess')
|
|
||||||
v-list-item(
|
|
||||||
:value='true'
|
|
||||||
active-class='indigo--text'
|
|
||||||
)
|
|
||||||
template(v-slot:default='{ active, toggle }')
|
|
||||||
v-list-item-action
|
|
||||||
v-checkbox(
|
|
||||||
:input-value='active'
|
|
||||||
:true-value='true'
|
|
||||||
color='indigo'
|
|
||||||
@click='toggle'
|
|
||||||
)
|
|
||||||
v-list-item-content
|
|
||||||
v-list-item-title {{$t('admin.api.newKeyFullAccess')}}
|
|
||||||
v-divider.mt-3
|
|
||||||
v-subheader.caption.indigo--text {{$t('admin.api.newKeyGroupPermissions')}}
|
|
||||||
v-list-item
|
|
||||||
v-select(
|
|
||||||
:disabled='fullAccess'
|
|
||||||
:items='groups'
|
|
||||||
item-text='name'
|
|
||||||
item-value='id'
|
|
||||||
outlined
|
|
||||||
color='indigo'
|
|
||||||
v-model='group'
|
|
||||||
:label='$t(`admin.api.newKeyGroup`)'
|
|
||||||
:hint='$t(`admin.api.newKeyGroupHint`)'
|
|
||||||
persistent-hint
|
|
||||||
)
|
|
||||||
v-card-chin
|
|
||||||
v-spacer
|
|
||||||
v-btn(text, @click='isShown = false', :disabled='loading') {{$t('common.actions.cancel')}}
|
|
||||||
v-btn.px-3(depressed, color='primary', @click='generate', :loading='loading')
|
|
||||||
v-icon(left) mdi-chevron-right
|
|
||||||
span {{$t('common.actions.generate')}}
|
|
||||||
|
|
||||||
v-dialog(
|
|
||||||
v-model='isCopyKeyDialogShown'
|
|
||||||
max-width='750'
|
|
||||||
persistent
|
|
||||||
overlay-color='blue darken-5'
|
|
||||||
overlay-opacity='.9'
|
|
||||||
)
|
|
||||||
v-card
|
|
||||||
v-toolbar(dense, flat, color='primary', dark) {{$t('admin.api.newKeyTitle')}}
|
|
||||||
v-card-text.pt-5
|
|
||||||
.body-2.text-center
|
|
||||||
i18next(tag='span', path='admin.api.newKeyCopyWarn')
|
|
||||||
strong(place='bold') {{$t('admin.api.newKeyCopyWarnBold')}}
|
|
||||||
v-textarea.mt-3(
|
|
||||||
ref='keyContentsIpt'
|
|
||||||
filled
|
|
||||||
no-resize
|
|
||||||
readonly
|
|
||||||
v-model='key'
|
|
||||||
:rows='10'
|
|
||||||
hide-details
|
|
||||||
)
|
|
||||||
v-card-chin
|
|
||||||
v-spacer
|
|
||||||
v-btn.px-3(depressed, dark, color='primary', @click='isCopyKeyDialogShown = false') {{$t('common.actions.close')}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import _ from 'lodash'
|
|
||||||
import gql from 'graphql-tag'
|
|
||||||
|
|
||||||
import groupsQuery from 'gql/admin/users/users-query-groups.gql'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
name: '',
|
|
||||||
expiration: '1y',
|
|
||||||
fullAccess: true,
|
|
||||||
groups: [],
|
|
||||||
group: null,
|
|
||||||
isCopyKeyDialogShown: false,
|
|
||||||
key: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
isShown: {
|
|
||||||
get() { return this.value },
|
|
||||||
set(val) { this.$emit('input', val) }
|
|
||||||
},
|
|
||||||
expirations() {
|
|
||||||
return [
|
|
||||||
{ value: '30d', text: this.$t('admin.api.expiration30d') },
|
|
||||||
{ value: '90d', text: this.$t('admin.api.expiration90d') },
|
|
||||||
{ value: '180d', text: this.$t('admin.api.expiration180d') },
|
|
||||||
{ value: '1y', text: this.$t('admin.api.expiration1y') },
|
|
||||||
{ value: '3y', text: this.$t('admin.api.expiration3y') }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
value (newValue, oldValue) {
|
|
||||||
if (newValue) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$refs.keyNameInput.focus()
|
|
||||||
}, 400)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async generate () {
|
|
||||||
try {
|
|
||||||
if (_.trim(this.name).length < 2 || this.name.length > 255) {
|
|
||||||
throw new Error(this.$t('admin.api.newKeyNameError'))
|
|
||||||
} else if (!this.fullAccess && !this.group) {
|
|
||||||
throw new Error(this.$t('admin.api.newKeyGroupError'))
|
|
||||||
} else if (!this.fullAccess && this.group === 2) {
|
|
||||||
throw new Error(this.$t('admin.api.newKeyGuestGroupError'))
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return this.$store.commit('showNotification', {
|
|
||||||
style: 'red',
|
|
||||||
message: err,
|
|
||||||
icon: 'alert'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
const resp = await this.$apollo.mutate({
|
|
||||||
mutation: gql`
|
|
||||||
mutation ($name: String!, $expiration: String!, $fullAccess: Boolean!, $group: Int) {
|
|
||||||
authentication {
|
|
||||||
createApiKey (name: $name, expiration: $expiration, fullAccess: $fullAccess, group: $group) {
|
|
||||||
key
|
|
||||||
responseResult {
|
|
||||||
succeeded
|
|
||||||
errorCode
|
|
||||||
slug
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
name: this.name,
|
|
||||||
expiration: this.expiration,
|
|
||||||
fullAccess: (this.fullAccess === true),
|
|
||||||
group: this.group
|
|
||||||
},
|
|
||||||
watchLoading (isLoading) {
|
|
||||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-create')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (_.get(resp, 'data.authentication.createApiKey.responseResult.succeeded', false)) {
|
|
||||||
this.$store.commit('showNotification', {
|
|
||||||
style: 'success',
|
|
||||||
message: this.$t('admin.api.newKeySuccess'),
|
|
||||||
icon: 'check'
|
|
||||||
})
|
|
||||||
|
|
||||||
this.name = ''
|
|
||||||
this.expiration = '1y'
|
|
||||||
this.fullAccess = true
|
|
||||||
this.group = null
|
|
||||||
this.isShown = false
|
|
||||||
this.$emit('refresh')
|
|
||||||
|
|
||||||
this.key = _.get(resp, 'data.authentication.createApiKey.key', '???')
|
|
||||||
this.isCopyKeyDialogShown = true
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$refs.keyContentsIpt.$refs.input.select()
|
|
||||||
}, 400)
|
|
||||||
} else {
|
|
||||||
this.$store.commit('showNotification', {
|
|
||||||
style: 'red',
|
|
||||||
message: _.get(resp, 'data.authentication.createApiKey.responseResult.message', 'An unexpected error occurred.'),
|
|
||||||
icon: 'alert'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
this.$store.commit('pushGraphError', err)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
apollo: {
|
|
||||||
groups: {
|
|
||||||
query: groupsQuery,
|
|
||||||
fetchPolicy: 'network-only',
|
|
||||||
update: (data) => data.groups.list,
|
|
||||||
watchLoading (isLoading) {
|
|
||||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-api-groups-refresh')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|