release v2.0.0 #4
|
@ -1,20 +1,25 @@
|
||||||
import { boot } from 'quasar/wrappers';
|
import { boot } from 'quasar/wrappers';
|
||||||
|
import { StateInterface } from '../store';
|
||||||
|
|
||||||
export default boot(({ Vue, router, store }) => {
|
export default boot(({ Vue, router, store }) => {
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
if (to.matched.some(record => record.meta.requiresAuth)) {
|
let user = (<StateInterface>store.state).user;
|
||||||
// this route requires auth, check if logged in
|
|
||||||
// if not, redirect to login page.
|
if (to.matched.some(record => {
|
||||||
if (!store.getters.isLoggedIn()) {
|
// permissions is set AND has NO matching permission
|
||||||
|
return record.meta.permissions !== undefined &&
|
||||||
|
!(record.meta.permissions.filter(
|
||||||
|
(value: string) =>
|
||||||
|
user.permissions.includes(value)
|
||||||
|
).length > 0);
|
||||||
|
})
|
||||||
|
) {
|
||||||
next({
|
next({
|
||||||
path: '/login',
|
path: '/login',
|
||||||
query: { redirect: to.fullPath }
|
query: { redirect: to.fullPath }
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
next()
|
next();
|
||||||
}
|
|
||||||
} else {
|
|
||||||
next() // make sure to always call next()!
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<q-page padding class="fit row justify-center items-center content-center">
|
<q-page
|
||||||
<q-card
|
padding
|
||||||
class="col-xs-11 col-sm-8 col-md-6 col-lg-4 justify-center items-center content-center"
|
class="fit row justify-center items-center content-center"
|
||||||
>
|
>
|
||||||
|
<q-card class="col-xs-11 col-sm-8 col-md-6 col-lg-4 justify-center items-center content-center">
|
||||||
<q-toolbar class="bg-primary text-white">
|
<q-toolbar class="bg-primary text-white">
|
||||||
<q-toolbar-title>
|
<q-toolbar-title>
|
||||||
Login
|
Login
|
||||||
|
@ -10,10 +11,14 @@
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
|
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<q-form ref="LoginForm" @submit="doLogin" class="q-gutter-md">
|
<q-form
|
||||||
|
ref="LoginForm"
|
||||||
|
@submit="doLogin"
|
||||||
|
class="q-gutter-md"
|
||||||
|
>
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
v-model="username"
|
v-model="userid"
|
||||||
label="Benutzername oder E-Mail"
|
label="Benutzername oder E-Mail"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
/>
|
/>
|
||||||
|
@ -25,7 +30,11 @@
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
/>
|
/>
|
||||||
<div class="row justify-end">
|
<div class="row justify-end">
|
||||||
<q-btn label="Login" type="submit" color="primary" />
|
<q-btn
|
||||||
|
label="Login"
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</q-form>
|
</q-form>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
@ -39,18 +48,22 @@ import { defineComponent, ref } from '@vue/composition-api';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
// name: 'PageName'
|
// name: 'PageName'
|
||||||
setup(_, ctx) {
|
setup(_, ctx) {
|
||||||
const username = ref('');
|
const userid = ref('');
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
const rules = [
|
const rules = [
|
||||||
(val: string) => (val && val.length > 0) || 'Feld darf nicht leer sein!'
|
(val: string) => (val && val.length > 0) || 'Feld darf nicht leer sein!',
|
||||||
];
|
];
|
||||||
|
|
||||||
function doLogin() {
|
function doLogin() {
|
||||||
console.log(username.value, password.value);
|
ctx.root.$store.dispatch('user/login', {
|
||||||
void ctx.root.$router.push({ name: 'main' });
|
userid: userid.value,
|
||||||
|
password: password.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
void ctx.root.$router.push({ name: 'user' });
|
||||||
}
|
}
|
||||||
|
|
||||||
return { username, password, doLogin, rules };
|
return { userid, password, doLogin, rules };
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -10,6 +10,7 @@ const routes: RouteConfig[] = [
|
||||||
{
|
{
|
||||||
path: 'plugin1',
|
path: 'plugin1',
|
||||||
name: 'plugin1',
|
name: 'plugin1',
|
||||||
|
meta: { permissions: ['user'] },
|
||||||
components: {
|
components: {
|
||||||
default: () => import('../pages/Plugin.vue'),
|
default: () => import('../pages/Plugin.vue'),
|
||||||
'plugin-nav': () => import('../components/navigation/PluginLinks.vue')
|
'plugin-nav': () => import('../components/navigation/PluginLinks.vue')
|
||||||
|
@ -18,11 +19,13 @@ const routes: RouteConfig[] = [
|
||||||
{
|
{
|
||||||
path: 'plugin1_1',
|
path: 'plugin1_1',
|
||||||
name: 'plugin1_1',
|
name: 'plugin1_1',
|
||||||
|
meta: { permissions: ['user'] },
|
||||||
component: () => import('../pages/NewPlugin.vue')
|
component: () => import('../pages/NewPlugin.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'plugin1_2',
|
path: 'plugin1_2',
|
||||||
name: 'plugin1_2',
|
name: 'plugin1_2',
|
||||||
|
meta: { permissions: ['user'] },
|
||||||
component: () => import('../pages/OldPlugin.vue')
|
component: () => import('../pages/OldPlugin.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,11 +3,22 @@
|
||||||
<q-page
|
<q-page
|
||||||
padding
|
padding
|
||||||
class="fit row justify-center content-center items-center"
|
class="fit row justify-center content-center items-center"
|
||||||
v-if="$route.name == mainLink.link"
|
v-if="true"
|
||||||
|
>
|
||||||
|
<q-card
|
||||||
|
class="col-4"
|
||||||
|
height=""
|
||||||
>
|
>
|
||||||
<q-card class="col-4" height="">
|
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
{{ mainLink.title }}
|
Name: {{ userState.user.firstname }} {{ userState.user.lastname }}<br />
|
||||||
|
E-Mail: {{ userState.user.mail }}<br />
|
||||||
|
Roles: <ul
|
||||||
|
v-for="role in userState.user.roles"
|
||||||
|
v-bind:key="role"
|
||||||
|
>
|
||||||
|
<li>{{ role }}</li>
|
||||||
|
</ul><br />
|
||||||
|
Token expires: {{ userState.token.expires }}
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-page>
|
</q-page>
|
||||||
|
@ -16,13 +27,17 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from '@vue/composition-api';
|
import { defineComponent, computed } from '@vue/composition-api';
|
||||||
import { mainLink } from '../plugin';
|
import { UserStateInterface } from '../../../store/module-user/state';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
// name: 'PageName'
|
// name: 'PageName'
|
||||||
setup(_, ctx) {
|
setup(_, { root }) {
|
||||||
const a = ctx.root.$flaschengeistPluginsMainLinks;
|
const userState = computed(
|
||||||
return { a, mainLink };
|
() => <UserStateInterface>root.$store.state.user
|
||||||
}
|
);
|
||||||
|
const a = root.$flaschengeistPluginsMainLinks;
|
||||||
|
return { a, userState };
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -20,12 +20,13 @@ const routes: RouteConfig[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/main',
|
path: '/main',
|
||||||
name: 'main',
|
redirect: 'user',
|
||||||
component: () => import('layouts/MainLayout.vue'),
|
component: () => import('layouts/MainLayout.vue'),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
name: 'about',
|
name: 'about',
|
||||||
path: 'about',
|
path: 'about',
|
||||||
|
meta: { 'permissions': ['user'] },
|
||||||
component: () => import('pages/about/About.vue')
|
component: () => import('pages/about/About.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { store } from 'quasar/wrappers';
|
import { store } from 'quasar/wrappers';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
|
|
||||||
// import example from './module-example';
|
import user from './module-user';
|
||||||
// import { ExampleStateInterface } from './module-example/state';
|
import { UserStateInterface } from './module-user/state';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If not building with SSR mode, you can
|
* If not building with SSR mode, you can
|
||||||
|
@ -11,17 +11,15 @@ import Vuex from 'vuex';
|
||||||
|
|
||||||
export interface StateInterface {
|
export interface StateInterface {
|
||||||
// Define your own store structure, using submodules if needed
|
// Define your own store structure, using submodules if needed
|
||||||
// example: ExampleStateInterface;
|
user: UserStateInterface;
|
||||||
// Declared as unknown to avoid linting issue. Best to strongly type as per the line above.
|
|
||||||
example: unknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default store(function({ Vue }) {
|
export default store(function ({ Vue }) {
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
const Store = new Vuex.Store<StateInterface>({
|
const Store = new Vuex.Store<StateInterface>({
|
||||||
modules: {
|
modules: {
|
||||||
// example
|
user
|
||||||
},
|
},
|
||||||
|
|
||||||
// enable strict mode (adds overhead!)
|
// enable strict mode (adds overhead!)
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { ActionTree } from 'vuex';
|
|
||||||
import { StateInterface } from '../index';
|
|
||||||
import { ExampleStateInterface } from './state';
|
|
||||||
|
|
||||||
const actions: ActionTree<ExampleStateInterface, StateInterface> = {
|
|
||||||
someAction (/* context */) {
|
|
||||||
// your code
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default actions;
|
|
|
@ -1,10 +0,0 @@
|
||||||
import { MutationTree } from 'vuex';
|
|
||||||
import { ExampleStateInterface } from './state';
|
|
||||||
|
|
||||||
const mutation: MutationTree<ExampleStateInterface> = {
|
|
||||||
someMutation (/* state: ExampleStateInterface */) {
|
|
||||||
// your code
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default mutation;
|
|
|
@ -1,9 +0,0 @@
|
||||||
export interface ExampleStateInterface {
|
|
||||||
prop: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state: ExampleStateInterface = {
|
|
||||||
prop: false
|
|
||||||
};
|
|
||||||
|
|
||||||
export default state;
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import { ActionTree } from 'vuex';
|
||||||
|
import { StateInterface } from '../index';
|
||||||
|
import { UserStateInterface } from './state';
|
||||||
|
|
||||||
|
const actions: ActionTree<UserStateInterface, StateInterface> = {
|
||||||
|
login ({ commit }, payload): any {
|
||||||
|
axios.post("/auth", {
|
||||||
|
userid: payload.userid,
|
||||||
|
password: payload.password
|
||||||
|
}).then(function (response) {
|
||||||
|
let token = (<UserStateInterface>response.data).token;
|
||||||
|
console.log(token);
|
||||||
|
if (token)
|
||||||
|
token.expires = new Date(token.expires);
|
||||||
|
commit('setUser', (<UserStateInterface>response.data).user);
|
||||||
|
commit('setToken', token);
|
||||||
|
commit('setPermissions', (<UserStateInterface>response.data).permissions);
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default actions;
|
|
@ -1,9 +1,10 @@
|
||||||
import { GetterTree } from 'vuex';
|
import { GetterTree } from 'vuex';
|
||||||
import { StateInterface } from '../index';
|
import { StateInterface } from '../index';
|
||||||
import { ExampleStateInterface } from './state';
|
import { UserStateInterface } from './state';
|
||||||
|
|
||||||
const getters: GetterTree<ExampleStateInterface, StateInterface> = {
|
const getters: GetterTree<UserStateInterface, StateInterface> = {
|
||||||
someAction (/* context */) {
|
someAction (/* context */) {
|
||||||
|
console.log("GOOOOOOOOOOOOOOOOOOO");
|
||||||
// your code
|
// your code
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -1,11 +1,11 @@
|
||||||
import { Module } from 'vuex';
|
import { Module } from 'vuex';
|
||||||
import { StateInterface } from '../index';
|
import { StateInterface } from '../index';
|
||||||
import state, { ExampleStateInterface } from './state';
|
import state, { UserStateInterface } from './state';
|
||||||
import actions from './actions';
|
import actions from './actions';
|
||||||
import getters from './getters';
|
import getters from './getters';
|
||||||
import mutations from './mutations';
|
import mutations from './mutations';
|
||||||
|
|
||||||
const exampleModule: Module<ExampleStateInterface, StateInterface> = {
|
const exampleModule: Module<UserStateInterface, StateInterface> = {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
actions,
|
actions,
|
||||||
getters,
|
getters,
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { MutationTree } from 'vuex';
|
||||||
|
import { TokenInterface, UserInterface, UserStateInterface } from './state';
|
||||||
|
|
||||||
|
const mutation: MutationTree<UserStateInterface> = {
|
||||||
|
setToken (state: UserStateInterface, token: TokenInterface) {
|
||||||
|
state.token = token
|
||||||
|
},
|
||||||
|
setUser (state: UserStateInterface, user: UserInterface) {
|
||||||
|
state.user = user
|
||||||
|
},
|
||||||
|
setPermissions (state: UserStateInterface, permissions: Array<string>) {
|
||||||
|
state.permissions = permissions
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default mutation;
|
|
@ -0,0 +1,29 @@
|
||||||
|
export interface TokenInterface {
|
||||||
|
token: string,
|
||||||
|
expires: Date,
|
||||||
|
lifetime: number,
|
||||||
|
browser: string,
|
||||||
|
platform: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserInterface {
|
||||||
|
display_name: string | null,
|
||||||
|
firstname: string,
|
||||||
|
lastname: string,
|
||||||
|
mail: string | null,
|
||||||
|
roles: Array<string>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserStateInterface {
|
||||||
|
token: TokenInterface | null,
|
||||||
|
user: UserInterface | null,
|
||||||
|
permissions: Array<string>
|
||||||
|
};
|
||||||
|
|
||||||
|
const state: UserStateInterface = {
|
||||||
|
token: null,
|
||||||
|
user: null,
|
||||||
|
permissions: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export default state;
|
Loading…
Reference in New Issue