Commit 14e34d43 authored by Nicolas Giard's avatar Nicolas Giard

feat: jwt renew via graphql + users create/authorize UI

parent 3caa842d
...@@ -7,10 +7,11 @@ import VueSimpleBreakpoints from 'vue-simple-breakpoints' ...@@ -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) ...@@ -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( ...@@ -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,
......
<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>
<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 @@ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -5,9 +5,7 @@ query {
name name
email email
providerKey providerKey
role
createdAt createdAt
updatedAt
} }
} }
} }
query {
authentication {
strategies(
isEnabled: true
) {
key
title
icon
color
}
}
}
...@@ -63,26 +63,26 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 = { ...@@ -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 { ...@@ -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 = { ...@@ -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() })
} }
......
This diff was suppressed by a .gitattributes entry.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment