feat: jwt renew via graphql + users create/authorize UI
This commit is contained in:
@@ -7,10 +7,11 @@ import VueSimpleBreakpoints from 'vue-simple-breakpoints'
|
|||||||
import VeeValidate from 'vee-validate'
|
import VeeValidate from 'vee-validate'
|
||||||
import { ApolloClient } from 'apollo-client'
|
import { ApolloClient } from 'apollo-client'
|
||||||
import { createPersistedQueryLink } from 'apollo-link-persisted-queries'
|
import { createPersistedQueryLink } from 'apollo-link-persisted-queries'
|
||||||
// import { BatchHttpLink } from 'apollo-link-batch-http'
|
import { BatchHttpLink } from 'apollo-link-batch-http'
|
||||||
import { split } from 'apollo-link'
|
import { ApolloLink, split } from 'apollo-link'
|
||||||
import { createHttpLink } from 'apollo-link-http'
|
// import { createHttpLink } from 'apollo-link-http'
|
||||||
import { WebSocketLink } from 'apollo-link-ws'
|
import { WebSocketLink } from 'apollo-link-ws'
|
||||||
|
import { ErrorLink } from 'apollo-link-error'
|
||||||
import { InMemoryCache } from 'apollo-cache-inmemory'
|
import { InMemoryCache } from 'apollo-cache-inmemory'
|
||||||
import { getMainDefinition } from 'apollo-utilities'
|
import { getMainDefinition } from 'apollo-utilities'
|
||||||
import VueApollo from 'vue-apollo'
|
import VueApollo from 'vue-apollo'
|
||||||
@@ -54,24 +55,47 @@ moment.locale(siteConfig.lang)
|
|||||||
const graphQLEndpoint = window.location.protocol + '//' + window.location.host + '/graphql'
|
const graphQLEndpoint = window.location.protocol + '//' + window.location.host + '/graphql'
|
||||||
const graphQLWSEndpoint = ((window.location.protocol === 'https:') ? 'wss:' : 'ws:') + '//' + window.location.host + '/graphql-subscriptions'
|
const graphQLWSEndpoint = ((window.location.protocol === 'https:') ? 'wss:' : 'ws:') + '//' + window.location.host + '/graphql-subscriptions'
|
||||||
|
|
||||||
const graphQLLink = createPersistedQueryLink().concat(
|
const graphQLLink = ApolloLink.from([
|
||||||
createHttpLink({
|
new ErrorLink(({ graphQLErrors, networkError }) => {
|
||||||
|
if (graphQLErrors) {
|
||||||
|
graphQLErrors.map(({ message, locations, path }) =>
|
||||||
|
console.error(
|
||||||
|
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
store.commit('showNotification', {
|
||||||
|
style: 'red',
|
||||||
|
message: `An expected error occured.`,
|
||||||
|
icon: 'warning'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (networkError) {
|
||||||
|
console.error(networkError)
|
||||||
|
store.commit('showNotification', {
|
||||||
|
style: 'red',
|
||||||
|
message: `Network Error: ${networkError.message}`,
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
createPersistedQueryLink(),
|
||||||
|
new BatchHttpLink({
|
||||||
includeExtensions: true,
|
includeExtensions: true,
|
||||||
uri: graphQLEndpoint,
|
uri: graphQLEndpoint,
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
fetch: (uri, options) => {
|
fetch: async (uri, options) => {
|
||||||
// Strip __typename fields from variables
|
// Strip __typename fields from variables
|
||||||
let body = JSON.parse(options.body)
|
let body = JSON.parse(options.body)
|
||||||
// body = body.map(bd => {
|
body = body.map(bd => {
|
||||||
// return ({
|
return ({
|
||||||
// ...bd,
|
...bd,
|
||||||
// variables: JSON.parse(JSON.stringify(bd.variables), (key, value) => { return key === '__typename' ? undefined : value })
|
variables: JSON.parse(JSON.stringify(bd.variables), (key, value) => { return key === '__typename' ? undefined : value })
|
||||||
// })
|
})
|
||||||
// })
|
})
|
||||||
body = {
|
// body = {
|
||||||
...body,
|
// ...body,
|
||||||
variables: JSON.parse(JSON.stringify(body.variables), (key, value) => { return key === '__typename' ? undefined : value })
|
// variables: JSON.parse(JSON.stringify(body.variables), (key, value) => { return key === '__typename' ? undefined : value })
|
||||||
}
|
// }
|
||||||
options.body = JSON.stringify(body)
|
options.body = JSON.stringify(body)
|
||||||
|
|
||||||
// Inject authentication token
|
// Inject authentication token
|
||||||
@@ -80,10 +104,17 @@ const graphQLLink = createPersistedQueryLink().concat(
|
|||||||
options.headers.Authorization = `Bearer ${jwtToken}`
|
options.headers.Authorization = `Bearer ${jwtToken}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(uri, options)
|
const resp = await fetch(uri, options)
|
||||||
|
|
||||||
|
// Handle renewed JWT
|
||||||
|
const newJWT = resp.headers.get('new-jwt')
|
||||||
|
if (newJWT) {
|
||||||
|
Cookies.set('jwt', newJWT, { expires: 365 })
|
||||||
|
}
|
||||||
|
return resp
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
])
|
||||||
|
|
||||||
const graphQLWSLink = new WebSocketLink({
|
const graphQLWSLink = new WebSocketLink({
|
||||||
uri: graphQLWSEndpoint,
|
uri: graphQLWSEndpoint,
|
||||||
|
|||||||
@@ -7,109 +7,121 @@
|
|||||||
.admin-header-title
|
.admin-header-title
|
||||||
.headline.blue--text.text--darken-2 Edit Group
|
.headline.blue--text.text--darken-2 Edit Group
|
||||||
.subheading.grey--text {{name}}
|
.subheading.grey--text {{name}}
|
||||||
v-btn(color='primary', fab, absolute, bottom, right, small, to='/groups'): v-icon arrow_upward
|
v-spacer
|
||||||
v-tabs(v-model='tab', :color='$vuetify.dark ? "primary" : "grey lighten-4"', fixed-tabs, :slider-color='$vuetify.dark ? "white" : "primary"', show-arrows)
|
v-btn(color='indigo', large, outline, to='/groups')
|
||||||
v-tab(key='properties') Properties
|
v-icon arrow_back
|
||||||
v-tab(key='rights') Permissions
|
v-dialog(v-model='deleteGroupDialog', max-width='500')
|
||||||
v-tab(key='users') Users
|
v-btn(color='red', large, outline, slot='activator')
|
||||||
|
v-icon(color='red') delete
|
||||||
|
v-card
|
||||||
|
.dialog-header.is-red Delete Group?
|
||||||
|
v-card-text Are you sure you want to delete group #[strong {{ name }}]? All users will be unassigned from this group.
|
||||||
|
v-card-actions
|
||||||
|
v-spacer
|
||||||
|
v-btn(flat, @click='deleteGroupDialog = false') Cancel
|
||||||
|
v-btn(color='red', dark, @click='deleteGroup') Delete
|
||||||
|
v-btn(color='primary', large, depressed, @click='updateGroup')
|
||||||
|
v-icon(left) check
|
||||||
|
span Update Group
|
||||||
|
v-card.mt-3
|
||||||
|
v-tabs(v-model='tab', :color='$vuetify.dark ? "primary" : "grey lighten-4"', fixed-tabs, :slider-color='$vuetify.dark ? "white" : "primary"', show-arrows)
|
||||||
|
v-tab(key='properties') Properties
|
||||||
|
v-tab(key='permissions') Permissions
|
||||||
|
v-tab(key='rules') Page Rules
|
||||||
|
v-tab(key='users') Users
|
||||||
|
|
||||||
v-tab-item(key='properties', :transition='false', :reverse-transition='false')
|
v-tab-item(key='properties', :transition='false', :reverse-transition='false')
|
||||||
v-card
|
|
||||||
v-card-text
|
|
||||||
v-text-field(v-model='name', label='Group Name', counter='255', prepend-icon='people')
|
|
||||||
v-card-actions.pa-3
|
|
||||||
v-btn(color='primary', @click='updateGroup')
|
|
||||||
v-icon(left) check
|
|
||||||
| Save Changes
|
|
||||||
.caption.ml-4.grey--text ID: {{group.id}}
|
|
||||||
v-spacer
|
|
||||||
v-dialog(v-model='deleteGroupDialog', max-width='500')
|
|
||||||
v-btn(color='red', flat, @click='', slot='activator')
|
|
||||||
v-icon(left) delete
|
|
||||||
| Delete Group
|
|
||||||
v-card
|
v-card
|
||||||
.dialog-header.is-red Delete Group?
|
v-card-text
|
||||||
v-card-text Are you sure you want to delete group #[strong {{ name }}]? All users will be unassigned from this group.
|
v-text-field(
|
||||||
v-card-actions
|
outline
|
||||||
|
background-color='grey lighten-3'
|
||||||
|
v-model='name'
|
||||||
|
label='Group Name'
|
||||||
|
counter='255'
|
||||||
|
prepend-icon='people'
|
||||||
|
)
|
||||||
|
v-divider
|
||||||
|
.caption.mt-3.grey--text ID: {{group.id}}
|
||||||
|
|
||||||
|
v-tab-item(key='permissions', :transition='false', :reverse-transition='false')
|
||||||
|
v-card
|
||||||
|
|
||||||
|
v-tab-item(key='rules', :transition='false', :reverse-transition='false')
|
||||||
|
v-card
|
||||||
|
v-card-title.pb-0
|
||||||
|
v-subheader
|
||||||
|
v-icon.mr-2 border_color
|
||||||
|
.subheading Read and Write
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn(flat, @click='deleteGroupDialog = false') Cancel
|
v-btn(flat, outline)
|
||||||
v-btn(color='red', dark, @click='deleteGroup') Delete
|
v-icon(left) arrow_drop_down
|
||||||
|
| Load Preset
|
||||||
|
v-btn(flat, outline)
|
||||||
|
v-icon(left) vertical_align_bottom
|
||||||
|
| Import Rules
|
||||||
|
.pa-3.pl-4
|
||||||
|
criterias
|
||||||
|
v-divider.my-0
|
||||||
|
v-card-title.pb-0
|
||||||
|
v-subheader
|
||||||
|
v-icon.mr-2 pageview
|
||||||
|
.subheading Read Only
|
||||||
|
v-spacer
|
||||||
|
v-btn(flat, outline)
|
||||||
|
v-icon(left) arrow_drop_down
|
||||||
|
| Load Preset
|
||||||
|
v-btn(flat, outline)
|
||||||
|
v-icon(left) vertical_align_bottom
|
||||||
|
| Import Rules
|
||||||
|
.pa-3.pl-4
|
||||||
|
criterias
|
||||||
|
v-divider.my-0
|
||||||
|
v-card-title.pb-0
|
||||||
|
v-subheader Legend
|
||||||
|
.px-4.pb-4
|
||||||
|
.body-1.px-1.py-2 Any number of rules can be used at the same time. However, some rules requires more processing time than others. Rule types are color-coded as followed:
|
||||||
|
.caption
|
||||||
|
v-icon(color='blue') stop
|
||||||
|
span Fast rules. None or insignificant latency introduced to all page loads.
|
||||||
|
.caption
|
||||||
|
v-icon(color='orange') stop
|
||||||
|
span Medium rules. Some latency added to all page loads.
|
||||||
|
.caption
|
||||||
|
v-icon(color='red') stop
|
||||||
|
span Slow rules. May adds noticeable latency to all page loads. Avoid using in multiple rules.
|
||||||
|
|
||||||
v-tab-item(key='rights', :transition='false', :reverse-transition='false')
|
v-tab-item(key='users', :transition='false', :reverse-transition='false')
|
||||||
v-card
|
v-card
|
||||||
v-card-title.pb-0
|
v-card-title.pb-0
|
||||||
v-subheader
|
v-btn(color='primary', @click='searchUserDialog = true')
|
||||||
v-icon.mr-2 border_color
|
v-icon(left) assignment_ind
|
||||||
.subheading Read and Write
|
| Assign User
|
||||||
v-spacer
|
v-data-table(
|
||||||
v-btn(flat, outline)
|
:items='group.users',
|
||||||
v-icon(left) arrow_drop_down
|
:headers='headers',
|
||||||
| Load Preset
|
:search='search',
|
||||||
v-btn(flat, outline)
|
:pagination.sync='pagination',
|
||||||
v-icon(left) vertical_align_bottom
|
:rows-per-page-items='[15]'
|
||||||
| Import Rules
|
hide-actions
|
||||||
.pa-3.pl-4
|
)
|
||||||
criterias
|
template(slot='items', slot-scope='props')
|
||||||
v-divider.my-0
|
tr(:active='props.selected')
|
||||||
v-card-title.pb-0
|
td.text-xs-right {{ props.item.id }}
|
||||||
v-subheader
|
td {{ props.item.name }}
|
||||||
v-icon.mr-2 pageview
|
td {{ props.item.email }}
|
||||||
.subheading Read Only
|
td
|
||||||
v-spacer
|
v-menu(bottom, right, min-width='200')
|
||||||
v-btn(flat, outline)
|
v-btn(icon, slot='activator'): v-icon.grey--text.text--darken-1 more_horiz
|
||||||
v-icon(left) arrow_drop_down
|
v-list
|
||||||
| Load Preset
|
v-list-tile(@click='unassignUser(props.item.id)')
|
||||||
v-btn(flat, outline)
|
v-list-tile-action: v-icon(color='orange') highlight_off
|
||||||
v-icon(left) vertical_align_bottom
|
v-list-tile-content
|
||||||
| Import Rules
|
v-list-tile-title Unassign
|
||||||
.pa-3.pl-4
|
template(slot='no-data')
|
||||||
criterias
|
v-alert.ma-3(icon='warning', :value='true', outline) No users to display.
|
||||||
v-divider.my-0
|
.text-xs-center.py-2(v-if='users.length > 15')
|
||||||
v-card-title.pb-0
|
v-pagination(v-model='pagination.page', :length='pages')
|
||||||
v-subheader Legend
|
|
||||||
.px-4.pb-4
|
|
||||||
.body-1.px-1.py-2 Any number of rules can be used at the same time. However, some rules requires more processing time than others. Rule types are color-coded as followed:
|
|
||||||
.caption
|
|
||||||
v-icon(color='blue') stop
|
|
||||||
span Fast rules. None or insignificant latency introduced to all page loads.
|
|
||||||
.caption
|
|
||||||
v-icon(color='orange') stop
|
|
||||||
span Medium rules. Some latency added to all page loads.
|
|
||||||
.caption
|
|
||||||
v-icon(color='red') stop
|
|
||||||
span Slow rules. May adds noticeable latency to all page loads. Avoid using in multiple rules.
|
|
||||||
|
|
||||||
v-tab-item(key='users', :transition='false', :reverse-transition='false')
|
|
||||||
v-card
|
|
||||||
v-card-title.pb-0
|
|
||||||
v-btn(color='primary', @click='searchUserDialog = true')
|
|
||||||
v-icon(left) assignment_ind
|
|
||||||
| Assign User
|
|
||||||
v-data-table(
|
|
||||||
:items='group.users',
|
|
||||||
:headers='headers',
|
|
||||||
:search='search',
|
|
||||||
:pagination.sync='pagination',
|
|
||||||
:rows-per-page-items='[15]'
|
|
||||||
hide-actions
|
|
||||||
)
|
|
||||||
template(slot='items', slot-scope='props')
|
|
||||||
tr(:active='props.selected')
|
|
||||||
td.text-xs-right {{ props.item.id }}
|
|
||||||
td {{ props.item.name }}
|
|
||||||
td {{ props.item.email }}
|
|
||||||
td
|
|
||||||
v-menu(bottom, right, min-width='200')
|
|
||||||
v-btn(icon, slot='activator'): v-icon.grey--text.text--darken-1 more_horiz
|
|
||||||
v-list
|
|
||||||
v-list-tile(@click='unassignUser(props.item.id)')
|
|
||||||
v-list-tile-action: v-icon(color='orange') highlight_off
|
|
||||||
v-list-tile-content
|
|
||||||
v-list-tile-title Unassign
|
|
||||||
template(slot='no-data')
|
|
||||||
v-alert.ma-3(icon='warning', :value='true', outline) No users to display.
|
|
||||||
.text-xs-center.py-2(v-if='users.length > 15')
|
|
||||||
v-pagination(v-model='pagination.page', :length='pages')
|
|
||||||
|
|
||||||
user-search(v-model='searchUserDialog', @select='assignUser')
|
user-search(v-model='searchUserDialog', @select='assignUser')
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
115
client/components/admin/admin-users-authorize.vue
Normal file
115
client/components/admin/admin-users-authorize.vue
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
v-dialog(v-model='isShown', max-width='550')
|
||||||
|
v-card
|
||||||
|
.dialog-header.is-short Authorize Social User
|
||||||
|
v-card-text
|
||||||
|
v-select.md2(
|
||||||
|
:items='providers'
|
||||||
|
item-text='title'
|
||||||
|
item-value='key'
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='business'
|
||||||
|
v-model='provider'
|
||||||
|
label='Provider'
|
||||||
|
)
|
||||||
|
v-text-field.md2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='email'
|
||||||
|
v-model='email'
|
||||||
|
label='Email Address'
|
||||||
|
ref='emailInput'
|
||||||
|
)
|
||||||
|
v-text-field.md2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='person'
|
||||||
|
v-model='name'
|
||||||
|
label='Name'
|
||||||
|
)
|
||||||
|
v-text-field.md2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='title'
|
||||||
|
v-model='jobTitle'
|
||||||
|
label='Job Title'
|
||||||
|
counter='255'
|
||||||
|
hint='Optional'
|
||||||
|
persistent-hint
|
||||||
|
)
|
||||||
|
v-text-field.md2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='public'
|
||||||
|
v-model='location'
|
||||||
|
label='Location'
|
||||||
|
counter='255'
|
||||||
|
hint='Optional'
|
||||||
|
persistent-hint
|
||||||
|
)
|
||||||
|
v-card-chin
|
||||||
|
v-spacer
|
||||||
|
v-btn(flat, @click='isShown = false') Cancel
|
||||||
|
v-btn(color='primary', @click='authorizeUser') Authorize
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import providersQuery from 'gql/admin/users/users-query-strategies.gql'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
providers: [],
|
||||||
|
provider: '',
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
jobTitle: '',
|
||||||
|
location: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isShown: {
|
||||||
|
get() { return this.value },
|
||||||
|
set(val) { this.$emit('input', val) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(newValue, oldValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.emailInput.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async authorizeUser() {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
apollo: {
|
||||||
|
providers: {
|
||||||
|
query: providersQuery,
|
||||||
|
fetchPolicy: 'network-only',
|
||||||
|
update: (data) => _.reject(data.authentication.strategies, ['key', 'local']),
|
||||||
|
watchLoading (isLoading) {
|
||||||
|
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-users-strategies-refresh')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
105
client/components/admin/admin-users-create.vue
Normal file
105
client/components/admin/admin-users-create.vue
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<template lang="pug">
|
||||||
|
v-dialog(v-model='isShown', max-width='550')
|
||||||
|
v-card
|
||||||
|
.dialog-header.is-short New Local User
|
||||||
|
v-card-text
|
||||||
|
v-text-field.md2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='email'
|
||||||
|
v-model='email'
|
||||||
|
label='Email Address'
|
||||||
|
ref='emailInput'
|
||||||
|
)
|
||||||
|
v-text-field.md2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='person'
|
||||||
|
v-model='name'
|
||||||
|
label='Name'
|
||||||
|
)
|
||||||
|
v-text-field.md2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='lock'
|
||||||
|
append-icon='casino'
|
||||||
|
v-model='password'
|
||||||
|
label='Password'
|
||||||
|
counter='255'
|
||||||
|
@click:append='generatePwd'
|
||||||
|
)
|
||||||
|
v-text-field.md2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='title'
|
||||||
|
v-model='jobTitle'
|
||||||
|
label='Job Title'
|
||||||
|
counter='255'
|
||||||
|
hint='Optional'
|
||||||
|
persistent-hint
|
||||||
|
)
|
||||||
|
v-text-field.md2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='grey lighten-4'
|
||||||
|
prepend-icon='public'
|
||||||
|
v-model='location'
|
||||||
|
label='Location'
|
||||||
|
counter='255'
|
||||||
|
hint='Optional'
|
||||||
|
persistent-hint
|
||||||
|
)
|
||||||
|
v-card-chin
|
||||||
|
v-spacer
|
||||||
|
v-btn(flat, @click='isShown = false') Cancel
|
||||||
|
v-btn(color='primary', @click='createUser') Create
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import uuidv4 from 'uuid/v4'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
jobTitle: '',
|
||||||
|
location: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isShown: {
|
||||||
|
get() { return this.value },
|
||||||
|
set(val) { this.$emit('input', val) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(newValue, oldValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.emailInput.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async createUser() {
|
||||||
|
|
||||||
|
},
|
||||||
|
generatePwd() {
|
||||||
|
this.password = uuidv4().slice(-12)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -8,99 +8,97 @@
|
|||||||
.headline.blue--text.text--darken-2 Users
|
.headline.blue--text.text--darken-2 Users
|
||||||
.subheading.grey--text Manage users
|
.subheading.grey--text Manage users
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn(outline, color='grey', large)
|
v-btn(outline, color='grey', large, @click='refresh')
|
||||||
v-icon refresh
|
v-icon refresh
|
||||||
v-btn(color='primary', large, depressed)
|
v-btn(color='primary', large, depressed, @click='authorizeUser')
|
||||||
v-icon(left) lock_outline
|
v-icon(left) lock_outline
|
||||||
span Authorize User
|
span Authorize Social User
|
||||||
v-btn(color='primary', large, depressed)
|
v-btn(color='primary', large, depressed, @click='createUser')
|
||||||
v-icon(left) add
|
v-icon(left) add
|
||||||
span New User
|
span New Local User
|
||||||
v-card.mt-3
|
v-card.mt-3
|
||||||
v-data-table(
|
v-data-table(
|
||||||
v-model='selected'
|
v-model='selected'
|
||||||
:items='items',
|
:items='users',
|
||||||
:headers='headers',
|
:headers='headers',
|
||||||
:search='search',
|
:search='search',
|
||||||
:pagination.sync='pagination',
|
:pagination.sync='pagination',
|
||||||
:rows-per-page-items='[15]'
|
:rows-per-page-items='[15]'
|
||||||
select-all,
|
|
||||||
hide-actions,
|
hide-actions,
|
||||||
disable-initial-sort
|
disable-initial-sort
|
||||||
)
|
)
|
||||||
template(slot='headers', slot-scope='props')
|
template(slot='headers', slot-scope='props')
|
||||||
tr
|
tr
|
||||||
th(width='50')
|
//- th(width='50')
|
||||||
th.text-xs-right(
|
|
||||||
width='80'
|
|
||||||
:class='[`column sortable`, pagination.descending ? `desc` : `asc`, pagination.sortBy === `id` ? `active` : ``]'
|
|
||||||
@click='changeSort(`id`)'
|
|
||||||
)
|
|
||||||
v-icon(small) arrow_upward
|
|
||||||
| ID
|
|
||||||
th.text-xs-left(
|
th.text-xs-left(
|
||||||
v-for='header in props.headers'
|
v-for='header in props.headers'
|
||||||
:key='header.text'
|
:key='header.text'
|
||||||
:width='header.width'
|
:width='header.width'
|
||||||
:class='[`column sortable`, pagination.descending ? `desc` : `asc`, header.value === pagination.sortBy ? `active` : ``]'
|
:class='[`column`, header.sortable ? `sortable` : ``, pagination.descending ? `desc` : `asc`, header.value === pagination.sortBy ? `active` : ``]'
|
||||||
@click='changeSort(header.value)'
|
@click='changeSort(header.value)'
|
||||||
)
|
)
|
||||||
| {{ header.text }}
|
| {{ header.text }}
|
||||||
v-icon(small) arrow_upward
|
v-icon(small, v-if='header.sortable') arrow_upward
|
||||||
template(slot='items', slot-scope='props')
|
template(slot='items', slot-scope='props')
|
||||||
tr(:active='props.selected')
|
tr(:active='props.selected')
|
||||||
td
|
//- td
|
||||||
v-checkbox(hide-details, :input-value='props.selected', color='blue darken-2', @click='props.selected = !props.selected')
|
v-checkbox(hide-details, :input-value='props.selected', color='blue darken-2', @click='props.selected = !props.selected')
|
||||||
td.text-xs-right {{ props.item.id }}
|
td.text-xs-right {{ props.item.id }}
|
||||||
|
td: strong {{ props.item.name }}
|
||||||
td {{ props.item.email }}
|
td {{ props.item.email }}
|
||||||
td {{ props.item.name }}
|
td {{ props.item.providerKey }}
|
||||||
td {{ props.item.provider }}
|
td {{ props.item.createdAt | moment('from') }}
|
||||||
td {{ props.item.createdOn }}
|
td
|
||||||
td {{ props.item.updatedOn }}
|
v-menu(bottom, right, min-width='200')
|
||||||
td: v-btn(icon): v-icon.grey--text.text--darken-1 more_horiz
|
v-btn(icon, slot='activator'): v-icon.grey--text.text--darken-1 more_horiz
|
||||||
|
v-list
|
||||||
|
v-list-tile(@click='')
|
||||||
|
v-list-tile-action
|
||||||
|
v-icon(color='primary') edit
|
||||||
|
v-list-tile-content
|
||||||
|
v-list-tile-title Edit
|
||||||
|
v-list-tile(@click='')
|
||||||
|
v-list-tile-action
|
||||||
|
v-icon(color='red') block
|
||||||
|
v-list-tile-content
|
||||||
|
v-list-tile-title Block
|
||||||
template(slot='no-data')
|
template(slot='no-data')
|
||||||
v-alert(icon='warning', :value='true') No users to display!
|
.pa-3
|
||||||
|
v-alert(icon='warning', :value='true', outline) No users to display!
|
||||||
.text-xs-center.py-2
|
.text-xs-center.py-2
|
||||||
v-pagination(v-model='pagination.page', :length='pages')
|
v-pagination(v-model='pagination.page', :length='pages')
|
||||||
|
|
||||||
|
user-authorize(v-model='isAuthorizeDialogShown')
|
||||||
|
user-create(v-model='isCreateDialogShown')
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import usersQuery from 'gql/admin/users/users-query-list.gql'
|
||||||
|
|
||||||
|
import UserAuthorize from './admin-users-authorize.vue'
|
||||||
|
import UserCreate from './admin-users-create.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
UserAuthorize,
|
||||||
|
UserCreate
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selected: [],
|
selected: [],
|
||||||
pagination: {},
|
pagination: {},
|
||||||
items: [
|
users: [],
|
||||||
{ id: 1, email: 'user@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 2, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 3, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 4, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 5, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 6, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 7, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 8, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 9, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 10, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 11, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 12, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 13, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 14, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 15, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 16, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 17, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 18, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 19, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
|
|
||||||
{ id: 20, email: 'dude@test.com', name: 'John Doe', provider: 'local' }
|
|
||||||
],
|
|
||||||
headers: [
|
headers: [
|
||||||
{ text: 'Email', value: 'email' },
|
{ text: 'ID', value: 'id', width: 80, sortable: true },
|
||||||
{ text: 'Name', value: 'name' },
|
{ text: 'Name', value: 'name', sortable: true },
|
||||||
{ text: 'Provider', value: 'provider' },
|
{ text: 'Email', value: 'email', sortable: true },
|
||||||
{ text: 'Created On', value: 'createdOn' },
|
{ text: 'Provider', value: 'provider', sortable: true },
|
||||||
{ text: 'Updated On', value: 'updatedOn' },
|
{ text: 'Created', value: 'createdAt', sortable: true },
|
||||||
{ text: '', value: 'actions', sortable: false, width: 50 }
|
{ text: '', value: 'actions', sortable: false, width: 50 }
|
||||||
],
|
],
|
||||||
search: ''
|
search: '',
|
||||||
|
isAuthorizeDialogShown: false,
|
||||||
|
isCreateDialogShown: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -113,6 +111,20 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
authorizeUser() {
|
||||||
|
this.isAuthorizeDialogShown = true
|
||||||
|
},
|
||||||
|
createUser() {
|
||||||
|
this.isCreateDialogShown = true
|
||||||
|
},
|
||||||
|
async refresh() {
|
||||||
|
await this.$apollo.queries.users.refetch()
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
message: 'Users list has been refreshed.',
|
||||||
|
style: 'success',
|
||||||
|
icon: 'cached'
|
||||||
|
})
|
||||||
|
},
|
||||||
changeSort (column) {
|
changeSort (column) {
|
||||||
if (this.pagination.sortBy === column) {
|
if (this.pagination.sortBy === column) {
|
||||||
this.pagination.descending = !this.pagination.descending
|
this.pagination.descending = !this.pagination.descending
|
||||||
@@ -128,6 +140,16 @@ export default {
|
|||||||
this.selected = this.items.slice()
|
this.selected = this.items.slice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
apollo: {
|
||||||
|
users: {
|
||||||
|
query: usersQuery,
|
||||||
|
fetchPolicy: 'network-only',
|
||||||
|
update: (data) => data.users.list,
|
||||||
|
watchLoading (isLoading) {
|
||||||
|
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-users-refresh')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ query {
|
|||||||
name
|
name
|
||||||
email
|
email
|
||||||
providerKey
|
providerKey
|
||||||
role
|
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
client/graph/admin/users/users-query-strategies.gql
Normal file
12
client/graph/admin/users/users-query-strategies.gql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
query {
|
||||||
|
authentication {
|
||||||
|
strategies(
|
||||||
|
isEnabled: true
|
||||||
|
) {
|
||||||
|
key
|
||||||
|
title
|
||||||
|
icon
|
||||||
|
color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
package.json
58
package.json
@@ -63,26 +63,26 @@
|
|||||||
"diff2html": "2.4.0",
|
"diff2html": "2.4.0",
|
||||||
"dotize": "^0.2.0",
|
"dotize": "^0.2.0",
|
||||||
"execa": "1.0.0",
|
"execa": "1.0.0",
|
||||||
"express": "4.16.3",
|
"express": "4.16.4",
|
||||||
"express-brute": "1.0.1",
|
"express-brute": "1.0.1",
|
||||||
"express-brute-redis": "0.0.1",
|
"express-brute-redis": "0.0.1",
|
||||||
"express-session": "1.15.6",
|
"express-session": "1.15.6",
|
||||||
"file-type": "9.0.0",
|
"file-type": "10.0.0",
|
||||||
"filesize.js": "1.0.2",
|
"filesize.js": "1.0.2",
|
||||||
"follow-redirects": "1.5.8",
|
"follow-redirects": "1.5.9",
|
||||||
"fs-extra": "7.0.0",
|
"fs-extra": "7.0.0",
|
||||||
"getos": "3.1.0",
|
"getos": "3.1.0",
|
||||||
"graphql": "14.0.2",
|
"graphql": "14.0.2",
|
||||||
"graphql-list-fields": "2.0.2",
|
"graphql-list-fields": "2.0.2",
|
||||||
"graphql-subscriptions": "1.0.0",
|
"graphql-subscriptions": "1.0.0",
|
||||||
"graphql-tools": "4.0.0",
|
"graphql-tools": "4.0.1",
|
||||||
"highlight.js": "9.12.0",
|
"highlight.js": "9.13.0",
|
||||||
"i18next": "11.9.0",
|
"i18next": "11.9.1",
|
||||||
"i18next-express-middleware": "1.4.0",
|
"i18next-express-middleware": "1.4.1",
|
||||||
"i18next-localstorage-cache": "1.1.1",
|
"i18next-localstorage-cache": "1.1.1",
|
||||||
"i18next-node-fs-backend": "2.1.0",
|
"i18next-node-fs-backend": "2.1.0",
|
||||||
"image-size": "0.6.3",
|
"image-size": "0.6.3",
|
||||||
"ioredis": "4.0.0",
|
"ioredis": "4.0.2",
|
||||||
"js-binary": "1.2.0",
|
"js-binary": "1.2.0",
|
||||||
"js-yaml": "3.12.0",
|
"js-yaml": "3.12.0",
|
||||||
"jsonwebtoken": "8.3.0",
|
"jsonwebtoken": "8.3.0",
|
||||||
@@ -107,9 +107,9 @@
|
|||||||
"mime-types": "2.1.20",
|
"mime-types": "2.1.20",
|
||||||
"moment": "2.22.2",
|
"moment": "2.22.2",
|
||||||
"moment-timezone": "0.5.21",
|
"moment-timezone": "0.5.21",
|
||||||
"mongodb": "3.1.6",
|
"mongodb": "3.1.8",
|
||||||
"mssql": "4.2.1",
|
"mssql": "4.2.1",
|
||||||
"multer": "1.4.0",
|
"multer": "1.4.1",
|
||||||
"mysql2": "1.6.1",
|
"mysql2": "1.6.1",
|
||||||
"node-2fa": "1.1.2",
|
"node-2fa": "1.1.2",
|
||||||
"node-cache": "4.2.0",
|
"node-cache": "4.2.0",
|
||||||
@@ -135,9 +135,9 @@
|
|||||||
"passport-slack": "0.0.7",
|
"passport-slack": "0.0.7",
|
||||||
"passport-twitch": "1.0.3",
|
"passport-twitch": "1.0.3",
|
||||||
"passport-windowslive": "1.0.2",
|
"passport-windowslive": "1.0.2",
|
||||||
"pg": "7.4.3",
|
"pg": "7.5.0",
|
||||||
"pg-hstore": "2.3.2",
|
"pg-hstore": "2.3.2",
|
||||||
"pm2": "3.1.3",
|
"pm2": "3.2.2",
|
||||||
"pug": "2.0.3",
|
"pug": "2.0.3",
|
||||||
"qr-image": "3.2.0",
|
"qr-image": "3.2.0",
|
||||||
"raven": "2.6.4",
|
"raven": "2.6.4",
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
"request-promise": "4.2.2",
|
"request-promise": "4.2.2",
|
||||||
"scim-query-filter-parser": "1.1.0",
|
"scim-query-filter-parser": "1.1.0",
|
||||||
"semver": "5.5.1",
|
"semver": "5.6.0",
|
||||||
"serve-favicon": "2.5.0",
|
"serve-favicon": "2.5.0",
|
||||||
"sqlite3": "4.0.2",
|
"sqlite3": "4.0.2",
|
||||||
"subscriptions-transport-ws": "0.9.15",
|
"subscriptions-transport-ws": "0.9.15",
|
||||||
@@ -172,9 +172,9 @@
|
|||||||
"@babel/polyfill": "^7.0.0",
|
"@babel/polyfill": "^7.0.0",
|
||||||
"@babel/preset-env": "^7.1.0",
|
"@babel/preset-env": "^7.1.0",
|
||||||
"@panter/vue-i18next": "0.13.0",
|
"@panter/vue-i18next": "0.13.0",
|
||||||
"@vue/cli": "3.0.4",
|
"@vue/cli": "3.0.5",
|
||||||
"animated-number-vue": "0.1.3",
|
"animated-number-vue": "0.1.3",
|
||||||
"apollo-cache-inmemory": "1.3.0",
|
"apollo-cache-inmemory": "1.3.5",
|
||||||
"apollo-client": "2.4.2",
|
"apollo-client": "2.4.2",
|
||||||
"apollo-fetch": "0.7.0",
|
"apollo-fetch": "0.7.0",
|
||||||
"apollo-link": "1.2.3",
|
"apollo-link": "1.2.3",
|
||||||
@@ -195,12 +195,12 @@
|
|||||||
"cache-loader": "1.2.2",
|
"cache-loader": "1.2.2",
|
||||||
"chart.js": "2.7.2",
|
"chart.js": "2.7.2",
|
||||||
"clean-webpack-plugin": "0.1.19",
|
"clean-webpack-plugin": "0.1.19",
|
||||||
"copy-webpack-plugin": "4.5.2",
|
"copy-webpack-plugin": "4.5.3",
|
||||||
"css-loader": "1.0.0",
|
"css-loader": "1.0.0",
|
||||||
"cssnano": "4.1.4",
|
"cssnano": "4.1.4",
|
||||||
"duplicate-package-checker-webpack-plugin": "3.0.0",
|
"duplicate-package-checker-webpack-plugin": "3.0.0",
|
||||||
"epic-spinners": "1.0.3",
|
"epic-spinners": "1.0.3",
|
||||||
"eslint": "5.6.1",
|
"eslint": "5.7.0",
|
||||||
"eslint-config-requarks": "1.0.7",
|
"eslint-config-requarks": "1.0.7",
|
||||||
"eslint-config-standard": "12.0.0",
|
"eslint-config-standard": "12.0.0",
|
||||||
"eslint-plugin-import": "2.14.0",
|
"eslint-plugin-import": "2.14.0",
|
||||||
@@ -212,15 +212,15 @@
|
|||||||
"grapesjs": "0.14.33",
|
"grapesjs": "0.14.33",
|
||||||
"graphiql": "0.12.0",
|
"graphiql": "0.12.0",
|
||||||
"graphql-persisted-document-loader": "1.0.1",
|
"graphql-persisted-document-loader": "1.0.1",
|
||||||
"graphql-tag": "^2.9.2",
|
"graphql-tag": "^2.10.0",
|
||||||
"graphql-voyager": "1.0.0-rc.25",
|
"graphql-voyager": "1.0.0-rc.26",
|
||||||
"hammerjs": "2.0.8",
|
"hammerjs": "2.0.8",
|
||||||
"html-webpack-plugin": "3.2.0",
|
"html-webpack-plugin": "3.2.0",
|
||||||
"html-webpack-pug-plugin": "0.3.0",
|
"html-webpack-pug-plugin": "0.3.0",
|
||||||
"i18next-xhr-backend": "1.5.1",
|
"i18next-xhr-backend": "1.5.1",
|
||||||
"ignore-loader": "0.1.2",
|
"ignore-loader": "0.1.2",
|
||||||
"js-cookie": "2.2.0",
|
"js-cookie": "2.2.0",
|
||||||
"mini-css-extract-plugin": "0.4.3",
|
"mini-css-extract-plugin": "0.4.4",
|
||||||
"node-sass": "4.9.3",
|
"node-sass": "4.9.3",
|
||||||
"offline-plugin": "5.0.5",
|
"offline-plugin": "5.0.5",
|
||||||
"optimize-css-assets-webpack-plugin": "5.0.1",
|
"optimize-css-assets-webpack-plugin": "5.0.1",
|
||||||
@@ -229,7 +229,7 @@
|
|||||||
"postcss-flexibility": "2.0.0",
|
"postcss-flexibility": "2.0.0",
|
||||||
"postcss-import": "12.0.0",
|
"postcss-import": "12.0.0",
|
||||||
"postcss-loader": "3.0.0",
|
"postcss-loader": "3.0.0",
|
||||||
"postcss-preset-env": "6.0.7",
|
"postcss-preset-env": "6.1.1",
|
||||||
"postcss-selector-parser": "5.0.0-rc.3",
|
"postcss-selector-parser": "5.0.0-rc.3",
|
||||||
"pug-lint": "2.5.0",
|
"pug-lint": "2.5.0",
|
||||||
"pug-loader": "2.4.0",
|
"pug-loader": "2.4.0",
|
||||||
@@ -242,12 +242,12 @@
|
|||||||
"sass-resources-loader": "1.3.3",
|
"sass-resources-loader": "1.3.3",
|
||||||
"script-ext-html-webpack-plugin": "2.0.1",
|
"script-ext-html-webpack-plugin": "2.0.1",
|
||||||
"simple-progress-webpack-plugin": "1.1.2",
|
"simple-progress-webpack-plugin": "1.1.2",
|
||||||
"style-loader": "0.23.0",
|
"style-loader": "0.23.1",
|
||||||
"stylus": "0.54.5",
|
"stylus": "0.54.5",
|
||||||
"stylus-loader": "3.0.2",
|
"stylus-loader": "3.0.2",
|
||||||
"twemoji-awesome": "1.0.6",
|
"twemoji-awesome": "1.0.6",
|
||||||
"url-loader": "1.1.1",
|
"url-loader": "1.1.2",
|
||||||
"vee-validate": "2.1.0-beta.9",
|
"vee-validate": "2.1.0-beta.11",
|
||||||
"velocity-animate": "1.5.2",
|
"velocity-animate": "1.5.2",
|
||||||
"viz.js": "2.0.0",
|
"viz.js": "2.0.0",
|
||||||
"vue": "2.5.17",
|
"vue": "2.5.17",
|
||||||
@@ -257,7 +257,7 @@
|
|||||||
"vue-codemirror": "4.0.5",
|
"vue-codemirror": "4.0.5",
|
||||||
"vue-hot-reload-api": "2.3.1",
|
"vue-hot-reload-api": "2.3.1",
|
||||||
"vue-loader": "15.4.2",
|
"vue-loader": "15.4.2",
|
||||||
"vue-material-design-icons": "2.1.1",
|
"vue-material-design-icons": "2.3.0",
|
||||||
"vue-moment": "4.0.0",
|
"vue-moment": "4.0.0",
|
||||||
"vue-router": "3.0.1",
|
"vue-router": "3.0.1",
|
||||||
"vue-simple-breakpoints": "1.0.3",
|
"vue-simple-breakpoints": "1.0.3",
|
||||||
@@ -267,7 +267,7 @@
|
|||||||
"vue-tree-navigation": "3.0.1",
|
"vue-tree-navigation": "3.0.1",
|
||||||
"vue2-animate": "2.1.0",
|
"vue2-animate": "2.1.0",
|
||||||
"vuedraggable": "2.16.0",
|
"vuedraggable": "2.16.0",
|
||||||
"vuetify": "1.2.5",
|
"vuetify": "1.2.9",
|
||||||
"vuex": "3.0.1",
|
"vuex": "3.0.1",
|
||||||
"vuex-pathify": "1.1.3",
|
"vuex-pathify": "1.1.3",
|
||||||
"vuex-persistedstate": "2.5.4",
|
"vuex-persistedstate": "2.5.4",
|
||||||
@@ -275,12 +275,12 @@
|
|||||||
"webpack-bundle-analyzer": "3.0.2",
|
"webpack-bundle-analyzer": "3.0.2",
|
||||||
"webpack-cli": "3.1.2",
|
"webpack-cli": "3.1.2",
|
||||||
"webpack-dev-middleware": "3.4.0",
|
"webpack-dev-middleware": "3.4.0",
|
||||||
"webpack-hot-middleware": "2.24.2",
|
"webpack-hot-middleware": "2.24.3",
|
||||||
"webpack-merge": "4.1.4",
|
"webpack-merge": "4.1.4",
|
||||||
"webpack-subresource-integrity": "1.1.0-rc.6",
|
"webpack-subresource-integrity": "1.2.0",
|
||||||
"whatwg-fetch": "3.0.0",
|
"whatwg-fetch": "3.0.0",
|
||||||
"write-file-webpack-plugin": "4.4.1",
|
"write-file-webpack-plugin": "4.4.1",
|
||||||
"xterm": "3.7.0"
|
"xterm": "3.8.0"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ module.exports = {
|
|||||||
UserQuery: {
|
UserQuery: {
|
||||||
async list(obj, args, context, info) {
|
async list(obj, args, context, info) {
|
||||||
return WIKI.models.users.query()
|
return WIKI.models.users.query()
|
||||||
.select('id', 'email', 'name', 'providerKey', 'role', 'createdAt', 'updatedAt')
|
.select('id', 'email', 'name', 'providerKey', 'createdAt')
|
||||||
},
|
},
|
||||||
async search(obj, args, context, info) {
|
async search(obj, args, context, info) {
|
||||||
return WIKI.models.users.query()
|
return WIKI.models.users.query()
|
||||||
.where('email', 'like', `%${args.query}%`)
|
.where('email', 'like', `%${args.query}%`)
|
||||||
.orWhere('name', 'like', `%${args.query}%`)
|
.orWhere('name', 'like', `%${args.query}%`)
|
||||||
.limit(10)
|
.limit(10)
|
||||||
.select('id', 'email', 'name', 'providerKey', 'role', 'createdAt', 'updatedAt')
|
.select('id', 'email', 'name', 'providerKey', 'createdAt')
|
||||||
},
|
},
|
||||||
async single(obj, args, context, info) {
|
async single(obj, args, context, info) {
|
||||||
let usr = await WIKI.models.users.query().findById(args.id)
|
let usr = await WIKI.models.users.query().findById(args.id)
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ type UserMinimal {
|
|||||||
name: String!
|
name: String!
|
||||||
email: String!
|
email: String!
|
||||||
providerKey: String!
|
providerKey: String!
|
||||||
|
createdAt: Date!
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ module.exports = {
|
|||||||
|
|
||||||
// Try headers, otherwise cookies for response
|
// Try headers, otherwise cookies for response
|
||||||
if (req.get('content-type') === 'application/json') {
|
if (req.get('content-type') === 'application/json') {
|
||||||
res.headers('new-jwt', newToken.token)
|
res.set('new-jwt', newToken.token)
|
||||||
} else {
|
} else {
|
||||||
res.cookie('jwt', newToken.token, { expires: moment().add(365, 'days').toDate() })
|
res.cookie('jwt', newToken.token, { expires: moment().add(365, 'days').toDate() })
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user