diff --git a/HAproxy.md b/HAproxy.md
index b738097..485e11e 100644
--- a/HAproxy.md
+++ b/HAproxy.md
@@ -62,14 +62,6 @@ OR
## Configure multiparty-meeting servers
-### App config
-
-mm/configs/app/config.js
-
-``` js
-multipartyServer : 'meet.example.com',
-```
-
### Server config
mm/configs/server/config.js
diff --git a/app/package.json b/app/package.json
index 1dab672..9392a4a 100644
--- a/app/package.json
+++ b/app/package.json
@@ -60,6 +60,7 @@
],
"devDependencies": {
"electron": "^7.1.1",
+ "eslint-plugin-react": "^7.19.0",
"foreman": "^3.0.1",
"redux-mock-store": "^1.5.3"
}
diff --git a/app/public/config/config.example.js b/app/public/config/config.example.js
index 9e16360..cf2703d 100644
--- a/app/public/config/config.example.js
+++ b/app/public/config/config.example.js
@@ -1,9 +1,9 @@
// eslint-disable-next-line
var config =
{
- loginEnabled : false,
- developmentPort : 3443,
- productionPort : 443,
+ loginEnabled : false,
+ developmentPort : 3443,
+ productionPort : 443,
/**
* If defaultResolution is set, it will override user settings when joining:
@@ -25,6 +25,7 @@ var config =
{ scaleResolutionDownBy: 2 },
{ scaleResolutionDownBy: 1 }
],
+
/**
* White listing browsers that support audio output device selection.
* It is not yet fully implemented in Firefox.
@@ -41,13 +42,18 @@ var config =
{
tcp : true
},
- lastN : 4,
- mobileLastN : 1,
- background : 'images/background.jpg',
+ defaultLayout : 'democratic', // democratic, filmstrip
+ lastN : 4,
+ mobileLastN : 1,
+ // Highest number of speakers user can select
+ maxLastN : 5,
+ // If truthy, users can NOT change number of speakers visible
+ lockLastN : false,
+ background : 'images/background.jpg',
// Add file and uncomment for adding logo to appbar
// logo : 'images/logo.svg',
- title : 'Multiparty meeting',
- theme :
+ title : 'Multiparty meeting',
+ theme :
{
palette :
{
diff --git a/app/public/privacy/privacy.html b/app/public/privacy/privacy.html
new file mode 100644
index 0000000..89b4959
--- /dev/null
+++ b/app/public/privacy/privacy.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ Pleaceholder for Privacy Statetment/Policy, AUP
+
+
+ Privacy Statement
+ Privacy Policy
+ Acceptable use policy (AUP)
+
+
\ No newline at end of file
diff --git a/app/src/RoomClient.js b/app/src/RoomClient.js
index 630851a..d2ab9bc 100644
--- a/app/src/RoomClient.js
+++ b/app/src/RoomClient.js
@@ -31,8 +31,7 @@ let Spotlights;
let requestTimeout,
transportOptions,
lastN,
- mobileLastN,
- defaultResolution;
+ mobileLastN;
if (process.env.NODE_ENV !== 'test')
{
@@ -40,8 +39,7 @@ if (process.env.NODE_ENV !== 'test')
requestTimeout,
transportOptions,
lastN,
- mobileLastN,
- defaultResolution
+ mobileLastN
} = window.config);
}
@@ -205,9 +203,6 @@ export default class RoomClient
// Our WebTorrent client
this._webTorrent = null;
- if (defaultResolution)
- store.dispatch(settingsActions.setVideoResolution(defaultResolution));
-
// Max spotlights
if (device.platform === 'desktop')
this._maxSpotlights = lastN;
@@ -470,9 +465,9 @@ export default class RoomClient
});
}
- login()
+ login(roomId = this._roomId)
{
- const url = `/auth/login?peerId=${this._peerId}&roomId=${this._roomId}`;
+ const url = `/auth/login?peerId=${this._peerId}&roomId=${roomId}`;
window.open(url, 'loginWindow');
}
@@ -534,11 +529,6 @@ export default class RoomClient
}
}
- notify(text)
- {
- store.dispatch(requestActions.notify({ text: text }));
- }
-
timeoutCallback(callback)
{
let called = false;
@@ -684,7 +674,7 @@ export default class RoomClient
{
if (err)
{
- return store.dispatch(requestActions.notify(
+ store.dispatch(requestActions.notify(
{
type : 'error',
text : intl.formatMessage({
@@ -692,6 +682,8 @@ export default class RoomClient
defaultMessage : 'Unable to save file'
})
}));
+
+ return;
}
saveAs(blob, file.name);
@@ -708,7 +700,9 @@ export default class RoomClient
if (existingTorrent)
{
// Never add duplicate torrents, use the existing one instead.
- return this._handleTorrent(existingTorrent);
+ this._handleTorrent(existingTorrent);
+
+ return;
}
this._webTorrent.add(magnetUri, this._handleTorrent);
@@ -720,11 +714,13 @@ export default class RoomClient
// same file was sent multiple times.
if (torrent.progress === 1)
{
- return store.dispatch(
+ store.dispatch(
fileActions.setFileDone(
torrent.magnetURI,
torrent.files
));
+
+ return;
}
let lastMove = 0;
@@ -767,7 +763,7 @@ export default class RoomClient
{
if (err)
{
- return store.dispatch(requestActions.notify(
+ store.dispatch(requestActions.notify(
{
type : 'error',
text : intl.formatMessage({
@@ -775,13 +771,30 @@ export default class RoomClient
defaultMessage : 'Unable to share file'
})
}));
+
+ return;
}
const existingTorrent = this._webTorrent.get(torrent);
if (existingTorrent)
{
- return this._sendFile(existingTorrent.magnetURI);
+ store.dispatch(requestActions.notify(
+ {
+ text : intl.formatMessage({
+ id : 'filesharing.successfulFileShare',
+ defaultMessage : 'File successfully shared'
+ })
+ }));
+
+ store.dispatch(fileActions.addFile(
+ this._peerId,
+ existingTorrent.magnetURI
+ ));
+
+ this._sendFile(existingTorrent.magnetURI);
+
+ return;
}
this._webTorrent.seed(
@@ -873,7 +886,7 @@ export default class RoomClient
store.dispatch(
lobbyPeerActions.addLobbyPeer(peer.peerId));
store.dispatch(
- lobbyPeerActions.setLobbyPeerDisplayName(peer.displayName));
+ lobbyPeerActions.setLobbyPeerDisplayName(peer.displayName, peer.peerId));
store.dispatch(
lobbyPeerActions.setLobbyPeerPicture(peer.picture));
});
diff --git a/app/src/components/ChooseRoom.js b/app/src/components/ChooseRoom.js
index b81ff47..3d549b3 100644
--- a/app/src/components/ChooseRoom.js
+++ b/app/src/components/ChooseRoom.js
@@ -86,7 +86,7 @@ const DialogTitle = withStyles(styles)((props) =>
return (
- { window.config && window.config.logo &&
}
+ { window.config.logo &&
}
{children}
);
@@ -125,7 +125,7 @@ const ChooseRoom = ({
}}
>
- { window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
+ { window.config.title ? window.config.title : 'Multiparty meeting' }
diff --git a/app/src/components/Containers/Peer.js b/app/src/components/Containers/Peer.js
index 559d625..827550b 100644
--- a/app/src/components/Containers/Peer.js
+++ b/app/src/components/Containers/Peer.js
@@ -12,8 +12,8 @@ import { useIntl, FormattedMessage } from 'react-intl';
import VideoView from '../VideoContainers/VideoView';
import Tooltip from '@material-ui/core/Tooltip';
import Fab from '@material-ui/core/Fab';
-import MicIcon from '@material-ui/icons/Mic';
-import MicOffIcon from '@material-ui/icons/MicOff';
+import VolumeUpIcon from '@material-ui/icons/VolumeUp';
+import VolumeOffIcon from '@material-ui/icons/VolumeOff';
import NewWindowIcon from '@material-ui/icons/OpenInNew';
import FullScreenIcon from '@material-ui/icons/Fullscreen';
import Volume from './Volume';
@@ -252,9 +252,9 @@ const Peer = (props) =>
}}
>
{ micEnabled ?
-
+
:
-
+
}
@@ -340,6 +340,7 @@ const Peer = (props) =>
videoMultiLayer={webcamConsumer && webcamConsumer.type !== 'simple'}
videoTrack={webcamConsumer && webcamConsumer.track}
videoVisible={videoVisible}
+ audioTrack={micConsumer && micConsumer.track}
audioCodec={micConsumer && micConsumer.codec}
videoCodec={webcamConsumer && webcamConsumer.codec}
audioScore={micConsumer ? micConsumer.score : null}
diff --git a/app/src/components/Controls/TopBar.js b/app/src/components/Controls/TopBar.js
index 2cc380a..277507b 100644
--- a/app/src/components/Controls/TopBar.js
+++ b/app/src/components/Controls/TopBar.js
@@ -80,6 +80,10 @@ const styles = (theme) =>
{
margin : theme.spacing(1, 0),
padding : theme.spacing(0, 1)
+ },
+ green :
+ {
+ color : 'rgba(0, 153, 0, 1)'
}
});
@@ -136,6 +140,7 @@ const TopBar = (props) =>
openUsersTab,
unread,
canLock,
+ canPromote,
classes
} = props;
@@ -194,14 +199,14 @@ const TopBar = (props) =>
- { window.config && window.config.logo &&
}
+ { window.config.logo &&
}
- { window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
+ { window.config.title ? window.config.title : 'Multiparty meeting' }
@@ -305,6 +310,7 @@ const TopBar = (props) =>
defaultMessage : 'Show lobby'
})}
color='inherit'
+ disabled={!canPromote}
onClick={() => setLockDialogOpen(!room.lockDialogOpen)}
>
{ myPicture ?
:
-
+
}
@@ -380,6 +386,7 @@ TopBar.propTypes =
openUsersTab : PropTypes.func.isRequired,
unread : PropTypes.number.isRequired,
canLock : PropTypes.bool.isRequired,
+ canPromote : PropTypes.bool.isRequired,
classes : PropTypes.object.isRequired,
theme : PropTypes.object.isRequired
};
@@ -397,7 +404,10 @@ const mapStateToProps = (state) =>
state.toolarea.unreadFiles,
canLock :
state.me.roles.some((role) =>
- state.room.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role))
+ state.room.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)),
+ canPromote :
+ state.me.roles.some((role) =>
+ state.room.permissionsFromRoles.PROMOTE_PEER.includes(role))
});
const mapDispatchToProps = (dispatch) =>
diff --git a/app/src/components/JoinDialog.js b/app/src/components/JoinDialog.js
index 85262b1..1f270c6 100644
--- a/app/src/components/JoinDialog.js
+++ b/app/src/components/JoinDialog.js
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core/styles';
import { withRoomContext } from '../RoomContext';
+import classnames from 'classnames';
import isElectron from 'is-electron';
import * as settingsActions from '../actions/settingsActions';
import PropTypes from 'prop-types';
@@ -128,9 +129,9 @@ const DialogTitle = withStyles(styles)((props) =>
return (
- { window.config && window.config.logo &&
}
+ { window.config.logo &&
}
{children}
- { window.config && window.config.loginEnabled &&
+ { window.config.loginEnabled &&
{ myPicture ?
:
-
+
}
@@ -217,11 +220,11 @@ const JoinDialog = ({
myPicture={myPicture}
onLogin={() =>
{
- loggedIn ? roomClient.logout() : roomClient.login();
+ loggedIn ? roomClient.logout() : roomClient.login(roomId);
}}
loggedIn={loggedIn}
>
- { window.config && window.config.title ? window.config.title : 'Multiparty meeting' }
+ { window.config.title ? window.config.title : 'Multiparty meeting' }
@@ -316,6 +319,7 @@ const JoinDialog = ({
className={classes.green}
gutterBottom
variant='h6'
+ style={{ fontWeight: '600' }}
align='center'
>
{ room.signInRequired ?
-
+
:
-
+
{
if (event.target.files.length > 0)
{
- props.roomClient.shareFiles(event.target.files);
+ await props.roomClient.shareFiles(event.target.files);
}
};
@@ -65,6 +65,8 @@ const FileSharing = (props) =>
type='file'
disabled={!canShare}
onChange={handleFileChange}
+ // Need to reset to be able to share same file twice
+ onClick={(e) => (e.target.value = null)}
id='share-files-button'
/>
+
+
{children}
@@ -309,52 +326,84 @@ class VideoView extends React.PureComponent
componentDidMount()
{
- const { videoTrack } = this.props;
+ const { videoTrack, audioTrack } = this.props;
- this._setTracks(videoTrack);
+ this._setTracks(videoTrack, audioTrack);
}
componentWillUnmount()
{
clearInterval(this._videoResolutionTimer);
+
+ const { videoElement } = this.refs;
+
+ if (videoElement)
+ {
+ videoElement.oncanplay = null;
+ videoElement.onplay = null;
+ videoElement.onpause = null;
+ }
}
- // eslint-disable-next-line camelcase
- UNSAFE_componentWillReceiveProps(nextProps)
+ componentWillUpdate()
{
- const { videoTrack } = nextProps;
-
- this._setTracks(videoTrack);
+ const { videoTrack, audioTrack } = this.props;
+ this._setTracks(videoTrack, audioTrack);
}
- _setTracks(videoTrack)
+ _setTracks(videoTrack, audioTrack)
{
- if (this._videoTrack === videoTrack)
+ if (this._videoTrack === videoTrack && this._audioTrack === audioTrack)
return;
this._videoTrack = videoTrack;
+ this._audioTrack = audioTrack;
clearInterval(this._videoResolutionTimer);
this._hideVideoResolution();
- const { video } = this.refs;
+ const { videoElement, audioElement } = this.refs;
if (videoTrack)
{
const stream = new MediaStream();
- if (videoTrack)
- stream.addTrack(videoTrack);
+ stream.addTrack(videoTrack);
- video.srcObject = stream;
+ videoElement.srcObject = stream;
- if (videoTrack)
- this._showVideoResolution();
+ videoElement.oncanplay = () => this.setState({ videoCanPlay: true });
+
+ videoElement.onplay = () =>
+ {
+ audioElement.play()
+ .catch((error) => logger.warn('audioElement.play() [error:"%o]', error));
+ };
+
+ videoElement.play()
+ .catch((error) => logger.warn('videoElement.play() [error:"%o]', error));
+
+ this._showVideoResolution();
}
else
{
- video.srcObject = null;
+ videoElement.srcObject = null;
+ }
+
+ if (audioTrack)
+ {
+ const stream = new MediaStream();
+
+ stream.addTrack(audioTrack);
+ audioElement.srcObject = stream;
+
+ audioElement.play()
+ .catch((error) => logger.warn('audioElement.play() [error:"%o]', error));
+ }
+ else
+ {
+ audioElement.srcObject = null;
}
}
@@ -363,16 +412,19 @@ class VideoView extends React.PureComponent
this._videoResolutionTimer = setInterval(() =>
{
const { videoWidth, videoHeight } = this.state;
- const { video } = this.refs;
+ const { videoElement } = this.refs;
// Don't re-render if nothing changed.
- if (video.videoWidth === videoWidth && video.videoHeight === videoHeight)
+ if (
+ videoElement.videoWidth === videoWidth &&
+ videoElement.videoHeight === videoHeight
+ )
return;
this.setState(
{
- videoWidth : video.videoWidth,
- videoHeight : video.videoHeight
+ videoWidth : videoElement.videoWidth,
+ videoHeight : videoElement.videoHeight
});
}, 1000);
}
@@ -392,6 +444,7 @@ VideoView.propTypes =
videoContain : PropTypes.bool,
advancedMode : PropTypes.bool,
videoTrack : PropTypes.any,
+ audioTrack : PropTypes.any,
videoVisible : PropTypes.bool.isRequired,
consumerSpatialLayers : PropTypes.number,
consumerTemporalLayers : PropTypes.number,
diff --git a/app/src/index.js b/app/src/index.js
index dd052fa..90bfa4d 100644
--- a/app/src/index.js
+++ b/app/src/index.js
@@ -37,6 +37,7 @@ import messagesCroatian from './translations/hr';
import messagesCzech from './translations/cs';
import messagesItalian from './translations/it';
import messagesUkrainian from './translations/uk';
+import messagesTurkish from './translations/tr';
import './index.css';
@@ -61,7 +62,8 @@ const messages =
'hr' : messagesCroatian,
'cs' : messagesCzech,
'it' : messagesItalian,
- 'uk' : messagesUkrainian
+ 'uk' : messagesUkrainian,
+ 'tr' : messagesTurkish
};
const locale = navigator.language.split(/[-_]/)[0]; // language without region code
diff --git a/app/src/reducers/room.js b/app/src/reducers/room.js
index 6340b40..54c4011 100644
--- a/app/src/reducers/room.js
+++ b/app/src/reducers/room.js
@@ -1,19 +1,22 @@
const initialState =
{
name : '',
- state : 'new', // new/connecting/connected/disconnected/closed,
+ // new/connecting/connected/disconnected/closed,
+ state : 'new',
locked : false,
inLobby : false,
signInRequired : false,
- accessCode : '', // access code to the room if locked and joinByAccessCode == true
- joinByAccessCode : true, // if true: accessCode is a possibility to open the room
+ // access code to the room if locked and joinByAccessCode == true
+ accessCode : '',
+ // if true: accessCode is a possibility to open the room
+ joinByAccessCode : true,
activeSpeakerId : null,
torrentSupport : false,
showSettings : false,
fullScreenConsumer : null, // ConsumerID
windowConsumer : null, // ConsumerID
toolbarsVisible : true,
- mode : 'democratic',
+ mode : window.config.defaultLayout || 'democratic',
selectedPeerId : null,
spotlights : [],
settingsOpen : false,
diff --git a/app/src/reducers/settings.js b/app/src/reducers/settings.js
index 0be91a8..2b2325b 100644
--- a/app/src/reducers/settings.js
+++ b/app/src/reducers/settings.js
@@ -4,7 +4,8 @@ const initialState =
selectedWebcam : null,
selectedAudioDevice : null,
advancedMode : false,
- resolution : 'medium', // low, medium, high, veryhigh, ultra
+ // low, medium, high, veryhigh, ultra
+ resolution : window.config.defaultResolution || 'medium',
lastN : 4,
permanentTopBar : true
};
diff --git a/app/src/translations/cn.json b/app/src/translations/cn.json
index e8905aa..2d5f9bc 100644
--- a/app/src/translations/cn.json
+++ b/app/src/translations/cn.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -69,6 +70,8 @@
"tooltip.settings": "显示设置",
"tooltip.participants": "显示参加者",
"tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "房间名称",
"label.chooseRoomButton": "继续",
diff --git a/app/src/translations/cs.json b/app/src/translations/cs.json
index bcac576..f9ca81d 100644
--- a/app/src/translations/cs.json
+++ b/app/src/translations/cs.json
@@ -54,6 +54,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -66,6 +67,10 @@
"tooltip.leaveFullscreen": "Vypnout režim celé obrazovky (fullscreen)",
"tooltip.lobby": "Ukázat Přijímací místnost",
"tooltip.settings": "Zobrazit nastavení",
+ "tooltip.participants": null,
+ "tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Jméno místnosti",
"label.chooseRoomButton": "Pokračovat",
diff --git a/app/src/translations/de.json b/app/src/translations/de.json
index 3de9c84..a921590 100644
--- a/app/src/translations/de.json
+++ b/app/src/translations/de.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": "Dein Browser unterstützt keine Spracherkennung",
+ "room.moderatoractions": null,
"me.mutedPTT": "Du bist stummgeschalted, Halte die SPACE-Taste um zu sprechen",
@@ -69,6 +70,8 @@
"tooltip.settings": "Einstellungen",
"tooltip.participants": "Teilnehmer",
"tooltip.kickParticipant": "Teilnehmer rauswerfen",
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Name des Raums",
"label.chooseRoomButton": "Weiter",
diff --git a/app/src/translations/dk.json b/app/src/translations/dk.json
index 72dd7fa..9296f73 100644
--- a/app/src/translations/dk.json
+++ b/app/src/translations/dk.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -69,6 +70,8 @@
"tooltip.settings": "Vis indstillinger",
"tooltip.participants": "Vis deltagere",
"tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Værelsesnavn",
"label.chooseRoomButton": "Fortsæt",
diff --git a/app/src/translations/el.json b/app/src/translations/el.json
index 4382464..579ba8c 100644
--- a/app/src/translations/el.json
+++ b/app/src/translations/el.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -69,6 +70,8 @@
"tooltip.settings": "Εμφάνιση ρυθμίσεων",
"tooltip.participants": "Εμφάνιση συμμετεχόντων",
"tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Όνομα δωματίου",
"label.chooseRoomButton": "Συνέχεια",
diff --git a/app/src/translations/en.json b/app/src/translations/en.json
index 64e573f..a5a7920 100644
--- a/app/src/translations/en.json
+++ b/app/src/translations/en.json
@@ -55,6 +55,7 @@
"room.clearChat": "Clear chat",
"room.clearFileSharing": "Clear files",
"room.speechUnsupported": "Your browser does not support speech recognition",
+ "room.moderatoractions": "Moderator actions",
"me.mutedPTT": "You are muted, hold down SPACE-BAR to talk",
@@ -69,6 +70,8 @@
"tooltip.settings": "Show settings",
"tooltip.participants": "Show participants",
"tooltip.kickParticipant": "Kick out participant",
+ "tooltip.muteParticipant": "Mute participant",
+ "tooltip.muteParticipantVideo": "Mute participant video",
"label.roomName": "Room name",
"label.chooseRoomButton": "Continue",
diff --git a/app/src/translations/es.json b/app/src/translations/es.json
index f9934f0..a7ef07c 100644
--- a/app/src/translations/es.json
+++ b/app/src/translations/es.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -69,6 +70,8 @@
"tooltip.settings": "Mostrar ajustes",
"tooltip.participants": "Mostrar participantes",
"tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Nombre de la sala",
"label.chooseRoomButton": "Continuar",
diff --git a/app/src/translations/fr.json b/app/src/translations/fr.json
index 92b0eeb..f4e8bf0 100644
--- a/app/src/translations/fr.json
+++ b/app/src/translations/fr.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -69,6 +70,8 @@
"tooltip.settings": "Afficher les paramètres",
"tooltip.participants": "Afficher les participants",
"tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Nom de la salle",
"label.chooseRoomButton": "Continuer",
diff --git a/app/src/translations/hr.json b/app/src/translations/hr.json
index 5b98eee..8e90529 100644
--- a/app/src/translations/hr.json
+++ b/app/src/translations/hr.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": "Vaš preglednik ne podržava prepoznavanje govora",
+ "room.moderatoractions": null,
"me.mutedPTT": "Utišani ste, pritisnite i držite SPACE tipku za razgovor",
@@ -69,6 +70,8 @@
"tooltip.settings": "Prikaži postavke",
"tooltip.participants": "Pokažite sudionike",
"tooltip.kickParticipant": "Izbaci sudionika",
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Naziv sobe",
"label.chooseRoomButton": "Nastavi",
diff --git a/app/src/translations/hu.json b/app/src/translations/hu.json
index 0f00171..1afaeea 100644
--- a/app/src/translations/hu.json
+++ b/app/src/translations/hu.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -69,6 +70,8 @@
"tooltip.settings": "Beállítások",
"tooltip.participants": "Résztvevők",
"tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Konferencia",
"label.chooseRoomButton": "Tovább",
diff --git a/app/src/translations/it.json b/app/src/translations/it.json
index 377c0b4..fb61222 100644
--- a/app/src/translations/it.json
+++ b/app/src/translations/it.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -68,6 +69,8 @@
"tooltip.lobby": "Mostra lobby",
"tooltip.settings": "Mostra impostazioni",
"tooltip.participants": "Mostra partecipanti",
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Nome della stanza",
"label.chooseRoomButton": "Continua",
diff --git a/app/src/translations/nb.json b/app/src/translations/nb.json
index 0ee6ed7..275ce5a 100644
--- a/app/src/translations/nb.json
+++ b/app/src/translations/nb.json
@@ -55,6 +55,7 @@
"room.clearChat": "Tøm chat",
"room.clearFileSharing": "Fjern filer",
"room.speechUnsupported": "Din nettleser støtter ikke stemmegjenkjenning",
+ "room.moderatoractions": "Moderatorhandlinger",
"me.mutedPTT": "Du er dempet, hold nede SPACE for å snakke",
@@ -69,6 +70,8 @@
"tooltip.settings": "Vis innstillinger",
"tooltip.participants": "Vis deltakere",
"tooltip.kickParticipant": "Spark ut deltaker",
+ "tooltip.muteParticipant": "Demp deltaker",
+ "tooltip.muteParticipantVideo": "Demp deltakervideo",
"label.roomName": "Møtenavn",
"label.chooseRoomButton": "Fortsett",
diff --git a/app/src/translations/pl.json b/app/src/translations/pl.json
index 6f1c684..f334513 100644
--- a/app/src/translations/pl.json
+++ b/app/src/translations/pl.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -69,6 +70,8 @@
"tooltip.settings": "Pokaż ustawienia",
"tooltip.participants": "Pokaż uczestników",
"tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Nazwa konferencji",
"label.chooseRoomButton": "Kontynuuj",
diff --git a/app/src/translations/pt.json b/app/src/translations/pt.json
index 6a9a1e3..0f8b940 100644
--- a/app/src/translations/pt.json
+++ b/app/src/translations/pt.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -69,6 +70,8 @@
"tooltip.settings": "Apresentar definições",
"tooltip.participants": "Apresentar participantes",
"tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Nome da sala",
"label.chooseRoomButton": "Continuar",
diff --git a/app/src/translations/ro.json b/app/src/translations/ro.json
index 89ba3cf..a28a9b5 100644
--- a/app/src/translations/ro.json
+++ b/app/src/translations/ro.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"me.mutedPTT": null,
@@ -69,6 +70,8 @@
"tooltip.settings": "Arată setăile",
"tooltip.participants": null,
"tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Numele camerei",
"label.chooseRoomButton": "Continuare",
diff --git a/app/src/translations/tr.json b/app/src/translations/tr.json
new file mode 100644
index 0000000..99f2556
--- /dev/null
+++ b/app/src/translations/tr.json
@@ -0,0 +1,150 @@
+{
+ "socket.disconnected": "Bağlantınız Kesildi",
+ "socket.reconnecting": "Bağlantınız kesildi, yeniden bağlanmaya çalışılıyor",
+ "socket.reconnected": "Yeniden bağlandınız",
+ "socket.requestError": "Sunucu isteğinde hata",
+
+ "room.chooseRoom": "Katılmak istediğiniz odanın adını seçin",
+ "room.cookieConsent": "Bu web sayfası kullanıcı deneyimini geliştirmek için çerezleri kullanmaktadır",
+ "room.consentUnderstand": "Anladım",
+ "room.joined": "Odaya katıldın",
+ "room.cantJoin": "Odaya katılamadın",
+ "room.youLocked": "Odayı kilitledin",
+ "room.cantLock": "Oda kilitlenemiyor",
+ "room.youUnLocked": "Odanın kilidini açtın",
+ "room.cantUnLock": "Odanın kilidi açılamıyor",
+ "room.locked": "Oda kilitlendi",
+ "room.unlocked": "Oda kilidi açıldı",
+ "room.newLobbyPeer": "Lobiye yeni katılımcı girdi",
+ "room.lobbyPeerLeft": "Lobiden katılımcı ayrıldı",
+ "room.lobbyPeerChangedDisplayName": "Lobideki katılımcı adını {displayName} olarak değiştirdi",
+ "room.lobbyPeerChangedPicture": "Lobideki katılımcı resim değiştirdi",
+ "room.setAccessCode": "Oda için erişim kodu güncellendi",
+ "room.accessCodeOn": "Oda erişim kodu etkinleştirildi",
+ "room.accessCodeOff": "Oda erişim kodu devre dışı",
+ "room.peerChangedDisplayName": "{oldDisplayName}, {displayName} olarak değiştirildi",
+ "room.newPeer": "{displayName} odaya katıldı",
+ "room.newFile": "Yeni dosya mevcut",
+ "room.toggleAdvancedMode": "Gelişmiş moda geçiş",
+ "room.setDemocraticView": "Demokratik görünüme geçtiniz",
+ "room.setFilmStripView": "Filmşeridi görünümüne geçtiniz",
+ "room.loggedIn": "Giriş yaptınız",
+ "room.loggedOut": "Çıkış yaptınız",
+ "room.changedDisplayName": "Adınız {displayName} olarak değiştirildi",
+ "room.changeDisplayNameError": "Adınız değiştirilirken bir hata oluştu",
+ "room.chatError": "Sohbet mesajı gönderilemiyor",
+ "room.aboutToJoin": "Toplantıya katılmak üzeresiniz",
+ "room.roomId": "Oda ID: {roomName}",
+ "room.setYourName": "Katılım için adınızı belirleyin ve nasıl katılmak istediğinizi seçin:",
+ "room.audioOnly": "Sadece ses",
+ "room.audioVideo": "Ses ve Video",
+ "room.youAreReady": "Tamam, hazırsın",
+ "room.emptyRequireLogin": "Oda boş! Toplantıyı başlatmak için oturum açabilirsiniz veya toplantı sahibi katılana kadar bekleyebilirsiniz",
+ "room.locketWait": "Oda kilitli - birisi içeri alana kadar bekleyiniz ...",
+ "room.lobbyAdministration": "Lobi Yöneticisi",
+ "room.peersInLobby": "Lobideki katılımcılar",
+ "room.lobbyEmpty": "Lobide katılımcı yok",
+ "room.hiddenPeers": "{hiddenPeersCount, plural, one {participant} other {participants}}",
+ "room.me": "Ben",
+ "room.spotlights": "Gündemdeki Katılımcılar",
+ "room.passive": "Pasif Katılımcılar",
+ "room.videoPaused": "Video duraklatıldı",
+ "room.muteAll": null,
+ "room.stopAllVideo": null,
+ "room.closeMeeting": null,
+ "room.clearChat": null,
+ "room.clearFileSharing": null,
+ "room.speechUnsupported": null,
+ "room.moderatoractions": null,
+
+ "tooltip.login": "Giriş",
+ "tooltip.logout": "Çıkış",
+ "tooltip.admitFromLobby": "Lobiden içeri al",
+ "tooltip.lockRoom": "Oda kilitle",
+ "tooltip.unLockRoom": "Oda kilidini aç",
+ "tooltip.enterFullscreen": "Tam Ekrana Geç",
+ "tooltip.leaveFullscreen": "Tam Ekrandan Çık",
+ "tooltip.lobby": "Lobiyi göster",
+ "tooltip.settings": "Ayarları göster",
+ "tooltip.participants": "Katılımcıları göster",
+ "tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
+
+ "label.roomName": "Oda adı",
+ "label.chooseRoomButton": "Devam",
+ "label.yourName": "Adınız",
+ "label.newWindow": "Yeni pencere",
+ "label.fullscreen": "Tam Ekran",
+ "label.openDrawer": "Çiziciyi aç",
+ "label.leave": "Ayrıl",
+ "label.chatInput": "Sohbet mesajı gir...",
+ "label.chat": "Sohbet",
+ "label.filesharing": "Dosya paylaşım",
+ "label.participants": "Katılımcı",
+ "label.shareFile": "Dosya paylaş",
+ "label.fileSharingUnsupported": "Dosya paylaşımı desteklenmiyor",
+ "label.unknown": "Bilinmeyen",
+ "label.democratic": "Demokratik görünüm",
+ "label.filmstrip": "Filmşeridi görünüm",
+ "label.low": "Düşük",
+ "label.medium": "Orta",
+ "label.high": "Yüksek (HD)",
+ "label.veryHigh": "Çok Yüksek (FHD)",
+ "label.ultra": "Ultra (UHD)",
+ "label.close": "Kapat",
+
+ "settings.settings": "Ayarlar",
+ "settings.camera": "Kamera",
+ "settings.selectCamera": "Video aygıtını seç",
+ "settings.cantSelectCamera": "Video aygıtı seçilemiyor",
+ "settings.audio": "Ses aygıtı",
+ "settings.selectAudio": "Ses aygıtını seç",
+ "settings.cantSelectAudio": "Ses aygıtı seçilemiyor",
+ "settings.resolution": "Video çözünürlüğü ayarla",
+ "settings.layout": "Oda düzeni",
+ "settings.selectRoomLayout": "Oda düzeni seç",
+ "settings.advancedMode": "Detaylı mod",
+ "settings.permanentTopBar": "Üst barı kalıcı yap",
+ "settings.lastn": "İzlenebilir video sayısı",
+
+ "filesharing.saveFileError": "Dosya kaydedilemiyor",
+ "filesharing.startingFileShare": "Paylaşılan dosyaya erişiliyor",
+ "filesharing.successfulFileShare": "Dosya başarıyla paylaşıldı",
+ "filesharing.unableToShare": "Dosya paylaşılamıyor",
+ "filesharing.error": "Dosya paylaşım hatası",
+ "filesharing.finished": "Dosya indirilmesi tamamlandı",
+ "filesharing.save": "Kaydet",
+ "filesharing.sharedFile": "{displayName} bir dosya paylaştı",
+ "filesharing.download": "İndir",
+ "filesharing.missingSeeds": "İşlem uzun zaman alıyorsa, bu torrent'i paylaşan kimse olmayabilir. İlgili dosyayı yeniden yüklemesini isteyin.",
+
+ "devices.devicesChanged": "Cihazlarınız değişti, ayarlar kutusundan cihazlarınızı yapılandırın",
+
+ "device.audioUnsupported": "Ses desteklenmiyor",
+ "device.activateAudio": "Sesi aktif et",
+ "device.muteAudio": "Sesi kıs",
+ "device.unMuteAudio": "Sesi aç",
+
+ "device.videoUnsupported": "Video desteklenmiyor",
+ "device.startVideo": "Video başlat",
+ "device.stopVideo": "Video durdur",
+
+ "device.screenSharingUnsupported": "Ekran paylaşımı desteklenmiyor",
+ "device.startScreenSharing": "Ekran paylaşımını başlat",
+ "device.stopScreenSharing": "Ekran paylaşımını durdur",
+
+ "devices.microphoneDisconnected": "Mikrofon bağlı değil",
+ "devices.microphoneError": "Mikrofononuza erişilirken bir hata oluştu",
+ "devices.microPhoneMute": "Mikrofonumu kıs",
+ "devices.micophoneUnMute": "Mikrofonumu aç",
+ "devices.microphoneEnable": "Mikrofonumu aktif et",
+ "devices.microphoneMuteError": "Mikrofonunuz kısılamıyor",
+ "devices.microphoneUnMuteError": "Mikrofonunuz açılamıyor",
+
+ "devices.screenSharingDisconnected" : "Ekran paylaşımı bağlı değil",
+ "devices.screenSharingError": "Ekranınıza erişilirken bir hata oluştu",
+
+ "devices.cameraDisconnected": "Kamera bağlı değil",
+ "devices.cameraError": "Kameranıza erişilirken bir hata oluştu"
+}
diff --git a/app/src/translations/uk.json b/app/src/translations/uk.json
index 4974c87..bd59894 100644
--- a/app/src/translations/uk.json
+++ b/app/src/translations/uk.json
@@ -55,6 +55,7 @@
"room.clearChat": null,
"room.clearFileSharing": null,
"room.speechUnsupported": null,
+ "room.moderatoractions": null,
"tooltip.login": "Увійти",
"tooltip.logout": "Вихід",
@@ -66,6 +67,9 @@
"tooltip.lobby": "Показати зал очікувань",
"tooltip.settings": "Показати налаштування",
"tooltip.participants": "Показати учасників",
+ "tooltip.kickParticipant": null,
+ "tooltip.muteParticipant": null,
+ "tooltip.muteParticipantVideo": null,
"label.roomName": "Назва кімнати",
"label.chooseRoomButton": "Продовжити",
diff --git a/server/config/config.example.js b/server/config/config.example.js
index 22f3dcb..a1f9b84 100644
--- a/server/config/config.example.js
+++ b/server/config/config.example.js
@@ -36,14 +36,14 @@ module.exports =
},
*/
// URI and key for requesting geoip-based TURN server closest to the client
- turnAPIKey : 'examplekey',
- turnAPIURI : 'https://example.com/api/turn',
- turnAPIparams : {
- 'uri_schema' : 'turn',
- 'transport' : 'tcp',
- 'ip_ver' : 'ipv4',
- 'servercount' : '2'
- },
+ turnAPIKey : 'examplekey',
+ turnAPIURI : 'https://example.com/api/turn',
+ turnAPIparams : {
+ 'uri_schema' : 'turn',
+ 'transport' : 'tcp',
+ 'ip_ver' : 'ipv4',
+ 'servercount' : '2'
+ },
// Backup turnservers if REST fails or is not configured
backupTurnServers : [
@@ -60,23 +60,31 @@ module.exports =
// session cookie secret
cookieSecret : 'T0P-S3cR3t_cook!e',
cookieName : 'multiparty-meeting.sid',
+ // if you use encrypted private key the set the passphrase
tls :
{
cert : `${__dirname}/../certs/mediasoup-demo.localhost.cert.pem`,
+ // passphrase: 'key_password'
key : `${__dirname}/../certs/mediasoup-demo.localhost.key.pem`
},
// listening Host or IP
// If omitted listens on every IP. ("0.0.0.0" and "::")
- //listeningHost: 'localhost',
+ // listeningHost: 'localhost',
// Listening port for https server.
listeningPort : 443,
// Any http request is redirected to https.
- // Listening port for http server.
+ // Listening port for http server.
listeningRedirectPort : 80,
// Listens only on http, only on listeningPort
// listeningRedirectPort disabled
// use case: loadbalancer backend
httpOnly : false,
+ // WebServer/Express trust proxy config for httpOnly mode
+ // You can find more info:
+ // - https://expressjs.com/en/guide/behind-proxies.html
+ // - https://www.npmjs.com/package/proxy-addr
+ // use case: loadbalancer backend
+ trustProxy : '',
// This logger class will have the log function
// called every time there is a room created or destroyed,
// or peer created or destroyed. This would then be able
@@ -109,12 +117,6 @@ module.exports =
});
}
}, */
- // WebServer/Express trust proxy config for httpOnly mode
- // You can find more info:
- // - https://expressjs.com/en/guide/behind-proxies.html
- // - https://www.npmjs.com/package/proxy-addr
- // use case: loadbalancer backend
- trustProxy : '',
// This function will be called on successful login through oidc.
// Use this function to map your oidc userinfo to the Peer object.
// The roomId is equal to the room name.
@@ -211,7 +213,7 @@ module.exports =
//
// Example:
// [ userRoles.MODERATOR, userRoles.AUTHENTICATED ]
- accessFromRoles : {
+ accessFromRoles : {
// The role(s) will gain access to the room
// even if it is locked (!)
BYPASS_ROOM_LOCK : [ userRoles.ADMIN ],
@@ -222,7 +224,7 @@ module.exports =
// function, and change to BYPASS_LOBBY : [ userRoles.AUTHENTICATED ]
BYPASS_LOBBY : [ userRoles.NORMAL ]
},
- permissionsFromRoles : {
+ permissionsFromRoles : {
// The role(s) have permission to lock/unlock a room
CHANGE_ROOM_LOCK : [ userRoles.NORMAL ],
// The role(s) have permission to promote a peer from the lobby
@@ -242,9 +244,9 @@ module.exports =
},
// When truthy, the room will be open to all users when as long as there
// are allready users in the room
- activateOnHostJoin : true,
+ activateOnHostJoin : true,
// Mediasoup settings
- mediasoup :
+ mediasoup :
{
numWorkers : Object.keys(os.cpus()).length,
// mediasoup Worker settings.
@@ -325,11 +327,12 @@ module.exports =
{
listenIps :
[
- // change ip to your servers IP address!
- { ip: '0.0.0.0', announcedIp: null }
+ // change 192.0.2.1 IPv4 to your server's IPv4 address!!
+ { ip: '192.0.2.1', announcedIp: null }
// Can have multiple listening interfaces
- // { ip: '::/0', announcedIp: null }
+ // change 2001:DB8::1 IPv6 to your server's IPv6 address!!
+ // { ip: '2001:DB8::1', announcedIp: null }
],
initialAvailableOutgoingBitrate : 1000000,
minimumAvailableOutgoingBitrate : 600000,
diff --git a/server/lib/Room.js b/server/lib/Room.js
index acb2a10..c95ed93 100644
--- a/server/lib/Room.js
+++ b/server/lib/Room.js
@@ -9,6 +9,27 @@ const config = require('../config/config');
const logger = new Logger('Room');
+// In case they are not configured properly
+const accessFromRoles =
+{
+ BYPASS_ROOM_LOCK : [ userRoles.ADMIN ],
+ BYPASS_LOBBY : [ userRoles.NORMAL ],
+ ...config.accessFromRoles
+};
+
+const permissionsFromRoles =
+{
+ CHANGE_ROOM_LOCK : [ userRoles.NORMAL ],
+ PROMOTE_PEER : [ userRoles.NORMAL ],
+ SEND_CHAT : [ userRoles.NORMAL ],
+ MODERATE_CHAT : [ userRoles.MODERATOR ],
+ SHARE_SCREEN : [ userRoles.NORMAL ],
+ SHARE_FILE : [ userRoles.NORMAL ],
+ MODERATE_FILES : [ userRoles.MODERATOR ],
+ MODERATE_ROOM : [ userRoles.MODERATOR ],
+ ...config.permissionsFromRoles
+};
+
class Room extends EventEmitter
{
/**
@@ -152,7 +173,7 @@ class Room extends EventEmitter
if (returning)
this._peerJoining(peer, true);
else if ( // Has a role that is allowed to bypass room lock
- peer.roles.some((role) => config.accessFromRoles.BYPASS_ROOM_LOCK.includes(role))
+ peer.roles.some((role) => accessFromRoles.BYPASS_ROOM_LOCK.includes(role))
)
this._peerJoining(peer);
else if (this._locked)
@@ -160,7 +181,7 @@ class Room extends EventEmitter
else
{
// Has a role that is allowed to bypass lobby
- peer.roles.some((role) => config.accessFromRoles.BYPASS_LOBBY.includes(role)) ?
+ peer.roles.some((role) => accessFromRoles.BYPASS_LOBBY.includes(role)) ?
this._peerJoining(peer) :
this._handleGuest(peer);
}
@@ -187,7 +208,11 @@ class Room extends EventEmitter
this._peerJoining(promotedPeer);
- for (const peer of this._getJoinedPeers())
+ for (
+ const peer of this._getPeersWithPermission({
+ permission : permissionsFromRoles.PROMOTE_PEER
+ })
+ )
{
this._notification(peer.socket, 'lobby:promotedPeer', { peerId: id });
}
@@ -196,7 +221,7 @@ class Room extends EventEmitter
this._lobby.on('peerRolesChanged', (peer) =>
{
if ( // Has a role that is allowed to bypass room lock
- peer.roles.some((role) => config.accessFromRoles.BYPASS_ROOM_LOCK.includes(role))
+ peer.roles.some((role) => accessFromRoles.BYPASS_ROOM_LOCK.includes(role))
)
{
this._lobby.promotePeer(peer.id);
@@ -206,7 +231,7 @@ class Room extends EventEmitter
if ( // Has a role that is allowed to bypass lobby
!this._locked &&
- peer.roles.some((role) => config.accessFromRoles.BYPASS_LOBBY.includes(role))
+ peer.roles.some((role) => accessFromRoles.BYPASS_LOBBY.includes(role))
)
{
this._lobby.promotePeer(peer.id);
@@ -219,7 +244,11 @@ class Room extends EventEmitter
{
const { id, displayName } = changedPeer;
- for (const peer of this._getJoinedPeers())
+ for (
+ const peer of this._getPeersWithPermission({
+ permission : permissionsFromRoles.PROMOTE_PEER
+ })
+ )
{
this._notification(peer.socket, 'lobby:changeDisplayName', { peerId: id, displayName });
}
@@ -229,7 +258,11 @@ class Room extends EventEmitter
{
const { id, picture } = changedPeer;
- for (const peer of this._getJoinedPeers())
+ for (
+ const peer of this._getPeersWithPermission({
+ permission : permissionsFromRoles.PROMOTE_PEER
+ })
+ )
{
this._notification(peer.socket, 'lobby:changePicture', { peerId: id, picture });
}
@@ -241,7 +274,11 @@ class Room extends EventEmitter
const { id } = closedPeer;
- for (const peer of this._getJoinedPeers())
+ for (
+ const peer of this._getPeersWithPermission({
+ permission : permissionsFromRoles.PROMOTE_PEER
+ })
+ )
{
this._notification(peer.socket, 'lobby:peerClosed', { peerId: id });
}
@@ -344,7 +381,11 @@ class Room extends EventEmitter
{
this._lobby.parkPeer(parkPeer);
- for (const peer of this._getJoinedPeers())
+ for (
+ const peer of this._getPeersWithPermission({
+ permission : permissionsFromRoles.PROMOTE_PEER
+ })
+ )
{
this._notification(peer.socket, 'parkedPeer', { peerId: parkPeer.id });
}
@@ -384,8 +425,8 @@ class Room extends EventEmitter
{
params : {
...config.turnAPIparams,
- 'api_key' : config.turnAPIKey,
- 'ip' : peer.socket.request.connection.remoteAddress
+ 'api_key' : config.turnAPIKey,
+ 'ip' : peer.socket.request.connection.remoteAddress
}
});
@@ -550,7 +591,7 @@ class Room extends EventEmitter
peers : peerInfos,
tracker : config.fileTracker,
authenticated : peer.authenticated,
- permissionsFromRoles : config.permissionsFromRoles,
+ permissionsFromRoles : permissionsFromRoles,
userRoles : userRoles
});
@@ -682,9 +723,10 @@ class Room extends EventEmitter
if (
appData.source === 'screen' &&
- !peer.roles.some((role) => config.permissionsFromRoles.SHARE_SCREEN.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.SHARE_SCREEN.includes(role))
)
- throw new Error('peer not authorized');
+ throw new Error('peer not authorized');
// Ensure the Peer is joined.
if (!peer.joined)
@@ -985,7 +1027,7 @@ class Room extends EventEmitter
case 'chatMessage':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.SEND_CHAT.includes(role))
+ !peer.roles.some((role) => permissionsFromRoles.SEND_CHAT.includes(role))
)
throw new Error('peer not authorized');
@@ -1008,7 +1050,9 @@ class Room extends EventEmitter
case 'moderator:clearChat':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_CHAT.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.MODERATE_CHAT.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1046,7 +1090,9 @@ class Room extends EventEmitter
case 'lockRoom':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1066,7 +1112,9 @@ class Room extends EventEmitter
case 'unlockRoom':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.CHANGE_ROOM_LOCK.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1126,7 +1174,9 @@ class Room extends EventEmitter
case 'promotePeer':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.PROMOTE_PEER.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.PROMOTE_PEER.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1143,7 +1193,9 @@ class Room extends EventEmitter
case 'promoteAllPeers':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.PROMOTE_PEER.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.PROMOTE_PEER.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1158,7 +1210,9 @@ class Room extends EventEmitter
case 'sendFile':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.SHARE_FILE.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.SHARE_FILE.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1181,7 +1235,9 @@ class Room extends EventEmitter
case 'moderator:clearFileSharing':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_FILES.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.MODERATE_FILES.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1217,7 +1273,9 @@ class Room extends EventEmitter
case 'moderator:muteAll':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1232,7 +1290,9 @@ class Room extends EventEmitter
case 'moderator:stopAllVideo':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1247,7 +1307,9 @@ class Room extends EventEmitter
case 'moderator:closeMeeting':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1264,7 +1326,9 @@ class Room extends EventEmitter
case 'moderator:kickPeer':
{
if (
- !peer.roles.some((role) => config.permissionsFromRoles.MODERATE_ROOM.includes(role))
+ !peer.roles.some(
+ (role) => permissionsFromRoles.MODERATE_ROOM.includes(role)
+ )
)
throw new Error('peer not authorized');
@@ -1452,6 +1516,19 @@ class Room extends EventEmitter
.filter((peer) => peer.joined && peer !== excludePeer);
}
+ _getPeersWithPermission({ permission = null, excludePeer = undefined, joined = true })
+ {
+ return Object.values(this._peers)
+ .filter(
+ (peer) =>
+ peer.joined === joined &&
+ peer !== excludePeer &&
+ peer.roles.some(
+ (role) => permission.includes(role)
+ )
+ );
+ }
+
_timeoutCallback(callback)
{
let called = false;
diff --git a/server/package.json b/server/package.json
index d156a62..77b641c 100644
--- a/server/package.json
+++ b/server/package.json
@@ -8,7 +8,8 @@
"main": "lib/index.js",
"scripts": {
"start": "DEBUG=${DEBUG:='*mediasoup* *INFO* *WARN* *ERROR*'} INTERACTIVE=${INTERACTIVE:='true'} node server.js",
- "connect": "node connect.js"
+ "connect": "node connect.js",
+ "lint": "eslint -c .eslintrc.json --ext .js *.js lib/"
},
"dependencies": {
"awaitqueue": "^1.0.0",
@@ -36,5 +37,8 @@
"socket.io": "^2.3.0",
"spdy": "^4.0.1",
"uuid": "^7.0.2"
+ },
+ "devDependencies": {
+ "eslint": "6.8.0"
}
}
diff --git a/server/server.js b/server/server.js
index c28eda8..2c102c8 100755
--- a/server/server.js
+++ b/server/server.js
@@ -203,7 +203,7 @@ function setupLTI(ltiConfig)
if (lti.user_id && lti.custom_room)
{
user.id = lti.user_id;
- user._userinfo = { "lti" : lti };
+ user._userinfo = { 'lti': lti };
}
if (lti.custom_room)
@@ -244,7 +244,18 @@ function setupOIDC(oidcIssuer)
// redirect_uri defaults to client.redirect_uris[0]
// response type defaults to client.response_types[0], then 'code'
// scope defaults to 'openid'
- const params = config.auth.oidc.clientOptions;
+
+ /* eslint-disable camelcase */
+ const params = (({
+ client_id,
+ redirect_uri,
+ scope
+ }) => ({
+ client_id,
+ redirect_uri,
+ scope
+ }))(config.auth.oidc.clientOptions);
+ /* eslint-enable camelcase */
// optional, defaults to false, when true req is passed as a first
// argument to verify fn
@@ -259,7 +270,9 @@ function setupOIDC(oidcIssuer)
{ client: oidcClient, params, passReqToCallback, usePKCE },
(tokenset, userinfo, done) =>
{
- if (userinfo && tokenset) {
+ if (userinfo && tokenset)
+ {
+ // eslint-disable-next-line camelcase
userinfo._tokenset_claims = tokenset.claims();
}
@@ -434,14 +447,14 @@ async function runHttpsServer()
// http
const redirectListener = http.createServer(app);
- if(config.listeningHost)
+ if (config.listeningHost)
redirectListener.listen(config.listeningRedirectPort, config.listeningHost);
else
redirectListener.listen(config.listeningRedirectPort);
}
// https or http
- if(config.listeningHost)
+ if (config.listeningHost)
mainListener.listen(config.listeningPort, config.listeningHost);
else
mainListener.listen(config.listeningPort);
diff --git a/server/userRoles.js b/server/userRoles.js
index b9d0d2b..555e47c 100644
--- a/server/userRoles.js
+++ b/server/userRoles.js
@@ -7,5 +7,5 @@ module.exports = {
// Don't change anything after this point
// All users have this role by default, do not change or remove this role
- NORMAL : 'normal'
+ NORMAL : 'normal'
};
\ No newline at end of file