Reworked user and session store, added Admin function for user.
* Sync Login with backend * Split Main into MainUserSettins and Settings * Added AdminSetting to change other users, added UserSelector Component for selecting users (can be reused for other stuff ;-) ). * Split hasPermission into helper file for code reuse
This commit is contained in:
parent
5c11e02b2c
commit
8689e84d47
|
@ -5,16 +5,8 @@ import { Store } from 'vuex';
|
|||
|
||||
export default boot<Store<StateInterface>>(({ router, store }) => {
|
||||
router.beforeEach((to, from, next) => {
|
||||
const user = store.state.user.currentUser;
|
||||
const session = store.state.session.currentSession;
|
||||
|
||||
let permissions: string[] = [];
|
||||
if (user) {
|
||||
user.roles.forEach(role => {
|
||||
permissions = permissions.concat(role.permissions);
|
||||
});
|
||||
}
|
||||
|
||||
if (to.name != 'login') {
|
||||
if (!session || session.expires <= new Date()) {
|
||||
store.dispatch('session/logout').catch(error => {
|
||||
|
@ -32,7 +24,7 @@ export default boot<Store<StateInterface>>(({ router, store }) => {
|
|||
return (<{ permissions: FG.Permission[] }>(
|
||||
record.meta
|
||||
)).permissions.every((permission: string) => {
|
||||
return permissions.includes(permission);
|
||||
return store.state.user.currentPermissions.includes(permission);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
import { computed, defineComponent } from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
import { hasPermissions } from 'src/components/permission';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'EssentialLink',
|
||||
|
@ -65,17 +66,9 @@ export default defineComponent({
|
|||
return props.title;
|
||||
});
|
||||
|
||||
const hasPermissions = computed(() => {
|
||||
let permissions = props.permissions;
|
||||
if (permissions) {
|
||||
return (<string[]>permissions).every(permission => {
|
||||
return (<{ 'user/permissions': string[] }>(
|
||||
(<Store<StateInterface>>root.$store).getters
|
||||
))['user/permissions'].includes(permission);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
const isGranted = computed(() =>
|
||||
hasPermissions(props.permissions || [], root.$store)
|
||||
);
|
||||
|
||||
return { realTitle: title, hasPermissions };
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<template>
|
||||
<q-btn flat dense :icon="icon" :to="{ name: link }" v-if="hasPermissions" />
|
||||
<q-btn flat dense :icon="icon" :to="{ name: link }" v-if="isGranted" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
import { hasPermissions } from 'src/components/permission';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ShortCutLink',
|
||||
|
@ -23,18 +24,10 @@ export default defineComponent({
|
|||
}
|
||||
},
|
||||
setup(props, { root }) {
|
||||
const hasPermissions = computed(() => {
|
||||
let permissions = props.permissions;
|
||||
if (permissions) {
|
||||
return (<string[]>permissions).every(permission => {
|
||||
return (<{ 'user/permissions': string[] }>(
|
||||
(<Store<StateInterface>>root.$store).getters
|
||||
))['user/permissions'].includes(permission);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return { hasPermissions };
|
||||
const isGranted = computed(() =>
|
||||
hasPermissions(props.permissions || [], root.$store)
|
||||
);
|
||||
return { isGranted };
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
|
||||
export function hasPermission(
|
||||
permission: string,
|
||||
store: Store<StateInterface>
|
||||
) {
|
||||
return store.state.user.currentPermissions.includes(permission);
|
||||
}
|
||||
|
||||
export function hasPermissions(needed: string[], store: Store<StateInterface>) {
|
||||
const permissions = store.state.user.currentPermissions;
|
||||
return needed.every(value => permissions.includes(value));
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<q-select
|
||||
filled
|
||||
label="Benutzer"
|
||||
@input="updated"
|
||||
v-model="user"
|
||||
:options="users"
|
||||
option-label="display_name"
|
||||
option-value="userid"
|
||||
map-options
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
|
||||
interface Props {
|
||||
user: FG.User;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UserSelector',
|
||||
props: ['user'],
|
||||
setup(props: Props, { root, emit }) {
|
||||
const store = <Store<StateInterface>>root.$store;
|
||||
const users = computed(() => store.state.user.users);
|
||||
const user = ref(props.user);
|
||||
const updated = (value: FG.User) => {
|
||||
emit('update:user', value);
|
||||
};
|
||||
|
||||
return {
|
||||
user,
|
||||
updated,
|
||||
users
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -1,191 +0,0 @@
|
|||
<template>
|
||||
<q-card class="col-12">
|
||||
<q-linear-progress indeterminate rounded color="primary" v-if="loading" />
|
||||
<q-form @submit="save" @reset="reset">
|
||||
<q-card-section class="fit row justify-start content-center items-center">
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Vorname"
|
||||
:rules="[notEmpty]"
|
||||
v-model="firstname"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Nachname"
|
||||
:rules="[notEmpty]"
|
||||
v-model="lastname"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Benutzername"
|
||||
readonly
|
||||
:value="user.userid"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="E-Mail"
|
||||
:rules="[isEmail, notEmpty]"
|
||||
v-model="mail"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Display Name"
|
||||
:rules="[notEmpty]"
|
||||
v-model="display_name"
|
||||
filled
|
||||
/>
|
||||
<q-select
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Rollen"
|
||||
readonly
|
||||
v-model="user.roles"
|
||||
:options="user.roles"
|
||||
filled
|
||||
>
|
||||
<template v-slot:selected-item="scope">
|
||||
<q-chip v-for="(item, index) in scope.opt" :key="'item' + index">
|
||||
{{ item.name }}
|
||||
</q-chip>
|
||||
</template>
|
||||
</q-select>
|
||||
</q-card-section>
|
||||
<q-separator />
|
||||
<q-card-section class="fit row justify-start content-center items-center">
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
|
||||
label="Password"
|
||||
type="password"
|
||||
hint="Password muss immer eingetragen werden"
|
||||
:rules="[notEmpty]"
|
||||
v-model="password"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
|
||||
label="Neues Password"
|
||||
type="password"
|
||||
v-model="new_password"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
|
||||
label="Wiederhole neues Password"
|
||||
type="password"
|
||||
:disable="new_password.length == 0"
|
||||
:rules="[samePassword]"
|
||||
v-model="new_password2"
|
||||
filled
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-actions align="right">
|
||||
<q-btn label="test" @click="$store.dispatch('user/getUser')" />
|
||||
<q-btn label="Reset" type="reset" />
|
||||
<q-btn color="primary" type="submit" label="Speichern" />
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Main',
|
||||
setup(_, { root }) {
|
||||
const store = <Store<StateInterface>>root.$store;
|
||||
|
||||
const user = computed(() => <FG.User>store.state.user.currentUser);
|
||||
|
||||
const firstname = ref(user.value?.firstname);
|
||||
const lastname = ref(user.value?.lastname);
|
||||
const mail = ref(user.value?.mail);
|
||||
const display_name = ref(user.value?.display_name);
|
||||
|
||||
const password = ref('');
|
||||
const new_password = ref('');
|
||||
const new_password2 = ref('');
|
||||
|
||||
function save() {
|
||||
let change_values: { [index: string]: string } = {
|
||||
firstname: firstname.value,
|
||||
lastname: lastname.value,
|
||||
mail: mail.value,
|
||||
display_name: display_name.value
|
||||
};
|
||||
Object.keys(change_values).forEach(key => {
|
||||
if (
|
||||
change_values[key] === (<{ [index: string]: any }>user.value)[key]
|
||||
) {
|
||||
delete change_values[key];
|
||||
}
|
||||
});
|
||||
change_values = Object.assign(change_values, {
|
||||
password: password.value
|
||||
});
|
||||
if (new_password.value != '') {
|
||||
change_values = Object.assign(change_values, {
|
||||
new_password: new_password.value
|
||||
});
|
||||
}
|
||||
store.dispatch('user/updateUser', change_values).catch(error => {
|
||||
console.warn(error);
|
||||
});
|
||||
}
|
||||
|
||||
function reset() {
|
||||
firstname.value = user.value.firstname;
|
||||
lastname.value = user.value.lastname;
|
||||
mail.value = user.value.mail;
|
||||
display_name.value = user.value.display_name;
|
||||
password.value = '';
|
||||
new_password.value = '';
|
||||
new_password2.value = '';
|
||||
}
|
||||
|
||||
function samePassword(val: string) {
|
||||
return val == new_password.value || 'Passwörter sind nicht identisch!';
|
||||
}
|
||||
|
||||
function notEmpty(val: string) {
|
||||
return !!val || 'Feld darf nicht leer sein!';
|
||||
}
|
||||
|
||||
function isEmail(val: string | null) {
|
||||
return (
|
||||
!val ||
|
||||
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w\w+)+$/.test(val) ||
|
||||
'E-Mail ist nicht valide.'
|
||||
);
|
||||
}
|
||||
|
||||
const loading = computed(() => {
|
||||
return (
|
||||
store.state.user.getUserLoading || store.state.user.updateUserLoading
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
user,
|
||||
firstname,
|
||||
lastname,
|
||||
mail,
|
||||
display_name,
|
||||
password,
|
||||
new_password,
|
||||
new_password2,
|
||||
samePassword,
|
||||
isEmail,
|
||||
notEmpty,
|
||||
save,
|
||||
reset,
|
||||
loading
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,194 @@
|
|||
<template>
|
||||
<q-form @submit="save" @reset="reset">
|
||||
<q-linear-progress indeterminate rounded color="primary" v-if="loading" />
|
||||
<q-card-section class="fit row justify-start content-center items-center">
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Vorname"
|
||||
:rules="[notEmpty]"
|
||||
v-model="props.user.firstname"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Nachname"
|
||||
:rules="[notEmpty]"
|
||||
v-model="props.user.lastname"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Benutzername"
|
||||
readonly
|
||||
:value="props.user.userid"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="E-Mail"
|
||||
:rules="[isEmail, notEmpty]"
|
||||
v-model="props.user.mail"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Display Name"
|
||||
:rules="[notEmpty]"
|
||||
v-model="props.user.display_name"
|
||||
filled
|
||||
/>
|
||||
<q-select
|
||||
class="col-xs-12 col-sm-6 q-pa-sm"
|
||||
label="Rollen"
|
||||
filled
|
||||
multiple
|
||||
use-chips
|
||||
v-model="props.user.roles"
|
||||
:readonly="() => canSetRoles()"
|
||||
:options="allRoles"
|
||||
option-label="name"
|
||||
option-value="name"
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-separator />
|
||||
<q-card-section class="fit row justify-start content-center items-center">
|
||||
<q-input
|
||||
v-if="isCurrentUser"
|
||||
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
|
||||
label="Password"
|
||||
type="password"
|
||||
hint="Password muss immer eingetragen werden"
|
||||
:rules="[notEmpty]"
|
||||
v-model="password"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
|
||||
label="Neues Password"
|
||||
type="password"
|
||||
v-model="new_password"
|
||||
filled
|
||||
/>
|
||||
<q-input
|
||||
class="col-xs-12 col-sm-6 col-md-4 q-pa-sm"
|
||||
label="Wiederhole neues Password"
|
||||
type="password"
|
||||
:disable="new_password.length == 0"
|
||||
:rules="[samePassword]"
|
||||
v-model="new_password2"
|
||||
filled
|
||||
/>
|
||||
</q-card-section>
|
||||
<q-card-actions align="right">
|
||||
<q-btn label="Reset" type="reset" />
|
||||
<q-btn color="primary" type="submit" label="Speichern" />
|
||||
</q-card-actions>
|
||||
</q-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
ref,
|
||||
onBeforeMount
|
||||
} from '@vue/composition-api';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
import { hasPermission } from 'src/components/permission';
|
||||
|
||||
interface Props {
|
||||
user?: FG.User;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MainUserSettings',
|
||||
props: ['user'],
|
||||
setup(props: Props, { root }) {
|
||||
const store = <Store<StateInterface>>root.$store;
|
||||
|
||||
onBeforeMount(() => {
|
||||
store.dispatch('user/getRoles', false).catch(error => {
|
||||
console.warn(error);
|
||||
});
|
||||
});
|
||||
|
||||
const isCurrentUser = computed(
|
||||
() => props.user?.userid === store.state.user.currentUser?.userid
|
||||
);
|
||||
|
||||
const canSetRoles = computed(() => hasPermission('users_set_roles', store));
|
||||
|
||||
const oldUser = computed(() => {
|
||||
if (isCurrentUser.value) return <FG.User>store.state.user.currentUser;
|
||||
else
|
||||
return store.state.user.users.filter(user => {
|
||||
user.userid === props.user?.userid;
|
||||
})[0];
|
||||
});
|
||||
|
||||
const allRoles = computed(() =>
|
||||
store.state.user.roles.map(role => role.name)
|
||||
);
|
||||
const password = ref('');
|
||||
const new_password = ref('');
|
||||
const new_password2 = ref('');
|
||||
|
||||
function save() {
|
||||
let changed = <FG.User>props.user;
|
||||
changed = Object.assign(changed, {
|
||||
password: password.value
|
||||
});
|
||||
if (new_password.value != '') {
|
||||
changed = Object.assign(changed, {
|
||||
new_password: new_password.value
|
||||
});
|
||||
}
|
||||
store.dispatch('user/updateUser', changed).catch(error => {
|
||||
console.warn(error);
|
||||
});
|
||||
}
|
||||
|
||||
function reset() {
|
||||
props.user = oldUser.value;
|
||||
password.value = '';
|
||||
new_password.value = '';
|
||||
new_password2.value = '';
|
||||
}
|
||||
|
||||
function samePassword(val: string) {
|
||||
return val == new_password.value || 'Passwörter sind nicht identisch!';
|
||||
}
|
||||
|
||||
function notEmpty(val: string) {
|
||||
return !!val || 'Feld darf nicht leer sein!';
|
||||
}
|
||||
|
||||
function isEmail(val: string | null) {
|
||||
return (
|
||||
!val ||
|
||||
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w\w+)+$/.test(val) ||
|
||||
'E-Mail ist nicht valide.'
|
||||
);
|
||||
}
|
||||
|
||||
const loading = computed(() => store.state.user.loading > 0);
|
||||
|
||||
return {
|
||||
props,
|
||||
allRoles,
|
||||
canSetRoles,
|
||||
password,
|
||||
new_password,
|
||||
new_password2,
|
||||
samePassword,
|
||||
isCurrentUser,
|
||||
isEmail,
|
||||
notEmpty,
|
||||
save,
|
||||
reset,
|
||||
loading
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<div>
|
||||
<q-page
|
||||
padding
|
||||
class="fit row justify-center content-center items-center q-gutter-sm"
|
||||
>
|
||||
<q-card class="col-12">
|
||||
<q-card-section
|
||||
class="fit row justify-start content-center items-center"
|
||||
>
|
||||
<div class="col-xs-12 col-sm-6 text-center text-h6">
|
||||
Benutzereinstellungen
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6 q-pa-sm">
|
||||
<UserSelector :user="user" @update:user="userUpdated" />
|
||||
</div>
|
||||
</q-card-section>
|
||||
<MainUserSettings :user="user" />
|
||||
</q-card>
|
||||
</q-page>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onBeforeMount, ref } from '@vue/composition-api';
|
||||
import UserSelector from '../components/UserSelector.vue';
|
||||
import MainUserSettings from '../components/settings/MainUserSettings.vue';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AdminSettings',
|
||||
components: { UserSelector, MainUserSettings },
|
||||
setup(_, { root }) {
|
||||
const store = <Store<StateInterface>>root.$store;
|
||||
|
||||
onBeforeMount(() => {
|
||||
store.dispatch('user/getUsers').catch(error => console.warn(error));
|
||||
});
|
||||
|
||||
const user = ref(<FG.User>store.state.user.currentUser);
|
||||
|
||||
// can be dropped with VUE3
|
||||
const userUpdated = (value: FG.User) => {
|
||||
user.value = value;
|
||||
console.log(value);
|
||||
};
|
||||
|
||||
return {
|
||||
user,
|
||||
userUpdated
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -3,42 +3,42 @@
|
|||
<q-page
|
||||
padding
|
||||
class="fit row justify-center content-center items-center q-gutter-sm"
|
||||
>
|
||||
<div
|
||||
class="fit row justify-center content-center items-center q-gutter-sm"
|
||||
>
|
||||
<circular-progress v-if="sessionsLoading" />
|
||||
<div class="col-12 text-left text-h6">
|
||||
Allgemeine Einstellungen:
|
||||
</div>
|
||||
<Main />
|
||||
<div class="col-12 text-left text-h6">
|
||||
Aktive Sessions:
|
||||
</div>
|
||||
<q-card class="col-12">
|
||||
<q-card-section
|
||||
class="fit row justify-start content-center items-center"
|
||||
>
|
||||
<div class="col-12 text-center text-h6">Benutzereinstellungen</div>
|
||||
</q-card-section>
|
||||
<MainUserSettings :user="currentUser" />
|
||||
</q-card>
|
||||
<div class="col-12 text-left text-h6">Aktive Sessions:</div>
|
||||
<sessions
|
||||
v-for="(session, index) in sessions"
|
||||
:key="'session' + index"
|
||||
:session="session"
|
||||
/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<q-btn label="show sessions" @click="showRootGetters" />
|
||||
</div>
|
||||
</q-page>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onBeforeMount } from '@vue/composition-api';
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onBeforeMount,
|
||||
ref
|
||||
} from '@vue/composition-api';
|
||||
import CircularProgress from 'components/loading/CircularProgress.vue';
|
||||
import Sessions from '../components/settings/Sessions.vue';
|
||||
import Main from '../components/settings/Main.vue';
|
||||
import MainUserSettings from '../components/settings/MainUserSettings.vue';
|
||||
import { Store } from 'vuex';
|
||||
import { StateInterface } from 'src/store';
|
||||
|
||||
export default defineComponent({
|
||||
// name: 'PageName'
|
||||
components: { CircularProgress, Sessions, Main },
|
||||
components: { CircularProgress, Sessions, MainUserSettings },
|
||||
setup(_, { root }) {
|
||||
const store = <Store<StateInterface>>root.$store;
|
||||
|
||||
|
@ -47,21 +47,13 @@ export default defineComponent({
|
|||
console.warn(error);
|
||||
});
|
||||
});
|
||||
|
||||
const currentUser = ref(<FG.User>store.state.user.currentUser);
|
||||
const sessions = computed(() => store.state.session.sessions);
|
||||
|
||||
function showRootGetters() {
|
||||
console.log(sessions.value);
|
||||
}
|
||||
|
||||
const sessionsLoading = computed(
|
||||
() =>
|
||||
store.state.session.loading ||
|
||||
store.state.user.getUserLoading ||
|
||||
store.state.user.updateUserLoading
|
||||
);
|
||||
const sessionsLoading = computed(() => store.state.session.loading);
|
||||
|
||||
return {
|
||||
showRootGetters,
|
||||
currentUser,
|
||||
sessionsLoading,
|
||||
sessions
|
||||
};
|
||||
|
|
|
@ -25,6 +25,15 @@ const mainRoutes: FG_Plugin.PluginRouteConfig[] = [
|
|||
shortcut: true,
|
||||
meta: { permissions: ['user'] },
|
||||
component: () => import('../pages/Settings.vue')
|
||||
},
|
||||
{
|
||||
title: 'Admin',
|
||||
icon: 'mdi-cog',
|
||||
path: 'admin',
|
||||
name: 'admin-settings',
|
||||
shortcut: false,
|
||||
meta: { permissions: ['users_edit_other'] },
|
||||
component: () => import('../pages/AdminSettings.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Module, MutationTree, ActionTree, GetterTree } from 'vuex';
|
|||
import { LoginData, LoginResponse } from 'src/plugins/user/models';
|
||||
import { StateInterface } from 'src/store';
|
||||
import { axios } from 'src/boot/axios';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { AxiosError, AxiosResponse } from 'axios';
|
||||
import { Router } from 'src/router';
|
||||
import { LocalStorage, Loading } from 'quasar';
|
||||
|
||||
|
@ -12,10 +12,15 @@ export interface SessionInterface {
|
|||
loading: boolean;
|
||||
}
|
||||
|
||||
function loadFromLocal() {
|
||||
const session = LocalStorage.getItem<FG.Session>('currentSession');
|
||||
if (session) session.expires = new Date(session.expires);
|
||||
return session;
|
||||
}
|
||||
|
||||
const state: SessionInterface = {
|
||||
sessions: [],
|
||||
currentSession:
|
||||
LocalStorage.getItem<FG.Session>('currentSession') || undefined,
|
||||
currentSession: loadFromLocal() || undefined,
|
||||
loading: false
|
||||
};
|
||||
|
||||
|
@ -47,12 +52,13 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
response.data.session.expires = new Date(response.data.session.expires);
|
||||
commit('setCurrentSession', response.data.session);
|
||||
commit('user/setCurrentUser', response.data.user, { root: true });
|
||||
void Router.push({ name: 'user-main' });
|
||||
})
|
||||
.catch(error => {
|
||||
console.exception(error);
|
||||
commit('user/setCurrentPermissions', response.data.permissions, {
|
||||
root: true
|
||||
});
|
||||
})
|
||||
.catch(error => console.warn(error))
|
||||
.finally(() => {
|
||||
void Router.push({ name: 'user-main' });
|
||||
Loading.hide();
|
||||
});
|
||||
},
|
||||
|
@ -70,7 +76,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
/**
|
||||
* Delete a given session
|
||||
*/
|
||||
deleteSession({ commit, rootState }, token: string | null) {
|
||||
deleteSession({ commit, dispatch, rootState }, token: string | null) {
|
||||
if (token === null) return;
|
||||
|
||||
commit('setLoading', true);
|
||||
|
@ -78,17 +84,23 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
|||
.delete(`/auth/${token}`)
|
||||
.then(() => {
|
||||
if (token === rootState.session.currentSession?.token) {
|
||||
commit('clearCurrentSession');
|
||||
commit('user/clearCurrentUser', null, { root: true });
|
||||
void Router.push({ name: 'login' });
|
||||
void dispatch('clearup');
|
||||
} else {
|
||||
commit('getSessions');
|
||||
}
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (!error.response || error.response.status != 401) throw error;
|
||||
})
|
||||
.finally(() => {
|
||||
commit('setLoading', false);
|
||||
});
|
||||
},
|
||||
clearup({ commit }) {
|
||||
commit('clearCurrentSession');
|
||||
commit('user/clearCurrentUser', null, { root: true });
|
||||
void Router.push({ name: 'login' });
|
||||
},
|
||||
/**
|
||||
* Get all sessions from current User
|
||||
*/
|
||||
|
|
|
@ -3,19 +3,25 @@ import { StateInterface } from 'src/store';
|
|||
import { axios } from 'boot/axios';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { SessionStorage } from 'quasar';
|
||||
import { CurrentUserResponse } from 'src/plugins/user/models';
|
||||
|
||||
export interface UserStateInterface {
|
||||
updateUserLoading: boolean;
|
||||
getUserLoading: boolean;
|
||||
currentUser?: FG.User;
|
||||
currentPermissions: FG.Permission[];
|
||||
users: FG.User[];
|
||||
roles: FG.Role[];
|
||||
permissions: FG.Permission[];
|
||||
loading: number;
|
||||
}
|
||||
|
||||
const state: UserStateInterface = {
|
||||
users: [],
|
||||
roles: [],
|
||||
permissions: [],
|
||||
currentUser: SessionStorage.getItem<FG.User>('currentUser') || undefined,
|
||||
updateUserLoading: false,
|
||||
getUserLoading: false
|
||||
currentPermissions:
|
||||
SessionStorage.getItem<FG.Permission[]>('currentPermissions') || [],
|
||||
loading: 0
|
||||
};
|
||||
|
||||
const mutations: MutationTree<UserStateInterface> = {
|
||||
|
@ -23,35 +29,46 @@ const mutations: MutationTree<UserStateInterface> = {
|
|||
SessionStorage.set('currentUser', data);
|
||||
state.currentUser = data;
|
||||
},
|
||||
setCurrentPermissions(state, data: FG.Permission[]) {
|
||||
SessionStorage.set('currentPermissions', data);
|
||||
state.currentPermissions = data;
|
||||
},
|
||||
clearCurrentUser(state) {
|
||||
SessionStorage.remove('currentUser');
|
||||
SessionStorage.remove('currentPermissions');
|
||||
state.currentUser = undefined;
|
||||
state.currentPermissions = [];
|
||||
},
|
||||
setUsers(state, data: FG.User[]) {
|
||||
state.users = data;
|
||||
},
|
||||
setLoading(
|
||||
state,
|
||||
data: { key: 'updateUserLoading' | 'getUserLoading'; data: boolean }
|
||||
) {
|
||||
state[data.key] = data.data;
|
||||
setRoles(state, data: FG.Role[]) {
|
||||
state.roles = data;
|
||||
},
|
||||
setPermissions(state, data: FG.Permission[]) {
|
||||
state.permissions = data;
|
||||
},
|
||||
setLoading(state, data = true) {
|
||||
if (data) state.loading += 1;
|
||||
else state.loading -= 1;
|
||||
}
|
||||
};
|
||||
|
||||
const actions: ActionTree<UserStateInterface, StateInterface> = {
|
||||
getCurrentUser({ commit, rootState }) {
|
||||
if (rootState.session.currentSession) {
|
||||
commit('setLoading', { key: 'getUserLoading', data: true });
|
||||
commit('setLoading');
|
||||
axios
|
||||
.get(`/users/${rootState.session.currentSession.userid}`)
|
||||
.then((response: AxiosResponse<FG.User>) => {
|
||||
.then((response: AxiosResponse<CurrentUserResponse>) => {
|
||||
commit('setCurrentUser', response.data);
|
||||
commit('setCurrentPermissions', response.data.permissions);
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn(err);
|
||||
})
|
||||
.finally(() => {
|
||||
commit('setLoading', { key: 'getUserLoading', data: false });
|
||||
commit('setLoading', false);
|
||||
});
|
||||
} else {
|
||||
console.debug('User not logged in, can not get current_user.');
|
||||
|
@ -60,7 +77,7 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
|
|||
|
||||
getUsers({ commit }) {
|
||||
axios
|
||||
.get(`/users`)
|
||||
.get('/users')
|
||||
.then((response: AxiosResponse<FG.User[]>) => {
|
||||
commit('setUsers', response.data);
|
||||
})
|
||||
|
@ -69,20 +86,43 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
|
|||
});
|
||||
},
|
||||
|
||||
updateUser({ commit, state, dispatch }, data) {
|
||||
commit('setLoading', { key: 'updateUserLoading', data: true });
|
||||
if (!state.currentUser) throw 'Not logged in';
|
||||
updateUser({ commit, state, dispatch }, data: FG.User) {
|
||||
commit('setLoading');
|
||||
axios
|
||||
.put(`/users/${state.currentUser.userid}`, data)
|
||||
.put(`/users/${data.userid}`, data)
|
||||
.then(() => {
|
||||
if (state.currentUser && state.currentUser.userid === data.userid)
|
||||
void dispatch('getCurrentUser');
|
||||
else void dispatch('getUsers');
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
})
|
||||
.finally(() => {
|
||||
commit('setLoading', { key: 'updateUserLoading', data: false });
|
||||
commit('setLoading', false);
|
||||
});
|
||||
},
|
||||
|
||||
getRoles({ commit, state }, force = true) {
|
||||
if (!force && state.roles.length > 0) return;
|
||||
commit('setLoading');
|
||||
axios
|
||||
.get('/roles')
|
||||
.then((response: AxiosResponse<FG.Role[]>) => {
|
||||
commit('setRoles', response.data);
|
||||
})
|
||||
.finally(() => commit('setLoading', false));
|
||||
},
|
||||
|
||||
getPermissions({ commit, state }, force = true) {
|
||||
if (!force && state.permissions.length > 0) return;
|
||||
commit('setLoading');
|
||||
axios
|
||||
.get('/roles')
|
||||
.then((response: AxiosResponse<FG.Permission[]>) => {
|
||||
commit('setPermissions', response.data);
|
||||
})
|
||||
.finally(() => commit('setLoading', false));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -93,20 +133,11 @@ const getters: GetterTree<UserStateInterface, StateInterface> = {
|
|||
users({ users }) {
|
||||
return users;
|
||||
},
|
||||
loading({ updateUserLoading, getUserLoading }) {
|
||||
return updateUserLoading || getUserLoading;
|
||||
loading({ loading }) {
|
||||
return loading > 0;
|
||||
},
|
||||
displayName({ currentUser }) {
|
||||
return currentUser?.display_name;
|
||||
},
|
||||
permissions({ currentUser }) {
|
||||
let permissions: string[] = [];
|
||||
if (currentUser) {
|
||||
currentUser.roles.forEach(role => {
|
||||
permissions = permissions.concat(role.permissions);
|
||||
});
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue