Skip to content

Commit 28eb3e1

Browse files
committed
adds support for kick/block/unblock
1 parent 39dc8bf commit 28eb3e1

File tree

5 files changed

+157
-6
lines changed

5 files changed

+157
-6
lines changed

.eslintrc.js

+62
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,65 @@ module.exports =
170170
'yoda': 2
171171
}
172172
};
173+
174+
switch (process.env.MEDIASOUP_NODE_LANGUAGE)
175+
{
176+
case 'typescript':
177+
{
178+
eslintConfig.parser = '@typescript-eslint/parser';
179+
eslintConfig.plugins =
180+
[
181+
...eslintConfig.plugins,
182+
'@typescript-eslint'
183+
];
184+
eslintConfig.extends =
185+
[
186+
'eslint:recommended',
187+
'plugin:@typescript-eslint/eslint-recommended',
188+
'plugin:@typescript-eslint/recommended'
189+
];
190+
eslintConfig.rules =
191+
{
192+
...eslintConfig.rules,
193+
'no-unused-vars' : 0,
194+
'@typescript-eslint/ban-ts-ignore' : 0,
195+
'@typescript-eslint/member-delimiter-style' : [ 2,
196+
{
197+
multiline : { delimiter: 'semi', requireLast: true },
198+
singleline : { delimiter: 'semi', requireLast: false }
199+
}
200+
],
201+
'@typescript-eslint/no-explicit-any' : 0,
202+
'@typescript-eslint/no-unused-vars' : [ 2,
203+
{
204+
vars : 'all',
205+
args : 'after-used',
206+
ignoreRestSiblings : false
207+
}
208+
],
209+
'@typescript-eslint/no-use-before-define' : 0,
210+
'@typescript-eslint/no-empty-function' : 0
211+
};
212+
213+
break;
214+
}
215+
216+
case 'javascript':
217+
{
218+
eslintConfig.env['jest/globals'] = true;
219+
eslintConfig.plugins =
220+
[
221+
...eslintConfig.plugins,
222+
'jest'
223+
];
224+
225+
break;
226+
}
227+
228+
default:
229+
{
230+
throw new TypeError('wrong/missing MEDIASOUP_NODE_LANGUAGE env');
231+
}
232+
}
233+
234+
module.exports = eslintConfig;

config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ const config =
133133
// Additional options that are not part of WebRtcTransportOptions.
134134
maxIncomingBitrate : 1500000
135135
}
136-
}
136+
},
137+
authKey: process.env.AUTH_KEY || `${__dirname}/certs/perms.pub.pem`
137138
};
138139

139140
if (process.env.MEDIASOUP_ANNOUNCED_IP)

index.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ const Logger = require('./lib/Logger');
2525
const Room = require('./lib/Room');
2626
const interactiveServer = require('./lib/interactiveServer');
2727
const interactiveClient = require('./lib/interactiveClient');
28+
const util = require('util');
29+
const readFile = util.promisify(fs.readFile);
2830

2931
const logger = new Logger();
3032
const queue = new AwaitQueue();
@@ -37,6 +39,8 @@ let protooWebSocketServer;
3739
const mediasoupWorkers = [];
3840
let nextMediasoupWorkerIdx = 0;
3941

42+
let authKey;
43+
4044
run();
4145

4246
async function run()
@@ -49,8 +53,14 @@ async function run()
4953
await runMediasoupWorkers();
5054
await createExpressApp();
5155
await runHttpsServer();
56+
try {
57+
authKey = await readFile(config.authKey, 'utf8');
58+
} catch (error) {
59+
logger.error("authKey not set; jwt verification will not work.", error);
60+
}
5261
await runProtooWebSocketServer();
5362

63+
5464
// Log rooms status every X seconds.
5565
setInterval(() =>
5666
{
@@ -418,7 +428,7 @@ async function getOrCreateRoom({ roomId })
418428

419429
const mediasoupWorker = getMediasoupWorker();
420430

421-
room = await Room.create({ mediasoupWorker, roomId });
431+
room = await Room.create({ mediasoupWorker, roomId, authKey });
422432

423433
rooms.set(roomId, room);
424434
room.on('close', () => rooms.delete(roomId));

lib/Room.js

+81-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const throttle = require('@sitespeed.io/throttle');
44
const Logger = require('./Logger');
55
const config = require('../config');
66
const Bot = require('./Bot');
7+
const jwt = require('jsonwebtoken');
78

89
const logger = new Logger('Room');
910

@@ -25,7 +26,7 @@ class Room extends EventEmitter
2526
* mediasoup Router must be created.
2627
* @param {String} roomId - Id of the Room instance.
2728
*/
28-
static async create({ mediasoupWorker, roomId })
29+
static async create({ mediasoupWorker, roomId, authKey })
2930
{
3031
logger.info('create() [roomId:%s]', roomId);
3132

@@ -54,11 +55,12 @@ class Room extends EventEmitter
5455
protooRoom,
5556
mediasoupRouter,
5657
audioLevelObserver,
57-
bot
58+
bot,
59+
authKey
5860
});
5961
}
6062

61-
constructor({ roomId, protooRoom, mediasoupRouter, audioLevelObserver, bot })
63+
constructor({ roomId, protooRoom, mediasoupRouter, audioLevelObserver, bot, authKey })
6264
{
6365
super();
6466
this.setMaxListeners(Infinity);
@@ -105,6 +107,8 @@ class Room extends EventEmitter
105107
// @type {Boolean}
106108
this._networkThrottled = false;
107109

110+
this._authKey = authKey;
111+
108112
// Handle audioLevelObserver.
109113
this._handleAudioLevelObserver();
110114

@@ -202,6 +206,8 @@ class Room extends EventEmitter
202206
peer.data.consumers = new Map();
203207
peer.data.dataProducers = new Map();
204208
peer.data.dataConsumers = new Map();
209+
peer.data.peerToConsumers = new Map();
210+
peer.data.blockedPeers = new Set();
205211

206212
peer.on('request', (request, accept, reject) =>
207213
{
@@ -705,7 +711,8 @@ class Room extends EventEmitter
705711
displayName,
706712
device,
707713
rtpCapabilities,
708-
sctpCapabilities
714+
sctpCapabilities,
715+
token
709716
} = request.data;
710717

711718
// Store client data into the protoo Peer data object.
@@ -714,6 +721,7 @@ class Room extends EventEmitter
714721
peer.data.device = device;
715722
peer.data.rtpCapabilities = rtpCapabilities;
716723
peer.data.sctpCapabilities = sctpCapabilities;
724+
peer.data.token = token;
717725

718726
// Tell the new Peer about already joined Peers.
719727
// And also create Consumers for existing Producers.
@@ -1387,6 +1395,71 @@ class Room extends EventEmitter
13871395
break;
13881396
}
13891397

1398+
case 'kick':
1399+
{
1400+
jwt.verify(peer.data.token, this._authKey, { algorithms: ['RS512'] }, (err, decoded) => {
1401+
if (err) {
1402+
reject(500, err);
1403+
} else {
1404+
if (decoded.kick_users) {
1405+
const consumerId = request.data.user_id;
1406+
if (this._protooRoom.hasPeer(consumerId)) {
1407+
this._protooRoom.getPeer(consumerId).close();
1408+
}
1409+
accept();
1410+
} else {
1411+
reject(401);
1412+
}
1413+
}
1414+
});
1415+
break;
1416+
}
1417+
1418+
case 'block':
1419+
{
1420+
const localPeer = peer;
1421+
const remotePeerId = request.data.whom;
1422+
1423+
const localConsumerId = localPeer.data.peerToConsumers.get(remotePeerId);
1424+
if (localPeer.data.consumers.has(localConsumerId)) {
1425+
localPeer.data.blockedPeers.add(remotePeerId);
1426+
localPeer.data.consumers.get(localConsumerId).pause();
1427+
1428+
if (this._protooRoom.hasPeer(remotePeerId)) {
1429+
const remotePeer = this._protooRoom.getPeer(remotePeerId)
1430+
const remoteConsumerId = remotePeer.data.peerToConsumers.get(localPeer.id);
1431+
if(remotePeer.data.consumers.has(remoteConsumerId)) {
1432+
remotePeer.data.consumers.get(remoteConsumerId).pause();
1433+
remotePeer.notify('peerBlocked', { peerId: localPeer.id }).catch(() => {});
1434+
}
1435+
}
1436+
}
1437+
accept();
1438+
break;
1439+
}
1440+
1441+
case 'unblock':
1442+
{
1443+
const localPeer = peer;
1444+
const remotePeerId = request.data.whom;
1445+
1446+
localPeer.data.blockedPeers.delete(remotePeerId);
1447+
1448+
if (this._protooRoom.hasPeer(remotePeerId)) {
1449+
const remotePeer = this._protooRoom.getPeer(remotePeerId);
1450+
if (!remotePeer.data.blockedPeers.has(localPeer.id) &&
1451+
localPeer.data.consumers.has(localPeer.data.peerToConsumers.get(remotePeerId)) &&
1452+
remotePeer.data.consumers.has(remotePeer.data.peerToConsumers.get(localPeer.id))) {
1453+
localPeer.data.consumers.get(localPeer.data.peerToConsumers.get(remotePeerId)).resume();
1454+
remotePeer.data.consumers.get(remotePeer.data.peerToConsumers.get(localPeer.id)).resume();
1455+
localPeer.notify('peerUnblocked', { peerId: remotePeer.id }).catch(() => {});
1456+
remotePeer.notify('peerUnblocked', { peerId: localPeer.id }).catch(() => {});
1457+
}
1458+
}
1459+
accept();
1460+
break;
1461+
}
1462+
13901463
default:
13911464
{
13921465
logger.error('unknown request.method "%s"', request.method);
@@ -1471,17 +1544,21 @@ class Room extends EventEmitter
14711544
// Store the Consumer into the protoo consumerPeer data Object.
14721545
consumerPeer.data.consumers.set(consumer.id, consumer);
14731546

1547+
consumerPeer.data.peerToConsumers.set(producerPeer.id, consumer.id);
1548+
14741549
// Set Consumer events.
14751550
consumer.on('transportclose', () =>
14761551
{
14771552
// Remove from its map.
14781553
consumerPeer.data.consumers.delete(consumer.id);
1554+
consumerPeer.data.peerToConsumers.delete(producerPeer.id);
14791555
});
14801556

14811557
consumer.on('producerclose', () =>
14821558
{
14831559
// Remove from its map.
14841560
consumerPeer.data.consumers.delete(consumer.id);
1561+
consumerPeer.data.peerToConsumers.delete(producerPeer.id);
14851562

14861563
consumerPeer.notify('consumerClosed', { consumerId: consumer.id })
14871564
.catch(() => {});

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"debug": "^4.1.1",
1818
"express": "^4.17.1",
1919
"heapdump": "^0.3.15",
20+
"jsonwebtoken": "^8.5.1",
2021
"mediasoup": "github:versatica/mediasoup#v3",
2122
"pidusage": "^2.0.18",
2223
"protoo-server": "^4.0.4",

0 commit comments

Comments
 (0)