release v2.0.0 #4
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const config = {
|
const config = {
|
||||||
baseURL: '/api'
|
baseURL: '/api'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="icons/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link rel="icon" type="image/ico" href="favicon.ico" />
|
||||||
|
</head>
|
||||||
|
|
||||||
<link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png">
|
<body>
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
<!-- DO NOT touch the following DIV -->
|
||||||
<link rel="icon" type="image/ico" href="favicon.ico">
|
<div id="q-app"></div>
|
||||||
</head>
|
</body>
|
||||||
|
</html>
|
||||||
<body>
|
|
||||||
<!-- DO NOT touch the following DIV -->
|
|
||||||
<div id="q-app"></div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(() => {
|
||||||
|
|
|
@ -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('');
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -2,3 +2,8 @@ export interface LoginData {
|
||||||
userid: string;
|
userid: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LoginResponse {
|
||||||
|
user: FG.User;
|
||||||
|
session: FG.Session;
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
@ -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 };
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -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 }) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue