From d32ee406cf9e423b29675cda2e581eb6871dbe93 Mon Sep 17 00:00:00 2001 From: Julian Kniephoff Date: Tue, 14 May 2024 12:19:03 +0200 Subject: [PATCH] Remove the proxy server Well, the outer one at least. For context: This app had two proxies: One comes with `create-react-app` as a middleware in the dev server, proxying all the requests the frontend makes (except for the static JS, CSS, etc. assets this project is generating) to some other server. In production, Opencast delivers the admin UI assets and the backend under the same URL, but in development this is not true. The development server delivers the assets and the backend is ... well it's complicated. So far you either started a static file server which was acting as a mock backend, or you started **another** proxy server that piped the requests through to an Opencast instance of your choosing while tacking on some digest authentication. Well, the dev server proxy can't (easily) do digest auth, but the fact that we insist on digest auth is a historical artifact, anyway, at least as far as I know. It can do basic auth, though, and it **is** a proxy, so it can do everything we need! This also updates the `README` appropriately and cleans it up some in general (though not completely). --- README.md | 111 +++++-------- app/src/setupProxy.js | 5 +- package-lock.json | 375 +----------------------------------------- package.json | 7 +- proxyServer.js | 74 --------- 5 files changed, 48 insertions(+), 524 deletions(-) delete mode 100644 proxyServer.js diff --git a/README.md b/README.md index 28682a6d5f..ebc1156451 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,71 @@ Opencast Admin UI -===================== +================= -The Opencast Admin UI is a graphical interface included by Opencast to give -admins an easy way of managing their Opencast instance. +The Opencast Admin UI is a graphical interface included with Opencast +that allows admins to easily manage their Opencast instance. -Quickstart ----------- +Development and testing +----------------------- -Commands to hack into your console to get to testing pull requests ASAP: +To get a local copy of the admin UI to test or develop on, you can do the following: -```console +```sh git clone git@github.com:opencast/opencast-admin-interface.git opencast-admin-interface-demo cd opencast-admin-interface-demo +git switch my-branch # or otherwise check out, pull, merge, etc. whatever branch you want to test/hack on npm ci cd app npm ci cd .. -npm run proxy-server http://stable.opencast.org opencast_system_account CHANGE_ME ``` -Open a second tab: +You can now run a local instance of the UI by saying -```bash -npm run client -``` - -Open a third tab to checkout the pull request you want to test. You need to know the pull request number!: - -```bash -git fetch origin pull/{PULL REQUEST NUMBER HERE}/head:some-branch-name-of-your-choosing -git checkout some-branch-name-of-your-choosing +```sh +npm start ``` -Development -------- - -Before starting, get the project dependencies by running `npm ci` beforehand both in the root and `/app` directory. +This runs a static file server in the background replying mock data to the various requests +the UI would normally send to the Opencast backend. +It also runs an autoreloading development server delivering the admin UI itself, +and automatically open a browser tab pointing to it. -To test with real data run: +Not all functionality of the admin UI works in this mode. If you need to test with real data, +or need the ability to change it, you can rely on the proxy functionality of said development server, +instead of running the static file server. Run: -```shell -npm run proxy-server http://stable.opencast.org *opencast_digest_username* *opencast_digest_password* +```sh +PROXY_TARGET=https://develop.opencast.org npm run client ``` -This will start a proxy server at localhost:5000. It will automatically proxy -requests to an Opencast instance at http://stable.opencast.org. You can change the URL to a different Opencast if you wish (e.g., http://localhost.8080 for -a local Opencast installation). Note that `http` is required. +Here, `PROXY_TARGET` is the target URL of the Opencast instance you want to test against. +This can also be a local one like `http://localhost:8080`. -You can then start the client in a different tab by running: +By default, this tries to authenticate backend requests using HTTP Basic Auth +as user `admin` with the default password `opencast`. +If you want to authenticate using different credentials, you can specify them +in the `PROXY_AUTH` variable in the format `user:password`, as in -```shell -npm run client +```sh +PROXY_TARGET=http://localhost:8080 PROXY_AUTH=jdoe:aligator3 npm run client ``` -This will start a client server in the development mode. -Open [http://localhost:3000](localhost:3000) to view it in the browser. +### Ports --------- +By default, the development server runs on port `3000`, and the static server on port `5000`. +If you need to change these ports, you have to do the following: -Alternatively, you can spin up a mock instance of the admin ui with: - -```shell -npm start +```sh +PORT=5001 npm run server ``` -This uses mock data instead of a real Opencast. This means certain features will -not work when using this mode. - -### Alternative ports - -The static file server and the proxy server serve their content -on the port 5000. If this is used already (as it is on macOS) -you can specify an alternative port in the `PORT` environment variable, -for example: +This runs the static server only, serving assets under `http://localhost:5001`. Now, in a second terminal, run: - PORT=5001 npm run proxy-server ... - -Note that you need to specify the same port when running the client, -this time in the `PROXY_PORT` variable: +```sh +PORT=3001 PROXY_TARGET=http://localhost:5001 npm run client +``` - PROXY_PORT=5001 npm run client +This runs the development server on port `3001` and tells it about the alternative file server location. How to cut a release for Opencast --------------------------------- @@ -88,10 +74,10 @@ How to cut a release for Opencast 1. Switch to the commit you want to turn into the release 1. Create and push a new tag - ```bash - DATE=$(date +%Y-%m-%d) - git tag -m Release -s "$DATE" - git push upstream "$DATE":"$DATE" + ```sh + DATE=$(date +%Y-%m-%d) + git tag -m Release -s "$DATE" + git push upstream "$DATE":"$DATE" ``` 1. Wait for the [Create release draft](https://github.com/opencast/opencast-admin-interface/actions/workflows/create-release.yml) @@ -104,19 +90,8 @@ How to cut a release for Opencast if necessary - Verify that the new release runs in Opencast, then create the pull request. -Opencast API used by the Admin UI -------------- - -The Admin UI accesses all endpoints in Opencast located under - -* `/admin-ng/*` - -If you want to use the current version of the frontend with an earlier Opencast -version, you will have to cherry-pick the relevant commits from the Opencast -repository yourself. - Translating the Admin UI -------------- +------------------------ You can help translate the Opencast Admin UI to your language on [crowdin.com/project/opencast-admin-interface](https://crowdin.com/project/opencast-admin-interface). Simply request to join the project on Crowdin and start translating. If you are interested in translating a language that is not a target language right now, please create [a GitHub issue](https://github.com/opencast/opencast-admin-interface/issues) and we will add the language. diff --git a/app/src/setupProxy.js b/app/src/setupProxy.js index 693d5a3cb8..8e1c76e900 100644 --- a/app/src/setupProxy.js +++ b/app/src/setupProxy.js @@ -1,7 +1,5 @@ const { createProxyMiddleware } = require("http-proxy-middleware"); -const port = process.env.PROXY_PORT || 5000; - module.exports = function (app) { app.use( [ @@ -16,8 +14,9 @@ module.exports = function (app) { "/ui", ], createProxyMiddleware({ - target: `http://localhost:${port}`, + target: process.env.PROXY_TARGET || "http://localhost:5000", changeOrigin: true, + auth: process.env.PROXY_AUTH || "admin:opencast", }), ); }; diff --git a/package-lock.json b/package-lock.json index 810cf75e44..d7495ea2be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,7 @@ "license": "ECL-2.0", "dependencies": { "express": "^4.18.2", - "nodemon": "^3.0.3", - "request-digest": "^1.0.13", - "url-parse": "^1.5.10" + "nodemon": "^3.0.3" } }, "node_modules/abbrev": { @@ -30,20 +28,6 @@ "node": ">= 0.6" } }, - "node_modules/ajv": { - "version": "6.12.6", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/anymatch": { "version": "3.1.3", "license": "ISC", @@ -59,46 +43,10 @@ "version": "1.1.1", "license": "MIT" }, - "node_modules/asn1": { - "version": "0.2.6", - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "license": "MIT" - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "license": "MIT" - }, "node_modules/balanced-match": { "version": "1.0.2", "license": "MIT" }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "license": "MIT", @@ -106,10 +54,6 @@ "node": ">=8" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "license": "MIT" - }, "node_modules/body-parser": { "version": "1.20.2", "license": "MIT", @@ -168,10 +112,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/caseless": { - "version": "0.12.0", - "license": "Apache-2.0" - }, "node_modules/chokidar": { "version": "3.5.3", "funding": [ @@ -197,16 +137,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/concat-map": { "version": "0.0.1", "license": "MIT" @@ -240,20 +170,6 @@ "version": "1.0.6", "license": "MIT" }, - "node_modules/core-util-is": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/dashdash": { - "version": "1.14.1", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/debug": { "version": "2.6.9", "license": "MIT", @@ -261,13 +177,6 @@ "ms": "2.0.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/depd": { "version": "2.0.0", "license": "MIT", @@ -283,14 +192,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" @@ -354,25 +255,6 @@ "node": ">= 0.10.0" } }, - "node_modules/extend": { - "version": "3.0.2", - "license": "MIT" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "license": "MIT" - }, "node_modules/fill-range": { "version": "7.0.1", "license": "MIT", @@ -399,13 +281,6 @@ "node": ">= 0.8" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, "node_modules/forwarded": { "version": "0.2.0", "license": "MIT", @@ -436,13 +311,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/getpass": { - "version": "0.1.7", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "license": "ISC", @@ -453,24 +321,6 @@ "node": ">= 6" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has": { "version": "1.0.3", "license": "MIT", @@ -512,19 +362,6 @@ "node": ">= 0.8" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "license": "MIT", @@ -584,47 +421,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/isstream": { - "version": "0.1.2", - "license": "MIT" - }, - "node_modules/jsbn": { - "version": "0.1.1", - "license": "MIT" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "license": "ISC" - }, - "node_modules/jsprim": { - "version": "1.4.2", - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "license": "MIT" - }, "node_modules/lru-cache": { "version": "6.0.0", "license": "ISC", @@ -779,13 +575,6 @@ "node": ">=0.10.0" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, "node_modules/object-inspect": { "version": "1.12.3", "license": "MIT", @@ -814,10 +603,6 @@ "version": "0.1.7", "license": "MIT" }, - "node_modules/performance-now": { - "version": "2.1.0", - "license": "MIT" - }, "node_modules/picomatch": { "version": "2.3.1", "license": "MIT", @@ -839,21 +624,10 @@ "node": ">= 0.10" } }, - "node_modules/psl": { - "version": "1.9.0", - "license": "MIT" - }, "node_modules/pstree.remy": { "version": "1.1.8", "license": "MIT" }, - "node_modules/punycode": { - "version": "2.3.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { "version": "6.11.0", "license": "BSD-3-Clause", @@ -867,10 +641,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "license": "MIT" - }, "node_modules/range-parser": { "version": "1.2.1", "license": "MIT", @@ -901,78 +671,6 @@ "node": ">=8.10.0" } }, - "node_modules/request": { - "version": "2.88.2", - "license": "Apache-2.0", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request-digest": { - "version": "1.0.13", - "license": "ISC", - "dependencies": { - "bluebird": "^3.3.3", - "lodash": "^4.17.11", - "request": "^2.88.0" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/tough-cookie": { - "version": "2.5.0", - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/safe-buffer": { "version": "5.2.1", "funding": [ @@ -1073,29 +771,6 @@ "node": ">=10" } }, - "node_modules/sshpk": { - "version": "1.17.0", - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/statuses": { "version": "2.0.1", "license": "MIT", @@ -1140,20 +815,6 @@ "nodetouch": "bin/nodetouch.js" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "license": "Unlicense" - }, "node_modules/type-is": { "version": "1.6.18", "license": "MIT", @@ -1176,21 +837,6 @@ "node": ">= 0.8" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/utils-merge": { "version": "1.0.1", "license": "MIT", @@ -1198,13 +844,6 @@ "node": ">= 0.4.0" } }, - "node_modules/uuid": { - "version": "3.4.0", - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/vary": { "version": "1.1.2", "license": "MIT", @@ -1212,18 +851,6 @@ "node": ">= 0.8" } }, - "node_modules/verror": { - "version": "1.10.0", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "node_modules/yallist": { "version": "4.0.0", "license": "ISC" diff --git a/package.json b/package.json index 1542726d2b..e3021001e2 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,10 @@ "scripts": { "client": "cd app && npm start", "server": "nodemon", - "start": "npm run server & npm run client", - "proxy-server": "nodemon proxyServer.js $npm_config_host $npm_config_username $npm_config_password" + "start": "npm run server & npm run client" }, "dependencies": { "express": "^4.18.2", - "nodemon": "^3.0.3", - "request-digest": "^1.0.13", - "url-parse": "^1.5.10" + "nodemon": "^3.0.3" } } diff --git a/proxyServer.js b/proxyServer.js deleted file mode 100644 index a3acf925b6..0000000000 --- a/proxyServer.js +++ /dev/null @@ -1,74 +0,0 @@ -const path = require("path"); -const express = require("express"); -const requestDigest = require('request-digest'); -const urlParser = require('url-parse'); - -const app = express(); -const port = process.env.PORT || 5000; - -// Get values of proxy host, username and password from npm command -let args = process.argv, - host = args[2], - username = args[3], - password = args[4]; - -// Validate settings -if (host === undefined) { - throw new Error("Runtime Parameter host is undefined!"); -} -if (username === undefined) { - throw new Error("Runtime Parameter username is undefined!"); -} -if (password === undefined) { - throw new Error("Runtime Parameter password is undefined!"); -} - -app.use('', (req, res, next) => { - console.log('Proxy ' + req.method + ' ' + req.url + ' -> ' + host + req.url); - - let onReadFromBackend = function (error, response, body) { - if (error && (typeof error != 'object' || !error.hasOwnProperty('statusCode') || !error.hasOwnProperty('body'))) { - console.error(error); - return; - } - - // forward to client - res.statusCode = (response || error).statusCode; - body = body || (error ? error.body : ''); - res.write(body); - res.end(); - }; - - let parsed = urlParser(req.url); - let parsedHost = urlParser(host); - let escapedQuery = parsed.query.replace(',', '%2C'); - - let onForwardToBackend = function (body) { - let authConfig = { - host: parsedHost.protocol + '//' + parsedHost.hostname, - port: parsedHost.port, - path: parsed.pathname + escapedQuery, - method: req.method, - headers: { - 'content-type': req.headers['content-type'], - 'X-Requested-Auth': 'Digest' - }, - jar: true - }; - if (body.length) { - authConfig.body = body; - } - - requestDigest(username, password).request(authConfig, onReadFromBackend); - }; - - let buffer = []; - req.on('data', function (chunk) { - buffer.push(chunk); - }); - req.on('end', function () { - onForwardToBackend(Buffer.concat(buffer)); - }); -}); - -app.listen(port, () => console.log(`Listening on port ${port}`));