169 lines
3.6 KiB
TypeScript
169 lines
3.6 KiB
TypeScript
import {
|
|
createRouter,
|
|
createWebHistory,
|
|
type RouteLocationNormalized,
|
|
type RouteLocationRaw,
|
|
} from 'vue-router'
|
|
import { useSessionStore } from '@/stores/session'
|
|
|
|
export type AuthRequirement = 'public' | 'session' | 'verified' | 'any'
|
|
|
|
const router = createRouter({
|
|
history: createWebHistory(import.meta.env.BASE_URL),
|
|
routes: [
|
|
{
|
|
path: '/',
|
|
component: () => import('@/views/RootView.vue'),
|
|
meta: { auth: 'any' },
|
|
},
|
|
{
|
|
path: '/login',
|
|
component: () => import('@/views/LoginView.vue'),
|
|
meta: { auth: 'public' },
|
|
},
|
|
{
|
|
path: '/register',
|
|
component: () => import('@/views/RegisterView.vue'),
|
|
meta: { auth: 'public' },
|
|
},
|
|
{
|
|
path: '/register/pending',
|
|
component: () => import('@/views/PendingView.vue'),
|
|
meta: { auth: 'session' },
|
|
},
|
|
{
|
|
path: '/register/validate',
|
|
component: () => import('@/views/VerifyView.vue'),
|
|
meta: { auth: 'public' },
|
|
},
|
|
{
|
|
path: '/register/reset/request',
|
|
component: () => import('@/views/ResetRequestView.vue'),
|
|
meta: { auth: 'public' },
|
|
},
|
|
{
|
|
path: '/register/reset/validate',
|
|
component: () => import('@/views/ResetConfirmView.vue'),
|
|
meta: { auth: 'public' },
|
|
},
|
|
{
|
|
path: '/invite/accept',
|
|
component: () => import('@/views/AcceptInviteView.vue'),
|
|
meta: { auth: 'public' },
|
|
},
|
|
{
|
|
path: '/home',
|
|
component: () => import('@/views/HomeView.vue'),
|
|
meta: { auth: 'verified', title: 'Home' },
|
|
},
|
|
{
|
|
path: '/article',
|
|
component: () => import('@/views/ArticleView.vue'),
|
|
meta: { auth: 'verified', title: 'Articles' },
|
|
},
|
|
{
|
|
path: '/article/new',
|
|
component: () => import('@/views/ArticleView.vue'),
|
|
meta: { auth: 'verified', title: 'Articles' },
|
|
},
|
|
{
|
|
path: '/article/:id/edit',
|
|
component: () => import('@/views/EditorView.vue'),
|
|
meta: { auth: 'verified', title: 'Editor' },
|
|
},
|
|
{
|
|
path: '/users',
|
|
component: () => import('@/views/UsersView.vue'),
|
|
meta: { auth: 'verified', title: 'Users' },
|
|
},
|
|
{
|
|
path: '/integration',
|
|
component: () => import('@/views/IntegrationView.vue'),
|
|
meta: { auth: 'verified', title: 'Integration' },
|
|
},
|
|
{
|
|
path: '/:pathMatch(.*)*',
|
|
component: () => import('@/views/NotFoundView.vue'),
|
|
meta: { auth: 'any' },
|
|
},
|
|
],
|
|
})
|
|
|
|
export function resolveAuthRedirect(
|
|
to: RouteLocationNormalized,
|
|
session: ReturnType<typeof useSessionStore>,
|
|
): RouteLocationRaw | true {
|
|
const auth = to.meta.auth as AuthRequirement
|
|
|
|
if (auth === 'public') {
|
|
return true
|
|
}
|
|
if (session.status !== 'ready') {
|
|
return true
|
|
}
|
|
|
|
const account = session.session
|
|
|
|
if (!account) {
|
|
if (auth === 'any') {
|
|
return { path: '/login' }
|
|
}
|
|
return {
|
|
path: '/login',
|
|
query: { redirect: to.fullPath, reason: 'expired' },
|
|
}
|
|
}
|
|
|
|
if (!account.verified) {
|
|
if (auth === 'verified' || auth === 'any') {
|
|
return { path: '/register/pending' }
|
|
}
|
|
return true
|
|
}
|
|
|
|
if (auth === 'any') {
|
|
return { path: '/home' }
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
router.beforeEach((to) => {
|
|
if (to.meta.auth === 'public') {
|
|
return true
|
|
}
|
|
|
|
const session = useSessionStore()
|
|
|
|
if (session.status === 'idle') {
|
|
session.check()
|
|
return true
|
|
}
|
|
if (session.status === 'checking') {
|
|
return true
|
|
}
|
|
|
|
return resolveAuthRedirect(to, session)
|
|
})
|
|
|
|
router.afterEach((to) => {
|
|
if (to.meta.auth === 'public') {
|
|
return
|
|
}
|
|
|
|
const session = useSessionStore()
|
|
const settled =
|
|
session.status === 'idle' || session.status === 'checking'
|
|
? session.check()
|
|
: Promise.resolve()
|
|
|
|
settled.then(() => {
|
|
const destination = resolveAuthRedirect(router.currentRoute.value, session)
|
|
if (destination !== true) {
|
|
router.replace(destination)
|
|
}
|
|
})
|
|
})
|
|
|
|
export default router
|