Use more logical seperation on user and session

* Seperated user and session more logical
* Fixed error with expired sessions
* Cache user only in SessionStore
* Use current backend responses
* Used prettier
This commit is contained in:
Ferdinand Thiessen 2020-11-04 23:53:10 +01:00
parent 245944b6a9
commit 4061d84ace
24 changed files with 307 additions and 305 deletions

View File

@ -103,7 +103,7 @@ module.exports = configure(function(ctx) {
// directives: [], // directives: [],
// Quasar plugins // Quasar plugins
plugins: ['LocalStorage', 'Loading'] plugins: ['LocalStorage', 'SessionStorage', 'Loading']
}, },
// animations: 'all', // --- includes all animations // animations: 'all', // --- includes all animations

View File

@ -7,6 +7,6 @@
import { defineComponent } from '@vue/composition-api'; import { defineComponent } from '@vue/composition-api';
export default defineComponent({ export default defineComponent({
name: 'App', name: 'App'
}); });
</script> </script>

View File

@ -16,9 +16,9 @@ export default boot<Store<StateInterface>>(({ Vue, store }) => {
axios.defaults.baseURL = config.baseURL; axios.defaults.baseURL = config.baseURL;
axios.interceptors.request.use(config => { axios.interceptors.request.use(config => {
const session = store.state.user.session; const session = store.state.session.currentSession;
if (session.token) { if (session?.token) {
config.headers = {'Authorization': 'Bearer ' + session.token}; config.headers = { Authorization: 'Bearer ' + session.token };
} }
return config; return config;
}); });

View File

@ -1,53 +1,49 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { StateInterface } from 'src/store'; import { StateInterface } from 'src/store';
import { RouteRecord } from 'vue-router'; import { RouteRecord } from 'vue-router';
import { Store } from 'vuex' import { Store } from 'vuex';
export default boot<Store<StateInterface>>(({ router, store }) => { export default boot<Store<StateInterface>>(({ router, store }) => {
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
store const user = store.state.user.currentUser;
.dispatch('user/loadFromLocalStorage') const session = store.state.session.currentSession;
.then(() => {
const user = store.state.user.user;
const session = store.state.user.session;
let permissions: string[] = []; let permissions: string[] = [];
user.roles.forEach(role => { if (user) {
permissions = permissions.concat(role.permissions); user.roles.forEach(role => {
}); permissions = permissions.concat(role.permissions);
if (to.name != 'login') {
if (session.expires >= new Date() || session.token === '') {
store.dispatch('user/doLogout').catch(error => {
console.warn(error);
});
return next({ name: 'login', query: { redirect: to.fullPath } });
}
if (
to.matched.every((record: RouteRecord) => {
if (!('meta' in record) || !('permissions' in record.meta))
return true;
if (record.meta) {
if ((<{permissions: FG.Permission[]}>record.meta).permissions) {
return (<{permissions: FG.Permission[]}>record.meta).permissions.every((permission: string) => {
return permissions.includes(
permission
);
})
}
}
})
) {
next();
} else {
next({ name: 'login', query: { redirect: to.fullPath } });
}
} else {
next();
}
})
.catch(error => {
console.exception(error);
}); });
}
if (to.name != 'login') {
if (!session || session.expires <= new Date()) {
store.dispatch('session/logout').catch(error => {
console.warn(error);
});
return next({ name: 'login', query: { redirect: to.fullPath } });
}
if (
to.matched.every((record: RouteRecord) => {
if (!('meta' in record) || !('permissions' in record.meta))
return true;
if (record.meta) {
if ((<{ permissions: FG.Permission[] }>record.meta).permissions) {
return (<{ permissions: FG.Permission[] }>(
record.meta
)).permissions.every((permission: string) => {
return permissions.includes(permission);
});
}
}
})
) {
next();
} else {
next({ name: 'login', query: { redirect: to.fullPath } });
}
} else {
next();
}
}); });
}); });

View File

@ -1,5 +1,5 @@
const config = { const config = {
baseURL: '/api' baseURL: '/api'
}; };
export default config; export default config;

111
src/flaschengeist.d.ts vendored
View File

@ -1,57 +1,58 @@
declare namespace FG { declare namespace FG {
interface Session { interface Session {
expires: Date; expires: Date;
token: string; token: string;
lifetime: number; lifetime: number;
browser: string; browser: string;
platform: string; platform: string;
} userid: string;
interface User { }
userid: string; interface User {
display_name: string; userid: string;
firstname: string; display_name: string;
lastname: string; firstname: string;
mail: string; lastname: string;
roles: Array<Role>; mail: string;
} roles: Array<Role>;
type Permission = string; }
interface Role { type Permission = string;
name: string; interface Role {
permissions: Array<Permission>; name: string;
} permissions: Array<Permission>;
interface Transaction { }
id: number; interface Transaction {
time: Date; id: number;
amount: number; time: Date;
sender_id: string; amount: number;
receiver_id: string; sender_id: string;
author_id: string; receiver_id: string;
} author_id: string;
interface Event { }
id: number; interface Event {
start: Date; id: number;
description?: any; start: Date;
type: EventType; description?: any;
slots: Array<EventSlot>; type: EventType;
} slots: Array<EventSlot>;
interface EventSlot { }
id: number; interface EventSlot {
start: Date; id: number;
end?: any; start: Date;
jobs: Array<JobSlot>; end?: any;
} jobs: Array<JobSlot>;
type EventType = string; }
interface Job { type EventType = string;
userid: string; interface Job {
value: number; userid: string;
} value: number;
interface JobSlot { }
type: JobType; interface JobSlot {
users: Array<Job>; type: JobType;
required_jobs: number; users: Array<Job>;
} required_jobs: number;
interface JobType { }
id: number; interface JobType {
name: string; id: number;
} name: string;
}
} }

View File

@ -1,24 +1,34 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<title><%= productName %></title>
<head> <meta charset="utf-8" />
<title><%= productName %></title> <meta name="description" content="<%= productDescription %>" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<meta
name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>"
/>
<meta charset="utf-8"> <link
<meta name="description" content="<%= productDescription %>"> rel="icon"
<meta name="format-detection" content="telephone=no"> type="image/png"
<meta name="msapplication-tap-highlight" content="no"> sizes="128x128"
<meta name="viewport" href="icons/favicon-128x128.png"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>"> />
<link
<link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png"> rel="icon"
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png"> type="image/png"
<link rel="icon" type="image/ico" href="favicon.ico"> sizes="32x32"
</head> href="icons/favicon-32x32.png"
/>
<body> <link rel="icon" type="image/ico" href="favicon.ico" />
<!-- DO NOT touch the following DIV --> </head>
<div id="q-app"></div>
</body>
<body>
<!-- DO NOT touch the following DIV -->
<div id="q-app"></div>
</body>
</html> </html>

View File

@ -14,7 +14,7 @@
<q-toolbar-title> <q-toolbar-title>
<q-avatar> <q-avatar>
<img src="logo.svg"/> <img src="logo.svg" />
</q-avatar> </q-avatar>
<span class="gt-xs"> <span class="gt-xs">
Flaschengeist Flaschengeist
@ -31,7 +31,7 @@
:permissions="shortcut.permissions" :permissions="shortcut.permissions"
/> />
</div> </div>
<q-btn flat round dense icon="mdi-exit-to-app" @click="logout()"/> <q-btn flat round dense icon="mdi-exit-to-app" @click="logout()" />
</q-toolbar> </q-toolbar>
</q-header> </q-header>
@ -54,7 +54,7 @@
:permissions="link.permissions" :permissions="link.permissions"
/> />
</q-list> </q-list>
<q-separator/> <q-separator />
<!-- Plugin functions --> <!-- Plugin functions -->
<!-- <router-view name="plugin-nav" /> --> <!-- <router-view name="plugin-nav" /> -->
@ -81,7 +81,7 @@
/> />
</div> </div>
<q-separator/> <q-separator />
<essential-link <essential-link
v-for="(link, index) in links" v-for="(link, index) in links"
@ -94,7 +94,7 @@
</q-drawer> </q-drawer>
<q-page-container> <q-page-container>
<router-view/> <router-view />
</q-page-container> </q-page-container>
</q-layout> </q-layout>
</template> </template>
@ -102,11 +102,11 @@
<script lang="ts"> <script lang="ts">
import EssentialLink from 'components/navigation/EssentialLink.vue'; import EssentialLink from 'components/navigation/EssentialLink.vue';
import ShortCutLink from 'components/navigation/ShortCutLink.vue'; import ShortCutLink from 'components/navigation/ShortCutLink.vue';
import {Screen} from 'quasar'; import { Screen } from 'quasar';
import {defineComponent, ref, computed} from '@vue/composition-api'; import { defineComponent, ref, computed } from '@vue/composition-api';
import {Store} from 'vuex'; import { Store } from 'vuex';
import {StateInterface} from 'src/store'; import { StateInterface } from 'src/store';
import {FG_Plugin} from 'src/plugins'; import { FG_Plugin } from 'src/plugins';
const links = [ const links = [
{ {
@ -140,7 +140,7 @@ declare module 'vue/types/vue' {
export default defineComponent({ export default defineComponent({
name: 'MainLayout', name: 'MainLayout',
components: {EssentialLink, ShortCutLink}, components: { EssentialLink, ShortCutLink },
setup(_, ctx) { setup(_, ctx) {
const leftDrawer = ref(false); const leftDrawer = ref(false);
@ -179,7 +179,7 @@ export default defineComponent({
function logout() { function logout() {
const store = <Store<StateInterface>>ctx.root.$store; const store = <Store<StateInterface>>ctx.root.$store;
store store
.dispatch('user/logout', store.state.user.session.token) .dispatch('session/logout', store.state.session.currentSession?.token)
.catch(error => { .catch(error => {
console.warn(error); console.warn(error);
}); });

View File

@ -1,5 +1,7 @@
<template> <template>
<div class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center"> <div
class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center"
>
<div> <div>
<div style="font-size: 30vh"> <div style="font-size: 30vh">
404 404
@ -26,6 +28,6 @@
import { defineComponent } from '@vue/composition-api'; import { defineComponent } from '@vue/composition-api';
export default defineComponent({ export default defineComponent({
name: 'Error404', name: 'Error404'
}); });
</script> </script>

View File

@ -48,7 +48,7 @@ export default defineComponent({
function doLogin() { function doLogin() {
console.log(userid.value, password.value); console.log(userid.value, password.value);
ctx.root.$store ctx.root.$store
.dispatch('user/login', { .dispatch('session/login', {
userid: userid.value, userid: userid.value,
password: password.value password: password.value
}) })

4
src/plugins.d.ts vendored
View File

@ -13,7 +13,7 @@ declare namespace FG_Plugin {
title: string; title: string;
icon: string; icon: string;
children?: PluginRouteConfig[]; children?: PluginRouteConfig[];
meta?: {permissions?: string[]} meta?: { permissions?: string[] };
} }
interface Plugin { interface Plugin {
@ -34,7 +34,7 @@ declare namespace FG_Plugin {
title: string; title: string;
link: string; link: string;
icon: string; icon: string;
permissions?: string[] permissions?: string[];
} }
interface LoadedPlugin { interface LoadedPlugin {

View File

@ -38,7 +38,8 @@ const mutations: MutationTree<BalanceInterface> = {
const actions: ActionTree<BalanceInterface, StateInterface> = { const actions: ActionTree<BalanceInterface, StateInterface> = {
getBalance({ commit, rootState }) { getBalance({ commit, rootState }) {
axios axios
.get(`/users/${rootState.user.user.userid}/balance`) /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
.get(`/users/${rootState.user.currentUser?.userid}/balance`)
.then(({ data }: AxiosResponse<BalanceResponse>) => { .then(({ data }: AxiosResponse<BalanceResponse>) => {
commit('setBalance', data.balance); commit('setBalance', data.balance);
commit('setCredit', data.credit); commit('setCredit', data.credit);
@ -50,7 +51,8 @@ const actions: ActionTree<BalanceInterface, StateInterface> = {
}, },
getLimit({ rootState }) { getLimit({ rootState }) {
axios axios
.get(`/users/${rootState.user.user.userid}/balance/limit`) /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
.get(`/users/${rootState.user.currentUser?.userid}/balance/limit`)
.then(({ data }) => { .then(({ data }) => {
console.log(data); console.log(data);
}) })
@ -60,7 +62,10 @@ const actions: ActionTree<BalanceInterface, StateInterface> = {
}, },
changeBalance({ rootState, dispatch }, amount: number) { changeBalance({ rootState, dispatch }, amount: number) {
axios axios
.put(`/users/${rootState.user.user.userid}/balance`, <{ amount: number }>{ /* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
.put(`/users/${rootState.user.currentUser?.userid}/balance`, <
{ amount: number }
>{
amount: amount amount: amount
}) })
.then(() => { .then(() => {

View File

@ -100,14 +100,12 @@ export default defineComponent({
setup(_, { root }) { setup(_, { root }) {
const store = <Store<StateInterface>>root.$store; const store = <Store<StateInterface>>root.$store;
const user = computed<FG.User>(() => { const user = computed(() => <FG.User>store.state.user.currentUser);
return store.state.user.user;
});
const firstname = ref(user.value.firstname); const firstname = ref(user.value?.firstname);
const lastname = ref(user.value.lastname); const lastname = ref(user.value?.lastname);
const mail = ref(user.value.mail); const mail = ref(user.value?.mail);
const display_name = ref(user.value.display_name); const display_name = ref(user.value?.display_name);
const password = ref(''); const password = ref('');
const new_password = ref(''); const new_password = ref('');

View File

@ -76,12 +76,12 @@ export default defineComponent({
} }
function deleteSession(token: string) { function deleteSession(token: string) {
store.dispatch('sessions/deleteSession', token).catch(error => { store.dispatch('session/deleteSession', token).catch(error => {
console.warn(error); console.warn(error);
}); });
} }
function isThisSession(token: string) { function isThisSession(token: string) {
return store.state.user.session.token == token; return store.state.session.currentSession?.token === token;
} }
return { return {

View File

@ -2,3 +2,8 @@ export interface LoginData {
userid: string; userid: string;
password: string; password: string;
} }
export interface LoginResponse {
user: FG.User;
session: FG.Session;
}

View File

@ -34,7 +34,7 @@ export default defineComponent({
const checkMain = computed(() => { const checkMain = computed(() => {
return root.$route.matched.length == 2; return root.$route.matched.length == 2;
}); });
return { checkMain, mainRoutes}; return { checkMain, mainRoutes };
} }
}); });
</script> </script>

View File

@ -43,11 +43,11 @@ export default defineComponent({
const store = <Store<StateInterface>>root.$store; const store = <Store<StateInterface>>root.$store;
onBeforeMount(() => { onBeforeMount(() => {
store.dispatch('sessions/getSessions').catch(error => { store.dispatch('session/getSessions').catch(error => {
console.warn(error); console.warn(error);
}); });
}); });
const sessions = computed(() => store.state.sessions.sessions); const sessions = computed(() => store.state.session.sessions);
function showRootGetters() { function showRootGetters() {
console.log(sessions.value); console.log(sessions.value);
@ -55,7 +55,7 @@ export default defineComponent({
const sessionsLoading = computed( const sessionsLoading = computed(
() => () =>
store.state.sessions.loading || store.state.session.loading ||
store.state.user.getUserLoading || store.state.user.getUserLoading ||
store.state.user.updateUserLoading store.state.user.updateUserLoading
); );

View File

@ -27,9 +27,9 @@ export default defineComponent({
setup(_, { root }) { setup(_, { root }) {
const store = <Store<StateInterface>>root.$store; const store = <Store<StateInterface>>root.$store;
const userObj = computed(() => store.state.user.user); const userObj = computed(() => store.state.user.currentUser);
const sessionObj = computed(() => store.state.user.session); const sessionObj = computed(() => store.state.session.currentSession);
return { userObj, sessionObj }; return { userObj, sessionObj };
} }

View File

@ -12,7 +12,7 @@ const plugin: FG_Plugin.Plugin = {
version: '0.0.1', version: '0.0.1',
store: new Map<string, Module<any, StateInterface>>([ store: new Map<string, Module<any, StateInterface>>([
['user', userStore], ['user', userStore],
['sessions', sessionsStore] ['session', sessionsStore]
]) ])
}; };

View File

@ -1,20 +1,33 @@
import { Module, MutationTree, ActionTree, GetterTree } from 'vuex'; import { Module, MutationTree, ActionTree, GetterTree } from 'vuex';
import { LoginData, LoginResponse } from 'src/plugins/user/models';
import { StateInterface } from 'src/store'; import { StateInterface } from 'src/store';
import { axios } from 'src/boot/axios'; import { axios } from 'src/boot/axios';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { Router } from 'src/router'; import { Router } from 'src/router';
import { LocalStorage, Loading } from 'quasar';
export interface SessionInterface { export interface SessionInterface {
currentSession?: FG.Session;
sessions: FG.Session[]; sessions: FG.Session[];
loading: boolean; loading: boolean;
} }
const state: SessionInterface = { const state: SessionInterface = {
sessions: [], sessions: [],
currentSession:
LocalStorage.getItem<FG.Session>('currentSession') || undefined,
loading: false loading: false
}; };
const mutations: MutationTree<SessionInterface> = { const mutations: MutationTree<SessionInterface> = {
setCurrentSession(state, session: FG.Session) {
LocalStorage.set('currentSession', session);
state.currentSession = session;
},
clearCurrentSession(state) {
LocalStorage.remove('currentSession');
state.currentSession = undefined;
},
setSessions(state, sessions: FG.Session[]) { setSessions(state, sessions: FG.Session[]) {
state.sessions = sessions; state.sessions = sessions;
}, },
@ -24,40 +37,75 @@ const mutations: MutationTree<SessionInterface> = {
}; };
const actions: ActionTree<SessionInterface, StateInterface> = { const actions: ActionTree<SessionInterface, StateInterface> = {
getSessions({ commit, rootState, dispatch }) { login({ commit }, data: LoginData) {
commit('setLoading', true); Loading.show({
axios message: 'Du wirst angemeldet'
.get('/auth') });
.then((response: AxiosResponse<FG.Session[]>) => { void axios
console.log(response.data); .post('/auth', data)
response.data.forEach(session => { .then((response: AxiosResponse<LoginResponse>) => {
session.expires = new Date(session.expires); response.data.session.expires = new Date(response.data.session.expires);
}); commit('setCurrentSession', response.data.session);
commit('setSessions', response.data); commit('user/setCurrentUser', response.data.user, { root: true });
const currentSession = response.data.find((session: FG.Session) => { void Router.push({ name: 'user-main' });
return session.token === rootState.user.session.token;
});
if (currentSession) {
void dispatch('user/setSession', currentSession, { root: true });
}
}) })
.catch(error => { .catch(error => {
console.exception(error); console.exception(error);
}) })
.finally(() => { .finally(() => {
commit('setLoading', false); Loading.hide();
}); });
}, },
deleteSession({ commit, dispatch, rootState }, token: string) { /**
* Logout from current session
*/
logout({ dispatch, rootState }) {
Loading.show({ message: 'Session wird abgemeldet' });
dispatch('deleteSession', rootState.session.currentSession?.token).finally(
() => {
Loading.hide();
}
);
},
/**
* Delete a given session
*/
deleteSession({ commit, rootState }, token: string | null) {
if (token === null) return;
commit('setLoading', true); commit('setLoading', true);
axios axios
.delete(`/auth/${token}`) .delete(`/auth/${token}`)
.then(() => { .then(() => {
if (token === rootState.user.session.token) { if (token === rootState.session.currentSession?.token) {
void dispatch('user/setSession', null, { root: true }); commit('clearCurrentSession');
Router.go(0); commit('user/clearCurrentUser', null, { root: true });
void Router.push({ name: 'login' });
} else { } else {
void dispatch('getSessions'); commit('getSessions');
}
})
.finally(() => {
commit('setLoading', false);
});
},
/**
* Get all sessions from current User
*/
getSessions({ commit, state, dispatch }) {
commit('setLoading', true);
axios
.get('/auth')
.then((response: AxiosResponse<FG.Session[]>) => {
response.data.forEach(session => {
session.expires = new Date(session.expires);
});
commit('setSessions', response.data);
const currentSession = response.data.find((session: FG.Session) => {
return session.token === state.currentSession?.token;
});
if (currentSession) {
void dispatch('setCurrentSession', currentSession);
} }
}) })
.catch(error => { .catch(error => {
@ -70,6 +118,9 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
}; };
const getters: GetterTree<SessionInterface, StateInterface> = { const getters: GetterTree<SessionInterface, StateInterface> = {
currentSession(state) {
return state.currentSession;
},
sessions(state) { sessions(state) {
return state.sessions; return state.sessions;
}, },

View File

@ -1,131 +1,81 @@
import { Module, MutationTree, ActionTree, GetterTree } from 'vuex'; import { Module, MutationTree, ActionTree, GetterTree } from 'vuex';
import { StateInterface } from 'src/store'; import { StateInterface } from 'src/store';
import { axios } from 'boot/axios'; import { axios } from 'boot/axios';
import { LoginData } from 'src/plugins/user/models';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { LocalStorage, Loading } from 'quasar'; import { SessionStorage } from 'quasar';
import { Router } from 'src/router';
export interface UserStateInterface extends LoginResponse { export interface UserStateInterface {
updateUserLoading: boolean; updateUserLoading: boolean;
getUserLoading: boolean; getUserLoading: boolean;
currentUser?: FG.User;
users: FG.User[];
} }
export interface LoginResponse {
user: FG.User;
session: FG.Session;
}
const empty_session: FG.Session = {
browser: '',
expires: new Date(),
lifetime: -1,
platform: '',
token: ''
};
const empty_user: FG.User = {
display_name: '',
firstname: '',
lastname: '',
mail: '',
roles: [],
userid: ''
};
const state: UserStateInterface = { const state: UserStateInterface = {
user: empty_user, users: [],
session: empty_session, currentUser: SessionStorage.getItem<FG.User>('currentUser') || undefined,
updateUserLoading: false, updateUserLoading: false,
getUserLoading: false getUserLoading: false
}; };
const mutations: MutationTree<UserStateInterface> = { const mutations: MutationTree<UserStateInterface> = {
setUser(state, data: FG.User) { setCurrentUser(state, data: FG.User) {
state.user = data; SessionStorage.set('currentUser', data);
state.currentUser = data;
}, },
setSession(state, data: FG.Session) { clearCurrentUser(state) {
state.session = data; SessionStorage.remove('currentUser');
state.currentUser = undefined;
},
setUsers(state, data: FG.User[]) {
state.users = data;
}, },
setLoading( setLoading(
state, state,
data: { key: 'updateUserLoading' | 'getUserLoading'; data: boolean } data: { key: 'updateUserLoading' | 'getUserLoading'; data: boolean }
) { ) {
state[data.key] = data.data; state[data.key] = data.data;
},
showState(state) {
console.log(state);
} }
}; };
const actions: ActionTree<UserStateInterface, StateInterface> = { const actions: ActionTree<UserStateInterface, StateInterface> = {
login({ commit }, data: LoginData) { getCurrentUser({ commit, rootState }) {
Loading.show({ if (rootState.session.currentSession) {
message: 'Du wirst eingeloggt' commit('setLoading', { key: 'getUserLoading', data: true });
}); axios
void axios .get(`/users/${rootState.session.currentSession.userid}`)
.post('/auth', data) .then((response: AxiosResponse<FG.User>) => {
.then((response: AxiosResponse<LoginResponse>) => { commit('setCurrentUser', response.data);
response.data.session.expires = new Date(response.data.session.expires); })
commit('setUser', response.data.user); .catch(err => {
commit('setSession', response.data.session); console.warn(err);
commit('showState'); })
LocalStorage.set('user', response.data.user); .finally(() => {
LocalStorage.set('session', response.data.session); commit('setLoading', { key: 'getUserLoading', data: false });
});
void Router.push({ name: 'user-main' }); } else {
}) console.debug('User not logged in, can not get current_user.');
.catch(error => { }
console.exception(error);
})
.finally(() => {
Loading.hide();
});
}, },
doLogout({ commit }, token: string) { getUsers({ commit }) {
Loading.show({ message: 'Du wirst ausgeloggt' });
void axios
.delete(`/auth/${token}`)
.then(() => {
commit('setUser', empty_user);
commit('setSession', empty_session);
})
.finally(() => {
LocalStorage.remove('user');
LocalStorage.remove('session');
Loading.hide();
});
},
logout({ dispatch }, token: string) {
dispatch('doLogout', token).finally(() => {
void Router.push({ name: 'login' });
});
},
getUser({ commit, state }) {
commit('setLoading', { key: 'getUserLoading', data: true });
axios axios
.get(`/users/${state.user.userid}`) .get(`/users`)
.then((response: AxiosResponse<FG.User>) => { .then((response: AxiosResponse<FG.User[]>) => {
commit('setUser', response.data); commit('setUsers', response.data);
LocalStorage.set('user', response.data);
}) })
.catch(err => { .catch(err => {
console.warn(err); console.warn(err);
})
.finally(() => {
commit('setLoading', { key: 'getUserLoading', data: false });
}); });
}, },
updateUser({ commit, state, dispatch }, data) { updateUser({ commit, state, dispatch }, data) {
commit('setLoading', { key: 'updateUserLoading', data: true }); commit('setLoading', { key: 'updateUserLoading', data: true });
if (!state.currentUser) throw 'Not logged in';
axios axios
.put(`/users/${state.user.userid}`, data) .put(`/users/${state.currentUser.userid}`, data)
.then(() => { .then(() => {
void dispatch('getUser'); void dispatch('getCurrentUser');
}) })
.catch(error => { .catch(error => {
console.log(error); console.log(error);
@ -133,46 +83,30 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
.finally(() => { .finally(() => {
commit('setLoading', { key: 'updateUserLoading', data: false }); commit('setLoading', { key: 'updateUserLoading', data: false });
}); });
},
loadFromLocalStorage({ commit }) {
let data = LocalStorage.getItem('user');
commit('setUser', data ? data : empty_user);
data = LocalStorage.getItem('session');
commit('setSession', data ? data : empty_session);
commit('showState');
},
setSession({ commit }, session: FG.Session) {
if (session) {
commit('setSession', session);
LocalStorage.set('session', session);
} else {
commit('setSession', empty_session);
LocalStorage.remove('session');
}
} }
}; };
const getters: GetterTree<UserStateInterface, StateInterface> = { const getters: GetterTree<UserStateInterface, StateInterface> = {
user({ user }) { currentUser({ currentUser }) {
return user; return currentUser;
}, },
displayName({ user }) { users({ users }) {
return user.display_name; return users;
},
session({ session }) {
return session;
}, },
loading({ updateUserLoading, getUserLoading }) { loading({ updateUserLoading, getUserLoading }) {
return updateUserLoading || getUserLoading; return updateUserLoading || getUserLoading;
}, },
permissions({user}) { displayName({ currentUser }) {
let permissions: string[] = [] return currentUser?.display_name;
user.roles.forEach(role => { },
permissions = permissions.concat(role.permissions); permissions({ currentUser }) {
}); let permissions: string[] = [];
return permissions if (currentUser) {
currentUser.roles.forEach(role => {
permissions = permissions.concat(role.permissions);
});
}
return permissions;
} }
}; };

View File

@ -26,7 +26,7 @@ const routes: RouteConfig[] = [
{ {
name: 'about', name: 'about',
path: 'about', path: 'about',
meta: { 'permission': 'user' }, meta: { permission: 'user' },
component: () => import('pages/about/About.vue') component: () => import('pages/about/About.vue')
} }
] ]

View File

@ -9,7 +9,7 @@ import { UserStateInterface } from 'src/plugins/user/store/user';
*/ */
export interface StateInterface { export interface StateInterface {
user: UserStateInterface; user: UserStateInterface;
sessions: SessionInterface; session: SessionInterface;
} }
export default store(function({ Vue }) { export default store(function({ Vue }) {

View File

@ -1,8 +1,8 @@
// THIS FEATURE-FLAG FILE IS AUTOGENERATED, // THIS FEATURE-FLAG FILE IS AUTOGENERATED,
// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING // REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
import "quasar/dist/types/feature-flag"; import 'quasar/dist/types/feature-flag';
declare module "quasar/dist/types/feature-flag" { declare module 'quasar/dist/types/feature-flag' {
interface QuasarFeatureFlags { interface QuasarFeatureFlags {
store: true; store: true;
} }