Cleanup + Login with plugin backend

This commit is contained in:
Ferdinand Thiessen 2020-10-15 03:36:25 +02:00
parent bea9f9f5dc
commit 82d4b52e24
14 changed files with 152 additions and 76 deletions

View File

@ -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
next({ return record.meta.permissions !== undefined &&
path: '/login', !(record.meta.permissions.filter(
query: { redirect: to.fullPath } (value: string) =>
}) user.permissions.includes(value)
} else { ).length > 0);
next() })
} ) {
next({
path: '/login',
query: { redirect: to.fullPath }
});
} else { } else {
next() // make sure to always call next()! next();
} }
}) })
}); });

View File

@ -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>

View File

@ -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')
} }
] ]

View File

@ -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>

View File

@ -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')
} }
] ]

View File

@ -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!)

View File

@ -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;

View File

@ -1,10 +0,0 @@
import { MutationTree } from 'vuex';
import { ExampleStateInterface } from './state';
const mutation: MutationTree<ExampleStateInterface> = {
someMutation (/* state: ExampleStateInterface */) {
// your code
}
};
export default mutation;

View File

@ -1,9 +0,0 @@
export interface ExampleStateInterface {
prop: boolean;
}
const state: ExampleStateInterface = {
prop: false
};
export default state;

View File

@ -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;

View File

@ -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
} }
}; };

View File

@ -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,

View File

@ -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;

View File

@ -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;