All files / src/router index.ts

80% Statements 32/40
80% Branches 20/25
73.33% Functions 11/15
82.05% Lines 32/39

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142        32x 32x   32x           4x           6x         2x                   3x           1x                       1x           1x           1x           1x                       1x         32x 29x       29x 7x 7x       29x 16x 16x       29x 6x 6x 6x       6x                           6x       29x 2x     27x 6x   21x          
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { useTenantsStore } from '@/stores/tenants'
 
let userLoaded = false
let tenantsLoaded = false
 
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: () => import('@/views/HomePage.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('@/views/LoginPage.vue')
    },
    {
      path: '/signup',
      name: 'signup',
      component: () => import('@/views/SignupPage.vue')
    },
    {
      path: '/forgot-password',
      name: 'forgot-password',
      component: () => import('@/views/ForgotPasswordPage.vue')
    },
    {
      path: '/flows',
      name: 'flows',
      component: () => import('@/views/TenantsPage.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/entities',
      name: 'entities',
      component: () => import('@/views/EntitiesPage.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/entities/:entityId',
      name: 'entity-items',
      component: () => import('@/views/EntityItemsPage.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/members',
      name: 'members',
      component: () => import('@/views/MembersPage.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/roles',
      name: 'roles',
      component: () => import('@/views/RolesPage.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/invitations',
      name: 'invitations',
      component: () => import('@/views/InvitationsPage.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/profile',
      name: 'profile',
      component: () => import('@/views/UserProfilePage.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/api-keys',
      name: 'api-keys',
      component: () => import('@/views/ApiKeysPage.vue'),
      meta: { requiresAuth: true }
    },
    {
      path: '/:pathMatch(.*)*',
      name: 'not-found',
      component: () => import('@/views/NotFoundPage.vue')
    }
  ]
})
 
router.beforeEach(async (to, _from, next) => {
  const authStore = useAuthStore()
 
  // Reset state flags when user goes to login (e.g. after logout),
  // so subsequent logins re-run loadUser + fetchMyTenants + auto-switch
  if (to.name === 'login') {
    userLoaded = false
    tenantsLoaded = false
  }
 
  // Load user only once on first navigation
  if (!userLoaded) {
    await authStore.loadUser()
    userLoaded = true
  }
 
  // Load tenants once after user is loaded and authenticated
  if (authStore.isAuthenticated && !tenantsLoaded) {
    const tenantsStore = useTenantsStore()
    try {
      await tenantsStore.fetchMyTenants()
      // Auto-switch to first tenant if no active tenant in JWT
      // This handles fresh logins where custom:tenantId is not yet set in the token.
      // Prefer a tenant the user owns — otherwise fall back to the first one available.
      Iif (!authStore.currentTenant && tenantsStore.myTenants.length > 0) {
        const preferredTenant =
          tenantsStore.myTenants.find((t) => t.isOwner) ?? tenantsStore.myTenants[0]
        if (preferredTenant) {
          try {
            await tenantsStore.switchTenant(preferredTenant.tenantId)
          } catch {
            // Switch failed, user can manually select tenant from /flows
          }
        }
      }
    } catch {
      // fetchMyTenants failing should not block navigation
    }
    tenantsLoaded = true
  }
 
  // If going to login/signup but already authenticated, redirect to home
  if ((to.name === 'login' || to.name === 'signup') && authStore.isAuthenticated) {
    next({ name: 'home' })
  }
  // If route requires auth and not authenticated, redirect to login
  else if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    next({ name: 'login' })
  } else {
    next()
  }
})
 
export default router