import { createRouter, createWebHistory } from 'vue-router'; import store from './store'; const ROUTE_NAMES = { ADMIN_EDIT_PAGES: 'admin-edit-pages', ADMIN_FILE_UPLOAD: 'admin-file-upload', ADMIN_NEWSLETTER_IMPORT: 'admin-newsletter-import', REGISTER: 'register', FORGOT_PASSWORD: 'forgot-password', RESET_PASSWORD: 'reset-password', AUTH_LOGIN: 'auth-login', HOME: 'home', TERMS: 'terms', PRIVACY_POLICY: 'privacy-policy', NOT_FOUND: 'not-found', }; // Vite kann `import(\`./content/${name}.vue\`)` nicht zuverlässig auflösen. // Daher bauen wir eine statische Import-Matrix über import.meta.glob. const contentModules = import.meta.glob('./content/**/*.vue'); function loadComponent(componentName) { return async () => { const normalized = componentName || 'DefaultComponent'; const key = `./content/${normalized}.vue`; const loader = contentModules[key]; if (!loader) { // Fallback, falls DB/Backend einen unbekannten Komponenten-Namen liefert. const fallback = contentModules['./content/DefaultComponent.vue']; return fallback ? fallback() : import('./content/DefaultComponent.vue'); } return loader(); }; } function normalizePath(path) { if (!path || typeof path !== 'string') { return '/'; } let value = path.trim(); if (!value.startsWith('/')) { value = `/${value}`; } if (value.length > 1) { value = value.replace(/\/+$/, ''); } return value || '/'; } function generateRoutesFromMenu(menu) { let routes = []; menu.forEach(item => { const normalizedLink = normalizePath(item.link || ''); if (normalizedLink === '/admin/edit-pages') { return; } let route = null; if (item.link && item.link !== '') { route = { path: normalizedLink, meta: { requiresAuth: item.requiresAuth || false }, components: { default: loadComponent(item.component), rightColumn: loadComponent('ImageContent') } }; } if (item.submenu && item.submenu.length > 0) { let children = generateRoutesFromMenu(item.submenu); routes.push(...children); } if (route) { routes.push(route); } }); return routes; } function findMenuItemByPath(menu, targetPath) { const wanted = normalizePath(targetPath); for (const item of menu || []) { if (normalizePath(item.link || '') === wanted) { return item; } if (item.submenu && item.submenu.length > 0) { const found = findMenuItemByPath(item.submenu, wanted); if (found) { return found; } } } return null; } function ensureMenuRouteForPath(path) { const normalizedPath = normalizePath(path); const exists = router.getRoutes().some(r => normalizePath(r.path) === normalizedPath); if (exists) { return true; } const menuItem = findMenuItemByPath(store.state.menuData, normalizedPath); if (!menuItem || !menuItem.link) { return false; } router.addRoute({ path: normalizedPath, meta: { requiresAuth: menuItem.requiresAuth || false }, components: { default: loadComponent(menuItem.component), rightColumn: loadComponent('ImageContent') } }); return true; } const router = createRouter({ history: createWebHistory(), routes: [] }); // Verhindert endlose Wiederholungen von fehlgeschlagenen Menü-Ladeversuchen let menuDataInitialized = false; router.beforeEach(async (to, from, next) => { if (!menuDataInitialized) { menuDataInitialized = true; await store.dispatch('loadMenuData'); const routes = generateRoutesFromMenu(store.state.menuData); routes.forEach(route => router.addRoute(route)); addEditPagesRoute(); addRegisterRoute(); addForgotPasswordRoute(); addResetPasswordRoute(); addAuthLoginRoute(); // NotFound-Route existiert bereits als Core-Route; bei DB-Ausfall aktualisieren wir sie. if (store.state.menuLoadError && router.hasRoute(ROUTE_NAMES.NOT_FOUND)) { router.removeRoute(ROUTE_NAMES.NOT_FOUND); router.addRoute({ path: '/:pathMatch(.*)*', components: { default: loadComponent('ServiceUnavailableComponent'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.NOT_FOUND }); } const normalizedToPath = normalizePath(to.path); if (normalizedToPath !== to.path) { next({ path: normalizedToPath, query: to.query, hash: to.hash, replace: true }); return; } next({ ...to, replace: true }); } else { // Sicherstellen, dass Kernrouten immer verfügbar sind ensureCoreRoutes(); const normalizedToPath = normalizePath(to.path); if (normalizedToPath !== to.path) { next({ path: normalizedToPath, query: to.query, hash: to.hash, replace: true }); return; } if (to.matched.length === 0 && ensureMenuRouteForPath(normalizedToPath)) { next({ path: normalizedToPath, query: to.query, hash: to.hash, replace: true }); return; } if (to.matched.some(record => record.meta.requiresAuth) && !store.getters.isLoggedIn) { next('/auth/login'); } else { next(); } } }); function addEditPagesRoute() { if (router.hasRoute(ROUTE_NAMES.ADMIN_EDIT_PAGES)) { router.removeRoute(ROUTE_NAMES.ADMIN_EDIT_PAGES); } router.addRoute({ path: '/admin/edit-pages', components: { default: loadComponent('admin/PagePreviewComponent'), rightColumn: loadComponent('admin/EditPagesComponent') }, name: ROUTE_NAMES.ADMIN_EDIT_PAGES }); } function addFileUploadRoute() { if (router.hasRoute(ROUTE_NAMES.ADMIN_FILE_UPLOAD)) { router.removeRoute(ROUTE_NAMES.ADMIN_FILE_UPLOAD); } router.addRoute({ path: '/admin/fileupload', meta: { requiresAuth: true }, components: { default: loadComponent('admin/UploadFileManagement'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.ADMIN_FILE_UPLOAD }); } function addNewsletterImportRoute() { if (router.hasRoute(ROUTE_NAMES.ADMIN_NEWSLETTER_IMPORT)) { router.removeRoute(ROUTE_NAMES.ADMIN_NEWSLETTER_IMPORT); } router.addRoute({ path: '/admin/newsletter-import', components: { default: loadComponent('admin/NewsletterImportManagement'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.ADMIN_NEWSLETTER_IMPORT }); } function addRegisterRoute() { if (router.hasRoute(ROUTE_NAMES.REGISTER)) { router.removeRoute(ROUTE_NAMES.REGISTER); } router.addRoute({ path: '/register', components: { default: () => import('./content/authentication/RegisterContent.vue'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.REGISTER }); } function addForgotPasswordRoute() { if (router.hasRoute(ROUTE_NAMES.FORGOT_PASSWORD)) { router.removeRoute(ROUTE_NAMES.FORGOT_PASSWORD); } router.addRoute({ path: '/forgot-password', components: { default: () => import('./content/authentication/ForgotPasswordContent.vue'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.FORGOT_PASSWORD }); } function addResetPasswordRoute() { if (router.hasRoute(ROUTE_NAMES.RESET_PASSWORD)) { router.removeRoute(ROUTE_NAMES.RESET_PASSWORD); } router.addRoute({ path: '/reset-password', components: { default: () => import('./content/authentication/ResetPasswordContent.vue'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.RESET_PASSWORD }); } function addAuthLoginRoute() { if (router.hasRoute(ROUTE_NAMES.AUTH_LOGIN)) { router.removeRoute(ROUTE_NAMES.AUTH_LOGIN); } router.addRoute({ path: '/auth/login', components: { default: () => import('./content/authentication/LoginContent.vue'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.AUTH_LOGIN }); } function addHomeRoute() { if (router.hasRoute(ROUTE_NAMES.HOME)) { router.removeRoute(ROUTE_NAMES.HOME); } router.addRoute({ path: '/', components: { default: loadComponent('DefaultContent'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.HOME }); } function addTermsRoute() { if (router.hasRoute(ROUTE_NAMES.TERMS)) { router.removeRoute(ROUTE_NAMES.TERMS); } router.addRoute({ path: '/terms', components: { default: () => import('./content/disclaimers/TermsComponent.vue'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.TERMS }); } function addPrivacyPolicyRoute() { if (router.hasRoute(ROUTE_NAMES.PRIVACY_POLICY)) { router.removeRoute(ROUTE_NAMES.PRIVACY_POLICY); } router.addRoute({ path: '/privacy-policy', components: { default: () => import('./content/disclaimers/PrivacyPolicyComponent.vue'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.PRIVACY_POLICY }); } function ensureNotFoundRoute() { // Catch-All nur einmal registrieren (sonst Doppelrouten/unklares Matching) if (router.hasRoute(ROUTE_NAMES.NOT_FOUND)) return; router.addRoute({ path: '/:pathMatch(.*)*', components: { default: loadComponent('DefaultComponent'), rightColumn: loadComponent('ImageContent') }, name: ROUTE_NAMES.NOT_FOUND }); } function ensureCoreRoutes() { if (!router.hasRoute(ROUTE_NAMES.ADMIN_EDIT_PAGES)) addEditPagesRoute(); if (!router.hasRoute(ROUTE_NAMES.ADMIN_FILE_UPLOAD)) addFileUploadRoute(); if (!router.hasRoute(ROUTE_NAMES.ADMIN_NEWSLETTER_IMPORT)) addNewsletterImportRoute(); if (!router.hasRoute(ROUTE_NAMES.REGISTER)) addRegisterRoute(); if (!router.hasRoute(ROUTE_NAMES.FORGOT_PASSWORD)) addForgotPasswordRoute(); if (!router.hasRoute(ROUTE_NAMES.RESET_PASSWORD)) addResetPasswordRoute(); if (!router.hasRoute(ROUTE_NAMES.AUTH_LOGIN)) addAuthLoginRoute(); if (!router.hasRoute(ROUTE_NAMES.HOME)) addHomeRoute(); if (!router.hasRoute(ROUTE_NAMES.TERMS)) addTermsRoute(); if (!router.hasRoute(ROUTE_NAMES.PRIVACY_POLICY)) addPrivacyPolicyRoute(); ensureNotFoundRoute(); } addEditPagesRoute(); addFileUploadRoute(); addNewsletterImportRoute(); addRegisterRoute(); addForgotPasswordRoute(); addResetPasswordRoute(); addAuthLoginRoute(); addHomeRoute(); addTermsRoute(); addPrivacyPolicyRoute(); ensureNotFoundRoute(); export default router;