Better offline detection (e.g. if database is offline)
This commit is contained in:
parent
97b60298ec
commit
c8708be39d
|
@ -3,7 +3,6 @@ import { boot } from 'quasar/wrappers';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import { Store } from 'vuex';
|
import { Store } from 'vuex';
|
||||||
import { StateInterface } from 'src/store';
|
import { StateInterface } from 'src/store';
|
||||||
import { Notify } from 'quasar';
|
|
||||||
|
|
||||||
declare module 'vue/types/vue' {
|
declare module 'vue/types/vue' {
|
||||||
interface Vue {
|
interface Vue {
|
||||||
|
@ -11,7 +10,7 @@ declare module 'vue/types/vue' {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default boot<Store<StateInterface>>(({ Vue, store }) => {
|
export default boot<Store<StateInterface>>(({ Vue, store, router }) => {
|
||||||
// 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;
|
||||||
|
@ -30,7 +29,7 @@ export default boot<Store<StateInterface>>(({ Vue, store }) => {
|
||||||
/***
|
/***
|
||||||
* Intercept responses
|
* Intercept responses
|
||||||
* - filter 401 --> logout
|
* - filter 401 --> logout
|
||||||
* - filter timeout or 502 --> backendOffline
|
* - filter timeout or 502-504 --> backendOffline
|
||||||
*/
|
*/
|
||||||
axios.interceptors.response.use(
|
axios.interceptors.response.use(
|
||||||
response => response,
|
response => response,
|
||||||
|
@ -39,17 +38,15 @@ export default boot<Store<StateInterface>>(({ Vue, store }) => {
|
||||||
const e = <AxiosError>error;
|
const e = <AxiosError>error;
|
||||||
if (
|
if (
|
||||||
e.code === 'ECONNABORTED' ||
|
e.code === 'ECONNABORTED' ||
|
||||||
(e.response && e.response.status === 502)
|
(e.response && e.response.status >= 502 && e.response.status <= 504)
|
||||||
) {
|
) {
|
||||||
store.commit('session/setOffline', true);
|
return router.push({
|
||||||
} else if (e.response && e.response.status == 401) {
|
name: 'offline',
|
||||||
return store.dispatch('session/clearCurrent');
|
query: { redirect: router.currentRoute.fullPath }
|
||||||
} else {
|
|
||||||
Notify.create({
|
|
||||||
type: 'error',
|
|
||||||
message: 'Eine Aktion konnte nicht durchgeführt werden.',
|
|
||||||
group: false
|
|
||||||
});
|
});
|
||||||
|
} else if (e.response && e.response.status == 401) {
|
||||||
|
if (router.currentRoute.name !== 'login')
|
||||||
|
return store.dispatch('session/clearCurrent');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="q-pa-md q-gutter-sm">
|
|
||||||
<q-dialog
|
|
||||||
v-model="offline"
|
|
||||||
persistent
|
|
||||||
transition-show="scale"
|
|
||||||
transition-hide="scale"
|
|
||||||
>
|
|
||||||
<q-card style="width: 400px">
|
|
||||||
<q-card-section class="bg-negative text-white">
|
|
||||||
<div class="text-h6 col-12">Backend offline</div>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-card-section>
|
|
||||||
<span>Es scheint, dass das Backend aktuell offline ist.</span>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-card-actions align="right" class="bg-white text-negative">
|
|
||||||
<q-btn flat label="Erneut versuchen" v-close-popup @click="reload" />
|
|
||||||
</q-card-actions>
|
|
||||||
</q-card>
|
|
||||||
</q-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { computed, defineComponent } from '@vue/composition-api';
|
|
||||||
import { Store } from 'vuex';
|
|
||||||
import { StateInterface } from 'src/store';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'BackendOffline',
|
|
||||||
setup(_, { root }) {
|
|
||||||
const offline = computed(
|
|
||||||
() => (<Store<StateInterface>>root.$store).state.session.backendOffline
|
|
||||||
);
|
|
||||||
|
|
||||||
const reload = function() {
|
|
||||||
root.$store.commit('session/setOffline', false);
|
|
||||||
root.$router.go(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
return { offline, reload };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -1,6 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<q-layout view="hHh lpr lFf">
|
<q-layout view="hHh lpr lFf">
|
||||||
<q-header elevated class="bg-primary text-white">
|
<q-header
|
||||||
|
elevated
|
||||||
|
class="bg-primary text-white"
|
||||||
|
>
|
||||||
<q-toolbar>
|
<q-toolbar>
|
||||||
<!-- Button um Navigationsleiset ein und auszublenden. Nötig bei Desktop? -->
|
<!-- Button um Navigationsleiset ein und auszublenden. Nötig bei Desktop? -->
|
||||||
<q-btn
|
<q-btn
|
||||||
|
@ -36,7 +39,13 @@
|
||||||
: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>
|
||||||
|
|
||||||
|
@ -74,7 +83,10 @@
|
||||||
/>
|
/>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
|
||||||
<div class="q-mini-drawer-hide absolute" style="top: 15px; right: -11px">
|
<div
|
||||||
|
class="q-mini-drawer-hide absolute"
|
||||||
|
style="top: 15px; right: -11px"
|
||||||
|
>
|
||||||
<q-btn
|
<q-btn
|
||||||
size="sm"
|
size="sm"
|
||||||
dense
|
dense
|
||||||
|
@ -97,9 +109,6 @@
|
||||||
:permissions="link.permissions"
|
:permissions="link.permissions"
|
||||||
/>
|
/>
|
||||||
</q-drawer>
|
</q-drawer>
|
||||||
|
|
||||||
<BackendOffline />
|
|
||||||
|
|
||||||
<q-page-container>
|
<q-page-container>
|
||||||
<router-view />
|
<router-view />
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
|
@ -109,7 +118,6 @@
|
||||||
<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 BackendOffline from 'components/loading/BackendOffline.vue';
|
|
||||||
import { Screen, Loading } from 'quasar';
|
import { Screen, Loading } 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';
|
||||||
|
@ -121,23 +129,23 @@ const links = [
|
||||||
name: 'about',
|
name: 'about',
|
||||||
title: 'Über Flaschengeist',
|
title: 'Über Flaschengeist',
|
||||||
link: 'about',
|
link: 'about',
|
||||||
icon: 'mdi-information'
|
icon: 'mdi-information',
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const shortcuts = [
|
const shortcuts = [
|
||||||
{
|
{
|
||||||
link: 'about',
|
link: 'about',
|
||||||
icon: 'mdi-information'
|
icon: 'mdi-information',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
link: 'user',
|
link: 'user',
|
||||||
icon: 'mdi-account'
|
icon: 'mdi-account',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
link: 'user-plugin1',
|
link: 'user-plugin1',
|
||||||
icon: 'mdi-account-plus'
|
icon: 'mdi-account-plus',
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
declare module 'vue/types/vue' {
|
declare module 'vue/types/vue' {
|
||||||
|
@ -148,14 +156,14 @@ declare module 'vue/types/vue' {
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'MainLayout',
|
name: 'MainLayout',
|
||||||
components: { EssentialLink, ShortCutLink, BackendOffline },
|
components: { EssentialLink, ShortCutLink },
|
||||||
setup(_, ctx) {
|
setup(_, ctx) {
|
||||||
const leftDrawer = ref(false);
|
const leftDrawer = ref(false);
|
||||||
|
|
||||||
const leftDrawerOpen = ref(
|
const leftDrawerOpen = ref(
|
||||||
computed({
|
computed({
|
||||||
get: () => (leftDrawer.value || Screen.gt.sm ? true : false),
|
get: () => (leftDrawer.value || Screen.gt.sm ? true : false),
|
||||||
set: (val: boolean) => (leftDrawer.value = val)
|
set: (val: boolean) => (leftDrawer.value = val),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
const leftDrawerMini = ref(false);
|
const leftDrawerMini = ref(false);
|
||||||
|
@ -200,8 +208,8 @@ export default defineComponent({
|
||||||
links,
|
links,
|
||||||
pluginChildLinks,
|
pluginChildLinks,
|
||||||
shortcuts,
|
shortcuts,
|
||||||
logout
|
logout,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -37,8 +37,6 @@
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
</q-header>
|
</q-header>
|
||||||
|
|
||||||
<BackendOffline />
|
|
||||||
|
|
||||||
<q-page-container>
|
<q-page-container>
|
||||||
<router-view />
|
<router-view />
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
|
@ -48,10 +46,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from '@vue/composition-api';
|
import { defineComponent } from '@vue/composition-api';
|
||||||
import ShortCutLink from 'components/navigation/ShortCutLink.vue';
|
import ShortCutLink from 'components/navigation/ShortCutLink.vue';
|
||||||
import BackendOffline from 'components/loading/BackendOffline.vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'OutLayout',
|
name: 'OutLayout',
|
||||||
components: { ShortCutLink, BackendOffline }
|
components: { ShortCutLink },
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div style="font-size: 30vh">
|
|
||||||
404
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-h2" style="opacity:.4">
|
|
||||||
Oops. Nothing here...
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<q-btn
|
|
||||||
class="q-mt-xl"
|
|
||||||
color="white"
|
|
||||||
text-color="blue"
|
|
||||||
unelevated
|
|
||||||
to="/"
|
|
||||||
label="Go Home"
|
|
||||||
no-caps
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from '@vue/composition-api';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'Error404'
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<div class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<svg
|
||||||
|
style="max-width: 400px;"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 292.761 292.761"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
enable-background="new 0 0 292.761 292.761"
|
||||||
|
fill="white"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<circle
|
||||||
|
cx="87.493"
|
||||||
|
cy="25.907"
|
||||||
|
r="25.907"
|
||||||
|
/>
|
||||||
|
<path d="m194.386,209.531l-42.802-36.895c-1.825-1.573-4.001-2.683-6.345-3.237l-72.533-17.136 47.755,.703v-34.439l-46.525-26.18 50.36,14.829c4.016,1.182 8.356,0.276 11.564-2.414l38.264-32.093c1.1-0.923 2.001-1.994 2.698-3.161 2.656-4.442 2.36-10.26-1.154-14.449-4.437-5.29-12.32-5.981-17.61-1.544l-33.127,27.785-45.605-13.428 41.402,1.292 16.027-13.442-6.021-2.955-3.631,3.945-18.134,2.86h-37.216c-9.665,0-17.501,7.835-17.501,17.501v90.395l.029-.024c0.252,6.569 4.819,12.433 11.527,14.019l68.966,16.292 40.024,34.501c2.834,2.442 6.318,3.639 9.787,3.639 4.213,0 8.402-1.765 11.368-5.206 5.41-6.278 4.708-15.75-1.567-21.158z" />
|
||||||
|
<path d="m233.888,50.21l-43.49-21.349c-2.17-1.065-4.545-1.612-6.94-1.612-0.861,0-1.724,0.071-2.581,0.213l-13.243,2.2-24.213-11.886c-6.197-3.043-13.688-0.485-16.728,5.713-3.042,6.197-0.484,13.687 5.713,16.729l14.402,7.069 3.539-2.969c4.405-3.694 9.994-5.729 15.738-5.729 7.266,0 14.11,3.192 18.777,8.756 6.702,7.991 7.61,19.371 2.258,28.32-0.529,0.884-1.127,1.717-1.759,2.523l28.035,13.762c0.916,0.45 1.914,0.664 2.967,0.664 5.79,0 13.263-6.495 18.05-16.247 5.66-11.524 5.424-23.236-0.525-26.157z" />
|
||||||
|
<path d="m102.363,202.426l2.531,6.9-13.835,65.324c-1.716,8.105 3.463,16.065 11.567,17.782 1.048,0.222 2.092,0.328 3.122,0.328 6.936-0.001 13.165-4.839 14.66-11.896l14.265-67.357-5.513-4.752-26.797-6.329z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="text-h2">
|
||||||
|
Der Admin is über's Kabel gestolpert!
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Aktuell kann der Backend Server nicht erreicht werden, wir versuchen es in {{reload}} Sekunden erneut.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
defineComponent,
|
||||||
|
ref,
|
||||||
|
onMounted,
|
||||||
|
onUnmounted,
|
||||||
|
} from '@vue/composition-api';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Offline',
|
||||||
|
setup(_, { root }) {
|
||||||
|
const reload = ref(10);
|
||||||
|
const ival = setInterval(() => {
|
||||||
|
reload.value -= 1;
|
||||||
|
if (reload.value === 0) {
|
||||||
|
const path = <string | null>root.$route.query.redirect || '/login';
|
||||||
|
root.$router.replace({ path: path });
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
onUnmounted(() => clearInterval(ival));
|
||||||
|
return { reload };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -11,7 +11,6 @@ export interface SessionInterface {
|
||||||
currentSession?: FG.Session;
|
currentSession?: FG.Session;
|
||||||
sessions: FG.Session[];
|
sessions: FG.Session[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
backendOffline: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,8 +26,7 @@ function loadCurrentSession() {
|
||||||
const state: SessionInterface = {
|
const state: SessionInterface = {
|
||||||
sessions: [],
|
sessions: [],
|
||||||
currentSession: loadCurrentSession() || undefined,
|
currentSession: loadCurrentSession() || undefined,
|
||||||
loading: false,
|
loading: false
|
||||||
backendOffline: false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mutations: MutationTree<SessionInterface> = {
|
const mutations: MutationTree<SessionInterface> = {
|
||||||
|
@ -45,9 +43,6 @@ const mutations: MutationTree<SessionInterface> = {
|
||||||
},
|
},
|
||||||
setLoading(state, value: boolean) {
|
setLoading(state, value: boolean) {
|
||||||
state.loading = value;
|
state.loading = value;
|
||||||
},
|
|
||||||
setOffline(state, value: boolean) {
|
|
||||||
state.backendOffline = value;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,15 +63,17 @@ const actions: ActionTree<SessionInterface, StateInterface> = {
|
||||||
root: true
|
root: true
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((error: AxiosError) => {
|
||||||
Notify.create({
|
if (error.response && error.response.status === 401)
|
||||||
group: false,
|
Notify.create({
|
||||||
type: 'negative',
|
group: false,
|
||||||
message: 'Benutzername oder Passwort sind falsch.',
|
type: 'negative',
|
||||||
timeout: 10000,
|
message: 'Benutzername oder Passwort sind falsch.',
|
||||||
progress: true,
|
timeout: 10000,
|
||||||
actions: [{ icon: 'mdi-close', color: 'white' }]
|
progress: true,
|
||||||
});
|
actions: [{ icon: 'mdi-close', color: 'white' }]
|
||||||
|
});
|
||||||
|
return Promise.reject(error.response);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,12 +43,16 @@ const routes: RouteConfig[] = [
|
||||||
name: 'error',
|
name: 'error',
|
||||||
component: () => import('pages/PluginError.vue')
|
component: () => import('pages/PluginError.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/offline',
|
||||||
|
name: 'offline',
|
||||||
|
component: () => import('pages/Offline.vue')
|
||||||
|
},
|
||||||
// Always leave this as last one,
|
// Always leave this as last one,
|
||||||
// but you can also remove it
|
// but you can also remove it
|
||||||
{
|
{
|
||||||
path: '*',
|
path: '*',
|
||||||
redirect: 'login',
|
redirect: 'login'
|
||||||
component: () => import('pages/Error404.vue')
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue