import { createRouter, createWebHistory, type RouterHistory, type RouteLocationNormalized, type RouteLocationRaw, type RouteRecordRaw, } from 'vue-router' import { useSessionStore } from '@/stores/session' import { installDocumentTitle } from '@/composables/documentTitle' export type AuthRequirement = 'public' | 'session' | 'verified' | 'any' const routes: RouteRecordRaw[] = [ { 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: '/media', component: () => import('@/views/MediaView.vue'), meta: { auth: 'verified', title: 'Media' }, }, { path: '/authors', component: () => import('@/views/AuthorsView.vue'), meta: { auth: 'verified', title: 'Authors' }, }, { path: '/users', component: () => import('@/views/UsersView.vue'), meta: { auth: 'verified', title: 'Users' }, }, { path: '/schema', component: () => import('@/views/SchemaView.vue'), meta: { auth: 'verified', title: 'Schema', needsBuiltinTypes: true }, }, { path: '/schema/new', component: () => import('@/views/SchemaBuilderView.vue'), meta: { auth: 'verified', title: 'Schema' }, }, { path: '/schema/:key', component: () => import('@/views/SchemaTypeView.vue'), meta: { auth: 'verified', title: 'Schema', needsBuiltinTypes: true }, }, { path: '/content/:key', component: () => import('@/views/ContentListView.vue'), meta: { auth: 'verified', title: 'Content' }, }, { path: '/content/:key/new', name: 'entry-new', component: () => import('@/views/EntryEditorView.vue'), meta: { auth: 'verified', title: 'Content' }, }, { path: '/content/:key/:id', name: 'entry-edit', component: () => import('@/views/EntryEditorView.vue'), meta: { auth: 'verified', title: 'Content' }, }, { path: '/activity', component: () => import('@/views/ActivityView.vue'), meta: { auth: 'verified', title: 'Activity' }, }, { path: '/profile', component: () => import('@/views/ProfileView.vue'), meta: { auth: 'verified', title: 'Profile' }, }, { path: '/integration', component: () => import('@/views/IntegrationView.vue'), meta: { auth: 'verified', title: 'Integration' }, }, { path: '/integration/api-keys', component: () => import('@/views/ApiKeysView.vue'), meta: { auth: 'verified', title: 'Integration' }, }, { path: '/settings', component: () => import('@/views/SettingsView.vue'), meta: { auth: 'verified', title: 'Settings' }, }, { path: '/settings/media/variants', component: () => import('@/views/VariantsPage.vue'), meta: { auth: 'verified', title: 'Settings' }, }, { path: '/settings/sites', component: () => import('@/views/SitesView.vue'), meta: { auth: 'verified', title: 'Settings' }, }, { path: '/settings/media/cache', component: () => import('@/views/CachePage.vue'), meta: { auth: 'verified', title: 'Settings' }, }, { path: '/:pathMatch(.*)*', component: () => import('@/views/NotFoundView.vue'), meta: { auth: 'public', documentTitle: '404' }, }, ] export function resolveAuthRedirect( to: RouteLocationNormalized, session: ReturnType, ): 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 } export function createAppRouter(history: RouterHistory) { const router = createRouter({ history, routes }) installDocumentTitle(router) 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) } }) }) return router } const router = createAppRouter(createWebHistory(import.meta.env.BASE_URL)) export default router