From f8474994f7d8b042f14c05fa44b90664a25ca1de Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Sun, 6 Dec 2020 15:09:46 +0100 Subject: [PATCH 01/17] Linting and remove use of 'any' --- src/index.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/index.ts b/src/index.ts index a12ec3a..5fc3470 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,16 +22,19 @@ interface Signal { to: string; } -app.set('view engine', 'pug') -app.use(morgan('combined')) -app.use(express.static('offsets')) +interface SetIDsPacket { + [key: string]: number +} + +app.set('view engine', 'pug'); +app.use(morgan('combined')); +app.use(express.static('offsets')); let connectionCount = 0; let address = 'loading...'; app.get('/', (req, res) => { res.render('index', { connectionCount, address }); -}) - +}); io.on('connection', (socket: socketIO.Socket) => { connectionCount++; @@ -48,9 +51,9 @@ io.on('connection', (socket: socketIO.Socket) => { socket.join(code); socket.to(code).broadcast.emit('join', socket.id, id); - let socketsInLobby = Object.keys(io.sockets.adapter.rooms[code].sockets); - let ids: any = {}; - for (let s of socketsInLobby) { + const socketsInLobby = Object.keys(io.sockets.adapter.rooms[code].sockets); + const ids: SetIDsPacket = {}; + for (const s of socketsInLobby) { if (s !== socket.id) ids[s] = playerIds.get(s); } @@ -65,12 +68,12 @@ io.on('connection', (socket: socketIO.Socket) => { } playerIds.set(socket.id, id); socket.to(code).broadcast.emit('setId', socket.id, id); - }) + }); socket.on('leave', () => { if (code) socket.leave(code); - }) + }); socket.on('signal', (signal: Signal) => { if (typeof signal !== 'object' || !signal.data || !signal.to || typeof signal.to !== 'string') { @@ -88,9 +91,8 @@ io.on('connection', (socket: socketIO.Socket) => { socket.on('disconnect', () => { connectionCount--; logger.info("Total connected: %d", connectionCount); - }) - -}) + }); +}); server.listen(port); (async () => { From 6d80d7bbc65558267c5afd1f1507601c24d88ad3 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Sun, 6 Dec 2020 19:28:12 +0100 Subject: [PATCH 02/17] Send custom ICE config to clients --- .gitignore | 4 +++- config/ice-servers.example.json | 13 +++++++++++++ src/ice.ts | 23 +++++++++++++++++++++++ src/index.ts | 3 +++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 config/ice-servers.example.json create mode 100644 src/ice.ts diff --git a/.gitignore b/.gitignore index 763301f..2f413fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ dist/ -node_modules/ \ No newline at end of file +node_modules/ +config/* +!config/*.example.json \ No newline at end of file diff --git a/config/ice-servers.example.json b/config/ice-servers.example.json new file mode 100644 index 0000000..6be0077 --- /dev/null +++ b/config/ice-servers.example.json @@ -0,0 +1,13 @@ +{ + "iceTransportPolicy": "relay", + "iceServers": [ + { + "urls": "stun:stun.l.google.com:19302" + }, + { + "urls": "turn:example.com", + "username": "TurnUsername", + "credential": "TurnCredential" + } + ] +} \ No newline at end of file diff --git a/src/ice.ts b/src/ice.ts new file mode 100644 index 0000000..4da051e --- /dev/null +++ b/src/ice.ts @@ -0,0 +1,23 @@ +import fs from 'fs'; +import path from 'path'; + +const ICE_CONFIG_PATH = path.join(__dirname, '..', 'config', 'ice-servers.json'); +const DEFAULT_ICE_CONFIG = { + iceServers: [ + { + urls: 'stun:stun.l.google.com:19302' + } + ] +}; + +let iceConfig: RTCConfiguration = DEFAULT_ICE_CONFIG; + +if (fs.existsSync(ICE_CONFIG_PATH)) { + try { + iceConfig = JSON.parse(fs.readFileSync(ICE_CONFIG_PATH).toString('utf8')); + } catch (err) { + console.error(`Unable to load ICE server config file. Make sure it is valid JSON.\n${err}`); + } +} + +export default iceConfig; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 5fc3470..54e0ec9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import socketIO from 'socket.io'; import Tracer from 'tracer'; import morgan from 'morgan'; import publicIp from 'public-ip'; +import iceConfig from './ice'; const port = parseInt(process.env.PORT || '9736'); @@ -41,6 +42,8 @@ io.on('connection', (socket: socketIO.Socket) => { logger.info("Total connected: %d", connectionCount); let code: string | null = null; + socket.emit('iceConfig', iceConfig); + socket.on('join', (c: string, id: number) => { if (typeof c !== 'string' || typeof id !== 'number') { socket.disconnect(); From 038c3df53ddbade8ce911f1514d787ba7dd63871 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Mon, 7 Dec 2020 01:26:55 +0100 Subject: [PATCH 03/17] Use YAML format for peer-config instead --- .gitignore | 2 +- config/ice-servers.example.json | 13 ------------- config/peer-config.example.yml | 32 ++++++++++++++++++++++++++++++++ package.json | 3 ++- src/ice.ts | 23 ----------------------- src/index.ts | 4 ++-- src/peer-config.ts | 26 ++++++++++++++++++++++++++ yarn.lock | 5 +++++ 8 files changed, 68 insertions(+), 40 deletions(-) delete mode 100644 config/ice-servers.example.json create mode 100644 config/peer-config.example.yml delete mode 100644 src/ice.ts create mode 100644 src/peer-config.ts diff --git a/.gitignore b/.gitignore index 2f413fc..dc27c73 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ dist/ node_modules/ config/* -!config/*.example.json \ No newline at end of file +!config/*.example.* \ No newline at end of file diff --git a/config/ice-servers.example.json b/config/ice-servers.example.json deleted file mode 100644 index 6be0077..0000000 --- a/config/ice-servers.example.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "iceTransportPolicy": "relay", - "iceServers": [ - { - "urls": "stun:stun.l.google.com:19302" - }, - { - "urls": "turn:example.com", - "username": "TurnUsername", - "credential": "TurnCredential" - } - ] -} \ No newline at end of file diff --git a/config/peer-config.example.yml b/config/peer-config.example.yml new file mode 100644 index 0000000..bdbd9f1 --- /dev/null +++ b/config/peer-config.example.yml @@ -0,0 +1,32 @@ +# IN ORDER TO USE THIS CONFIG FILE, RENAME IT AND REMOVE THE ".example" PART OF THE FILENAME! +# +# CrewLink peer to peer connection configuration. +# +# This file lets you modify how CrewLink connects players to each other. +# +# CrewLink uses peer to peer connections to establish voice calls between players. This means that every player has to +# be able to connect to every other player some way or another in order for CrewLink to work. Below you can tweak the +# mechanisms CrewLink uses to establish peer to peer connections. + +# Force CrewLink to only work through TURN servers. This is great if you want to protect the IP addresses of players +# as no direct connection will be made. If set to true, a TURN server is required. +forceRelayOnly: false + +# STUN servers +# A part of establishing peer to peer connections is finding out the IP address of each participant and letting people +# connect directly to each other. A stun server, simply put, allows you to find out your public IP address and port, +# which is something other participants need to know in order to connect to you. By default CrewLink will use one of +# Google's STUN servers. +stunServers: + - url: 'stun:stun.l.google.com:19302' + +# TURN servers: +# Sometimes you find yourself behind a strict firewall or NAT that flat out rejects peer to peer connections. TURN +# servers provide a way around this by acting as a middleman between peers, relaying all traffic sent between them. +# TURN servers are the best way to guarantee that everyone can use CrewLink. By default CrewLink comes with no TURN +# servers, so you will need to set one up. Beware that TURN servers take up a lot of bandwidth as they have to relay +# ALL traffic between peers that use them. +turnServers: + - url: 'turn:example.com' + username: 'TurnUsername' + credential: 'TurnPassword' \ No newline at end of file diff --git a/package.json b/package.json index 6cf95e7..248f177 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "public-ip": "^4.0.2", "pug": "^3.0.0", "socket.io": "^2.3.0", - "tracer": "^1.1.4" + "tracer": "^1.1.4", + "yaml": "^1.10.0" }, "scripts": { "start": "yarn compile && node dist/index.js", diff --git a/src/ice.ts b/src/ice.ts deleted file mode 100644 index 4da051e..0000000 --- a/src/ice.ts +++ /dev/null @@ -1,23 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -const ICE_CONFIG_PATH = path.join(__dirname, '..', 'config', 'ice-servers.json'); -const DEFAULT_ICE_CONFIG = { - iceServers: [ - { - urls: 'stun:stun.l.google.com:19302' - } - ] -}; - -let iceConfig: RTCConfiguration = DEFAULT_ICE_CONFIG; - -if (fs.existsSync(ICE_CONFIG_PATH)) { - try { - iceConfig = JSON.parse(fs.readFileSync(ICE_CONFIG_PATH).toString('utf8')); - } catch (err) { - console.error(`Unable to load ICE server config file. Make sure it is valid JSON.\n${err}`); - } -} - -export default iceConfig; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 54e0ec9..72a33f7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import socketIO from 'socket.io'; import Tracer from 'tracer'; import morgan from 'morgan'; import publicIp from 'public-ip'; -import iceConfig from './ice'; +import peerConfig from './peer-config'; const port = parseInt(process.env.PORT || '9736'); @@ -42,7 +42,7 @@ io.on('connection', (socket: socketIO.Socket) => { logger.info("Total connected: %d", connectionCount); let code: string | null = null; - socket.emit('iceConfig', iceConfig); + socket.emit('peerConfig', peerConfig); socket.on('join', (c: string, id: number) => { if (typeof c !== 'string' || typeof id !== 'number') { diff --git a/src/peer-config.ts b/src/peer-config.ts new file mode 100644 index 0000000..642eca4 --- /dev/null +++ b/src/peer-config.ts @@ -0,0 +1,26 @@ +import YAML from 'yaml'; +import path from 'path'; +import fs from 'fs'; + +const PEER_CONFIG_PATH = path.join(__dirname, '..', 'config', 'peer-config.yml'); +const DEFAULT_PEER_CONFIG = { + forceRelayOnly: false, + stunServers: [ + { + url: 'stun:stun.l.google.com:19302' + } + ] +}; + +let peerConfig = DEFAULT_PEER_CONFIG; +if (fs.existsSync(PEER_CONFIG_PATH)) { + try { + peerConfig = YAML.parse(fs.readFileSync(PEER_CONFIG_PATH).toString('utf8')); + } catch (err) { + console.error(`Unable to load ICE server config file. Make sure it is valid YAML.\n${err}`); + } +} + +console.log(peerConfig); + +export default peerConfig; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index baeb50d..13d2d08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1267,6 +1267,11 @@ xmlhttprequest-ssl@~1.5.4: resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= +yaml@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" + integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== + yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" From dfe6f496d5e0babd2269b84ed143eaf6dce045be Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Mon, 7 Dec 2020 01:30:49 +0100 Subject: [PATCH 04/17] Updated README for peer config and fix Heroku URLs --- README.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 21aa2f0..211cf78 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ * [Getting Started](#getting-started) * [Prerequisites](#prerequisites) * [Installation](#installation) + * [Fixing Connection Issues](#fixing-connection-issue) + * [Protecting Player IP Addresses](#protecting-player-ip-addresses) * [Contributing](#contributing) * [License](#license) @@ -44,7 +46,7 @@ To get up and running quickly, you can deploy to Heroku using the button below [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) -This will deploy an instance of the crewlink-server. You can get the URL of your server by using the app name that you gave when you launched the app on heroku and appending `.herokuapp.com`. You can also find the URL of your server by going to "Settings", scrolling down to "Domains", and removing the `https://` and trailing slash from the url. Using this URL, follow step 4 of the [installation instructions](https://github.com/ottomated/CrewLink-server#manual-installation) to connect your client to your server instance. +This will deploy an instance of the crewlink-server. You can get the URL of your server by using the app name that you gave when you launched the app on heroku and appending `.herokuapp.com`. You can also find the URL of your server by going to "Settings", scrolling down to "Domains". Using this URL, follow step 4 of the [installation instructions](https://github.com/ottomated/CrewLink-server#manual-installation) to connect your client to your server instance. ## Docker Quickstart @@ -101,7 +103,22 @@ yarn install ```JS yarn start ``` -4. Copy your server's IP and port into CrewLink settings. Make sure everyone in your lobby is using the same server. +4. Copy your server URL into CrewLink settings. Make sure everyone in your lobby is using the same server. + +### Fixing Connection Issues + +CrewLink-server should work out of the box for most people. You may come across people who are unable to hear other +people in the game, no matter what they try. This could be because of a NAT or firewall that is preventing peer to peer +connections. In this case you may want to set up your own relay server to act as a middleman for your voice traffic. +CrewLink needs a specific type of server for this called a TURN server. As of yet CrewLink does not come with one by +default. + +You can configure a turn server by creating a file called ``peer-config.yml`` in the config folder. Use the +Provided example as a template. A good, open source TURN server implementation is [Coturn](https://github.com/coturn/coturn). + +### Protecting Player IP Addresses +A relay server may also be desirable in case you want to prevent CrewLink players from detecting the IP addresses of +everyone in the same Among Us room. Check the section above on how to set one up. ## Contributing From 4a41ee1304397f6abb7c5988e2484f29ad596ad2 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Mon, 7 Dec 2020 01:40:15 +0100 Subject: [PATCH 05/17] Comment out fake example TURN servers as the config would not work --- config/peer-config.example.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/peer-config.example.yml b/config/peer-config.example.yml index bdbd9f1..75a68d7 100644 --- a/config/peer-config.example.yml +++ b/config/peer-config.example.yml @@ -26,7 +26,7 @@ stunServers: # TURN servers are the best way to guarantee that everyone can use CrewLink. By default CrewLink comes with no TURN # servers, so you will need to set one up. Beware that TURN servers take up a lot of bandwidth as they have to relay # ALL traffic between peers that use them. -turnServers: - - url: 'turn:example.com' - username: 'TurnUsername' - credential: 'TurnPassword' \ No newline at end of file +# turnServers: +# - url: 'turn:example.com' +# username: 'TurnUsername' +# credential: 'TurnPassword' \ No newline at end of file From 68fa0544e2fd307b7b28bb9106c95b43a21ec579 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Mon, 7 Dec 2020 02:00:18 +0100 Subject: [PATCH 06/17] Small change to README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 211cf78..4fdc9f8 100644 --- a/README.md +++ b/README.md @@ -107,8 +107,8 @@ yarn start ### Fixing Connection Issues -CrewLink-server should work out of the box for most people. You may come across people who are unable to hear other -people in the game, no matter what they try. This could be because of a NAT or firewall that is preventing peer to peer +CrewLink-server should work out of the box for most people. You may, however, come across people who are unable to hear +other people, no matter what they try. This could be because of a NAT or firewall that is preventing peer to peer connections. In this case you may want to set up your own relay server to act as a middleman for your voice traffic. CrewLink needs a specific type of server for this called a TURN server. As of yet CrewLink does not come with one by default. @@ -118,7 +118,7 @@ Provided example as a template. A good, open source TURN server implementation i ### Protecting Player IP Addresses A relay server may also be desirable in case you want to prevent CrewLink players from detecting the IP addresses of -everyone in the same Among Us room. Check the section above on how to set one up. +everyone in the same Among Us room using CrewLink. Check the section above on how to set one up. ## Contributing From 4ea7a7c020d7839dd01a375030029c0709ca4251 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Mon, 7 Dec 2020 02:11:15 +0100 Subject: [PATCH 07/17] Remove console.log --- src/peer-config.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/peer-config.ts b/src/peer-config.ts index 642eca4..935e0a0 100644 --- a/src/peer-config.ts +++ b/src/peer-config.ts @@ -21,6 +21,4 @@ if (fs.existsSync(PEER_CONFIG_PATH)) { } } -console.log(peerConfig); - export default peerConfig; \ No newline at end of file From 6bbc394ea91c141f7c805229197515a0f17396e1 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Tue, 8 Dec 2020 19:02:00 +0100 Subject: [PATCH 08/17] Small grammar fixes in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4fdc9f8..4ab9cf3 100644 --- a/README.md +++ b/README.md @@ -113,8 +113,8 @@ connections. In this case you may want to set up your own relay server to act as CrewLink needs a specific type of server for this called a TURN server. As of yet CrewLink does not come with one by default. -You can configure a turn server by creating a file called ``peer-config.yml`` in the config folder. Use the -Provided example as a template. A good, open source TURN server implementation is [Coturn](https://github.com/coturn/coturn). +You can configure a TURN server by creating a file called ``peer-config.yml`` in the config folder. Use the +provided example as a template. A good, open source TURN server implementation is [Coturn](https://github.com/coturn/coturn). ### Protecting Player IP Addresses A relay server may also be desirable in case you want to prevent CrewLink players from detecting the IP addresses of From cd63781cb73788c61e24bc1bfaf8330600061d1f Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Fri, 11 Dec 2020 16:14:10 +0100 Subject: [PATCH 09/17] Indent STUN server section as well --- config/peer-config.example.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/peer-config.example.yml b/config/peer-config.example.yml index 75a68d7..246059f 100644 --- a/config/peer-config.example.yml +++ b/config/peer-config.example.yml @@ -13,10 +13,10 @@ forceRelayOnly: false # STUN servers -# A part of establishing peer to peer connections is finding out the IP address of each participant and letting people -# connect directly to each other. A stun server, simply put, allows you to find out your public IP address and port, -# which is something other participants need to know in order to connect to you. By default CrewLink will use one of -# Google's STUN servers. +# A part of establishing peer to peer connections is finding out the IP address of each participant and letting people +# connect directly to each other. A stun server, simply put, allows you to find out your public IP address and port, +# which is something other participants need to know in order to connect to you. By default CrewLink will use one of +# Google's STUN servers. stunServers: - url: 'stun:stun.l.google.com:19302' From 268c996040e5ee4038986dbe7ebbe2fbf97f34ad Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Sun, 27 Dec 2020 20:36:15 +0100 Subject: [PATCH 10/17] Delete package lock --- package-lock.json | 1443 --------------------------------------------- 1 file changed, 1443 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 90a0b5a..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1443 +0,0 @@ -{ - "name": "crewlink-server", - "version": "1.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" - }, - "@babel/parser": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", - "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==" - }, - "@babel/types": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", - "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@types/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.34", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", - "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/engine.io": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz", - "integrity": "sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/express": { - "version": "4.17.9", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.9.tgz", - "integrity": "sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.15.tgz", - "integrity": "sha512-pb71P0BrBAx7cQE+/7QnA1HTQUkdBKMlkPY7lHUMn0YvPJkL2UA+KW3BdWQ309IT+i9En/qm45ZxpjIcpgEhNQ==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/mime": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", - "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", - "dev": true - }, - "@types/morgan": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.2.tgz", - "integrity": "sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "14.14.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.12.tgz", - "integrity": "sha512-ASH8OPHMNlkdjrEdmoILmzFfsJICvhBsFfAum4aKZ/9U4B6M6tTmTPh+f3ttWdD74CEGV5XvXWkbyfSdXaTd7g==", - "dev": true - }, - "@types/qs": { - "version": "6.9.5", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", - "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", - "dev": true - }, - "@types/serve-static": { - "version": "1.13.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz", - "integrity": "sha512-MoJhSQreaVoL+/hurAZzIm8wafFR6ajiTM1m4A0kv6AGeVBl4r4pOV8bGFrjjq1sGxDTnCoF8i22o0/aE5XCyA==", - "dev": true, - "requires": { - "@types/mime": "*", - "@types/node": "*" - } - }, - "@types/socket.io": { - "version": "2.1.12", - "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.12.tgz", - "integrity": "sha512-oStc5VFkpb0AsjOxQUj9ztX5Iziatyla/rjZTYbFGoVrrKwd+JU2mtxk7iSl5RGYx9WunLo6UXW1fBzQok/ZyA==", - "dev": true, - "requires": { - "@types/engine.io": "*", - "@types/node": "*", - "@types/socket.io-parser": "*" - } - }, - "@types/socket.io-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@types/socket.io-parser/-/socket.io-parser-2.2.1.tgz", - "integrity": "sha512-+JNb+7N7tSINyXPxAJb62+NcpC1x/fPn7z818W4xeNCdPTp6VsO/X8fCsg6+ug4a56m1v9sEiTIIUKVupcHOFQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "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==" - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "assert-never": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", - "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "babel-walk": { - "version": "3.0.0-canary-5", - "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", - "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", - "requires": { - "@babel/types": "^7.9.6" - } - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, - "base64-arraybuffer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "requires": { - "callsite": "1.0.0" - } - }, - "blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - } - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" - }, - "character-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", - "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", - "requires": { - "is-regex": "^1.0.3" - } - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "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=" - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" - }, - "constantinople": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", - "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", - "requires": { - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.1" - } - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "dns-packet": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.2.1.tgz", - "integrity": "sha512-JHj2yJeKOqlxzeuYpN1d56GfhzivAxavNwHj9co3qptECel27B1rLY5PifJAvubsInX5pGLDjAHuCfCUc2Zv/w==", - "requires": { - "ip": "^1.1.5" - } - }, - "dns-socket": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/dns-socket/-/dns-socket-4.2.1.tgz", - "integrity": "sha512-fNvDq86lS522+zMbh31X8cQzYQd6xumCNlxsuZF5TKxQThF/e+rJbVM6K8mmlsdcSm6yNjKJQq3Sf38viAJj8g==", - "requires": { - "dns-packet": "^5.1.2" - } - }, - "doctypes": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "engine.io": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", - "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", - "requires": { - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "0.3.1", - "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", - "ws": "^7.1.2" - }, - "dependencies": { - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "engine.io-client": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz", - "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==", - "requires": { - "component-emitter": "~1.3.0", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.2.0", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.6", - "parseuri": "0.0.6", - "ws": "~6.1.0", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - }, - "dependencies": { - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "parseqs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" - }, - "parseuri": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" - }, - "ws": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", - "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "engine.io-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", - "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.4", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "requires": { - "isarray": "2.0.1" - } - }, - "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=" - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "ip-regex": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.2.0.tgz", - "integrity": "sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A==" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", - "requires": { - "has": "^1.0.3" - } - }, - "is-expression": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", - "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", - "requires": { - "acorn": "^7.1.1", - "object-assign": "^4.1.1" - } - }, - "is-ip": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", - "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", - "requires": { - "ip-regex": "^4.0.0" - } - }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - }, - "js-stringify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "jstransformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", - "requires": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" - } - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "requires": { - "json-buffer": "3.0.0" - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "requires": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "public-ip": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-ip/-/public-ip-4.0.3.tgz", - "integrity": "sha512-IofiJJWoZ8hZHBk25l4ozLvcET0pjZSxocbUfh4sGkjidMOm4iZNzzWxezGqGsVY7HuxiK7SkyJKHNeT0YQ7uw==", - "requires": { - "dns-socket": "^4.2.1", - "got": "^9.6.0", - "is-ip": "^3.1.0" - } - }, - "pug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.0.tgz", - "integrity": "sha512-inmsJyFBSHZaiGLaguoFgJGViX0If6AcfcElimvwj9perqjDpUpw79UIEDZbWFmoGVidh08aoE+e8tVkjVJPCw==", - "requires": { - "pug-code-gen": "^3.0.0", - "pug-filters": "^4.0.0", - "pug-lexer": "^5.0.0", - "pug-linker": "^4.0.0", - "pug-load": "^3.0.0", - "pug-parser": "^6.0.0", - "pug-runtime": "^3.0.0", - "pug-strip-comments": "^2.0.0" - } - }, - "pug-attrs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", - "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", - "requires": { - "constantinople": "^4.0.1", - "js-stringify": "^1.0.2", - "pug-runtime": "^3.0.0" - } - }, - "pug-code-gen": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.1.tgz", - "integrity": "sha512-xJIGvmXTQlkJllq6hqxxjRWcay2F9CU69TuAuiVZgHK0afOhG5txrQOcZyaPHBvSWCU/QQOqEp5XCH94rRZpBQ==", - "requires": { - "constantinople": "^4.0.1", - "doctypes": "^1.1.0", - "js-stringify": "^1.0.2", - "pug-attrs": "^3.0.0", - "pug-error": "^2.0.0", - "pug-runtime": "^3.0.0", - "void-elements": "^3.1.0", - "with": "^7.0.0" - } - }, - "pug-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", - "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" - }, - "pug-filters": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", - "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", - "requires": { - "constantinople": "^4.0.1", - "jstransformer": "1.0.0", - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0", - "resolve": "^1.15.1" - } - }, - "pug-lexer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.0.tgz", - "integrity": "sha512-52xMk8nNpuyQ/M2wjZBN5gXQLIylaGkAoTk5Y1pBhVqaopaoj8Z0iVzpbFZAqitL4RHNVDZRnJDsqEYe99Ti0A==", - "requires": { - "character-parser": "^2.2.0", - "is-expression": "^4.0.0", - "pug-error": "^2.0.0" - } - }, - "pug-linker": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", - "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", - "requires": { - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0" - } - }, - "pug-load": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", - "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", - "requires": { - "object-assign": "^4.1.1", - "pug-walk": "^2.0.0" - } - }, - "pug-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", - "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", - "requires": { - "pug-error": "^2.0.0", - "token-stream": "1.0.0" - } - }, - "pug-runtime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.0.tgz", - "integrity": "sha512-GoEPcmQNnaTsePEdVA05bDpY+Op5VLHKayg08AQiqJBWU/yIaywEYv7TetC5dEQS3fzBBoyb2InDcZEg3mPTIA==" - }, - "pug-strip-comments": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", - "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", - "requires": { - "pug-error": "^2.0.0" - } - }, - "pug-walk": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", - "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "socket.io": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", - "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", - "requires": { - "debug": "~4.1.0", - "engine.io": "~3.4.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.3.0", - "socket.io-parser": "~3.4.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "socket.io-adapter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", - "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" - }, - "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==", - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "engine.io-client": "~3.4.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.3.0", - "to-array": "0.1.4" - }, - "dependencies": { - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "socket.io-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz", - "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==", - "requires": { - "component-emitter": "~1.3.0", - "debug": "~3.1.0", - "isarray": "2.0.1" - }, - "dependencies": { - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - } - } - }, - "socket.io-parser": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", - "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==", - "requires": { - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "isarray": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "tinytim": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/tinytim/-/tinytim-0.1.1.tgz", - "integrity": "sha1-yWih5VWa2VUyJO92J7qzTjyu+Kg=" - }, - "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=" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "token-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", - "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" - }, - "tracer": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/tracer/-/tracer-1.1.4.tgz", - "integrity": "sha512-43Ws4c/V6VK9i2MLjmeYVtXZ+YUHU/qFXznJqYgI8F5nNrIQ4v9ImBAk+JjfPHS4StlpmaHzgR5qpBydbD9TkA==", - "requires": { - "colors": "1.4.0", - "dateformat": "3.0.3", - "mkdirp": "^1.0.4", - "tinytim": "0.1.1" - } - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typescript": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", - "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=" - }, - "with": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", - "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", - "requires": { - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", - "assert-never": "^1.2.1", - "babel-walk": "3.0.0-canary-5" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", - "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==" - }, - "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" - }, - "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==" - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" - } - } -} From 5dd7772591f14267df53d0b6597fe069ffd4bac0 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Sun, 27 Dec 2020 21:17:33 +0100 Subject: [PATCH 11/17] Add integrated TURN server --- config/peer-config.example.yml | 32 -------- config/peerConfig.example.yml | 35 +++++++++ package.json | 1 + src/ICEServer.ts | 5 ++ src/index.ts | 70 +++++++++++++++-- src/peer-config.ts | 24 ------ src/peerConfig.ts | 47 ++++++++++++ tsconfig.json | 2 +- types/node-turn/index.d.ts | 53 +++++++++++++ yarn.lock | 134 ++++++++++++++++++++++++++++++++- 10 files changed, 340 insertions(+), 63 deletions(-) delete mode 100644 config/peer-config.example.yml create mode 100644 config/peerConfig.example.yml create mode 100644 src/ICEServer.ts delete mode 100644 src/peer-config.ts create mode 100644 src/peerConfig.ts create mode 100644 types/node-turn/index.d.ts diff --git a/config/peer-config.example.yml b/config/peer-config.example.yml deleted file mode 100644 index 246059f..0000000 --- a/config/peer-config.example.yml +++ /dev/null @@ -1,32 +0,0 @@ -# IN ORDER TO USE THIS CONFIG FILE, RENAME IT AND REMOVE THE ".example" PART OF THE FILENAME! -# -# CrewLink peer to peer connection configuration. -# -# This file lets you modify how CrewLink connects players to each other. -# -# CrewLink uses peer to peer connections to establish voice calls between players. This means that every player has to -# be able to connect to every other player some way or another in order for CrewLink to work. Below you can tweak the -# mechanisms CrewLink uses to establish peer to peer connections. - -# Force CrewLink to only work through TURN servers. This is great if you want to protect the IP addresses of players -# as no direct connection will be made. If set to true, a TURN server is required. -forceRelayOnly: false - -# STUN servers -# A part of establishing peer to peer connections is finding out the IP address of each participant and letting people -# connect directly to each other. A stun server, simply put, allows you to find out your public IP address and port, -# which is something other participants need to know in order to connect to you. By default CrewLink will use one of -# Google's STUN servers. -stunServers: - - url: 'stun:stun.l.google.com:19302' - -# TURN servers: -# Sometimes you find yourself behind a strict firewall or NAT that flat out rejects peer to peer connections. TURN -# servers provide a way around this by acting as a middleman between peers, relaying all traffic sent between them. -# TURN servers are the best way to guarantee that everyone can use CrewLink. By default CrewLink comes with no TURN -# servers, so you will need to set one up. Beware that TURN servers take up a lot of bandwidth as they have to relay -# ALL traffic between peers that use them. -# turnServers: -# - url: 'turn:example.com' -# username: 'TurnUsername' -# credential: 'TurnPassword' \ No newline at end of file diff --git a/config/peerConfig.example.yml b/config/peerConfig.example.yml new file mode 100644 index 0000000..4b9397b --- /dev/null +++ b/config/peerConfig.example.yml @@ -0,0 +1,35 @@ +# IN ORDER TO USE THIS CONFIG FILE, RENAME IT AND REMOVE THE ".example" PART OF THE FILENAME! +# +# CrewLink peer to peer connection configuration. +# +# This file lets you modify how CrewLink connects players to each other. +# +# CrewLink uses peer to peer connections to establish voice calls between players. This means that every player has to +# be able to connect to every other player some way or another in order for CrewLink to work. Below you can tweak the +# mechanisms CrewLink uses to establish peer to peer connections. + +# Force CrewLink to only work through TURN servers. This is great if you want to protect the IP addresses of players +# as no direct connection will be made. At least one TURN server is required if set to true. +forceRelayOnly: false + +# Settings for the built-in TURN/STUN server. +integratedRelay: + enabled: true # Use the-built in relay server if you run a small-scale server for private use. + minPort: 49152 + maxPort: 65535 + listeningPort: 3478 + # Can be one of: OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL + debugLevel: 'INFO' + +# If you run into performance issues, you may want to look into offloading relay duties to a native TURN/STUN +# implementation. You can configure them below. + +# Custom TURN/STUN servers +iceServers: + - urls: 'stun:stun.l.google.com:19302' +# - urls: 'turn:example.com' +# username: 'TurnUsername' +# credential: 'TurnPassword' +# - urls: 'stun:example.com' +# username: 'StunUsername' +# credential: 'StunPassword' \ No newline at end of file diff --git a/package.json b/package.json index 248f177..79a17a6 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "express": "^4.17.1", "morgan": "^1.10.0", + "node-turn": "0.0.6", "public-ip": "^4.0.2", "pug": "^3.0.0", "socket.io": "^2.3.0", diff --git a/src/ICEServer.ts b/src/ICEServer.ts new file mode 100644 index 0000000..9bacc96 --- /dev/null +++ b/src/ICEServer.ts @@ -0,0 +1,5 @@ +export interface ICEServer { + urls: string|string[]; + username?: string; + credential?: string; +} diff --git a/src/index.ts b/src/index.ts index e52c391..c065ed0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,10 @@ import socketIO from 'socket.io'; import Tracer from 'tracer'; import morgan from 'morgan'; import publicIp from 'public-ip'; -import peerConfig from './peer-config'; +import TurnServer from 'node-turn'; +import crypto from 'crypto'; +import peerConfig from './peerConfig'; +import { ICEServer } from './ICEServer'; const httpsEnabled = !!process.env.HTTPS; @@ -20,6 +23,11 @@ const logger = Tracer.colorConsole({ format: "{{timestamp}} <{{title}}> {{message}}" }); +const turnLogger = Tracer.colorConsole({ + format: "{{timestamp}} <{{title}}> {{message}}", + level: peerConfig.integratedRelay.debugLevel.toLowerCase() +}) + const app = express(); let server: HttpsServer | Server; if (httpsEnabled) { @@ -30,6 +38,24 @@ if (httpsEnabled) { } else { server = new Server(app); } + +let turnServer: TurnServer | null = null; +if (peerConfig.integratedRelay.enabled) { + turnServer = new TurnServer({ + minPort: peerConfig.integratedRelay.minPort, + maxPort: peerConfig.integratedRelay.maxPort, + listeningPort: peerConfig.integratedRelay.listeningPort, + authMech: 'long-term', + debugLevel: peerConfig.integratedRelay.debugLevel, + realm: 'crewlink', + debug: (level, message) => { + turnLogger[level.toLowerCase()](message) + } + }) + + turnServer.start(); +} + const io = socketIO(server); const playerIds = new Map(); @@ -43,10 +69,16 @@ interface SetIDsPacket { [key: string]: number } +interface ClientPeerConfig { + forceRelayOnly: boolean; + iceServers: ICEServer[] +} + app.set('view engine', 'pug'); app.use(morgan('combined')); app.use(express.static('offsets')); let connectionCount = 0; +let ip = process.env.IP_ADDRESS; let address = process.env.ADDRESS; app.get('/', (_, res) => { @@ -68,7 +100,23 @@ io.on('connection', (socket: socketIO.Socket) => { logger.info("Total connected: %d", connectionCount); let code: string | null = null; - socket.emit('peerConfig', peerConfig); + const clientPeerConfig: ClientPeerConfig = { + forceRelayOnly: peerConfig.forceRelayOnly, + iceServers: peerConfig.iceServers? [...peerConfig.iceServers] : [] + } + + if (turnServer) { + const turnCredential = crypto.randomBytes(32).toString('base64'); + turnServer.addUser(socket.id, turnCredential); + logger.info(`Adding socket "${socket.id}" as TURN user.`) + clientPeerConfig.iceServers.push({ + urls: `turn:${ip}:${peerConfig.integratedRelay.listeningPort}`, + username: socket.id, + credential: turnCredential + }); + } + + socket.emit('clientPeerConfig', clientPeerConfig); socket.on('join', (c: string, id: number) => { if (typeof c !== 'string' || typeof id !== 'number') { @@ -120,13 +168,25 @@ io.on('connection', (socket: socketIO.Socket) => { socket.on('disconnect', () => { connectionCount--; playerIds.delete(socket.id); + + if (turnServer) { + logger.info(`Removing socket "${socket.id}" as TURN user.`) + turnServer.removeUser(socket.id); + } + logger.info("Total connected: %d", connectionCount); }); }); -server.listen(port); (async () => { - if (!address) - address = `http://${await publicIp.v4()}:${port}`; + if (!ip) { + ip = await publicIp.v4(); + } + + if (!address) { + address = `${httpsEnabled ? 'https' : 'http'}://${ip}:${port}`; + } + + server.listen(port); logger.info('CrewLink Server started: %s', address); })(); \ No newline at end of file diff --git a/src/peer-config.ts b/src/peer-config.ts deleted file mode 100644 index 935e0a0..0000000 --- a/src/peer-config.ts +++ /dev/null @@ -1,24 +0,0 @@ -import YAML from 'yaml'; -import path from 'path'; -import fs from 'fs'; - -const PEER_CONFIG_PATH = path.join(__dirname, '..', 'config', 'peer-config.yml'); -const DEFAULT_PEER_CONFIG = { - forceRelayOnly: false, - stunServers: [ - { - url: 'stun:stun.l.google.com:19302' - } - ] -}; - -let peerConfig = DEFAULT_PEER_CONFIG; -if (fs.existsSync(PEER_CONFIG_PATH)) { - try { - peerConfig = YAML.parse(fs.readFileSync(PEER_CONFIG_PATH).toString('utf8')); - } catch (err) { - console.error(`Unable to load ICE server config file. Make sure it is valid YAML.\n${err}`); - } -} - -export default peerConfig; \ No newline at end of file diff --git a/src/peerConfig.ts b/src/peerConfig.ts new file mode 100644 index 0000000..5fe94e6 --- /dev/null +++ b/src/peerConfig.ts @@ -0,0 +1,47 @@ +import YAML from 'yaml'; +import path from 'path'; +import fs from 'fs'; +import { ICEServer } from './ICEServer'; + +const PEER_CONFIG_PATH = path.join(__dirname, '..', 'config', 'peerConfig.yml'); + +interface IntegratedRelaySettings { + enabled: boolean; + minPort: number; + maxPort: number; + listeningPort: number; + debugLevel: 'OFF' | 'FATAL' | 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE' | 'ALL'; +} + +interface PeerConfig { + forceRelayOnly: boolean; + integratedRelay: IntegratedRelaySettings; + iceServers?: ICEServer[] +} + +const DEFAULT_PEER_CONFIG: PeerConfig = { + forceRelayOnly: false, + integratedRelay: { + enabled: true, + minPort: 49152, + maxPort: 65535, + listeningPort: 3478, + debugLevel: 'INFO' + }, + iceServers: [ + { + urls: 'stun:stun.l.google.com:19302' + } + ] +}; + +let peerConfig = DEFAULT_PEER_CONFIG; +if (fs.existsSync(PEER_CONFIG_PATH)) { + try { + peerConfig = YAML.parse(fs.readFileSync(PEER_CONFIG_PATH).toString('utf8')); + } catch (err) { + console.error(`Unable to load peer config file. Make sure it is valid YAML.\n${err}`); + } +} + +export default peerConfig diff --git a/tsconfig.json b/tsconfig.json index 0d84041..1c6de08 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,6 @@ } }, "include": [ - "src/**/*" + "src/**/*", "types/node-turn/index.d.ts" ] } \ No newline at end of file diff --git a/types/node-turn/index.d.ts b/types/node-turn/index.d.ts new file mode 100644 index 0000000..4086a60 --- /dev/null +++ b/types/node-turn/index.d.ts @@ -0,0 +1,53 @@ +declare module "node-turn" { + + interface TurnCredentials { + [username: string]: string; + } + + type DebugLevel = 'OFF' | 'FATAL' | 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE' | 'ALL'; + + interface TurnOptions { + listeningPort?: number; + listeningIps?: string[]; + relayIps?: string[] + externalIps?: string[] + minPort?: number; + maxPort?: number; + authMech?: 'short-term' | 'long-term' | 'none'; + credentials?: TurnCredentials + realm?: string; + debugLevel?: DebugLevel; + debug?: (debugLevel: DebugLevel, message: string) => void + } + + class Turn { + constructor (options: TurnOptions); + + /** + * Start the server. + */ + start (): void; + + /** + * Stop the server. + */ + stop (): void; + + /** + * Add a user to credential mechanism. + * + * @param username + * @param password + */ + addUser (username: string, password: string): void; + + /** + * Remove a user from credential mechanism. + * + * @param username + */ + removeUser (username: string): void; + } + + export = Turn; +} diff --git a/yarn.lock b/yarn.lock index 13d2d08..f91ad88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -135,6 +135,13 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -182,6 +189,11 @@ base64-arraybuffer@0.1.5: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + base64id@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" @@ -222,6 +234,14 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" +buffer@^5.1.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -319,6 +339,23 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +crc@~3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" + integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== + dependencies: + buffer "^5.1.0" + +date-format@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" + integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== + +date-format@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" + integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== + dateformat@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -331,6 +368,13 @@ debug@2.6.9: dependencies: ms "2.0.0" +debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -458,6 +502,11 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -512,6 +561,11 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +flatted@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -522,6 +576,15 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -558,6 +621,11 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" +graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + has-binary2@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" @@ -616,6 +684,11 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" @@ -690,11 +763,26 @@ js-stringify@^1.0.2: resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds= +js-yaml@~3.14.0: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + jstransformer@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" @@ -715,6 +803,17 @@ lodash@^4.17.19: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +log4js@~6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb" + integrity sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw== + dependencies: + date-format "^3.0.0" + debug "^4.1.1" + flatted "^2.0.1" + rfdc "^1.1.4" + streamroller "^2.2.4" + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" @@ -788,7 +887,7 @@ ms@2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -798,6 +897,15 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +node-turn@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/node-turn/-/node-turn-0.0.6.tgz#fddaa106ce65b2c8e5864d25dfe68feab1483a9b" + integrity sha512-HJRfWIADk5I61jZlrKHwx/A+IgusnN7Fs/M9wl0xuSxTynKnh4ZBLvvIeBH5w556bl2bFFkzHiCJFIZ3FR7fmA== + dependencies: + crc "~3.8.0" + js-yaml "~3.14.0" + log4js "~6.3.0" + normalize-url@^4.1.0: version "4.5.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" @@ -1051,6 +1159,11 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +rfdc@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" + integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== + safe-buffer@5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -1150,11 +1263,25 @@ socket.io@^2.3.0: socket.io-client "2.3.0" socket.io-parser "~3.4.0" +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +streamroller@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.4.tgz#c198ced42db94086a6193608187ce80a5f2b0e53" + integrity sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ== + dependencies: + date-format "^2.1.0" + debug "^4.1.1" + fs-extra "^8.1.0" + tinytim@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/tinytim/-/tinytim-0.1.1.tgz#c968a1e5559ad9553224ef7627bab34e3caef8a8" @@ -1208,6 +1335,11 @@ typescript@^4.0.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389" integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ== +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" From 4366666705989d840d3d7aad97a4e86df3f36bec Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Sun, 27 Dec 2020 21:55:31 +0100 Subject: [PATCH 12/17] Update some documentation --- README.md | 21 ++++++--------------- config/peerConfig.example.yml | 3 ++- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index df38069..935a2ea 100644 --- a/README.md +++ b/README.md @@ -114,21 +114,12 @@ yarn install yarn start ``` 4. Copy your server URL into CrewLink settings. Make sure everyone in your lobby is using the same server. - -### Fixing Connection Issues - -CrewLink-server should work out of the box for most people. You may, however, come across people who are unable to hear -other people, no matter what they try. This could be because of a NAT or firewall that is preventing peer to peer -connections. In this case you may want to set up your own relay server to act as a middleman for your voice traffic. -CrewLink needs a specific type of server for this called a TURN server. As of yet CrewLink does not come with one by -default. - -You can configure a TURN server by creating a file called ``peer-config.yml`` in the config folder. Use the -provided example as a template. A good, open source TURN server implementation is [Coturn](https://github.com/coturn/coturn). - -### Protecting Player IP Addresses -A relay server may also be desirable in case you want to prevent CrewLink players from detecting the IP addresses of -everyone in the same Among Us room using CrewLink. Check the section above on how to set one up. +### Customizing Peer to Peer Behavior +By default CrewLink clients will attempt to establish connections directly to each other for sending voice and game +state data. As a fallback mechanism, CrewLink-server ships with an integrated TURN server in the event clients cannot +directly connect to each other. You may want to customize this behavior, to, for example exclusively use the TURN relay +to protect player IP addresses. To do so, head into the ``config`` folder and rename ``peerConfig.example.yml`` to +``peerConfig.yml`` and make the desired changes. ## Contributing diff --git a/config/peerConfig.example.yml b/config/peerConfig.example.yml index 4b9397b..be383b3 100644 --- a/config/peerConfig.example.yml +++ b/config/peerConfig.example.yml @@ -12,7 +12,7 @@ # as no direct connection will be made. At least one TURN server is required if set to true. forceRelayOnly: false -# Settings for the built-in TURN/STUN server. +# Settings for the built-in TURN server. integratedRelay: enabled: true # Use the-built in relay server if you run a small-scale server for private use. minPort: 49152 @@ -26,6 +26,7 @@ integratedRelay: # Custom TURN/STUN servers iceServers: + # Google's STUN server is used by default - urls: 'stun:stun.l.google.com:19302' # - urls: 'turn:example.com' # username: 'TurnUsername' From 6dde958db1a9bf7dc8d8a1560aee6892ef09f8ce Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Sun, 27 Dec 2020 21:58:32 +0100 Subject: [PATCH 13/17] Minor fixes in Readme --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 935a2ea..9ad3771 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,7 @@ * [Getting Started](#getting-started) * [Prerequisites](#prerequisites) * [Installation](#installation) - * [Fixing Connection Issues](#fixing-connection-issue) - * [Protecting Player IP Addresses](#protecting-player-ip-addresses) + * [Customizing Peer to Peer Behavior](#customizing-peer-to-peer-behavior) * [Contributing](#contributing) * [License](#license) @@ -117,7 +116,7 @@ yarn start ### Customizing Peer to Peer Behavior By default CrewLink clients will attempt to establish connections directly to each other for sending voice and game state data. As a fallback mechanism, CrewLink-server ships with an integrated TURN server in the event clients cannot -directly connect to each other. You may want to customize this behavior, to, for example exclusively use the TURN relay +directly connect to each other. You may want to customize this behavior to, for example, exclusively use the TURN relay to protect player IP addresses. To do so, head into the ``config`` folder and rename ``peerConfig.example.yml`` to ``peerConfig.yml`` and make the desired changes. From dd16a8bfb924727f1351e9686896bea0f6499485 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Fri, 1 Jan 2021 14:40:55 +0100 Subject: [PATCH 14/17] Remove integrated TURN server support for now. --- README.md | 16 ++-- config/peerConfig.example.yml | 20 +---- package.json | 1 - src/index.ts | 51 ++----------- src/peerConfig.ts | 16 ---- yarn.lock | 137 ---------------------------------- 6 files changed, 19 insertions(+), 222 deletions(-) diff --git a/README.md b/README.md index 9ad3771..d2d7caa 100644 --- a/README.md +++ b/README.md @@ -114,12 +114,16 @@ yarn start ``` 4. Copy your server URL into CrewLink settings. Make sure everyone in your lobby is using the same server. ### Customizing Peer to Peer Behavior -By default CrewLink clients will attempt to establish connections directly to each other for sending voice and game -state data. As a fallback mechanism, CrewLink-server ships with an integrated TURN server in the event clients cannot -directly connect to each other. You may want to customize this behavior to, for example, exclusively use the TURN relay -to protect player IP addresses. To do so, head into the ``config`` folder and rename ``peerConfig.example.yml`` to -``peerConfig.yml`` and make the desired changes. - +CrewLink-server should work out of the box for most people. You may, however, come across people who are unable to hear +other people, no matter what they try. This could be because of a NAT or firewall that is preventing peer to peer +connections. In this case you may want to set up your own relay server to act as a middleman for your voice traffic. +CrewLink needs a specific type of server for this called a TURN server. As of yet CrewLink does not come with one by +default. + +You can configure a TURN server by creating a file called ``peerConfig.yml`` in the config folder. Use the +provided example as a template. A good, open source TURN server implementation is +[Coturn](https://github.com/coturn/coturn). A relay server may also be desirable in case you want to prevent CrewLink +players from detecting the IP addresses of everyone in the same Among Us room using your voice server. ## Contributing diff --git a/config/peerConfig.example.yml b/config/peerConfig.example.yml index be383b3..c3c50f8 100644 --- a/config/peerConfig.example.yml +++ b/config/peerConfig.example.yml @@ -4,27 +4,15 @@ # # This file lets you modify how CrewLink connects players to each other. # -# CrewLink uses peer to peer connections to establish voice calls between players. This means that every player has to -# be able to connect to every other player some way or another in order for CrewLink to work. Below you can tweak the -# mechanisms CrewLink uses to establish peer to peer connections. +# CrewLink uses peer to peer connections to send voice and game state data between players. This means that every player +# has to be able to connect to every other player some way or another in order for CrewLink to work. Below you can tweak +# the mechanisms CrewLink uses to establish peer to peer connections. # Force CrewLink to only work through TURN servers. This is great if you want to protect the IP addresses of players # as no direct connection will be made. At least one TURN server is required if set to true. forceRelayOnly: false -# Settings for the built-in TURN server. -integratedRelay: - enabled: true # Use the-built in relay server if you run a small-scale server for private use. - minPort: 49152 - maxPort: 65535 - listeningPort: 3478 - # Can be one of: OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL - debugLevel: 'INFO' - -# If you run into performance issues, you may want to look into offloading relay duties to a native TURN/STUN -# implementation. You can configure them below. - -# Custom TURN/STUN servers +# If you run into connection issues, you may want to add your own TURN server here. iceServers: # Google's STUN server is used by default - urls: 'stun:stun.l.google.com:19302' diff --git a/package.json b/package.json index ecfa9b0..9ed8c09 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "dependencies": { "express": "^4.17.1", "morgan": "^1.10.0", - "node-turn": "0.0.6", "pug": "^3.0.0", "socket.io": "^2.3.0", "tracer": "^1.1.4", diff --git a/src/index.ts b/src/index.ts index 3bd35a8..3afd53e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,12 +6,10 @@ import { join } from 'path'; import socketIO from 'socket.io'; import Tracer from 'tracer'; import morgan from 'morgan'; -import TurnServer from 'node-turn'; -import crypto from 'crypto'; import peerConfig from './peerConfig'; import { ICEServer } from './ICEServer'; -const supportedCrewLinkVersions = new Set(['1.2.0', '0.0.0']); +const supportedCrewLinkVersions = new Set(['1.2.0']); const httpsEnabled = !!process.env.HTTPS; const port = process.env.PORT || (httpsEnabled ? '443' : '9736'); @@ -22,11 +20,6 @@ const logger = Tracer.colorConsole({ format: "{{timestamp}} <{{title}}> {{message}}" }); -const turnLogger = Tracer.colorConsole({ - format: "{{timestamp}} <{{title}}> {{message}}", - level: peerConfig.integratedRelay.debugLevel.toLowerCase() -}) - const app = express(); let server: HttpsServer | Server; if (httpsEnabled) { @@ -38,21 +31,10 @@ if (httpsEnabled) { server = new Server(app); } -let turnServer: TurnServer | null = null; -if (peerConfig.integratedRelay.enabled) { - turnServer = new TurnServer({ - minPort: peerConfig.integratedRelay.minPort, - maxPort: peerConfig.integratedRelay.maxPort, - listeningPort: peerConfig.integratedRelay.listeningPort, - authMech: 'long-term', - debugLevel: peerConfig.integratedRelay.debugLevel, - realm: 'crewlink', - debug: (level, message) => { - turnLogger[level.toLowerCase()](message) - } - }) - - turnServer.start(); +let address = process.env.ADDRESS; +if (!address) { + logger.error('You must set the ADDRESS environment variable.'); + process.exit(1); } const io = socketIO(server); @@ -78,11 +60,6 @@ app.set('view engine', 'pug'); app.use(morgan('combined')); let connectionCount = 0; -let address = process.env.ADDRESS; -if (!address) { - logger.error('You must set the ADDRESS environment variable.'); - process.exit(1); -} app.get('/', (_, res) => { res.render('index', { connectionCount, address }); @@ -125,17 +102,6 @@ io.on('connection', (socket: socketIO.Socket) => { iceServers: peerConfig.iceServers? [...peerConfig.iceServers] : [] } - if (turnServer) { - const turnCredential = crypto.randomBytes(32).toString('base64'); - turnServer.addUser(socket.id, turnCredential); - logger.info(`Adding socket "${socket.id}" as TURN user.`) - clientPeerConfig.iceServers.push({ - urls: `turn:${address}:${peerConfig.integratedRelay.listeningPort}`, - username: socket.id, - credential: turnCredential - }); - } - socket.emit('clientPeerConfig', clientPeerConfig); socket.on('join', (c: string, id: number, clientId: number) => { @@ -212,13 +178,6 @@ io.on('connection', (socket: socketIO.Socket) => { clients.delete(socket.id); connectionCount--; logger.info("Total connected: %d", connectionCount); - - if (turnServer) { - logger.info(`Removing socket "${socket.id}" as TURN user.`) - turnServer.removeUser(socket.id); - } - - logger.info("Total connected: %d", connectionCount); }) }); diff --git a/src/peerConfig.ts b/src/peerConfig.ts index 5fe94e6..5e33ac5 100644 --- a/src/peerConfig.ts +++ b/src/peerConfig.ts @@ -5,29 +5,13 @@ import { ICEServer } from './ICEServer'; const PEER_CONFIG_PATH = path.join(__dirname, '..', 'config', 'peerConfig.yml'); -interface IntegratedRelaySettings { - enabled: boolean; - minPort: number; - maxPort: number; - listeningPort: number; - debugLevel: 'OFF' | 'FATAL' | 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE' | 'ALL'; -} - interface PeerConfig { forceRelayOnly: boolean; - integratedRelay: IntegratedRelaySettings; iceServers?: ICEServer[] } const DEFAULT_PEER_CONFIG: PeerConfig = { forceRelayOnly: false, - integratedRelay: { - enabled: true, - minPort: 49152, - maxPort: 65535, - listeningPort: 3478, - debugLevel: 'INFO' - }, iceServers: [ { urls: 'stun:stun.l.google.com:19302' diff --git a/yarn.lock b/yarn.lock index c2267d8..89c79a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -131,13 +131,6 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -185,11 +178,6 @@ base64-arraybuffer@0.1.5: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - base64id@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" @@ -230,14 +218,6 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" -buffer@^5.1.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -315,23 +295,6 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -crc@~3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" - integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== - dependencies: - buffer "^5.1.0" - -date-format@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" - integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== - -date-format@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" - integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== - dateformat@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -344,13 +307,6 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@^4.1.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -440,11 +396,6 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -499,11 +450,6 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -flatted@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -514,25 +460,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.4" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" - integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== - has-binary2@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" @@ -586,11 +518,6 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" @@ -648,21 +575,6 @@ js-stringify@^1.0.2: resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds= -js-yaml@~3.14.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - jstransformer@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" @@ -676,17 +588,6 @@ lodash@^4.17.19: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -log4js@~6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb" - integrity sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw== - dependencies: - date-format "^3.0.0" - debug "^4.1.1" - flatted "^2.0.1" - rfdc "^1.1.4" - streamroller "^2.2.4" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -745,11 +646,6 @@ ms@2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -760,15 +656,6 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -node-turn@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/node-turn/-/node-turn-0.0.6.tgz#fddaa106ce65b2c8e5864d25dfe68feab1483a9b" - integrity sha512-HJRfWIADk5I61jZlrKHwx/A+IgusnN7Fs/M9wl0xuSxTynKnh4ZBLvvIeBH5w556bl2bFFkzHiCJFIZ3FR7fmA== - dependencies: - crc "~3.8.0" - js-yaml "~3.14.0" - log4js "~6.3.0" - object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -976,11 +863,6 @@ resolve@^1.15.1: is-core-module "^2.1.0" path-parse "^1.0.6" -rfdc@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" - integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== - safe-buffer@5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -1080,25 +962,11 @@ socket.io@^2.3.0: socket.io-client "2.3.0" socket.io-parser "~3.4.0" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -streamroller@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.4.tgz#c198ced42db94086a6193608187ce80a5f2b0e53" - integrity sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ== - dependencies: - date-format "^2.1.0" - debug "^4.1.1" - fs-extra "^8.1.0" - tinytim@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/tinytim/-/tinytim-0.1.1.tgz#c968a1e5559ad9553224ef7627bab34e3caef8a8" @@ -1147,11 +1015,6 @@ typescript@^4.0.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" From 5cc528a7a8fc6cbd0bf8dd76496b57cad0992703 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Fri, 1 Jan 2021 14:44:05 +0100 Subject: [PATCH 15/17] Remove obsolete node-turn typings --- types/node-turn/index.d.ts | 53 -------------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 types/node-turn/index.d.ts diff --git a/types/node-turn/index.d.ts b/types/node-turn/index.d.ts deleted file mode 100644 index 4086a60..0000000 --- a/types/node-turn/index.d.ts +++ /dev/null @@ -1,53 +0,0 @@ -declare module "node-turn" { - - interface TurnCredentials { - [username: string]: string; - } - - type DebugLevel = 'OFF' | 'FATAL' | 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE' | 'ALL'; - - interface TurnOptions { - listeningPort?: number; - listeningIps?: string[]; - relayIps?: string[] - externalIps?: string[] - minPort?: number; - maxPort?: number; - authMech?: 'short-term' | 'long-term' | 'none'; - credentials?: TurnCredentials - realm?: string; - debugLevel?: DebugLevel; - debug?: (debugLevel: DebugLevel, message: string) => void - } - - class Turn { - constructor (options: TurnOptions); - - /** - * Start the server. - */ - start (): void; - - /** - * Stop the server. - */ - stop (): void; - - /** - * Add a user to credential mechanism. - * - * @param username - * @param password - */ - addUser (username: string, password: string): void; - - /** - * Remove a user from credential mechanism. - * - * @param username - */ - removeUser (username: string): void; - } - - export = Turn; -} From 9f44d6ea7ae28cb612e332faac8ff5c55cdb3b1b Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Sat, 2 Jan 2021 17:23:00 +0100 Subject: [PATCH 16/17] Added warning about TURN servers to config/readme --- README.md | 16 ++++++++++++++-- config/peerConfig.example.yml | 11 +++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d2d7caa..e5fb14b 100644 --- a/README.md +++ b/README.md @@ -117,13 +117,25 @@ yarn start CrewLink-server should work out of the box for most people. You may, however, come across people who are unable to hear other people, no matter what they try. This could be because of a NAT or firewall that is preventing peer to peer connections. In this case you may want to set up your own relay server to act as a middleman for your voice traffic. -CrewLink needs a specific type of server for this called a TURN server. As of yet CrewLink does not come with one by -default. +CrewLink needs a specific type of server for this called a TURN server. You can configure a TURN server by creating a file called ``peerConfig.yml`` in the config folder. Use the provided example as a template. A good, open source TURN server implementation is [Coturn](https://github.com/coturn/coturn). A relay server may also be desirable in case you want to prevent CrewLink players from detecting the IP addresses of everyone in the same Among Us room using your voice server. + +CAUTION: Hosting your own TURN server is a great way to solve most connection issues but they're not without problems. +These servers, by design, let anyone with the credentials relay packets to anywhere. Because you are sending the TURN +credentials to everyone connecting to your CrewLink server, obtaining them becomes very easy. If you choose to set one +up, make sure you: + - Know what you're doing + - Configure the server in a way that disallows relaying packets to places that make no sense (localhost, private networks, etc) + - Consider limiting the bandwidth per user and across the whole TURN server + - Keep an eye on TURN server activity while you're running one + - Change TURN credentials regularly + - This list is not exhaustive, see point 1 + +It is because of this that you should only deploy a TURN server for small, private CrewLink servers. ## Contributing diff --git a/config/peerConfig.example.yml b/config/peerConfig.example.yml index c3c50f8..0af014a 100644 --- a/config/peerConfig.example.yml +++ b/config/peerConfig.example.yml @@ -13,6 +13,17 @@ forceRelayOnly: false # If you run into connection issues, you may want to add your own TURN server here. +# +# CAUTION: Hosting your own TURN server is a great way to solve most connection issues but they're not without problems. +# These servers, by design, let anyone with the credentials relay packets to anywhere. Because you are sending the TURN +# credentials to everyone connecting to your CrewLink server, obtaining them becomes very easy. If you choose to set one +# up, make sure you: +# - Know what you're doing +# - Configure the server in a way that disallows relaying packets to places that make no sense (localhost, private networks, etc) +# - Consider limiting the bandwidth per user and across the whole TURN server +# - Keep an eye on TURN server activity while you're running one +# - Change TURN credentials regularly +# - This list is not exhaustive, see point 1 iceServers: # Google's STUN server is used by default - urls: 'stun:stun.l.google.com:19302' From 753964b9502ccf0dba8c2b72208e5cc7d68e5be3 Mon Sep 17 00:00:00 2001 From: Sjoerd Date: Sat, 2 Jan 2021 17:38:50 +0100 Subject: [PATCH 17/17] Remove node-turn from tsconfig --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 1c6de08..0d84041 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,6 @@ } }, "include": [ - "src/**/*", "types/node-turn/index.d.ts" + "src/**/*" ] } \ No newline at end of file