diff --git a/packages/build/src/build/validateConfig.js b/packages/build/src/build/validateConfig.js index 048692f93..676047c8d 100644 --- a/packages/build/src/build/validateConfig.js +++ b/packages/build/src/build/validateConfig.js @@ -39,6 +39,11 @@ async function validateConfig({ components }) { if (type.isNone(components.config.theme)) { components.config.theme = {}; } + if (type.isString(components.config.basePath)) { + if (components.config.basePath[0] !== '/') { + throw Error('Base path must start with "/".'); + } + } validate({ schema: lowdefySchema.definitions.authConfig, data: components.config.auth, diff --git a/packages/build/src/build/validateConfig.test.js b/packages/build/src/build/validateConfig.test.js index e2b8be085..42483c788 100644 --- a/packages/build/src/build/validateConfig.test.js +++ b/packages/build/src/build/validateConfig.test.js @@ -155,3 +155,31 @@ test('validateConfig config error when protected or public are false.', async () 'Public pages can not be set to false.' ); }); + +test('validateConfig config error when basePath does not start with "/".', async () => { + let components = { + config: { + basePath: '/base', + }, + }; + const result = await validateConfig({ components, context }); + expect(result).toEqual({ + config: { + auth: { + pages: { + roles: {}, + }, + }, + basePath: '/base', + theme: {}, + }, + }); + components = { + config: { + basePath: 'base', + }, + }; + await expect(validateConfig({ components, context })).rejects.toThrow( + 'Base path must start with "/".' + ); +}); diff --git a/packages/build/src/lowdefySchema.js b/packages/build/src/lowdefySchema.js index 63aa33541..efc069897 100644 --- a/packages/build/src/lowdefySchema.js +++ b/packages/build/src/lowdefySchema.js @@ -598,6 +598,13 @@ export default { auth: { $ref: '#/definitions/authConfig', }, + basePath: { + type: 'string', + description: 'App base path to apply to all routes. Base path must start with "/".', + errorMessage: { + type: 'App "config.basePath" should be a string.', + }, + }, homePageId: { type: 'string', description: diff --git a/packages/docs/concepts/lowdefy-schema.yaml b/packages/docs/concepts/lowdefy-schema.yaml index cff79d3b9..74818b5ed 100644 --- a/packages/docs/concepts/lowdefy-schema.yaml +++ b/packages/docs/concepts/lowdefy-schema.yaml @@ -42,6 +42,7 @@ _ref: The config object has the following properties: + - `basePath: string`: Set the base path to serve the Lowdefy application from. This will route all pages under `https://example.com//` instead of the default `https://example.com/`. The basePath value must start with "/". - `homePageId: string`: The pageId of the page that should be loaded when a user loads the app without a pageId in the url route. This is the page that is loaded when you navigate to `yourdomain.com`. - `experimental_initPageId: string`: The pageId of the page that should be loaded when app is initialized. User is then redirected to requeted page. You can use onInit/onInitAsync/onEnter/onEnterAsync events to fetch and prepare global variables for other parts of the app. diff --git a/packages/docs/deployment/docker.yaml b/packages/docs/deployment/docker.yaml index 088b5c25a..b56bf82f2 100644 --- a/packages/docs/deployment/docker.yaml +++ b/packages/docs/deployment/docker.yaml @@ -32,7 +32,7 @@ _ref: The Lowdefy server can be configured using the following environment variables: - - `LOWDEFY_SERVER_BASE_PATH`: Set the base path to serve the Lowdefy application from. This will serve the application under `https://example.com/`instead of `https://example.com`, and all pages under `https://example.com//` instead of the default `https://example.com/`. + - `LOWDEFY_BASE_PATH`: Set the base path to serve the Lowdefy application from. This will serve the application under `https://example.com/`instead of `https://example.com`, and all pages under `https://example.com//` instead of the default `https://example.com/`. - `LOWDEFY_SERVER_BUILD_DIRECTORY`: The directory of the built Lowdefy configuration (The output of `lowdefy build`, usually found at `./.lowdefy/build` in your project repository). The default is `./build` (or `/home/node/lowdefy/build`). - `LOWDEFY_SERVER_PUBLIC_DIRECTORY`: The directory of the public assets to be served. The default is `./public` (or `/home/node/lowdefy/public`). - `LOWDEFY_SERVER_PORT`: The port (inside the container) at which to run the server. The default is `3000`. diff --git a/packages/docs/deployment/node-server.yaml b/packages/docs/deployment/node-server.yaml index c958f8205..00ccf64dc 100644 --- a/packages/docs/deployment/node-server.yaml +++ b/packages/docs/deployment/node-server.yaml @@ -105,7 +105,7 @@ _ref: The following environment variables can be specified: - - `LOWDEFY_SERVER_BASE_PATH`: Set the base path to serve the Lowdefy application from. This will serve the application under `https://example.com/`instead of `https://example.com`, and all pages under `https://example.com//` instead of the default `https://example.com/`. + - `LOWDEFY_BASE_PATH`: Set the base path to serve the Lowdefy application from. This will serve the application under `https://example.com/`instead of `https://example.com`, and all pages under `https://example.com//` instead of the default `https://example.com/`. - `LOWDEFY_SERVER_BUILD_DIRECTORY`: The directory of the built Lowdefy configuration (The output of `lowdefy build`, usually found at `./.lowdefy/build` in your project repository). The default is `./.lowdefy/build`. - `LOWDEFY_SERVER_PORT`: The port at which to run the server. The default is `3000`. - `LOWDEFY_SERVER_PUBLIC_DIRECTORY`: The directory of the public assets to be served. The default is `./public`. diff --git a/packages/server-dev/next.config.js b/packages/server-dev/next.config.js index 9f34f921f..82f9b1a43 100644 --- a/packages/server-dev/next.config.js +++ b/packages/server-dev/next.config.js @@ -7,6 +7,7 @@ module.exports = withLess({ modifyVars: lowdefyConfig.theme.lessVariables, }, }, + basePath: process.env.LOWDEFY_BASE_PATH || lowdefyConfig.basePath, // reactStrictMode: true, webpack: (config, { isServer }) => { if (!isServer) { diff --git a/packages/server/next.config.js b/packages/server/next.config.js index bfa151754..3f2aaa47f 100644 --- a/packages/server/next.config.js +++ b/packages/server/next.config.js @@ -1,7 +1,9 @@ const withLess = require('next-with-less'); const lowdefyConfig = require('./build/config.json'); +// TODO: Trance env and args from cli that is required on the server. module.exports = withLess({ + basePath: process.env.LOWDEFY_BASE_PATH || lowdefyConfig.basePath, lessLoaderOptions: { lessOptions: { modifyVars: lowdefyConfig.theme.lessVariables, diff --git a/packages/server/src/components/LowdefyContext.js b/packages/server/src/components/LowdefyContext.js index 9b1402503..a566528e1 100644 --- a/packages/server/src/components/LowdefyContext.js +++ b/packages/server/src/components/LowdefyContext.js @@ -25,6 +25,7 @@ const LowdefyContext = ({ children, lowdefy }) => { lowdefy._internal = { blockComponents, callRequest, + components: {}, document, operators, updaters: {}, diff --git a/packages/server/src/components/Page.js b/packages/server/src/components/Page.js index f8347d76a..9deb754e0 100644 --- a/packages/server/src/components/Page.js +++ b/packages/server/src/components/Page.js @@ -32,10 +32,13 @@ const Page = ({ lowdefy, pageConfig, rootConfig }) => { lowdefy._internal.router = router; lowdefy._internal.link = setupLink(lowdefy); lowdefy._internal.components = createComponents(lowdefy); + + lowdefy.basePath = lowdefy._internal.router.basePath; lowdefy.home = rootConfig.home; lowdefy.lowdefyGlobal = rootConfig.lowdefyGlobal; lowdefy.menus = rootConfig.menus; lowdefy.urlQuery = urlQuery.parse(window.location.search.slice(1)); + return ( {(context, loading) => { diff --git a/packages/server/src/components/block/CategorySwitch.js b/packages/server/src/components/block/CategorySwitch.js index a68bfeb53..a700c62d6 100644 --- a/packages/server/src/components/block/CategorySwitch.js +++ b/packages/server/src/components/block/CategorySwitch.js @@ -64,12 +64,10 @@ const CategorySwitch = ({ block, Blocks, context, lowdefy }) => { setValue: block.setValue, triggerEvent: block.triggerEvent, })} - // TODO: React throws a basePath warning - basePath={lowdefy._internal.basePath} + basePath={lowdefy.basePath} blockId={block.blockId} components={lowdefy._internal.components} events={block.eval.events} - homePageId={lowdefy.home.pageId} key={block.blockId} loading={block.loading} menus={lowdefy.menus} @@ -98,11 +96,10 @@ const CategorySwitch = ({ block, Blocks, context, lowdefy }) => { registerMethod: block.registerMethod, triggerEvent: block.triggerEvent, })} - basePath={lowdefy._internal.basePath} + basePath={lowdefy.basePath} blockId={block.blockId} components={lowdefy._internal.components} events={block.eval.events} - homePageId={lowdefy.home.pageId} key={block.blockId} loading={block.loading} menus={lowdefy.menus} diff --git a/packages/server/src/components/block/Container.js b/packages/server/src/components/block/Container.js index 773761c25..8c8f5e840 100644 --- a/packages/server/src/components/block/Container.js +++ b/packages/server/src/components/block/Container.js @@ -65,12 +65,11 @@ const Container = ({ block, Blocks, Component, context, lowdefy }) => { registerMethod: block.registerMethod, triggerEvent: block.triggerEvent, })} - basePath={lowdefy._internal.basePath} + basePath={lowdefy.basePath} blockId={block.blockId} components={lowdefy._internal.components} content={content} events={block.eval.events} - homePageId={lowdefy.home.pageId} key={block.blockId} loading={block.loading} menus={lowdefy.menus} diff --git a/packages/server/src/components/block/List.js b/packages/server/src/components/block/List.js index db46e5b5a..bd3db80e2 100644 --- a/packages/server/src/components/block/List.js +++ b/packages/server/src/components/block/List.js @@ -72,11 +72,10 @@ const List = ({ block, Blocks, Component, context, lowdefy }) => { triggerEvent: block.triggerEvent, unshiftItem: block.unshiftItem, })} - basePath={lowdefy._internal.basePath} + basePath={lowdefy.basePath} blockId={block.blockId} components={lowdefy._internal.components} events={block.eval.events} - homePageId={lowdefy.home.pageId} key={block.blockId} list={contentList} loading={block.loading} diff --git a/packages/server/src/pages/index.js b/packages/server/src/pages/index.js index 5d34caf7b..18395368e 100644 --- a/packages/server/src/pages/index.js +++ b/packages/server/src/pages/index.js @@ -19,6 +19,7 @@ import { createApiContext, getPageConfig, getRootConfig } from '@lowdefy/api'; import Page from '../components/Page.js'; export async function getServerSideProps() { + // TODO: is this build directory configurable from the cli? const apiContext = await createApiContext({ buildDirectory: './build' }); const rootConfig = await getRootConfig(apiContext); const { home } = rootConfig; diff --git a/packages/utils/block-utils/src/createIcon.js b/packages/utils/block-utils/src/createIcon.js index a85589893..d735f7177 100644 --- a/packages/utils/block-utils/src/createIcon.js +++ b/packages/utils/block-utils/src/createIcon.js @@ -30,7 +30,6 @@ const lowdefyProps = [ 'components', 'content', 'eventLog', - 'homePageId', 'list', 'loading', 'menus', diff --git a/packages/utils/node-utils/src/getConfigFromEnv.js b/packages/utils/node-utils/src/getConfigFromEnv.js index 56014246d..1cee6d3da 100644 --- a/packages/utils/node-utils/src/getConfigFromEnv.js +++ b/packages/utils/node-utils/src/getConfigFromEnv.js @@ -20,7 +20,7 @@ function getConfigFromEnv() { logLevel: process.env.LOWDEFY_SERVER_LOG_LEVEL, publicDirectory: process.env.LOWDEFY_SERVER_PUBLIC_DIRECTORY, port: process.env.LOWDEFY_SERVER_PORT && parseInt(process.env.LOWDEFY_SERVER_PORT), - serverBasePath: process.env.LOWDEFY_SERVER_BASE_PATH, + basePath: process.env.LOWDEFY_BASE_PATH, }; } diff --git a/packages/utils/node-utils/src/getConfigFromEnv.test.js b/packages/utils/node-utils/src/getConfigFromEnv.test.js index 1d2aa439e..4dd41c09e 100644 --- a/packages/utils/node-utils/src/getConfigFromEnv.test.js +++ b/packages/utils/node-utils/src/getConfigFromEnv.test.js @@ -37,7 +37,7 @@ test('Get config from env', () => { buildDirectory: 'build', publicDirectory: 'public', port: 8080, - serverBasePath: 'base', + basePath: 'base', }); });