diff --git a/.gitignore b/.gitignore index 1aaff58..61d9aa3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,6 @@ build/ !build/.gitkeep node_modules/ coverage/ -config/local.json +config/local.yaml lib/ tsconfig.tsbuildinfo diff --git a/README.md b/README.md index 257de58..a3ffd10 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,18 @@ # Peer Calls -[![Build Status](https://travis-ci.org/jeremija/peer-calls.svg?branch=master)](https://travis-ci.org/jeremija/peer-calls) [![npm](https://img.shields.io/npm/v/peer-calls.svg)](https://www.npmjs.com/package/peer-calls) +[![Build Status][travis-badge]][travis] +[![NPM Package][npm-badge]][npm] + +[travis-badge]: https://travis-ci.org/jeremija/peer-calls.svg?branch=master +[travis]: https://travis-ci.org/jeremija/peer-calls +[npm-badge]: https://img.shields.io/npm/v/peer-calls.svg +[npm]: https://www.npmjs.com/package/peer-calls + +[peer-calls]: https://peercalls.com +[config]: https://raw.githubusercontent.com/jeremija/peer-calls/master/config/default.json WebRTC peer to peer calls for everyone. See it live in action at -[peercalls.com](https://peercalls.com). +[peercalls.com][peer-calls]. Work in progress. @@ -14,7 +23,16 @@ Work in progress. ## From npm -create directory `./peer-calls` and copy [config/default.json](https://raw.githubusercontent.com/jeremija/peer-calls/master/config/default.json) into it. +### Local install + +```bash +npm install peer-calls +mkdir config/ +curl -o config/local.json https://raw.githubusercontent.com/jeremija/peer-calls/master/config/default.json +./node_modules/.bin/peer-calls +``` + +create directory `./peer-calls` and copy [config/default.json][config] into it. ```bash npm install --global peer-calls diff --git a/config/custom-environment-variables.json b/config/custom-environment-variables.json deleted file mode 100644 index 836b406..0000000 --- a/config/custom-environment-variables.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "baseUrl": "PEERCALLS_BASE_URL", - "iceServers": "PEERCALLS_ICE_SERVERS" -} diff --git a/config/default.json b/config/default.json deleted file mode 100644 index 8934191..0000000 --- a/config/default.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "baseUrl": "", - "iceServers": [{ - "url": "stun:stun.l.google.com:19302", - "urls": "stun:stun.l.google.com:19302" - }] -} diff --git a/config/default.yaml b/config/default.yaml new file mode 100644 index 0000000..ed72ee5 --- /dev/null +++ b/config/default.yaml @@ -0,0 +1,5 @@ +--- +baseUrl: '' +iceServers: + - url: stun:stun.l.google.com:19302 + urls: stun:stun.l.google.com:19302 diff --git a/config/test.json b/config/test.json deleted file mode 100644 index 0967ef4..0000000 --- a/config/test.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/package-lock.json b/package-lock.json index b112f1f..2d8fb41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2368,12 +2368,6 @@ "integrity": "sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A==", "dev": true }, - "@types/config": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/@types/config/-/config-0.0.36.tgz", - "integrity": "sha512-EoAeT1MyFWh2BJvBDEFInY714bQBbHOAucqxqqhprhbBFqr+B7fuN5T9CJqUIGDzvwubnKKRqmSo6yPo0aSpNw==", - "dev": true - }, "@types/connect": { "version": "3.4.32", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", @@ -2466,6 +2460,12 @@ "jest-diff": "^24.3.0" } }, + "@types/js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA==", + "dev": true + }, "@types/json-schema": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", @@ -2787,7 +2787,6 @@ "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, "requires": { "mime-types": "~2.1.24", "negotiator": "0.6.2" @@ -2796,14 +2795,12 @@ "mime-db": { "version": "1.42.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", - "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", - "dev": true + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" }, "mime-types": { "version": "2.1.25", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", - "dev": true, "requires": { "mime-db": "1.42.0" } @@ -2871,8 +2868,7 @@ "after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" }, "ajv": { "version": "6.10.2", @@ -3289,7 +3285,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -3366,8 +3361,7 @@ "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", - "dev": true + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" }, "asap": { "version": "2.0.6", @@ -3454,8 +3448,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, "asynckit": { "version": "0.4.0", @@ -3847,8 +3840,7 @@ "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, "balanced-match": { "version": "1.0.0", @@ -3926,8 +3918,7 @@ "base64-arraybuffer": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" }, "base64-js": { "version": "1.3.0", @@ -3938,8 +3929,7 @@ "base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -3954,7 +3944,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, "requires": { "callsite": "1.0.0" } @@ -3968,8 +3957,7 @@ "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", - "dev": true + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" }, "block-stream": { "version": "0.0.9", @@ -4420,8 +4408,7 @@ "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" }, "callsites": { "version": "3.1.0", @@ -4761,20 +4748,17 @@ "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "component-inherit": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" }, "concat-map": { "version": "0.0.1", @@ -4794,14 +4778,6 @@ "typedarray": "^0.0.6" } }, - "config": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/config/-/config-3.2.4.tgz", - "integrity": "sha512-H1XIGfnU1EAkfjSLn9ZvYDRx9lOezDViuzLDgiJ/lMeqjYe3q6iQfpcLt2NInckJgpAeekbNhQkmnnbdEDs9rw==", - "requires": { - "json5": "^1.0.1" - } - }, "configstore": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", @@ -4879,8 +4855,7 @@ "cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, "cookie-signature": { "version": "1.0.6", @@ -5405,7 +5380,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz", "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==", - "dev": true, "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", @@ -5419,7 +5393,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz", "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==", - "dev": true, "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", @@ -5438,7 +5411,6 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", - "dev": true, "requires": { "async-limiter": "~1.0.0" } @@ -5449,7 +5421,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", - "dev": true, "requires": { "after": "0.8.2", "arraybuffer.slice": "~0.0.7", @@ -5916,8 +5887,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.0.1", @@ -7241,7 +7211,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "dev": true, "requires": { "isarray": "2.0.1" }, @@ -7249,16 +7218,14 @@ "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" } } }, "has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" }, "has-flag": { "version": "3.0.0", @@ -7558,8 +7525,7 @@ "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" }, "inflight": { "version": "1.0.6", @@ -9373,7 +9339,6 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -9499,14 +9464,6 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, - "json5": { - "version": "1.0.1", - "resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -9901,7 +9858,8 @@ "minimist": { "version": "1.2.0", "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true }, "mixin-deep": { "version": "1.3.2", @@ -10029,8 +9987,7 @@ "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "neo-async": { "version": "2.6.1", @@ -10274,8 +10231,7 @@ "object-component": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" }, "object-copy": { "version": "0.1.0", @@ -10646,7 +10602,6 @@ "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, "requires": { "better-assert": "~1.0.0" } @@ -10655,7 +10610,6 @@ "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, "requires": { "better-assert": "~1.0.0" } @@ -12477,7 +12431,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", - "dev": true, "requires": { "debug": "~4.1.0", "engine.io": "~3.4.0", @@ -12490,14 +12443,12 @@ "socket.io-adapter": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", - "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", - "dev": true + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" }, "socket.io-client": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", - "dev": true, "requires": { "backo2": "1.0.2", "base64-arraybuffer": "0.1.5", @@ -12518,14 +12469,12 @@ "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" }, "socket.io-parser": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", - "dev": true, "requires": { "component-emitter": "1.2.1", "debug": "~3.1.0", @@ -12536,7 +12485,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -12549,7 +12497,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz", "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==", - "dev": true, "requires": { "component-emitter": "1.2.1", "debug": "~4.1.0", @@ -12559,8 +12506,7 @@ "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" } } }, @@ -12651,8 +12597,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.15.2", @@ -13208,8 +13153,7 @@ "to-array": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" }, "to-arraybuffer": { "version": "1.0.1", @@ -14201,7 +14145,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz", "integrity": "sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg==", - "dev": true, "requires": { "async-limiter": "^1.0.0" } @@ -14221,8 +14164,7 @@ "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", - "dev": true + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" }, "xtend": { "version": "4.0.1", @@ -14315,8 +14257,7 @@ "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" }, "yn": { "version": "3.1.1", diff --git a/package.json b/package.json index 986795d..2afdad3 100644 --- a/package.json +++ b/package.json @@ -49,9 +49,9 @@ "author": "Jerko Steiner", "license": "MIT", "dependencies": { - "config": "^3.2.4", "debug": "^4.1.1", "express": "^4.17.1", + "js-yaml": "^3.13.1", "lodash": "^4.17.15", "pug": "^2.0.4", "socket.io": "^2.3.0", @@ -62,10 +62,10 @@ "@babel/polyfill": "^7.7.0", "@babel/preset-env": "^7.7.1", "@types/classnames": "^2.2.9", - "@types/config": "0.0.36", "@types/debug": "^4.1.5", "@types/express": "^4.17.2", "@types/jest": "^24.0.23", + "@types/js-yaml": "^3.12.1", "@types/lodash": "^4.14.148", "@types/node": "^12.12.8", "@types/react": "^16.9.11", diff --git a/src/server/app.test.ts b/src/server/app.test.ts index 8116b41..e10e3db 100644 --- a/src/server/app.test.ts +++ b/src/server/app.test.ts @@ -13,7 +13,7 @@ import request from 'supertest' const io = SocketIO() -const BASE_URL: string = config.get('baseUrl') +const BASE_URL: string = config.baseUrl describe('server/app', () => { diff --git a/src/server/app.ts b/src/server/app.ts index f3cba76..1761dd2 100644 --- a/src/server/app.ts +++ b/src/server/app.ts @@ -10,7 +10,7 @@ import index from './routes/index' const debug = _debug('peercalls') -const BASE_URL: string = config.get('baseUrl') +const BASE_URL: string = config.baseUrl const SOCKET_URL = `${BASE_URL}/ws` debug(`WebSocket URL: ${SOCKET_URL}`) @@ -19,6 +19,7 @@ const app = express() const server = createServer(config, app) const io = SocketIO(server, { path: SOCKET_URL }) +app.set('x-powered-by', false) app.locals.version = require('../../package.json').version app.locals.baseUrl = BASE_URL diff --git a/src/server/config.ts b/src/server/config.ts index c5c3772..f2cafae 100644 --- a/src/server/config.ts +++ b/src/server/config.ts @@ -1,4 +1,4 @@ -import cfg, { IConfig } from 'config' +import { readConfig } from './readConfig' export type ICEServer = { url: string @@ -17,10 +17,16 @@ export type ICEServer = { export interface Config { baseUrl: string iceServers: ICEServer[] - ssl: { + ssl?: { cert: string key: string } } -export const config = cfg as IConfig & Config +const cfg = readConfig() + +export const config: Config = { + baseUrl: cfg.get('baseUrl', ''), + iceServers: cfg.get('iceServers'), + ssl: cfg.get('ssl', undefined), +} diff --git a/src/server/readConfig/index.ts b/src/server/readConfig/index.ts new file mode 100644 index 0000000..6cb8aa6 --- /dev/null +++ b/src/server/readConfig/index.ts @@ -0,0 +1 @@ +export { ReadConfig, readConfig } from './readConfig' diff --git a/src/server/readConfig/readConfig.test.ts b/src/server/readConfig/readConfig.test.ts new file mode 100644 index 0000000..441eac7 --- /dev/null +++ b/src/server/readConfig/readConfig.test.ts @@ -0,0 +1,210 @@ +import { ReadConfig, getAllConfigLocations, getAllConfigFilesInDirectory, findPackageRoot, mergeConfig, readConfig, toSnakeCase } from './readConfig' +import { existsSync, mkdirSync, rmdirSync } from 'fs' +import { join } from 'path' + +describe('Config', () => { + + const config = new ReadConfig({ + a: 1, + b: { + c: 'test1', + d: 'test2', + }, + }) + + describe('get', () => { + it('reads config values recursively', () => { + expect(config.get('a')).toBe(1) + expect(config.get('b')).toEqual({c: 'test1', d: 'test2'}) + expect(config.get('b.c')).toBe('test1') + expect(config.get('b.d')).toBe('test2') + }) + it('throws an error when key does not exist', () => { + expect(() => config.get('e')).toThrowError(/does not exist/) + expect(() => config.get('e.f')).toThrowError(/does not exist/) + }) + it('returns a default value when provided', () => { + expect(config.get('b.c', 'test')).toBe('test1') + expect(config.get('e', 1)).toBe(1) + expect(config.get('e.f', 2)).toBe(2) + }) + }) + + describe('has', () => { + it('returns true when config property exists, false otherwise', () => { + expect(config.has('a')).toBe(true) + expect(config.has('b')).toBe(true) + expect(config.has('b.c')).toBe(true) + expect(config.has('b.d')).toBe(true) + expect(config.has('e')).toBe(false) + expect(config.has('e.f')).toBe(false) + }) + }) + +}) + +describe('findPackageRoot', () => { + const dir = join(__dirname, 'package.json') + beforeEach(() => { + if (existsSync(dir)) { + rmdirSync(dir) + } + mkdirSync(dir) + }) + afterEach(() => { + if (existsSync(dir)) { + rmdirSync(dir) + } + }) + it('finds package root folder', () => { + expect(findPackageRoot()).toEqual(jasmine.any(String)) + }) + it('finds package root folder', () => { + expect(findPackageRoot(__dirname)).toEqual(jasmine.any(String)) + }) + it('throws an error when not found', () => { + expect(() => findPackageRoot('/tmp')).toThrowError() + }) +}) + +describe('getAllConfigFilesInDirectory', () => { + it('returns default and local files', () => { + const files = getAllConfigFilesInDirectory('/test', undefined) + expect(files).toEqual([ + '/test/default.yaml', + '/test/local.yaml', + ]) + }) + it('returns default, environment, and local files', () => { + const files = getAllConfigFilesInDirectory('/test', 'test') + expect(files).toEqual([ + '/test/default.yaml', + '/test/test.yaml', + '/test/local.yaml', + ]) + }) +}) + +describe('getAllConfigLocations', () => { + it('returns package and local dirs when separate', () => { + const files = getAllConfigLocations('/test1', '/test2', 'test') + expect(files).toEqual([ + '/test1/default.yaml', + '/test1/test.yaml', + '/test1/local.yaml', + '/test2/default.yaml', + '/test2/test.yaml', + '/test2/local.yaml', + ]) + }) + + it('returns only package dir when local dir is same', () => { + const files = getAllConfigLocations('/test', '/test', 'test') + expect(files).toEqual([ + '/test/default.yaml', + '/test/test.yaml', + '/test/local.yaml', + ]) + }) + it('adds an extra config file', () => { + const files = getAllConfigLocations( + '/test', '/test', 'test', '/test/test-extra.yaml') + expect(files).toEqual([ + '/test/default.yaml', + '/test/test.yaml', + '/test/local.yaml', + '/test/test-extra.yaml', + ]) + }) + it('does not add extra config file when it is the same', () => { + const files = getAllConfigLocations( + '/test', '/test', 'test', '/test/test.yaml') + expect(files).toEqual([ + '/test/default.yaml', + '/test/test.yaml', + '/test/local.yaml', + ]) + }) +}) + +describe('toSnakeCase', () => { + it('converts uppercase, underscore-separated words to snake case', () => { + expect(toSnakeCase('TEST')).toBe('test') + expect(toSnakeCase('TEST_1')).toBe('test1') + expect(toSnakeCase('TEST_VALUE')).toBe('testValue') + expect(toSnakeCase('TEST_VALUE_TWO')).toBe('testValueTwo') + }) +}) + +describe('mergeConfig', () => { + it('merges source config into destination', () => { + const dest = { + a: 1, + b: [2], + c: { + d: 3, + }, + } + expect(mergeConfig({ + a: 4, + b: {value: 5}, + c: { + e: 6, + }, + }, dest)).toEqual({ + a: 4, + b: {value: 5}, + c: { + d: 3, + e: 6, + }, + }) + }) +}) + +describe('readConfig', () => { + + it('reads from a number of files', () => { + const result = readConfig() + expect(result).toBeInstanceOf(ReadConfig) + }) + + it('reads from an extra config file', () => { + }) + + describe('errors', () => { + const dir = join(__dirname, 'test.dir') + beforeEach(() => { + mkdirSync(dir) + }) + afterEach(() => { + rmdirSync(dir) + }) + it('fails on errors different than ENOENT', () => { + expect(() => readConfig( + process.env, + undefined, + undefined, + dir, + )).toThrowError(/EISDIR/) + }) + }) + + it('does not fail when no config files found', () => { + readConfig({}, '/tmp', '/tmp') + }) + + it('reads values from environment variables', () => { + const config = readConfig({ + PEERCALLS__TEST_VALUE__SUB_VALUE_1: '1', + PEERCALLS__TEST_VALUE__SUB_VALUE_2: JSON.stringify({a: 2}), + }, '/tmp', '/tmp') + expect(config.value()).toEqual({ + testValue: { + subValue1: 1, + subValue2: {a: 2}, + }, + }) + }) + +}) diff --git a/src/server/readConfig/readConfig.ts b/src/server/readConfig/readConfig.ts new file mode 100644 index 0000000..f3b7783 --- /dev/null +++ b/src/server/readConfig/readConfig.ts @@ -0,0 +1,182 @@ +/* eslint @typescript-eslint/no-explicit-any: 0 */ +import { readFileSync, statSync } from 'fs' +import { resolve, join } from 'path' +import { safeLoad } from 'js-yaml' +import _debug from 'debug' + +const debug = _debug('peercalls') + +const isObject = (value: unknown) => value !== null && typeof value === 'object' + +export class ReadConfig { + constructor(protected readonly config: any) {} + + get(key: string, defaultValue?: any) { + let value = this.config + try { + key.split('.').forEach(k => { + if (!Object.prototype.hasOwnProperty.call(value, k)) { + throw new Error(`Property "${k}" from "${key}" does not exist`) + } + value = value[k] + }) + } catch (err) { + if (arguments.length === 2) { + return defaultValue + } else { + throw err + } + } + return value + } + + has(key: string) { + let c = this.config + return key.split('.').every(k => { + const has = Object.prototype.hasOwnProperty.call(c, k) + if (has) { + c = c[k] + } + return has + }) + } + + value() { + return this.config + } + +} + +function readConfigFile(filename: string): any { + return safeLoad(readFileSync(filename, 'utf8')) +} + +export function mergeConfig(source: any, destination: any): any { + const stack = [{src: source, dest: destination}] + + while (stack.length) { + const {src, dest} = stack.pop()! + const keys = Object.keys(src) + + keys.forEach(key => { + const value = src[key] + if (isObject(value) && !Array.isArray(value)) { + if ( + !Object.prototype.hasOwnProperty.call(dest, key) || + Array.isArray(dest[key]) || + !isObject(dest[key]) + ) { + dest[key] = {} + } + stack.push({src: value, dest: dest[key]}) + return + } + dest[key] = value + }) + } + + return destination +} + +export function findPackageRoot(path = __dirname): string { + path = resolve(path) + let lastPath: undefined | string + while (lastPath !== path) { + const file = join(path, 'package.json') + try { + const result = statSync(file) + if (result.isFile()) { + return path + } + } catch (err) { + // ignore error + } + lastPath = path + path = join(path, '..') + } + throw new Error('No package.json found') +} + +export function getAllConfigFilesInDirectory( + dir: string, + environment: string | undefined, +): string[] { + const files: string[] = [join(dir, 'default.yaml')] + if (environment) { + files.push(join(dir, environment + '.yaml')) + } + files.push(join(dir, 'local.yaml')) + return files +} + +export function getAllConfigLocations( + packageDir: string, + localDir: string, + environment: string | undefined, + extraConfigFile?: string, +): string[] { + const locations: string[] = [ + ...getAllConfigFilesInDirectory(packageDir, environment), + ] + if (localDir !== packageDir) { + locations.push(...getAllConfigFilesInDirectory(localDir, environment)) + } + if (extraConfigFile && locations.every(loc => loc !== extraConfigFile)) { + locations.push(resolve(extraConfigFile)) + } + return locations +} + +export function toSnakeCase(string: string) { + const value = string.split('_') + .map(item => item[0].toUpperCase() + item.slice(1).toLowerCase()) + .join('') + return value[0].toLowerCase() + value.slice(1) +} + +export function readConfig( + env = process.env, + packageDir = join(findPackageRoot(), 'config'), + localDir = join(process.cwd(), 'config'), + extraConfigFile?: string, +) { + const locations = getAllConfigLocations( + packageDir, localDir, env.NODE_ENV, extraConfigFile) + + const readFiles = locations + .map(location => { + try { + const result = readConfigFile(location) + debug('Read config file: %s', location) + return result + } catch (err) { + if (!/ENOENT/.test(err.message)) { + throw err + } + return undefined + } + }) + .filter(item => item !== undefined) + + const config = readFiles + .reduce((merged, config) => mergeConfig(config, merged), {}) + + const envConfig: any = {} + Object.keys(env) + .filter(key => key.startsWith('PEERCALLS__')) + .forEach(key => { + const value = env[key]! + key = key.slice('PEERCALLS__'.length) + let cfg = envConfig + const keys = key.split('__').map(toSnakeCase) + const lastKey = keys[keys.length - 1] + keys + .slice(0, keys.length - 1) + .forEach(shortKey => { + cfg = cfg[shortKey] = cfg[shortKey] || {} + }) + cfg[lastKey] = JSON.parse(value) + }) + + return new ReadConfig(mergeConfig(envConfig, config)) +} diff --git a/src/server/routes/call.ts b/src/server/routes/call.ts index 8c74bbf..af307f5 100644 --- a/src/server/routes/call.ts +++ b/src/server/routes/call.ts @@ -1,12 +1,12 @@ -import { config, ICEServer } from '../config' +import { config } from '../config' import * as turn from '../turn' import { Router } from 'express' import { v4 } from 'uuid' const router = Router() -const BASE_URL: string = config.get('baseUrl') -const cfgIceServers = config.get('iceServers') as ICEServer[] +const BASE_URL: string = config.baseUrl +const cfgIceServers = config.iceServers router.get('/', (req, res) => { res.redirect(`${BASE_URL}/call/${v4()}`)