Merge branch 'identity/develop' of https://github.com/kobolol/Hoard into identity/develop

This commit is contained in:
Jonas
2026-04-28 21:30:45 +02:00
3 changed files with 101 additions and 99 deletions
+3 -87
View File
@@ -5,12 +5,11 @@ import { useDisplay, useTheme } from 'vuetify'
import { useRoute, useRouter } from 'vue-router'
import iconImage from '@/assets/images/icon.svg'
import { Visibility, routes } from '@/plugins/routesLayout'
import { useSidebarRoutes } from '@/composables/useSidebarRoutes'
import { routes } from '@/plugins/routesLayout'
import {
AuthRequestError,
ROLE_ADMIN,
fetchCurrentUser,
hasRole,
logout,
type CurrentUser,
} from '@/services/authSession'
@@ -28,90 +27,7 @@ const isLoggingOut = ref(false)
const appBannersStore = useAppBannersStore()
const { banners } = storeToRefs(appBannersStore)
function normalizeRoutePath(path: string) {
if (!path || path === '/') {
return '/'
}
return path.endsWith('/') ? path.slice(0, -1) : path
}
function isWithinRoutePath(currentPath: string, targetPath: string) {
const normalizedCurrentPath = normalizeRoutePath(currentPath)
const normalizedTargetPath = normalizeRoutePath(targetPath)
if (normalizedTargetPath === '/') {
return normalizedCurrentPath === '/'
}
return (
normalizedCurrentPath === normalizedTargetPath ||
normalizedCurrentPath.startsWith(`${normalizedTargetPath}/`)
)
}
function resolveVisibilityRoutes(path: string, visibilityRoute?: string | string[]) {
if (Array.isArray(visibilityRoute)) {
return visibilityRoute.map((entry) => entry.trim()).filter((entry) => entry.length > 0)
}
const normalizedVisibilityRoute = visibilityRoute?.trim()
if (normalizedVisibilityRoute && normalizedVisibilityRoute.length > 0) {
return [normalizedVisibilityRoute]
}
return [path]
}
const sidebarRoutes = computed(() =>
routes.filter((item) => {
if (item.visible === Visibility.Public) {
return true
}
if (item.visible === Visibility.Authenticated) {
return currentUser.value !== null
}
if (item.visible === Visibility.Unauthenticated) {
return currentUser.value === null
}
if (item.visible === Visibility.Authorized) {
if (!currentUser.value) {
return false
}
if (!item.requiredRoles || item.requiredRoles.length === 0) {
return true
}
return item.requiredRoles.every((role) => hasRole(currentUser.value, role))
}
if (item.visible !== Visibility.Route) {
return false
}
return resolveVisibilityRoutes(item.path, item.visibilityRoute).some((targetPath) =>
isWithinRoutePath(route.path, targetPath),
)
}),
)
const adminSidebarRoutes = computed(() =>
sidebarRoutes.value.filter(
(item) =>
item.visible === Visibility.Authorized &&
Array.isArray(item.requiredRoles) &&
item.requiredRoles.some((role) => role.trim().toLowerCase() === ROLE_ADMIN),
),
)
const primarySidebarRoutes = computed(() =>
sidebarRoutes.value.filter(
(item) => !adminSidebarRoutes.value.some((adminItem) => adminItem.path === item.path),
),
)
const footerRoutes = computed(() => routes.filter((x) => x.visible === Visibility.Footer))
const { adminSidebarRoutes, primarySidebarRoutes, footerRoutes } = useSidebarRoutes(currentUser)
const activeRoute = computed(() => {
const byName = routes.find((x) => x.meta?.name === route.name)
+98
View File
@@ -0,0 +1,98 @@
import { computed, type Ref } from 'vue'
import { useRoute } from 'vue-router'
import { Visibility, routes, type LayoutRoute } from '@/plugins/routesLayout'
import { ROLE_ADMIN, hasRole, type CurrentUser } from '@/services/authSession'
function normalizeRoutePath(path: string) {
if (!path || path === '/') {
return '/'
}
return path.endsWith('/') ? path.slice(0, -1) : path
}
function isWithinRoutePath(currentPath: string, targetPath: string) {
const normalizedCurrent = normalizeRoutePath(currentPath)
const normalizedTarget = normalizeRoutePath(targetPath)
if (normalizedTarget === '/') {
return normalizedCurrent === '/'
}
return (
normalizedCurrent === normalizedTarget ||
normalizedCurrent.startsWith(`${normalizedTarget}/`)
)
}
function resolveVisibilityRoutes(path: string, visibilityRoute?: string | string[]) {
if (Array.isArray(visibilityRoute)) {
return visibilityRoute.map((entry) => entry.trim()).filter((entry) => entry.length > 0)
}
const normalized = visibilityRoute?.trim()
if (normalized && normalized.length > 0) {
return [normalized]
}
return [path]
}
function isVisible(item: LayoutRoute, currentPath: string, user: CurrentUser | null) {
switch (item.visible) {
case Visibility.Public:
return true
case Visibility.Authenticated:
return user !== null
case Visibility.Unauthenticated:
return user === null
case Visibility.Authorized:
if (!user) {
return false
}
if (!item.requiredRoles || item.requiredRoles.length === 0) {
return true
}
return item.requiredRoles.every((role) => hasRole(user, role))
case Visibility.Route:
return resolveVisibilityRoutes(item.path, item.visibilityRoute).some((target) =>
isWithinRoutePath(currentPath, target),
)
default:
return false
}
}
function isAdminRoute(item: LayoutRoute) {
return (
item.visible === Visibility.Authorized &&
Array.isArray(item.requiredRoles) &&
item.requiredRoles.some((role) => role.trim().toLowerCase() === ROLE_ADMIN)
)
}
export function useSidebarRoutes(currentUser: Ref<CurrentUser | null>) {
const route = useRoute()
const sidebarRoutes = computed(() =>
routes.filter((item) => isVisible(item, route.path, currentUser.value)),
)
const adminSidebarRoutes = computed(() => sidebarRoutes.value.filter(isAdminRoute))
const primarySidebarRoutes = computed(() =>
sidebarRoutes.value.filter((item) => !isAdminRoute(item)),
)
const footerRoutes = computed(() =>
routes.filter((item) => item.visible === Visibility.Footer),
)
return {
sidebarRoutes,
adminSidebarRoutes,
primarySidebarRoutes,
footerRoutes,
}
}
-12
View File
@@ -1,12 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})