├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── package-lock.json ├── package.json ├── server.js └── web ├── .babelrc ├── .dockerignore ├── .eslintrc.json ├── .gitignore ├── .prettierrc.json ├── Dockerfile ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.css ├── App.test.tsx ├── App.tsx ├── Components │ └── Video │ │ └── index.tsx ├── index.css ├── index.tsx ├── logo.svg ├── react-app-env.d.ts ├── setupTests.ts └── types │ └── index.ts ├── tsconfig.json └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | README.md 4 | docker-compose.yml 5 | node_modules 6 | web -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .idea 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | RUN npm install -g nodemon 4 | RUN mkdir -p /usr/src/app 5 | WORKDIR /usr/src/app 6 | 7 | COPY package*.json ./ 8 | RUN npm install 9 | 10 | COPY . . 11 | 12 | EXPOSE 8080 13 | CMD [ "nodemon", "server.js" ] 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Seungmin Lee 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 | # Typescript-ReactJS-WebRTC-1-N-P2P 2 | 3 | ## Features 4 | - 1:N communication (peer to peer) 5 | - React with Typescript 6 | - Node.js as a Signaling server 7 | - Docker 8 | 9 | ## How to start 10 | 11 | ### 1. Docker version 12 | ```sh 13 | # server use 8080 port 14 | # web use 8085 port 15 | # You can connect to http://localhost:8085 16 | cd Typescript-ReactJS-WebRTC-1-N-P2P 17 | docker-compose up -d 18 | ``` 19 | 20 | ### 2. Non-Docker version(Node.js and React.js) 21 | You need to install Node.js 22 | ```sh 23 | cd Typescript-ReactJS-WebRTC-1-N-P2P 24 | npm install 25 | node server.js 26 | cd web 27 | npm install 28 | npm start 29 | ``` 30 | 31 | ### If you want to increase the total number of people change server.js file 32 | ```js 33 | // change maximum value 34 | 16 const maximum = 4; 35 | ``` 36 | 37 | ## Performance test 38 | 39 | ### 1. devices 40 | |device|description| 41 | |:--:|:--:| 42 | |CPU|AMD Ryzen 5 3600 6-Core Processor 3.59GHz| 43 | |RAM|32GB| 44 | |GPU|NVIDIA GeForce RTX 2060 SUPER| 45 | |OS|Windows 10 Pro| 46 | 47 | ### 2. Settings 48 | - all clients and server were in same devices 49 | - a video conference style 50 | 51 | ### Client side performance(Chrome browser CPU usage) 52 | |the number of users|P2P/Mesh(Signaling Server)|SFU(Media Server)| 53 | |:--:|:--:|:--:| 54 | |2|4%|5%| 55 | |3|10%|8%| 56 | |4|22%|9.5%| 57 | |5|34%|18%| 58 | |6|47%|25%| 59 | |7|64%|30%| 60 | |8|80%|30%| 61 | 62 | ### Server side performance(CPU usage) 63 | |the number of users|P2P/Mesh(Signaling Server)|SFU(Media Server)| 64 | |:--:|:--:|:--:| 65 | |2|0.1%|2%| 66 | |3|0.1%|13%| 67 | |4|0.1%|24%| 68 | |5|0.1%|32%| 69 | |6|0.1%|41%| 70 | |7|0.1%|48%| 71 | |8|0.1%|50%| 72 | 73 | ### 3. Results 74 | ### SFU Server (Media Server) 75 | - The SFU server has lost its real-time performance since it had 6 clients. 76 | - Some images were stopped when there were more than 7 clients. 77 | - SFU Server had significantly higher CPU usage than Signaling Server. 78 | - Client-side CPU usage decreased by approximately half when using SFU Server. 79 | 80 | ### Signaling Server (P2P/Mesh) 81 | - There was a slight delay as the client increased, but the video did not stop. 82 | - The CPU usage of the Signaling Server was kept at 0.1%. 83 | - As the number of clients increased, the CPU usage of the client increased significantly. 84 | 85 | --- 86 | 87 | ### Previous Upload was... 88 | - 1:1 (peer to peer) WebRTC https://github.com/Seung3837/Typescript-react-webrtc-1-1 89 | 90 | ### More Details... 91 | - [Korean](https://millo-l.github.io/WebRTC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-1-N-P2P/) 92 | - [English](https://millo-l.github.io/Implementing-WebRTC-using-ReactJS-and-Typescript-1-N-P2P/) 93 | 94 | ### Next Upload is... 95 | - https://github.com/millo-L/Typescript-ReactJS-WebRTC-1-N-SFU 96 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | services: 3 | server: 4 | build: . 5 | environment: 6 | - PORT=8080 7 | - MAXIMUM=4 8 | ports: 9 | - "8080:8080" 10 | networks: 11 | - docker-net 12 | web: 13 | build: 14 | context: ./web 15 | stdin_open: true 16 | tty: true 17 | ports: 18 | - "8085:5000" 19 | networks: 20 | - docker-net 21 | networks: 22 | docker-net: 23 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket_server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "after": { 17 | "version": "0.8.2", 18 | "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", 19 | "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" 20 | }, 21 | "array-flatten": { 22 | "version": "1.1.1", 23 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 24 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 25 | }, 26 | "arraybuffer.slice": { 27 | "version": "0.0.7", 28 | "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", 29 | "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" 30 | }, 31 | "async-limiter": { 32 | "version": "1.0.1", 33 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 34 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" 35 | }, 36 | "backo2": { 37 | "version": "1.0.2", 38 | "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", 39 | "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" 40 | }, 41 | "base64-arraybuffer": { 42 | "version": "0.1.4", 43 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", 44 | "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" 45 | }, 46 | "base64id": { 47 | "version": "2.0.0", 48 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", 49 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" 50 | }, 51 | "better-assert": { 52 | "version": "1.0.2", 53 | "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", 54 | "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", 55 | "requires": { 56 | "callsite": "1.0.0" 57 | } 58 | }, 59 | "blob": { 60 | "version": "0.0.5", 61 | "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", 62 | "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" 63 | }, 64 | "body-parser": { 65 | "version": "1.19.0", 66 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 67 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 68 | "requires": { 69 | "bytes": "3.1.0", 70 | "content-type": "~1.0.4", 71 | "debug": "2.6.9", 72 | "depd": "~1.1.2", 73 | "http-errors": "1.7.2", 74 | "iconv-lite": "0.4.24", 75 | "on-finished": "~2.3.0", 76 | "qs": "6.7.0", 77 | "raw-body": "2.4.0", 78 | "type-is": "~1.6.17" 79 | }, 80 | "dependencies": { 81 | "debug": { 82 | "version": "2.6.9", 83 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 84 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 85 | "requires": { 86 | "ms": "2.0.0" 87 | } 88 | }, 89 | "ms": { 90 | "version": "2.0.0", 91 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 92 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 93 | } 94 | } 95 | }, 96 | "bytes": { 97 | "version": "3.1.0", 98 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 99 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 100 | }, 101 | "callsite": { 102 | "version": "1.0.0", 103 | "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", 104 | "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" 105 | }, 106 | "component-bind": { 107 | "version": "1.0.0", 108 | "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", 109 | "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" 110 | }, 111 | "component-emitter": { 112 | "version": "1.2.1", 113 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", 114 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" 115 | }, 116 | "component-inherit": { 117 | "version": "0.0.3", 118 | "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", 119 | "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" 120 | }, 121 | "content-disposition": { 122 | "version": "0.5.3", 123 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 124 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 125 | "requires": { 126 | "safe-buffer": "5.1.2" 127 | } 128 | }, 129 | "content-type": { 130 | "version": "1.0.4", 131 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 132 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 133 | }, 134 | "cookie": { 135 | "version": "0.3.1", 136 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 137 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 138 | }, 139 | "cookie-signature": { 140 | "version": "1.0.6", 141 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 142 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 143 | }, 144 | "cors": { 145 | "version": "2.8.5", 146 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 147 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 148 | "requires": { 149 | "object-assign": "^4", 150 | "vary": "^1" 151 | } 152 | }, 153 | "debug": { 154 | "version": "4.1.1", 155 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 156 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 157 | "requires": { 158 | "ms": "^2.1.1" 159 | } 160 | }, 161 | "depd": { 162 | "version": "1.1.2", 163 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 164 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 165 | }, 166 | "destroy": { 167 | "version": "1.0.4", 168 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 169 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 170 | }, 171 | "ee-first": { 172 | "version": "1.1.1", 173 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 174 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 175 | }, 176 | "encodeurl": { 177 | "version": "1.0.2", 178 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 179 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 180 | }, 181 | "engine.io": { 182 | "version": "3.4.2", 183 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", 184 | "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", 185 | "requires": { 186 | "accepts": "~1.3.4", 187 | "base64id": "2.0.0", 188 | "cookie": "0.3.1", 189 | "debug": "~4.1.0", 190 | "engine.io-parser": "~2.2.0", 191 | "ws": "^7.1.2" 192 | } 193 | }, 194 | "engine.io-client": { 195 | "version": "3.4.4", 196 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz", 197 | "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==", 198 | "requires": { 199 | "component-emitter": "~1.3.0", 200 | "component-inherit": "0.0.3", 201 | "debug": "~3.1.0", 202 | "engine.io-parser": "~2.2.0", 203 | "has-cors": "1.1.0", 204 | "indexof": "0.0.1", 205 | "parseqs": "0.0.6", 206 | "parseuri": "0.0.6", 207 | "ws": "~6.1.0", 208 | "xmlhttprequest-ssl": "~1.5.4", 209 | "yeast": "0.1.2" 210 | }, 211 | "dependencies": { 212 | "component-emitter": { 213 | "version": "1.3.0", 214 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", 215 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" 216 | }, 217 | "debug": { 218 | "version": "3.1.0", 219 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 220 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 221 | "requires": { 222 | "ms": "2.0.0" 223 | } 224 | }, 225 | "ms": { 226 | "version": "2.0.0", 227 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 228 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 229 | }, 230 | "parseqs": { 231 | "version": "0.0.6", 232 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", 233 | "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" 234 | }, 235 | "parseuri": { 236 | "version": "0.0.6", 237 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", 238 | "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" 239 | }, 240 | "ws": { 241 | "version": "6.1.4", 242 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", 243 | "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", 244 | "requires": { 245 | "async-limiter": "~1.0.0" 246 | } 247 | } 248 | } 249 | }, 250 | "engine.io-parser": { 251 | "version": "2.2.1", 252 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", 253 | "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", 254 | "requires": { 255 | "after": "0.8.2", 256 | "arraybuffer.slice": "~0.0.7", 257 | "base64-arraybuffer": "0.1.4", 258 | "blob": "0.0.5", 259 | "has-binary2": "~1.0.2" 260 | } 261 | }, 262 | "escape-html": { 263 | "version": "1.0.3", 264 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 265 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 266 | }, 267 | "etag": { 268 | "version": "1.8.1", 269 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 270 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 271 | }, 272 | "express": { 273 | "version": "4.17.1", 274 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 275 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 276 | "requires": { 277 | "accepts": "~1.3.7", 278 | "array-flatten": "1.1.1", 279 | "body-parser": "1.19.0", 280 | "content-disposition": "0.5.3", 281 | "content-type": "~1.0.4", 282 | "cookie": "0.4.0", 283 | "cookie-signature": "1.0.6", 284 | "debug": "2.6.9", 285 | "depd": "~1.1.2", 286 | "encodeurl": "~1.0.2", 287 | "escape-html": "~1.0.3", 288 | "etag": "~1.8.1", 289 | "finalhandler": "~1.1.2", 290 | "fresh": "0.5.2", 291 | "merge-descriptors": "1.0.1", 292 | "methods": "~1.1.2", 293 | "on-finished": "~2.3.0", 294 | "parseurl": "~1.3.3", 295 | "path-to-regexp": "0.1.7", 296 | "proxy-addr": "~2.0.5", 297 | "qs": "6.7.0", 298 | "range-parser": "~1.2.1", 299 | "safe-buffer": "5.1.2", 300 | "send": "0.17.1", 301 | "serve-static": "1.14.1", 302 | "setprototypeof": "1.1.1", 303 | "statuses": "~1.5.0", 304 | "type-is": "~1.6.18", 305 | "utils-merge": "1.0.1", 306 | "vary": "~1.1.2" 307 | }, 308 | "dependencies": { 309 | "cookie": { 310 | "version": "0.4.0", 311 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 312 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 313 | }, 314 | "debug": { 315 | "version": "2.6.9", 316 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 317 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 318 | "requires": { 319 | "ms": "2.0.0" 320 | } 321 | }, 322 | "ms": { 323 | "version": "2.0.0", 324 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 325 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 326 | } 327 | } 328 | }, 329 | "finalhandler": { 330 | "version": "1.1.2", 331 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 332 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 333 | "requires": { 334 | "debug": "2.6.9", 335 | "encodeurl": "~1.0.2", 336 | "escape-html": "~1.0.3", 337 | "on-finished": "~2.3.0", 338 | "parseurl": "~1.3.3", 339 | "statuses": "~1.5.0", 340 | "unpipe": "~1.0.0" 341 | }, 342 | "dependencies": { 343 | "debug": { 344 | "version": "2.6.9", 345 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 346 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 347 | "requires": { 348 | "ms": "2.0.0" 349 | } 350 | }, 351 | "ms": { 352 | "version": "2.0.0", 353 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 354 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 355 | } 356 | } 357 | }, 358 | "forwarded": { 359 | "version": "0.1.2", 360 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 361 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 362 | }, 363 | "fresh": { 364 | "version": "0.5.2", 365 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 366 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 367 | }, 368 | "has-binary2": { 369 | "version": "1.0.3", 370 | "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", 371 | "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", 372 | "requires": { 373 | "isarray": "2.0.1" 374 | } 375 | }, 376 | "has-cors": { 377 | "version": "1.1.0", 378 | "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", 379 | "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" 380 | }, 381 | "http": { 382 | "version": "0.0.1-security", 383 | "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", 384 | "integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==" 385 | }, 386 | "http-errors": { 387 | "version": "1.7.2", 388 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 389 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 390 | "requires": { 391 | "depd": "~1.1.2", 392 | "inherits": "2.0.3", 393 | "setprototypeof": "1.1.1", 394 | "statuses": ">= 1.5.0 < 2", 395 | "toidentifier": "1.0.0" 396 | } 397 | }, 398 | "iconv-lite": { 399 | "version": "0.4.24", 400 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 401 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 402 | "requires": { 403 | "safer-buffer": ">= 2.1.2 < 3" 404 | } 405 | }, 406 | "indexof": { 407 | "version": "0.0.1", 408 | "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", 409 | "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" 410 | }, 411 | "inherits": { 412 | "version": "2.0.3", 413 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 414 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 415 | }, 416 | "ipaddr.js": { 417 | "version": "1.9.1", 418 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 419 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 420 | }, 421 | "isarray": { 422 | "version": "2.0.1", 423 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", 424 | "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" 425 | }, 426 | "media-typer": { 427 | "version": "0.3.0", 428 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 429 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 430 | }, 431 | "merge-descriptors": { 432 | "version": "1.0.1", 433 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 434 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 435 | }, 436 | "methods": { 437 | "version": "1.1.2", 438 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 439 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 440 | }, 441 | "mime": { 442 | "version": "1.6.0", 443 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 444 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 445 | }, 446 | "mime-db": { 447 | "version": "1.44.0", 448 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 449 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" 450 | }, 451 | "mime-types": { 452 | "version": "2.1.27", 453 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 454 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 455 | "requires": { 456 | "mime-db": "1.44.0" 457 | } 458 | }, 459 | "ms": { 460 | "version": "2.1.2", 461 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 462 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 463 | }, 464 | "negotiator": { 465 | "version": "0.6.2", 466 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 467 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 468 | }, 469 | "object-assign": { 470 | "version": "4.1.1", 471 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 472 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 473 | }, 474 | "object-component": { 475 | "version": "0.0.3", 476 | "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", 477 | "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" 478 | }, 479 | "on-finished": { 480 | "version": "2.3.0", 481 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 482 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 483 | "requires": { 484 | "ee-first": "1.1.1" 485 | } 486 | }, 487 | "parseqs": { 488 | "version": "0.0.5", 489 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", 490 | "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", 491 | "requires": { 492 | "better-assert": "~1.0.0" 493 | } 494 | }, 495 | "parseuri": { 496 | "version": "0.0.5", 497 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", 498 | "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", 499 | "requires": { 500 | "better-assert": "~1.0.0" 501 | } 502 | }, 503 | "parseurl": { 504 | "version": "1.3.3", 505 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 506 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 507 | }, 508 | "path-to-regexp": { 509 | "version": "0.1.7", 510 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 511 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 512 | }, 513 | "proxy-addr": { 514 | "version": "2.0.6", 515 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 516 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 517 | "requires": { 518 | "forwarded": "~0.1.2", 519 | "ipaddr.js": "1.9.1" 520 | } 521 | }, 522 | "qs": { 523 | "version": "6.7.0", 524 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 525 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 526 | }, 527 | "range-parser": { 528 | "version": "1.2.1", 529 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 530 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 531 | }, 532 | "raw-body": { 533 | "version": "2.4.0", 534 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 535 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 536 | "requires": { 537 | "bytes": "3.1.0", 538 | "http-errors": "1.7.2", 539 | "iconv-lite": "0.4.24", 540 | "unpipe": "1.0.0" 541 | } 542 | }, 543 | "safe-buffer": { 544 | "version": "5.1.2", 545 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 546 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 547 | }, 548 | "safer-buffer": { 549 | "version": "2.1.2", 550 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 551 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 552 | }, 553 | "send": { 554 | "version": "0.17.1", 555 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 556 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 557 | "requires": { 558 | "debug": "2.6.9", 559 | "depd": "~1.1.2", 560 | "destroy": "~1.0.4", 561 | "encodeurl": "~1.0.2", 562 | "escape-html": "~1.0.3", 563 | "etag": "~1.8.1", 564 | "fresh": "0.5.2", 565 | "http-errors": "~1.7.2", 566 | "mime": "1.6.0", 567 | "ms": "2.1.1", 568 | "on-finished": "~2.3.0", 569 | "range-parser": "~1.2.1", 570 | "statuses": "~1.5.0" 571 | }, 572 | "dependencies": { 573 | "debug": { 574 | "version": "2.6.9", 575 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 576 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 577 | "requires": { 578 | "ms": "2.0.0" 579 | }, 580 | "dependencies": { 581 | "ms": { 582 | "version": "2.0.0", 583 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 584 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 585 | } 586 | } 587 | }, 588 | "ms": { 589 | "version": "2.1.1", 590 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 591 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 592 | } 593 | } 594 | }, 595 | "serve-static": { 596 | "version": "1.14.1", 597 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 598 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 599 | "requires": { 600 | "encodeurl": "~1.0.2", 601 | "escape-html": "~1.0.3", 602 | "parseurl": "~1.3.3", 603 | "send": "0.17.1" 604 | } 605 | }, 606 | "setprototypeof": { 607 | "version": "1.1.1", 608 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 609 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 610 | }, 611 | "socket.io": { 612 | "version": "2.3.0", 613 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", 614 | "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", 615 | "requires": { 616 | "debug": "~4.1.0", 617 | "engine.io": "~3.4.0", 618 | "has-binary2": "~1.0.2", 619 | "socket.io-adapter": "~1.1.0", 620 | "socket.io-client": "2.3.0", 621 | "socket.io-parser": "~3.4.0" 622 | } 623 | }, 624 | "socket.io-adapter": { 625 | "version": "1.1.2", 626 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", 627 | "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" 628 | }, 629 | "socket.io-client": { 630 | "version": "2.3.0", 631 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", 632 | "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", 633 | "requires": { 634 | "backo2": "1.0.2", 635 | "base64-arraybuffer": "0.1.5", 636 | "component-bind": "1.0.0", 637 | "component-emitter": "1.2.1", 638 | "debug": "~4.1.0", 639 | "engine.io-client": "~3.4.0", 640 | "has-binary2": "~1.0.2", 641 | "has-cors": "1.1.0", 642 | "indexof": "0.0.1", 643 | "object-component": "0.0.3", 644 | "parseqs": "0.0.5", 645 | "parseuri": "0.0.5", 646 | "socket.io-parser": "~3.3.0", 647 | "to-array": "0.1.4" 648 | }, 649 | "dependencies": { 650 | "base64-arraybuffer": { 651 | "version": "0.1.5", 652 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", 653 | "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" 654 | }, 655 | "ms": { 656 | "version": "2.0.0", 657 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 658 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 659 | }, 660 | "socket.io-parser": { 661 | "version": "3.3.1", 662 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz", 663 | "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==", 664 | "requires": { 665 | "component-emitter": "~1.3.0", 666 | "debug": "~3.1.0", 667 | "isarray": "2.0.1" 668 | }, 669 | "dependencies": { 670 | "component-emitter": { 671 | "version": "1.3.0", 672 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", 673 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" 674 | }, 675 | "debug": { 676 | "version": "3.1.0", 677 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 678 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 679 | "requires": { 680 | "ms": "2.0.0" 681 | } 682 | } 683 | } 684 | } 685 | } 686 | }, 687 | "socket.io-parser": { 688 | "version": "3.4.1", 689 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", 690 | "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==", 691 | "requires": { 692 | "component-emitter": "1.2.1", 693 | "debug": "~4.1.0", 694 | "isarray": "2.0.1" 695 | } 696 | }, 697 | "statuses": { 698 | "version": "1.5.0", 699 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 700 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 701 | }, 702 | "to-array": { 703 | "version": "0.1.4", 704 | "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", 705 | "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" 706 | }, 707 | "toidentifier": { 708 | "version": "1.0.0", 709 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 710 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 711 | }, 712 | "type-is": { 713 | "version": "1.6.18", 714 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 715 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 716 | "requires": { 717 | "media-typer": "0.3.0", 718 | "mime-types": "~2.1.24" 719 | } 720 | }, 721 | "unpipe": { 722 | "version": "1.0.0", 723 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 724 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 725 | }, 726 | "utils-merge": { 727 | "version": "1.0.1", 728 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 729 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 730 | }, 731 | "vary": { 732 | "version": "1.1.2", 733 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 734 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 735 | }, 736 | "ws": { 737 | "version": "7.4.0", 738 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", 739 | "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==" 740 | }, 741 | "xmlhttprequest-ssl": { 742 | "version": "1.5.5", 743 | "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", 744 | "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" 745 | }, 746 | "yeast": { 747 | "version": "0.1.2", 748 | "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", 749 | "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" 750 | } 751 | } 752 | } 753 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket_server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cors": "2.8.5", 14 | "express": "4.17.1", 15 | "http": "0.0.1-security", 16 | "socket.io": "2.3.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | let express = require('express'); 2 | let http = require('http'); 3 | let app = express(); 4 | let cors = require('cors'); 5 | let server = http.createServer(app); 6 | let socketio = require('socket.io'); 7 | let io = socketio.listen(server); 8 | 9 | app.use(cors()); 10 | const PORT = process.env.PORT || 8080; 11 | 12 | let users = {}; 13 | 14 | let socketToRoom = {}; 15 | 16 | const maximum = process.env.MAXIMUM || 4; 17 | 18 | io.on('connection', socket => { 19 | socket.on('join_room', data => { 20 | if (users[data.room]) { 21 | const length = users[data.room].length; 22 | if (length === maximum) { 23 | socket.to(socket.id).emit('room_full'); 24 | return; 25 | } 26 | users[data.room].push({id: socket.id, email: data.email}); 27 | } else { 28 | users[data.room] = [{id: socket.id, email: data.email}]; 29 | } 30 | socketToRoom[socket.id] = data.room; 31 | 32 | socket.join(data.room); 33 | console.log(`[${socketToRoom[socket.id]}]: ${socket.id} enter`); 34 | 35 | const usersInThisRoom = users[data.room].filter(user => user.id !== socket.id); 36 | 37 | console.log(usersInThisRoom); 38 | 39 | io.sockets.to(socket.id).emit('all_users', usersInThisRoom); 40 | }); 41 | 42 | socket.on('offer', data => { 43 | //console.log(data.sdp); 44 | socket.to(data.offerReceiveID).emit('getOffer', {sdp: data.sdp, offerSendID: data.offerSendID, offerSendEmail: data.offerSendEmail}); 45 | }); 46 | 47 | socket.on('answer', data => { 48 | //console.log(data.sdp); 49 | socket.to(data.answerReceiveID).emit('getAnswer', {sdp: data.sdp, answerSendID: data.answerSendID}); 50 | }); 51 | 52 | socket.on('candidate', data => { 53 | //console.log(data.candidate); 54 | socket.to(data.candidateReceiveID).emit('getCandidate', {candidate: data.candidate, candidateSendID: data.candidateSendID}); 55 | }) 56 | 57 | socket.on('disconnect', () => { 58 | console.log(`[${socketToRoom[socket.id]}]: ${socket.id} exit`); 59 | const roomID = socketToRoom[socket.id]; 60 | let room = users[roomID]; 61 | if (room) { 62 | room = room.filter(user => user.id !== socket.id); 63 | users[roomID] = room; 64 | if (room.length === 0) { 65 | delete users[roomID]; 66 | return; 67 | } 68 | } 69 | socket.to(roomID).emit('user_exit', {id: socket.id}); 70 | console.log(users); 71 | }) 72 | }); 73 | 74 | server.listen(PORT, () => { 75 | console.log(`server running on ${PORT}`); 76 | }); -------------------------------------------------------------------------------- /web/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "babel-preset" 4 | ], 5 | "plugins": [ 6 | "transform-react-jsx-source", 7 | [ 8 | "module-resolver", 9 | { 10 | "alias": { 11 | "~/*": "./src" 12 | }, 13 | "cwd": "babelrc" 14 | } 15 | ] 16 | ] 17 | } -------------------------------------------------------------------------------- /web/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | README.md 4 | node_modules -------------------------------------------------------------------------------- /web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": [ 4 | "react", 5 | "react-hooks", 6 | "@typescript-eslint", 7 | "prettier" 8 | ], 9 | "extends": [ 10 | "plugin:react/recommended", 11 | "plugin:jsx-a11y/recommended", 12 | "plugin:import/errors", 13 | "plugin:import/warnings", 14 | "plugin:@typescript-eslint/recommended", 15 | "airbnb", 16 | "airbnb/hooks", 17 | "prettier" 18 | ], 19 | "rules": { 20 | "react/jsx-filename-extension": [1, { "extensions": [".tsx", ".ts"] }], 21 | "@typescript-eslint/camelcase": 0, 22 | "@typescript-eslint/no-explicit-any": 0, 23 | "@typescript-eslint/explicit-function-return-type": 0, 24 | "camelcase": [0, {"properties": "always"}], 25 | "import/extensions": 0, 26 | "import/no-extraneous-dependencies": [2, {"devDependencies": true}], 27 | "no-param-reassign": [2, { "props": false }], 28 | "react/prop-types": 0, 29 | "no-shadow": 0, 30 | "react-hooks/exhaustive-deps": 1, 31 | "no-underscore-dangle": 0, 32 | "jsx-a11y/label-has-associated-control": 1, 33 | "jsx-a11y/accessible-emoji": 0, 34 | "no-nested-ternary": 0, 35 | "react/no-array-index-key": 1, 36 | "prefer-promise-reject-errors": 1, 37 | "no-use-before-define": "off", 38 | "@typescript-eslint/no-use-before-define": ["error"], 39 | "import/prefer-default-export": 1, 40 | "@typescript-eslint/no-empty-function": 1, 41 | "global-require": 0, 42 | "no-unused-expressions":1, 43 | "react/destructuring-assignment":0, 44 | "react/require-default-props" :0, 45 | "react/no-unused-prop-types":1, 46 | "import/no-unresolved": [ 47 | "error", 48 | {"caseSensitive": false} 49 | ], 50 | 51 | "react-hooks/rules-of-hooks":1, 52 | "@typescript-eslint/no-unused-vars":2, 53 | "react/no-children-prop":1, 54 | "no-undef": "warn", 55 | "no-alert": "off", 56 | "no-console": "off", 57 | "@typescript-eslint/explicit-module-boundary-types": "off" 58 | }, 59 | "overrides": [ 60 | { 61 | "files": ["**/*.test.tsx"], 62 | "rules": { 63 | "react/jsx-props-no-spreading": 0 64 | } 65 | } 66 | ], 67 | "settings": { 68 | "import/resolver": { 69 | "node": { 70 | "extensions": [".js", ".jsx", ".ts", ".tsx"] 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /web/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "parser": "typescript", 4 | "trailingComma": "all", 5 | "semi": true, 6 | "useTabs": true, 7 | "tabWidth": 4, 8 | "printWidth": 100, 9 | "endOfLine": "lf", 10 | "jsxSingleQuote": false, 11 | "proseWrap": "preserve", 12 | "quoteProps": "as-needed", 13 | "arrowParens": "always", 14 | "bracketSpacing": true 15 | } 16 | -------------------------------------------------------------------------------- /web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | RUN mkdir -p /usr/src/app 4 | WORKDIR /usr/src/app 5 | 6 | ENV PATH /usr/src/app/node_modules/.bin:$PATH 7 | 8 | COPY package*.json ./ 9 | RUN npm install 10 | 11 | COPY . . 12 | 13 | RUN npm run build 14 | RUN npm install -g serve -y 15 | 16 | EXPOSE 5000 17 | CMD [ "npx", "serve", "-s", "build" ] 18 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `yarn test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `yarn build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `yarn eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "4.11.0", 7 | "@testing-library/jest-dom": "^4.2.4", 8 | "@testing-library/react": "^9.3.2", 9 | "@testing-library/user-event": "^7.1.2", 10 | "@types/jest": "^24.0.0", 11 | "@types/material-ui": "0.21.8", 12 | "@types/node": "^12.0.0", 13 | "@types/react": "^16.9.0", 14 | "@types/react-custom-scrollbars": "4.0.7", 15 | "@types/react-dom": "^16.9.0", 16 | "@types/react-redux": "7.1.9", 17 | "@types/react-router-dom": "^5.1.6", 18 | "@types/redux": "3.6.0", 19 | "@types/simple-peer": "9.6.1", 20 | "@types/socket.io-client": "1.4.34", 21 | "@types/styled-components": "5.1.3", 22 | "@types/uuid": "8.3.0", 23 | "axios": "0.21.0", 24 | "jwt-decode": "3.1.2", 25 | "moment": "2.29.0", 26 | "react": "^16.13.1", 27 | "react-custom-scrollbars": "4.2.1", 28 | "react-dom": "^16.13.1", 29 | "react-icons": "3.11.0", 30 | "react-redux": "7.2.1", 31 | "react-router-dom": "^5.2.0", 32 | "react-scripts": "3.4.3", 33 | "redux": "4.0.5", 34 | "simple-peer": "9.6.2", 35 | "socket.io-client": "2.3.0", 36 | "styled-components": "5.2.0", 37 | "typescript": "~3.7.2", 38 | "uuid": "8.3.1" 39 | }, 40 | "scripts": { 41 | "start": "react-scripts start", 42 | "build": "react-scripts build", 43 | "test": "react-scripts test", 44 | "eject": "react-scripts eject" 45 | }, 46 | "eslintConfig": { 47 | "extends": "react-app" 48 | }, 49 | "browserslist": { 50 | "production": [ 51 | ">0.2%", 52 | "not dead", 53 | "not op_mini all" 54 | ], 55 | "development": [ 56 | "last 1 chrome version", 57 | "last 1 firefox version", 58 | "last 1 safari version" 59 | ] 60 | }, 61 | "devDependencies": { 62 | "@babel/preset-react": "7.10.4", 63 | "@typescript-eslint/eslint-plugin": "^4.31.0", 64 | "@typescript-eslint/parser": "^4.31.0", 65 | "babel-plugin-module-resolver": "4.0.0", 66 | "eslint": "^6.6.0", 67 | "eslint-config-airbnb": "18.2.1", 68 | "eslint-config-prettier": "^8.3.0", 69 | "eslint-import-resolver-typescript": "^2.4.0", 70 | "eslint-plugin-import": "^2.22.1", 71 | "eslint-plugin-jsx-a11y": "^6.4.1", 72 | "eslint-plugin-prettier": "^4.0.0", 73 | "eslint-plugin-react": "^7.21.5", 74 | "eslint-plugin-react-hooks": "^1.7.0", 75 | "prettier": "^2.4.1" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/millo-L/Typescript-ReactJS-WebRTC-1-N-P2P/1e4875a58b846e9921ef17dd91af415e98dfd045/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 |