@sap-ux/backend-proxy-middleware-cf 0.0.98 → 0.1.0
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/README.md +62 -174
- package/dist/approuter/approuter.d.ts +27 -0
- package/dist/approuter/approuter.js +38 -0
- package/dist/approuter/extensions.d.ts +29 -0
- package/dist/approuter/extensions.js +90 -0
- package/dist/config/config.d.ts +10 -0
- package/dist/config/config.js +35 -0
- package/dist/config/constants.d.ts +10 -0
- package/dist/config/constants.js +13 -0
- package/dist/config/env.d.ts +32 -0
- package/dist/config/env.js +129 -0
- package/dist/index.d.ts +1 -1
- package/dist/middleware.js +88 -21
- package/dist/platform/bas.d.ts +19 -0
- package/dist/platform/bas.js +39 -0
- package/dist/platform/xssecurity.d.ts +10 -0
- package/dist/platform/xssecurity.js +51 -0
- package/dist/proxy/proxy.d.ts +22 -0
- package/dist/proxy/proxy.js +99 -0
- package/dist/proxy/routes.d.ts +18 -0
- package/dist/proxy/routes.js +103 -0
- package/dist/proxy/utils.d.ts +65 -0
- package/dist/proxy/utils.js +114 -0
- package/dist/types.d.ts +146 -49
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +25 -0
- package/package.json +12 -12
- package/dist/proxy.d.ts +0 -42
- package/dist/proxy.js +0 -91
- package/dist/token/factory.d.ts +0 -41
- package/dist/token/factory.js +0 -97
- package/dist/token/index.d.ts +0 -3
- package/dist/token/index.js +0 -8
- package/dist/token/provider.d.ts +0 -42
- package/dist/token/provider.js +0 -109
- package/dist/validation.d.ts +0 -11
- package/dist/validation.js +0 -30
package/README.md
CHANGED
|
@@ -2,70 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
# [`@sap-ux/backend-proxy-middleware-cf`](https://github.com/SAP/open-ux-tools/tree/main/packages/backend-proxy-middleware-cf)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
UI5 server middleware that uses `@sap/approuter` to make destinations configured in SAP Business Technology Platform (BTP) available for local development. Requests to destination routes are proxied to a local approuter instance via `http-proxy-middleware`.
|
|
6
6
|
|
|
7
7
|
> **⚠️ Experimental**: This middleware is currently experimental and may be subject to breaking changes or even removal in future versions. Use with caution and be prepared to update your configuration or migrate to alternative solutions if needed.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## [Prerequisites](#prerequisites)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- [@ui5/cli](https://ui5.github.io/cli/) 4.0 or later (specVersion 4.0 and later in `ui5.yaml`)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
| ------------------- | ---------- | ---------------- | ------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
15
|
-
| `url` | `string` | **required** | `undefined` | Destination URL to proxy requests to. |
|
|
16
|
-
| `paths` | `string[]` | **required** | `[]` | Array of OData source paths to proxy to this destination. Each path represents an OData service that should be proxied. Requests matching these paths will have the path prefix removed before forwarding. |
|
|
17
|
-
| `pathRewrite` | `string` | optional | `undefined` | Optional path rewriting. When specified, the matched path prefix will be replaced with this value before forwarding to the backend. If not specified, the matched path is simply removed. Example: path `/resources/lib/api` with pathRewrite `/api` transforms `/resources/lib/api/v1/Service` to `/api/v1/Service`. |
|
|
18
|
-
| `credentials` | object | optional | `undefined` | Manual OAuth credentials. If not provided, middleware attempts to auto-detect from Cloud Foundry ADP project. |
|
|
19
|
-
| `credentials.clientId` | `string` | mandatory (if credentials provided) | `undefined` | OAuth2 client ID. |
|
|
20
|
-
| `credentials.clientSecret` | `string` | mandatory (if credentials provided) | `undefined` | OAuth2 client secret. |
|
|
21
|
-
| `credentials.url` | `string` | mandatory (if credentials provided) | `undefined` | Base URL for the OAuth service. The token endpoint will be constructed as `{url}/oauth/token`. |
|
|
22
|
-
| `debug` | `boolean` | optional | `false` | Enable debug logging for troubleshooting. |
|
|
13
|
+
## [Install](#install)
|
|
23
14
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
### [Basic Configuration](#basic-configuration)
|
|
27
|
-
|
|
28
|
-
```yaml
|
|
29
|
-
server:
|
|
30
|
-
customMiddleware:
|
|
31
|
-
- name: backend-proxy-middleware-cf
|
|
32
|
-
afterMiddleware: compression
|
|
33
|
-
configuration:
|
|
34
|
-
backends:
|
|
35
|
-
- url: https://your-backend-service
|
|
36
|
-
paths:
|
|
37
|
-
- /odata/v4/visitorservice
|
|
38
|
-
- /odata
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add -D @sap-ux/backend-proxy-middleware-cf
|
|
39
17
|
```
|
|
40
18
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
19
|
+
## [Configuration (ui5.yaml)](#configuration-ui5yaml)
|
|
20
|
+
|
|
21
|
+
| Option | Type | Default | Description |
|
|
22
|
+
|--------|------|---------|-------------|
|
|
23
|
+
| `debug` | `boolean` | `false` | Verbose logging |
|
|
24
|
+
| `port` | `number` | `5000` | Port for the local approuter |
|
|
25
|
+
| `xsappJsonPath` | `string` | `"./xs-app.json"` | Path to the CF-style approuter config (e.g. `xs-app.json`). Destination route sources should match the pattern `/[^/]*\/(.*\/)?[^/]*/` (e.g. `"^/backend/(.*)$"`). |
|
|
26
|
+
| `envOptionsPath` | `string` | — | Path (relative to project root) to a JSON file. Each top-level key is set on `process.env` (objects/arrays are JSON-stringified) so the approuter can read credentials and services. |
|
|
27
|
+
| `destinations` | `array` or `string` | `[]` | List of `{ name, url }` destinations (names must match routes in `xs-app.json`). |
|
|
28
|
+
| `allowServices` | `boolean` | `false` | Allow BTP services referenced in `xs-app.json` (requires authenticated BTP session). |
|
|
29
|
+
| `authenticationMethod` | `"none"` \| `"route"` | `"none"` | Authentication for routes |
|
|
30
|
+
| `allowLocalDir` | `boolean` | `false` | Allow approuter to serve static assets (usually ui5-server serves them). |
|
|
31
|
+
| `rewriteContent` | `boolean` | `true` | Replace proxied URLs in response body with the server URL |
|
|
32
|
+
| `rewriteContentTypes` | `string[]` | `["text/html", "application/json", "application/atom+xml", "application/xml"]` | Content types to rewrite when `rewriteContent` is true |
|
|
33
|
+
| `extensions` | `array` | `[]` | Approuter extensions: `{ module: string, parameters?: Record<string, string> }`. Parameters are passed as the 4th argument to extension handlers. |
|
|
34
|
+
| `appendAuthRoute` | `boolean` | `false` | Add a route for HTML pages to trigger XSUAA login when `authenticationMethod` is not `"none"`. |
|
|
35
|
+
| `disableWelcomeFile` | `boolean` | `false` | Disable welcome file handling from `xs-app.json`. |
|
|
36
|
+
| `disableUi5ServerRoutes` | `boolean` | `false` | Disable automatic injection of the `ui5-server` HTML auth route for `/test/*` and `/local/*` pages. |
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
server:
|
|
47
|
-
customMiddleware:
|
|
48
|
-
- name: backend-proxy-middleware-cf
|
|
49
|
-
afterMiddleware: compression
|
|
50
|
-
configuration:
|
|
51
|
-
backends:
|
|
52
|
-
- url: https://your-backend-service
|
|
53
|
-
paths:
|
|
54
|
-
- /odata/v4/visitorservice
|
|
55
|
-
- /odata
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
The middleware will:
|
|
59
|
-
|
|
60
|
-
1. Read the `app-variant-bundler-build` custom task from `ui5.yaml`
|
|
61
|
-
2. Extract `serviceInstanceName` and `serviceInstanceGuid`
|
|
62
|
-
3. Retrieve service keys using `@sap-ux/adp-tooling`
|
|
63
|
-
4. Extract UAA credentials and construct the token endpoint
|
|
64
|
-
5. Automatically add Bearer tokens to proxied requests
|
|
65
|
-
|
|
66
|
-
### [Manual Credentials](#manual-credentials)
|
|
38
|
+
## [Usage](#usage)
|
|
67
39
|
|
|
68
|
-
|
|
40
|
+
1. Add the middleware in `ui5.yaml`:
|
|
69
41
|
|
|
70
42
|
```yaml
|
|
71
43
|
server:
|
|
@@ -73,140 +45,56 @@ server:
|
|
|
73
45
|
- name: backend-proxy-middleware-cf
|
|
74
46
|
afterMiddleware: compression
|
|
75
47
|
configuration:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
clientSecret: "your-client-secret"
|
|
84
|
-
url: "https://example.authentication"
|
|
85
|
-
debug: true
|
|
48
|
+
authenticationMethod: "none"
|
|
49
|
+
debug: true
|
|
50
|
+
port: 5000
|
|
51
|
+
xsappJsonPath: "./xs-app.json"
|
|
52
|
+
destinations:
|
|
53
|
+
- name: "backend"
|
|
54
|
+
url: "https://your-backend.example/path"
|
|
86
55
|
```
|
|
87
56
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
### Multiple OData Sources
|
|
57
|
+
2. Point your path to the location of xs-app.json.
|
|
91
58
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
```yaml
|
|
95
|
-
server:
|
|
96
|
-
customMiddleware:
|
|
97
|
-
- name: backend-proxy-middleware-cf
|
|
98
|
-
afterMiddleware: compression
|
|
99
|
-
configuration:
|
|
100
|
-
backends:
|
|
101
|
-
- url: https://your-backend-service
|
|
102
|
-
paths:
|
|
103
|
-
- /odata/v4/service1
|
|
104
|
-
- /odata/v4/service2
|
|
105
|
-
- /odata/v2/legacy
|
|
106
|
-
```
|
|
59
|
+
### [Env options file (VCAP_SERVICES, credentials)](#env-options-file-vcap_services-credentials)
|
|
107
60
|
|
|
108
|
-
|
|
61
|
+
To run the approuter with BTP-style credentials (e.g. XSUAA, destinations), point `envOptionsPath` to a JSON file. Each top-level key is applied to `process.env` so the approuter can find them; object and array values are stored as JSON strings. The key `destinations` in the file is ignored so that the middleware's `destinations` from ui5.yaml are used.
|
|
109
62
|
|
|
110
|
-
|
|
63
|
+
Example shape (minimal):
|
|
111
64
|
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
- url: https://my-backend.example.com
|
|
120
|
-
paths:
|
|
121
|
-
- /resources/my/app/ui/api/example
|
|
122
|
-
pathRewrite: /api/example
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"VCAP_SERVICES": {
|
|
68
|
+
"xsuaa": [{ "label": "xsuaa", "credentials": { "clientid": "...", "clientsecret": "...", "url": "..." } }],
|
|
69
|
+
"destination": [{ "label": "destination", "credentials": { ... } }]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
123
72
|
```
|
|
124
73
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- **Path rewriting:** `/api/example`
|
|
129
|
-
- **Forwarded to backend:** `/api/example/v1/ExampleService/$metadata`
|
|
74
|
+
You can export a real `VCAP_SERVICES` (and optionally other keys) from your BTP app and save it to a file (e.g. `./default-env.json`); do not commit secrets. In ui5.yaml set `envOptionsPath: "./default-env.json"`.
|
|
75
|
+
|
|
76
|
+
## [How it works](#how-it-works)
|
|
130
77
|
|
|
131
|
-
|
|
132
|
-
- **Request:** `/odata/v4/service/EntitySet`
|
|
133
|
-
- **Matched path:** `/odata`
|
|
134
|
-
- **Forwarded:** `/v4/service/EntitySet`
|
|
78
|
+
The middleware starts a local `@sap/approuter` process with your `xs-app.json` and destinations. The UI5 server proxies requests that match approuter routes (e.g. login callback, welcome file, destination routes) to `http://localhost:<port>`. Response content can be rewritten so that backend URLs in the body are replaced with the server URL for the relevant content types.
|
|
135
79
|
|
|
136
|
-
|
|
80
|
+
## [Extensions](#extensions)
|
|
137
81
|
|
|
138
|
-
You can
|
|
82
|
+
You can plug in approuter extensions and pass parameters:
|
|
139
83
|
|
|
140
84
|
```yaml
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
backends:
|
|
147
|
-
- url: https://your-backend-service1
|
|
148
|
-
paths:
|
|
149
|
-
- /odata/v4/service1
|
|
150
|
-
- /odata/v4/service2
|
|
151
|
-
- /odata/v2/legacy
|
|
152
|
-
- url: https://your-backend-service2
|
|
153
|
-
paths:
|
|
154
|
-
- /odata/v4/service1
|
|
155
|
-
- /odata/v4/service2
|
|
156
|
-
- /odata/v2/legacy
|
|
85
|
+
configuration:
|
|
86
|
+
extensions:
|
|
87
|
+
- module: ./approuter-local-ext.js
|
|
88
|
+
parameters:
|
|
89
|
+
userId: "user@example.com"
|
|
157
90
|
```
|
|
158
91
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
Enable debug logging to troubleshoot issues:
|
|
92
|
+
In the extension, parameters are available as the 4th argument: `function (req, res, next, params)`.
|
|
162
93
|
|
|
163
|
-
|
|
164
|
-
server:
|
|
165
|
-
customMiddleware:
|
|
166
|
-
- name: backend-proxy-middleware-cf
|
|
167
|
-
afterMiddleware: compression
|
|
168
|
-
configuration:
|
|
169
|
-
backends:
|
|
170
|
-
- url: https://your-backend-service.cfapps.eu12.hana.ondemand.com
|
|
171
|
-
paths:
|
|
172
|
-
- /odata
|
|
173
|
-
debug: true
|
|
174
|
-
```
|
|
94
|
+
## [Keywords](#keywords)
|
|
175
95
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
4. **Credentials**: Extracts `serviceInstanceName` and `serviceInstanceGuid` from the custom task configuration.
|
|
182
|
-
5. **Service Keys**: Retrieves service keys using `@sap-ux/adp-tooling`, which communicates with Cloud Foundry CLI.
|
|
183
|
-
6. **Token Endpoint**: Constructs the token endpoint from the UAA base URL as `{url}/oauth/token`.
|
|
184
|
-
7. **Token Management**: Requests OAuth tokens using client credentials flow.
|
|
185
|
-
8. **Caching**: Caches tokens in memory and refreshes them automatically 60 seconds before expiry.
|
|
186
|
-
9. **Request Proxying**: Adds `Authorization: Bearer <token>` header to proxied requests before forwarding.
|
|
187
|
-
|
|
188
|
-
## Error Handling
|
|
189
|
-
|
|
190
|
-
- If `url` is not provided, the middleware will be inactive and log a warning.
|
|
191
|
-
- If no paths are configured, the middleware will be inactive and log a warning.
|
|
192
|
-
- If auto-detection fails and no manual credentials are provided, the middleware will proxy requests without OAuth tokens (may fail if backend requires authentication).
|
|
193
|
-
- If token request fails, an error is logged but the request may still proceed (depending on the backend's authentication requirements).
|
|
194
|
-
- All errors are logged for debugging purposes.
|
|
195
|
-
|
|
196
|
-
## Security Considerations
|
|
197
|
-
|
|
198
|
-
- Credentials are never logged in production mode.
|
|
199
|
-
- Tokens are cached in memory only and never persisted to disk.
|
|
200
|
-
- Token refresh happens automatically 60 seconds before expiry to avoid using expired tokens.
|
|
201
|
-
- Service keys are obtained securely through Cloud Foundry CLI.
|
|
202
|
-
- The middleware only proxies requests matching any of the configured path prefixes.
|
|
203
|
-
- If no paths are configured, the middleware will be inactive and log a warning.
|
|
204
|
-
|
|
205
|
-
## Keywords
|
|
206
|
-
|
|
207
|
-
- OAuth2 Middleware
|
|
208
|
-
- Cloud Foundry ADP
|
|
209
|
-
- Bearer Token
|
|
210
|
-
- Fiori tools
|
|
211
|
-
- SAP UI5
|
|
212
|
-
- Proxy Middleware
|
|
96
|
+
- UI5 middleware
|
|
97
|
+
- Cloud Foundry
|
|
98
|
+
- Approuter
|
|
99
|
+
- Destination proxy
|
|
100
|
+
- SAP BTP
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import createApprouter from '@sap/approuter';
|
|
2
|
+
import type { ToolsLogger } from '@sap-ux/logger';
|
|
3
|
+
import type { XsappConfig } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Options for starting the approuter.
|
|
6
|
+
*/
|
|
7
|
+
interface StartApprouterOptions {
|
|
8
|
+
/** Port to run the approuter on. */
|
|
9
|
+
port: number;
|
|
10
|
+
/** Parsed xs-app.json configuration. */
|
|
11
|
+
xsappConfig: XsappConfig;
|
|
12
|
+
/** Project root path (working directory for approuter). */
|
|
13
|
+
rootPath: string;
|
|
14
|
+
/** Approuter extension modules. */
|
|
15
|
+
modules: unknown[];
|
|
16
|
+
/** Logger instance. */
|
|
17
|
+
logger: ToolsLogger;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Start the approuter and register it globally for cleanup.
|
|
21
|
+
*
|
|
22
|
+
* @param options - Approuter start options.
|
|
23
|
+
* @returns The started approuter instance.
|
|
24
|
+
*/
|
|
25
|
+
export declare function startApprouter(options: StartApprouterOptions): ReturnType<typeof createApprouter>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=approuter.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startApprouter = startApprouter;
|
|
7
|
+
const approuter_1 = __importDefault(require("@sap/approuter"));
|
|
8
|
+
/**
|
|
9
|
+
* Start the approuter and register it globally for cleanup.
|
|
10
|
+
*
|
|
11
|
+
* @param options - Approuter start options.
|
|
12
|
+
* @returns The started approuter instance.
|
|
13
|
+
*/
|
|
14
|
+
function startApprouter(options) {
|
|
15
|
+
const { port, xsappConfig, rootPath, modules, logger } = options;
|
|
16
|
+
const approuter = (0, approuter_1.default)();
|
|
17
|
+
try {
|
|
18
|
+
approuter.start({
|
|
19
|
+
port,
|
|
20
|
+
xsappConfig,
|
|
21
|
+
workingDir: rootPath,
|
|
22
|
+
extensions: modules
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
27
|
+
throw new Error(`Failed to start approuter on port ${port}: ${message}`);
|
|
28
|
+
}
|
|
29
|
+
logger.debug(`Approuter started on port ${port}`);
|
|
30
|
+
// Register approuter globally for cleanup
|
|
31
|
+
const globalKey = 'backend-proxy-middleware-cf';
|
|
32
|
+
const g = globalThis;
|
|
33
|
+
if (typeof g[globalKey]?.approuters === 'object') {
|
|
34
|
+
g[globalKey].approuters?.push(approuter);
|
|
35
|
+
}
|
|
36
|
+
return approuter;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=approuter.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ToolsLogger } from '@sap-ux/logger';
|
|
2
|
+
import type { ApprouterExtension, ExtensionModule, LoadedExtensions } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Load and prepare extension modules for the approuter.
|
|
5
|
+
*
|
|
6
|
+
* @param rootPath - Project root path for resolving module paths.
|
|
7
|
+
* @param extensions - Extension configs from effectiveOptions.
|
|
8
|
+
* @param logger - Logger instance.
|
|
9
|
+
* @returns Loaded extension modules and list of extension routes (paths) they register.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadExtensions(rootPath: string, extensions: ApprouterExtension[] | undefined, logger: ToolsLogger): LoadedExtensions;
|
|
12
|
+
/**
|
|
13
|
+
* Collect route paths registered by an extension module via insertMiddleware.
|
|
14
|
+
*
|
|
15
|
+
* @param extensionModule - The extension module to inspect.
|
|
16
|
+
* @returns Array of path strings from insertMiddleware entries that define a path.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getExtensionRoutes(extensionModule: ExtensionModule): string[];
|
|
19
|
+
/**
|
|
20
|
+
* Convert an extension config to a loaded extension module with parameter-injected handlers.
|
|
21
|
+
* Does not mutate any shared state; use getExtensionRoutes to obtain paths from the returned module.
|
|
22
|
+
*
|
|
23
|
+
* @param extension - The extension to convert.
|
|
24
|
+
* @param rootPath - The root path of the project for resolving the module.
|
|
25
|
+
* @param logger - The logger to use.
|
|
26
|
+
* @returns The extension module, or undefined if the module cannot be resolved.
|
|
27
|
+
*/
|
|
28
|
+
export declare function toExtensionModule(extension: ApprouterExtension, rootPath: string, logger: ToolsLogger): ExtensionModule | undefined;
|
|
29
|
+
//# sourceMappingURL=extensions.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadExtensions = loadExtensions;
|
|
4
|
+
exports.getExtensionRoutes = getExtensionRoutes;
|
|
5
|
+
exports.toExtensionModule = toExtensionModule;
|
|
6
|
+
/**
|
|
7
|
+
* Create a wrapper that injects parameters as 4th argument for approuter extension handlers.
|
|
8
|
+
*
|
|
9
|
+
* @param middleware - Original handler (req, res, next, params).
|
|
10
|
+
* @param params - Parameters to inject (from extension config).
|
|
11
|
+
* @returns Wrapper function with ≤3 args so approuter invokes it.
|
|
12
|
+
*/
|
|
13
|
+
function createParametersInjector(middleware, params) {
|
|
14
|
+
return function injectParameters(req, res, next) {
|
|
15
|
+
req['backend-proxy-middleware-cf'] = { parameters: params };
|
|
16
|
+
middleware.apply(this, [req, res, next, params]);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load and prepare extension modules for the approuter.
|
|
21
|
+
*
|
|
22
|
+
* @param rootPath - Project root path for resolving module paths.
|
|
23
|
+
* @param extensions - Extension configs from effectiveOptions.
|
|
24
|
+
* @param logger - Logger instance.
|
|
25
|
+
* @returns Loaded extension modules and list of extension routes (paths) they register.
|
|
26
|
+
*/
|
|
27
|
+
function loadExtensions(rootPath, extensions, logger) {
|
|
28
|
+
const modules = (extensions ?? [])
|
|
29
|
+
.map((extension) => toExtensionModule(extension, rootPath, logger))
|
|
30
|
+
.filter((e) => e != null);
|
|
31
|
+
const routes = modules.flatMap(getExtensionRoutes);
|
|
32
|
+
return { modules, routes };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Collect route paths registered by an extension module via insertMiddleware.
|
|
36
|
+
*
|
|
37
|
+
* @param extensionModule - The extension module to inspect.
|
|
38
|
+
* @returns Array of path strings from insertMiddleware entries that define a path.
|
|
39
|
+
*/
|
|
40
|
+
function getExtensionRoutes(extensionModule) {
|
|
41
|
+
const insertMiddleware = extensionModule.insertMiddleware;
|
|
42
|
+
if (!insertMiddleware) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const paths = [];
|
|
46
|
+
for (const type of Object.keys(insertMiddleware)) {
|
|
47
|
+
for (const entry of insertMiddleware[type]) {
|
|
48
|
+
if (typeof entry !== 'function' && entry.path) {
|
|
49
|
+
paths.push(entry.path);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return paths;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Convert an extension config to a loaded extension module with parameter-injected handlers.
|
|
57
|
+
* Does not mutate any shared state; use getExtensionRoutes to obtain paths from the returned module.
|
|
58
|
+
*
|
|
59
|
+
* @param extension - The extension to convert.
|
|
60
|
+
* @param rootPath - The root path of the project for resolving the module.
|
|
61
|
+
* @param logger - The logger to use.
|
|
62
|
+
* @returns The extension module, or undefined if the module cannot be resolved.
|
|
63
|
+
*/
|
|
64
|
+
function toExtensionModule(extension, rootPath, logger) {
|
|
65
|
+
try {
|
|
66
|
+
const extensionModulePath = require.resolve(extension.module, { paths: [rootPath] });
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- dynamic user extension path
|
|
68
|
+
const originalModule = require(extensionModulePath);
|
|
69
|
+
const insertMiddleware = originalModule?.insertMiddleware;
|
|
70
|
+
if (!insertMiddleware) {
|
|
71
|
+
return originalModule;
|
|
72
|
+
}
|
|
73
|
+
// Shallow-copy to avoid mutating the require'd module cache
|
|
74
|
+
const wrappedMiddleware = {};
|
|
75
|
+
for (const type of Object.keys(insertMiddleware)) {
|
|
76
|
+
wrappedMiddleware[type] = insertMiddleware[type].map((module) => {
|
|
77
|
+
if (typeof module === 'function') {
|
|
78
|
+
return createParametersInjector(module, extension.parameters);
|
|
79
|
+
}
|
|
80
|
+
return { ...module, handler: createParametersInjector(module.handler, extension.parameters) };
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return { ...originalModule, insertMiddleware: wrappedMiddleware };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
logger.warn(`Failed to resolve extension "${extension.module}". Extension will be ignored.`);
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=extensions.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BackendProxyMiddlewareCfConfig, EffectiveOptions } from '../types';
|
|
2
|
+
export declare const DEFAULT_REWRITE_CONTENT_TYPES: string[];
|
|
3
|
+
/**
|
|
4
|
+
* Merge user configuration with defaults.
|
|
5
|
+
*
|
|
6
|
+
* @param configuration - Configuration from ui5.yaml.
|
|
7
|
+
* @returns Effective options with all defaults applied.
|
|
8
|
+
*/
|
|
9
|
+
export declare function mergeEffectiveOptions(configuration: BackendProxyMiddlewareCfConfig): EffectiveOptions;
|
|
10
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_REWRITE_CONTENT_TYPES = void 0;
|
|
4
|
+
exports.mergeEffectiveOptions = mergeEffectiveOptions;
|
|
5
|
+
exports.DEFAULT_REWRITE_CONTENT_TYPES = [
|
|
6
|
+
'text/html',
|
|
7
|
+
'application/json',
|
|
8
|
+
'application/atom+xml',
|
|
9
|
+
'application/xml'
|
|
10
|
+
];
|
|
11
|
+
/**
|
|
12
|
+
* Merge user configuration with defaults.
|
|
13
|
+
*
|
|
14
|
+
* @param configuration - Configuration from ui5.yaml.
|
|
15
|
+
* @returns Effective options with all defaults applied.
|
|
16
|
+
*/
|
|
17
|
+
function mergeEffectiveOptions(configuration) {
|
|
18
|
+
return {
|
|
19
|
+
debug: false,
|
|
20
|
+
port: 5000,
|
|
21
|
+
xsappJsonPath: './xs-app.json',
|
|
22
|
+
destinations: [],
|
|
23
|
+
allowServices: false,
|
|
24
|
+
authenticationMethod: 'none',
|
|
25
|
+
allowLocalDir: false,
|
|
26
|
+
rewriteContent: true,
|
|
27
|
+
rewriteContentTypes: [...exports.DEFAULT_REWRITE_CONTENT_TYPES],
|
|
28
|
+
appendAuthRoute: false,
|
|
29
|
+
disableWelcomeFile: false,
|
|
30
|
+
disableUi5ServerRoutes: false,
|
|
31
|
+
extensions: [],
|
|
32
|
+
...configuration
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Destination name for the local UI5 server (used in xs-app.json routes and env config).
|
|
3
|
+
*/
|
|
4
|
+
export declare const UI5_SERVER_DESTINATION = "ui5-server";
|
|
5
|
+
/**
|
|
6
|
+
* Header set by the proxy on requests forwarded to the approuter.
|
|
7
|
+
* Used to detect approuter loop-back requests and prevent infinite loops.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PROXY_MARKER_HEADER = "x-backend-proxy-middleware-cf";
|
|
10
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROXY_MARKER_HEADER = exports.UI5_SERVER_DESTINATION = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Destination name for the local UI5 server (used in xs-app.json routes and env config).
|
|
6
|
+
*/
|
|
7
|
+
exports.UI5_SERVER_DESTINATION = 'ui5-server';
|
|
8
|
+
/**
|
|
9
|
+
* Header set by the proxy on requests forwarded to the approuter.
|
|
10
|
+
* Used to detect approuter loop-back requests and prevent infinite loops.
|
|
11
|
+
*/
|
|
12
|
+
exports.PROXY_MARKER_HEADER = 'x-backend-proxy-middleware-cf';
|
|
13
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ToolsLogger } from '@sap-ux/logger';
|
|
2
|
+
import type { EffectiveOptions } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Load env options from file or CF, apply to process.env, and add destinations from effectiveOptions.
|
|
5
|
+
*
|
|
6
|
+
* When effectiveOptions.envOptionsPath is set, loads that JSON file. When null, loads mta.yaml one level
|
|
7
|
+
* above rootPath and fetches VCAP_SERVICES from CF. effectiveOptions.destinations is applied so
|
|
8
|
+
* middleware config takes precedence over file/env.
|
|
9
|
+
*
|
|
10
|
+
* @param rootPath - Project root path.
|
|
11
|
+
* @param effectiveOptions - Merged config; envOptionsPath and destinations are used.
|
|
12
|
+
* @param logger - Logger for CF path.
|
|
13
|
+
* @returns Promise resolving when env options are loaded and applied.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadAndApplyEnvOptions(rootPath: string, effectiveOptions: EffectiveOptions, logger: ToolsLogger): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Ensure the ui5-server destination exists and has the correct URL.
|
|
18
|
+
* If ui5-server doesn't exist in configuration, it will be auto-created.
|
|
19
|
+
* If it exists but has a different port, it will be updated.
|
|
20
|
+
* In BAS, the external URL is used instead of localhost so the approuter
|
|
21
|
+
* builds correct redirect URIs.
|
|
22
|
+
*
|
|
23
|
+
* This enables multi-instance support and removes the need to manually
|
|
24
|
+
* configure ui5-server in ui5.yaml - it's auto-configured based on the actual port.
|
|
25
|
+
*
|
|
26
|
+
* @param effectiveOptions - Merged options containing destinations.
|
|
27
|
+
* @param actualPort - The actual port detected from the incoming request.
|
|
28
|
+
* @param basExternalUrl - Optional BAS external URL; when set, used as the destination URL.
|
|
29
|
+
* @returns True if destination was created or updated, false if no change needed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function updateUi5ServerDestinationPort(effectiveOptions: EffectiveOptions, actualPort: number, basExternalUrl?: URL): boolean;
|
|
32
|
+
//# sourceMappingURL=env.d.ts.map
|