@open-rap/runtime 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +14 -2
- package/dist/abap/background.d.ts +14 -0
- package/dist/abap/background.js +130 -0
- package/dist/abap/background.js.map +1 -0
- package/dist/abap/bridge.d.ts +62 -0
- package/dist/abap/bridge.js +402 -0
- package/dist/abap/bridge.js.map +1 -0
- package/dist/abap/convert.d.ts +26 -0
- package/dist/abap/convert.js +197 -0
- package/dist/abap/convert.js.map +1 -0
- package/dist/abap/http.d.ts +34 -0
- package/dist/abap/http.js +133 -0
- package/dist/abap/http.js.map +1 -0
- package/dist/abap/mail.d.ts +10 -0
- package/dist/abap/mail.js +83 -0
- package/dist/abap/mail.js.map +1 -0
- package/dist/abap/odata-client.d.ts +33 -0
- package/dist/abap/odata-client.js +576 -0
- package/dist/abap/odata-client.js.map +1 -0
- package/dist/abap/sql.d.ts +92 -0
- package/dist/abap/sql.js +261 -0
- package/dist/abap/sql.js.map +1 -0
- package/dist/abap/utils.d.ts +41 -0
- package/dist/abap/utils.js +976 -0
- package/dist/abap/utils.js.map +1 -0
- package/dist/app.d.ts +53 -0
- package/dist/app.js +169 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/auth.d.ts +68 -0
- package/dist/auth/auth.js +140 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/authorizations.d.ts +39 -0
- package/dist/auth/authorizations.js +103 -0
- package/dist/auth/authorizations.js.map +1 -0
- package/dist/auth/business-users.d.ts +15 -0
- package/dist/auth/business-users.js +36 -0
- package/dist/auth/business-users.js.map +1 -0
- package/dist/auth/dev-oauth.d.ts +38 -0
- package/dist/auth/dev-oauth.js +156 -0
- package/dist/auth/dev-oauth.js.map +1 -0
- package/dist/auth/environment.d.ts +7 -0
- package/dist/auth/environment.js +57 -0
- package/dist/auth/environment.js.map +1 -0
- package/dist/auth/session.d.ts +45 -0
- package/dist/auth/session.js +257 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/user.d.ts +21 -0
- package/dist/auth/user.js +12 -0
- package/dist/auth/user.js.map +1 -0
- package/dist/db/database.d.ts +30 -0
- package/dist/db/database.js +131 -0
- package/dist/db/database.js.map +1 -0
- package/dist/db/seed.d.ts +9 -0
- package/dist/db/seed.js +63 -0
- package/dist/db/seed.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/launchpad/dev.d.ts +14 -0
- package/dist/launchpad/dev.js +48 -0
- package/dist/launchpad/dev.js.map +1 -0
- package/dist/launchpad/proxy.d.ts +30 -0
- package/dist/launchpad/proxy.js +98 -0
- package/dist/launchpad/proxy.js.map +1 -0
- package/dist/launchpad/server.d.ts +77 -0
- package/dist/launchpad/server.js +177 -0
- package/dist/launchpad/server.js.map +1 -0
- package/dist/mcp/server.d.ts +22 -0
- package/dist/mcp/server.js +222 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/odata/access.d.ts +31 -0
- package/dist/odata/access.js +142 -0
- package/dist/odata/access.js.map +1 -0
- package/dist/odata/batch.d.ts +36 -0
- package/dist/odata/batch.js +147 -0
- package/dist/odata/batch.js.map +1 -0
- package/dist/odata/csrf.d.ts +13 -0
- package/dist/odata/csrf.js +27 -0
- package/dist/odata/csrf.js.map +1 -0
- package/dist/odata/custom.d.ts +90 -0
- package/dist/odata/custom.js +373 -0
- package/dist/odata/custom.js.map +1 -0
- package/dist/odata/draft.d.ts +78 -0
- package/dist/odata/draft.js +590 -0
- package/dist/odata/draft.js.map +1 -0
- package/dist/odata/errors.d.ts +27 -0
- package/dist/odata/errors.js +41 -0
- package/dist/odata/errors.js.map +1 -0
- package/dist/odata/handler.d.ts +90 -0
- package/dist/odata/handler.js +312 -0
- package/dist/odata/handler.js.map +1 -0
- package/dist/odata/json.d.ts +23 -0
- package/dist/odata/json.js +85 -0
- package/dist/odata/json.js.map +1 -0
- package/dist/odata/read.d.ts +90 -0
- package/dist/odata/read.js +686 -0
- package/dist/odata/read.js.map +1 -0
- package/dist/odata/service.d.ts +45 -0
- package/dist/odata/service.js +145 -0
- package/dist/odata/service.js.map +1 -0
- package/dist/odata/uri/ast.d.ts +103 -0
- package/dist/odata/uri/ast.js +3 -0
- package/dist/odata/uri/ast.js.map +1 -0
- package/dist/odata/uri/parser.d.ts +26 -0
- package/dist/odata/uri/parser.js +697 -0
- package/dist/odata/uri/parser.js.map +1 -0
- package/dist/odata/write.d.ts +127 -0
- package/dist/odata/write.js +799 -0
- package/dist/odata/write.js.map +1 -0
- package/dist/platform.d.ts +14 -0
- package/dist/platform.js +66 -0
- package/dist/platform.js.map +1 -0
- package/dist/rap/bo.d.ts +76 -0
- package/dist/rap/bo.js +184 -0
- package/dist/rap/bo.js.map +1 -0
- package/dist/rap/kernel.d.ts +7 -0
- package/dist/rap/kernel.js +287 -0
- package/dist/rap/kernel.js.map +1 -0
- package/dist/rap/runtime.d.ts +84 -0
- package/dist/rap/runtime.js +128 -0
- package/dist/rap/runtime.js.map +1 -0
- package/dist/rap/transaction.d.ts +227 -0
- package/dist/rap/transaction.js +1385 -0
- package/dist/rap/transaction.js.map +1 -0
- package/dist/rap/types.d.ts +74 -0
- package/dist/rap/types.js +25 -0
- package/dist/rap/types.js.map +1 -0
- package/dist/server.d.ts +34 -0
- package/dist/server.js +114 -0
- package/dist/server.js.map +1 -0
- package/dist/ui/plugin.d.ts +14 -0
- package/dist/ui/plugin.js +47 -0
- package/dist/ui/plugin.js.map +1 -0
- package/dist/ui/resources.d.ts +22 -0
- package/dist/ui/resources.js +88 -0
- package/dist/ui/resources.js.map +1 -0
- package/dist/ui/variants.d.ts +25 -0
- package/dist/ui/variants.js +68 -0
- package/dist/ui/variants.js.map +1 -0
- package/dist/values.d.ts +43 -0
- package/dist/values.js +367 -0
- package/dist/values.js.map +1 -0
- package/package.json +60 -3
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The development launchpad of `rapt dev` (decision D49): at `/`, a launchpad with the application
|
|
3
|
+
* and the development tools (documentation, $metadata, MCP tools), served by the application
|
|
4
|
+
* itself. Its configuration is `home/launchpad.json` of the development build; `/launchpad/config`
|
|
5
|
+
* gives the application and the documentation app the shell bar of the launchpad.
|
|
6
|
+
*/
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { launchpadView } from "./server.js";
|
|
10
|
+
const TYPES = {
|
|
11
|
+
".html": "text/html; charset=utf-8",
|
|
12
|
+
".js": "application/javascript; charset=utf-8",
|
|
13
|
+
".json": "application/json; charset=utf-8",
|
|
14
|
+
".xml": "application/xml; charset=utf-8",
|
|
15
|
+
".css": "text/css; charset=utf-8",
|
|
16
|
+
".svg": "image/svg+xml",
|
|
17
|
+
};
|
|
18
|
+
/** Serves the files of `dir` at `/<mount>/`. */
|
|
19
|
+
function serveFolder(app, mount, dir) {
|
|
20
|
+
const root = path.resolve(dir);
|
|
21
|
+
app.get(`/${mount}/*`, async (request, reply) => {
|
|
22
|
+
const relative = path.posix.normalize(request.params["*"] || "index.html").replace(/^(\.\.(\/|$))+/, "");
|
|
23
|
+
const file = path.join(root, relative);
|
|
24
|
+
if (!file.startsWith(root)) {
|
|
25
|
+
return reply.status(404).send("Not found");
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
return reply.header("Cache-Control", "no-cache").type(TYPES[path.extname(file)] ?? "application/octet-stream").send(await readFile(file));
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return reply.status(404).type("text/plain").send("Not found");
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
app.get(`/${mount}`, async (_request, reply) => reply.redirect(`/${mount}/index.html`));
|
|
35
|
+
}
|
|
36
|
+
export function registerDevLaunchpad(app, options) {
|
|
37
|
+
serveFolder(app, "home", path.join(options.dir, "home"));
|
|
38
|
+
serveFolder(app, "docs", path.join(options.dir, "docs"));
|
|
39
|
+
app.get("/launchpad/config", async (request, reply) => {
|
|
40
|
+
const user = request.user ?? (await options.authenticator.fromAuthorization(request.headers.authorization).catch(() => undefined));
|
|
41
|
+
if (user === undefined) {
|
|
42
|
+
return reply.status(401).send({ error: "authentication required" });
|
|
43
|
+
}
|
|
44
|
+
const view = launchpadView(options.config, user, undefined);
|
|
45
|
+
return reply.header("Cache-Control", "no-store").send({ ...view, home: "/", logo: "/home/logo.svg" });
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/launchpad/dev.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forwarding of the requests of the launchpad to its applications (decision D37): the launchpad
|
|
3
|
+
* is the only origin the browser sees, as the front-end server of the SAP Fiori launchpad. The UI
|
|
4
|
+
* of an application is mounted at `/apps/<id>/` (the prefix is removed), its services keep their
|
|
5
|
+
* paths. The cookies go with the request: the application reads the session the launchpad opened.
|
|
6
|
+
*/
|
|
7
|
+
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
8
|
+
export interface ForwardOptions {
|
|
9
|
+
/** The mount path of the application (`/apps/travel`): its redirections are rewritten below it. */
|
|
10
|
+
prefix?: string;
|
|
11
|
+
/** Timeout of the request (ms). */
|
|
12
|
+
timeout: number;
|
|
13
|
+
/** Name of the application, for the error message. */
|
|
14
|
+
app: string;
|
|
15
|
+
/** The answer is an OData error when the application cannot be reached (service paths). */
|
|
16
|
+
odata: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A redirection of the application, for the browser of the launchpad: `/ui/index.html` becomes
|
|
20
|
+
* `/apps/<id>/ui/index.html`; a login (`/auth/login?returnTo=/ui/…`) goes to the login of the
|
|
21
|
+
* launchpad, back to the page of the application. Other URLs are kept.
|
|
22
|
+
*/
|
|
23
|
+
export declare function rewriteLocation(location: string, prefix: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* The URL of a path of the application (`/ui/index.html?x=1`): below the URL of the application,
|
|
26
|
+
* whatever the path holds (`/http://other.host/` stays a path: no request to another host).
|
|
27
|
+
*/
|
|
28
|
+
export declare function targetUrl(base: URL, pathAndQuery: string): URL;
|
|
29
|
+
/** Sends the request to `target` (the URL of the application with the path and the query) and its answer back. */
|
|
30
|
+
export declare function forward(request: FastifyRequest, reply: FastifyReply, target: URL, options: ForwardOptions): Promise<FastifyReply>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forwarding of the requests of the launchpad to its applications (decision D37): the launchpad
|
|
3
|
+
* is the only origin the browser sees, as the front-end server of the SAP Fiori launchpad. The UI
|
|
4
|
+
* of an application is mounted at `/apps/<id>/` (the prefix is removed), its services keep their
|
|
5
|
+
* paths. The cookies go with the request: the application reads the session the launchpad opened.
|
|
6
|
+
*/
|
|
7
|
+
/** Headers of one connection (RFC 9110 §7.6.1), not forwarded. */
|
|
8
|
+
const HOP_BY_HOP = new Set(["connection", "keep-alive", "proxy-authenticate", "proxy-authorization", "proxy-connection", "te", "trailer", "transfer-encoding", "upgrade", "host", "content-length"]);
|
|
9
|
+
/**
|
|
10
|
+
* A redirection of the application, for the browser of the launchpad: `/ui/index.html` becomes
|
|
11
|
+
* `/apps/<id>/ui/index.html`; a login (`/auth/login?returnTo=/ui/…`) goes to the login of the
|
|
12
|
+
* launchpad, back to the page of the application. Other URLs are kept.
|
|
13
|
+
*/
|
|
14
|
+
export function rewriteLocation(location, prefix) {
|
|
15
|
+
if (!location.startsWith("/") || location.startsWith("//")) {
|
|
16
|
+
return location;
|
|
17
|
+
}
|
|
18
|
+
if (location === "/auth" || location.startsWith("/auth/") || location.startsWith("/auth?")) {
|
|
19
|
+
const url = new URL(location, "http://launchpad.invalid");
|
|
20
|
+
const returnTo = url.searchParams.get("returnTo");
|
|
21
|
+
if (returnTo !== null && returnTo.startsWith("/") && !returnTo.startsWith("//") && !returnTo.startsWith(`${prefix}/`)) {
|
|
22
|
+
url.searchParams.set("returnTo", `${prefix}${returnTo}`);
|
|
23
|
+
}
|
|
24
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
25
|
+
}
|
|
26
|
+
return `${prefix}${location}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The URL of a path of the application (`/ui/index.html?x=1`): below the URL of the application,
|
|
30
|
+
* whatever the path holds (`/http://other.host/` stays a path: no request to another host).
|
|
31
|
+
*/
|
|
32
|
+
export function targetUrl(base, pathAndQuery) {
|
|
33
|
+
const target = new URL(base.href);
|
|
34
|
+
const question = pathAndQuery.indexOf("?");
|
|
35
|
+
const pathname = question < 0 ? pathAndQuery : pathAndQuery.slice(0, question);
|
|
36
|
+
target.pathname = `${base.pathname.replace(/\/$/, "")}/${pathname.replace(/^\/+/, "")}`;
|
|
37
|
+
target.search = question < 0 ? "" : pathAndQuery.slice(question);
|
|
38
|
+
target.hash = "";
|
|
39
|
+
return target;
|
|
40
|
+
}
|
|
41
|
+
function forwardedHeaders(request, prefix) {
|
|
42
|
+
const headers = new Headers();
|
|
43
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
44
|
+
const lower = name.toLowerCase();
|
|
45
|
+
// the body is sent decoded: no compression negotiated with the application
|
|
46
|
+
if (value === undefined || HOP_BY_HOP.has(lower) || lower === "accept-encoding") {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
for (const item of Array.isArray(value) ? value : [value]) {
|
|
50
|
+
headers.append(lower, item);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const forwardedFor = request.headers["x-forwarded-for"];
|
|
54
|
+
headers.set("x-forwarded-for", forwardedFor === undefined ? request.ip : `${String(forwardedFor)}, ${request.ip}`);
|
|
55
|
+
headers.set("x-forwarded-host", request.headers.host ?? "");
|
|
56
|
+
headers.set("x-forwarded-proto", request.protocol);
|
|
57
|
+
if (prefix !== undefined) {
|
|
58
|
+
headers.set("x-forwarded-prefix", prefix);
|
|
59
|
+
}
|
|
60
|
+
return headers;
|
|
61
|
+
}
|
|
62
|
+
/** Sends the request to `target` (the URL of the application with the path and the query) and its answer back. */
|
|
63
|
+
export async function forward(request, reply, target, options) {
|
|
64
|
+
const init = {
|
|
65
|
+
method: request.method,
|
|
66
|
+
headers: forwardedHeaders(request, options.prefix),
|
|
67
|
+
redirect: "manual",
|
|
68
|
+
signal: AbortSignal.timeout(options.timeout),
|
|
69
|
+
};
|
|
70
|
+
if (request.method !== "GET" && request.method !== "HEAD" && Buffer.isBuffer(request.body)) {
|
|
71
|
+
init.body = request.body;
|
|
72
|
+
}
|
|
73
|
+
let response;
|
|
74
|
+
try {
|
|
75
|
+
response = await fetch(target, init);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
request.log.warn({ err: error, app: options.app }, "application not reachable");
|
|
79
|
+
const message = `The application ${options.app} is not reachable`;
|
|
80
|
+
reply.status(502);
|
|
81
|
+
return options.odata ? reply.type("application/json").send({ error: { code: "BAD_GATEWAY", message } }) : reply.type("text/plain").send(message);
|
|
82
|
+
}
|
|
83
|
+
reply.status(response.status);
|
|
84
|
+
response.headers.forEach((value, name) => {
|
|
85
|
+
// fetch has decoded the body; the cookies are set one by one below
|
|
86
|
+
if (HOP_BY_HOP.has(name) || name === "content-encoding" || name === "set-cookie") {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
reply.header(name, name === "location" && options.prefix !== undefined ? rewriteLocation(value, options.prefix) : value);
|
|
90
|
+
});
|
|
91
|
+
const cookies = response.headers.getSetCookie();
|
|
92
|
+
if (cookies.length > 0) {
|
|
93
|
+
reply.header("set-cookie", cookies);
|
|
94
|
+
}
|
|
95
|
+
const body = request.method === "HEAD" ? Buffer.alloc(0) : Buffer.from(await response.arrayBuffer());
|
|
96
|
+
return reply.send(body);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/launchpad/proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The launchpad of applications (decision D37), the counterpart of the SAP Fiori launchpad built
|
|
3
|
+
* with OpenUI5: one origin for the browser, one login, and tiles assigned to roles.
|
|
4
|
+
*
|
|
5
|
+
* - `/` and `/ui/`: the launchpad (ShellBar, groups of tiles, dynamic tiles with a `$count`);
|
|
6
|
+
* - `/launchpad/config`: the tiles the user sees (roles of the identity provider), the user;
|
|
7
|
+
* - `/go/<SemanticObject>-<action>`: navigation by intent to the application of a tile;
|
|
8
|
+
* - `/apps/<id>/…`: the UI of an application, forwarded to it; its services keep their paths;
|
|
9
|
+
* - `/auth/…`: the login (OIDC, authorization code with PKCE) whose session the applications read
|
|
10
|
+
* (same `SESSION_SECRET`): a single sign-on, as the session of the front-end server of SAP.
|
|
11
|
+
*
|
|
12
|
+
* Configuration by the environment, as an application (server.ts): `PORT` (8090), `HOST`,
|
|
13
|
+
* `PUBLIC_URL`, `LOG_LEVEL`, `AUTH_MODE`, `DEV_USER`, `DEV_ROLES`, `OIDC_*`, `SESSION_SECRET`, and
|
|
14
|
+
* `APP_<ID>_URL` for the internal URL of each application.
|
|
15
|
+
*/
|
|
16
|
+
import { type FastifyInstance, type FastifyServerOptions } from "fastify";
|
|
17
|
+
import { type LaunchpadModel, type LaunchpadTile } from "@open-rap/model";
|
|
18
|
+
import { type AuthOptions } from "../auth/auth.js";
|
|
19
|
+
import type { User } from "../auth/user.js";
|
|
20
|
+
import { type UiOptions } from "../ui/plugin.js";
|
|
21
|
+
export interface LaunchpadOptions {
|
|
22
|
+
config: LaunchpadModel;
|
|
23
|
+
auth?: AuthOptions;
|
|
24
|
+
/** The `ui/` folder of the generated launchpad and the OpenUI5 resources. */
|
|
25
|
+
ui?: UiOptions;
|
|
26
|
+
/** Internal URLs of the applications, replacing those of the configuration (`APP_<ID>_URL`). */
|
|
27
|
+
appUrls?: Record<string, string>;
|
|
28
|
+
logger?: FastifyServerOptions["logger"];
|
|
29
|
+
/** Timeout of a request forwarded to an application (ms, default 120 s). */
|
|
30
|
+
timeout?: number;
|
|
31
|
+
}
|
|
32
|
+
/** The tiles of the launchpad for a user, as `/launchpad/config` answers them. */
|
|
33
|
+
export interface LaunchpadView {
|
|
34
|
+
title: string;
|
|
35
|
+
/** The home page and the mark of the launchpad, for the shell bar of the applications. */
|
|
36
|
+
home: string;
|
|
37
|
+
logo: string;
|
|
38
|
+
user: {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
initials: string;
|
|
42
|
+
};
|
|
43
|
+
/** The logout of the launchpad (OIDC sessions). */
|
|
44
|
+
logout?: string;
|
|
45
|
+
groups: ViewGroup[];
|
|
46
|
+
/** The spaces and pages of the user (D48): one space of one page when launchpad.json has groups only. */
|
|
47
|
+
spaces: Array<{
|
|
48
|
+
id: string;
|
|
49
|
+
title: string;
|
|
50
|
+
pages: Array<{
|
|
51
|
+
id: string;
|
|
52
|
+
title: string;
|
|
53
|
+
groups: ViewGroup[];
|
|
54
|
+
}>;
|
|
55
|
+
}>;
|
|
56
|
+
}
|
|
57
|
+
type ViewGroup = {
|
|
58
|
+
title: string;
|
|
59
|
+
tiles: Array<Omit<LaunchpadTile, "roles" | "app">>;
|
|
60
|
+
};
|
|
61
|
+
/** The tiles a user sees; the groups without such a tile are left out. */
|
|
62
|
+
export declare function launchpadView(config: LaunchpadModel, user: User, logout: string | undefined): LaunchpadView;
|
|
63
|
+
export declare function createLaunchpad(options: LaunchpadOptions): Promise<FastifyInstance>;
|
|
64
|
+
export interface StartLaunchpadOptions {
|
|
65
|
+
/** The generated launchpad folder (launchpad/config.json, ui/). */
|
|
66
|
+
dir: string;
|
|
67
|
+
env?: Record<string, string | undefined>;
|
|
68
|
+
listen?: boolean;
|
|
69
|
+
}
|
|
70
|
+
export interface RunningLaunchpad {
|
|
71
|
+
app: FastifyInstance;
|
|
72
|
+
url: string;
|
|
73
|
+
close(): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
/** Start-up of a generated launchpad (`node server.mjs`), configured by the environment. */
|
|
76
|
+
export declare function startLaunchpad(options: StartLaunchpadOptions): Promise<RunningLaunchpad>;
|
|
77
|
+
export {};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The launchpad of applications (decision D37), the counterpart of the SAP Fiori launchpad built
|
|
3
|
+
* with OpenUI5: one origin for the browser, one login, and tiles assigned to roles.
|
|
4
|
+
*
|
|
5
|
+
* - `/` and `/ui/`: the launchpad (ShellBar, groups of tiles, dynamic tiles with a `$count`);
|
|
6
|
+
* - `/launchpad/config`: the tiles the user sees (roles of the identity provider), the user;
|
|
7
|
+
* - `/go/<SemanticObject>-<action>`: navigation by intent to the application of a tile;
|
|
8
|
+
* - `/apps/<id>/…`: the UI of an application, forwarded to it; its services keep their paths;
|
|
9
|
+
* - `/auth/…`: the login (OIDC, authorization code with PKCE) whose session the applications read
|
|
10
|
+
* (same `SESSION_SECRET`): a single sign-on, as the session of the front-end server of SAP.
|
|
11
|
+
*
|
|
12
|
+
* Configuration by the environment, as an application (server.ts): `PORT` (8090), `HOST`,
|
|
13
|
+
* `PUBLIC_URL`, `LOG_LEVEL`, `AUTH_MODE`, `DEV_USER`, `DEV_ROLES`, `OIDC_*`, `SESSION_SECRET`, and
|
|
14
|
+
* `APP_<ID>_URL` for the internal URL of each application.
|
|
15
|
+
*/
|
|
16
|
+
import { platformEnvironment } from "../platform.js";
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import Fastify, {} from "fastify";
|
|
20
|
+
import { appUrlVariable, tileVisible } from "@open-rap/model";
|
|
21
|
+
import { AuthenticationError, Authenticator } from "../auth/auth.js";
|
|
22
|
+
import { authFromEnvironment } from "../auth/environment.js";
|
|
23
|
+
import { OidcSessions } from "../auth/session.js";
|
|
24
|
+
import { registerUi } from "../ui/plugin.js";
|
|
25
|
+
import { forward, targetUrl } from "./proxy.js";
|
|
26
|
+
function initials(name) {
|
|
27
|
+
const words = name.split(/[\s._@-]+/).filter((w) => w !== "");
|
|
28
|
+
const letters = words.length >= 2 ? `${words[0]?.[0] ?? ""}${words[words.length - 1]?.[0] ?? ""}` : name.slice(0, 2);
|
|
29
|
+
return letters.toUpperCase();
|
|
30
|
+
}
|
|
31
|
+
/** The tiles a user sees; the groups without such a tile are left out. */
|
|
32
|
+
export function launchpadView(config, user, logout) {
|
|
33
|
+
const name = user.name ?? user.id;
|
|
34
|
+
const view = { title: config.title, home: "/", logo: "/ui/logo.svg", user: { id: user.id, name, initials: initials(name) }, groups: [], spaces: [] };
|
|
35
|
+
if (logout !== undefined) {
|
|
36
|
+
view.logout = logout;
|
|
37
|
+
}
|
|
38
|
+
const visibleGroups = (groups) => groups
|
|
39
|
+
.map((group) => ({ title: group.title, tiles: group.tiles.filter((t) => tileVisible(t, user.roles)).map(({ roles: _roles, app: _app, ...tile }) => tile) }))
|
|
40
|
+
.filter((group) => group.tiles.length > 0);
|
|
41
|
+
const spaces = config.spaces ?? [{ id: "home", title: config.title, roles: [], pages: [{ id: "home", title: config.title, sections: config.groups }] }];
|
|
42
|
+
for (const space of spaces) {
|
|
43
|
+
if (!tileVisible(space, user.roles)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const pages = space.pages.map((page) => ({ id: page.id, title: page.title, groups: visibleGroups(page.sections) })).filter((page) => page.groups.length > 0);
|
|
47
|
+
if (pages.length > 0) {
|
|
48
|
+
view.spaces.push({ id: space.id, title: space.title, pages });
|
|
49
|
+
view.groups.push(...pages.flatMap((page) => page.groups));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return view;
|
|
53
|
+
}
|
|
54
|
+
const servicePathOf = (pathname, apps) => apps.find(({ root }) => pathname === root || pathname.startsWith(`${root}/`));
|
|
55
|
+
export async function createLaunchpad(options) {
|
|
56
|
+
const { config } = options;
|
|
57
|
+
const app = Fastify({ logger: options.logger ?? false, bodyLimit: 20 * 1024 * 1024, return503OnClosing: true });
|
|
58
|
+
const auth = options.auth ?? { mode: "none" };
|
|
59
|
+
const authenticator = new Authenticator(auth);
|
|
60
|
+
if (auth.mode === "oidc" && auth.session === undefined) {
|
|
61
|
+
throw new Error("the launchpad needs browser sessions: set OIDC_CLIENT_ID and SESSION_SECRET");
|
|
62
|
+
}
|
|
63
|
+
const sessions = auth.mode === "oidc" && auth.session !== undefined ? new OidcSessions(authenticator, { ...auth, session: auth.session }) : undefined;
|
|
64
|
+
const timeout = options.timeout ?? 120_000;
|
|
65
|
+
const urls = new Map(config.apps.map((a) => [a.id, new URL(options.appUrls?.[a.id] ?? a.url)]));
|
|
66
|
+
const services = config.apps.flatMap((a) => a.servicePaths.map((p) => ({ app: a, root: p.replace(/\/$/, "") })));
|
|
67
|
+
// bodies are forwarded as they came
|
|
68
|
+
app.removeAllContentTypeParsers();
|
|
69
|
+
app.addContentTypeParser("*", { parseAs: "buffer" }, (_request, body, done) => done(null, body));
|
|
70
|
+
app.decorateRequest("user", undefined);
|
|
71
|
+
app.addHook("onRequest", async (request, reply) => {
|
|
72
|
+
const pathname = request.url.split("?")[0] ?? "/";
|
|
73
|
+
if (pathname === "/health" || pathname === "/auth" || pathname.startsWith("/auth/") || pathname.startsWith("/resources/")) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const service = servicePathOf(pathname, services) !== undefined;
|
|
77
|
+
const api = service || pathname.startsWith("/launchpad/");
|
|
78
|
+
try {
|
|
79
|
+
// a session of the launchpad, else (API clients) a bearer token; the development user
|
|
80
|
+
const user = (request.headers.authorization === undefined ? sessions?.user(request) : undefined) ?? (sessions === undefined || request.headers.authorization !== undefined ? await authenticator.fromAuthorization(request.headers.authorization) : undefined);
|
|
81
|
+
if (user !== undefined) {
|
|
82
|
+
request.user = user;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!api && sessions !== undefined && (request.method === "GET" || request.method === "HEAD")) {
|
|
86
|
+
return reply.redirect(sessions.loginUrl(pathname === "/" ? "/ui/index.html" : request.url));
|
|
87
|
+
}
|
|
88
|
+
throw new AuthenticationError("authentication required");
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const status = error instanceof AuthenticationError ? error.status : 401;
|
|
92
|
+
const message = error.message;
|
|
93
|
+
if (status === 401) {
|
|
94
|
+
reply.header("WWW-Authenticate", 'Bearer realm="open-rap-runtime"');
|
|
95
|
+
}
|
|
96
|
+
return api ? reply.status(status).type("application/json").send({ error: { code: status === 403 ? "FORBIDDEN" : "UNAUTHORIZED", message } }) : reply.status(status).type("text/plain").send(message);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const logout = sessions === undefined ? undefined : "/auth/logout";
|
|
100
|
+
app.get("/launchpad/config", async (request, reply) => reply.header("Cache-Control", "no-store").send(launchpadView(config, request.user, logout)));
|
|
101
|
+
// navigation by intent: the first tile of the user with this semantic object and action
|
|
102
|
+
app.get("/go/:intent", async (request, reply) => {
|
|
103
|
+
const intent = request.params.intent;
|
|
104
|
+
const user = request.user;
|
|
105
|
+
const tile = config.groups.flatMap((g) => g.tiles).find((t) => t.intent === intent && tileVisible(t, user.roles));
|
|
106
|
+
if (tile === undefined) {
|
|
107
|
+
return reply.status(404).type("text/plain").send(`No application of the launchpad for the intent ${intent}`);
|
|
108
|
+
}
|
|
109
|
+
const question = request.url.indexOf("?");
|
|
110
|
+
const query = question < 0 ? "" : request.url.slice(question + 1);
|
|
111
|
+
const target = query === "" ? tile.target : `${tile.target}${tile.target.includes("?") ? "&" : "?"}${query}`;
|
|
112
|
+
return reply.redirect(target);
|
|
113
|
+
});
|
|
114
|
+
const mounted = async (request, reply) => {
|
|
115
|
+
const { id } = request.params;
|
|
116
|
+
const base = urls.get(id);
|
|
117
|
+
if (base === undefined) {
|
|
118
|
+
return reply.status(404).type("text/plain").send(`No application ${id} in the launchpad`);
|
|
119
|
+
}
|
|
120
|
+
const prefix = `/apps/${id}`;
|
|
121
|
+
const rest = request.url.slice(prefix.length);
|
|
122
|
+
if (rest === "" || rest.startsWith("?")) {
|
|
123
|
+
return reply.redirect(`${prefix}/${rest}`);
|
|
124
|
+
}
|
|
125
|
+
return forward(request, reply, targetUrl(base, rest), { prefix, timeout, app: id, odata: false });
|
|
126
|
+
};
|
|
127
|
+
const methods = ["GET", "HEAD", "POST", "PATCH", "PUT", "DELETE"];
|
|
128
|
+
app.route({ method: methods, url: "/apps/:id", handler: mounted });
|
|
129
|
+
app.route({ method: methods, url: "/apps/:id/*", handler: mounted });
|
|
130
|
+
// the services of the applications, at their own paths (the URIs of their UIs)
|
|
131
|
+
for (const { app: target, root } of services) {
|
|
132
|
+
const handler = async (request, reply) => {
|
|
133
|
+
const base = urls.get(target.id);
|
|
134
|
+
return forward(request, reply, targetUrl(base, request.url), { timeout, app: target.id, odata: true });
|
|
135
|
+
};
|
|
136
|
+
app.route({ method: methods, url: root, handler });
|
|
137
|
+
app.route({ method: methods, url: `${root}/*`, handler });
|
|
138
|
+
}
|
|
139
|
+
app.get("/health", async () => ({ status: "ok" }));
|
|
140
|
+
sessions?.register(app);
|
|
141
|
+
if (options.ui !== undefined) {
|
|
142
|
+
await registerUi(app, options.ui);
|
|
143
|
+
}
|
|
144
|
+
return app;
|
|
145
|
+
}
|
|
146
|
+
/** Start-up of a generated launchpad (`node server.mjs`), configured by the environment. */
|
|
147
|
+
export async function startLaunchpad(options) {
|
|
148
|
+
const env = platformEnvironment(options.env ?? process.env);
|
|
149
|
+
const dir = path.resolve(options.dir);
|
|
150
|
+
const port = Number(env["PORT"] ?? 8090);
|
|
151
|
+
const host = env["HOST"] ?? "0.0.0.0";
|
|
152
|
+
const publicUrl = env["PUBLIC_URL"] ?? `http://localhost:${port}/`;
|
|
153
|
+
const config = JSON.parse(readFileSync(path.join(dir, "launchpad", "config.json"), "utf8"));
|
|
154
|
+
const appUrls = {};
|
|
155
|
+
for (const a of config.apps) {
|
|
156
|
+
const url = env[appUrlVariable(a.id)];
|
|
157
|
+
if (url !== undefined && url !== "") {
|
|
158
|
+
appUrls[a.id] = url;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const app = await createLaunchpad({
|
|
162
|
+
config,
|
|
163
|
+
auth: authFromEnvironment(env, publicUrl, config.claims),
|
|
164
|
+
appUrls,
|
|
165
|
+
logger: { level: env["LOG_LEVEL"] ?? "info" },
|
|
166
|
+
ui: { dir: path.join(dir, "ui"), resourcesFrom: dir },
|
|
167
|
+
});
|
|
168
|
+
let url = publicUrl;
|
|
169
|
+
if (options.listen !== false) {
|
|
170
|
+
url = (await app.listen({ port, host })).replace("0.0.0.0", "localhost");
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
await app.ready();
|
|
174
|
+
}
|
|
175
|
+
return { app, url, close: () => app.close() };
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/launchpad/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The MCP server of the application (decision D47): the Model Context Protocol over HTTP
|
|
3
|
+
* ("Streamable HTTP" transport, JSON responses) at `/mcp`. Each tool is an OData request run by the
|
|
4
|
+
* service itself, as the user of the MCP request: authorizations, access controls, feature
|
|
5
|
+
* control, validations and messages are those of the OData service.
|
|
6
|
+
*
|
|
7
|
+
* Authentication: a bearer token (OIDC), or the development user with AUTH_MODE=none. Browser
|
|
8
|
+
* sessions (cookies) are not accepted: a web page could otherwise call the tools in the user's name.
|
|
9
|
+
*/
|
|
10
|
+
import type { FastifyInstance } from "fastify";
|
|
11
|
+
import type { McpToolModel, McpToolsModel } from "@open-rap/model";
|
|
12
|
+
import { type Authenticator } from "../auth/auth.js";
|
|
13
|
+
import type { ODataHandler, ODataRequest } from "../odata/handler.js";
|
|
14
|
+
export declare const MCP_PATH = "/mcp";
|
|
15
|
+
export interface McpOptions {
|
|
16
|
+
tools: McpToolsModel;
|
|
17
|
+
handler: ODataHandler;
|
|
18
|
+
authenticator: Authenticator;
|
|
19
|
+
}
|
|
20
|
+
/** The OData request of a tool call. */
|
|
21
|
+
export declare function odataRequest(tool: McpToolModel, args: Record<string, unknown>): ODataRequest;
|
|
22
|
+
export declare function registerMcp(app: FastifyInstance, options: McpOptions): void;
|