Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui5-middleware-ui5): serve apps via their namespace #857

Merged
merged 1 commit into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ packages
├── ui5-middleware-serveframework // middleware extension: serve resources of a locally built framework
├── ui5-middleware-servestatic // middleware extension: serve static resources
├── ui5-middleware-simpleproxy // middleware extension: simple express proxy
├── ui5-middleware-ui5 // middleware extension: enable UI5 application dependencies for UI5 dev server
├── ui5-middleware-webjars // middleware extension: deliver content from JAR files
├── ui5-middleware-websocket // middleware extension: enable web sockets for the UI5 Tooling
├── ui5-task-cachebuster // task extension: enables cachebusting for standalone applications
Expand Down Expand Up @@ -202,6 +203,7 @@ Available middlewares in this project:
| [ui5-middleware-servestatic](packages/ui5-middleware-servestatic/README.md) | serve static resources | [![npm version](https://badge.fury.io/js/ui5-middleware-servestatic.svg)](https://badge.fury.io/js/ui5-middleware-servestatic) |
| [ui5-middleware-onelogin](packages/ui5-middleware-onelogin/README.md) | enable a generic login support | [![npm version](https://badge.fury.io/js/ui5-middleware-onelogin.svg)](https://badge.fury.io/js/ui5-middleware-onelogin) |
| [ui5-middleware-simpleproxy](packages/ui5-middleware-simpleproxy/README.md) | simple express proxy | [![npm version](https://badge.fury.io/js/ui5-middleware-simpleproxy.svg)](https://badge.fury.io/js/ui5-middleware-simpleproxy) |
| [ui5-middleware-ui5](packages/ui5-middleware-ui5/README.md) | enable UI5 application dependencies | [![npm version](https://badge.fury.io/js/ui5-middleware-ui5.svg)](https://badge.fury.io/js/ui5-middleware-ui5) |
| [ui5-middleware-webjars](packages/ui5-middleware-webjars/README.md) | deliver content from JAR files | [![npm version](https://badge.fury.io/js/ui5-middleware-webjars.svg)](https://badge.fury.io/js/ui5-middleware-webjars) |
| [ui5-middleware-websocket](packages/ui5-middleware-websocket/README.md) | enable web sockets for UI5 tooling | [![npm version](https://badge.fury.io/js/ui5-middleware-websocket.svg)](https://badge.fury.io/js/ui5-middleware-websocket) |

Expand Down
3 changes: 3 additions & 0 deletions packages/ui5-middleware-ui5/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ The plugin accepts configuration to control the mount path and selected configur
- debug: `boolean`
enable detailed logging

- serveFromNamespace: `boolean` (defaults to `true`)
serves the UI5 application from the `/resources/%namespace%` so that the UI5 application can be resolved without any additional `resourceroots` mapping; when disabled (setting option to `false`) the UI5 application will be served from it's `mountPath` (typically the UI5 app `name` or as fallback the `moduleId`); a custom `mountPath` configuration (see below) disables this configuration option!

- modules: `Object`
modules configuration (key = moduleId, values = object) - details in the following sections about the module configuration

Expand Down
39 changes: 19 additions & 20 deletions packages/ui5-middleware-ui5/lib/createPatchedRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,22 @@ module.exports = async function createPatchedRouter() {
// rewite the path to simulate requests on the root level
req.originalUrl = req.url;
req.baseUrl = "/";
// only accept requests for html-related content (via accept header)
const accept = req.headers["accept"]?.indexOf("html") !== -1;
// disable the compression when livereload is used
// for loading html-related content (via accept header)
// otherwise we run into compression issue with CDS livereload
const accept = req.headers["accept"]?.indexOf("html") !== -1;
if (accept && res._livereload) {
req.headers["accept-encoding"] = "identity";
}
// override UI5 server directory listing if:
// 1.) only if it ends with a slash
// 2.) not forwarded to a welcome page
if (req.url?.endsWith("/") && req.url === (req?.["ui5-middleware-index"]?.url || req.url)) {
// override UI5 server directory listing if not forwarded to a welcome page
if (accept && req.url === (req["ui5-middleware-index"]?.url || req.url)) {
// determine context path (approuter contains x-forwarded-path header)
let contextPath = baseUrl;
if (req.headers["x-forwarded-path"]) {
if (req.headers["x-forwarded-path"]?.endsWith(url)) {
// determine the context path by removing the subpath from the forwarded path
contextPath = req.headers["x-forwarded-path"].slice(0, -1 * url.length);
} else if (req["ui5-patched-router"].originalUrl) {
} else if (req["ui5-patched-router"].originalUrl?.endsWith(url)) {
// determine the context path by removing the subpath from the originalUrl
contextPath = req["ui5-patched-router"].originalUrl.slice(0, -1 * url.length);
}
Expand All @@ -51,21 +50,21 @@ module.exports = async function createPatchedRouter() {
},
(doc) => {
const title = doc.getElementsByTagName("title")?.[0];
if (title) {
if (title && title.innerHTML.startsWith(`Index of ${req.url}`)) {
title.innerHTML = `Index of ${contextPath}/`;
const files = doc.getElementById("files");
const filesas = files?.getElementsByTagName("a");
filesas?.forEach((a) => {
a.setAttribute("href", `${contextPath}${a.getAttribute("href")}`);
});
const h1 = doc.getElementsByTagName("h1")?.[0];
const h1as = h1?.getElementsByTagName("a");
h1as?.forEach((a) => {
const path = a.getAttribute("href") === "/" ? "/" : a.getAttribute("href") + "/";
a.setAttribute("href", `${contextPath}${path}`);
});
h1?.insertAdjacentHTML("afterbegin", `<a href="/">🏡</a> / `);
}
const files = doc.getElementById("files");
const filesas = files?.getElementsByTagName("a");
filesas?.forEach((a) => {
a.setAttribute("href", `${contextPath}${a.getAttribute("href")}`);
});
const h1 = doc.getElementsByTagName("h1")?.[0];
const h1as = h1?.getElementsByTagName("a");
h1as?.forEach((a) => {
const path = a.getAttribute("href") === "/" ? "/" : a.getAttribute("href") + "/";
a.setAttribute("href", `${contextPath}${path}`);
});
h1?.insertAdjacentHTML("afterbegin", `<a href="/">🏡</a> / `);
}
);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/ui5-middleware-ui5/lib/findUI5Modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,12 @@ module.exports = async function findUI5Modules({ cwd, config, log }) {
// modules:
// %moduleId%:
// mountPath: "/app"
const name = ui5Config?.metadata?.name || moduleId;
let mountPath =
config?.modules?.[moduleId]?.mountPath ||
ui5Config?.customConfiguration?.["ui5-middleware-ui5"]?.mountPath ||
ui5Config?.customConfiguration?.mountPath ||
ui5Config?.metadata?.name;
(config?.serveFromNamespace ? `/resources/${name.replace(/\./g, "/")}` : name);
if (!/^\//.test(mountPath)) {
mountPath = `/${mountPath}`; // always start with /
}
Expand Down
12 changes: 11 additions & 1 deletion packages/ui5-middleware-ui5/lib/ui5.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,23 @@ const createPatchedRouter = require("./createPatchedRouter");
* @returns {Function} Middleware function to use
*/
module.exports = async ({ log, options }) => {
// determine the effective configuration
const config = Object.assign(
{},
{
debug: false,
serveFromNamespace: true,
modules: {},
},
options?.configuration
);

// do not run the middleware in the context of the cds-plugin-ui5
// to avoid cyclic requests between the express middlewares
if (process.env["cds-plugin-ui5"]) {
log.info("Skip middleware as the UI5 application has been started embedded in the CDS server!");
} else {
const cwd = process.cwd();
const config = options.configuration;
return hook(
"ui5-middleware-ui5",
async ({ app }) => {
Expand Down
7 changes: 4 additions & 3 deletions showcases/ui5-bookshop-viewer/ui5.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ server:
- name: ui5-middleware-ui5
afterMiddleware: compression
configuration:
modules:
ui5-app-simple:
mountPath: "/app"
#serveFromNamespace: false
#modules:
# ui5-app-simple: # by default available via: http://localhost:8080/resources/ui5/ecosystem/demo/simpleapp/
# mountPath: "/bookshop"
# Last middleware (for the same afterMiddleware) comes first!
- name: ui5-middleware-livereload
afterMiddleware: compression
Expand Down