Files
miriamgemeinde/src/router.js

361 lines
10 KiB
JavaScript

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;