mirror of
https://github.com/edumeet/edumeet.git
synced 2026-01-23 02:34:58 +00:00
adding docker compose configuration for edumeet / prometheus / grafana
This commit is contained in:
parent
b7f07dcce9
commit
89e7dbbed5
8 changed files with 955 additions and 0 deletions
1
compose/.gitignore
vendored
Normal file
1
compose/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
data
|
||||
15
compose/README.md
Normal file
15
compose/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Running the development environment
|
||||
|
||||
Starting:
|
||||
|
||||
```sh
|
||||
docker-compose up --build -d
|
||||
|
||||
docker-compose logs -f edumeet
|
||||
```
|
||||
|
||||
Accessing endpoints:
|
||||
|
||||
- Edumeet: https://127.0.0.1:4443/
|
||||
- Prometheus: http://127.0.0.1:9090/
|
||||
- Grafana: http://127.0.0.1:9091/
|
||||
275
compose/config/edumeet-app-config.js
Normal file
275
compose/config/edumeet-app-config.js
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
// eslint-disable-next-line
|
||||
var config =
|
||||
{
|
||||
loginEnabled : false,
|
||||
developmentPort : 3443,
|
||||
productionPort : 3443,
|
||||
|
||||
/**
|
||||
* Supported browsers version
|
||||
* in bowser satisfy format.
|
||||
* See more:
|
||||
* https://www.npmjs.com/package/bowser#filtering-browsers
|
||||
* Otherwise you got a unsupported browser page
|
||||
*/
|
||||
supportedBrowsers :
|
||||
{
|
||||
'windows' : {
|
||||
'internet explorer' : '>12',
|
||||
'microsoft edge' : '>18'
|
||||
},
|
||||
'safari' : '>12',
|
||||
'firefox' : '>=60',
|
||||
'chrome' : '>=74',
|
||||
'chromium' : '>=74',
|
||||
'opera' : '>=62',
|
||||
'samsung internet for android' : '>=11.1.1.52'
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolutions:
|
||||
*
|
||||
* low ~ 320x240
|
||||
* medium ~ 640x480
|
||||
* high ~ 1280x720
|
||||
* veryhigh ~ 1920x1080
|
||||
* ultra ~ 3840x2560
|
||||
*
|
||||
**/
|
||||
|
||||
/**
|
||||
* Frame rates:
|
||||
*
|
||||
* 1, 5, 10, 15, 20, 25, 30
|
||||
*
|
||||
**/
|
||||
// The aspect ratio of the videos as shown on
|
||||
// the screen. This is changeable in client settings.
|
||||
// This value must match one of the defined values in
|
||||
// viewAspectRatios EXACTLY (e.g. 1.333)
|
||||
viewAspectRatio : 1.777,
|
||||
// These are the selectable aspect ratios in the settings
|
||||
viewAspectRatios : [ {
|
||||
value : 1.333, // 4 / 3
|
||||
label : '4 : 3'
|
||||
}, {
|
||||
value : 1.777, // 16 / 9
|
||||
label : '16 : 9'
|
||||
} ],
|
||||
// The aspect ratio of the video from the camera
|
||||
// this is not changeable in settings, only config
|
||||
videoAspectRatio : 1.777,
|
||||
defaultResolution : 'high',
|
||||
defaultFrameRate : 25,
|
||||
defaultScreenResolution : 'veryhigh',
|
||||
defaultScreenSharingFrameRate : 5,
|
||||
// Enable or disable simulcast for webcam video
|
||||
simulcast : true,
|
||||
// Enable or disable simulcast for screen sharing video
|
||||
simulcastSharing : false,
|
||||
// Simulcast encoding layers and levels
|
||||
simulcastEncodings :
|
||||
[
|
||||
/* { scaleResolutionDownBy: 4 },
|
||||
{ scaleResolutionDownBy: 2 },
|
||||
{ scaleResolutionDownBy: 1 } */
|
||||
{ maxBitRate: 50000 },
|
||||
{ maxBitRate: 1000000 },
|
||||
{ maxBitRate: 4800000 }
|
||||
],
|
||||
// The adaptive spatial layer selection scaling factor (in the range [0.5, 1.0])
|
||||
// example:
|
||||
// with level width=640px, the minimum width required to trigger the
|
||||
// level change will be: 640 * 0.75 = 480px
|
||||
adaptiveScalingFactor: 0.75,
|
||||
|
||||
/**
|
||||
* Alternative simulcast setting:
|
||||
* [
|
||||
* { maxBitRate: 50000 },
|
||||
* { maxBitRate: 1000000 },
|
||||
* { maxBitRate: 4800000 }
|
||||
*],
|
||||
**/
|
||||
|
||||
/**
|
||||
* White listing browsers that support audio output device selection.
|
||||
* It is not yet fully implemented in Firefox.
|
||||
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=1498512
|
||||
*/
|
||||
audioOutputSupportedBrowsers :
|
||||
[
|
||||
'chrome',
|
||||
'opera'
|
||||
],
|
||||
// Socket.io request timeout
|
||||
requestTimeout : 20000,
|
||||
requestRetries : 3,
|
||||
transportOptions :
|
||||
{
|
||||
tcp : true
|
||||
},
|
||||
// defaults for audio setting on new clients / can be customized and overruled from client side
|
||||
defaultAudio:
|
||||
{
|
||||
autoGainControl : false, // default : false
|
||||
echoCancellation : true, // default : true
|
||||
noiseSuppression : true, // default : true
|
||||
voiceActivatedUnmute : false, // default : false / Automatically unmute speaking above noisThereshold
|
||||
noiseThreshold : -60 // default -60 / This is only for voiceActivatedUnmute and audio-indicator
|
||||
},
|
||||
// Audio options for now only centrally from config file:
|
||||
centralAudioOptions:
|
||||
{
|
||||
sampleRate : 96000, // default : 96khz / will not eat that much bandwith thanks to opus
|
||||
channelCount : 1, // default : 1 / usually mics are mono so this saves bandwidth
|
||||
volume : 1.0, // default : 1.0
|
||||
sampleSize : 16, // default : 16
|
||||
opusStereo : false, // default : false / usually mics are mono so this saves bandwidth
|
||||
opusDtx : true, // default : true / will save bandwidth
|
||||
opusFec : true, // default : true / forward error correction
|
||||
opusPtime : '20', // default : 20 / minimum packet time (3, 5, 10, 20, 40, 60, 120)
|
||||
opusMaxPlaybackRate : 96000
|
||||
},
|
||||
/**
|
||||
* Set max number participants in one room that join
|
||||
* unmuted. Next participant will join automatically muted
|
||||
* Default value is 4
|
||||
*
|
||||
* Set it to 0 to auto mute all,
|
||||
* Set it to negative (-1) to never automatically auto mute
|
||||
* but use it with caution
|
||||
* full mesh audio strongly decrease room capacity!
|
||||
*/
|
||||
autoMuteThreshold : 4,
|
||||
background : 'images/background.jpg',
|
||||
defaultLayout : 'democratic', // democratic, filmstrip
|
||||
// If true, will show media control buttons in separate
|
||||
// control bar, not in the ME container.
|
||||
buttonControlBar : false,
|
||||
// If false, will push videos away to make room for side
|
||||
// drawer. If true, will overlay side drawer over videos
|
||||
drawerOverlayed : true,
|
||||
// Position of notifications
|
||||
notificationPosition : 'right',
|
||||
// Timeout for autohiding topbar and button control bar
|
||||
hideTimeout : 3000,
|
||||
// max number of participant that will be visible in
|
||||
// as speaker
|
||||
lastN : 4,
|
||||
mobileLastN : 1,
|
||||
// Highest number of lastN the user can select manually in
|
||||
// userinteface
|
||||
maxLastN : 25,
|
||||
// If truthy, users can NOT change number of speakers visible
|
||||
lockLastN : false,
|
||||
// Show logo if "logo" is not null, else show title
|
||||
// Set logo file name using logo.* pattern like "logo.png" to not track it by git
|
||||
logo : 'images/logo.edumeet.svg',
|
||||
title : 'edumeet',
|
||||
// Service & Support URL
|
||||
// if not set then not displayed on the about modals
|
||||
supportUrl : 'https://support.example.com',
|
||||
// Privacy and dataprotection URL or path
|
||||
// by default privacy/privacy.html
|
||||
// that is a placeholder for your policies
|
||||
//
|
||||
// but an external url could be also used here
|
||||
privacyUrl : 'privacy/privacy.html',
|
||||
theme :
|
||||
{
|
||||
palette :
|
||||
{
|
||||
primary :
|
||||
{
|
||||
main : '#313131'
|
||||
}
|
||||
},
|
||||
overrides :
|
||||
{
|
||||
MuiAppBar :
|
||||
{
|
||||
colorPrimary :
|
||||
{
|
||||
backgroundColor : '#313131'
|
||||
}
|
||||
},
|
||||
MuiButton :
|
||||
{
|
||||
containedPrimary :
|
||||
{
|
||||
backgroundColor : '#5F9B2D',
|
||||
'&:hover' :
|
||||
{
|
||||
backgroundColor : '#5F9B2D'
|
||||
}
|
||||
},
|
||||
containedSecondary :
|
||||
{
|
||||
backgroundColor : '#f50057',
|
||||
'&:hover' :
|
||||
{
|
||||
backgroundColor : '#f50057'
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
MuIconButton :
|
||||
{
|
||||
colorPrimary :
|
||||
{
|
||||
backgroundColor : '#5F9B2D',
|
||||
'&:hover' :
|
||||
{
|
||||
backgroundColor : '#5F9B2D'
|
||||
}
|
||||
},
|
||||
colorSecondary :
|
||||
{
|
||||
backgroundColor : '#f50057',
|
||||
'&:hover' :
|
||||
{
|
||||
backgroundColor : '#f50057'
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
MuiFab :
|
||||
{
|
||||
primary :
|
||||
{
|
||||
backgroundColor : '#5F9B2D',
|
||||
'&:hover' :
|
||||
{
|
||||
backgroundColor : '#5F9B2D'
|
||||
}
|
||||
},
|
||||
secondary :
|
||||
{
|
||||
backgroundColor : '#f50057',
|
||||
'&:hover' :
|
||||
{
|
||||
backgroundColor : '#f50057'
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
MuiBadge :
|
||||
{
|
||||
colorPrimary :
|
||||
{
|
||||
backgroundColor : '#5F9B2D',
|
||||
'&:hover' :
|
||||
{
|
||||
backgroundColor : '#518029'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
typography :
|
||||
{
|
||||
useNextVariants : true
|
||||
}
|
||||
}
|
||||
};
|
||||
505
compose/config/edumeet-server-config.js
Normal file
505
compose/config/edumeet-server-config.js
Normal file
|
|
@ -0,0 +1,505 @@
|
|||
const os = require('os');
|
||||
// const fs = require('fs');
|
||||
|
||||
const userRoles = require('../userRoles');
|
||||
|
||||
const {
|
||||
BYPASS_ROOM_LOCK,
|
||||
BYPASS_LOBBY
|
||||
} = require('../access');
|
||||
|
||||
const {
|
||||
CHANGE_ROOM_LOCK,
|
||||
PROMOTE_PEER,
|
||||
MODIFY_ROLE,
|
||||
SEND_CHAT,
|
||||
MODERATE_CHAT,
|
||||
SHARE_AUDIO,
|
||||
SHARE_VIDEO,
|
||||
SHARE_SCREEN,
|
||||
EXTRA_VIDEO,
|
||||
SHARE_FILE,
|
||||
MODERATE_FILES,
|
||||
MODERATE_ROOM
|
||||
} = require('../permissions');
|
||||
|
||||
// const AwaitQueue = require('awaitqueue');
|
||||
// const axios = require('axios');
|
||||
|
||||
// To gather ip address only on interface like eth0, ens0p3
|
||||
const ifaceWhiteListRegex = /^(eth.*)|(ens.*)|(tun.*)/
|
||||
|
||||
function getListenIps() {
|
||||
let listenIP = [];
|
||||
const ifaces = os.networkInterfaces();
|
||||
Object.keys(ifaces).forEach(function (ifname) {
|
||||
if (ifname.match(ifaceWhiteListRegex)) {
|
||||
ifaces[ifname].forEach(function (iface) {
|
||||
if (
|
||||
(iface.family !== "IPv4" &&
|
||||
(iface.family !== "IPv6" || iface.scopeid !== 0)) ||
|
||||
iface.internal !== false
|
||||
) {
|
||||
// skip over internal (i.e. 127.0.0.1) and non-ipv4 or ipv6 non global addresses
|
||||
return;
|
||||
}
|
||||
listenIP.push({ ip: iface.address, announcedIp: iface.address });
|
||||
});
|
||||
}
|
||||
});
|
||||
console.log('Using listenips:', listenIP);
|
||||
return listenIP;
|
||||
}
|
||||
|
||||
module.exports =
|
||||
{
|
||||
|
||||
// Auth conf
|
||||
/*
|
||||
auth :
|
||||
{
|
||||
// Always enabled if configured
|
||||
lti :
|
||||
{
|
||||
consumerKey : 'key',
|
||||
consumerSecret : 'secret'
|
||||
},
|
||||
|
||||
// Auth strategy to use (default oidc)
|
||||
strategy : 'oidc',
|
||||
oidc :
|
||||
{
|
||||
// The issuer URL for OpenID Connect discovery
|
||||
// The OpenID Provider Configuration Document
|
||||
// could be discovered on:
|
||||
// issuerURL + '/.well-known/openid-configuration'
|
||||
|
||||
// e.g. google OIDC config
|
||||
// Follow this guide to get credential:
|
||||
// https://developers.google.com/identity/protocols/oauth2/openid-connect
|
||||
// use this issuerURL
|
||||
// issuerURL : 'https://accounts.google.com/',
|
||||
|
||||
issuerURL : 'https://example.com',
|
||||
clientOptions :
|
||||
{
|
||||
client_id : '',
|
||||
client_secret : '',
|
||||
scope : 'openid email profile',
|
||||
// where client.example.com is your edumeet server
|
||||
redirect_uri : 'https://client.example.com/auth/callback'
|
||||
}
|
||||
|
||||
},
|
||||
saml :
|
||||
{
|
||||
// where edumeet.example.com is your edumeet server
|
||||
callbackUrl : 'https://edumeet.example.com/auth/callback',
|
||||
issuer : 'https://edumeet.example.com',
|
||||
entryPoint : 'https://openidp.feide.no/simplesaml/saml2/idp/SSOService.php',
|
||||
privateCert : fs.readFileSync('config/saml_privkey.pem', 'utf-8'),
|
||||
signingCert : fs.readFileSync('config/saml_cert.pem', 'utf-8'),
|
||||
decryptionPvk : fs.readFileSync('config/saml_privkey.pem', 'utf-8'),
|
||||
decryptionCert : fs.readFileSync('config/saml_cert.pem', 'utf-8'),
|
||||
// Federation cert
|
||||
cert : fs.readFileSync('config/federation_cert.pem', 'utf-8')
|
||||
},
|
||||
|
||||
// to create password hash use: node server/utils/password_encode.js cleartextpassword
|
||||
local :
|
||||
{
|
||||
users : [
|
||||
{
|
||||
id : 1,
|
||||
username : 'alice',
|
||||
passwordHash : '$2b$10$PAXXw.6cL3zJLd7ZX.AnL.sFg2nxjQPDmMmGSOQYIJSa0TrZ9azG6',
|
||||
displayName : 'Alice',
|
||||
emails : [ { value: 'alice@atlanta.com' } ]
|
||||
},
|
||||
{
|
||||
id : 2,
|
||||
username : 'bob',
|
||||
passwordHash : '$2b$10$BzAkXcZ54JxhHTqCQcFn8.H6klY/G48t4jDBeTE2d2lZJk/.tvv0G',
|
||||
displayName : 'Bob',
|
||||
emails : [ { value: 'bob@biloxi.com' } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
*/
|
||||
// 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'
|
||||
//},
|
||||
//turnAPITimeout : 2 * 1000,
|
||||
// Backup turnservers if REST fails or is not configured
|
||||
//backupTurnServers : [
|
||||
// {
|
||||
// urls : [
|
||||
// 'turn:turn.example.com:443?transport=tcp'
|
||||
// ],
|
||||
// username : 'example',
|
||||
// credential : 'example'
|
||||
// }
|
||||
//],
|
||||
// bittorrent tracker
|
||||
fileTracker : 'wss://tracker.lab.vvc.niif.hu:443',
|
||||
// redis server options
|
||||
redisOptions : {
|
||||
host: 'redis',
|
||||
port: 6379
|
||||
},
|
||||
// session cookie secret
|
||||
cookieSecret : 'T0P-S3cR3t_cook!e',
|
||||
cookieName : 'edumeet.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',
|
||||
// Listening port for https server.
|
||||
listeningPort : 3443,
|
||||
// Any http request is redirected to https.
|
||||
// Listening port for http server.
|
||||
listeningRedirectPort : 8080,
|
||||
// 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
|
||||
// to log to a file or external service.
|
||||
/* StatusLogger : class
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this._queue = new AwaitQueue();
|
||||
}
|
||||
|
||||
// rooms: rooms object
|
||||
// peers: peers object
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async log({ rooms, peers })
|
||||
{
|
||||
this._queue.push(async () =>
|
||||
{
|
||||
// Do your logging in here, use queue to keep correct order
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Number of rooms: ', rooms.size);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Number of peers: ', peers.size);
|
||||
})
|
||||
.catch((error) =>
|
||||
{
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('error in log', error);
|
||||
});
|
||||
}
|
||||
}, */
|
||||
// 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.
|
||||
// See examples below.
|
||||
// Examples:
|
||||
/*
|
||||
// All authenicated users will be MODERATOR and AUTHENTICATED
|
||||
userMapping : async ({ peer, room, roomId, userinfo }) =>
|
||||
{
|
||||
peer.addRole(userRoles.MODERATOR);
|
||||
peer.addRole(userRoles.AUTHENTICATED);
|
||||
},
|
||||
// All authenicated users will be AUTHENTICATED,
|
||||
// and those with the moderator role set in the userinfo
|
||||
// will also be MODERATOR
|
||||
userMapping : async ({ peer, room, roomId, userinfo }) =>
|
||||
{
|
||||
if (
|
||||
Array.isArray(userinfo.meet_roles) &&
|
||||
userinfo.meet_roles.includes('moderator')
|
||||
)
|
||||
{
|
||||
peer.addRole(userRoles.MODERATOR);
|
||||
}
|
||||
|
||||
if (
|
||||
Array.isArray(userinfo.meet_roles) &&
|
||||
userinfo.meet_roles.includes('meetingadmin')
|
||||
)
|
||||
{
|
||||
peer.addRole(userRoles.ADMIN);
|
||||
}
|
||||
|
||||
peer.addRole(userRoles.AUTHENTICATED);
|
||||
},
|
||||
// First authenticated user will be moderator,
|
||||
// all others will be AUTHENTICATED
|
||||
userMapping : async ({ peer, room, roomId, userinfo }) =>
|
||||
{
|
||||
if (room)
|
||||
{
|
||||
const peers = room.getJoinedPeers();
|
||||
|
||||
if (peers.some((_peer) => _peer.authenticated))
|
||||
peer.addRole(userRoles.AUTHENTICATED);
|
||||
else
|
||||
{
|
||||
peer.addRole(userRoles.MODERATOR);
|
||||
peer.addRole(userRoles.AUTHENTICATED);
|
||||
}
|
||||
}
|
||||
},
|
||||
// All authenicated users will be AUTHENTICATED,
|
||||
// and those with email ending with @example.com
|
||||
// will also be MODERATOR
|
||||
userMapping : async ({ peer, room, roomId, userinfo }) =>
|
||||
{
|
||||
if (userinfo.email && userinfo.email.endsWith('@example.com'))
|
||||
{
|
||||
peer.addRole(userRoles.MODERATOR);
|
||||
}
|
||||
|
||||
peer.addRole(userRoles.AUTHENTICATED);
|
||||
},
|
||||
// All authenicated users will be AUTHENTICATED,
|
||||
// and those with email ending with @example.com
|
||||
// will also be MODERATOR
|
||||
userMapping : async ({ peer, room, roomId, userinfo }) =>
|
||||
{
|
||||
if (userinfo.email && userinfo.email.endsWith('@example.com'))
|
||||
{
|
||||
peer.addRole(userRoles.MODERATOR);
|
||||
}
|
||||
|
||||
peer.addRole(userRoles.AUTHENTICATED);
|
||||
},
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
userMapping : async ({ peer, room, roomId, userinfo }) =>
|
||||
{
|
||||
if (userinfo.picture != null)
|
||||
{
|
||||
if (!userinfo.picture.match(/^http/g))
|
||||
{
|
||||
peer.picture = `data:image/jpeg;base64, ${userinfo.picture}`;
|
||||
}
|
||||
else
|
||||
{
|
||||
peer.picture = userinfo.picture;
|
||||
}
|
||||
}
|
||||
if (userinfo['urn:oid:0.9.2342.19200300.100.1.60'] != null)
|
||||
{
|
||||
peer.picture = `data:image/jpeg;base64, ${userinfo['urn:oid:0.9.2342.19200300.100.1.60']}`;
|
||||
}
|
||||
|
||||
if (userinfo.nickname != null)
|
||||
{
|
||||
peer.displayName = userinfo.nickname;
|
||||
}
|
||||
|
||||
if (userinfo.name != null)
|
||||
{
|
||||
peer.displayName = userinfo.name;
|
||||
}
|
||||
|
||||
if (userinfo.displayName != null)
|
||||
{
|
||||
peer.displayName = userinfo.displayName;
|
||||
}
|
||||
|
||||
if (userinfo['urn:oid:2.16.840.1.113730.3.1.241'] != null)
|
||||
{
|
||||
peer.displayName = userinfo['urn:oid:2.16.840.1.113730.3.1.241'];
|
||||
}
|
||||
|
||||
if (userinfo.email != null)
|
||||
{
|
||||
peer.email = userinfo.email;
|
||||
}
|
||||
},
|
||||
// All users have the role "NORMAL" by default. Other roles need to be
|
||||
// added in the "userMapping" function. The following accesses and
|
||||
// permissions are arrays of roles. Roles can be changed in userRoles.js
|
||||
//
|
||||
// Example:
|
||||
// [ userRoles.MODERATOR, userRoles.AUTHENTICATED ]
|
||||
accessFromRoles : {
|
||||
// The role(s) will gain access to the room
|
||||
// even if it is locked (!)
|
||||
[BYPASS_ROOM_LOCK] : [ userRoles.ADMIN ],
|
||||
// The role(s) will gain access to the room without
|
||||
// going into the lobby. If you want to restrict access to your
|
||||
// server to only directly allow authenticated users, you could
|
||||
// add the userRoles.AUTHENTICATED to the user in the userMapping
|
||||
// function, and change to BYPASS_LOBBY : [ userRoles.AUTHENTICATED ]
|
||||
[BYPASS_LOBBY] : [ userRoles.NORMAL ]
|
||||
},
|
||||
permissionsFromRoles : {
|
||||
// The role(s) have permission to lock/unlock a room
|
||||
[CHANGE_ROOM_LOCK] : [ userRoles.MODERATOR ],
|
||||
// The role(s) have permission to promote a peer from the lobby
|
||||
[PROMOTE_PEER] : [ userRoles.NORMAL ],
|
||||
// The role(s) have permission to give/remove other peers roles
|
||||
[MODIFY_ROLE] : [ userRoles.NORMAL ],
|
||||
// The role(s) have permission to send chat messages
|
||||
[SEND_CHAT] : [ userRoles.NORMAL ],
|
||||
// The role(s) have permission to moderate chat
|
||||
[MODERATE_CHAT] : [ userRoles.MODERATOR ],
|
||||
// The role(s) have permission to share audio
|
||||
[SHARE_AUDIO] : [ userRoles.NORMAL ],
|
||||
// The role(s) have permission to share video
|
||||
[SHARE_VIDEO] : [ userRoles.NORMAL ],
|
||||
// The role(s) have permission to share screen
|
||||
[SHARE_SCREEN] : [ userRoles.NORMAL ],
|
||||
// The role(s) have permission to produce extra video
|
||||
[EXTRA_VIDEO] : [ userRoles.NORMAL ],
|
||||
// The role(s) have permission to share files
|
||||
[SHARE_FILE] : [ userRoles.NORMAL ],
|
||||
// The role(s) have permission to moderate files
|
||||
[MODERATE_FILES] : [ userRoles.MODERATOR ],
|
||||
// The role(s) have permission to moderate room (e.g. kick user)
|
||||
[MODERATE_ROOM] : [ userRoles.MODERATOR ]
|
||||
},
|
||||
// Array of permissions. If no peer with the permission in question
|
||||
// is in the room, all peers are permitted to do the action. The peers
|
||||
// that are allowed because of this rule will not be able to do this
|
||||
// action as soon as a peer with the permission joins. In this example
|
||||
// everyone will be able to lock/unlock room until a MODERATOR joins.
|
||||
allowWhenRoleMissing : [ CHANGE_ROOM_LOCK ],
|
||||
// When truthy, the room will be open to all users when as long as there
|
||||
// are allready users in the room
|
||||
activateOnHostJoin : true,
|
||||
// When set, maxUsersPerRoom defines how many users can join
|
||||
// a single room. If not set, there is no limit.
|
||||
// maxUsersPerRoom : 20,
|
||||
// Room size before spreading to new router
|
||||
routerScaleSize : 40,
|
||||
// Socket timout value
|
||||
requestTimeout : 20000,
|
||||
// Socket retries when timeout
|
||||
requestRetries : 3,
|
||||
// Mediasoup settings
|
||||
mediasoup :
|
||||
{
|
||||
numWorkers : Object.keys(os.cpus()).length,
|
||||
// mediasoup Worker settings.
|
||||
worker :
|
||||
{
|
||||
logLevel : 'warn',
|
||||
logTags :
|
||||
[
|
||||
'info',
|
||||
'ice',
|
||||
'dtls',
|
||||
'rtp',
|
||||
'srtp',
|
||||
'rtcp'
|
||||
],
|
||||
rtcMinPort : 40000,
|
||||
rtcMaxPort : 49999
|
||||
},
|
||||
// mediasoup Router settings.
|
||||
router :
|
||||
{
|
||||
// Router media codecs.
|
||||
mediaCodecs :
|
||||
[
|
||||
{
|
||||
kind : 'audio',
|
||||
mimeType : 'audio/opus',
|
||||
clockRate : 48000,
|
||||
channels : 2
|
||||
},
|
||||
{
|
||||
kind : 'video',
|
||||
mimeType : 'video/VP8',
|
||||
clockRate : 90000,
|
||||
parameters :
|
||||
{
|
||||
'x-google-start-bitrate' : 1000
|
||||
}
|
||||
},
|
||||
{
|
||||
kind : 'video',
|
||||
mimeType : 'video/VP9',
|
||||
clockRate : 90000,
|
||||
parameters :
|
||||
{
|
||||
'profile-id' : 2,
|
||||
'x-google-start-bitrate' : 1000
|
||||
}
|
||||
},
|
||||
{
|
||||
kind : 'video',
|
||||
mimeType : 'video/h264',
|
||||
clockRate : 90000,
|
||||
parameters :
|
||||
{
|
||||
'packetization-mode' : 1,
|
||||
'profile-level-id' : '4d0032',
|
||||
'level-asymmetry-allowed' : 1,
|
||||
'x-google-start-bitrate' : 1000
|
||||
}
|
||||
},
|
||||
{
|
||||
kind : 'video',
|
||||
mimeType : 'video/h264',
|
||||
clockRate : 90000,
|
||||
parameters :
|
||||
{
|
||||
'packetization-mode' : 1,
|
||||
'profile-level-id' : '42e01f',
|
||||
'level-asymmetry-allowed' : 1,
|
||||
'x-google-start-bitrate' : 1000
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// mediasoup WebRtcTransport settings.
|
||||
webRtcTransport :
|
||||
{
|
||||
listenIps : getListenIps(),
|
||||
/*[
|
||||
// change 192.0.2.1 IPv4 to your server's IPv4 address!!
|
||||
//{ ip: '192.0.2.1', announcedIp: null }
|
||||
// Can have multiple listening interfaces
|
||||
// change 2001:DB8::1 IPv6 to your server's IPv6 address!!
|
||||
// { ip: '2001:DB8::1', announcedIp: null }
|
||||
],*/
|
||||
initialAvailableOutgoingBitrate : 1000000,
|
||||
minimumAvailableOutgoingBitrate : 600000,
|
||||
// Additional options that are not part of WebRtcTransportOptions.
|
||||
maxIncomingBitrate : 1500000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
,
|
||||
// Prometheus exporter
|
||||
prometheus : {
|
||||
deidentify : false, // deidentify IP addresses
|
||||
// listen : 'localhost', // exporter listens on this address
|
||||
numeric : false, // show numeric IP addresses
|
||||
port : 8889, // allocated port
|
||||
quiet : false // include fewer labels
|
||||
}
|
||||
|
||||
};
|
||||
28
compose/config/grafana-prometheus-datasource.yml
Normal file
28
compose/config/grafana-prometheus-datasource.yml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: 1
|
||||
|
||||
deleteDatasources:
|
||||
- name: Prometheus
|
||||
orgId: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://prometheus:9090
|
||||
password:
|
||||
user:
|
||||
database: prometheus
|
||||
basicAuth: false
|
||||
basicAuthUser:
|
||||
basicAuthPassword:
|
||||
withCredentials:
|
||||
isDefault: true
|
||||
jsonData:
|
||||
tlsAuth: false
|
||||
tlsAuthWithCACert: false
|
||||
secureJsonData:
|
||||
tlsCACert: ""
|
||||
tlsClientCert: ""
|
||||
tlsClientKey: ""
|
||||
version: 1
|
||||
editable: true
|
||||
26
compose/config/prometheus.yml
Normal file
26
compose/config/prometheus.yml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# global config
|
||||
global:
|
||||
scrape_interval: 120s # By default, scrape targets every 15 seconds.
|
||||
evaluation_interval: 120s # By default, scrape targets every 15 seconds.
|
||||
# scrape_timeout is set to the global default (10s).
|
||||
# Attach these labels to any time series or alerts when communicating with
|
||||
# external systems (federation, remote storage, Alertmanager).
|
||||
external_labels:
|
||||
monitor: 'edumeet'
|
||||
|
||||
# Load and evaluate rules in this file every 'evaluation_interval' seconds.
|
||||
rule_files:
|
||||
# - "alert.rules"
|
||||
# - "first.rules"
|
||||
# - "second.rules"
|
||||
|
||||
# A scrape configuration containing exactly one endpoint to scrape:
|
||||
# Here it's Prometheus itself.
|
||||
scrape_configs:
|
||||
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
|
||||
- job_name: 'prometheus'
|
||||
scrape_interval: 15s
|
||||
# metrics_path defaults to '/metrics'
|
||||
# scheme defaults to 'http'.
|
||||
static_configs:
|
||||
- targets: ['localhost:9090','node-exporter:9100','edumeet:8889']
|
||||
97
compose/docker-compose.yml
Normal file
97
compose/docker-compose.yml
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
networks:
|
||||
edumeet:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 172.22.0.0/24
|
||||
|
||||
services:
|
||||
|
||||
edumeet:
|
||||
build: ./edumeet
|
||||
container_name: edumeet
|
||||
restart: unless-stopped
|
||||
user: "${UID}:${GID}"
|
||||
volumes:
|
||||
- ${PWD}/..:/edumeet
|
||||
- ${PWD}/config/edumeet-server-config.js:/edumeet/server/config/config.js:ro
|
||||
- ${PWD}/config/edumeet-app-config.js:/edumeet/app/public/config/config.js:ro
|
||||
environment:
|
||||
- DEBUG="edumeet*,mediasoup*"
|
||||
network_mode: "host"
|
||||
extra_hosts:
|
||||
redis: 172.22.0.2
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
redis:
|
||||
image: redis
|
||||
container_name: edumeet_redis
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 6379
|
||||
networks:
|
||||
edumeet:
|
||||
ipv4_address: 172.22.0.2
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
user: root
|
||||
container_name: edumeet_prometheus
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- ./data/prometheus:/prometheus
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--storage.tsdb.path=/prometheus'
|
||||
expose:
|
||||
- 9090
|
||||
ports:
|
||||
- 9090:9090
|
||||
links:
|
||||
#- cadvisor:cadvisor
|
||||
- node-exporter:node-exporter
|
||||
- edumeet:edumeet
|
||||
extra_hosts:
|
||||
edumeet: 172.22.0.1
|
||||
|
||||
node-exporter:
|
||||
image: prom/node-exporter:latest
|
||||
container_name: edumeet_node_exporter
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 9100
|
||||
|
||||
#cadvisor:
|
||||
# image: google/cadvisor:latest
|
||||
# container_name: edumeet_cadvisor
|
||||
# restart: unless-stopped
|
||||
# volumes:
|
||||
# - /:/rootfs:ro
|
||||
# - /var/run:/var/run:rw
|
||||
# - /sys:/sys:ro
|
||||
# - /var/lib/docker/:/var/lib/docker:ro
|
||||
# expose:
|
||||
# - 8080
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
user: root
|
||||
container_name: edumeet_grafana
|
||||
restart: unless-stopped
|
||||
links:
|
||||
- prometheus:prometheus
|
||||
expose:
|
||||
- 9091
|
||||
ports:
|
||||
- 9091:3000
|
||||
volumes:
|
||||
- ./config/grafana-prometheus-datasource.yml:/etc/grafana/provisioning/datasources/prometheus.yml
|
||||
- ./data/grafana:/var/lib/grafana
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_USER=admin
|
||||
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||
- GF_USERS_ALLOW_SIGN_UP=false
|
||||
8
compose/edumeet/Dockerfile
Normal file
8
compose/edumeet/Dockerfile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
FROM node:14-slim
|
||||
RUN apt-get update && \
|
||||
apt-get install -y git build-essential python && \
|
||||
apt-get clean
|
||||
WORKDIR /edumeet
|
||||
RUN npm install -g nodemon && \
|
||||
npm install -g concurrently
|
||||
CMD concurrently --names "server,app" "cd server && yarn && nodemon server.js" "cd app && yarn && yarn start"
|
||||
Loading…
Add table
Add a link
Reference in a new issue