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}`));