import { boot } from 'quasar/wrappers'; import { Store } from 'vuex'; import { FG_Plugin } from 'src/plugins'; import routes from 'src/router/routes'; import { api } from 'boot/axios'; import { AxiosResponse } from 'axios'; import { Router, RouteRecordRaw } from 'vue-router'; import { UserSessionState } from 'src/plugins/user/store'; const config: { [key: string]: Array } = { // Do not change required Modules !! requiredModules: ['User'], // here you can import plugins. loadModules: ['Balance', 'Schedule', 'Pricelist'], }; // do not change anything here !! // combine routes from source to target interface BackendPlugin { permissions: string[]; version: string; } interface BackendPlugins { [key: string]: BackendPlugin | null; } interface Backend { plugins: BackendPlugins[]; version: string; } export { Backend }; function setPermissions(object: FG_Plugin.PluginRouteConfig) { if (object.route.meta === undefined) object.route.meta = {}; object.route.meta['permissions'] = object.permissions; } function convertRoutes(parent: RouteRecordRaw, children?: FG_Plugin.PluginRouteConfig[]) { if (children === undefined) return; children.forEach((child) => { setPermissions(child); convertRoutes(child.route, child.children); if (parent.children === undefined) parent.children = []; parent.children.push(child.route); }); } function combineRoutes( target: RouteRecordRaw[], source: FG_Plugin.PluginRouteConfig[], mainPath: '/' | '/main' = '/' ): RouteRecordRaw[] { // Search parent target.forEach((target) => { if (target.path === mainPath) { // Parent found = target source.forEach((sourceMainConfig: FG_Plugin.PluginRouteConfig) => { // Check if source is already in target const targetMainConfig = target.children?.find((targetMainConfig: RouteRecordRaw) => { return sourceMainConfig.route.path === targetMainConfig.path; }); // Already in target routes, add only children if (targetMainConfig) { convertRoutes(targetMainConfig, sourceMainConfig.children); } else { // Append to target if (target.children === undefined) { target.children = []; } convertRoutes(sourceMainConfig.route, sourceMainConfig.children); if ( sourceMainConfig.children && sourceMainConfig.children.length > 0 && !sourceMainConfig.route.component ) Object.assign(sourceMainConfig.route, { component: () => import('src/components/navigation/EmptyParent.vue'), }); target.children.push(sourceMainConfig.route); } }); } }); return target; } // combine Links of Plugins from source to target function combineMainLinks( target: FG_Plugin.PluginMainLink[], source: FG_Plugin.PluginRouteConfig ): FG_Plugin.PluginMainLink[] { const targetPluginMainLink: FG_Plugin.PluginMainLink | undefined = target.find( (targetPluginMainLink: FG_Plugin.PluginMainLink) => { console.log(targetPluginMainLink.title, source.title); return targetPluginMainLink.title == source.title; } ); if (targetPluginMainLink) { source.children?.forEach((sourcePluginChildLink: FG_Plugin.PluginRouteConfig) => { targetPluginMainLink.children.push({ title: sourcePluginChildLink.title, icon: sourcePluginChildLink.icon, link: sourcePluginChildLink.route.name, name: sourcePluginChildLink.route.name, permissions: sourcePluginChildLink.permissions, }); }); } else { const mainLink: FG_Plugin.PluginMainLink = { title: source.title, icon: source.icon, link: source.route.name, name: source.route.name, permissions: source.permissions, }; source.children?.forEach((child) => { if (mainLink.children === undefined) { mainLink.children = []; } mainLink.children.push({ title: child.title, icon: child.icon, link: child.route.name, name: child.route.name, permissions: child.permissions, }); }); target.push(mainLink); } return target; } function loadShortCuts( target: FG_Plugin.ShortCutLink[], source: FG_Plugin.PluginRouteConfig[] ): FG_Plugin.ShortCutLink[] { source.forEach((route) => { if (route.shortcut) { target.push({ link: route.route.name, icon: route.icon, permissions: route.permissions, }); } if (route.children) { target = loadShortCuts(target, route.children); } }); return target; } // loade plugins function loadPlugin( loadedPlugins: FG_Plugin.Flaschengeist, modules: string[], backendpromise: Promise, plugins: FG_Plugin.Plugin[], store: Store, router: Router ): FG_Plugin.Flaschengeist { modules.forEach((requiredModule) => { const plugin = plugins.find((plugin) => { return plugin.name == requiredModule; }); if (plugin) { if (plugin.mainRoutes) { loadedPlugins.routes = combineRoutes(loadedPlugins.routes, plugin.mainRoutes, '/main'); plugin.mainRoutes.forEach((route) => { loadedPlugins.mainLinks = combineMainLinks(loadedPlugins.mainLinks, route); }); loadedPlugins.shortcuts = loadShortCuts(loadedPlugins.shortcuts, plugin.mainRoutes); console.log(loadedPlugins); } if (plugin.outRoutes) { loadedPlugins.routes = combineRoutes(loadedPlugins.routes, plugin.outRoutes); loadedPlugins.shortcutsOut = loadShortCuts(loadedPlugins.shortcutsOut, plugin.outRoutes); } if (plugin.widgets.length > 0) { plugin.widgets.forEach((widget) => (widget.name = plugin.name + '_' + widget.name)); Array.prototype.push.apply(loadedPlugins.widgets, plugin.widgets); } if (plugin.store) { plugin.store.forEach((store_plugin, store_namespace) => { store.registerModule(store_namespace, store_plugin); }); } loadedPlugins.plugins.push({ name: plugin.name, version: plugin.version, }); } else { console.exception(`Could not find required Plugin ${requiredModule}`); router.push({ name: 'error' }).catch((e) => { console.warn(e); }); } }); return loadedPlugins; } async function getBackend(): Promise { let backend: Backend | null = null; try { const response: AxiosResponse = await api.get('/'); backend = response.data; } catch (e) { console.log(e); return null; } finally { return backend; } } // "async" is optional; // more info on params: https://quasar.dev/quasar-cli/cli-documentation/boot-files#Anatomy-of-a-boot-file export default boot(({ router, store, app }) => { const plugins: FG_Plugin.Plugin[] = []; const backendPromise = getBackend(); let loadedPlugins: FG_Plugin.Flaschengeist = { routes, plugins: [], mainLinks: [], shortcuts: [], shortcutsOut: [], widgets: [], }; // get all plugins const pluginsContext = require.context('src/plugins', true, /.+\/plugin.ts$/); pluginsContext.keys().forEach((fileName: string) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access plugins.push(pluginsContext(fileName).default); }); // check dependencies backendPromise .then((backend) => { console.log(backend); if (backend) { plugins.forEach((plugin: FG_Plugin.Plugin) => { plugin.requiredModules.forEach((requiredModule: string) => { if ( !( config.requiredModules.includes(requiredModule) || config.loadModules.includes(requiredModule) ) ) { console.error(`Plugin ${plugin.name} need Plugin ${requiredModule}`); router.push({ name: 'error' }).catch((e) => { console.warn(e); }); } }); plugin.requiredBackendModules.forEach((requiredBackendModule: string) => { if (!(requiredBackendModule in backend.plugins)) { console.error( `Plugin ${plugin.name} need Plugin ${requiredBackendModule} in backend.` ); router.push({ name: 'error' }).catch((err) => { console.warn(err); }); } }); }); } }) .catch((e) => { console.error(e); }); // load plugins loadedPlugins = loadPlugin( loadedPlugins, config.requiredModules, backendPromise, plugins, store, router ); loadedPlugins = loadPlugin( loadedPlugins, config.loadModules, backendPromise, plugins, store, router ); loadedPlugins.widgets.sort((a, b) => b.priority - a.priority); loadedPlugins.routes.forEach((route) => router.addRoute(route)); // save plugins in VM-variable app.provide('flaschengeist', loadedPlugins); });