Merge branch '3.2-fixes'

master
Mészáros Mihály 2020-04-01 12:06:11 +02:00
commit b975b16dac
18 changed files with 590 additions and 47 deletions

View File

@ -1,20 +1,32 @@
# Changelog
## 3.2.1
* Fix: permananent top bar by default
* Fix: `httpOnly` mode https redirect
* Add some extra checks for video stream and track
* Add Italian translation
* Add Czech translation
* Add new server option `trustProxy` for load balancing http only use case
* Add HAproxy load balance example
* Add LTI LMS integration documentation
* Fix spacing of leave button
* Fix for sharing same file multiple times
## 3.2
* Add munin plugin
* Add muted=true search param to disble audio by deffault
* Add `muted=true` search param to disable audio by default
* Modify webtorrent tracker
* Add key shortcut `space` for audio mute
* Add key shortcut `v` for video mute
* Add user configurable LastN
* Add option to sticky top bar (sticky by default)
* update mediasoup server
* Add simulcast options to app config (disabled by default)
* Add stats option to get counts of rooms and peers
* Add httpOnly option for loadbalancer backend setups
* Add option to permananent top bar (permanent by default)
* Update mediasoup server
* Add `simulcast` options to app config (disabled by default)
* Add `stats` option to get counts of rooms and peers
* Add `httpOnly` option for loadbalancer backend setups
* LTI integration for LMS systems like moodle
* Add muted=false search parameter
* Add translations (12+1 languages)
* Add support IPv6
* Many other fixes and refactorings
@ -33,10 +45,10 @@
* Updated to mediasoup v3
* Replace lib "passport-datporten" with "openid-client" (a general OIDC certified client)
- OpenID Connect discovery
- Auth code flow
* OpenID Connect discovery
* Auth code flow
* Add spdy http2 support.
- Notice it does not supports node 11.x
* Notice it does not supports node 11.x
* Updated to Material UI v4
## 2.0

109
HAproxy.md 100644
View File

@ -0,0 +1,109 @@
# Howto deploy a (room based) load balanced cluster
This example will show how to setup an HA proxy to provide load balancing between several
multiparty-meeting servers.
## IP and DNS
In this basic example we use the following names and ips:
### Backend
* `mm1.example.com` <=> `192.0.2.1`
* `mm2.example.com` <=> `192.0.2.2`
* `mm3.example.com` <=> `192.0.2.3`
### Redis
* `redis.example.com` <=> `192.0.2.4`
### Load balancer HAproxy
* `meet.example.com` <=> `192.0.2.5`
## Deploy multiple multiparty-meeting servers
This is most easily done using Ansible (see below), but can be done
in any way you choose (manual, Docker, Ansible).
Read more here: [mm-ansible](https://github.com/misi/mm-ansible)
[![asciicast](https://asciinema.org/a/311365.svg)](https://asciinema.org/a/311365)
## Setup Redis for central HTTP session store
### Use one Redis for all multiparty-meeting servers
* Deploy a Redis cluster for all instances.
* We will use in our actual example `192.0.2.4` as redis HA cluster ip. It is out of scope howto deploy it.
OR
* For testing you can use Redis from one the multiparty-meeting servers. e.g. If you plan only for testing on your first multiparty-meeting server.
* Configure Redis `redis.conf` to not only bind to your loopback but also to your global ip address too:
``` plaintext
bind 192.0.2.1
```
This example sets this to `192.0.2.1`, change this according to your local installation.
* Change your firewall config to allow incoming Redis. Example (depends on the type of firewall):
``` plaintext
chain INPUT {
policy DROP;
saddr mm2.example.com proto tcp dport 6379 ACCEPT;
saddr mm3.example.com proto tcp dport 6379 ACCEPT;
}
```
* **Set a password, or if you don't (like in this basic example) take care to set strict firewall rules**
## Configure multiparty-meeting servers
### App config
mm/configs/app/config.js
``` js
multipartyServer : 'meet.example.com',
```
### Server config
mm/configs/server/config.js
``` js
redisOptions : { host: '192.0.2.4'},
listeningPort: 80,
httpOnly: true,
trustProxy : ['192.0.2.5'],
```
## Deploy HA proxy
* Configure cerificate / letsencrypt for `meet.example.com`
* In this example we put a complete chain and private key in /root/certificate.pem.
* Install and setup haproxy
`apt install haproxy`
* Add to /etc/haproxy/haproxy.cfg config
``` plaintext
backend multipartymeeting
balance url_param roomId
hash-type consistent
server mm1 192.0.2.1:80 check maxconn 20 verify none
server mm2 192.0.2.2:80 check maxconn 20 verify none
server mm3 192.0.2.3:80 check maxconn 20 verify none
frontend meet.example.com
bind 192.0.2.5:80
bind 192.0.2.5:443 ssl crt /root/certificate.pem
http-request redirect scheme https unless { ssl_fc }
reqadd X-Forwarded-Proto:\ https
default_backend multipartymeeting
```

61
LTI/LTI.md 100644
View File

@ -0,0 +1,61 @@
# Learning Tools Interoperability (LTI)
## LTI
Read more about IMS Global defined interface for tools like our VideoConference system integration with Learning Managment Systems(LMS) (e.g. moodle).
See: [IMS Global Learning Tool Interoperability](https://www.imsglobal.org/activity/learning-tools-interoperability)
We implemented LTI interface version 1.0/1.1
### Server config auth section LTI settings
Set in server configuration a random key and secret
``` json
auth :
{
lti :
{
consumerKey : 'key',
consumerSecret : 'secret'
},
}
```
### Configure your LMS system with secret and key settings above
#### Auth tool URL
Set tool URL to your server with path /auth/lti
``` url
https://mm.example.com/auth/lti
```
#### In moodle find external tool plugin setting and external tool action
See: [moodle external tool settings](https://docs.moodle.org/38/en/External_tool_settings)
#### Add and activity
![Add external tool](lti1.png)
#### Setup Activity
##### Activity setup basic form
Open fully the settings **Click on show more!!**
![Add external tool config](lti2.png)
##### Empty full form
![Opened external tool config](lti3.png)
##### Filled out form
![Filled out external tool config](lti4.png)
## moodle plugin
Alternativly you can use multipartymeeting moodle plugin:
[https://github.com/misi/moodle-mod_multipartymeeting](https://github.com/misi/moodle-mod_multipartymeeting)

BIN
LTI/lti1.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
LTI/lti2.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
LTI/lti3.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
LTI/lti4.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View File

@ -21,7 +21,6 @@ If you want the automatic approach, you can find a docker image [here](https://h
If you want the ansible approach, you can find ansible role [here](https://github.com/misi/mm-ansible/).
[![asciicast](https://asciinema.org/a/311365.svg)](https://asciinema.org/a/311365)
## Manual installation
* Prerequisites:
Currently multiparty-meeting will only run on nodejs v10.*
@ -106,6 +105,12 @@ $ systemctl enable multiparty-meeting
* 4443/tcp (default `npm start` port for developing with live browser reload, not needed in production enviroments - adjustable in app/package.json)
* 40000-49999/udp/tcp (media ports - adjustable in `server/config.js`)
## Load balanced installation
To deploy this as a load balanced cluster, have a look at [HAproxy](HAproxy.md).
## Learning management integration
To integrate with an LMS (e.g. Moodle), have a look at [LTI](LTI/LTI.md).
## TURN configuration
* You need an addtional [TURN](https://github.com/coturn/coturn)-server for clients located behind restrictive firewalls! Add your server and credentials to `app/config.js`

View File

@ -11,9 +11,10 @@
"@material-ui/core": "^4.5.1",
"@material-ui/icons": "^4.5.1",
"bowser": "^2.7.0",
"create-torrent": "^4.4.1",
"dompurify": "^2.0.7",
"domready": "^1.0.8",
"end-of-stream": "1.4.0",
"end-of-stream": "1.4.1",
"file-saver": "^2.0.2",
"hark": "^1.2.3",
"is-electron": "^2.2.0",
@ -37,7 +38,7 @@
"riek": "^1.1.0",
"socket.io-client": "^2.3.0",
"source-map-explorer": "^2.1.0",
"webtorrent": "^0.107.16"
"webtorrent": "^0.107.17"
},
"scripts": {
"analyze": "source-map-explorer build/static/js/*",

View File

@ -14,6 +14,8 @@ import * as consumerActions from './actions/consumerActions';
import * as producerActions from './actions/producerActions';
import * as notificationActions from './actions/notificationActions';
let createTorrent;
let WebTorrent;
let saveAs;
@ -704,26 +706,48 @@ export default class RoomClient
})
}));
this._webTorrent.seed(
files,
{ announceList: [['wss://tracker.lab.vvc.niif.hu:443']] },
(torrent) =>
createTorrent(files, (err, torrent) =>
{
if (err)
{
store.dispatch(requestActions.notify(
return store.dispatch(requestActions.notify(
{
type : 'error',
text : intl.formatMessage({
id : 'filesharing.successfulFileShare',
defaultMessage : 'File successfully shared'
id : 'filesharing.unableToShare',
defaultMessage : 'Unable to share file'
})
}));
}
store.dispatch(fileActions.addFile(
this._peerId,
torrent.magnetURI
));
const existingTorrent = this._webTorrent.get(torrent);
this._sendFile(torrent.magnetURI);
});
if (existingTorrent)
{
return this._sendFile(existingTorrent.magnetURI);
}
this._webTorrent.seed(
files,
{ announceList: [ [ 'wss://tracker.lab.vvc.niif.hu:443' ] ] },
(newTorrent) =>
{
store.dispatch(requestActions.notify(
{
text : intl.formatMessage({
id : 'filesharing.successfulFileShare',
defaultMessage : 'File successfully shared'
})
}));
store.dispatch(fileActions.addFile(
this._peerId,
newTorrent.magnetURI
));
this._sendFile(newTorrent.magnetURI);
});
});
}
// { file, name, picture }
@ -1081,8 +1105,8 @@ export default class RoomClient
logger.debug(
'changeWebcam() | new selected webcam [device:%o]',
device);
this._webcamProducer.track.stop();
if (this._webcamProducer && this._webcamProducer.track)
this._webcamProducer.track.stop();
logger.debug('changeWebcam() | calling getUserMedia()');
@ -1094,14 +1118,21 @@ export default class RoomClient
...VIDEO_CONSTRAINS[resolution]
}
});
const track = stream.getVideoTracks()[0];
await this._webcamProducer.replaceTrack({ track });
store.dispatch(
producerActions.setProducerTrack(this._webcamProducer.id, track));
if (stream){
const track = stream.getVideoTracks()[0];
if (track) {
await this._webcamProducer.replaceTrack({ track });
store.dispatch(
producerActions.setProducerTrack(this._webcamProducer.id, track));
} else {
logger.warn('getVideoTracks Error: First Video Track is null')
}
} else {
logger.warn ('getUserMedia Error: Stream is null!')
}
store.dispatch(settingsActions.setSelectedWebcamDevice(deviceId));
await this._updateWebcams();
@ -1337,6 +1368,13 @@ export default class RoomClient
async _loadDynamicImports()
{
({ default: createTorrent } = await import(
/* webpackPrefetch: true */
/* webpackChunkName: "createtorrent" */
'create-torrent'
));
({ default: WebTorrent } = await import(
/* webpackPrefetch: true */
@ -2059,6 +2097,30 @@ export default class RoomClient
try
{
this._torrentSupport = WebTorrent.WEBRTC_SUPPORT;
this._webTorrent = this._torrentSupport && new WebTorrent({
tracker : {
rtcConfig : {
iceServers : this._turnServers
}
}
});
this._webTorrent.on('error', (error) =>
{
logger.error('Filesharing [error:"%o"]', error);
store.dispatch(requestActions.notify(
{
type : 'error',
text : intl.formatMessage({
id : 'filesharing.error',
defaultMessage : 'There was a filesharing error'
})
}));
});
this._mediasoupDevice = new mediasoupClient.Device();
const routerRtpCapabilities =
@ -2177,7 +2239,7 @@ export default class RoomClient
canShareFiles : this._torrentSupport
}));
const { peers } = await this.sendRequest(
const { peers, authenticated } = await this.sendRequest(
'join',
{
displayName : displayName,
@ -2185,6 +2247,8 @@ export default class RoomClient
rtpCapabilities : this._mediasoupDevice.rtpCapabilities
});
store.dispatch(meActions.loggedIn(authenticated));
logger.debug('_joinRoom() joined, got peers [peers:"%o"]', peers);
for (const peer of peers)

View File

@ -78,8 +78,8 @@ const styles = (theme) =>
},
actionButton :
{
margin : theme.spacing(1),
padding : 0
margin : theme.spacing(1, 0),
padding : theme.spacing(0, 1)
}
});
@ -439,4 +439,4 @@ export default withRoomContext(connect(
);
}
}
)(withStyles(styles, { withTheme: true })(TopBar)));
)(withStyles(styles, { withTheme: true })(TopBar)));

View File

@ -34,6 +34,8 @@ import messagesPortuguese from './translations/pt';
import messagesChinese from './translations/cn';
import messagesSpanish from './translations/es';
import messagesCroatian from './translations/hr';
import messagesCzech from './translations/cs';
import messagesItalian from './translations/it';
import './index.css';
@ -55,7 +57,9 @@ const messages =
'pt' : messagesPortuguese,
'zh' : messagesChinese,
'es' : messagesSpanish,
'hr' : messagesCroatian
'hr' : messagesCroatian,
'cs' : messagesCzech,
'it' : messagesItalian
};
const locale = navigator.language.split(/[-_]/)[0]; // language without region code

View File

@ -5,7 +5,8 @@ const initialState =
selectedAudioDevice : null,
advancedMode : false,
resolution : 'medium', // low, medium, high, veryhigh, ultra
lastN : 4
lastN : 4,
permanentTopBar : true
};
const settings = (state = initialState, action) =>

View File

@ -0,0 +1,136 @@
{
"socket.disconnected": "Spojení přerušeno",
"socket.reconnecting": "Spojení přerušeno, navazuji spojení..",
"socket.reconnected": "Znovu připojeno",
"socket.requestError": "Chyba při požadavku na server",
"room.chooseRoom": "Vyberte jméno místnosti, kam se chcete připojit",
"room.cookieConsent": "Tato stránka používá cookies s cílem zajistit co možná nejlepší uživatelský zážitek",
"room.joined": "Připojili jste se do místnosti",
"room.cantJoin": "Není možné se připojit do místnosti",
"room.youLocked": "Místnost byla uzamčena",
"room.cantLock": "Není možné uzamknout místnost",
"room.youUnLocked": "Místnost byla odemčena",
"room.cantUnLock": "Není možné odemknout místnost",
"room.locked": "Místnost je uzamknutá",
"room.unlocked": "Místnost je odemknutá",
"room.newLobbyPeer": "Nový účastník vstoupil do přijímací místnosti",
"room.lobbyPeerLeft": "Účastník opustil přijímací místnost",
"room.lobbyPeerChangedDisplayName": "Účastník v přijímací místnosti změnil jméno na {displayName}",
"room.lobbyPeerChangedPicture": "Účastník v přijímací místnosti změnil obrázek",
"room.setAccessCode": "Přístupový kód místnosti aktualizován",
"room.accessCodeOn": "Přístupový kód místnosti je aktivován",
"room.accessCodeOff": "Přístupový kód místnosti je deaktivován",
"room.peerChangedDisplayName": "{oldDisplayName} je nyní {displayName}",
"room.newPeer": "{displayName} se připojil do místnosti",
"room.newFile": "Je dostupný nový soubor",
"room.toggleAdvancedMode": "Přepnuto do pokročilého módu",
"room.setDemocraticView": "Rozvržení změněno na: demokratický pohled",
"room.setFilmStripView": "Rozvržení změněno na: filmový pás",
"room.loggedIn": "Jste přihlášeni",
"room.loggedOut": "Jste odhlášeni",
"room.changedDisplayName": "Vaše jméno bylo změněno na {displayName}",
"room.changeDisplayNameError": "Při změně Vašeho jména se vyskytla chyba",
"room.chatError": "Zprávu nebylo možné odeslat",
"room.aboutToJoin": "Připojujete se ke konferenci",
"room.roomId": "ID místnosti: {roomName}",
"room.setYourName": "Zadejte své jméno a vyberte, jak se chcete připojit:",
"room.audioOnly": "Pouze Audio",
"room.audioVideo": "Audio a Video",
"room.youAreReady": "Ok, jste připraven",
"room.emptyRequireLogin": "Místnost je prázdná! Pro spuštění konference se přihlašte nebo počkejte, než se připojí hostitel",
"room.locketWait": "Místnost je uzamčena - vydržte, než vás někdo pustí dovnitř ...",
"room.lobbyAdministration": "Administrace Přijímací místnosti",
"room.peersInLobby": "Účastníci v Přijímací místnosti",
"room.lobbyEmpty": "V Přijímací místnosti v současné době nikdo není",
"room.hiddenPeers": "{hiddenPeersCount, plural, one {účastník} other {účastníci}}",
"room.me": "Já",
"room.spotlights": "Aktivní Účastníci",
"room.passive": "Pasivní Účastníci",
"room.videoPaused": "Toto video bylo pozastaveno",
"tooltip.login": "Přihlášení",
"tooltip.logout": "Odhlášení",
"tooltip.admitFromLobby": "Povolit uživatele z Přijímací místnosti",
"tooltip.lockRoom": "Uzamknout místnost",
"tooltip.unLockRoom": "Odemknout místnost",
"tooltip.enterFullscreen": "Zapnout režim celé obrazovky (fullscreen)",
"tooltip.leaveFullscreen": "Vypnout režim celé obrazovky (fullscreen)",
"tooltip.lobby": "Ukázat Přijímací místnost",
"tooltip.settings": "Zobrazit nastavení",
"label.roomName": "Jméno místnosti",
"label.chooseRoomButton": "Pokračovat",
"label.yourName": "Vaše jméno",
"label.newWindow": "Nové okno",
"label.fullscreen": "Režim celé obrazovky (Fullscreen)",
"label.openDrawer": "Otevřít zásuvku",
"label.leave": "Opustit",
"label.chatInput": "Napište zprávu chatu...",
"label.chat": "Chat",
"label.filesharing": "Sdílení souborů",
"label.participants": "Účastníci",
"label.shareFile": "Sdílet soubor",
"label.fileSharingUnsupported": "Sdílení souborů není podporováno",
"label.unknown": "Neznámý",
"label.democratic": "Rozvržení: Demokratické",
"label.filmstrip": "Rozvržení: Filmový pás",
"label.low": "Nízké",
"label.medium": "Střední",
"label.high": "Vysoké (HD)",
"label.veryHigh": "Velmi vysoké (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Zavřít",
"settings.settings": "Nastavení",
"settings.camera": "Kamera",
"settings.selectCamera": "Vyberte video zařízení",
"settings.cantSelectCamera": "Není možné vybrat video zařízení",
"settings.audio": "Audio zařízení",
"settings.selectAudio": "Vyberte audio zařízení",
"settings.cantSelectAudio": "Není možno vybrat audio zařízení",
"settings.resolution": "Vyberte rozlišení vašeho videa",
"settings.layout": "Rozvržení místnosti",
"settings.selectRoomLayout": "Vyberte rozvržení místnosti",
"settings.advancedMode": "Pokočilý mód",
"filesharing.saveFileError": "Není možné uložit soubor",
"filesharing.startingFileShare": "Pokouším se sdílet soubor",
"filesharing.successfulFileShare": "Soubor byl úspěšně sdílen",
"filesharing.unableToShare": "Není možné sdílet soubor",
"filesharing.error": "Při sdílení souboru se vyskytla chyba",
"filesharing.finished": "Stahování souboru dokončeno",
"filesharing.save": "Uložit",
"filesharing.sharedFile": "{displayName} sdílel(a) soubor",
"filesharing.download": "Stáhnout",
"filesharing.missingSeeds": "Pokud tento proces trvá dlouho, nikdo zřejmě neseeduje tento torrent. Pokuste se někoho požádat, aby znovu nahrál soubor, který potřebujete.",
"devices.devicesChanged": "Vaše zařízení se změnila, nastavte je v dialogu nastavení",
"device.audioUnsupported": "Audio není podporováno",
"device.activateAudio": "Aktivovat audio",
"device.muteAudio": "Ztišit zvuk",
"device.unMuteAudio": "Zrušit ztišení zvuku",
"device.videoUnsupported": "Video není podporováno",
"device.startVideo": "Spustit video",
"device.stopVideo": "Zastavit video",
"device.screenSharingUnsupported": "Sdílení obrazovky není podporováno",
"device.startScreenSharing": "Spustit sdílení obrazovky",
"device.stopScreenSharing": "Zastavit sdílení obrazovky",
"devices.microphoneDisconnected": "Mikrofon odpojen",
"devices.microphoneError": "Při přístupu k vašemu mikrofonu se vyskytla chyba",
"devices.microPhoneMute": "Mikrofon ztišen",
"devices.micophoneUnMute": "Ztišení mikrofonu zrušeno",
"devices.microphoneEnable": "Mikrofon povolen",
"devices.microphoneMuteError": "Není možné ztišit váš mikrofon",
"devices.microphoneUnMuteError": "Není možné zrušit ztišení vašeho mikrofonu",
"devices.screenSharingDisconnected" : "Sdílení obrazovky odpojeno",
"devices.screenSharingError": "Při přístupu k vaší obrazovce se vyskytla chyba",
"devices.cameraDisconnected": "Kamera odpojena",
"devices.cameraError": "Při přístupu k vaší kameře se vyskytla chyba"
}

View File

@ -0,0 +1,140 @@
{
"socket.disconnected": "Sei stato disconnesso",
"socket.reconnecting": "Sei stato disconnesso, stiamo provando a riconnetterti",
"socket.reconnected": "Sei stato riconnesso",
"socket.requestError": "Errore di richiesta al server",
"room.chooseRoom": "Scegli il nome della stanza in cui vorresti entrare",
"room.cookieConsent": "Questo sito fa uso di cookies per migliorare l'esperienza utente",
"room.consentUnderstand": "Ho capito",
"room.joined": "Sei entrato nella stanza",
"room.cantJoin": "Impossibile entrare nella stanza",
"room.youLocked": "Hai bloccato la stanza",
"room.cantLock": "Impossibile bloccare la stanza",
"room.youUnLocked": "Hai sbloccato la stanza",
"room.cantUnLock": "Impossibile sbloccare la stanza",
"room.locked": "La stanza è ora bloccata",
"room.unlocked": "La stanza è ora sbloccata",
"room.newLobbyPeer": "Un nuovo partecipante è entrato nella lobby",
"room.lobbyPeerLeft": "Un partecipante ha abbandonato la lobby",
"room.lobbyPeerChangedDisplayName": "Un partecipante della lobby ha cambiato nome in {displayName}",
"room.lobbyPeerChangedPicture": "Un partecipante della lobby ha cambiato immagine del profilo",
"room.setAccessCode": "Codice d'accesso della stanza aggiornato",
"room.accessCodeOn": "Il codice d'accesso della stanza è attivato",
"room.accessCodeOff": "Il codice d'accesso della stanza è disattivato",
"room.peerChangedDisplayName": "{oldDisplayName} ora si chiama {displayName}",
"room.newPeer": "{displayName} è entrato nella stanza",
"room.newFile": "Nuovo file disponibile",
"room.toggleAdvancedMode": "Attivata/disattivata modalità avanzata",
"room.setDemocraticView": "Aspetto cambiato in vista democratica",
"room.setFilmStripView": "Aspetto cambiato in vista filmstrip",
"room.loggedIn": "Hai eseguito il log in",
"room.loggedOut": "Hai eseguito il log out",
"room.changedDisplayName": "Il tuo nome visualizzato è cambiato in{displayName}",
"room.changeDisplayNameError": "Errore nel modificare il tuo nome visualizzato",
"room.chatError": "Impossibile mandare messaggio in chat",
"room.aboutToJoin": "Stai per entrare in un meeting",
"room.roomId": "ID stanza: {roomName}",
"room.setYourName": "Imposta il tuo nome per la partecipazione, e scegli in che modo vuoi entrare:",
"room.audioOnly": "Solo Audio",
"room.audioVideo": "Audio e Video",
"room.youAreReady": "Ok, sei pronto",
"room.emptyRequireLogin": "La stanza è vuota! Puoi fare il log in per iniziare il meeting, o aspettare finchè l'host non entra",
"room.locketWait": "La stanza è bloccata - resta in attesa finchè qualcuno non ti fa entrare...",
"room.lobbyAdministration": "Amministrazione lobby",
"room.peersInLobby": "Participanti della lobby",
"room.lobbyEmpty": "Attualmente non c'è nessuno nella lobby",
"room.hiddenPeers": "{hiddenPeersCount, plural, one {participant} other {participants}}",
"room.me": "Io",
"room.spotlights": "Partecipanti in Evidenza",
"room.passive": "Participanti Passivi",
"room.videoPaused": "Il video è in pausa",
"tooltip.login": "Log in",
"tooltip.logout": "Log out",
"tooltip.admitFromLobby": "Ammetti dalla lobby",
"tooltip.lockRoom": "Blocca stanza",
"tooltip.unLockRoom": "Sblocca stanza",
"tooltip.enterFullscreen": "Modalità schermo intero",
"tooltip.leaveFullscreen": "Esci dalla modalità schermo intero",
"tooltip.lobby": "Mostra lobby",
"tooltip.settings": "Mostra impostazioni",
"tooltip.participants": "Mostra partecipanti",
"label.roomName": "Nome della stanza",
"label.chooseRoomButton": "Continua",
"label.yourName": "Il tuo nome",
"label.newWindow": "Nuova finestra",
"label.fullscreen": "Schermo intero",
"label.openDrawer": "Apri cassetto",
"label.leave": "Esci",
"label.chatInput": "Inserire messaggio...",
"label.chat": "Chat",
"label.filesharing": "Condivisione file",
"label.participants": "Partecipanti",
"label.shareFile": "Condividi file",
"label.fileSharingUnsupported": "Condivisione file non supportata",
"label.unknown": "Sconosciuto",
"label.democratic": "Vista Democratica",
"label.filmstrip": "Vista Filmstrip",
"label.low": "Bassa",
"label.medium": "Media",
"label.high": "Alta (HD)",
"label.veryHigh": "Molto alta (FHD)",
"label.ultra": "Ultra (UHD)",
"label.close": "Chiudi",
"settings.settings": "Impostazioni",
"settings.camera": "Videocamera",
"settings.selectCamera": "Seleziona dispositivo video",
"settings.cantSelectCamera": "Impossibile selezionare dispositivo video",
"settings.audio": "Dispositivo audio",
"settings.selectAudio": "Seleziona dispositivo audio",
"settings.cantSelectAudio": "Impossibile selezionare dispositivo audio",
"settings.resolution": "Seleziona risoluzione",
"settings.layout": "Aspetto stanza",
"settings.selectRoomLayout": "Seleziona aspetto stanza",
"settings.advancedMode": "Modalità avanzata",
"settings.permanentTopBar": "Barra superiore permanente",
"settings.lastn": "Numero di video visibili",
"filesharing.saveFileError": "Impossibile salvare file",
"filesharing.startingFileShare": "Tentativo di condivisione file",
"filesharing.successfulFileShare": "File condiviso con successo",
"filesharing.unableToShare": "Impossibile condividere file",
"filesharing.error": "Errore generico nella condivisione file",
"filesharing.finished": "Il file è stato scaricato",
"filesharing.save": "Salva",
"filesharing.sharedFile": "{displayName} ha condiviso un file",
"filesharing.download": "Download",
"filesharing.missingSeeds": "Se questo processo impiega troppo tempo, questo torrent potrebbe non avere alcun seed. Prova a chiedere a qualcuno di caricare nuovamente il file che desideri.",
"devices.devicesChanged": "Il tuo dispositivo è cambiato, configura i dispositivi nel menù di impostazioni",
"device.audioUnsupported": "Dispositivo audio non supportato",
"device.activateAudio": "Attiva audio",
"device.muteAudio": "Silenzia audio",
"device.unMuteAudio": "Riattiva audio",
"device.videoUnsupported": "Dispositivo video non supportato",
"device.startVideo": "Attiva video",
"device.stopVideo": "Disattiva video",
"device.screenSharingUnsupported": "Condivisione Schermo non supportata",
"device.startScreenSharing": "Inizia Condivisione Schermo",
"device.stopScreenSharing": "Termina Condivisione Schermo",
"devices.microphoneDisconnected": "Microfono scollegato",
"devices.microphoneError": "Errore con l'accesso al microfono",
"devices.microPhoneMute": "Microfono silenziato",
"devices.micophoneUnMute": "Microfono riattivato",
"devices.microphoneEnable": "Microfono attivo",
"devices.microphoneMuteError": "Impossibile silenziare il microfono",
"devices.microphoneUnMuteError": "Impossibile riattivare il microfono",
"devices.screenSharingDisconnected" : "Disconnesso da Condivisione Schermo",
"devices.screenSharingError": "Errore con l'accesso al tuo schermo",
"devices.cameraDisconnected": "Videocamera scollegata",
"devices.cameraError": "Errore con l'accesso alla videocamera"
}

View File

@ -50,6 +50,12 @@ module.exports =
// 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 : '',
// If this is set to true, only signed-in users will be able
// to join a room directly. Non-signed-in users (guests) will
// always be put in the lobby regardless of room lock status.

View File

@ -472,7 +472,7 @@ class Room extends EventEmitter
.filter((joinedPeer) => joinedPeer.id !== peer.id)
.map((joinedPeer) => (joinedPeer.peerInfo));
cb(null, { peers: peerInfos });
cb(null, { peers: peerInfos, authenticated: peer.authenticated });
// Mark the new Peer as joined.
peer.joined = true;

View File

@ -99,6 +99,10 @@ const session = expressSession({
}
});
if (config.trustProxy) {
app.set('trust proxy', config.trustProxy);
}
app.use(session);
passport.serializeUser((user, done) =>
@ -167,7 +171,7 @@ function setupLTI(ltiConfig)
const ltiStrategy = new LTIStrategy(
ltiConfig,
function(req, lti, done)
(req, lti, done) =>
{
// LTI launch parameters
if (lti)
@ -332,7 +336,7 @@ async function setupAuth()
// lti launch
app.post('/auth/lti',
passport.authenticate('lti', { failureRedirect: '/' }),
function(req, res)
(req, res) =>
{
res.redirect(`/${req.user.room}`);
}
@ -342,7 +346,7 @@ async function setupAuth()
app.get('/auth/logout', (req, res) =>
{
req.logout();
res.send(logoutHelper());
req.session.destroy(() => res.send(logoutHelper()));
});
// callback
@ -391,7 +395,7 @@ async function runHttpsServer()
app.all('*', async (req, res, next) =>
{
if (req.secure)
if (req.secure || config.httpOnly )
{
const ltiURL = new URL(`${req.protocol }://${ req.get('host') }${req.originalUrl}`);