115 lines
3.5 KiB
TypeScript
115 lines
3.5 KiB
TypeScript
import 'nprogress/nprogress.css';
|
|
|
|
import NProgress from 'nprogress';
|
|
import { MessagePlugin } from 'tdesign-vue-next';
|
|
import type { RouteLocationNormalized } from 'vue-router';
|
|
|
|
import { getSetupStatus } from '@/api/setup';
|
|
import router from '@/router';
|
|
import { getPermissionStore, useUserStore } from '@/store';
|
|
import { hasRoleAccess } from '@/store/modules/menu-access';
|
|
|
|
NProgress.configure({ showSpinner: false });
|
|
|
|
const isPublic = (to: RouteLocationNormalized) =>
|
|
to.path === '/login' || to.path === '/result/404' || to.path === '/setup';
|
|
|
|
let setupStatusRequest: ReturnType<typeof getSetupStatus> | null = null;
|
|
|
|
function loadSetupStatus() {
|
|
if (setupStatusRequest) return setupStatusRequest;
|
|
|
|
// Share only an in-flight request. Keeping the resolved setup state would
|
|
// send the user back to /setup after the installation marker is written.
|
|
const request = getSetupStatus();
|
|
setupStatusRequest = request;
|
|
request.then(
|
|
() => {
|
|
if (setupStatusRequest === request) setupStatusRequest = null;
|
|
},
|
|
() => {
|
|
if (setupStatusRequest === request) setupStatusRequest = null;
|
|
},
|
|
);
|
|
return setupStatusRequest;
|
|
}
|
|
|
|
router.beforeEach(async (to) => {
|
|
NProgress.start();
|
|
const userStore = useUserStore();
|
|
const permissionStore = getPermissionStore();
|
|
|
|
let setupRequired = false;
|
|
let setupStatusAvailable = false;
|
|
try {
|
|
const setupStatus = await loadSetupStatus();
|
|
setupRequired = setupStatus.required;
|
|
setupStatusAvailable = true;
|
|
} catch (error) {
|
|
setupStatusRequest = null;
|
|
if (to.path === '/setup') {
|
|
const statusCode = (error as { response?: { status?: number } }).response?.status;
|
|
if (statusCode !== 404) return true;
|
|
}
|
|
}
|
|
if (setupStatusAvailable && setupRequired && to.path !== '/setup') return '/setup';
|
|
if (to.path === '/setup') return setupStatusAvailable && setupRequired ? true : '/login';
|
|
|
|
if (isPublic(to)) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
if (!userStore.authenticated) await userStore.restoreSession();
|
|
permissionStore.initRoutes();
|
|
} catch {
|
|
userStore.clearSession();
|
|
permissionStore.restoreRoutes();
|
|
if (to.path !== '/login') {
|
|
MessagePlugin.warning('登录会话已失效,请重新登录');
|
|
return { path: '/login', query: { redirect: to.fullPath } };
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (to.path !== '/role-select' && to.path !== '/profile' && !userStore.currentRole) {
|
|
return { path: '/role-select', query: { redirect: to.fullPath } };
|
|
}
|
|
|
|
if (userStore.user.mustChangePassword && to.path !== '/profile') {
|
|
return { path: '/profile', query: { forcePassword: '1', redirect: to.fullPath } };
|
|
}
|
|
|
|
const roles = (to.meta.roleCodes || []) as string[];
|
|
const permission = to.meta.permission as string | string[] | undefined;
|
|
if (!hasRoleAccess(roles, userStore.currentRole)) return '/result/403';
|
|
if (
|
|
permission &&
|
|
(Array.isArray(permission)
|
|
? !permission.some((item) => userStore.hasPermission(item))
|
|
: !userStore.hasPermission(permission))
|
|
) {
|
|
return '/result/403';
|
|
}
|
|
return true;
|
|
});
|
|
|
|
router.afterEach(() => NProgress.done());
|
|
|
|
let handlingSessionExpiry = false;
|
|
|
|
window.addEventListener('kaidi:session-expired', async () => {
|
|
if (handlingSessionExpiry) return;
|
|
handlingSessionExpiry = true;
|
|
const userStore = useUserStore();
|
|
userStore.clearSession();
|
|
getPermissionStore().restoreRoutes();
|
|
try {
|
|
if (router.currentRoute.value.path !== '/login') {
|
|
await router.replace({ path: '/login', query: { redirect: router.currentRoute.value.fullPath } });
|
|
}
|
|
} finally {
|
|
handlingSessionExpiry = false;
|
|
}
|
|
});
|