├── .github └── workflows │ └── npm-publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── lib └── proxy.js ├── package-lock.json ├── package.json └── ui5.yaml /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v1 16 | with: 17 | node-version: 12 18 | registry-url: https://registry.npmjs.org/ 19 | - run: npm ci 20 | - run: npm publish 21 | env: 22 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 lemaiwo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UI5 proxy middleware using routes 2 | 3 | Middleware for [ui5-server](https://github.com/SAP/ui5-server), enabling proxy support. 4 | 5 | ## Install 6 | 7 | ```bash 8 | npm install ui5-middleware-route-proxy --save-dev 9 | ``` 10 | 11 | ## Configuration options (in `$yourapp/ui5.yaml`) 12 | 13 | - debug: `boolean` 14 | enable logging 15 | - root directory of the request uri: `object` 16 | used to match and forward requests to your server. Needs to include the leading slash character (`/`). 17 | - target: `string` 18 | hostname of your backend server 19 | - replacePath: `string` optional. If the request path needs to be modified by taking out the root directory uri 20 | - auth: `object` 21 | authorization object with username and password ( pass `false` if authorization is not required ) 22 | - user: `string` 23 | - pass: `string` 24 | - client: `string` optional. If the client is not the default client on the SAP system 25 | - header: `string` optional. Is used as value for the `Authorization` header if available. `user` and `pass` values are not used in this case. 26 | - changeOrigin: `boolean` optional. If provided, the value will be added to the [options of the http-proxy](https://www.npmjs.com/package/http-proxy#options). 27 | 28 | Each `boolean` or `string` field in a root path object can either represent the actual value or the name of a parameter in a .env file. 29 | 30 | Example: 31 | ```yml 32 | debug: true 33 | /sap: 34 | target: http(s)://host:port 35 | replacePath: true 36 | auth: 37 | user: Username 38 | pass: Password! 39 | client: '100' 40 | ``` 41 | 42 | Example with target/user/pass in .env file: 43 | ```yaml 44 | debug: true 45 | /sap: 46 | target: PROXY_TARGET 47 | auth: 48 | user: PROXY_USERNAME 49 | pass: PROXY_PASSWORD 50 | client: PROXY_CLIENT 51 | ``` 52 | 53 | Example with target and dedicated `Authorization` header in .env file: 54 | ```yaml 55 | debug: true 56 | /sap: 57 | target: PROXY_TARGET 58 | auth: 59 | header: PROXY_AUTHORIZATION 60 | changeOrigin: true 61 | ``` 62 | 63 | ## Usage 64 | 65 | 1. Define the dependency in `$yourapp/package.json`: 66 | 67 | ```json 68 | "devDependencies": { 69 | // ... 70 | "ui5-middleware-route-proxy": "*" 71 | // ... 72 | }, 73 | "ui5": { 74 | "dependencies": [ 75 | // ... 76 | "ui5-middleware-route-proxy", 77 | // ... 78 | ] 79 | } 80 | ``` 81 | 82 | > As the devDependencies are not recognized by the UI5 tooling, they need to be listed in the `ui5 > dependencies` array. In addition, once using the `ui5 > dependencies` array you need to list all UI5 tooling relevant dependencies. 83 | 84 | 2. configure it in `$yourapp/ui5.yaml`: 85 | 86 | ```yaml 87 | server: 88 | customMiddleware: 89 | - name: ui5-middleware-route-proxy 90 | afterMiddleware: compression 91 | configuration: 92 | debug: true 93 | /routeRootPath: 94 | target: PROXY_TARGET 95 | auth: 96 | user: PROXY_USERNAME 97 | pass: PROXY_PASSWORD 98 | ``` 99 | 100 | 3. Add a `.env` file with your target, username and password for the proxy: 101 | 102 | ```yaml 103 | PROXY_TARGET= 104 | PROXY_USERNAME= 105 | PROXY_PASSWORD= 106 | ``` 107 | -------------------------------------------------------------------------------- /lib/proxy.js: -------------------------------------------------------------------------------- 1 | const httpProxy = require('http-proxy'); 2 | const dotenv = require("dotenv"); 3 | 4 | module.exports = function ({ 5 | resources, 6 | options 7 | }) { 8 | const proxy = new httpProxy.createProxyServer( {secure:false} ); 9 | const routing = options.configuration; 10 | 11 | 12 | // Call dotenv, so that contents in the file are stored in the environment variables 13 | dotenv.config(); 14 | 15 | return function (req, res, next) { 16 | if (req.header) { 17 | res.header('Access-Control-Allow-Origin', '*'); 18 | res.header('Access-Control-Allow-Credentials', 'true'); 19 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); 20 | res.header('Access-Control-Allow-Headers', 'X-Requested-With, Accept, Origin, Referer, User-Agent, Content-Type, Authorization, X-Mindflash-SessionID'); 21 | // intercept OPTIONS method 22 | if ('OPTIONS' === req.method) { 23 | res.header(200); 24 | if (routing.debug) { 25 | console.log(req.method + '(options): ' + req.url); 26 | } 27 | next(); 28 | return; 29 | } 30 | } 31 | 32 | // Check whether to intercept the call 33 | var dirname = Object.keys(routing).filter(sPath => req.url.startsWith(sPath)) 34 | 35 | if (dirname.length === 0 || !routing[dirname[0]]) { 36 | if (routing.debug) { 37 | console.log(req.method + ': ' + req.url); 38 | } 39 | next(); 40 | return; 41 | } 42 | 43 | dirname = dirname[0]; 44 | // Adjust if we need to replace the path 45 | req.url = (routing[dirname].replacePath) ? req.url.replace(dirname, "") : req.url 46 | 47 | // Adjust if we need to add a client 48 | if (routing[dirname].auth.client && req.url.indexOf("sap-client") === -1){ 49 | var client = (req.url.indexOf("?") > -1) ? "&" : "?"; 50 | client += "sap-client=" + (process.env[routing[dirname].auth.client] || routing[dirname].auth.client); 51 | req.url = req.url + client; 52 | } 53 | 54 | let proxyOptions = {}; 55 | 56 | //get target 57 | let envOrDirectTarget; 58 | envOrDirectTarget = process.env[routing[dirname].target] || routing[dirname].target; 59 | if (routing.debug) { 60 | console.log(req.method + ' (redirect): ' + envOrDirectTarget + req.url); 61 | } 62 | proxyOptions.target = envOrDirectTarget; 63 | 64 | //get specific auth for given service 65 | if(routing[dirname].auth){ 66 | let header = process.env[routing[dirname].auth.header] || routing[dirname].auth.header; 67 | username = process.env[routing[dirname].auth.user] || routing[dirname].auth.user; 68 | password = process.env[routing[dirname].auth.pass] || routing[dirname].auth.pass; 69 | if(header){ 70 | proxyOptions.headers = { Authorization: header }; 71 | if (routing.debug) { 72 | console.log(req.method + ' (redirect): Using dedicated Authorization Header'); 73 | } 74 | }else{ 75 | proxyOptions.auth = username + ":" + password; 76 | if (routing.debug) { 77 | console.log(req.method + ' (redirect): Using user & pass for BasicAuthentication'); 78 | } 79 | } 80 | } 81 | 82 | // additional proxy settings 83 | let changeOrigin = process.env[routing[dirname].auth.changeOrigin] || routing[dirname].changeOrigin; 84 | if(changeOrigin){ 85 | proxyOptions.changeOrigin = changeOrigin; 86 | } 87 | 88 | proxy.web(req, res, proxyOptions, function (err) { 89 | if (err) { 90 | next(err); 91 | } 92 | }); 93 | } 94 | }; 95 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui5-middleware-route-proxy", 3 | "version": "1.0.5", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "debug": { 8 | "version": "3.2.6", 9 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 10 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 11 | "requires": { 12 | "ms": "^2.1.1" 13 | } 14 | }, 15 | "dotenv": { 16 | "version": "8.2.0", 17 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", 18 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" 19 | }, 20 | "eventemitter3": { 21 | "version": "4.0.4", 22 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", 23 | "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" 24 | }, 25 | "follow-redirects": { 26 | "version": "1.11.0", 27 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", 28 | "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==", 29 | "requires": { 30 | "debug": "^3.0.0" 31 | } 32 | }, 33 | "http-proxy": { 34 | "version": "1.18.1", 35 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", 36 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", 37 | "requires": { 38 | "eventemitter3": "^4.0.0", 39 | "follow-redirects": "^1.0.0", 40 | "requires-port": "^1.0.0" 41 | } 42 | }, 43 | "ms": { 44 | "version": "2.1.2", 45 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 46 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 47 | }, 48 | "requires-port": { 49 | "version": "1.0.0", 50 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 51 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui5-middleware-route-proxy", 3 | "version": "1.0.10", 4 | "description": "UI5tooling middleware route proxy", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Wouter Lemaire", 10 | "license": "MIT", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/lemaiwo/ui5-middleware-route-proxy.git" 14 | }, 15 | "dependencies": { 16 | "http-proxy": "^1.18.0", 17 | "dotenv": "^8.2.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ui5.yaml: -------------------------------------------------------------------------------- 1 | specVersion: '1.0' 2 | metadata: 3 | name: ui5-middleware-route-proxy 4 | kind: extension 5 | type: server-middleware 6 | middleware: 7 | path: lib/proxy.js --------------------------------------------------------------------------------