feat: PrimeVue replaced by Nuxt UI using token files from web/src/theme, with AuthShell, DashboardShell, AppBoot and the route guards from routes.md

This commit is contained in:
fsylvain committed 2026-09-25 17:09:34 +02:00
1 parent 24f95a8fd7
commit eed661bb24
38 files changed
+2365 -108

No files matched your search

+143 -3
View File
@@ -1,11 +1,151 @@
import { createRouter, createWebHistory } from 'vue-router'
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/HomeView.vue') },
{ path: '/article', component: () => import('@/views/ArticleView.vue') },
{ 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: '/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: '/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