Offizielle Typehints, Anpassungen an aktuelles backend, kleine Fehler behoben.

* Typehints für das Backend hinzugefügt, autogeneriert mit run_flaschengeist.
* Kleinere Warnungen behoben (typing issues)
* Anpassungen an restruckturierungen des Backends
This commit is contained in:
Ferdinand Thiessen 2020-10-19 01:45:06 +02:00
parent ed41acfdd9
commit 3c8748f044
12 changed files with 153 additions and 169 deletions

View File

@ -1,7 +1,8 @@
import axios, { AxiosInstance } from 'axios'; import axios, { AxiosInstance } from 'axios';
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { Token, UserStateInterface } from 'src/plugins/user/store/user'; import { UserStateInterface } from 'src/plugins/user/store/user';
import config from '../config'; import config from '../config';
import { Store } from 'vuex';
declare module 'vue/types/vue' { declare module 'vue/types/vue' {
interface Vue { interface Vue {
@ -9,18 +10,15 @@ declare module 'vue/types/vue' {
} }
} }
export default boot(({ Vue, store }) => { export default boot<Store<any>>(({ Vue, store }) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
Vue.prototype.$axios = axios; Vue.prototype.$axios = axios;
axios.defaults.baseURL = config.baseURL; axios.defaults.baseURL = config.baseURL;
axios.interceptors.request.use(config => { axios.interceptors.request.use(config => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment const session: Session = store.getters['user/session'];
const token: Token = store.getters['user/token']; if (session) {
if (token) { config.headers['Authorization'] = 'Token ' + session.token;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
config.headers['Token'] = token.token;
//config.headers['Authorization'] = 'Token ' + token.token;
} }
return config; return config;
}); });

View File

@ -1,41 +1,37 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { UserStateInterface } from 'src/plugins/user/store/user';
import { RouteRecord } from 'vue-router'; import { RouteRecord } from 'vue-router';
import { Store } from 'vuex'
import { StateInterface } from 'src/store';
export default boot(({ router, store }) => { export default boot<Store<StateInterface>>(({ router, store }) => {
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access store.dispatch('user/loadFromLocalStorage').then(() => {
store.dispatch('user/loadFromLocalStorage'); const permissions = store.getters['user/permissions'];
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/ban-ts-comment console.log('login_boot', permissions);
// @ts-ignore if (
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access to.matched.some((record: RouteRecord) => {
//const user: UserStateInterface = (<UserStateInterface>store.state).user; // permissions is set AND has NO matching permission
//const user: UserStateInterface = store.getters['user/user']; return (
const permissions: [] = store.getters['user/permissions']; permissions in record.meta &&
console.log('login_boot', permissions); !(
if ( (
to.matched.some((record: RouteRecord) => { record.meta.permissions.filter((value: string) =>
// permissions is set AND has NO matching permission permissions.includes(value)
return ( ).length > 0
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access )
record.meta.permissions !== undefined &&
!(
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
(
record.meta.permissions.filter((value: string) =>
permissions.includes(value)
).length > 0
) )
) );
); })
}) ) {
) { next({
next({ path: '/login',
path: '/login', query: { redirect: to.fullPath }
query: { redirect: to.fullPath } });
}); } else {
} else { next();
next(); }
} }).catch(error => {
}); console.exception(error);
});
})
}); });

View File

@ -1,6 +1,7 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { RouteConfig } from 'vue-router'; import { RouteConfig } from 'vue-router';
import { Module, Store } from 'vuex'; import { Module, Store } from 'vuex';
import { StateInterface } from 'src/store';
const config = { const config = {
// Do not change required Modules !! // Do not change required Modules !!
@ -19,7 +20,7 @@ interface ShortCutLink {
interface Plugin { interface Plugin {
name: string; name: string;
routes: RouteConfig[]; routes: RouteConfig[];
store?: Map<string, Module<any, any>>; store?: Map<string, Module<any, StateInterface>>;
mainLink: PluginMainLink; mainLink: PluginMainLink;
requiredModules: string[]; requiredModules: string[];
shortcuts: ShortCutLink[]; shortcuts: ShortCutLink[];
@ -61,7 +62,7 @@ export {
}; };
// combine routes from source to target // combine routes from source to target
function combineRoutes( function combineRoutes (
target: RouteConfig[], target: RouteConfig[],
source: RouteConfig[] source: RouteConfig[]
): RouteConfig[] { ): RouteConfig[] {
@ -74,15 +75,15 @@ function combineRoutes(
); );
if (targetRouteConfig) { if (targetRouteConfig) {
// if exists first layer in target exist iterate through 2nd layer e.g. /main/user, /main/about // if exists first layer in target exist iterate through 2nd layer e.g. /main/user, /main/about
sourceRouteConfig.children?.forEach( sourceRouteConfig.children ?.forEach(
(sourcePluginChildRouteConfig: RouteConfig) => { (sourcePluginChildRouteConfig: RouteConfig) => {
const targetPluginRouteConfig: const targetPluginRouteConfig:
| RouteConfig | RouteConfig
| undefined = targetRouteConfig.children?.find( | undefined = targetRouteConfig.children ?.find(
(routeConfig: RouteConfig) => { (routeConfig: RouteConfig) => {
return sourcePluginChildRouteConfig.path == routeConfig.path; return sourcePluginChildRouteConfig.path == routeConfig.path;
} }
); );
if (targetPluginRouteConfig) { if (targetPluginRouteConfig) {
// if 2nd layer in target exist check if target has children path. // if 2nd layer in target exist check if target has children path.
if (targetPluginRouteConfig.children) { if (targetPluginRouteConfig.children) {
@ -113,7 +114,7 @@ function combineRoutes(
} }
// combine Links of Plugins from source to target // combine Links of Plugins from source to target
function combineMainLinks( function combineMainLinks (
target: PluginMainLink[], target: PluginMainLink[],
source: PluginMainLink source: PluginMainLink
): PluginMainLink[] { ): PluginMainLink[] {
@ -134,7 +135,7 @@ function combineMainLinks(
} }
// loade plugins // loade plugins
function loadPlugin( function loadPlugin (
loadedPlugins: LoadedPlugins, loadedPlugins: LoadedPlugins,
modules: string[], modules: string[],
plugins: Plugin[], plugins: Plugin[],
@ -180,7 +181,7 @@ function loadPlugin(
// "async" is optional; // "async" is optional;
// more info on params: https://quasar.dev/quasar-cli/cli-documentation/boot-files#Anatomy-of-a-boot-file // more info on params: https://quasar.dev/quasar-cli/cli-documentation/boot-files#Anatomy-of-a-boot-file
export default boot(({ Vue, router, store }) => { export default boot<Store<StateInterface>>(({ Vue, router, store }) => {
const plugins: Plugin[] = []; const plugins: Plugin[] = [];
let loadedPlugins: LoadedPlugins = { let loadedPlugins: LoadedPlugins = {
routes: [], routes: [],

View File

@ -1,5 +1,5 @@
const config = { const config = {
baseURL: "http://flaschengeist.local:5000" baseURL: 'http://flaschengeist.local:5000'
}; };
export default config; export default config;

31
src/flaschengeist.d.ts vendored Normal file
View File

@ -0,0 +1,31 @@
interface Session {
user: User;
expires: Date;
token: string;
lifetime: number;
browser: string;
platform: string;
}
interface User {
roles: Array<Role>;
userid: string;
display_name: string;
firstname: string;
lastname: string;
mail: string;
}
interface Permission {
id: number;
name: string;
}
interface Role {
permissions: Array<Permission>;
id: number;
name: string;
}
interface UserAttribute {
id: number;
user: number;
name: string;
value: string;
}

View File

@ -7,9 +7,7 @@
<div class="text-h5 row"> <div class="text-h5 row">
Deine Sessions: Deine Sessions:
</div> </div>
<div <div class="fit row justify-center content-center items-center q-gutter-sm">
class="fit row justify-center content-center items-center q-gutter-sm"
>
<circular-progress v-if="sessionsLoading" /> <circular-progress v-if="sessionsLoading" />
<q-card <q-card
class="col-12" class="col-12"
@ -27,12 +25,18 @@
<div class="row"> <div class="row">
<div class="col-xs-12 col-sm-6"> <div class="col-xs-12 col-sm-6">
Browser: Browser:
<q-icon :name="getBrowserIcon(session.browser)" size="24px" /> <q-icon
:name="getBrowserIcon(session.browser)"
size="24px"
/>
{{ session.browser }} {{ session.browser }}
</div> </div>
<div class="col-xs-12 col-sm-6"> <div class="col-xs-12 col-sm-6">
Plattform: Plattform:
<q-icon :name="getPlatformIcon(session.platform)" size="24px" /> <q-icon
:name="getPlatformIcon(session.platform)"
size="24px"
/>
{{ session.platform }} {{ session.platform }}
</div> </div>
</div> </div>
@ -58,7 +62,10 @@
</q-card> </q-card>
</div> </div>
<div class="row"> <div class="row">
<q-btn label="show sessions" @click="showRootGetters" /> <q-btn
label="show sessions"
@click="showRootGetters"
/>
</div> </div>
</q-page> </q-page>
</div> </div>
@ -103,7 +110,7 @@ export default defineComponent({
root.$store.dispatch('sessions/deleteSession', token); root.$store.dispatch('sessions/deleteSession', token);
} }
function isThisSession(token: string) { function isThisSession(token: string) {
return root.$store.getters['user/token'].token == token; return root.$store.getters['user/session'].token == token;
} }
const sessionsLoading = computed(() => { const sessionsLoading = computed(() => {

View File

@ -5,16 +5,22 @@
class="fit row justify-center content-center items-center" class="fit row justify-center content-center items-center"
v-if="checkMain" v-if="checkMain"
> >
<q-card class="col-4" height=""> <q-card
class="col-4"
height=""
>
<q-card-section> <q-card-section>
Name: {{ userState.firstname }} {{ userState.lastname }}<br /> Name: {{ userSession.user.firstname }} {{ userSession.user.lastname }}<br />
E-Mail: {{ userState.mail }}<br /> E-Mail: {{ userSession.user.mail }}<br />
Roles: Roles:
<ul v-for="role in userState.roles" v-bind:key="role"> <ul
v-for="role in userSession.user.roles"
v-bind:key="role"
>
<li>{{ role }}</li> <li>{{ role }}</li>
</ul> </ul>
<br /> <br />
Token expires: {{ userToken.expires }} Token expires: {{ userSession.expires }}
</q-card-section> </q-card-section>
</q-card> </q-card>
</q-page> </q-page>
@ -30,19 +36,16 @@ import { mainLink } from '../plugin';
export default defineComponent({ export default defineComponent({
// name: 'PageName' // name: 'PageName'
setup(_, { root }) { setup(_, { root }) {
const userState = computed(
() => <UserStateInterface>root.$store.getters['user/user']
);
const userPermissions = computed( const userPermissions = computed(
() => <UserStateInterface>root.$store.getters['user/permissions'] () => <UserStateInterface>root.$store.getters['user/permissions']
); );
const userToken = computed( const userSession = computed(
() => <UserStateInterface>root.$store.getters['user/token'] () => <UserStateInterface>root.$store.getters['user/session']
); );
const checkMain = computed(() => { const checkMain = computed(() => {
return mainLink.name == root.$route.name; return mainLink.name == root.$route.name;
}); });
return { userState, userPermissions, userToken, checkMain }; return { userPermissions, userSession, checkMain };
} }
}); });
</script> </script>

View File

@ -3,6 +3,7 @@ import { Module } from 'vuex';
import userStore from './store/user'; import userStore from './store/user';
import sessionsStore from './store/session'; import sessionsStore from './store/session';
import routes from './routes'; import routes from './routes';
import { StateInterface } from 'src/store';
const mainLink: PluginMainLink = { const mainLink: PluginMainLink = {
name: 'user', name: 'user',
@ -27,7 +28,7 @@ const plugin: Plugin = {
shortcutsOut: [], shortcutsOut: [],
shortcuts: [], shortcuts: [],
version: '0.0.1', version: '0.0.1',
store: new Map<string, Module<any, any>>([ store: new Map<string, Module<any, StateInterface>>([
['user', userStore], ['user', userStore],
['sessions', sessionsStore] ['sessions', sessionsStore]
]) ])

View File

@ -1,38 +1,29 @@
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 'src/boot/axios';
import { LoginData } from 'src/plugins/user/models';
import { AxiosResponse } from 'axios';
import { LocalStorage } from 'quasar';
import { Router } from 'src/router';
export interface SessionInterface { export interface SessionInterface {
sessions: Session[]; sessions: Session[];
loading: boolean; loading: boolean;
} }
export interface Session {
browser: string;
expires: string;
lifetime: number;
platform: string;
token: string;
}
const state: SessionInterface = { const state: SessionInterface = {
sessions: [], sessions: [],
loading: false loading: false
}; };
const mutations: MutationTree<SessionInterface> = { const mutations: MutationTree<SessionInterface> = {
setSessions(state, sessions: Session[]) { setSessions (state, sessions: Session[]) {
state.sessions = sessions; state.sessions = sessions;
}, },
setLoading(state, value: boolean) { setLoading (state, value: boolean) {
state.loading = value; state.loading = value;
} }
}; };
const actions: ActionTree<SessionInterface, StateInterface> = { const actions: ActionTree<SessionInterface, StateInterface> = {
getSessions({ commit, rootGetters }) { getSessions ({ commit, rootGetters }) {
console.log(rootGetters); console.log(rootGetters);
commit('setLoading', true); commit('setLoading', true);
axios axios
@ -48,7 +39,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
commit('setLoading', false); commit('setLoading', false);
}); });
}, },
deleteSession({ commit, dispatch, rootGetters }, token) { deleteSession ({ commit, dispatch }, token: string) {
commit('setLoading', true); commit('setLoading', true);
console.log('axios', axios); console.log('axios', axios);
axios axios
@ -57,7 +48,7 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
void dispatch('getSessions'); void dispatch('getSessions');
}) })
.catch(error => { .catch(error => {
console.exception(); console.exception(error);
}) })
.finally(() => { .finally(() => {
commit('setLoading', false); commit('setLoading', false);
@ -66,10 +57,10 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
}; };
const getters: GetterTree<SessionInterface, StateInterface> = { const getters: GetterTree<SessionInterface, StateInterface> = {
sessions(state) { sessions (state) {
return state.sessions; return state.sessions;
}, },
loading(state) { loading (state) {
return state.loading; return state.loading;
} }
}; };

View File

@ -6,69 +6,48 @@ import { AxiosResponse } from 'axios';
import { LocalStorage, Loading } from 'quasar'; import { LocalStorage, Loading } from 'quasar';
import { Router } from 'src/router'; import { Router } from 'src/router';
export interface Token {
browser: string;
expires: string;
lifetime: number;
platform: string;
token: string;
}
export interface User {
display_name: string | null;
firstname: string;
lastname: string;
mail: string;
roles: string[];
userid: string;
}
export interface UserStateInterface extends LoginResponse { export interface UserStateInterface extends LoginResponse {
loginLoading: boolean; loginLoading: boolean;
} }
export interface LoginResponse { export interface LoginResponse {
permissions: string[]; permissions: string[];
token: Token; session: Session;
user: User;
userid: string;
} }
const state: UserStateInterface = { const state: UserStateInterface = {
permissions: [], permissions: [],
token: { browser: '', expires: '', lifetime: -1, platform: '', token: '' }, session: {
user: { browser: '', expires: new Date(), lifetime: -1, platform: '', token: '',
display_name: '', user: {
firstname: '', display_name: '',
lastname: '', firstname: '',
mail: '', lastname: '',
roles: [], mail: '',
userid: '' roles: [],
userid: ''
}
}, },
loginLoading: false loginLoading: false
}; };
const mutations: MutationTree<UserStateInterface> = { const mutations: MutationTree<UserStateInterface> = {
setPermissions(state, data: []) { setPermissions (state, data: []) {
state.permissions = data; state.permissions = data;
}, },
setToken(state, data: Token) { setSession (state, data: Session) {
state.token = data; state.session = data;
}, },
setUser(state, data: User) { setLoginLoading (state, data: boolean) {
state.user = data;
},
setLoginLoading(state, data: boolean) {
state.loginLoading = data; state.loginLoading = data;
}, },
showState(state) { showState (state) {
console.log(state); console.log(state);
} }
}; };
const actions: ActionTree<UserStateInterface, StateInterface> = { const actions: ActionTree<UserStateInterface, StateInterface> = {
login({ commit }, data: LoginData) { login ({ commit }, data: LoginData) {
console.log('bla');
commit('setLoginLoading', true); commit('setLoginLoading', true);
Loading.show({ message: 'Du wirst eingeloggt' }); Loading.show({ message: 'Du wirst eingeloggt' });
void axios void axios
@ -76,12 +55,10 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
.then((response: AxiosResponse<LoginResponse>) => { .then((response: AxiosResponse<LoginResponse>) => {
commit('setPermissions', response.data.permissions); commit('setPermissions', response.data.permissions);
console.log('saved permisisons'); console.log('saved permisisons');
commit('setToken', response.data.token); commit('setSession', response.data.session);
commit('setUser', response.data.user);
commit('showState'); commit('showState');
LocalStorage.set('permissions', response.data.permissions); LocalStorage.set('permissions', response.data.permissions);
LocalStorage.set('token', response.data.token); LocalStorage.set('session', response.data.session);
LocalStorage.set('user', response.data.user);
void Router.push({ name: 'user' }); void Router.push({ name: 'user' });
}) })
@ -93,46 +70,29 @@ const actions: ActionTree<UserStateInterface, StateInterface> = {
Loading.hide(); Loading.hide();
}); });
}, },
loadFromLocalStorage({ commit }) { loadFromLocalStorage ({ commit }) {
console.log('load from store'); console.log('load from store');
let data = LocalStorage.getItem('permissions'); let data = LocalStorage.getItem('permissions');
commit('setPermissions', data ? data : []); commit('setPermissions', data ? data : []);
data = LocalStorage.getItem('token'); data = LocalStorage.getItem('session');
commit( commit(
'setToken', 'setSession',
data data
? data ? data
: { browser: '', expires: '', lifetime: -1, platform: '', token: '' } : { browser: '', expires: new Date(), lifetime: -1, platform: '', token: '' }
);
data = LocalStorage.getItem('user');
commit(
'setUser',
data
? data
: {
display_name: '',
firstname: '',
lastname: '',
mail: '',
roles: [],
userid: ''
}
); );
commit('showState'); commit('showState');
} }
}; };
const getters: GetterTree<UserStateInterface, StateInterface> = { const getters: GetterTree<UserStateInterface, StateInterface> = {
permissions({ permissions }) { permissions ({ permissions }) {
return permissions; return permissions;
}, },
token({ token }) { session ({ session }) {
return token; return session;
}, },
user({ user }) { loginLoading ({ loginLoading }) {
return user;
},
loginLoading({ loginLoading }) {
return loginLoading; return loginLoading;
} }
}; };

View File

@ -1,8 +1,8 @@
import { route } from 'quasar/wrappers'; import { route } from 'quasar/wrappers';
import VueRouter from 'vue-router'; import VueRouter from 'vue-router';
import { Store } from 'vuex'; import { Store } from 'vuex';
import { StateInterface } from '../store';
import routes from './routes'; import routes from './routes';
import { StateInterface } from 'src/store';
/* /*
* If not building with SSR mode, you can * If not building with SSR mode, you can
@ -19,7 +19,7 @@ export const Router: VueRouter = new VueRouter({
base: process.env.VUE_ROUTER_BASE base: process.env.VUE_ROUTER_BASE
}); });
export default route<Store<StateInterface>>(function({ Vue }) { export default route<Store<StateInterface>>(function ({ Vue }) {
Vue.use(VueRouter); Vue.use(VueRouter);
return Router; return Router;

View File

@ -1,20 +1,16 @@
import { store } from 'quasar/wrappers'; import { store } from 'quasar/wrappers';
import Vuex from 'vuex'; import Vuex from 'vuex';
import user from './module-user';
import { UserStateInterface } from './module-user/state';
/* /*
* If not building with SSR mode, you can * If not building with SSR mode, you can
* directly export the Store instantiation * directly export the Store instantiation
*/ */
export interface StateInterface { export interface StateInterface {
// Define your own store structure, using submodules if needed
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>({