feat: user profile page - save info + change pwd
This commit is contained in:
parent
c7f3c9d908
commit
1e4d513252
@ -123,7 +123,7 @@
|
|||||||
v-list-item-avatar(size='24', tile): v-icon mdi-heart-outline
|
v-list-item-avatar(size='24', tile): v-icon mdi-heart-outline
|
||||||
v-list-item-title {{ $t('admin:contribute.title') }}
|
v-list-item-title {{ $t('admin:contribute.title') }}
|
||||||
|
|
||||||
v-content(:class='darkMode ? "grey darken-4" : ""')
|
v-content(:class='darkMode ? "grey darken-4" : "grey lighten-5"')
|
||||||
transition(name='admin-router')
|
transition(name='admin-router')
|
||||||
router-view
|
router-view
|
||||||
|
|
||||||
|
@ -195,7 +195,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import nanoid from 'nanoid/non-secure/generate'
|
import { customAlphabet } from 'nanoid/non-secure'
|
||||||
|
|
||||||
|
const nanoid = customAlphabet('1234567890abcdef', 10)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -241,7 +243,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
addRule(group) {
|
addRule(group) {
|
||||||
this.group.pageRules.push({
|
this.group.pageRules.push({
|
||||||
id: nanoid('1234567890abcdef', 10),
|
id: nanoid(),
|
||||||
path: '',
|
path: '',
|
||||||
roles: [],
|
roles: [],
|
||||||
match: 'START',
|
match: 'START',
|
||||||
|
@ -166,7 +166,7 @@ export default {
|
|||||||
return 'mdi-linux'
|
return 'mdi-linux'
|
||||||
}
|
}
|
||||||
case 'win32':
|
case 'win32':
|
||||||
return 'mdi-windows'
|
return 'mdi-microsoft-windows'
|
||||||
default:
|
default:
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
@ -355,8 +355,12 @@ export default {
|
|||||||
},
|
},
|
||||||
async saveAndClose() {
|
async saveAndClose() {
|
||||||
try {
|
try {
|
||||||
await this.save({ rethrow: true })
|
if (this.$store.get('editor/mode') === 'create') {
|
||||||
await this.exit()
|
await this.save()
|
||||||
|
} else {
|
||||||
|
await this.save({ rethrow: true })
|
||||||
|
await this.exit()
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Error is already handled
|
// Error is already handled
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@
|
|||||||
v-spacer
|
v-spacer
|
||||||
|
|
||||||
loader(v-model='isLoading', :color='loaderColor', :title='loaderTitle', :subtitle='$t(`auth:pleaseWait`)')
|
loader(v-model='isLoading', :color='loaderColor', :title='loaderTitle', :subtitle='$t(`auth:pleaseWait`)')
|
||||||
nav-footer(color='grey darken-5')
|
nav-footer(color='grey darken-5', dark-color='grey darken-5')
|
||||||
notify
|
notify
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
.headline.primary--text.animated.fadeInLeft {{$t('profile:title')}}
|
.headline.primary--text.animated.fadeInLeft {{$t('profile:title')}}
|
||||||
.subheading.grey--text.animated.fadeInLeft {{$t('profile:subtitle')}}
|
.subheading.grey--text.animated.fadeInLeft {{$t('profile:subtitle')}}
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn.animated.fadeInDown(outlined, color='primary', disabled).mr-0
|
//- v-btn.animated.fadeInDown(outlined, color='primary', disabled).mr-0
|
||||||
v-icon(left) mdi-earth
|
//- v-icon(left) mdi-earth
|
||||||
span {{$t('profile:viewPublicProfile')}}
|
//- span {{$t('profile:viewPublicProfile')}}
|
||||||
v-flex(lg6 xs12)
|
v-flex(lg6 xs12)
|
||||||
v-card
|
v-card
|
||||||
v-toolbar(color='primary', dark, dense, flat)
|
v-toolbar(color='primary', dark, dense, flat)
|
||||||
@ -30,8 +30,9 @@
|
|||||||
left
|
left
|
||||||
)
|
)
|
||||||
template(v-slot:activator='{ on }')
|
template(v-slot:activator='{ on }')
|
||||||
v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptDisplayName`)')
|
v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptDisplayName`)')
|
||||||
v-icon mdi-pencil
|
v-icon(left) mdi-pencil
|
||||||
|
span {{ $t('common:actions:edit') }}
|
||||||
v-card
|
v-card
|
||||||
v-text-field(
|
v-text-field(
|
||||||
ref='iptDisplayName'
|
ref='iptDisplayName'
|
||||||
@ -59,8 +60,9 @@
|
|||||||
left
|
left
|
||||||
)
|
)
|
||||||
template(v-slot:activator='{ on }')
|
template(v-slot:activator='{ on }')
|
||||||
v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptLocation`)')
|
v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptLocation`)')
|
||||||
v-icon mdi-pencil
|
v-icon(left) mdi-pencil
|
||||||
|
span {{ $t('common:actions:edit') }}
|
||||||
v-card
|
v-card
|
||||||
v-text-field(
|
v-text-field(
|
||||||
ref='iptLocation'
|
ref='iptLocation'
|
||||||
@ -88,8 +90,9 @@
|
|||||||
left
|
left
|
||||||
)
|
)
|
||||||
template(v-slot:activator='{ on }')
|
template(v-slot:activator='{ on }')
|
||||||
v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptJobTitle`)')
|
v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptJobTitle`)')
|
||||||
v-icon mdi-pencil
|
v-icon(left) mdi-pencil
|
||||||
|
span {{ $t('common:actions:edit') }}
|
||||||
v-card
|
v-card
|
||||||
v-text-field(
|
v-text-field(
|
||||||
ref='iptJobTitle'
|
ref='iptJobTitle'
|
||||||
@ -117,8 +120,9 @@
|
|||||||
left
|
left
|
||||||
)
|
)
|
||||||
template(v-slot:activator='{ on }')
|
template(v-slot:activator='{ on }')
|
||||||
v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptTimezone`)')
|
v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptTimezone`)')
|
||||||
v-icon mdi-pencil
|
v-icon(left) mdi-pencil
|
||||||
|
span {{ $t('common:actions:edit') }}
|
||||||
v-card
|
v-card
|
||||||
v-select(
|
v-select(
|
||||||
ref='iptTimezone'
|
ref='iptTimezone'
|
||||||
@ -135,15 +139,15 @@
|
|||||||
)
|
)
|
||||||
v-card-chin
|
v-card-chin
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn.px-4(color='success')
|
v-btn.px-4(color='success', depressed, @click='saveProfile', :loading='saveLoading')
|
||||||
v-icon(left) mdi-content-save
|
v-icon(left) mdi-content-save
|
||||||
span {{$t('common:actions.save')}}
|
span {{$t('common:actions.save')}}
|
||||||
v-card.mt-3
|
v-card.mt-3
|
||||||
v-toolbar(color='primary', dark, dense, flat)
|
v-toolbar(color='primary', dark, dense, flat)
|
||||||
v-toolbar-title
|
v-toolbar-title
|
||||||
.subtitle-1 Authentication
|
.subtitle-1 {{$t('profile:auth.title')}}
|
||||||
v-card-text
|
v-card-text.pt-0
|
||||||
v-subheader.pl-0 Provider
|
v-subheader.pl-0: span.subtitle-2 {{$t('profile:auth.provider')}}
|
||||||
v-toolbar(
|
v-toolbar(
|
||||||
flat
|
flat
|
||||||
:color='$vuetify.theme.dark ? "grey darken-2" : "purple lighten-5"'
|
:color='$vuetify.theme.dark ? "grey darken-2" : "purple lighten-5"'
|
||||||
@ -151,36 +155,64 @@
|
|||||||
:class='$vuetify.theme.dark ? "grey--text text--lighten-1" : "purple--text text--darken-4"'
|
:class='$vuetify.theme.dark ? "grey--text text--lighten-1" : "purple--text text--darken-4"'
|
||||||
)
|
)
|
||||||
v-icon(:color='$vuetify.theme.dark ? "grey lighten-1" : "purple darken-4"') mdi-shield-lock
|
v-icon(:color='$vuetify.theme.dark ? "grey lighten-1" : "purple darken-4"') mdi-shield-lock
|
||||||
.subheading.ml-3 Local
|
.subheading.ml-3 {{ user.providerName }}
|
||||||
v-divider.mt-3
|
//- v-divider.mt-3
|
||||||
v-subheader.pl-0 Two-Factor Authentication (2FA)
|
//- v-subheader.pl-0: span.subtitle-2 Two-Factor Authentication (2FA)
|
||||||
.caption.mb-2 2FA adds an extra layer of security by requiring a unique code generated on your smartphone when signing in.
|
//- .caption.mb-2 2FA adds an extra layer of security by requiring a unique code generated on your smartphone when signing in.
|
||||||
v-btn(color='purple darken-4', disabled).ml-0 Enable 2FA
|
//- v-btn(color='purple darken-4', disabled).ml-0 Enable 2FA
|
||||||
v-btn(color='purple darken-4', dark, depressed, disabled).ml-0 Disable 2FA
|
//- v-btn(color='purple darken-4', dark, depressed, disabled).ml-0 Disable 2FA
|
||||||
v-divider.mt-3
|
template(v-if='user.providerKey === `local`')
|
||||||
v-subheader.pl-0 Change Password
|
v-divider.mt-3
|
||||||
v-text-field(label='Current Password', type='password', prepend-icon='mdi-textbox-password')
|
v-subheader.pl-0: span.subtitle-2 {{$t('profile:auth.changePassword')}}
|
||||||
v-text-field(label='New Password', type='password', prepend-icon='mdi-textbox-password')
|
v-text-field(
|
||||||
v-text-field(label='Confirm New Password', type='password', prepend-icon='mdi-textbox-password')
|
ref='iptCurrentPass'
|
||||||
|
v-model='currentPass'
|
||||||
|
outlined
|
||||||
|
:label='$t(`profile:auth.currentPassword`)'
|
||||||
|
type='password'
|
||||||
|
prepend-inner-icon='mdi-textbox-password'
|
||||||
|
)
|
||||||
|
v-text-field(
|
||||||
|
ref='iptNewPass'
|
||||||
|
v-model='newPass'
|
||||||
|
outlined
|
||||||
|
:label='$t(`profile:auth.newPassword`)'
|
||||||
|
type='password'
|
||||||
|
prepend-inner-icon='mdi-textbox-password'
|
||||||
|
counter='255'
|
||||||
|
loading
|
||||||
|
)
|
||||||
|
password-strength(slot='progress', v-model='newPass')
|
||||||
|
v-text-field(
|
||||||
|
ref='iptVerifyPass'
|
||||||
|
v-model='verifyPass'
|
||||||
|
outlined
|
||||||
|
:label='$t(`profile:auth.verifyPassword`)'
|
||||||
|
type='password'
|
||||||
|
prepend-inner-icon='mdi-textbox-password'
|
||||||
|
hide-details
|
||||||
|
)
|
||||||
v-card-chin
|
v-card-chin
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn.px-4(color='purple darken-4', dark)
|
v-btn.px-4(color='purple darken-4', dark, depressed, @click='changePassword', :loading='changePassLoading')
|
||||||
v-icon(left) mdi-progress-check
|
v-icon(left) mdi-progress-check
|
||||||
span Change Password
|
span {{$t('profile:auth.changePassword')}}
|
||||||
v-flex(lg6 xs12)
|
v-flex(lg6 xs12)
|
||||||
|
//- v-card
|
||||||
|
//- v-toolbar(color='primary', dark, dense, flat)
|
||||||
|
//- v-toolbar-title
|
||||||
|
//- .subtitle-1 Picture
|
||||||
|
//- v-card-title
|
||||||
|
//- v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
|
||||||
|
//- span.white--text.subheading {{picture.initials}}
|
||||||
|
//- v-avatar(v-else-if='picture.kind === `image`', :size='40')
|
||||||
|
//- v-img(:src='picture.url')
|
||||||
|
//- v-btn(outlined).mx-4 Upload Picture
|
||||||
|
//- v-btn(outlined, disabled) Remove Picture
|
||||||
v-card
|
v-card
|
||||||
v-toolbar(color='primary', dark, dense, flat)
|
v-toolbar(color='primary', dark, dense, flat)
|
||||||
v-toolbar-title
|
v-toolbar-title
|
||||||
.subtitle-1 Picture
|
.subtitle-1 {{$t('profile:groups.title')}}
|
||||||
v-card-title
|
|
||||||
v-avatar(size='64', color='grey')
|
|
||||||
v-icon(size='64', color='grey lighten-2') mdi-account-circle
|
|
||||||
v-btn(depressed).mx-4.elevation-1 Upload Picture
|
|
||||||
v-btn(depressed, disabled).elevation-1 Remove Picture
|
|
||||||
v-card.mt-3
|
|
||||||
v-toolbar(color='primary', dark, dense, flat)
|
|
||||||
v-toolbar-title
|
|
||||||
.subtitle-1 Groups
|
|
||||||
v-list(dense)
|
v-list(dense)
|
||||||
template(v-for='(grp, idx) of user.groups')
|
template(v-for='(grp, idx) of user.groups')
|
||||||
v-list-item(:key='`grp-id-` + grp')
|
v-list-item(:key='`grp-id-` + grp')
|
||||||
@ -192,34 +224,51 @@
|
|||||||
v-card.mt-3
|
v-card.mt-3
|
||||||
v-toolbar(color='teal', dark, dense, flat)
|
v-toolbar(color='teal', dark, dense, flat)
|
||||||
v-toolbar-title
|
v-toolbar-title
|
||||||
.subtitle-1 Activity
|
.subtitle-1 {{$t('profile:activity.title')}}
|
||||||
v-card-text.grey--text.text--darken-2
|
v-card-text.grey--text.text--darken-2
|
||||||
.caption.grey--text Joined on
|
.caption.grey--text {{$t('profile:activity.joinedOn')}}
|
||||||
.body-2: strong {{ user.createdAt | moment('LLLL') }}
|
.body-2: strong {{ user.createdAt | moment('LLLL') }}
|
||||||
.caption.grey--text.mt-3 Profile last updated on
|
.caption.grey--text.mt-3 {{$t('profile:activity.lastUpdatedOn')}}
|
||||||
.body-2: strong {{ user.updatedAt | moment('LLLL') }}
|
.body-2: strong {{ user.updatedAt | moment('LLLL') }}
|
||||||
.caption.grey--text.mt-3 Last login on
|
.caption.grey--text.mt-3 {{$t('profile:activity.lastLoginOn')}}
|
||||||
.body-2: strong {{ user.lastLoginOn | moment('LLLL') }}
|
.body-2: strong {{ user.lastLoginAt | moment('LLLL') }}
|
||||||
v-divider.mt-3
|
v-divider.mt-3
|
||||||
.caption.grey--text.mt-3 Pages created
|
.caption.grey--text.mt-3 {{$t('profile:activity.pagesCreated')}}
|
||||||
.body-2: strong 0
|
.body-2: strong {{ user.pagesTotal }}
|
||||||
.caption.grey--text.mt-3 Comments posted
|
.caption.grey--text.mt-3 {{$t('profile:activity.commentsPosted')}}
|
||||||
.body-2: strong 0
|
.body-2: strong 0
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { get } from 'vuex-pathify'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import Cookies from 'js-cookie'
|
||||||
|
import validate from 'validate.js'
|
||||||
|
|
||||||
|
import PasswordStrength from '../common/password-strength.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
PasswordStrength
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
saveLoading: false,
|
||||||
|
changePassLoading: false,
|
||||||
user: {
|
user: {
|
||||||
|
name: 'unknown',
|
||||||
|
location: '',
|
||||||
|
jobTitle: '',
|
||||||
|
timezone: '',
|
||||||
createdAt: '1970-01-01',
|
createdAt: '1970-01-01',
|
||||||
updatedAt: '1970-01-01',
|
updatedAt: '1970-01-01',
|
||||||
lastLoginOn: '1970-01-01',
|
lastLoginAt: '1970-01-01',
|
||||||
groups: []
|
groups: []
|
||||||
},
|
},
|
||||||
|
currentPass: '',
|
||||||
|
newPass: '',
|
||||||
|
verifyPass: '',
|
||||||
editPop: {
|
editPop: {
|
||||||
name: false,
|
name: false,
|
||||||
location: false,
|
location: false,
|
||||||
@ -480,6 +529,27 @@ export default {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
pictureUrl: get('user/pictureUrl'),
|
||||||
|
picture () {
|
||||||
|
if (this.pictureUrl && this.pictureUrl.length > 1) {
|
||||||
|
return {
|
||||||
|
kind: 'image',
|
||||||
|
url: this.pictureUrl
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const nameParts = this.user.name.toUpperCase().split(' ')
|
||||||
|
let initials = _.head(nameParts).charAt(0)
|
||||||
|
if (nameParts.length > 1) {
|
||||||
|
initials += _.last(nameParts).charAt(0)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
kind: 'initials',
|
||||||
|
initials
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
/**
|
||||||
* Focus an input after delay
|
* Focus an input after delay
|
||||||
@ -490,6 +560,164 @@ export default {
|
|||||||
this.$refs[ipt].focus()
|
this.$refs[ipt].focus()
|
||||||
}, 200)
|
}, 200)
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Save User Profile
|
||||||
|
*/
|
||||||
|
async saveProfile () {
|
||||||
|
this.saveLoading = true
|
||||||
|
this.$store.commit(`loadingStart`, 'profile-save')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const respRaw = await this.$apollo.mutate({
|
||||||
|
mutation: gql`
|
||||||
|
mutation ($name: String!, $location: String!, $jobTitle: String!, $timezone: String!) {
|
||||||
|
users {
|
||||||
|
updateProfile(name: $name, location: $location, jobTitle: $jobTitle, timezone: $timezone) {
|
||||||
|
responseResult {
|
||||||
|
succeeded
|
||||||
|
errorCode
|
||||||
|
slug
|
||||||
|
message
|
||||||
|
}
|
||||||
|
jwt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
name: this.user.name,
|
||||||
|
location: this.user.location,
|
||||||
|
jobTitle: this.user.jobTitle,
|
||||||
|
timezone: this.user.timezone
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const resp = _.get(respRaw, 'data.users.updateProfile.responseResult', {})
|
||||||
|
if (resp.succeeded) {
|
||||||
|
Cookies.set('jwt', _.get(respRaw, 'data.users.updateProfile.jwt', ''), { expires: 365 })
|
||||||
|
this.$store.set('user/name', this.user.name)
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
message: this.$t('profile:save.success'),
|
||||||
|
style: 'success',
|
||||||
|
icon: 'check'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error(resp.message)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.$store.commit('pushGraphError', err)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit(`loadingStop`, 'profile-save')
|
||||||
|
this.saveLoading = false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Change Password
|
||||||
|
*/
|
||||||
|
async changePassword () {
|
||||||
|
const validation = validate({
|
||||||
|
current: this.currentPass,
|
||||||
|
password: this.newPass,
|
||||||
|
verifyPassword: this.verifyPass
|
||||||
|
}, {
|
||||||
|
current: {
|
||||||
|
presence: {
|
||||||
|
message: this.$t('auth:missingPassword'),
|
||||||
|
allowEmpty: false
|
||||||
|
},
|
||||||
|
length: {
|
||||||
|
minimum: 6,
|
||||||
|
tooShort: this.$t('auth:passwordTooShort')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
presence: {
|
||||||
|
message: this.$t('auth:missingPassword'),
|
||||||
|
allowEmpty: false
|
||||||
|
},
|
||||||
|
length: {
|
||||||
|
minimum: 6,
|
||||||
|
tooShort: this.$t('auth:passwordTooShort')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verifyPassword: {
|
||||||
|
equality: {
|
||||||
|
attribute: 'password',
|
||||||
|
message: this.$t('auth:passwordNotMatch')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { fullMessages: false })
|
||||||
|
|
||||||
|
if (validation) {
|
||||||
|
if (validation.current) {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
style: 'red',
|
||||||
|
message: validation.current[0],
|
||||||
|
icon: 'warning'
|
||||||
|
})
|
||||||
|
this.$refs.iptCurrentPass.focus()
|
||||||
|
} else if (validation.password) {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
style: 'red',
|
||||||
|
message: validation.password[0],
|
||||||
|
icon: 'warning'
|
||||||
|
})
|
||||||
|
this.$refs.iptNewPass.focus()
|
||||||
|
} else if (validation.verifyPassword) {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
style: 'red',
|
||||||
|
message: validation.verifyPassword[0],
|
||||||
|
icon: 'warning'
|
||||||
|
})
|
||||||
|
this.$refs.iptVerifyPass.focus()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.changePassLoading = true
|
||||||
|
this.$store.commit(`loadingStart`, 'profile-changepassword')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const respRaw = await this.$apollo.mutate({
|
||||||
|
mutation: gql`
|
||||||
|
mutation ($current: String!, $new: String!) {
|
||||||
|
users {
|
||||||
|
changePassword(current: $current, new: $new) {
|
||||||
|
responseResult {
|
||||||
|
succeeded
|
||||||
|
errorCode
|
||||||
|
slug
|
||||||
|
message
|
||||||
|
}
|
||||||
|
jwt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
current: this.currentPass,
|
||||||
|
new: this.newPass
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const resp = _.get(respRaw, 'data.users.changePassword.responseResult', {})
|
||||||
|
if (resp.succeeded) {
|
||||||
|
this.currentPass = ''
|
||||||
|
this.newPass = ''
|
||||||
|
this.verifyPass = ''
|
||||||
|
Cookies.set('jwt', _.get(respRaw, 'data.users.changePassword.jwt', ''), { expires: 365 })
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
message: this.$t('profile:auth.changePassSuccess'),
|
||||||
|
style: 'success',
|
||||||
|
icon: 'check'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error(resp.message)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.$store.commit('pushGraphError', err)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit(`loadingStop`, 'profile-changepassword')
|
||||||
|
this.changePassLoading = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
apollo: {
|
apollo: {
|
||||||
@ -501,6 +729,7 @@ export default {
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
email
|
email
|
||||||
|
providerKey
|
||||||
providerName
|
providerName
|
||||||
isSystem
|
isSystem
|
||||||
isVerified
|
isVerified
|
||||||
@ -509,8 +738,9 @@ export default {
|
|||||||
timezone
|
timezone
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
lastLoginOn
|
lastLoginAt
|
||||||
groups
|
groups
|
||||||
|
pagesTotal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
solo
|
solo
|
||||||
flat
|
flat
|
||||||
prepend-icon='mdi-email'
|
prepend-icon='mdi-email'
|
||||||
background-color='grey lighten-4'
|
:background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
|
||||||
hide-details
|
hide-details
|
||||||
ref='iptEmail'
|
ref='iptEmail'
|
||||||
v-model='email'
|
v-model='email'
|
||||||
@ -34,7 +34,7 @@
|
|||||||
solo
|
solo
|
||||||
flat
|
flat
|
||||||
prepend-icon='mdi-textbox-password'
|
prepend-icon='mdi-textbox-password'
|
||||||
background-color='grey lighten-4'
|
:background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
|
||||||
ref='iptPassword'
|
ref='iptPassword'
|
||||||
v-model='password'
|
v-model='password'
|
||||||
:append-icon='hidePassword ? "mdi-eye-off" : "mdi-eye"'
|
:append-icon='hidePassword ? "mdi-eye-off" : "mdi-eye"'
|
||||||
@ -50,7 +50,7 @@
|
|||||||
solo
|
solo
|
||||||
flat
|
flat
|
||||||
prepend-icon='mdi-textbox-password'
|
prepend-icon='mdi-textbox-password'
|
||||||
background-color='grey lighten-4'
|
:background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
|
||||||
hide-details
|
hide-details
|
||||||
ref='iptVerifyPassword'
|
ref='iptVerifyPassword'
|
||||||
v-model='verifyPassword'
|
v-model='verifyPassword'
|
||||||
@ -63,7 +63,7 @@
|
|||||||
solo
|
solo
|
||||||
flat
|
flat
|
||||||
prepend-icon='mdi-account'
|
prepend-icon='mdi-account'
|
||||||
background-color='grey lighten-4'
|
:background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
|
||||||
ref='iptName'
|
ref='iptName'
|
||||||
v-model='name'
|
v-model='name'
|
||||||
:placeholder='$t("auth:fields.name")'
|
:placeholder='$t("auth:fields.name")'
|
||||||
@ -85,14 +85,14 @@
|
|||||||
) {{ $t('auth:actions.register') }}
|
) {{ $t('auth:actions.register') }}
|
||||||
v-spacer
|
v-spacer
|
||||||
v-divider
|
v-divider
|
||||||
v-card-actions.py-3.grey.lighten-4
|
v-card-actions.py-3.grey(:class='$vuetify.theme.dark ? `darken-4-l1` : `lighten-4`')
|
||||||
v-spacer
|
v-spacer
|
||||||
i18next.caption(path='auth:switchToLogin.text', tag='div')
|
i18next.caption(path='auth:switchToLogin.text', tag='div')
|
||||||
a.caption(href='/login', place='link') {{ $t('auth:switchToLogin.link') }}
|
a.caption(href='/login', place='link') {{ $t('auth:switchToLogin.link') }}
|
||||||
v-spacer
|
v-spacer
|
||||||
|
|
||||||
loader(v-model='isLoading', :mode='loaderMode', :icon='loaderIcon', :color='loaderColor', :title='loaderTitle', :subtitle='loaderSubtitle')
|
loader(v-model='isLoading', :mode='loaderMode', :icon='loaderIcon', :color='loaderColor', :title='loaderTitle', :subtitle='loaderSubtitle')
|
||||||
nav-footer(color='grey darken-4', dark-color='grey darken-4')
|
nav-footer(color='grey darken-5', dark-color='grey darken-5')
|
||||||
notify
|
notify
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -29,9 +29,8 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
company: get('site/company'),
|
company: get('site/company'),
|
||||||
contentLicense: get('site/contentLicense'),
|
contentLicense: get('site/contentLicense'),
|
||||||
darkMode: get('site/dark'),
|
|
||||||
bgColor() {
|
bgColor() {
|
||||||
if (!this.darkMode) {
|
if (!this.$vuetify.theme.dark) {
|
||||||
return this.color
|
return this.color
|
||||||
} else {
|
} else {
|
||||||
return this.darkColor
|
return this.darkColor
|
||||||
|
72
package.json
72
package.json
@ -48,13 +48,13 @@
|
|||||||
"apollo-server": "2.11.0",
|
"apollo-server": "2.11.0",
|
||||||
"apollo-server-express": "2.11.0",
|
"apollo-server-express": "2.11.0",
|
||||||
"auto-load": "3.0.4",
|
"auto-load": "3.0.4",
|
||||||
"aws-sdk": "2.639.0",
|
"aws-sdk": "2.653.0",
|
||||||
"azure-search-client": "3.1.5",
|
"azure-search-client": "3.1.5",
|
||||||
"bcryptjs-then": "1.0.1",
|
"bcryptjs-then": "1.0.1",
|
||||||
"bluebird": "3.7.2",
|
"bluebird": "3.7.2",
|
||||||
"body-parser": "1.19.0",
|
"body-parser": "1.19.0",
|
||||||
"brute-knex": "4.0.0",
|
"brute-knex": "4.0.0",
|
||||||
"chalk": "3.0.0",
|
"chalk": "4.0.0",
|
||||||
"cheerio": "1.0.0-rc.3",
|
"cheerio": "1.0.0-rc.3",
|
||||||
"chokidar": "3.3.1",
|
"chokidar": "3.3.1",
|
||||||
"clean-css": "4.2.3",
|
"clean-css": "4.2.3",
|
||||||
@ -75,7 +75,7 @@
|
|||||||
"express-session": "1.17.0",
|
"express-session": "1.17.0",
|
||||||
"file-type": "14.1.4",
|
"file-type": "14.1.4",
|
||||||
"filesize": "6.1.0",
|
"filesize": "6.1.0",
|
||||||
"fs-extra": "8.1.0",
|
"fs-extra": "9.0.0",
|
||||||
"getos": "3.1.5",
|
"getos": "3.1.5",
|
||||||
"graphql": "14.6.0",
|
"graphql": "14.6.0",
|
||||||
"graphql-list-fields": "2.0.2",
|
"graphql-list-fields": "2.0.2",
|
||||||
@ -84,7 +84,7 @@
|
|||||||
"graphql-tools": "4.0.7",
|
"graphql-tools": "4.0.7",
|
||||||
"he": "1.2.0",
|
"he": "1.2.0",
|
||||||
"highlight.js": "9.18.1",
|
"highlight.js": "9.18.1",
|
||||||
"i18next": "19.3.2",
|
"i18next": "19.3.4",
|
||||||
"i18next-express-middleware": "1.9.1",
|
"i18next-express-middleware": "1.9.1",
|
||||||
"i18next-node-fs-backend": "2.1.3",
|
"i18next-node-fs-backend": "2.1.3",
|
||||||
"image-size": "0.8.3",
|
"image-size": "0.8.3",
|
||||||
@ -94,7 +94,7 @@
|
|||||||
"jsonwebtoken": "8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
"katex": "0.11.1",
|
"katex": "0.11.1",
|
||||||
"klaw": "3.0.0",
|
"klaw": "3.0.0",
|
||||||
"knex": "0.20.11",
|
"knex": "0.20.13",
|
||||||
"lodash": "4.17.15",
|
"lodash": "4.17.15",
|
||||||
"markdown-it": "10.0.0",
|
"markdown-it": "10.0.0",
|
||||||
"markdown-it-abbr": "1.0.4",
|
"markdown-it-abbr": "1.0.4",
|
||||||
@ -118,10 +118,10 @@
|
|||||||
"mssql": "6.2.0",
|
"mssql": "6.2.0",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.2",
|
||||||
"mysql2": "2.1.0",
|
"mysql2": "2.1.0",
|
||||||
"nanoid": "2.1.11",
|
"nanoid": "3.0.2",
|
||||||
"node-2fa": "1.1.2",
|
"node-2fa": "1.1.2",
|
||||||
"node-cache": "5.1.0",
|
"node-cache": "5.1.0",
|
||||||
"nodemailer": "6.4.5",
|
"nodemailer": "6.4.6",
|
||||||
"objection": "2.1.3",
|
"objection": "2.1.3",
|
||||||
"passport": "0.4.1",
|
"passport": "0.4.1",
|
||||||
"passport-auth0": "1.3.2",
|
"passport-auth0": "1.3.2",
|
||||||
@ -130,7 +130,7 @@
|
|||||||
"passport-discord": "0.1.3",
|
"passport-discord": "0.1.3",
|
||||||
"passport-dropbox-oauth2": "1.1.0",
|
"passport-dropbox-oauth2": "1.1.0",
|
||||||
"passport-facebook": "3.0.0",
|
"passport-facebook": "3.0.0",
|
||||||
"passport-github2": "0.1.11",
|
"passport-github2": "0.1.12",
|
||||||
"passport-gitlab2": "5.0.0",
|
"passport-gitlab2": "5.0.0",
|
||||||
"passport-google-oauth20": "2.0.0",
|
"passport-google-oauth20": "2.0.0",
|
||||||
"passport-jwt": "4.0.0",
|
"passport-jwt": "4.0.0",
|
||||||
@ -143,9 +143,9 @@
|
|||||||
"passport-saml": "1.3.3",
|
"passport-saml": "1.3.3",
|
||||||
"passport-twitch-oauth": "1.0.0",
|
"passport-twitch-oauth": "1.0.0",
|
||||||
"pem-jwk": "2.0.0",
|
"pem-jwk": "2.0.0",
|
||||||
"pg": "7.18.2",
|
"pg": "8.0.0",
|
||||||
"pg-hstore": "2.3.3",
|
"pg-hstore": "2.3.3",
|
||||||
"pg-query-stream": "3.0.3",
|
"pg-query-stream": "3.0.4",
|
||||||
"pg-tsquery": "8.1.0",
|
"pg-tsquery": "8.1.0",
|
||||||
"pug": "2.0.4",
|
"pug": "2.0.4",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
@ -162,22 +162,22 @@
|
|||||||
"simple-git": "1.132.0",
|
"simple-git": "1.132.0",
|
||||||
"solr-node": "1.2.1",
|
"solr-node": "1.2.1",
|
||||||
"sqlite3": "4.1.1",
|
"sqlite3": "4.1.1",
|
||||||
"ssh2": "0.8.8",
|
"ssh2": "0.8.9",
|
||||||
"ssh2-promise": "0.1.6",
|
"ssh2-promise": "0.1.6",
|
||||||
"striptags": "3.1.1",
|
"striptags": "3.1.1",
|
||||||
"subscriptions-transport-ws": "0.9.16",
|
"subscriptions-transport-ws": "0.9.16",
|
||||||
"tar-fs": "2.0.0",
|
"tar-fs": "2.0.1",
|
||||||
"twemoji": "12.1.5",
|
"twemoji": "12.1.5",
|
||||||
"uslug": "1.0.4",
|
"uslug": "1.0.4",
|
||||||
"uuid": "7.0.2",
|
"uuid": "7.0.3",
|
||||||
"validate.js": "0.13.1",
|
"validate.js": "0.13.1",
|
||||||
"winston": "3.2.1",
|
"winston": "3.2.1",
|
||||||
"xss": "1.0.6",
|
"xss": "1.0.6",
|
||||||
"yargs": "15.3.0"
|
"yargs": "15.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.8.4",
|
"@babel/cli": "^7.8.4",
|
||||||
"@babel/core": "^7.8.7",
|
"@babel/core": "^7.9.0",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||||
"@babel/plugin-proposal-decorators": "^7.8.3",
|
"@babel/plugin-proposal-decorators": "^7.8.3",
|
||||||
"@babel/plugin-proposal-export-namespace-from": "^7.8.3",
|
"@babel/plugin-proposal-export-namespace-from": "^7.8.3",
|
||||||
@ -188,11 +188,11 @@
|
|||||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/plugin-syntax-import-meta": "^7.8.3",
|
"@babel/plugin-syntax-import-meta": "^7.8.3",
|
||||||
"@babel/polyfill": "^7.8.7",
|
"@babel/polyfill": "^7.8.7",
|
||||||
"@babel/preset-env": "^7.8.7",
|
"@babel/preset-env": "^7.9.0",
|
||||||
"@mdi/font": "5.0.45",
|
"@mdi/font": "5.0.45",
|
||||||
"@panter/vue-i18next": "0.15.2",
|
"@panter/vue-i18next": "0.15.2",
|
||||||
"@requarks/ckeditor5": "12.4.0-wiki.14",
|
"@requarks/ckeditor5": "12.4.0-wiki.14",
|
||||||
"@vue/babel-preset-app": "4.2.3",
|
"@vue/babel-preset-app": "4.3.0",
|
||||||
"animate-sass": "0.8.2",
|
"animate-sass": "0.8.2",
|
||||||
"animated-number-vue": "1.0.0",
|
"animated-number-vue": "1.0.0",
|
||||||
"apollo-cache-inmemory": "1.6.5",
|
"apollo-cache-inmemory": "1.6.5",
|
||||||
@ -204,10 +204,10 @@
|
|||||||
"apollo-link-persisted-queries": "0.2.2",
|
"apollo-link-persisted-queries": "0.2.2",
|
||||||
"apollo-link-ws": "1.0.19",
|
"apollo-link-ws": "1.0.19",
|
||||||
"apollo-utilities": "1.3.3",
|
"apollo-utilities": "1.3.3",
|
||||||
"autoprefixer": "9.7.4",
|
"autoprefixer": "9.7.5",
|
||||||
"babel-eslint": "10.1.0",
|
"babel-eslint": "10.1.0",
|
||||||
"babel-jest": "25.1.0",
|
"babel-jest": "25.2.6",
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.1.0",
|
||||||
"babel-plugin-graphql-tag": "2.5.0",
|
"babel-plugin-graphql-tag": "2.5.0",
|
||||||
"babel-plugin-lodash": "3.3.4",
|
"babel-plugin-lodash": "3.3.4",
|
||||||
"babel-plugin-prismjs": "2.0.1",
|
"babel-plugin-prismjs": "2.0.1",
|
||||||
@ -216,37 +216,37 @@
|
|||||||
"chart.js": "2.9.3",
|
"chart.js": "2.9.3",
|
||||||
"clean-webpack-plugin": "3.0.0",
|
"clean-webpack-plugin": "3.0.0",
|
||||||
"clipboard": "2.0.6",
|
"clipboard": "2.0.6",
|
||||||
"codemirror": "5.52.0",
|
"codemirror": "5.52.2",
|
||||||
"copy-webpack-plugin": "5.1.1",
|
"copy-webpack-plugin": "5.1.1",
|
||||||
"core-js": "3.6.4",
|
"core-js": "3.6.4",
|
||||||
"css-loader": "3.4.2",
|
"css-loader": "3.4.2",
|
||||||
"cssnano": "4.1.10",
|
"cssnano": "4.1.10",
|
||||||
"d3": "5.15.0",
|
"d3": "5.15.1",
|
||||||
"duplicate-package-checker-webpack-plugin": "3.0.0",
|
"duplicate-package-checker-webpack-plugin": "3.0.0",
|
||||||
"epic-spinners": "1.1.0",
|
"epic-spinners": "1.1.0",
|
||||||
"eslint": "6.8.0",
|
"eslint": "6.8.0",
|
||||||
"eslint-config-requarks": "1.0.7",
|
"eslint-config-requarks": "1.0.7",
|
||||||
"eslint-config-standard": "14.1.0",
|
"eslint-config-standard": "14.1.1",
|
||||||
"eslint-plugin-import": "2.20.1",
|
"eslint-plugin-import": "2.20.2",
|
||||||
"eslint-plugin-node": "11.0.0",
|
"eslint-plugin-node": "11.1.0",
|
||||||
"eslint-plugin-promise": "4.2.1",
|
"eslint-plugin-promise": "4.2.1",
|
||||||
"eslint-plugin-standard": "4.0.1",
|
"eslint-plugin-standard": "4.0.1",
|
||||||
"eslint-plugin-vue": "6.2.2",
|
"eslint-plugin-vue": "6.2.2",
|
||||||
"fibers": "4.0.2",
|
"fibers": "4.0.2",
|
||||||
"file-loader": "5.1.0",
|
"file-loader": "6.0.0",
|
||||||
"filepond": "4.13.0",
|
"filepond": "4.13.0",
|
||||||
"filepond-plugin-file-validate-type": "1.2.4",
|
"filepond-plugin-file-validate-type": "1.2.5",
|
||||||
"filesize.js": "2.0.0",
|
"filesize.js": "2.0.0",
|
||||||
"graphql-persisted-document-loader": "2.0.0",
|
"graphql-persisted-document-loader": "2.0.0",
|
||||||
"graphql-tag": "^2.10.3",
|
"graphql-tag": "^2.10.3",
|
||||||
"hammerjs": "2.0.8",
|
"hammerjs": "2.0.8",
|
||||||
"html-webpack-plugin": "4.0.0-beta.8",
|
"html-webpack-plugin": "4.0.4",
|
||||||
"html-webpack-pug-plugin": "2.0.0",
|
"html-webpack-pug-plugin": "2.0.0",
|
||||||
"i18next-chained-backend": "2.0.1",
|
"i18next-chained-backend": "2.0.1",
|
||||||
"i18next-localstorage-backend": "3.1.1",
|
"i18next-localstorage-backend": "3.1.1",
|
||||||
"i18next-xhr-backend": "3.2.2",
|
"i18next-xhr-backend": "3.2.2",
|
||||||
"ignore-loader": "0.1.2",
|
"ignore-loader": "0.1.2",
|
||||||
"jest": "25.1.0",
|
"jest": "25.2.7",
|
||||||
"js-cookie": "2.2.1",
|
"js-cookie": "2.2.1",
|
||||||
"mermaid": "8.4.8",
|
"mermaid": "8.4.8",
|
||||||
"mini-css-extract-plugin": "0.9.0",
|
"mini-css-extract-plugin": "0.9.0",
|
||||||
@ -260,7 +260,7 @@
|
|||||||
"postcss-loader": "3.0.0",
|
"postcss-loader": "3.0.0",
|
||||||
"postcss-preset-env": "6.7.0",
|
"postcss-preset-env": "6.7.0",
|
||||||
"postcss-selector-parser": "6.0.2",
|
"postcss-selector-parser": "6.0.2",
|
||||||
"prismjs": "1.19.0",
|
"prismjs": "1.20.0",
|
||||||
"pug-lint": "2.6.0",
|
"pug-lint": "2.6.0",
|
||||||
"pug-loader": "2.4.0",
|
"pug-loader": "2.4.0",
|
||||||
"pug-plain-loader": "1.0.0",
|
"pug-plain-loader": "1.0.0",
|
||||||
@ -272,9 +272,9 @@
|
|||||||
"script-ext-html-webpack-plugin": "2.1.4",
|
"script-ext-html-webpack-plugin": "2.1.4",
|
||||||
"simple-progress-webpack-plugin": "1.1.2",
|
"simple-progress-webpack-plugin": "1.1.2",
|
||||||
"style-loader": "1.1.3",
|
"style-loader": "1.1.3",
|
||||||
"terser": "4.6.6",
|
"terser": "4.6.10",
|
||||||
"twemoji-awesome": "1.0.6",
|
"twemoji-awesome": "1.0.6",
|
||||||
"url-loader": "3.0.0",
|
"url-loader": "4.0.0",
|
||||||
"velocity-animate": "1.5.2",
|
"velocity-animate": "1.5.2",
|
||||||
"viz.js": "2.1.2",
|
"viz.js": "2.1.2",
|
||||||
"vue": "2.6.11",
|
"vue": "2.6.11",
|
||||||
@ -283,7 +283,7 @@
|
|||||||
"vue-clipboards": "1.3.0",
|
"vue-clipboards": "1.3.0",
|
||||||
"vue-filepond": "6.0.2",
|
"vue-filepond": "6.0.2",
|
||||||
"vue-hot-reload-api": "2.3.4",
|
"vue-hot-reload-api": "2.3.4",
|
||||||
"vue-loader": "15.9.0",
|
"vue-loader": "15.9.1",
|
||||||
"vue-moment": "4.1.0",
|
"vue-moment": "4.1.0",
|
||||||
"vue-router": "3.1.6",
|
"vue-router": "3.1.6",
|
||||||
"vue-status-indicator": "1.2.1",
|
"vue-status-indicator": "1.2.1",
|
||||||
@ -291,12 +291,12 @@
|
|||||||
"vue2-animate": "2.1.3",
|
"vue2-animate": "2.1.3",
|
||||||
"vuedraggable": "2.23.2",
|
"vuedraggable": "2.23.2",
|
||||||
"vuescroll": "4.15.0",
|
"vuescroll": "4.15.0",
|
||||||
"vuetify": "2.2.17",
|
"vuetify": "2.2.20",
|
||||||
"vuetify-loader": "1.4.3",
|
"vuetify-loader": "1.4.3",
|
||||||
"vuex": "3.1.3",
|
"vuex": "3.1.3",
|
||||||
"vuex-pathify": "1.4.1",
|
"vuex-pathify": "1.4.1",
|
||||||
"vuex-persistedstate": "2.7.1",
|
"vuex-persistedstate": "3.0.1",
|
||||||
"webpack": "4.42.0",
|
"webpack": "4.42.1",
|
||||||
"webpack-bundle-analyzer": "3.6.1",
|
"webpack-bundle-analyzer": "3.6.1",
|
||||||
"webpack-cli": "3.3.11",
|
"webpack-cli": "3.3.11",
|
||||||
"webpack-dev-middleware": "3.7.2",
|
"webpack-dev-middleware": "3.7.2",
|
||||||
|
@ -278,6 +278,10 @@ router.get(['/i', '/i/:id'], async (req, res, next) => {
|
|||||||
* Profile
|
* Profile
|
||||||
*/
|
*/
|
||||||
router.get(['/p', '/p/*'], (req, res, next) => {
|
router.get(['/p', '/p/*'], (req, res, next) => {
|
||||||
|
if (!req.user || req.user.id < 1 || req.user.id === 2) {
|
||||||
|
return res.render('unauthorized', { action: 'view' })
|
||||||
|
}
|
||||||
|
|
||||||
_.set(res.locals, 'pageMeta.title', 'User Profile')
|
_.set(res.locals, 'pageMeta.title', 'User Profile')
|
||||||
res.render('profile')
|
res.render('profile')
|
||||||
})
|
})
|
||||||
|
8
server/db/migrations-sqlite/2.3.10.js
Normal file
8
server/db/migrations-sqlite/2.3.10.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
exports.up = knex => {
|
||||||
|
return knex.schema
|
||||||
|
.alterTable('users', table => {
|
||||||
|
table.string('lastLoginAt')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = knex => { }
|
8
server/db/migrations/2.3.10.js
Normal file
8
server/db/migrations/2.3.10.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
exports.up = knex => {
|
||||||
|
return knex.schema
|
||||||
|
.alterTable('users', table => {
|
||||||
|
table.string('lastLoginAt')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = knex => { }
|
@ -9,7 +9,7 @@ const moment = require('moment')
|
|||||||
const graphHelper = require('../../helpers/graph')
|
const graphHelper = require('../../helpers/graph')
|
||||||
const request = require('request-promise')
|
const request = require('request-promise')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const nanoid = require('nanoid/non-secure/generate')
|
const nanoid = require('nanoid/non-secure').customAlphabet('1234567890abcdef', 10)
|
||||||
|
|
||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ module.exports = {
|
|||||||
roles = _.concat(roles, ['write:pages', 'manage:pages', 'read:source', 'read:history', 'write:assets', 'manage:assets'])
|
roles = _.concat(roles, ['write:pages', 'manage:pages', 'read:source', 'read:history', 'write:assets', 'manage:assets'])
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
id: nanoid('1234567890abcdef', 10),
|
id: nanoid(),
|
||||||
roles: roles,
|
roles: roles,
|
||||||
match: r.exact ? 'EXACT' : 'START',
|
match: r.exact ? 'EXACT' : 'START',
|
||||||
deny: r.deny,
|
deny: r.deny,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const graphHelper = require('../../helpers/graph')
|
const graphHelper = require('../../helpers/graph')
|
||||||
|
const _ = require('lodash')
|
||||||
|
|
||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
@ -35,15 +36,16 @@ module.exports = {
|
|||||||
if (!usr.isActive) {
|
if (!usr.isActive) {
|
||||||
throw new WIKI.Error.AuthAccountBanned()
|
throw new WIKI.Error.AuthAccountBanned()
|
||||||
}
|
}
|
||||||
const usrGroups = await usr.$relatedQuery('groups')
|
|
||||||
return {
|
const providerInfo = _.find(WIKI.data.authentication, ['key', usr.providerKey])
|
||||||
...usr,
|
|
||||||
password: '',
|
usr.providerName = _.get(providerInfo, 'title', 'Unknown')
|
||||||
providerKey: '',
|
usr.lastLoginAt = usr.lastLoginAt || usr.updatedAt
|
||||||
tfaSecret: '',
|
usr.password = ''
|
||||||
lastLoginOn: '1970-01-01',
|
usr.providerId = ''
|
||||||
groups: usrGroups.map(g => g.name)
|
usr.tfaSecret = ''
|
||||||
}
|
|
||||||
|
return usr
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UserMutation: {
|
UserMutation: {
|
||||||
@ -124,11 +126,88 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
resetPassword (obj, args) {
|
resetPassword (obj, args) {
|
||||||
return false
|
return false
|
||||||
|
},
|
||||||
|
async updateProfile (obj, args, context) {
|
||||||
|
try {
|
||||||
|
if (!context.req.user || context.req.user.id < 1 || context.req.user.id === 2) {
|
||||||
|
throw new WIKI.Error.AuthRequired()
|
||||||
|
}
|
||||||
|
const usr = await WIKI.models.users.query().findById(context.req.user.id)
|
||||||
|
if (!usr.isActive) {
|
||||||
|
throw new WIKI.Error.AuthAccountBanned()
|
||||||
|
}
|
||||||
|
if (!usr.isVerified) {
|
||||||
|
throw new WIKI.Error.AuthAccountNotVerified()
|
||||||
|
}
|
||||||
|
|
||||||
|
await WIKI.models.users.updateUser({
|
||||||
|
id: usr.id,
|
||||||
|
name: _.trim(args.name),
|
||||||
|
jobTitle: _.trim(args.jobTitle),
|
||||||
|
location: _.trim(args.location),
|
||||||
|
timezone: args.timezone
|
||||||
|
})
|
||||||
|
|
||||||
|
const newToken = await WIKI.models.users.refreshToken(usr.id)
|
||||||
|
|
||||||
|
return {
|
||||||
|
responseResult: graphHelper.generateSuccess('User profile updated successfully'),
|
||||||
|
jwt: newToken.token
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return graphHelper.generateError(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async changePassword (obj, args, context) {
|
||||||
|
try {
|
||||||
|
if (!context.req.user || context.req.user.id < 1 || context.req.user.id === 2) {
|
||||||
|
throw new WIKI.Error.AuthRequired()
|
||||||
|
}
|
||||||
|
const usr = await WIKI.models.users.query().findById(context.req.user.id)
|
||||||
|
if (!usr.isActive) {
|
||||||
|
throw new WIKI.Error.AuthAccountBanned()
|
||||||
|
}
|
||||||
|
if (!usr.isVerified) {
|
||||||
|
throw new WIKI.Error.AuthAccountNotVerified()
|
||||||
|
}
|
||||||
|
if (usr.providerKey !== 'local') {
|
||||||
|
throw new WIKI.Error.AuthProviderInvalid()
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await usr.verifyPassword(args.current)
|
||||||
|
} catch (err) {
|
||||||
|
throw new WIKI.Error.AuthPasswordInvalid()
|
||||||
|
}
|
||||||
|
|
||||||
|
await WIKI.models.users.updateUser({
|
||||||
|
id: usr.id,
|
||||||
|
newPassword: args.new
|
||||||
|
})
|
||||||
|
|
||||||
|
const newToken = await WIKI.models.users.refreshToken(usr)
|
||||||
|
|
||||||
|
return {
|
||||||
|
responseResult: graphHelper.generateSuccess('Password changed successfully'),
|
||||||
|
jwt: newToken.token
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return graphHelper.generateError(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
User: {
|
User: {
|
||||||
groups(usr) {
|
groups (usr) {
|
||||||
return usr.$relatedQuery('groups')
|
return usr.$relatedQuery('groups')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
UserProfile: {
|
||||||
|
async groups (usr) {
|
||||||
|
const usrGroups = await usr.$relatedQuery('groups')
|
||||||
|
return usrGroups.map(g => g.name)
|
||||||
|
},
|
||||||
|
async pagesTotal (usr) {
|
||||||
|
const result = await WIKI.models.pages.query().count('* as total').where('creatorId', usr.id).first()
|
||||||
|
return _.toSafeInteger(result.total)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,18 @@ type UserMutation {
|
|||||||
resetPassword(
|
resetPassword(
|
||||||
id: Int!
|
id: Int!
|
||||||
): DefaultResponse
|
): DefaultResponse
|
||||||
|
|
||||||
|
updateProfile(
|
||||||
|
name: String!
|
||||||
|
location: String!
|
||||||
|
jobTitle: String!
|
||||||
|
timezone: String!
|
||||||
|
): UserTokenResponse
|
||||||
|
|
||||||
|
changePassword(
|
||||||
|
current: String!
|
||||||
|
new: String!
|
||||||
|
): UserTokenResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
@ -117,6 +129,7 @@ type UserProfile {
|
|||||||
id: Int!
|
id: Int!
|
||||||
name: String!
|
name: String!
|
||||||
email: String!
|
email: String!
|
||||||
|
providerKey: String
|
||||||
providerName: String
|
providerName: String
|
||||||
isSystem: Boolean!
|
isSystem: Boolean!
|
||||||
isVerified: Boolean!
|
isVerified: Boolean!
|
||||||
@ -125,6 +138,12 @@ type UserProfile {
|
|||||||
timezone: String!
|
timezone: String!
|
||||||
createdAt: Date!
|
createdAt: Date!
|
||||||
updatedAt: Date!
|
updatedAt: Date!
|
||||||
lastLoginOn: Date!
|
lastLoginAt: Date
|
||||||
groups: [String]!
|
groups: [String]!
|
||||||
|
pagesTotal: Int!
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserTokenResponse {
|
||||||
|
responseResult: ResponseStatus!
|
||||||
|
jwt: String
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,10 @@ module.exports = {
|
|||||||
message: 'Invalid email / username or password.',
|
message: 'Invalid email / username or password.',
|
||||||
code: 1002
|
code: 1002
|
||||||
}),
|
}),
|
||||||
|
AuthPasswordInvalid: CustomError('AuthPasswordInvalid', {
|
||||||
|
message: 'Password is incorrect.',
|
||||||
|
code: 1020
|
||||||
|
}),
|
||||||
AuthProviderInvalid: CustomError('AuthProviderInvalid', {
|
AuthProviderInvalid: CustomError('AuthProviderInvalid', {
|
||||||
message: 'Invalid authentication provider.',
|
message: 'Invalid authentication provider.',
|
||||||
code: 1003
|
code: 1003
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const Model = require('objection').Model
|
const Model = require('objection').Model
|
||||||
const moment = require('moment')
|
const moment = require('moment')
|
||||||
const nanoid = require('nanoid')
|
const nanoid = require('nanoid').nanoid
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users model
|
* Users model
|
||||||
|
@ -341,6 +341,9 @@ module.exports = class User extends Model {
|
|||||||
user.groups = await user.$relatedQuery('groups').select('groups.id', 'permissions')
|
user.groups = await user.$relatedQuery('groups').select('groups.id', 'permissions')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update Last Login Date
|
||||||
|
await WIKI.models.users.query().findById(user.id).patch({ lastLoginAt: new Date().toISOString() })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token: jwt.sign({
|
token: jwt.sign({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
|
Loading…
Reference in New Issue
Block a user