import { createRouter, createWebHistory } from 'vue-router' import MainApp from './views/MainApp.vue' import LoginPage from './views/LoginPage.vue' import TeamPage from './views/TeamPage.vue' import ArchivePage from './views/ArchivePage.vue' import NoProjectsPage from './views/NoProjectsPage.vue' import InvitesPage from './views/InvitesPage.vue' import { authApi } from './api' import { useProjectsStore } from './stores/projects' // Кэш авторизации (чтобы не делать запрос при каждой навигации) let authCache = { isAuthenticated: null, // null = не проверяли, true/false = результат user: null, // Данные пользователя lastCheck: 0 } // Время жизни кэша авторизации (5 минут) const AUTH_CACHE_TTL = 5 * 60 * 1000 // Проверка авторизации (с кэшированием) const checkAuth = async (forceCheck = false) => { const now = Date.now() // Используем кэш если он валиден и не форсируем проверку if (!forceCheck && authCache.isAuthenticated !== null && (now - authCache.lastCheck) < AUTH_CACHE_TTL) { return authCache.isAuthenticated } try { const data = await authApi.check() authCache.isAuthenticated = data.success === true authCache.user = data.user || null authCache.lastCheck = now return authCache.isAuthenticated } catch { // При ошибке сети — используем кэш если есть, иначе false if (authCache.isAuthenticated !== null) { return authCache.isAuthenticated } return false } } // Сброс кэша (вызывать при logout) export const clearAuthCache = () => { authCache = { isAuthenticated: null, user: null, lastCheck: 0 } } // Глобальный доступ для api.js (избегаем циклической зависимости) window.__clearAuthCache = clearAuthCache // Установка кэша (вызывать при успешном login) export const setAuthCache = (isAuth, user = null) => { authCache = { isAuthenticated: isAuth, user, lastCheck: Date.now() } } // Получить закэшированного пользователя export const getCachedUser = () => authCache.user const routes = [ { path: '/', name: 'main', component: MainApp, meta: { requiresAuth: true, requiresProject: true } }, { path: '/team', name: 'team', component: TeamPage, meta: { requiresAuth: true, requiresProject: true } }, { path: '/archive', name: 'archive', component: ArchivePage, meta: { requiresAuth: true, requiresProject: true } }, { path: '/no-projects', name: 'no-projects', component: NoProjectsPage, meta: { requiresAuth: true } }, { path: '/invites', name: 'invites', component: InvitesPage, meta: { requiresAuth: true } }, { path: '/login', name: 'login', component: LoginPage } ] const router = createRouter({ history: createWebHistory(), routes }) // Navigation guard — проверка авторизации и наличия проектов router.beforeEach(async (to, from, next) => { // Если переходим между защищёнными страницами и кэш валиден — не проверяем сеть const needsAuth = to.meta.requiresAuth const needsProject = to.meta.requiresProject const fromProtected = from.meta?.requiresAuth // Форсируем проверку только при переходе на защищённую страницу извне // или при переходе на /login (чтобы редиректнуть если уже авторизован) const forceCheck = (needsAuth && !fromProtected) || to.path === '/login' const isAuth = await checkAuth(forceCheck) if (needsAuth && !isAuth) { // Не авторизован — на логин next('/login') return } if (to.path === '/login' && isAuth) { // Уже авторизован — на главную next('/') return } // Проверка наличия проектов для страниц, которые их требуют if (needsProject && isAuth) { const store = useProjectsStore() // Инициализируем store если ещё не инициализирован if (!store.initialized) { await store.init() } // Нет проектов — на страницу /no-projects if (store.projects.length === 0) { next('/no-projects') return } } // Если на /no-projects но проекты есть — на главную if (to.path === '/no-projects' && isAuth) { const store = useProjectsStore() if (!store.initialized) { await store.init() } if (store.projects.length > 0) { next('/') return } } next() }) export default router