2020-11-24 16:35:11 +00:00
|
|
|
import { boot } from 'quasar/wrappers';
|
|
|
|
import { FG_Plugin } from 'src/plugins';
|
2020-10-31 14:09:02 +00:00
|
|
|
import routes from 'src/router/routes';
|
2021-01-30 07:38:44 +00:00
|
|
|
import { api } from 'boot/axios';
|
2020-11-24 16:35:11 +00:00
|
|
|
import { AxiosResponse } from 'axios';
|
2021-03-22 02:24:27 +00:00
|
|
|
import { RouteRecordRaw } from 'vue-router';
|
2021-03-24 16:18:10 +00:00
|
|
|
import { Notify } from 'quasar';
|
2020-10-12 21:49:05 +00:00
|
|
|
|
2021-01-30 07:38:44 +00:00
|
|
|
const config: { [key: string]: Array<string> } = {
|
2020-10-14 20:27:20 +00:00
|
|
|
// Do not change required Modules !!
|
2020-10-31 14:09:02 +00:00
|
|
|
requiredModules: ['User'],
|
2020-10-14 20:27:20 +00:00
|
|
|
// here you can import plugins.
|
2021-03-18 16:23:57 +00:00
|
|
|
loadModules: ['Balance', 'Schedule', 'Pricelist'],
|
2020-10-14 20:27:20 +00:00
|
|
|
};
|
2020-10-12 21:49:05 +00:00
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
/* Stop!
|
|
|
|
|
2020-10-14 20:27:20 +00:00
|
|
|
// do not change anything here !!
|
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
// You can not even read? I said stop!
|
|
|
|
|
|
|
|
// Really are you stupid? Stop scrolling down here!
|
|
|
|
|
|
|
|
// Every line you scroll down, an unicorn will die painfully!
|
|
|
|
|
|
|
|
// Ok you must hate unicorns... But what if I say you I joked... Baby otters will die!
|
|
|
|
|
|
|
|
.-"""-.
|
|
|
|
/ o\
|
|
|
|
| o 0).-.
|
|
|
|
| .-;(_/ .-.
|
|
|
|
\ / /)).---._| `\ ,
|
|
|
|
'. ' /(( `'-./ _/|
|
|
|
|
\ .' ) .-.;` /
|
|
|
|
'. | `\-'
|
|
|
|
'._ -' /
|
|
|
|
``""--`------`
|
|
|
|
*/
|
|
|
|
|
|
|
|
/****************************************************
|
|
|
|
******** Internal area for some magic **************
|
|
|
|
****************************************************/
|
2020-10-31 14:09:02 +00:00
|
|
|
|
2020-11-13 12:42:15 +00:00
|
|
|
interface BackendPlugin {
|
|
|
|
permissions: string[];
|
|
|
|
version: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface BackendPlugins {
|
2021-03-22 02:24:27 +00:00
|
|
|
[key: string]: BackendPlugin;
|
2020-11-13 12:42:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
interface Backend {
|
2021-03-22 02:24:27 +00:00
|
|
|
plugins: BackendPlugins;
|
2020-11-13 12:42:15 +00:00
|
|
|
version: string;
|
|
|
|
}
|
2020-11-24 16:35:11 +00:00
|
|
|
export { Backend };
|
2020-11-13 12:42:15 +00:00
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
// Combine routes, shortcuts and widgets from plugins
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper function, set permissions from MenuRoute to meta from RouteRecordRaw
|
|
|
|
* @param object MenuRoute to set route meta
|
|
|
|
*/
|
|
|
|
function setPermissions(object: FG_Plugin.MenuRoute) {
|
2021-03-19 20:34:49 +00:00
|
|
|
if (object.permissions !== undefined) {
|
|
|
|
if (object.route.meta === undefined) object.route.meta = {};
|
|
|
|
object.route.meta['permissions'] = object.permissions;
|
|
|
|
}
|
2021-01-30 03:19:30 +00:00
|
|
|
}
|
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
/**
|
|
|
|
* Helper function to convert MenuRoute to the parents RouteRecordRaw
|
|
|
|
* @param parent Parent RouteRecordRaw
|
|
|
|
* @param children MenuRoute to convert
|
|
|
|
*/
|
|
|
|
function convertRoutes(parent: RouteRecordRaw, children?: FG_Plugin.MenuRoute[]) {
|
|
|
|
if (children !== undefined) {
|
|
|
|
children.forEach((child) => {
|
|
|
|
setPermissions(child);
|
|
|
|
convertRoutes(child.route, child.children);
|
|
|
|
if (parent.children === undefined) parent.children = [];
|
|
|
|
parent.children.push(child.route);
|
|
|
|
});
|
|
|
|
}
|
2021-01-30 07:38:44 +00:00
|
|
|
}
|
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
/**
|
|
|
|
* Combines routes from plugin MenuRoute to Vue-Router RouteRecordRaw to get a clean route-tree
|
|
|
|
* @param target
|
|
|
|
* @param source
|
|
|
|
* @param mainPath
|
|
|
|
*/
|
|
|
|
function combineMenuRoutes(
|
2021-01-30 03:19:30 +00:00
|
|
|
target: RouteRecordRaw[],
|
2021-03-22 02:24:27 +00:00
|
|
|
source: FG_Plugin.MenuRoute[],
|
|
|
|
mainPath: '/' | '/in' = '/'
|
2021-01-30 03:19:30 +00:00
|
|
|
): RouteRecordRaw[] {
|
2021-01-30 07:38:44 +00:00
|
|
|
// Search parent
|
2021-03-18 16:23:57 +00:00
|
|
|
target.forEach((target) => {
|
2020-10-31 14:09:02 +00:00
|
|
|
if (target.path === mainPath) {
|
2021-01-30 07:38:44 +00:00
|
|
|
// Parent found = target
|
2021-03-22 02:24:27 +00:00
|
|
|
source.forEach((sourceMainConfig: FG_Plugin.MenuRoute) => {
|
2021-01-30 07:38:44 +00:00
|
|
|
// Check if source is already in target
|
2021-01-30 03:19:30 +00:00
|
|
|
const targetMainConfig = target.children?.find((targetMainConfig: RouteRecordRaw) => {
|
|
|
|
return sourceMainConfig.route.path === targetMainConfig.path;
|
2020-11-24 17:35:37 +00:00
|
|
|
});
|
2021-01-30 07:38:44 +00:00
|
|
|
// Already in target routes, add only children
|
2020-10-31 14:09:02 +00:00
|
|
|
if (targetMainConfig) {
|
2021-01-30 07:38:44 +00:00
|
|
|
convertRoutes(targetMainConfig, sourceMainConfig.children);
|
2020-10-31 14:09:02 +00:00
|
|
|
} else {
|
2021-01-30 07:38:44 +00:00
|
|
|
// Append to target
|
2020-10-31 14:09:02 +00:00
|
|
|
if (target.children === undefined) {
|
|
|
|
target.children = [];
|
2020-10-13 18:17:00 +00:00
|
|
|
}
|
2021-01-30 07:38:44 +00:00
|
|
|
convertRoutes(sourceMainConfig.route, sourceMainConfig.children);
|
2020-11-24 17:35:37 +00:00
|
|
|
if (
|
|
|
|
sourceMainConfig.children &&
|
|
|
|
sourceMainConfig.children.length > 0 &&
|
2021-01-30 03:19:30 +00:00
|
|
|
!sourceMainConfig.route.component
|
2020-11-24 17:35:37 +00:00
|
|
|
)
|
2021-01-30 07:38:44 +00:00
|
|
|
Object.assign(sourceMainConfig.route, {
|
2021-01-30 03:19:30 +00:00
|
|
|
component: () => import('src/components/navigation/EmptyParent.vue'),
|
|
|
|
});
|
2021-01-30 07:38:44 +00:00
|
|
|
target.children.push(sourceMainConfig.route);
|
2020-10-13 18:17:00 +00:00
|
|
|
}
|
2020-10-31 14:09:02 +00:00
|
|
|
});
|
2020-10-13 18:17:00 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
function combineRoutes(
|
|
|
|
target: RouteRecordRaw[],
|
|
|
|
source: FG_Plugin.NamedRouteRecordRaw[],
|
|
|
|
mainPath: '/' | '/in'
|
|
|
|
) {
|
|
|
|
// Search parent
|
|
|
|
target.forEach((target) => {
|
|
|
|
if (target.path === mainPath) {
|
|
|
|
// Parent found = target
|
|
|
|
source.forEach((sourceRoute) => {
|
|
|
|
// Check if source is already in target
|
|
|
|
const targetRoot = target.children?.find(
|
|
|
|
(targetRoot) => sourceRoute.path === targetRoot.path
|
|
|
|
);
|
|
|
|
// Already in target routes, add only children
|
|
|
|
if (targetRoot) {
|
|
|
|
if (targetRoot.children === undefined) targetRoot.children = [];
|
|
|
|
targetRoot.children.push(...(sourceRoute.children || []));
|
|
|
|
} else {
|
|
|
|
// Append to target
|
|
|
|
if (target.children === undefined) target.children = [];
|
|
|
|
if (
|
|
|
|
sourceRoute.children &&
|
|
|
|
sourceRoute.children.length > 0 &&
|
|
|
|
sourceRoute.component === undefined
|
|
|
|
)
|
|
|
|
Object.assign(sourceRoute, {
|
|
|
|
component: () => import('src/components/navigation/EmptyParent.vue'),
|
|
|
|
});
|
|
|
|
target.children.push(sourceRoute);
|
|
|
|
}
|
2020-11-24 17:35:37 +00:00
|
|
|
});
|
2021-03-22 02:24:27 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Combine MenuRoutes into Flaschengeist MenuLinks for the main menu
|
|
|
|
* @param target Flaschengeist list of menu links
|
|
|
|
* @param source MenuRoutes to combine
|
|
|
|
*/
|
|
|
|
function combineMenuLinks(target: FG_Plugin.MenuLink[], source: FG_Plugin.MenuRoute) {
|
|
|
|
let idx = target.findIndex((link) => link.title == source.title);
|
|
|
|
// Link not found, add new one
|
|
|
|
if (idx === -1) {
|
|
|
|
idx += target.push({
|
2020-10-31 14:09:02 +00:00
|
|
|
title: source.title,
|
|
|
|
icon: source.icon,
|
2021-01-30 03:19:30 +00:00
|
|
|
link: source.route.name,
|
|
|
|
permissions: source.permissions,
|
2020-10-31 14:09:02 +00:00
|
|
|
});
|
2020-10-13 18:17:00 +00:00
|
|
|
}
|
2021-03-22 02:24:27 +00:00
|
|
|
if (target[idx].children === undefined) {
|
|
|
|
target[idx].children = [];
|
|
|
|
}
|
|
|
|
source.children?.forEach((sourceChild) => {
|
|
|
|
target[idx].children?.push({
|
|
|
|
title: sourceChild.title,
|
|
|
|
icon: sourceChild.icon,
|
|
|
|
link: sourceChild.route.name,
|
|
|
|
permissions: sourceChild.permissions,
|
|
|
|
});
|
|
|
|
});
|
2020-10-13 18:17:00 +00:00
|
|
|
}
|
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
/**
|
|
|
|
* Combine shortcuts from Plugin MenuRouts into the Flaschenbeist Shortcut list
|
|
|
|
* @param target Flaschengeist list of shortcuts
|
|
|
|
* @param source MenuRoutes to extract shortcuts from
|
|
|
|
*/
|
|
|
|
function combineShortcuts(target: FG_Plugin.Shortcut[], source: FG_Plugin.MenuRoute[]) {
|
2021-03-18 16:23:57 +00:00
|
|
|
source.forEach((route) => {
|
2020-10-31 14:09:02 +00:00
|
|
|
if (route.shortcut) {
|
2021-03-22 02:24:27 +00:00
|
|
|
target.push(<FG_Plugin.Shortcut>{
|
2021-01-30 03:19:30 +00:00
|
|
|
link: route.route.name,
|
2020-10-31 16:33:09 +00:00
|
|
|
icon: route.icon,
|
2021-01-30 03:19:30 +00:00
|
|
|
permissions: route.permissions,
|
2020-10-31 14:09:02 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
if (route.children) {
|
2021-03-22 02:24:27 +00:00
|
|
|
combineShortcuts(target, route.children);
|
2020-10-31 14:09:02 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
/**
|
|
|
|
* Load a Flaschengeist plugin
|
|
|
|
* @param loadedPlugins Flaschgeist object
|
|
|
|
* @param pluginName Plugin to load
|
|
|
|
* @param context RequireContext of plugins
|
|
|
|
* @param router VueRouter instance
|
|
|
|
*/
|
2020-10-31 14:09:02 +00:00
|
|
|
function loadPlugin(
|
2021-02-04 01:42:49 +00:00
|
|
|
loadedPlugins: FG_Plugin.Flaschengeist,
|
2021-03-22 02:24:27 +00:00
|
|
|
pluginName: string,
|
|
|
|
context: __WebpackModuleApi.RequireContext,
|
|
|
|
backend: Backend
|
|
|
|
) {
|
|
|
|
// Check if already loaded
|
|
|
|
if (loadedPlugins.plugins.findIndex((p) => p.name === pluginName) !== -1) return true;
|
|
|
|
|
|
|
|
// Search if plugin is installed
|
|
|
|
const available = context.keys();
|
|
|
|
const plugin = available.includes(`./${pluginName.toLowerCase()}/plugin.ts`)
|
|
|
|
? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
|
|
<FG_Plugin.Plugin>context(`./${pluginName.toLowerCase()}/plugin.ts`).default
|
|
|
|
: undefined;
|
|
|
|
|
|
|
|
if (!plugin) {
|
|
|
|
// Plugin is not found, results in an error
|
|
|
|
console.exception(`Could not find required Plugin ${pluginName}`);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
// Plugin found. Check backend dependencies
|
|
|
|
if (
|
|
|
|
!plugin.requiredBackendModules.every((required) => backend.plugins[required] !== undefined)
|
|
|
|
) {
|
|
|
|
console.error(`Plugin ${pluginName}: Backend modules not satisfied`);
|
|
|
|
return false;
|
2020-10-13 18:17:00 +00:00
|
|
|
}
|
2021-03-22 02:24:27 +00:00
|
|
|
|
|
|
|
// Check frontend dependencies
|
|
|
|
if (
|
|
|
|
!plugin.requiredModules.every((required) =>
|
|
|
|
loadPlugin(loadedPlugins, required, context, backend)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
console.error(`Plugin ${pluginName}: Backend modules not satisfied`);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start combining and loading routes, shortcuts etc
|
|
|
|
if (plugin.internalRoutes) {
|
|
|
|
combineRoutes(loadedPlugins.routes, plugin.internalRoutes, '/in');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plugin.innerRoutes) {
|
|
|
|
// Routes for Vue Router
|
|
|
|
combineMenuRoutes(loadedPlugins.routes, plugin.innerRoutes, '/in');
|
|
|
|
// Combine links for menu
|
|
|
|
plugin.innerRoutes.forEach((route) => combineMenuLinks(loadedPlugins.menuLinks, route));
|
|
|
|
// Combine shortcuts
|
|
|
|
combineShortcuts(loadedPlugins.shortcuts, plugin.innerRoutes);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plugin.outerRoutes) {
|
|
|
|
combineMenuRoutes(loadedPlugins.routes, plugin.outerRoutes);
|
|
|
|
combineShortcuts(loadedPlugins.outerShortcuts, plugin.outerRoutes);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (plugin.widgets.length > 0) {
|
|
|
|
plugin.widgets.forEach((widget) => (widget.name = plugin.name + '_' + widget.name));
|
|
|
|
Array.prototype.push.apply(loadedPlugins.widgets, plugin.widgets);
|
|
|
|
}
|
|
|
|
|
|
|
|
loadedPlugins.plugins.push({
|
|
|
|
name: plugin.name,
|
|
|
|
version: plugin.version,
|
|
|
|
});
|
|
|
|
|
|
|
|
return plugin;
|
|
|
|
}
|
2020-10-14 20:27:20 +00:00
|
|
|
}
|
2020-10-13 18:17:00 +00:00
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
/**
|
|
|
|
* Loading backend information
|
|
|
|
* @returns Backend object or null
|
|
|
|
*/
|
|
|
|
async function getBackend() {
|
2020-11-13 12:42:15 +00:00
|
|
|
try {
|
2021-03-22 02:24:27 +00:00
|
|
|
const { data }: AxiosResponse<Backend> = await api.get('/');
|
|
|
|
return data;
|
2020-11-13 12:42:15 +00:00
|
|
|
} catch (e) {
|
2021-02-03 12:35:31 +00:00
|
|
|
console.warn(e);
|
2020-11-13 12:42:15 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
/**
|
|
|
|
* Boot file, load all required plugins, check for dependencies
|
|
|
|
*/
|
|
|
|
export default boot(async ({ router, app }) => {
|
|
|
|
const backend = await getBackend();
|
2021-03-29 05:35:23 +00:00
|
|
|
if (backend === null) {
|
2021-03-22 02:24:27 +00:00
|
|
|
void router.push({ name: 'error' });
|
|
|
|
return;
|
|
|
|
}
|
2020-11-13 12:42:15 +00:00
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
const loadedPlugins: FG_Plugin.Flaschengeist = {
|
2020-10-31 14:09:02 +00:00
|
|
|
routes,
|
2020-10-14 20:27:20 +00:00
|
|
|
plugins: [],
|
2021-03-22 02:24:27 +00:00
|
|
|
menuLinks: [],
|
2020-10-14 20:27:20 +00:00
|
|
|
shortcuts: [],
|
2021-03-22 02:24:27 +00:00
|
|
|
outerShortcuts: [],
|
2021-03-18 16:23:57 +00:00
|
|
|
widgets: [],
|
2020-10-14 20:27:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// get all plugins
|
2021-02-02 00:28:23 +00:00
|
|
|
const pluginsContext = require.context('src/plugins', true, /.+\/plugin.ts$/);
|
2020-10-14 20:27:20 +00:00
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
// Start loading plugins
|
2021-03-24 19:49:54 +00:00
|
|
|
// Load required modules, if not found or error when loading this will forward the user to the error page
|
2021-03-22 02:24:27 +00:00
|
|
|
config.requiredModules.forEach((required) => {
|
|
|
|
const plugin = loadPlugin(loadedPlugins, required, pluginsContext, backend);
|
|
|
|
if (!plugin) {
|
|
|
|
void router.push({ name: 'error' });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
});
|
2020-11-14 09:58:21 +00:00
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
// Load user defined plugins
|
2021-03-24 19:49:54 +00:00
|
|
|
// If there is an error with loading a plugin, the user will get informed.
|
2021-03-24 16:18:10 +00:00
|
|
|
const failed: string[] = [];
|
2021-03-22 02:24:27 +00:00
|
|
|
config.loadModules.forEach((required) => {
|
|
|
|
const plugin = loadPlugin(loadedPlugins, required, pluginsContext, backend);
|
|
|
|
if (!plugin) {
|
2021-03-24 16:18:10 +00:00
|
|
|
failed.push(required);
|
2021-03-22 02:24:27 +00:00
|
|
|
}
|
|
|
|
});
|
2021-03-24 16:18:10 +00:00
|
|
|
if (failed.length > 0) {
|
2021-03-24 19:49:54 +00:00
|
|
|
// Log failed plugins
|
2021-03-24 16:18:10 +00:00
|
|
|
console.error('Could not load all plugins', failed);
|
2021-03-24 19:49:54 +00:00
|
|
|
// Inform user about error
|
2021-03-24 16:18:10 +00:00
|
|
|
Notify.create({
|
|
|
|
type: 'negative',
|
|
|
|
message:
|
2021-03-24 19:49:54 +00:00
|
|
|
'Fehler beim Laden: Nicht alle Funktionen stehen zur Verfügung. Bitte wende dich an den Admin!',
|
2021-03-24 16:18:10 +00:00
|
|
|
timeout: 10000,
|
|
|
|
progress: true,
|
|
|
|
});
|
|
|
|
}
|
2020-10-14 20:27:20 +00:00
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
// Sort widgets by priority
|
2020-11-10 00:33:55 +00:00
|
|
|
loadedPlugins.widgets.sort((a, b) => b.priority - a.priority);
|
2020-10-14 20:27:20 +00:00
|
|
|
|
2021-03-22 02:24:27 +00:00
|
|
|
// Add loaded routes to router
|
2021-01-30 03:19:30 +00:00
|
|
|
loadedPlugins.routes.forEach((route) => router.addRoute(route));
|
2020-10-14 20:27:20 +00:00
|
|
|
|
|
|
|
// save plugins in VM-variable
|
2021-02-04 01:42:49 +00:00
|
|
|
app.provide('flaschengeist', loadedPlugins);
|
2020-10-12 21:49:05 +00:00
|
|
|
});
|