Détail du package

@mashroom/mashroom-portal

nonblocking975MIT2.9.2

Mashroom Portal

readme

Mashroom Portal

Plugin for Mashroom Server, a Microfrontend Integration Platform.

This plugin adds a Portal component which allows composing pages from Single Page Applications (SPAs).

Registered SPAs (Portal Apps) can be placed on arbitrary pages via Drag'n'Drop. Each instance receives a config object during startup and a bunch of client services, which for example allow access to the message bus. The config is basically an arbitrary JSON object, which can be edited via Admin Toolbar. A Portal App can also bring its own config editor App, which again is just a simple SPA.

One of the provided client services allow Portal Apps to load any other App (known by name) into any existing DOM node. This can be used to:

  • Create dynamic cockpits where Apps are loaded dynamically based on some user input or search result
  • Create Composite Apps that consist of other Apps (which again could be used within other Apps again)

The Portal supports hybrid rendering for both the Portal pages and SPAs. So, if an SPA supports server side rendering the initial HTML can be incorporated into the initial HTML page. Navigating to another page dynamically replaces the SPAs in the content area via client side rendering (needs to be supported by the Theme).

The Portal also supports i18n, theming, role based security, a client-side message bus which can be connected to a server-side broker and a registry for Portal Apps on a separate server or container.

Usage

Since this plugin requires a lot of other plugins the easiest way to use it is to clone this quickstart repository: mashroom-portal-quickstart

You can find a full documentation of Mashroom Server and this portal plugin with a setup and configuration guide here: https://www.mashroom-server.com/documentation

The plugin allows the following configuration properties:

{
  "plugins": {
        "Mashroom Portal WebApp": {
            "path": "/portal",
            "adminApp": "Mashroom Portal Admin App",
            "defaultTheme": "Mashroom Portal Default Theme",
            "defaultLayout": "Mashroom Portal Default Layouts 1 Column",
            "authenticationExpiration": {
              "warnBeforeExpirationSec": 60,
              "autoExtend": false,
              "onExpiration": {
                "strategy": "reload"
              }
            },
            "ignoreMissingAppsOnPages": false,
            "versionHashSalt": null,
            "resourceFetchConfig": {
                "fetchTimeoutMs": 3000,
                "httpMaxSocketsPerHost": 3,
                "httpRejectUnauthorized": true
            },
            "defaultProxyConfig": {
                "sendPermissionsHeader": false,
                "restrictToRoles": ["ROLE_X"]
            },
            "ssrConfig": {
                "ssrEnable": true,
                "renderTimoutMs": 2000,
                "cacheEnable": true,
                "cacheTTLSec": 300,
                "inlineStyles": true
            },
            "addDemoPages": true
        }
    }
}
  • path: The portal base path (Default: /portal)
  • adminApp: The admin to use (Default: Mashroom Portal Admin App)
  • defaultTheme: The default theme if none is selected in the site or page configuration (Default: Mashroom Portal Default Theme)
  • defaultLayout: The default layout if none is selected in the site or page configuration (Default: Mashroom Portal Default Layouts 1 Column)
  • authenticationExpiration:
    • warnBeforeExpirationSec: The time when the Portal should start to warn that the authentication is about to expire. A value of 0 or lower than 0 disables the warning. (Default: 60)
    • autoExtend: Automatically extend the authentication as long as the portal page is open (Default: false)
    • onExpiration: What to do if the session expires. Possible strategies are stayOnPage, reload, redirect and displayDomElement. (Default: reload)
  • ignoreMissingAppsOnPages: If an App on a page can't be found just show nothing instead of an error message (Default: false)
  • versionHashSalt: If you need unique resource version hashes per server instance provide here a string (Default: null)
  • resourceFetchConfig: Optional config for resource fetching (App and plugin resources like js/css files)
    • fetchTimeoutMs: Timeout for fetching (Default: 3000)
    • httpMaxSocketsPerHost: Max sockets per host for fetching resources from Remote Apps (Default: 10)
    • httpRejectUnauthorized: Reject resources from servers with invalid certificates (Default: true)
  • defaultProxyConfig: Optional default http proxy config for portal apps (see below the documentation of portal-app2 plugins). The restrictToRoles here cannot be removed per app, but apps can define other roles that are also allowed to access a proxy.
  • ssrConfig: Optional config for server side rendering
    • ssrEnable: Allow server side rendering if Apps support it (Default: true)
    • renderTimoutMs: Timeout for SSR which defines how long the page rendering can be blocked. Even if SSR takes too long the result is put into the cache and might be available for the next page rendering (Default: 2000)
    • _cacheEnable": Enable cache for server-side rendered HTML (Default: true)
    • cacheTTLSec: The timeout in seconds for cached SSR HTML (Default: 300)
    • inlineStyles: Inline the App's CSS to avoid sudden layout shifts after loading the initial HTML (Default: true)
  • addDemoPages: Add some demo pages if the configuration storage is empty (Default: true)

Browser support

The Portal supports only modern Browsers and requires ES6.

Services

MashroomPortalService

The exposed service is accessible through pluginContext.services.portal.service

Interface:

export interface MashroomPortalService {
    /**
     * Get all registered portal apps
     */
    getPortalApps(): Readonly<Array<MashroomPortalApp>>;

    /**
     * Get all registered theme plugins
     */
    getThemes(): Readonly<Array<MashroomPortalTheme>>;

    /**
     * Get all registered layout plugins
     */
    getLayouts(): Readonly<Array<MashroomPortalLayout>>;

    /**
     * Get all registered page enhancement plugins
     */
    getPortalPageEnhancements(): Readonly<Array<MashroomPortalPageEnhancement>>;

    /**
     * Get all registered app enhancement plugins
     */
    getPortalAppEnhancements(): Readonly<Array<MashroomPortalAppEnhancement>>;

    /**
     * Get all sites
     */
    getSites(limit?: number): Promise<Array<MashroomPortalSite>>;

    /**
     * Get the site with the given id
     */
    getSite(siteId: string): Promise<MashroomPortalSite | null | undefined>;

    /**
     * Find the site with given path
     */
    findSiteByPath(path: string): Promise<MashroomPortalSite | null | undefined>;

    /**
     * Insert new site
     */
    insertSite(site: MashroomPortalSite): Promise<void>;

    /**
     * Update site
     */
    updateSite(site: MashroomPortalSite): Promise<void>;

    /**
     * Delete site
     */
    deleteSite(req: Request, siteId: string): Promise<void>;

    /**
     * Get page with given id
     */
    getPage(pageId: string): Promise<MashroomPortalPage | null | undefined>;

    /**
     * Find the page ref within a site with given friendly URL
     */
    findPageRefByFriendlyUrl(site: MashroomPortalSite, friendlyUrl: string): Promise<MashroomPortalPageRef | null | undefined>;

    /**
     * Find the page ref within a site by the given pageId
     */
    findPageRefByPageId(site: MashroomPortalSite, pageId: string): Promise<MashroomPortalPageRef | null | undefined>;

    /**
     * Insert new page
     */
    insertPage(page: MashroomPortalPage): Promise<void>;

    /**
     * Update page
     */
    updatePage(page: MashroomPortalPage): Promise<void>;

    /**
     * Insert new page
     */
    deletePage(req: Request, pageId: string): Promise<void>;

    /**
     * GetPortal App instance
     */
    getPortalAppInstance(pluginName: string, instanceId: string | null | undefined): Promise<MashroomPortalAppInstance | null | undefined>;

    /**
     * Insert a new Portal App instance
     */
    insertPortalAppInstance(portalAppInstance: MashroomPortalAppInstance): Promise<void>;

    /**
     * Update given Portal App instance
     */
    updatePortalAppInstance(portalAppInstance: MashroomPortalAppInstance): Promise<void>;

    /**
     * Delete given portal Portal App instance
     */
    deletePortalAppInstance(req: Request, pluginName: string, instanceId: string | null | undefined): Promise<void>;
}

Plugin Types

portal-app

Deprecated since Mashroom v2, please use portal-app2.

portal-app2

This plugin type makes a Single Page Application (SPA) available in the Portal.

To register a new portal-app plugin add this to package.json:

{
    "mashroom": {
        "plugins": [
            {
                "name": "My Single Page App",
                "type": "portal-app2",
                "clientBootstrap": "startMyApp",
                "resources": {
                    "js": [
                        "bundle.js"
                    ]
                },
                "local": {
                    "resourcesRoot": "./dist",
                    "ssrBootstrap": "./dist/renderToString.js"
                },
                "defaultConfig": {
                    "appConfig": {
                        "myProperty": "foo"
                    }
                }
            }
        ]
    }
}

A full config with all optional properties would look like this:

{
    "mashroom": {
        "plugins": [
            {
                "name": "My Single Page App",
                "type": "portal-app2",
                "clientBootstrap": "startMyApp",
                "resources": {
                    "js": [
                        "bundle.js"
                    ],
                    "css": []
                },
                "sharedResources": {
                    "js": []
                },
                "screenshots": [
                    "screenshot1.png"
                ],
                "local": {
                    "resourcesRoot": "./dist",
                    "ssrBootstrap": "/dist/renderToString.js"
                },
                "remote": {
                    "resourcesRoot": "/public",
                    "ssrInitialHtmlPath": "/ssr"
                },
                "defaultConfig": {
                    "title": {
                        "en": "My Single Page App",
                        "de": "Meine Single Page App"
                    },
                    "category": "My Category",
                    "tags": ["my", "stuff"],
                    "description": {
                        "en": "Here the english description",
                        "de": "Hier die deutsche Beschreibung"
                    },
                    "metaInfo": {
                        "capabilities": ["foo"]
                    },
                    "defaultRestrictViewToRoles": ["Role1"],
                    "rolePermissions": {
                        "doSomethingSpecial": ["Role2", "Role3"]
                    },
                    "caching": {
                        "ssrHtml": "same-config-and-user"
                    },
                    "editor": {
                        "editorPortalApp": "Demo Config Editor",
                        "position": "in-place",
                        "appConfig": {
                        }
                    },
                    "proxies": {
                        "spaceXApi": {
                            "targetUri": "https://api.spacexdata.com/v3",
                            "sendPermissionsHeader": false,
                            "restrictToRoles": ["Role1"]
                        }
                    },
                    "appConfig": {
                        "myProperty": "foo"
                    }
                }
            }
        ]
    }
}
  • clientBootstrap: The global function exposed on the client side to launch the App (see below for an example)
  • resources: Javascript and CSS resources that must be loaded before the bootstrap method is invoked. All resource paths are relative to resourcesRoot.
  • sharedResources: Optional. Same as resources but a shared resource with a given name is only loaded once, even if multiple Portal Apps declare it. This is useful if apps want to share vendor libraries or styles or such. Here you can find a demo how to use the Webpack DllPlugin together with this feature: Mashroom Demo Shared DLL
  • screenshots: Optional some screenshots of the App. The screenshots paths are relative to resourcesRoot.
  • local: Basic configuration if the App is deployed locally
    • resourcesRoot: The root path for APP resources such as JavaScript files and images. Needs to be relative within the package
    • ssrBootstrap: An optional local SSR bootstrap that returns an initial HTML for the App, relative within the package (see below for an example)
  • remote: Optional configuration if the App is accessed remotely
    • resourcesRoot: The root path for App resources such as JavaScript files and images
    • ssrInitialHtmlPath: The optional path to a route that renders the initial HTML. The Portal will send a POST to this route with a JSON body of type MashroomPortalAppSSRRemoteRequest and expects a plain text/html response or an application/json response that satisfies MashroomPortalAppSSRResult.
  • defaultConfig: The default config that can be overwritten in the Mashroom config file
    • title: Optional human-readable title of the App. Can be a string or an object with translations.
    • category: An optional category to group the Apps in the Admin App
    • tags: An optional list of tags that can also be used in the search (in the Admin App)
    • description: Optional App description. Can be a string or an object with translations.
    • defaultRestrictViewToRoles: Optional default list of roles that have the VIEW permission if not set via Admin App. Use this to prevent that an App can just be loaded via JS API (dynamically) by any user, even an anonymous one.
    • rolePermissions: Optional mapping between App specific permissions and roles. This corresponds to the permission object passed with the user information to the App.
    • caching: Optional caching configuration
      • ssrHtml: Optional SSR caching configuration (Default: same-config-and-user)
    • editor: Optional custom editor configuration that should be used for the appConfig by the Admin Toolbar
      • editorPortalApp: The name of the Portal App that should be used to edit the appConfig of this App. The App will receive an extra appConfig property editorTarget of type MashroomPortalConfigEditorTarget.
      • position: Optional hint where to launch the editor. Possible values: in-place, sidebar. (Default: in-place)
      • appConfig: The optional appConfig the editor App should be launched with (Default: {})
    • proxies: Defines proxies to access the App's backend (HTTP or WebSocket)
      • targetUri: The API target URI
      • sendPermissionsHeader: Optional. Add the header X-USER-PERMISSIONS with a comma separated list of permissions calculated from rolePermissions (Default: false)
      • restrictToRoles: Optional list of roles that are permitted to access the proxy. The difference to using ACL rules to restrict the access to an API is that not even the Administrator role can access the proxy if this property is set. You can use this to protect sensitive data only a small group of users is allowed to access.
    • metaInfo: Optional meta info that could be used to lookup for Apps with specific features or capabilities
    • appConfig: The default configuration that will be passed to the App. Can be adapted in the Admin App.

The clientBootstrap is in this case a global function that starts the App within the given host element. Here for example a React app:

import React from 'react';
import {render, hydrate, unmountComponentAtNode} from 'react-dom';
import App from './App';

import type {MashroomPortalAppPluginBootstrapFunction} from '@mashroom/mashroom-portal/type-definitions';

const bootstrap: MashroomPortalAppPluginBootstrapFunction = (element, portalAppSetup, clientServices) => {
    const {appConfig, restProxyPaths, lang} = portalAppSetup;
    const {messageBus} = clientServices;

    // Check if the Apps has been rendered in the server-side, if this is a Hybrid App and a ssrBootstrap is configured
    //const ssrHost = element.querySelector('[data-ssr-host="true"]');
    //if (ssrHost) {
    //    hydrate(<App appConfig={appConfig} messageBus={messageBus}/>, ssrHost);
    //} else {
        // CSR
        render(<App appConfig={appConfig} messageBus={messageBus}/>, element);
    //}

    return {
        willBeRemoved: () => {
            unmountComponentAtNode(portalAppHostElement);
        },
        updateAppConfig: (appConfig) => {
            // Implement if dynamic app config should be possible
        }
    };
};

global.startMyApp = bootstrap;

And for an Angular app:

import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {MashroomPortalAppPluginBootstrapFunction} from '@mashroom/mashroom-portal/type-definitions';
import {AppModule} from './app/app.module';

const bootstrap: MashroomPortalAppPluginBootstrapFunction = (hostElement, portalAppSetup, portalClientServices) => {

    return platformBrowserDynamic([
        {provide: 'host.element', useValue: hostElement },
        {provide: 'app.setup', useValue: portalAppSetup},
        {provide: 'client.services', useValue: portalClientServices}
    ]).bootstrapModule(AppModule).then(
        (module) => {
            return {
                willBeRemoved: () => {
                    console.info('Destroying Angular module');
                    module.destroy();
                }
            };
        }
    );
};

global.startAngularDemoApp = bootstrap;

In case of a Hybrid App which supports Server Side Rendering (SSR) the server side bootstrap would look like this:

import React from 'react';
import {renderToString} from 'react-dom/server';
import App from './App';

import type {MashroomPortalAppPluginSSRBootstrapFunction} from '@mashroom/mashroom-portal/type-definitions';

const bootstrap: MashroomPortalAppPluginSSRBootstrapFunction = (portalAppSetup, req) => {
    const {appConfig, restProxyPaths, lang} = portalAppSetup;
    const dummyMessageBus: any = {};
    const html = renderToString(<App appConfig={appConfig} messageBus={dummyMessageBus}/>);

    return html;
    // Alternatively (supports Composite Apps)
    /*
    return {
        html,
        embeddedApps: [],
    }
    */
};

export default bootstrap;

The portalAppSetup has the following structure:

export type MashroomPortalAppSetup = {
    readonly appId: string;
    readonly title: string | null | undefined;
    readonly proxyPaths: MashroomPortalProxyPaths;
    // Legacy, will be removed in Mashroom v3
    readonly restProxyPaths: MashroomPortalProxyPaths;
    readonly resourcesBasePath: string;
    readonly globalLaunchFunction: string;
    readonly lang: string;
    readonly user: MashroomPortalAppUser;
    readonly appConfig: MashroomPluginConfig;
}
  • appId: The unique appId
  • title: Translated title (according to current lang)
  • proxyPaths: The base paths to the proxies defined in the plugin config. In the example below the base path to the spaceXApi would be in portalAppSetup.restProxyPaths.spaceXApi.
  • resourceBasePath: Base path to access assets in resourceRoot such as images
  • lang: The current user language (e.g.: en)
  • user: User information such as username, user display name and roles. It has the following structure:
     export type MashroomPortalAppUser = {
         readonly guest: boolean;
         readonly username: string;
         readonly displayName: string;
         readonly email: string | null;
         readonly permissions: MashroomPortalAppUserPermissions;
         readonly [customProp: string]: any;
     }
    
  • appConfig: The App config object. The default is defined in defaultConfig.appConfig, but it can be overwritten per instance (per Admin App).

The clientServices argument contains the client services, see below.

Client Services

The following client side services are available for all portal apps:

  • MashroomPortalMessageBus: A simple message bus implementation for inter app communication
  • MashroomPortalStateService: State management
  • MashroomPortalAppService: Provides methods wo load, unload and reload apps
  • MashroomPortalUserService: User management services (such as logout)
  • MashroomPortalSiteService: Site services
  • MashroomPortalPageService: Page services
  • MashroomPortalRemoteLogger: A facility to log messages on the server
  • MashroomPortalAdminService: Provides methods to administer sites and pages (only available for users with the Administrator role)

MashroomPortalMessageBus

export interface MashroomPortalMessageBus {
    /**
     * Subscribe to given topic.
     * Topics starting with getRemotePrefix() will be subscribed server side via WebSocket (if available).
     * Remote topics can also contain wildcards: # for multiple levels and + or * for a single level
     * (e.g. remote:/foo/+/bar)
     */
    subscribe(topic: string, callback: MashroomPortalMessageBusSubscriberCallback): Promise<void>;

    /**
     * Subscribe once to given topic. The handler will be removed after the first message has been received.
     * Remote topics are accepted.
     */
    subscribeOnce(topic: string, callback: MashroomPortalMessageBusSubscriberCallback): Promise<void>;

    /**
     * Unsubscribe from given topic.
     * Remote topics are accepted.
     */
    unsubscribe(topic: string, callback: MashroomPortalMessageBusSubscriberCallback): Promise<void>;

    /**
     * Publish to given topic.
     * Remote topics are accepted.
     */
    publish(topic: string, data: any): Promise<void>;

    /**
     * Get the private user topic for the given user or the currently authenticated user if no argument given.
     * You can subscribe to "sub" topics as well, e.g. <private_topic>/foo
     */
    getRemoteUserPrivateTopic(username?: string): string | null | undefined;

    /**
     * The prefix for remote topics
     */
    getRemotePrefix(): string;

    /**
     * Register a message interceptor.
     * An interceptor can be useful for debugging or to manipulate the messages.
     * It can change the data of an event by return a different value or block messages
     * by calling cancelMessage() from the interceptor arguments.
     */
    registerMessageInterceptor(interceptor: MashroomPortalMessageBusInterceptor): void;

    /**
     * Unregister a message interceptor.
     */
    unregisterMessageInterceptor(interceptor: MashroomPortalMessageBusInterceptor): void;
}

MashroomPortalStateService

export interface MashroomPortalStateService {
    /**
     * Get a property from state.
     * It will be looked up in the URL (query param or encoded) and in the local and session storage
     */
    getStateProperty(key: string): any | null | undefined;

    /**
     * Add given key value pair into the URL (encoded)
     */
    setUrlStateProperty(key: string, value: any | null | undefined): void;

    /**
     * Add given key value pair to the session storage
     */
    setSessionStateProperty(key: string, value: any): void;

    /**
     * Add given key value pair to the local storage
     */
    setLocalStoreStateProperty(key: string, value: any): void;
}

MashroomPortalAppService

export interface MashroomPortalAppService {
    /**
     * Get all Portal Apps available to the user
     */
    getAvailableApps(): Promise<Array<MashroomAvailablePortalApp>>;

    /**
     * Search for all known Apps.
     */
    searchApps(filter?: AppSearchFilter): Promise<Array<MashroomKnownPortalApp>>;

    /**
     * Load a Portal App into given host element at given position (or at the end if position is not set)
     *
     * The returned promise will always resolve! If there was a loading error the MashroomPortalLoadedPortalApp.error property will be true.
     */
    loadApp(appAreaId: string, pluginName: string, instanceId: string | null | undefined, position?: number | null | undefined, overrideAppConfig?: any | null | undefined): Promise<MashroomPortalLoadedPortalApp>;

    /**
     * Load a Portal App into a modal overlay.
     *
     * The returned promise will always resolve! If there was a loading error the MashroomPortalLoadedPortalApp.error property will be true.
     */
    loadAppModal(pluginName: string, title?: string | null | undefined, overrideAppConfig?: any | null | undefined, onClose?: ModalAppCloseCallback | null | undefined): Promise<MashroomPortalLoadedPortalApp>;

    /**
     * Reload given Portal App.
     *
     * The returned promise will always resolve!
     * If there was a loading error the MashroomPortalLoadedPortalApp.error property will be true.
     */
    reloadApp(id: string, overrideAppConfig?: any | null | undefined): Promise<MashroomPortalLoadedPortalApp>;

    /**
     * Unload given Portal App.
     */
    unloadApp(id: string): Promise<void>;

    /**
     * Move a loaded App to another area (to another host element within the DOM)
     */
    moveApp(id: string, newAppAreaId: string, newPosition?: number): void;

    /**
     * Show the name and version for all currently loaded App in an overlay (for debug purposes)
     */
    showAppInfos(customize?: (portalApp: MashroomPortalLoadedPortalApp, overlay: HTMLDivElement) => void): void;

    /**
     * Hide all App info overlays
     */
    hideAppInfos(): void;

    /**
     * Add a listener for load events (fired after an App has been loaded and attached to the page)
     */
    registerAppLoadedListener(listener: MashroomPortalAppLoadListener): void;

    /**
     * Remove a listener for load events
     */
    unregisterAppLoadedListener(listener: MashroomPortalAppLoadListener): void;

    /**
     * Add a listener for unload events (fired before an App will be detached from the page)
     */
    registerAppAboutToUnloadListener(listener: MashroomPortalAppLoadListener): void;

    /**
     * Remove a listener for unload events
     */
    unregisterAppAboutToUnloadListener(listener: MashroomPortalAppLoadListener): void;

    /**
     * Load the setup for given App/plugin name on the current page
     */
    loadAppSetup(pluginName: string, instanceId: string | null | undefined): Promise<MashroomPortalAppSetup>;

    /**
     * Get some stats about a loaded App
     */
    getAppStats(pluginName: string): MashroomPortalLoadedPortalAppStats | null;

    /**
     * Check if some loaded Portal Apps have been updated (and have a different version on the server).
     * This can be used to check if the user should refresh the current page.
     *
     * Returns the list of upgraded Apps.
     */
    checkLoadedPortalAppsUpdated(): Promise<Array<string>>;

    /**
     * Prefetch resources of given App/plugin. This is useful if you know which apps you will have to load
     * in the future and want to minimize the loading time.
     */
    prefetchResources(pluginName: string): Promise<void>;

    readonly loadedPortalApps: Array<MashroomPortalLoadedPortalApp>;
}

MashroomPortalUserService

export interface MashroomPortalUserService {
    /**
     * Get the authentication expiration time in unix time ms.
     * Returns null if the check fails and "0" if the check returns 403.
     */
    getAuthenticationExpiration(): Promise<number | null>;

    /**
     * Get the unix ms left until authentication expiration.
     * Returns null if the check fails and "0" if the check returns 403.
     */
    getTimeToAuthenticationExpiration(): Promise<number | null>;

    /**
     * Extend the authentication.
     * Can be used to update the authentication when no server interaction has occurred for a while and the authentication is about to expire.
     */
    extendAuthentication(): void;

    /**
     * Logout the current user
     */
    logout(): Promise<void>;

    /**
     * Get the current user's language
     */
    getUserLanguage(): string;

    /**
     * Set the new user language
     */
    setUserLanguage(lang: string): Promise<void>;

    /**
     * Get all available languages (e.g. en, de)
     */
    getAvailableLanguages(): Promise<Array<string>>;

    /**
     * Get the configured default language
     */
    getDefaultLanguage(): Promise<string>;
}

MashroomPortalSiteService

export interface MashroomPortalSiteService {
    /**
     * Get the base url for the current site
     */
        getCurrentSiteUrl(): string;

    /**
     * Get a list with all sites
     */
        getSites(): Promise<Array<MashroomPortalSiteLinkLocalized>>;

    /**
     * Get the page tree for given site
     */
        getPageTree(siteId: string): Promise<Array<MashroomPortalPageRefLocalized>>;
}

MashroomPortalPageService

export interface MashroomPortalPageService {
    /**
     * Get current pageId
     */
    getCurrentPageId(): string;
    /**
     * Get the page friendlyUrl from given URL (e.g. /portal/web/test?x=1 -> /test)
     */
    getPageFriendlyUrl(pageUrl: string): string;
    /**
     * Find the pageId for given URL (can be a page friendlyUrl or a full URL as seen by the client).
     */
    getPageId(pageUrl: string): Promise<string | undefined>;
    /**
     * Get the content for given pageId.
     * It also calculates if the correct theme and all necessary page enhancements for the requested page
     * are already loaded. Otherwise fullPageLoadRequired is going to be true and no content returned.
     */
    getPageContent(pageId: string): Promise<MashroomPortalPageContent>;
}

MashroomPortalRemoteLogger

export interface MashroomPortalRemoteLogger {
    /**
     * Send a client error to the server log
     */
    error(msg: string, error?: Error): void;

    /**
     * Send a client warning to the server log
     */
    warn(msg: string, error?: Error): void;

    /**
     * Send a client info to the server log
     */
    info(msg: string): void;
}

MashroomPortalAdminService

export interface MashroomPortalAdminService {
    /**
     * Get all existing themes
     */
    getAvailableThemes(): Promise<Array<MashroomAvailablePortalTheme>>;

    /**
     * Get all existing layouts
     */
    getAvailableLayouts(): Promise<Array<MashroomAvailablePortalLayout>>;

    /**
     * Get all currently existing roles
     */
    getExistingRoles(): Promise<Array<RoleDefinition>>;

    /**
     * Get all app instances on current page
     */
    getAppInstances(): Promise<Array<MashroomPagePortalAppInstance>>;

    /**
     * Add an app to the current page.
     */
    addAppInstance(pluginName: string, areaId: string, position?: number, appConfig?: any): Promise<MashroomPagePortalAppInstance>;

    /**
     * Update given app instance config or position
     */
    updateAppInstance(pluginName: string, instanceId: string, areaId: string | null | undefined, position: number | null | undefined, appConfig: any | null | undefined): Promise<void>;

    /**
     * Remove given app instance from page
     */
    removeAppInstance(pluginName: string, instanceId: string): Promise<void>;

    /**
     * Get roles that are permitted to view the app (no roles means everyone is permitted)
     */
    getAppInstancePermittedRoles(pluginName: string, instanceId: string): Promise<string[] | null | undefined>;

    /**
     * Update roles that are permitted to view the app (undefined or null means everyone is permitted)
     */
    updateAppInstancePermittedRoles(pluginName: string, instanceId: string, roles: string[] | null | undefined): Promise<void>;

    /**
     * Get current pageId
     */
    getCurrentPageId(): string;

    /**
     * Get page data
     */
    getPage(pageId: string): Promise<MashroomPortalPage>;

    /**
     * Add new page
     */
    addPage(page: MashroomPortalPage): Promise<MashroomPortalPage>;

    /**
     * Update an existing page
     */
    updatePage(page: MashroomPortalPage): Promise<void>;

    /**
     * Delete the given page
     */
    deletePage(pageId: string): Promise<void>;

    /**
     * Get roles that are permitted to view the page (no roles means everyone is permitted)
     */
    getPagePermittedRoles(pageId: string): Promise<string[] | null | undefined>;

    /**
     * Update roles that are permitted to view the page (undefined or null means everyone is permitted)
     */
    updatePagePermittedRoles(pageId: string, roles: string[] | null | undefined): Promise<void>;

    /**
     * Get current siteId
     */
    getCurrentSiteId(): string;

    /**
     * Get site with given id
     */
    getSite(siteId: string): Promise<MashroomPortalSite>;

    /**
     * Add new site
     */
    addSite(site: MashroomPortalSite): Promise<MashroomPortalSite>;

    /**
     * Update existing site
     */
    updateSite(site: MashroomPortalSite): Promise<void>;

    /**
     * Delete the given site
     */
    deleteSite(siteId: string): Promise<void>;

    /**
     * Get roles that are permitted to view the site (no roles means everyone is permitted)
     */
    getSitePermittedRoles(siteId: string): Promise<string[] | null | undefined>;

    /**
     * Update roles that are permitted to view the site (undefined or null means everyone is permitted)
     */
    updateSitePermittedRoles(siteId: string, roles: string[] | null | undefined): Promise<void>;
}

portal-theme

This plugin types adds a theme to the Portal.

To register a new portal-theme plugin add this to package.json:

{
     "mashroom": {
        "plugins": [
           {
                "name": "My Theme",
                "type": "portal-theme",
                "bootstrap": "./dist/mashroom-bootstrap.js",
                "resourcesRoot": "./dist",
                "views": "./views",
                "defaultConfig": {
                    "param1": true
                 }
            }
        ]
     }
}
  • resourcesRoot: Folder that contains assets (can be accessed in the theme via resourcesBasePath)
  • views: The folder with the views. There must exist a view portal which renders a portal page

Since Mashroom Portal uses the Express render mechanism all Template Engines supported by Express can be used to define the template. The bootstrap returns the template engine and the engine name like so:

import {engine} from 'express-handlebars';
import path from 'path';

import type {MashroomPortalThemePluginBootstrapFunction} from '@mashroom/mashroom-portal/type-definitions';

const bootstrap: MashroomPortalThemePluginBootstrapFunction = async () => {
    return {
        engineName: 'handlebars',
        engineFactory: () => {
            return engine({
                partialsDir: path.resolve(__dirname, '../views/partials/'),
            });
        },
    };
};

export default bootstrap;

NOTE: Even if Express.js could automatically load the template engine (like for Pug) you have to provide the engineFactory here, otherwise plugin local modules can not be loaded. In that case define the engineFactory like this:
engineFactory: () => require('pug').__express

The theme can contain the following views:

  • portal: The portal page (required)
  • appWrapper: The wrapper for any Portal App (optional)
  • appError: The error message if the loading of a Portal App fails (optional)

A typical portal view with Handlebars might look like this:

<!doctype html>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <meta name="description" content="{{model.page.description}}">
    <meta name="keywords" content="{{model.page.keywords}}">
    {{#if csrfToken}}
        <meta name="csrf-token" content="{{csrfToken}}">
    {{/if}}

    <title>{{site.title}} - {{page.title}}</title>

    <link rel="stylesheet" type="text/css" href="{{resourcesBasePath}}/style.css">

    {{{portalResourcesHeader}}}

    {{#if page.extraCss}}
        <style>
            {{{page.extraCss}}}
        </style>
    {{/if}}
</head>
<body>
    <div id="mashroom-portal-admin-app-container">
        <!-- Admin app goes here -->
    </div>

    <header>
        <div class="site-name">
            <h1>{{site.title}}</h1>
        </div>
    </header>

    <main>
        {{> navigation}}

        <div id="portal-page-content" class="mashroom-portal-apps-container container-fluid">
            {{{pageContent}}}
        </div>
    </main>

    <div id="mashroom-portal-modal-overlay">
        <div class="mashroom-portal-modal-overlay-wrapper">
            <div class="mashroom-portal-modal-overlay-header">
                <div id="mashroom-portal-modal-overlay-title">Title</div>
                <div id="mashroom-portal-modal-overlay-close" class="close-button"></div>
            </div>
            <div class="mashroom-portal-modal-overlay-content">
                <div id="mashroom-portal-modal-overlay-app">
                    <!-- Modal apps go here -->
                </div>
            </div>
        </div>
    </div>

    <div id="mashroom-portal-auth-expires-warning">
        <div class="mashroom-portal-auth-expires-warning-message">
            {{{__ messages "authenticationExpiresWarning"}}}
        </div>
    </div>

    {{{portalResourcesFooter}}}
</body>
</html>

The pageContent variable contains the actual content with the Portal layout (see below) and the Apps.

Here all available variables:

export type MashroomPortalPageRenderModel = {
    readonly portalName: string;
    readonly siteBasePath: string;
    readonly apiBasePath: string;
    readonly resourcesBasePath: string | null | undefined;
    readonly site: MashroomPortalSiteLocalized;
    readonly page: MashroomPortalPage & MashroomPortalPageRefLocalized;
    readonly portalResourcesHeader: string;
    readonly portalResourcesFooter: string;
    readonly pageContent: string;
    // Deprecated, use pageContent
    readonly portalLayout: string;
    readonly lang: string;
    readonly availableLanguages: Readonly<Array<string>>;
    readonly messages: (key: string) => string;
    readonly user: MashroomPortalUser;
    readonly csrfToken: string | null | undefined;
    readonly userAgent: UserAgent;
    readonly lastThemeReloadTs: number;
    readonly themeVersionHash: string;
}

portal-layouts

This plugin type adds portal layouts to the portal. A layout defines a areas where portal-apps can be placed.

To register a new portal-layouts plugin add this to package.json:

{
     "mashroom": {
        "plugins": [
           {
                "name": "My Layouts",
                "type": "portal-layouts",
                "layouts": {
                    "1 Column": "./layouts/1column.html",
                    "2 Columns": "./layouts/2columns.html",
                    "2 Columns 70/30": "./layouts/2columns_70_30.html",
                    "2 Columns with 1 Column Header": "./layouts/2columnsWith1columnHeader.html"
                }
            }
        ]
     }
}
  • layouts: A map with the layout html files (on the local file system)

A layout looks like this:

<div class="row">
    <div class="col-md-8 mashroom-portal-app-area" id="app-area1">
        <!-- Portal apps go here -->
    </div>
    <div class="col-md-4 mashroom-portal-app-area" id="app-area2">
        <!-- Portal apps go here -->
    </div>
</div>

Important is the class mashroom-portal-app-area and a unique id element.

portal-app-registry

This plugin type adds a registry for portal-apps to the Portal. Can be used to integrate Portal Apps in some customized way.

To register a new portal-app-registry plugin add this to package.json:

{
     "mashroom": {
        "plugins": [
           {
                "name": "My Custom App Registry",
                "type": "portal-app-registry",
                "bootstrap": "./dist/registry/mashroom-bootstrap.js",
                "defaultConfig": {
                    "priority": 100
                }
            }
        ]
     }
}
  • defaultConfig.priority: Priority of this registry if a portal-app with the same name is registered multiple times (Default: 1)

And the bootstrap must return an implementation of MashroomPortalAppRegistry:

import {MyRegistry} from './MyRegistry';

import type {MashroomPortalAppRegistryBootstrapFunction} from '@mashroom/mashroom-portal/type-definitions';

const bootstrap: MashroomPortalAppRegistryBootstrapFunction = async (pluginName, pluginConfig, pluginContext) => {
    return new MyRegistry();
};

export default bootstrap;

The plugin must implement the following interface:

export interface MashroomPortalAppRegistry {
    readonly portalApps: Readonly<Array<MashroomPortalApp>>;
}

h3.

portal-page-enhancement

This plugin type allows it to add extra resources (JavaScript and CSS) to a Portal page based on some (optional) rules. This can be used to add polyfills or some analytics stuff without the need to change a theme.

To register a new portal-page-enhancement plugin add this to package.json:

{
     "mashroom": {
        "plugins": [
           {
                "name": "My Portal Page Enhancement",
                "type": "portal-page-enhancement",
                "bootstrap": "./dist/mashroom-bootstrap.js",
               "resourcesRoot": "./dist/public",
                "pageResources": {
                    "js": [{
                        "path": "my-extra-scripts.js",
                        "rule": "includeExtraScript",
                        "location": "header",
                        "inline": false
                    }, {
                        "dynamicResource": "myScript",
                        "location": "header"
                    }],
                    "css": []
                },
                "defaultConfig": {
                    "order": 100
                }
            }
        ]
     }
}
  • bootstrap: Path to the script that contains the bootstrap for the plugin (optional)
  • resourcesRoot: The root for all resources (can be a local path or an HTTP url)
  • pageResources: A list of JavaScript and CSS resourced that should be added to all portal pages. They can be static or dynamically generated. And they can be added to the header or footer (location) and also be inlined. The (optional) rule property refers to a rule in the instantiated plugin (bootstrap), see below.
  • defaultConfig.order: The weight of the resources - the higher it is the later they will be added to the page (Default: 1000)

The bootstrap returns a map of rules and could look like this:

import type {MashroomPortalPageEnhancementPluginBootstrapFunction} from '@mashroom/mashroom-portal/type-definitions';

const bootstrap: MashroomPortalPageEnhancementPluginBootstrapFunction = () => {
    return {
        dynamicResources: {
            myScript: () => `console.info('My Script loaded');`,
        },
        rules: {
            // Example rule: Show only for IE
            includeExtraScript: (sitePath, pageFriendlyUrl, lang, userAgent) => userAgent.browser.name === 'IE',
        }
    }
};

export default bootstrap;

The JavaScript or CSS resource can also be generated dynamically by the plugin. In that case it will always be inlined. To use this state a dynamicResource name instead of a path and include the function that actually generates the content to the object returned by the bootstrap:

{
     "mashroom": {
        "plugins": [
           {
                "name": "My Portal Page Enhancement",
                "type": "portal-page-enhancement",
                "bootstrap": "./dist/mashroom-bootstrap.js",
                "pageResources": {
                    "js": [{
                        "dynamicResource": "extraScript",
                        "location": "header"
                    }],
                    "css": []
                }
            }
        ]
     }
}
import type {MashroomPortalPageEnhancementPluginBootstrapFunction} from '@mashroom/mashroom-portal/type-definitions';

const bootstrap: MashroomPortalPageEnhancementPluginBootstrapFunction = () => {
    return {
        dynamicResources: {
            extraScript: (sitePath, pageFriendlyUrl, lang, userAgent) => `console.info('test');`,
        }
    }
};

export default bootstrap;

portal-app-enhancement

This plugin type allows it to update or rewrite the portalAppSetup that is passed to Portal Apps at startup. This can be used to add extra config or user properties from a context. Additionally, this plugin allows it to pass extra clientServices to Portal Apps or replace one of the default ones.

To register a new portal-app-enhancement plugin add this to package.json:

{
     "mashroom": {
        "plugins": [
           {
              "name": "My Portal App Enhancement",
              "type": "portal-app-enhancement",
              "bootstrap": "./dist/mashroom-bootstrap.js",
              "portalCustomClientServices": {
                  "customService": "MY_CUSTOM_SERVICE"
              }
           }
        ]
     }
}
  • bootstrap: Path to the script that contains the bootstrap for the plugin (could be omitted, if portalCustomClientServices is used)
  • portalCustomClientServices: A map of client services that should be injected in the clientServices object the Portal Apps receive. The value (in this example MY_CUSTOM_SERVICE) needs to be an existing global variable on the page (in window).

The bootstrap returns the actual enhancer plugin:

import MyPortalAppEnhancementPlugin from './MyPortalAppEnhancementPlugin';
import type {MashroomPortalAppEnhancementPluginBootstrapFunction} from '@mashroom/mashroom-portal/type-definitions';

const bootstrap: MashroomPortalAppEnhancementPluginBootstrapFunction = () => {
    return new MyPortalAppEnhancementPlugin();
};

export default bootstrap;

The plugin has to implement the following interface:

export interface MashroomPortalAppEnhancementPlugin {
    /**
     * Enhance the portalAppSetup object passed as the first argument (if necessary)
     */
    enhancePortalAppSetup: (portalAppSetup: MashroomPortalAppSetup, portalApp: MashroomPortalApp, request: Request) => Promise<MashroomPortalAppSetup>;
}

changelog

Change Log

[unreleased]

2.9.2 (August 29, 2025)

  • Admin App: Fixed the problem that the old App config was shown after re-opening the App config dialog
  • Tabify App: Fixed a timing problem when adding a new App dynamically, which made it invisible until clicking on the tab header

2.9.1 (July 22, 2025)

  • Replaced react-intl by react-i18next because it is not really suitable for Microfrontends, where you potentially have multiple React versions on the same page. See this issue: https://github.com/formatjs/formatjs/issues/4507
  • K8S Remote App Registry: Fixed displaying plugin definition if some string properties contain single quotes (')
  • JSON Schemas: Allow mjs extension for JS resources

2.9.0 (June 23, 2025)

  • Portal: The Portal throws an error now if an App has a boostrap function name that is already used by another one (this prevents Apps from overriding each other)
  • Replaced node-fetch by native (Node.js) fetch implementation
  • Added support for using TypeScript config files with Node.js 24 (see packages/test/test-server7 for a demo)
  • Added support for Node.js 24, dropped support for 18.x which reached EOL
  • Admin Toolbar: Upgrade to React 19 and modernized
  • Sandbox App: Upgrade to React 19 and modernized
  • Added source maps to all Apps
  • Made sure the redirectUrl parameters of the login and logout routes cannot be used to redirect to external hosts

2.8.1 (April 2, 2025)

  • Fixed packaging of App Gallery App

2.8.0 (April 2, 2025)

  • Login Web-App: Introduced a dark mode (by default enabled if the preferred color scheme of the browser is dark, but can be disabled in the configuration)
  • Portal Default Theme: Overhaul:
    • Introduced tons of CSS variables
    • Introduced a dark mode (by default enabled if the preferred color scheme of the browser is dark, but can be disabled in the configuration)
    • Added the possibility to override CSS variables and style with an external file:
         :root {
           --mashroom-portal-color-primary: green;
         }
      
      Example plugin config:
      {
        "Mashroom Portal Default Theme": {
          "showEnvAndVersions": false,
          "showPortalAppHeaders": false,
          "spaMode": true,
          "darkMode": "auto",
          "styleFile": "./defaultThemeOverrides.css"
        }
      }
      
  • Added a App Gallery App that shows all registered Apps with a screenshot (if available), some basic data and link to the Sandbox to try it out
  • Svelte Demo App: SSR support added
  • Portal: The server-side rendering bootstrap can now also return some script that needs to be injected into the head, which is useful if you want to pass some loaded data to the client-side for hydration. E.g.:

    const ssrBootstrap: MashroomPortalAppPluginSSRBootstrapFunction = async (portalAppSetup) => {
      const {appId} = portalAppSetup;
    
      // TODO: Load data and render
      let someLoadedData = {};
      let html = '';
    
      return {
        html,
        injectHeadScript: `
          window['__my_data_${appId}'] = ${JSON.stringify(someLoadedData)};
        `
       };
    };
    
  • Portal: Simplified server-side rendering: One the client side you can now check for portalAppSetup.serverSideRendered and call hydrate accordingly. E.g.:
     if (portalAppSetup.serverSideRendered) {
         root = hydrateRoot(portalAppHostElement, (
             <App />
         ));
     } else {
         // Default: render on client side
         root = createRoot(portalAppHostElement);
         root.render((
             <App />
         ));
     }
    
  • I18N Service: Added new method getMessageIfExists which will return null if no message with the given key exists. The existing method getMessage will now return '??key??' if the key was not found.
  • Portal: Fixed handling of multiple Modal Apps at the same time: Only the latest is shown at a time and on close the next one will get visible.
  • Sandbox App: Generate a permanent link for an active Sandbox (that can be sent to another user)
  • Sandbox App: Show unavailable (no permitted) Apps in the selection (just no one is wondering why an expected App is not there)
  • Portal: Renamed the plugin type remote-portal-app-registry to just portal-app-registry because the Portal Apps don't need to be remote. The old type is still supported for the moment.
  • Portal: The resourcesRoot property in portal-page-enhancement plugins is now expect to be top level:
    // Before
    {
      "name": "My Portal Page Enhancement",
      "type": "portal-page-enhancement",
      "pageResources": {
        "js": [{
          "path": "test.js",
          "location": "header"
         }]
       },
      "defaultConfig": {
        "resourcesRoot": "./dist/backend/page-enhancements",
        "order": "500"
      }
    }
    // Now
    {
      "name": "My Portal Page Enhancement",
      "type": "portal-page-enhancement",
      "resourcesRoot": "./dist/backend/page-enhancements",
      "pageResources": {
        "js": [{
          "path": "test.js",
          "location": "header"
         }]
       },
      "defaultConfig": {
        "order": "500"
      }
    }
    
    The old way is still supported for the moment.
  • Fixed errors occurring when mashroom-monitoring-metrics-collector was not present
  • Metrics Collector: Fixed forwarding open-telemetry errors to Mashroom log
  • HTTP Proxy: Fixed the problem that proxy took the wrong agent if an interceptor plugin changed the target URI protocol from http to https

2.7.1 (July 8, 2024)

  • All package.json fixed to avoid errors during publishing

2.7.0 (July 8, 2024)

  • VHost Path Mapper: Replace the location header URL with the relative path if the host matches the frontend host. This saves an unnecessary round trip if the protocol does not match the frontend protocol (see #121)
  • VHost Path Mapper: Fixed reverse mapping of the location header if the URL is absolute
  • HTTP Proxy: Adds now a proper x-forwarded-host header (if createForwardedForHeaders is set)
  • HTTP Proxy: Added missing createForwardedForHeaders config prop to the JSON schema
  • Portal: It is now possible to configure what should happen if the authentication expires. Until now the strategy was to just reload the current page, now you can choose between multiple strategies:

    • stayOnPage
    • reload (Default, same behaviour as before)
    • redirect (redirect to another page)
    • displayDomElement (set display: block on the DOM element with given ID)

    BREAKING CHANGE: Mashroom Portal WebApp plugin properties warnBeforeAuthenticationExpiresSec and autoExtendAuthentication have been removed, instead it expects a configuration like this:

      "authenticationExpiration": {
        "warnBeforeExpirationSec": 60,
        "autoExtend": false,
        "onExpiration": {
          "strategy": "reload"
        }
      },
    
  • LDAP Security Provider: Replaced decommissioned ldapjs client
  • Session Plugin: Uses now a session cookie (without a maxAge) by default, so, the session can only expire on the server side and not because the cookie expires

2.6.1 (Nay 12, 2024)

  • Portal: Fixed detection which App wrote a browser console error (the portalAppName will now be set properly in the server log)

2.6.0 (April 6, 2024)

  • Supply Chain Safety: Generate npm provenance statements when publishing (via Github Actions workflow)
  • Supply Chain Safety: Disabled all dependency lifecycle scripts by default
  • HTTP Proxy: Added support for transforming the request/response body. Proxy interceptors can now return streamTransformers (implementing stream.Transform) that can be used to compress/encrypt the communication to backend servers. See #116. Example:
    export default class MyInterceptor implements MashroomHttpProxyInterceptor {
       async interceptRequest(targetUri) {
           if (targetUri.startsWith('https://my-backend-server.com')) {
               return {
                   addHeaders: {
                       'content-encoding': 'gzip',
                   },
                   streamTransformers: [
                       zlib.createGzip(),
                   ],
               };
           }
       }
       async interceptResponse(targetUri, existingHeaders) {
           if (targetUri.startsWith('https://my-backend-server.com') && existingHeaders['content-encoding'] === 'gzip') {
               return {
                   removeHeaders: [
                       'content-encoding',
                   ],
                   streamTransformers: [
                       zlib.createGunzip(),
                   ],
               };
           }
       }
    }
    
  • HTTP Proxy: Removed request based proxy implementation because the module is deprecated for over 4 years now
  • Upgrade to Express 4.19 + all other libraries upgraded
  • Portal: Fixed the problem that remote subscriptions can receive the same message multiple times if subscription patterns overlap See #115

2.5.4 (January 28, 2024)

  • HTTP Proxy: Ignore empty headers added by HTTP interceptors because it will lead to an error
  • HTTP Proxy: Don't log an error if the client aborts during sending the request body
  • Portal: Don't log an error if the client aborts before the resource response is being sent (Error: aborted)

2.5.3 (January 22, 2024)

  • Remote App Registry: Renamed and added metrics:
    • Renamed: mashroom_remote_apps_total -> mashroom_remote_app_endpoints_total
    • Renamed: mashroom_remote_apps_error_total -> mashroom_remote_app_endpoints_error_total
    • Renamed: mashroom_remote_apps_connection_timeout_total -> mashroom_remote_app_endpoints_connection_timeout_total
    • Added: mashroom_remote_app_endpoints_connection_failed_total
    • Added: mashroom_remote_app_k8s_services_connection_failed_total
  • K8S Remote App Registry: Renamed and added metrics:
    • Renamed: mashroom_remote_apps_k8s_total -> mashroom_remote_app_k8s_services_total
    • Renamed: mashroom_remote_apps_k8s_error_total -> mashroom_remote_app_k8s_services_error_total
    • Renamed: mashroom_remote_apps_k8s_connection_timeout_total -> mashroom_remote_app_k8s_services_connection_timeout_total
    • Added: mashroom_remote_app_k8s_services_no_plugin_definition_total
    • Added: mashroom_remote_app_k8s_services_connection_failed_total
  • K8S Remote App Registry: Added a config property serviceProcessingBatchSize that determines how many services are processed in parallel. Can be used to optimize the time until the plugin (and therefore the Pod) gets ready on Kubernetes.
  • Remote App Registry: Made sure all endpoint URLs and properly registered before the first scan starts
  • Remote App Registry and K8S Remote App Registry: Improved error handling (shows now in the Admin UI which kind of error occurred, e.g. Timeout)

2.5.2 (January 19, 2024)

  • Background Jobs: Fixed measuring execution time for async jobs
  • Background Jobs: Added a config property invokeImmediately to invoke the job immediately once (in addition to the cron schedule)
  • Remote App Registry and K8S Remote App Registry: Made sure the plugins do not get ready until the first full scan has completed. On Kubernetes this means the Pod only gets ready if all Remote Apps have been registered already.

2.5.1 (January 17, 2024)

  • Portal: Doesn't log an error anymore if the client just aborted the connection (ERR_STREAM_PREMATURE_CLOSE)

2.5.0 (January 10, 2024)

  • PM2 Metric Exporter: Allows it now to grab the OpenTelemetry metrics from PM2 workers via inter-process communication. Works similar to the approach previously implemented in the Prometheus Metric Exporter, only the serialization to Prometheus format is not done automatically
  • Prometheus Metric Exporter: BREAKING CHANGE Removed the possibility to fetch prometheus metrics via inter-process communication within a PM2 cluster
  • Metrics Collector: Uses now OpenTelemetry to gather and export metrics. Changes:

    • The collector service supports now asynchronous metric gathering via callback
    • It is possible now to directly use the OpenTelemetry API
    • The metrics are more accurate since the get measured during export

    Due to the API structure of OpenTelemetry there are also BREAKING CHANGES if you use the metrics collector service in your custom plugins:

    • Summary metrics are no longer available (use Prometheus histogram_quantile())
    • Aggregation is no longer integrated (use Prometheus aggregations)
    • Histograms: observe() has been renamed to record()
    • Counters exist now as synchronous version with only an inc() method and as an asynchronous version with a set() method
    • Gauges only exists synchronous version with a set() method now
    • Asynchronous metrics can be used in the service.addObservableCallback() callback, like so:

       const collectorService: MashroomMonitoringMetricsCollectorService = pluginContext.services.metrics.service;
      
       collectorService.addObservableCallback((asyncCollectorService) => {
           // ... somehow get the value to measure
           asyncCollectorService.gauge('http_pool_active_connections', 'HTTP Pool Active Connections').set(theValue);
       });
      
  • Portal: Added metrics for remote resource requests (mashroom_portal_remoteresources), like request count, error count and pool stats
  • HTTP Proxy: The proxies do no longer automatically add x-forwarded- headers, because if you are using public APIs you might not want to disclose details of your internal network. Added a new config property createForwardedForHeaders to explicitly turn this feature on again. Also fixed the implementation, it takes now existing headers from reverse proxies into consideration and keeps them or extends them (which means x-forwarded-for contains now the IP address of the original client as well if there are reverse proxies).
  • HTTP Proxy: Added metrics:

    • mashroom_http_proxy_requests_ws_connection_errors

    BREAKING CHANGE Renamed metrics:

    • mashroom_http_proxy_requests_connection_errors -> mashroom_http_proxy_requests_http_connection_errors
    • mashroom_http_proxy_requests_timeouts -> mashroom_http_proxy_requests_http_timeouts
  • HTTP Proxy: The pool properties like poolMaxSocketsPerHost are now only for HTTP requests, WebSockets are handled separately and can be limited by wsMaxConnectionsPerHost
  • HTTP Proxy: Added a new implementation based on the Node.js Stream API, which is also the default now. It deliberately does not use any 3rd party libraries, because those available (like request and node-http-server) are either deprecated or unmaintained. It uses the Stream.pipeline API introduced in Node.js 10 which has an improved error handling and cleanup mechanisms compared to the old Readable.pipe used by most libraries. The performance and resource usage characteristics are very similar to the node-http-proxy based implementation. According to our tests you can even expect slightly improved throughput. If you run into to troubles here you can switch back to the previous implementation like this:
     "Mashroom Http Proxy Services": {
       "proxyImpl": "nodeHttpProxy"
     }
    
  • HTTP Proxy: Return HTTP 502 (Bad Gateway) instead of 503 if the target does not respond or is not available
  • HTTP Proxy: Fixed target URL in metrics (protocol part contained two colons)
  • Portal: Fetching and delivering App resources (js/css) improved
    • Fetching resources from remote servers and slow network devices has now a proper timeout set, because non-responding servers could potentially lead to a memory leak due to an increasing number of socket/file handles
    • The content-length header is now always correctly set
    • For remote resource 502 (Bad Gateway) or 504 (Gateway Timeout) is returned instead of just 500 if something goes wrong
    • Properties like timeout and max sockets can now be set like this in the server config:
       "Mashroom Portal WebApp": {
         "resourceFetchConfig": {
           "fetchTimeoutMs": 3000,
           "httpMaxSocketsPerHost": 10,
           "httpRejectUnauthorized": true
         }
       }
      
  • mashroom-utils refactoring: Added an index file that should be used exclusively to import utils BREAKING CHANGE: If you have used mashroom-utils in your custom plugins you have to change the imports
  • LDAP Security Provider: Fixed escaping of special characters in the DN. Didn't work if the same special character occurred multiple times.
  • Dropped support for Node.js 16 - required is now >= 18
  • All 3rd party libraries upgrades and switched to npm workspaces for dependency management BREAKING CHANGE: The request.session type no longer accept any property, you have to declare your session properties like this now:
      declare module 'express-session' {
        interface SessionData {
          foo?: string;
        }
      }
    
  • File Storage: Performance improvements:
    • It polls for external changes to the DB file every checkExternalChangePeriodMs instead with every access. This will reduce the CPU load slightly. Default value of checkExternalChangePeriodMs is now 100, but you can increase it further if you run a single server, the config is readonly or performance is more important than consistency
    • The DB file will no longer be reloaded periodically, but only if the modified timestamp of the DB file changes
  • HTTP Proxy: fixed deprecated API usages

2.4.5 (November 29, 2023)

  • Portal: It is now guaranteed that all client-side (browser) errors are actually sent to the server and logged there, even if there is a huge amount of errors and also on the browser unload event.
  • Core: Fixed deprecation warnings from the spdy module. It is now loaded lazy, but since it is no longer maintained and only works with Node.js <= 14 HTTP/2 support is de-facto broken. Shouldn't be a big deal because usual a reverse proxy handles HTTP/2 and TLS.

2.4.4 (September 8, 2023)

  • MongoDB Storage Provider: Fixed readiness probe, didn't work properly under same circumstances
  • MongoDB Session Provider: Fixed readiness probe, didn't work properly under same circumstances
  • HTTP Proxy: Pass the x-forwarded- headers to backends by default to allow them to get the actual client IP
  • HTTP Proxy: Header filtering applies now the forwardHeaders patterns to the full header (instead of accepting headers that just contain a part of the pattern, e.g. x-my-own-content-type was accepted because of content-type)

2.4.3 (July 27, 2023)

  • Portal: The time until session expiration is now calculated on the server-side. This fixes problems with premature logouts due to an invalid system time on the client. See #114
  • Portal: portalAppService.checkLoadedPortalAppsUpdated() checks now if the server version is actually different and not just compares the reload timestamps
  • Portal: Improved again the detection which App caused a browser error (for unhandled promise rejections)

2.4.2 (June 21, 2023)

  • HTTP Proxy: Provides now also metrics for total requests, errors and timeouts per target host
  • HTTP Proxy: Fixed poolMaxWaitingRequestsPerHost handling if an HTTP interceptor rewrites the target URL

2.4.1 (June 14, 2023)

  • Admin UI: The auto refresh of Admin UI pages is now disabled by default; can be enabled via link in the top right corner
  • Kubernetes Remote App Registry: If the service port changes also service url should change where the config if obtained from
  • MongoDB Storage Provider: Fixed a potential problem in the health probe which could lead to the situation that the server never reaches ready state
  • External Redis Messaging Provider: Fixed a bug in the health probe which could lead to the situation that the server never reaches ready state
  • Rest Proxy Demo App: Replaced legacy spacexdata.com API

2.4.0 (June 9, 2023)

  • Core: Ignore dot folders during initial plugin scan
  • Added an external messaging provider plugin that uses Redis PubSub (plugin name: mashroom-messaging-external-provider-redis). This allows it to use Redis for messaging between Mashroom clusters (client-side and server-side) and for communication with 3rd party system. See #35
  • Remote Messaging Demo App: Also subscribe to topic global-notifications that allows it to broadcast a message to all users
  • Helmet Middleware: Fixed default order to avoid ERR_HTTP_HEADERS_SENT errors
  • Core: Added Support for Node.js 20
  • Core: BREAKING CHANGE Dropped support for Node.js 14 which reached EOL
  • Core: BREAKING CHANGE Renamed the server config property devModePreferredBuildTool to devModeDisableNxSupport which makes more sense, because that was the idea behind the property
  • HTTP Proxy: Added new metrics for WebSocket connections:
    • mashroom_http_proxy_ws_connections_active_total
    • mashroom_http_proxy_ws_connections_active
  • HTTP Proxy: Added the possibility to limit WebSocket connections through the proxy. New config properties:
    • wsMaxConnectionsTotal (setting this to 0 disables proxying WS connections)
    • wsMaxConnectionsPerHost
  • HTTP Proxy: BREAKING_CHANGE Renamed pool metrics
    • mashroom_http_proxy_active_connections_total -> mashroom_http_proxy_http_pool_connections_active_total
    • mashroom_http_proxy_idle_connections_total -> mashroom_http_proxy_http_pool_connections_idle_total
    • mashroom_http_proxy_waiting_requests_total -> mashroom_http_proxy_http_pool_waiting_requests
    • mashroom_https_proxy_active_connections_total -> mashroom_http_proxy_https_pool_connections_active_total
    • mashroom_https_proxy_idle_connections_total -> mashroom_http_proxy_https_pool_connections_idle_total
    • mashroom_https_proxy_waiting_requests_total -> mashroom_http_proxy_https_pool_waiting_requests_total
  • HTTP Proxy: Added additional config properties to fine tune the HTTP connection pool:
    • poolMaxTotalSockets
    • poolMaxSocketsPerHost (should be used instead of poolMaxSockets which is now deprecated)
    • poolMaxWaitingRequestsPerHost - limit the number of waiting requests if all connections for a host are already occupied. Helps to avoid the problem that a single unresponsive API/backend can fill up the reverse proxy connection pools and might render the whole server unreachable - see #112
  • Add User Headers plugin: Remove all characters not allowed in HTTP headers from the display name
  • HTTP Proxy: Added 4 new metrics for active and waiting requests per target URL, see #111
    • mashroom_http_proxy_http_pool_connections_active
    • mashroom_http_proxy_http_pool_waiting_requests
    • mashroom_http_proxy_https_pool_connections_active
    • mashroom_http_proxy_https_pool_waiting_requests
  • Metrics Collector: Added the possibility to reset Gauges, this is useful if some label dynamically "disappear" and needs to be removed

2.3.2 (April 14, 2023)

  • Core: Added the possibility to set the preferred build tool in the server config (devModePreferredBuildTool). This can be used to enforce npm if you experience troubles with nx.
  • Core: Never run npm install in dev mode if a given package is not a root module and part of a mono-repo. Because in that case running npm install might break the lock file; and it is also not possible to detect if running it is necessary, because there could simply be no node_modules folder because of hoisting.

2.3.1 (April 3, 2023)

  • Portal: Added a config property versionHashSalt that allows it to generate different resource version hashes per server instance. Can be used to make sure future hashes cannot be predicted or if you want to switch between different server versions on the same domain without polluting the reverse proxy cache.
  • Portal: Added to property adminApp to the page render model, so the Admin panel can be removed if no Admin App has been set. Fixes the problem that an empty black panel remained at top in this case.
  • Portal: The client side log now determines correctly which App caused an error, even within Composite Apps

2.3.0 (February 10, 2023)

  • Portal: Made sure that the session is not touched for resource requests (images, JS, CSS) and the set-cookie header not set. Otherwise, the resources will not be cached by proxy servers.
  • Kubernetes Remote App Registry: If the service port changes the App definition gets reloaded with the next scan
  • Portal: Hot reload of Apps works now on all sites and when mashroom-vhost-path-mapper is being used
  • Kubernetes Remote App Registry: Added a config property unregisterAppsAfterScanErrors to control if Apps should be unregistered if a service cannot be reached anymore
  • Remote App Registry: Added a config property unregisterAppsAfterScanErrors to if Apps should be unregistered if an endpoint cannot be reached anymore. This fixes the problem that Apps got unregistered if the endpoint was down during the refresh. Default is -1 which means Apps are never unregistered automatically. A value of 3 would mean that Apps would be unregistered after 3 retries or 3 minutes if the scan interval is 1 minute.
  • Remote App Registry: Unregister Apps properly if they disappear from an endpoint with multiple Apps
  • Admin Toolbar: If a page gets deleted all subpages are moved up the parent level (until now they just disappeared)
  • Admin Toolbar: Doesn't allow to remove the last Site anymore
  • Portal: Made sure that all related resources are removed from the storage if a Site or Page is deleted (Permissions, App Instances, ...)
  • Portal: Added a method checkLoadedPortalAppsUpdated() to the portalAppService which allows it to check if the Portal Apps loaded in the Browser have been redeployed. This could be used in a (long-running) dynamic cockpit to inform the user that some Apps might not work as expected anymore and a reload of the page would be recommended.
  • Sandbox App: Shows now the number of loaded resources, the resources size and (if available) the memory usage of the page
  • Portal: The App Info shows now also the number of the loaded resources for an App and the decoded size of those resources
  • Core: Uses nx for building in dev mode if it is available. This should lead to a much faster startup in dev mode, especially if the distributed cloud cache is used.
  • Core: Improved support for ts-node. If Mashroom runs with ts-node all config files can be written in TypeScript. This includes plugin config files. Example server config file mashroom.ts:
     import type {MashroomServerConfig} from '@mashroom/mashroom-json-schemas/type-definitions';
     const serverConfig: MashroomServerConfig = {
         name: 'Mashroom Test Server 7',
         port: 5050,
         // ...
     ];
     export default serverConfig;
    
  • Portal: Disabled caching of Portal App chunks (from code splitting) that do not include a content hash in the file name. Because in that case the Browser would cache the chunk forever even if the content changes. If you use webpack you can add the content hash like this to chunk names:
     output: {
         // ...
         chunkFilename: 'my-app.[contenthash].js',
     }
    
  • Portal: Added support for ES6 modules in Apps. It will automatically be turned on if the bootstrap file name ends with .mjs. Checkout the example here: https://github.com/nonblocking/mashroom-plugin-demos/tree/master/packages/mashroom-demo-plain-es6-portal-app That is just a neat tech demo, in the real world you should always use a bundler, because loading dozens of uncompressed small files is very inefficient, and it is also not possible to load libraries from node_modules.
  • Portal: Added support for code-splitting in shared libraries. The only precondition is that the name of the chunks needs to be <shared_lib_base_name>.<chunk_name>.js; you would configure that in webpack like this:
      output: {
        path: __dirname + '/dist',
        filename: 'my_shared_library.js',
        chunkFilename: 'my_shared_library.[contenthash].js'
      }
    
  • Core: Fixed the type of pluginContext.service.<service_ns>: it can now be undefined because the plugin might not be loaded. This can be a BREAKING CHANGE, and you have to following options to fix TypeScript errors:

     // If the services is added as "required" in the plugin definition
     const requiredService: MashroomSecurityService = pluginContext.services.security!.service;
     // Otherwise
     const optionalService: MashroomSecurityService | unknown = pluginContext.services.security?.service;
    
     // Alternatively extend MashroomServicePluginNamespaces in a type declaration file
     declare module '@mashroom/mashroom/type-definitions' {
         export interface MashroomServicePluginNamespaces {
             security: { service: MashroomSecurityService; } | /* might not be loaded yet */ undefined;
             // Orther service plugins
         }
     }
    

2.2.3 (December 19, 2022)

  • Metrics Collector: Replace values in route labels (/my-api/customers/123456 -> /my-api/customers/#val)
  • Core: Properly exit after HTTP server shutdown

2.2.2 (December 17, 2022)

  • LDAP Security Provider and Simple Security Provider: Fixed the problem that some URL query parameters got lost after login. E.g. an URL like http://localhost:5050/portal/web/test1/sub1?a=1&b=2&c=3 was reduced to http://localhost:5050/portal/web/test1/sub1?a=1 after login.
  • Sandbox App: Introduced a query flag sbAutoTest that replaces all code inputs by simple text areas, which makes it possible to fill them with automated test tools
  • Core: Fixed shutdown of fs watcher in development mode (hung sometimes)
  • Metrics Collector: Reduced the number generated labels for mashroom_httprequest metrics. This reduces the Prometheus load, because every label generates a new time series
  • Prometheus Exporter: Fixed a memory leak when the metrics were obtained via PM2 intercom
  • OpenID Connect Security Provider: Fixed the problem that authentication attempts could fail if the IDP redirected back very quickly, but the session was not persisted in the store yet
  • Portal: Added the attribute data-mr-app-name to the default App wrapper to simplify end-2-end testing
  • Portal: The SSR route of Remote Apps will receive now also the path and the query parameters of the original request The body of the POST request looks like this now:
    export type MashroomPortalAppSSRRemoteRequest = {
       readonly originalRequest: {
        readonly path: string;
        readonly queryParameters: Record<string, any>;
      };
      readonly portalAppSetup: MashroomPortalAppSetup;
    }
    

2.2.1 (November 4, 2022)

  • Portal: Fixed the MashroomPortalMessageBusInterceptor signature and behaviour: It should be possible to change the event data by just returning a different value; but if the interceptor function returns nothing the data should stay untouched (was set to undefined before)
  • Admin Toolbar: Minor style fixes

2.2.0 (November 4, 2022)

  • Portal: Fixed the problem that users were kicked out of the Portal when requests to /api/users/authenticated/authExpiration failed (see issue #99)
  • Portal: Added support for server-side rendering of Composite Apps, which use other Portal Apps as their building blocks. It is now possible to define embedded Portal Apps in the SSR bootstrap like so:

     const bootstrap: MashroomPortalAppPluginSSRBootstrapFunction = async (portalAppSetup, req) => {
       // Generate server-side HTML that contains a <div id="unique-host-element-id"></div>
       const html = renderToString(<App/>);
    
       return {
          html,
          embeddedApps: [
             {
                 pluginName: 'The other App',
                 appConfig: {},
                 appAreaId: 'unique-host-element-id',
             }
          ]
       };
     };
    

    In the Composite App make sure you don't call portalAppService.loadApp() for that already integrated App, instead you can get the appId of the server-side embedded App like this to unload/reload it later:

      const ssrPreloadedApp = portalAppService.loadedPortalApps.find(({ pluginName, portalAppAreaId }) => pluginName === 'The other App' && portalAppAreaId === 'unique-host-element-id');
      let appId;
      if (!ssrPreloadedApp) {
       // SSR failed, load client-side
       const result = await portalAppService.loadApp('host-element-id', 'The other App', null, null, {});
       if (!result.error) {
         appId = result.id;
       }
      } else {
         appId = ssrPreloadedApp.id;
      }
    

    Checkout the mashroom-portal-demo-composite-app package for a working example.

    NOTE: You have to make sure the embedded Apps aren't removed by the render framework during hydration, in React you have to add dangerouslySetInnerHTML={{ __html: '' }} to nodes whose children shall be ignored during hydration

  • Kubernetes Remote App Registry:
    • Support for multiple Namespace and Service label selectors
    • For duplicate Portal Apps the active one is now more deterministic and depends on the namespace lookup (check the README in the mashroom-portal-remote-app-registry-k8s package)
    • For multiple Portal Apps per service: if one definition is invalid the other ones will be activated nevertheless
    • Support for duplicate service names in different namespaces
    • If a service gets removed all Portal Apps are unregistered immediately (without delay)
  • Remote App Registry: For multiple Portal Apps per endpoint, if one definition is invalid the other ones will be activated nevertheless
  • Core: Removed the forcefully stopping of the server after 5sec because this may interrupt pending requests. It also makes in impossible to increase the shutdown period via terminationGracePeriodSeconds on Kubernetes
  • Prometheus Exporter: Added support for Node.js clusters. It is now possible to use prom-client's AggregatorRegistry to gather the metrics in the master process and also to get the worker metrics within a PM2 cluster. Check out the README in the mashroom-monitoring-prometheus-exporter module for details
  • BREAKING CHANGE: Renamed the plugin mashroom-http-proxy-add-id-token to mashroom-http-proxy-add-access-token because access tokens should be used to make API requests on behalf of a user
  • Core: Failing ready and health probes log now the causes. This is helpful on Kubernetes when the Admin UI is not available if the ready probe fails
  • Added a SolidJS demo Portal App (Microfrontend)
  • Portal: BREAKING CHANGE: Themes must set now a CSS variable with the (fontawsome compatible) icon font, like so:
      :root {
         --mashroom-portal-font-icon: 'Font Awesome 6 Free';
      }
    
  • Portal: Dropped support for IE11 (and all legacy browsers which don't support ES6)
  • Admin Toolbar: Upgrade to CodeMirror 6 with autocomplete support in the CSS editor
  • Admin Toolbar: Cleanup the DOM properly after a drag ends

2.1.3 (July 2, 2022)

  • Simple Security Provider, LDAP Security Provider: Prevent a redirect loop if the user has no permission to access the login page

2.1.2 (June 14, 2022)

  • Sandbox App, Admin Toolbar: Make sure the chunks get new names when the content changes to avoid problems with browser caching
  • Error Pages: Don't show an error page if the response has content type application/json
  • Core: The health and readiness probes return now a JSON with the actual errors

2.1.1 (June 13, 2022)

  • Admin Toolbar: Fixed validation of route '/'

2.1.0 (June 13, 2022)

  • Portal: Re-check authentication expiration at least every 60sec, so, if the session for some reason expires (or gets revoked) the user will get notified faster.
  • Core: Dropped Node.js 12.x support
  • Portal: Prevent a loop if error messages can not be sent to the server
  • MongoDB Session Provider: BREAKING CHANGE: Changed config structure to be able to pass parameters to connect-mongo, such as ttl and autoRemove.

    Before:

    {
      "uri": "mongodb://username:password@localhost:27017/mashroom_session_db?connectTimeoutMS=1000&socketTimeoutMS=2500",
      "collection": "sessions",
      "connectionOptions": {
        "poolSize": 5
      }
    }
    

    After:

    {
      "client": {
        "uri": "mongodb://username:password@localhost:27017/mashroom_session_db?connectTimeoutMS=1000&socketTimeoutMS=2500",
        "connectionOptions": {
           "poolSize": 5
        }
      },
      "collectionName": "sessions",
      "ttl": 86400
    }
    
  • Redis Session Provider: BREAKING CHANGE: Changed config structure to be able to pass parameters to connect-redis, such as prefix and ttl. Setting prefix on this level instead of the Redis client level fixed the session count metric, which was broken.

    Before:

    {
      "redisOptions": {
        "host": "localhost",
        "port": "6379",
        "keyPrefix": "mashroom:sess:"
      },
      "cluster": false
    }
    

    After:

    {
      "client": {
        "redisOptions": {
          "host": "localhost",
          "port": "6379",
        },
        "cluster": false
      },
      "prefix": "mashroom:sess:",
      "ttl": 86400
    }
    
  • Admin Toolbar: Only allow valid characters (according to RFC 3986) in Routes
  • Admin Toolbar: Added checkbox for client-side routing and renamed friendlyUrl to Route because that's more what it is.
  • Portal: Added support for client-side routing. If you enable it everything appended to the page URL is ignored.
  • Portal: Added new property portalAppHost to the log context of Remote Portal Apps

2.0.7 (June 2, 2022)

  • Portal: Switched to cache-control public for App resources, even for authenticated users
  • Portal: Fixed cache busting. The v=xxx query parameter is now constant for a plugin version (if the Portal ist not in dev mode). You should now replace lastThemeReloadTs with themeVersionHash in your themes.
  • Security Service: Don't log an error for AJAX requests to restricted resources (just return 403)

2.0.6 (June 1, 2022)

  • Remote App Registry and K8S Remote App Registry: Fixed registering Apps of type portal-app2 without SSR capability
  • HTTP Proxy: Added a retry if the target resets or drops the connection (ECONNRESET) which can happen when:
  • HTTP Proxy: Fixed handling of client connection termination. Among others the misleading error message 'Target endpoint did not send a response within xxxxx' will no longer be logged in the case the client dropped the connection.
  • Portal: State encoded in URL via MashroomPortalStateService is encoded/decoded now

2.0.5 (Mai 20, 2022)

  • Portal: The resource and the proxy target URLs of Remote Apps can now overlap as long as requested resources have an extension. E.g.: If your plugin definition looks like this:
     {
       "name": "My Remote App",
       "type": "portal-app2",
       "remote": {
         "resourcesRoot": "/"
       },
       "defaultConfig": {
         "proxies": {
           "bff": {
             "targetUri": "http://localhost:6089"
           }
         }
       }
     }
    
    the Portal will calculate a resource base URL http://yourhost.com/ and a base URL for the bff proxy of http://yourhost.com/, so they overlap. Now you can request a resource /index.js with this setup, previously you couldn't, because the Portal has treated it as an attempt to fetch API data via (potentially less protected) resource request.
  • Security Service: Start authentication flow (e.g. redirect to the login page) only for GET and non-Ajax requests

2.0.4 (Mai 9, 2022)

  • Remote App Registry and K8S Remote App Registry: Show Apps with errors on top of the list in the Admin UI
  • K8S Remote App Registry: The Admin UI shows now all successfully registered Apps even if scanning some namespaces fails due to missing permissions

2.0.3 (Mai 6, 2022)

  • Portal: Fixed Express.js view caching if multiple Themes are involved. If NODE_ENV = production it was possible that views from the wrong Theme were used.

2.0.2 (Mai 2, 2022)

  • K8S Remote App Registry: The admin UI shows now the scanned namespaces
  • Portal: If an App on a page cannot be found (if it is not registered (yet)), an error message will be displayed now instead of just showing nothing. The old behaviour can be restored by setting the Mashroom Portal WebApp config property ignoreMissingAppsOnPages. On the client side you can check if an App is in error state because the plugin does not exist with
         clientServices.portalAppService.loadedPortalApps[0].errorPluginMissing;
    
  • Portal: Fixed SSR cache key to avoid possible collisions
  • Portal: Added possibility to turn of the SSR cache (even if @mashroom/mashroom-memory-cache is present)
  • OpenID Connect Security Provider: Removed the options httpRequestRejectUnauthorized and httpRequestRetry because they are no longer supported by openid-client
  • Vue Demo App: Upgrade to Vue3 and server-side rendering added
  • Sandbox App: Show all Apps for the Administrator role, even if defaultRestrictViewToRoles is set

2.0.1 (April 25, 2022)

  • VHost Path Mapper: Fixed root url handling

2.0.0 (April 25, 2022)

  • Portal: Proxy paths starting with '/' are now accepted for Remote Apps (and considered to be on the server that hosts the App)
  • Portal: Fixed clientServices.stateService.setUrlStateProperty()
  • Storage: Added support for $not in filters
  • File Storage: Fixed invalid totalCount if limit was set

2.0.0-alpha.4 (March 24, 2022)

  • Portal: The themes expose now CSS variables, which could be used in Microfrontends (Portal Apps)
  • Portal: The Portal tries now to resolve the origin App for all console errors sent to the server. The App name and version is appended to the message and added to the log context. See issue #93
  • OpenID Connect Security Provider: If token validation in the callback fails retry authentication instead of just responding with 403
  • LDAP Security Provider: Made username lookup in userToRoleMapping case-insensitive
  • Admin Toolbar: Fixed applying new appConfig after reload
  • Default Login: Improved the style and added another config property pageTitle for the title in the header (Default is the server name).

2.0.0-alpha.3 (February 7, 2022)

  • Admin Toolbar: The Portal App selection shows now the i18n title instead of the App name and the i18n description
  • Portal: Plugins of type portal-app2 can now have an internationalized description that will be showed in the Admin Toolbar:
    {
        "plugins": [
            {
                 "name": "Unique Name For My App",
                 // ...
                "defaultConfig": {
                    "title": {
                       "en": "My App",
                       "de": "Meine App"
                    },
                    "description": {
                        "en": "A simple React SPA with cool features",
                        "de": "Ein einfacher React SPA mit tollen Features"
                    }
                    //...
               }
            }
        ]
    }
    
  • Portal Default Theme: Inlined critical CSS for performance reasons
  • Portal: Added the Express Request to the SSR bootstrap, so it can access the pluginContext (logger, services)
  • VHost Path Mapper: It is now possible to map multiple Portal sites to different base paths on the same virtual host
  • VHost Path Mapper: Fixed reverse mapping of the location header if a frontendBasePath exists

2.0.0-alpha.2 (February 1, 2022)

  • Storage: Added a new method updateMany to update multiple entries at once
  • Portal: Allowed the Theme templates to access the full user, including extraData
  • Added health probes for the Remote App registry, so, the server will only be ready once the initial scan has been done (otherwise requests will hit instances with missing Apps).
  • Added health probes for Mongo, Redis, MQTT and AMQP. This means, if some plugins (e.g. storage) rely on them, the server ready probe (/mashroom/health/ready) will return an error if they are not available.
  • Core: Added the possibility to register health probes for plugins. Use this if your plugin relies on external service, and you want the flag the instance not ready if it is not available. Usage:

    const bootstrap: MashroomStoragePluginBootstrapFunction = async (pluginName, pluginConfig, pluginContextHolder) => {
        const {services: {core: {pluginService, healthProbeService}}} = pluginContextHolder.getPluginContext();
    
        healthProbeService.registerProbe(pluginName, healthProbe);
    
        pluginService.onUnloadOnce(pluginName, () => {
            healthProbeService.unregisterProbe(pluginName);
        });
    
        // ...
    };
    
  • Portal: Disabled browser cache for public pages as well, because they can contain dynamic content from enhancement plugins.
  • Storage: BREAKING CHANGE: MashroomStorageCollection.find() returns now a wrapper object with metadata such as the totalCount instead of directly the result
  • JSON Schemas: Fixed validation of custom plugin definitions

2.0.0-alpha.1 (January 21, 2022)

  • Portal: Prevent misusing resource requests for Remote Apps to access proxy targets (if a proxy target is a sub-path of the resource base URL)
  • Portal: Added config property addDemoPages to start with an empty Portal if set to false
  • Theme refurbishment: Switched to a new cool logo and a slightly more blueish primary color
  • Portal: Added CDN support for Theme and all Portal App resources. All you need to do is to add mashroom-cdn to your dependencies and configure it like shown below.
  • Added a CDN Service that can be used to obtain a CDN host to fetch resources. Basically, it just returns a host from a configurable list (round-robin):
      {
        "Mashroom CDN Services": {
          "cdnHosts": [
            "//cdn1.my-portal.com",
            "//cdn2.my-portal.com"
          ]
        }
      }
    
  • Added a middleware to deliver a robots.txt
  • Portal: Added to possibility to define custom App Config editors per Portal App. This is useful for Apps that have an editable content (e.g. from a Headless CMS). A custom editor is basically just another Portal App (SPA) that receives a special object within the appConfig with the config of the target App and a function to update it:

     const bootstrap: MashroomPortalAppPluginBootstrapFunction = (portalAppHostElement, portalAppSetup, clientServices) => {
       const {appConfig: {editorTarget /* MashroomPortalConfigEditorTarget */}} = portalAppSetup;
    
       const currentAppConfig = editorTarget.appConfig;
       // Open Editor with current config
    
       // Update with new Config
       editorTarget.updateAppConfig(newAppConfig);
     };
    

    In the App that wants to use the editor just update the plugin definition like this:

         "defaultConfig": {
           "editor": {
             "editorPortalApp": "My Editor App",
             "position": "in-place"
           }
        }
    

    Since the target App remains active it is also possible to use the message bus to exchange information between the editor and the actual App.

  • Portal: Support for Hybrid Apps with server side rendering added. When a page is rendered the Portal tries to get the initial HTML for all Apps on in and integrated it into the template. The server side HTML will also be cached (if configured). If the server side rendering takes too long (default more than 2000 ms) the Portal automatically switches to client side rendering, but puts the result into the cache anyways for subsequent page requests. The additional configuration in the mashroom-portal plugin looks like this:
      {
        "ssrConfig": {
          "ssrEnabled": true,
          "renderTimoutMs": 2000,
          "cacheTTLSec": 300,
          "inlineStyles": true
        }
      }
    
  • Portal: New plugin definition for Portal Apps with type portal-app2 added to be able to integrate new features such as SSR and config editor. Changes are compared to portal-app are:
    • Moved title, tags and category to defaultConfig, so it can be overwritten per server instance
    • bootstrap has been renamed to clientBootstrap
    • The resourcesRoot can now be defined for local deployment and remote access separately
    • restProxies has been renamed to proxies because the proxy supports all kinds of HTTP and WebSocket connections
    • Caching config added
    • Custom editor config added Existing portal-app definitions are still valid, but if you want to upgrade, change the following:
      {
        "name": "My Single Page App",
        "title": "My Single Page App",
        "category": "Demo",
        "tags": ["what", "ever"],
        "type": "portal-app",
        "bootstrap": "startMyApp",
        "defaultConfig": {
          "resourcesRoot": "./dist",
          "restProxies": {
             "spaceXApi": {
                 "targetUri": "https://api.spacexdata.com/v3",
                 "sendPermissionsHeader": false,
                 "restrictToRoles": ["Role1"]
             }
          }
        }
      }
      
      to:
      {
        "name": "My Single Page App",
        "type": "portal-app2",
        "clientBootstrap": "startMyApp",
        "local": {
          "resourcesRoot": "./dist",
          "ssrBootstrap": "optional-ssr-bootstrap-file"
        },
        "remote": {
          "resourcesRoot": "/if-remote-access-supported",
          "ssrInitialHtmlPath": "optional-ssr-route"
        },
        "defaultConfig": {
          "title": "My Single Page App",
          "category": "Demo",
          "tags": ["what", "ever"],
          "caching": {
            "ssrHtml": "same-config-and-user"
          },
          "editor": {
            "editorPortalApp": "My Optional App Config Editor",
            "position": "in-place",
            "appConfig": {
            }
          },
          "proxies": {
             "spaceXApi": {
                 "targetUri": "https://api.spacexdata.com/v3",
                 "sendPermissionsHeader": false,
                 "restrictToRoles": ["Role1"]
             }
          }
        }
      }
      
  • Storage: The Storage API (MashroomStorage) supports now a subset of Mongo's filter operations ($gt, $regex, ...), sorting and proper paging (skip + limit). So you can do something like:
         await storage.find({ $and: [{ b: { $gt: 1 }}, { x: { $exists: false }}]}, 10, 0, { b: 'asc' })
    

1.9.3 (January 11, 2022)

  • Portal: Added i18n title to MashroomPortalAppService.getAvailableApps()
  • Background Jobs: The cronSchedule property is now optional for backend jobs. If you omit it the job will be executed exactly once at startup. Which is useful if you want to do something during startup.
  • Core: When the build fails, also info gathered from stdout is logged. Tools like eslint use this channel to provide more detailed info about the failure

1.9.2 (December 11, 2021)

  • Security: The security providers that rely on the Express Session (Simple, LDAP, OpenID-Connect) are going to log an error now if the session configuration is not suitable (e.g. when the access token lives longer than the session that stores it).
  • Browser Cache: Increased the default TTL to 30d to improve the Lighthouse score. Make sure all your resources in the Theme use cache busting or set a lower TTL value for your server like this:
     {
       "plugins": {
         "Mashroom Cache Control Services": {
           "maxAgeSec": 86400
         }
       }
     }
    
  • Portal Default Theme: Fixed setting and updating meta description and keywords
  • Portal Default Theme: Fixed crawlability (some hrefs were missing)
  • MongoDB Session Provider: Fixed startup and detecting connected state
  • Admin Toolbar: Works now if the site path is "/" (because the /portal/my-site is mapped to / via vhost path mapper)
  • Portal: Made sure the MashroomPortalStateService works with dynamic routing (when paths are updated via History API)

1.9.1 (November 8, 2021)

  • Portal: Fixed loading multiple instances of an App on the same page (was broken due to a caching issue, see issue #89)
  • LDAP Security Provider: Fixed searching for groups if the distinguished name contains special characters (as defined in RFC 2253)

1.9.0 (October 18, 2021)

  • Portal: BREAKING CHANGE: Removed portalBasePath from the page render model because it doesn't make sense in combination with the vhost path mapper. Only use siteBasePath.
  • Portal: Client log messages contain now the path of the page where they were generated
  • K8S Remote App Registry: Added the possibility to filter services by label selectors Example:
      "k8sNamespacesLabelSelector": "environment=development,tier=frontend",
      "k8sNamespaces": null,
      "k8sServiceLabelSelector": "microfrontend=true,channel!=alpha"
    
  • K8S Remote App Registry: Added the possibility to scan in namespaces identified by a labelSelector. Example: Scan all services in namespaces with label environment=development and microfrontend in the name:
      "k8sNamespacesLabelSelector": "environment=development",
      "k8sNamespaces": null,
      "serviceNameFilter": "(microfrontend-)",
    
  • Portal: BREAKING CHANGE: Removed MashroomRestService from client services because it is only intended for internal use. Portal Apps should use fetch directly.
  • Default Theme: Added an SPA mode where the theme will try to operate like an SPA and loads new page content via AJAX and replaces the DOM. This only works until the user does not navigate on a page with a different theme or different page enhancements, in that case a full page load is triggered.
  • Portal: Added an API endpoint to just fetch the page content without header, navigation, page enhancements and so on. This can be used for themes that work like an SPA. Example: http://localhost:5050/portal/web/___/api/pages/test2/content?currentPageId=subpage1 Means: Give me the content (and scripts to launch/hydrate the Apps) for page test2, and I'm currently on page subpage1, tell me if I need a full page load because the theme or something else outside the content area is different.
  • Portal: Initial pages are now completely rendered on the server side (including the App wrapper). To make it more clear that not only the layout is rendered anymore, the property portalLayout in the render model has been deprecated and the new property pageContent should be used instead.
  • Portal: It is now possible to define how to render an App and errors during App loading are rendered in the theme. You just need to add the new views appWrapper and appError. The content of appWrapper could look like this (when using Handlebars):
    <div id="portal-app-{{appId}}" class="mashroom-portal-app-wrapper portal-app-{{safePluginName}}">
      <div class="mashroom-portal-app-header">
        <div class="mashroom-portal-app-header-title" data-replace-content="title">{{title}}</div>
      </div>
      <div class="mashroom-portal-app-host" data-replace-content="app">
       {{#if appSSRHtml}}
         {{{appSSRHtml}}}
       {{else}}
         <div class="mashroom-portal-app-loading"><span/></div>
       {{/if}}
     </div>
    </div>
    
    BREAKING CHANGE: Previously it was possible to customize the App wrapper and error message using the client side functions MashroomPortalCreateAppWrapperFunc and MashroomPortalCreateLoadingErrorFunc - those are ignored now.
  • Default Theme: Added a flag (showPortalAppHeaders) to the config to be able to hide the App headers
  • Admin App: Show/Hide App Control is now persisted during page navigation
  • Added a demo Composite App: Demonstrates the possibility to use existing Apps as building blocks within other Apps. Basically it uses the MashroomPortalAppService to tell the Portal where it should place an App with a given name and a custom appConfig. Additional it demonstrates how such a Composite App can have a "private" message bus.
  • Portal: BREAKING CHANGE: Removed sendUserHeaders and addHeaders from the proxy config of Portal Apps because both should be done via HTTP Proxy Interceptor now. If you were using sendUserHeaders just add mashroom-http-proxy-add-user-headers to your plugins.
  • Added a plugin to add the ID/JWT token to backend requests if OpenID connect is used (mashroom-http-proxy-add-id-token)
  • Added a plugin to add user data as headers to backend requests (mashroom-http-proxy-add-user-headers)
  • HTTP Proxy: The HTTP interceptor can now also process WebSocket upgrade requests (added optional method interceptWsRequest())
  • MongoDB client upgraded to v4 BREAKING CHANGE: If you use mashroom-session-provider-mongodb or mashroom-storage-provider-mongodb please check your connection options since they have changed. E.g. poolSize and useUnifiedTopology no longer exist. Check out https://mongodb.github.io/node-mongodb-native/4.1/classes/MongoClient.html#options
  • Admin App: Bundle size cut in halve, loads now faster
  • Sandbox App: It is possible now to search for Apps (autocomplete)
  • Portal: Fixed the problem that pages with special characters (like Umlaute) in their path didn't work

1.8.3 (September 11, 2021)

  • HTTP Proxy: The default implementation forwards now query parameters correctly if the base path already contains query parameters - fixes #85
  • Sandbox App: Shows only Apps which are available for the authenticated user now (previously it also showed Apps that could not be loaded by the user)
  • Admin App: Fixed broken autocomplete of roles
  • Sandbox App: Apps are sorted by their name now

1.8.2 (September 6, 2021)

  • Legacy Browser Support: Added a polyfill for document.currentScript to support lazy loading via webpack 5 automatic public path
  • Portal: Improved client side error handling; console errors are also logged to the server now and errors are serialized properly
  • HTTP Proxy: Added additional metrics such as the served requests, connection errors and timeouts
  • HTTP Proxy: Fixed the error handling in the node-http-proxy based (default) proxy implementation; this fixes #77
  • WebSocket Demo Portal App: Switched to a working WebSocket echo server
  • Portal: Fixed caching problem in IE11 that confused the auth expiration check

1.8.1 (August 23, 2021)

  • Portal: Allow proxies to cache shared resources such as Theme assets and shared page enhancement assets (by setting "public" in the Cache-Control header)
  • Portal IFrame App: Waiting until the iframe content is available works now in Chrome also (prevents an empty iframe from being shown)

1.8.0 (August 9, 2021)

  • Portal: Fixed login failures due to "ENOENT: no such file or directory" errors when using mashroom-session-provider-filestore under Windows
  • Portal: Reduced the number of session expiration checks to the server
  • OpenID Connect Security Provider: Allowed multiple parallel auth requests. This fixes the problem that the login failed if multiple browser tabs were open and triggered the login at the same time.
  • OpenID Connect Security Provider: Reduced the number of token refreshes
  • Added a demo App for WebSocket proxy usage (@mashroom/mashroom-portal-demo-websocket-proxy-app)
  • Portal: The App proxy supports now WebSocket. This means, that Apps (Microfrontends) can open WebSocket connections to servers "behind" the Portal. The usual (optional) Security headers going to be sent with the initial upgrade/handshake request. Proxy interceptors are ignored for WebSocket connections.
  • HTTP Proxy: Added WebSocket support
  • HTTP Proxy: Fixed rewriting the host header, so forwarding works even if the target server uses virtual hosting
  • HTTP Proxy: The node-http-proxy based implementation is now default
  • Added HTTP/2 support for HTTPS - this currently uses the node-spdy modules which has a known problem with compressed data. So, don't use this if your API server compresses responses. Also, don't use this if you rely on WebSocket or SSE. To enable it add this to you your server config:
    {
       "enableHttp2": true
    }
    
  • Added TLS support (HTTPS). Can be enabled like this in the server config:
    {
       "httpsPort": 5443,
       "tlsOptions": {
           "key": "./certs/key.pem",
           "cert": "./certs/cert.pem"
       }
    }
    
    The tlsOptions are passed to https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener
  • Remote App Registry and Remote App Registry Kubernetes: Added support external plugin definitions. The need to be in JSON format and also expose on / by the server.
  • Core: Mashroom supports now "external" plugin definition files, so the "mashroom" node in package.json can be in a separate file, by default mashroom.json or mashroom.js. E.g.:
    {
        "$schema": "https://www.mashroom-server.com/schemas/mashroom-plugins.json",
        "devModeBuildScript": "build",
        "plugins": [
            {
                "name": "Mashroom Portal Demo React App",
                "type": "portal-app",
                "bootstrap": "startReactDemoApp",
                "resources": {
                    "js": [
                        "bundle.js"
                    ],
                    "css": []
                },
                "defaultConfig": {
                    "resourcesRoot": "./dist"
                }
            }
        ]
    }
    
    The possible file name can be changed in the server config via the externalPluginConfigFileNames config property.
  • Introduced JSON Schemas for all config files:
    • package.json: schemas/mashroom-packagejson-extension.json
    • mashroom.json (Server config): schemas/mashroom-server-config.json
    • acl.json: schemas/mashroom-security-acl.json
    • groupToRoleMapping.json: schemas/mashroom-security-ldap-provider-group-to-role-mapping.json
    • userToRoleMapping.json: schemas/mashroom-security-ldap-provider-user-to-role-mapping.json
    • users.json: schemas/mashroom-security-simple-provider-users.json
    • topicACL.json: schemas/mashroom-security-topic-acl.json
    • remotePortalApps.json: schemas/mashroom-portal-remote-apps.json The schema can be applied by adding @mashroom/mashroom-json-schemas to your dependencies:
      {
       "$schema": "./node_modules/@mashroom/mashroom-json-schemas/schemas/mashroom-packagejson-extension.json",
       "name": "my-package"
      }
      
      or by using the remote location:
      {
       "$schema": "https://www.mashroom-server.com/schemas/mashroom-packagejson-extension.json",
       "name": "my-package"
      }
      
  • BREAKING CHANGE: All default config file names are now in camel case. The following config files had been renamed:
    • remote-portal-apps.json -> remotePortalApps.json
    • topic_acl.json -> topicACL.json
  • Tabify App: Added the possibility to have fixed titles for the tabs (appConfig.fixedTabTitles)
  • Portal: Added metaInfo and screenshots to MashroomPortalAppService.getAvailableApps() response. This allows an App to launch another App based on metadata and could be used to show a preview image.
  • OpenID Connect Security Provider: Allow mapping arbitrary claims to user.extraData
  • OpenID Connect Security Provider: Allow configuring HTTP timeout and number of retries when contacting the Authorization Server

1.7.10 (June 22, 2021)

  • Sandbox App: Fixed App container style

1.7.9 (June 19, 2021)

  • Core: Added a new property devModeNpmExecutionTimeoutSec to the server config to increase the npm execution timeout in dev mode on slow computers (default is 180)
  • Sandbox App: The sandbox now uses the "real" message bus and therefore supports Apps that use the message bus interceptor, and can also simulate communication with other Apps on the same page. Also, the Sandbox can now load itself properly ;-)
  • Portal: MashroomPortalRemoteLogger improved:
    • Added an info() method
    • Fixed client side error serialisation
  • Virtual host path mapper: Make sure the reverse mapped location header is never empty (fixes #79)

1.7.8 (May 31, 2021)

  • 3rd party libraries with know vulnerabilities upgraded
  • Angular Demo App: Remove zone.js because it pollutes the public space and possibly influences other Apps
  • Portal: Added the possibility to prefetch Portal App resources (MashroomPortalAppService.prefetchResources) which is useful if you know which apps you will have to load in the future and want to minimize the loading time.
  • Portal: Fixed loading shared CSS resources
  • Portal: Allow messages with empty data (null or undefined)
  • Portal: Take existing appConfig for Portal Apps from server config file even if the App itself has no appConfig defined

1.7.7 (May 20, 2021)

  • Simple Security Provider, LDAP Security Provider: Fixed adding roles to storage (caused lock exceptions under some circumstances)

1.7.6 (May 10, 2021)

  • Portal: Disable the browser caching for all pages if a CSRF token is present, otherwise stale tokens could be used
  • K8S Remote App Registry: Improved compatibility with Kubernetes 1.20
  • Added the possibility to delay the server shutdown after receiving SIGTERM via environment variable WAIT_BEFORE_SERVER_CLOSE, which contains the seconds to wait. This is required for a non-disruptive rolling deployment on Kubernetes where the kube-proxy takes some time to rewrite iptables. It also allows active request to finish properly. See: https://blog.laputa.io/graceful-shutdown-in-kubernetes-85f1c8d586da
  • Http Proxy: Allow it to disable connection keep-alive; mitigates #77
  • Prevented plugins with the same name to silently overwrite each other. If a plugin with the same name already exists it will not be loaded anymore, and an error in the Admin UI will be shown
  • Made the name property required for all plugins and disallowed some characters like '/' and '?' to prevent problems when the name is used in the path
  • LDAP Security Provider: Retry the login if the first attempt fails with ECONNRESET (happens sporadically with Active Directory)
  • Buffering WS messages when client connection is in state "closing". Otherwise, any attempt to send a message produces an error

1.7.5 (April 20, 2021)

  • Portal: Cache busting improved:
    • A new property lastThemeReloadTs can now be used in themes for resource URLs
    • Added ?v=<lastRelaodTs> to all resources exposed via Page Enhancement Plugin
  • Error Pages: Fixed the problem that error pages were open to reflected XSS if the $REQUEST_URL was used in the template

1.7.4 (March 17, 2021)

  • Fixed bug in the K8S registry

1.7.3 (March 17, 2021)

  • K8S Remote App Registry: Just ignore services without a proper descriptor (instead of throwing an error)
  • Http Proxy: Removed double request path URI-decoding in forward method (request path already URI-decoded by Express was decoded again)

1.7.2 (March 10, 2021)

  • Portal: Fixed loading of remote Apps with invalid proxy targetUri

1.7.1 (March 10, 2021)

  • Fixed the broken mashroom-portal-remote-app-registry-k8s plugin (the K8S connector was not initialized properly)

1.7.0 (March 9, 2021)

  • Build under windows fixed
  • Problems with Node 15.x fixed
  • Http Proxy: Fixed encoding of query parameters (already URL encoded parameters were encoded again)
  • Portal: Added the possibility to define a default proxy config for Portal Apps. This is useful if you want to enable the permissions header for all Apps
  • Moved out some example plugins to the new https://github.com/nonblocking/mashroom-plugin-demos repo
  • Renamed mashroom-portal-demo-remote-messaging to mashroom-portal-remote-messaging-app because it's more a test util than a demo, just like the sandbox app
  • Admin UI (under /mashroom) polished up
  • Added a new plugin type admin-ui-integration to register an arbitrary web-app or static plugin as panel in the Admin UI (will be integrated via iframe)
  • Remote App Registry Kubernetes: BREAKING CHANGE: Since it uses ow the new background job scheduler to scan the k8s cluster, you also need to add mashroom-background-jobs to your dependencies, and the configuration slightly changed. Checkout the README in mashroom-portal-remote-app-registry-k8s.
  • Remote App Registry: BREAKING CHANGE: Since it uses ow the new background job scheduler to scan for remote apps, you also need to add mashroom-background-jobs to your dependencies, and the configuration slightly changed. Checkout the README in mashroom-portal-remote-app-registry.
  • Added a background job plugin (mashroom-background-jobs) that allows it to schedule arbitrary jobs based on cron expressions
  • HTTP Proxy: Added a second proxy implementation based on node-http-proxy, since request is deprecated. It can be enabled like this in the config:
    {
      "Mashroom Http Proxy Services": {
        "proxyImpl": "nodeHttpProxy"
      }
    }
    
    Default is still the request based implementation.
  • Migration to TypeScript completed (but flow types are still available).

    The type aliases for express (ExpressRequest, ExpressResponse) are no longer required, so you can directly use the express types. E.g. in a middleware plugin:

     import type {Request, Response, NextFunction} from 'express';
     import type {MashroomMiddlewarePluginBootstrapFunction} from '@mashroom/mashroom/type-definitions';
    
     const myMiddleware = (req: Request, res: Response, next: NextFunction) => {
         const logger = req.pluginContext.loggerFactory('my.middleware');
         logger.info('woohoo');
         // TODO
         next();
     };
    
     const bootstrap: MashroomMiddlewarePluginBootstrapFunction = async (pluginName, pluginConfig) => {
         return myMiddleware;
     };
    
     export default bootstrap;
    
  • Error Pages: Added the possibility to add default messages if mashroom-i18n is not (yet) available
  • LDAP Security Provider: Under all circumstances prevent a login with an empty password since some LDAP servers accept it and allow a simple login
  • Portal: Load the JS resources for apps sequentially, because if there is more than one bundle they typically depend on each other
  • LDAP Security Provider: Add all roles from the groupToRoleMapping and userToRoleMapping to the known roles to improve the autocomplete in the Admin UI
  • Simple Security Provider: Add all roles from users.json to the known roles, to improve the autocomplete in the Admin UI

1.6.4 (February 1, 2021)

  • HTTP Proxy: Added an optional order property to interceptor configs that allows it to determine the execution order
  • HTTP Proxy: Allow it to intercept/modify the response from the target service BREAKING CHANGE: The MashroomHttpProxyInterceptor interface changed and is not compatible with the previous one:
    • intercept() has been renamed to interceptRequest()
    • A new method interceptResponse() has been added
    • Instead of returning result.reject you can now call res.sendStatus() yourself and just return result.responseHandled as a hint that the proxy doesn't have to do anything.
  • HTTP Proxy: Fixed a problem with special characters in target URIs
  • LDAP Security Provider: Added an optional user to role mapping which is useful if you want to give just a specific user access to a Portal page and such.
  • Security: The MashroomSecurityService.login() method also returns now a reason if possible (e.g. Invalid credentials). This works at the moment for the Simple Provider and the LDAP Provider (only Active Directory and OpenLDAP).
  • Portal: Fixed the client log handler (didn't terminate correctly)

1.6.3 (December 20, 2020)

  • Error Pages: Deliver error pages only if text/html explicitly is accepted (and not for xhr/fetch requests).

1.6.2 (December 14, 2020)

1.6.1 (November 26, 2020)

  • Portal: Fixed the problem that incomplete pages and sites could be saved in the Portal (which could lead to a corrupt storage)
  • Portal: The portal-page-enhancement plugins have now a config property "order" that defines in which order the resources are added to a page (useful for polyfills that needs to be started first)
  • Portal: Fixed logout when exposing a site via vhost-path-mapper and frontendBasePath is empty or "/"

1.6.0 (November 11, 2020)

  • Portal: Decreased start time of static Portal Apps by delivering the appSetup with the Portal Page
  • Core: Plugins are now only built when they changed since the last start. This dramatically decreases the start time in dev mode.
  • Added a new plugin mashroom-portal-legacy-browser-support that adds polyfills for legacy browsers (such as IE11) to all portal pages (only if IE detected)
  • Portal: Added a new plugin type portal-app-enhancement that allows it to update or rewrite the portalAppSetup that is passed to Portal Apps at startup. This can be used to add extra appConfig or user properties from a context. Additionally, this plugin allows it to pass extra clientServices to Portal Apps or replace one of the default ones.
  • Portal: Added a new plugin type portal-page-enhancement that allows it to add extra resources (JavaScript and CSS) to a Portal page based on some (optional) rules. The resources can also be generated dynamically. This can be used to add polyfills or some analytics stuff without the need to change the theme.
  • HTTP Proxy: The HTTP interceptor plugins now receive the original headers from the incoming request without filtering
  • Portal: Plugin updates are now pushed to the Browser via SSE (in development mode). So, Portal Apps are reloaded faster after an update. Also, the portal page reloads on theme or layout changes.
  • Portal: Fixed the problem that the CSRF token was invalidated on public pages when an ajax request was rejected by the ACL check. And after the invalidation all subsequent ajax POST's were failing
  • Http Proxy: Fixed nasty bug that caused http headers to be dropped randomly because RegExp.lastIndex is not reset when test() returns true

1.5.4 (October 29, 2020)

  • Portal: The base backend proxy path is now also passed to the apps. This simplifies the handling of mulitple proxis. E.g. if you have a proxy definition like this:
    {
       "restProxies": {
          "spaceXApi": {
              "targetUri": "https://api.spacexdata.com/v3"
          },
          "secondApi": {
              "targetUri": "..."
          }
      }
    }
    
    You could fetch SpaceX's rocket starts like this:
      const apiUrl = portalAppSetup.restProxyPaths.__base;
      fetch(`${apiUrl}/spaceXApi/launches/upcoming`)
      // Instead of:
      // fetch(`${portalAppSetup.restProxyPaths.spaceXApi}/launches/upcoming`)
    
  • LDAP Security Provider: Added possibility to map arbitrary LDAP attributes to user.secrets
  • OpenID Connect security provider: Add the access token to user.secrets.accessToken so it could for example be used in a Http Proxy Interceptor
  • Simple Security Provider: Allow to set extraData and secrets for users
  • Added a property secrets to the user. In opposite to the extraData property the data will never be exposed to the browser (e.g. to Portal Apps).
  • Added a plugin that exports Node.js and plugin metrics to PM2 (via pm2/io)
  • Fixed broken support for older browsers (IE11)

1.5.3 (October 18, 2020)

  • Core: Show cluster details such as the worker PIDs if the Node.js cluster module is active
  • Http Proxy: Added a new plugin type http-proxy-interceptor which allows it to rewrite target URIs and headers. This can be used to add security headers to backend calls. BREAKING CHANGE: The getApiSecurityHeaders() method in the security provider interfaces has been removed since the http-proxy-interceptor is the more generic approach to solve the same problem.
  • LDAP Security Provider: Added possibility to map arbitrary LDAP attributes to user.extraData
  • LDAP Security Provider: Use the LDAP attributes displayName or givenName + sn as displayName instead of cn
  • Portal: The mashroom-portal-demo-alternative-theme module uses now express-react-views and TypeScript to demonstrate a type save theme template

1.5.2 (October 6, 2020)

  • BREAKING CHANGE: All paths (config, sessions, ...) are now relative to the Mashroom config file (if they are not absolute)
  • WebSockets: Clients can now reconnect to the previous session and receive missed messages if they use the clientId generated by the server
  • LDAP Security Provider: Improved reliability and performance
  • Portal: ReasonReact based demo app added
  • Upgraded libraries with known vulnerabilities

1.5.1 (August 25, 2020)

  • Portal: The MashroomPortalStateService prefixes now data in the browser storage to avoid conflicts. So, its save now to use it to persist the application state like this:
         stateService.setLocalStoreStateProperty('state', store.getState());
    
  • Portal: Made it possible to use environment variables in the plugin definition of remote portal apps as well
  • Portal: Call the willBeRemoved lifecycle hook of all apps on page unload; this gives the app a chance to do some cleanup or persist its state properly
  • Portal: Made defaultTheme and defaultLayout in the site configuration optional and derive it from the server configuration if not set

1.5.0 (June 14, 2020)

  • Sandbox: Added query parameter sbPreselectAppName to preselect an app without loading it
  • Added metrics for: Plugin count, Remote apps, Session count, HTTP proxy pool, WebSocket connections, Redis/MongoDB/MQTT/AMQP connection status
  • Added a plugin that exports the collected metrics for the Prometheus monitoring system
  • Added a metrics collector plugin that adds request metrics and exposes a service for other plugins to add more metrics
  • MongoDB Storage Provider: Added the possibility pass connection properties such as pool size. BREAKING CHANGE: Renamed the connectionUri property to uri.
  • Remote Portal App Registries: Added an option socketTimeoutSec to configure the timeout when trying to access remote apps
  • Storage Service: Can now leverage the new Memory Cache Service to accelerate the access. Can be configured like this:
    "Mashroom Storage Services": {
        "provider": "Mashroom Storage Filestore Provider",
        "memoryCache": {
            "enabled": true,
            "ttlSec": 120,
            "invalidateOnUpdate": true,
            "collections": {
                "mashroom-portal-pages": {
                   "ttlSec": 300
                }
            }
        }
    }
    
  • Added a Redis provider plugin for the Memory Cache
  • Added a general purpose Memory Cache Service with a built-in provider implementation based on node-cache
  • Login web-app: All query parameters in the redirectUrl are now preserved after login
  • Core: Added a new property serverInfo.devMode to the plugin context that can be used to determine if some packages are in development mode
  • Added a wrapper security provider that adds support for Basic authentication to any other security provider that implements login() properly. Useful for end-2-end or load tests and if you want to access some API from an external system.
  • Core: Enable Express "view cache" when no plugin package is in devMode, even if NODE_ENV is not production.

1.4.5 (May 5, 2020)

  • Security: A valid response object will now be passed to security providers during a silent login (when canAuthenticateWithoutUserInteraction() is true). It was not possible to set cookies.
  • OIDC Security Provider: Fixed rejectUnauthorized - didn't work as expected

1.4.4 (May 4, 2020)

  • Upgraded libraries with known vulnerabilities
  • Default Login Webapp: Renamed the redirect query parameter to redirectUrl
  • Portal: The logout route accepts now a redirectUrl parameter with the page that should be redirected to after revoking the authentication (default is still the Site's index page)

1.4.3 (May 2, 2020)

  • Portal: Keep query parameters when redirecting to default site
  • OIDC Security Provider: Added a rejectUnauthorized config property for Identity Providers with self-signed certificates
  • Portal: Fixed mapping of email property in the portalAppSetup

1.4.2 (April 25, 2020)

  • Security Provider: Added new method getApiSecurityHeaders(req, targetUri) that allows it to add security headers to backend/API calls. Useful to add extra user context or access tokens to backend requests.
  • Portal: Removed the REST proxy property sendRoles because the concept of permissions should be used in backends as well.
  • Portal: If the REST proxy property sendUserHeaders is true the following headers will be sent additionally with each REST request:
    • X-USER-DISPLAY-NAME
    • X-USER-EMAIL
  • Portal: Fixed mapping Sites to virtual hosts when the frontend base path is /
  • Virtual host path mapper: Added a config property to explicitly set the http headers that should be considered (default is x-forwarded-host) to determine the actual host

1.4.1 (April 20, 2020)

  • Added a virtual host path mapper plugin: Allows it to map internal paths based on virtual hosts and web apps to get the actual "frontend path" to generate absolute links at the same time. Can be used to expose Portal Sites to virtual hosts like so:

    https://www.my-company.com/new-portal -> http://internal-portal-host/portal/web

    For this example configure your reverse proxy to forward calls from https://www.my-company.com/public to http://internal-portal-host/ and additionally configure the new plugin like this:

    "Mashroom VHost Path Mapper Middleware": {
      "hosts": {
        "www.my-company.com": {
          "frontendBasePath": "/new-portal",
            "mapping": {
              "/login": "/login",
              "/": "/portal/web"
            }
         }
      }
    }
    

1.4.0 (April 6, 2020)

  • Portal: The sites work now completely independent (all URLs are relative to <portal_path>/<site_path>). That means in particular you can have both public sites and private (protected) sites at the same time with an ACL configuration like this:
        {
          "/portal/public-site/**": {
            "*": {
              "allow": "any"
            }
        }
        "/portal/**": {
          "*": {
            "allow": {
              "roles": ["Authenticated"]
            }
          }
        }
      }
    
  • Security: Extended the ACL rules:
    • "any" is now a possible value for allow/deny; this matches also anonymous users which is useful for public sub-pages
    • it is now possible to pass an object to allow/deny with a list of roles and ip addresses
      {
       "/portal/**": {
         "*": {
           "allow": {
             "roles": ["Authenticated"],
             "ips": ["10.1.2.*", "168.**"]
           },
           "deny": {
             "ips": ["1.2.3.4"]
           }
         }
       }
      }
      
  • Security: Added a new method canAuthenticateWithoutUserInteraction() to the Security Provider interface that allows it to check if a user could be logged in silently on public pages, which could be desirable
  • Security: Added a new config property to the mashroom-security plugin that allows to forward specific query parameters to the authorization system (e.g. a hint which identity provider to use):
     "Mashroom Security Services": {
        "provider": "Mashroom Security Simple Provider",
        "forwardQueryHintsToProvider": ["kc_idp_hint"]
     }
    
  • Portal: Fixed anonymous access to pages
  • Added OpenID Connect security provider
  • Angular Demo Portal App: Works now with AOP and the Ivy Compiler
  • External MQTT Messaging Provider: Supports now MQTT 5
  • Removed support for Node 8
  • Added MongoDB storage provider
  • Security: The middleware regenerates the session now before and after a login instead of destroying it. Because session.destroy() removes the request.session property completely but some security provider might need a session during authentication.

1.3.2 (February 22, 2020)

  • File Storage: Locking works now also on NFS correctly
  • Removed log statements that could expose passwords

1.3.1 (February 8, 2020)

  • Remote App Registry Kubernetes: Show all Kubernetes services matching the pattern and a proper error message if no portal apps could be found. Remove portal apps after some time if the Kubernetes services disappeared.
  • Remote App Registry: Added plugin config property to hide the Add a new Remote Portal App Endpoint form from the Admin UI
  • Remote App Registry: Moved config properties from the Mashroom Portal Remote App Registry Webapp plugin to the Mashroom Portal Remote App Registry plugin where it belongs (BREAKING CHANGE)

1.3.0 (January 27, 2020)

  • Portal: Fixed broken IE11 support
  • Portal: Admin Toolbar cleanup and small fixes
  • Added support for messaging via AMQP (Advanced Messaging Queuing) protocol, supported by almost all message brokers (RabbitMQ, Qpid, ActiveMQ, Artemis, Azure Service Bus, ...)
  • Added Remote Portal App registry that automatically scans Kubernetes namespaces for apps
  • Tabify App: The tab buttons have now a new attribute (data-app-ref) that contains the id of the corresponding app wrapper div. This is useful for end-2-end tests to determine if an app is visible.
  • Sandbox App: Fixed loading of portal apps with bootstrap methods that don't return anything
  • Core: Made it possible to use environment variables in server and plugin configuration. If the config value is a valid template string it gets evaluated and the environment variables are accessible through the env object. Example:
     {
         "name": "${env.USER}'s Mashroom Server",
         "port": 5050
     }
    
  • Added TypeScript definitions for all API's. Works now similar than with flow:
      // index.ts
      import {MashroomPortalAppPluginBootstrapFunction} from '@mashroom/mashroom-portal/type-definitions';
      const bootstrap: MashroomPortalAppPluginBootstrapFunction = (hostElement, portalAppSetup, portalClientServices) => {
        // ...
      }
    

1.2.3 (January 11, 2020)

  • Core: Added health checks that can for example be used as readiness/liveness probes in Kubernetes (/mashroom/health)
  • Core: Moved Admin UI from /mashroom to /mashroom/admin
  • Svelte based demo Portal App added
  • Sandbox App: Loads now also shared resources properly
  • Portal: When a portal app gets unloaded all its message bus listeners will automatically be unregistered (in case the app does not unregister the listeners properly on onmount)

1.2.2 (December 7, 2019)

  • Forward query parameters to the index page
  • Upgraded some dependencies because of security vulnerabilities

1.2.1 (November 25, 2019)

  • Redis Session Provider: Added cluster support
  • Session Middleware: Log error messages of providers (Redis, MongoDB) properly

1.2.0 (November 15, 2019)

  • Portal: The Angular 8 demo App can now be loaded multiple times on the same page with a different configuration (bootstrap fixed).
  • Portal: Added support for sharing resources between portal apps (e.g. vendor libraries or styles). A shared resource with a given name will only loaded once, even if multiple Portal Apps declare it. A shared resource can be added like this in the plugin definition:
     {
         "name": "Demo Shared DLL App 1",
         "type": "portal-app",
         "bootstrap": "startupDemoSharedDLLApp1",
         "sharedResources": {
             "js": [
                 "demo_shared_dll_910502a6fce2f139eff8.js"
             ]
         }
     }
    
    Check out the demo project here: https://github.com/nonblocking/mashroom-demo-shared-dll
  • Portal: A remote Portal App which is not reachable for a long time is now unregistered instead of complete removed from the list of remote Apps
  • Added MongoDB session provider plugin
  • Added Redis session provider plugin
  • Portal: Show a proper error if a configured Portal App on a page cannot be loaded (instead of showing nothing)

1.1.4 (October 23, 2019)

  • Core: Logger instances created via req.pluginContext.loggerFactory('category') share now the context with all other loggers created from the same request. This can for example be used to output tracing information with each log entry. The following context properties will be added automatically to each request:
    • clientIP
    • browser (e.g. Chrome, Firefox)
    • browserVersion
    • os (e.g. Windows)
    • sessionID (if a session is available)
    • portalAppName (if the request is related to a portal app)
    • portalAppVersion (if the request is related to a portal app) To add additional properties to the logger context use the new logger.addContext() method (e.g. within a middleware). If you want to output context properties with the log entries you could configure the log4js appender like this:
      "console": {
        "type": "console",
        "layout": {
            "type": "pattern",
            "pattern": "%d %p %X{sessionID} %X{browser} %X{browserVersion} %X{username} %X{portalAppName} %X{portalAppVersion} %c - %m"
        }
      }
      
  • HTTP Proxy: White listed Jaeger, OpenZipkin and W3C Trace Context HTTP headers by default
  • HTTP Proxy: Fixed the problem that all requests headers got forwarded to the target, even cookie and other security relevant ones

1.1.3 (October 15, 2019)

  • Tabify App: Allow to update the title for a specific app id. This is useful for dynamic cockpits where you might want to load the same App multiple times in a tabbed area.
  • Portal: Fixed a problem with token highlighting in the add app panel

1.1.2 (September 30, 2019)

  • Added a middleware plugin that introduces Helmet which sets a bunch of protective HTTP headers on each response
  • Upgraded some dependencies because of security vulnerabilities

1.1.1 (September 26, 2019)

  • WebSocket server now sends keep alive messages to prevent reverse proxies and firewalls from closing the connection
  • Portal: MashroomMessageBus.getRemoteUserPrivateTopic() takes now an optional argument username if you want to obtain the private topic of a particular user instead of the "own" (the private topic of the authenticated user)

1.1.0 (September 19, 2019)

  • Portal: Added two new (optional) security related properties to the default config of portal apps:
    • defaultRestrictViewToRoles: Same as the previous defaultRestrictedToRoles but renamed to make its purpose clearer. These roles can be overwritten via Admin App per App instance in the UI.
    • restProxy.restrictToRoles: If this is set only users with one of the given roles can access the rest proxy. In contrast to all other permissions the Administrator role has not automatically access.
  • Added a provider plugin to support MQTT as external messaging system
  • Added a demo portal app to demonstrate remote messaging
  • Portal: Added support for remote messaging. Portal apps can now subscribe to server side topics (prefixed with :remote) and communicate with apps on other pages and browser tabs. If the service side messaging is connected to an external messaging system (e.g. MQTT) it is also possible to subscribe and publish messages to the external system.
  • Added a Service plugin for server-side messaging that comes with a WebSocket interface which allows sending messages across clients (and browser tabs). Furthermore it be connected to an external messaging system (such as MQTT) via provider plugin.
  • Core: Added the possibility to listen on Plugin load and unload events via MashroomPluginService. Useful if you want to cleanup when your plugin unloads or in the rare case where you have to hold a plugin instance and want to get notified about an unload or reload.
  • Added a Service plugin to handle WebSocket connections (mashroom-websocket)
  • Core: web-app Plugins can now additionally have handlers for upgrade requests (WebSocket support) and for unload
  • Core: The Middleware tab in the Admin UI shows now the actual order of the stack (until now the order was just calculated)

1.0.94 (August 28, 2019)

  • Portal: Made it configurable when the Portal will start to warn that the authentication is about to expire
  • Renamed MashroomSecurityProvider.refreshAuthentication() to checkAuthentication()

1.0.93 (August 27, 2019)

  • Portal: Added configuration property to automatically extend the authentication (so it stays valid as long as the browser page is opened)
  • Portal: Removed the "auto-logout" feature, instead the Portal warns now when the authentication is about to expire.
  • Decoupled authentication from session, in particular the authentication expiration. This simplifies the implementation for providers like OAuth2. BREAKING CHANGE: The MashroomSecurityProvider interface has been extended.

1.0.92 (August 12, 2019)

  • Portal: The app filter in Portal Admin Toolbar considers now tags also. And the categories are sorted alphabetically now.
  • Portal: All initial query parameters are now added again after login

1.0.91 (August 9, 2019)

  • Core: Added optional tags (array) property to the plugin definition
  • Bunch of small default theme improvements
  • Common UI library: Highlight input fields with validation errors
  • Portal: Added a Sandbox App to test Portal Apps. It allows it to load any Portal App with a specific configuration and to interact with the App via Message Bus. Can also be used for end-2-end testing with tools such as Selenium.

1.0.90 (July 18, 2019)

First public release