diff --git a/.env.sample b/.env.sample index 4230985..6a7b94c 100644 --- a/.env.sample +++ b/.env.sample @@ -3,4 +3,11 @@ PORT=5000 # you can call `sudo bbb-conf --secret` for the following two values BBB_API_URL=https://demo.bigbluebutton.org/bigbluebutton BBB_API_SECRET=FIXME -WELCOME_MESSAGE="Welcome to %%CONFNAME%%!

Invite others with following URL: %%JOINURL%%

Use a headset to avoid causing background noise for others." + +## Welcome Message for new users +WELCOME_MESSAGE="Welcome to%%CONFNAME%%.

Use a headset to avoid causing background noise for others." +## welcome message for new users if no manual password is set +NOPW_WELCOME_MESSAGE=""Welcome to %%CONFNAME%%!

Invite others with following URL: %%JOINURL%%

Use a headset to avoid causing background noise for others." +## welcome message for moderators only. Will only be shown if a manual roompassword is set +MOD_WELCOME_MESSAGE="Dear Moderator,

Invite others with following URL: %%JOINURL%%

Provide the following passwords to join:
Moderator: %%MPASSWD%%
User: %%UPASSWD%%" + diff --git a/README.md b/README.md index 42a3d48..f9e7a93 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ bbb-easy-join ------------- -An easy frontend to BigBlueButton without signups - everybody can just entry a meeting room name, the first user gets moderator level access. +An easy frontend to BigBlueButton without signups - everybody can just enter a meeting room name and an optional password. +The first user gets moderator level access. + @@ -9,10 +11,16 @@ An easy frontend to BigBlueButton without signups - everybody can just entry a m Tested with an BBB instance installed with [bbb-install.sh](https://github.com/bigbluebutton/bbb-install). +Installation requires git and bbb to be installed on the same machine + +```bash +sudo apt install git +``` + Checkout this repository to /var/www/bbb-easy-join ``` -git clone https://github.com/stadtulm/bbb-easy-join.git /var/www/bbb-easy-join +git clone https://github.com/svoeth/bbb-easy-join.git /var/www/bbb-easy-join sudo chown -r bigbluebutton: /var/www/bbb-easy-join sudo -iu bigbluebutton cd /var/www/bbb-easy-join @@ -23,11 +31,21 @@ Create an `.env` file (use `.env.sample` as a template) and enter your BBB API U For serving the pages, copy `bbb-easy-join.nginx` to `/etc/bigbluebutton/nginx/`. If there are already `greenlight-redirect.nginx` and/or `greenlight.nginx`, rename (remove the .nginx suffix) or delete them. -If you want to keep the service running, the systemd service file `bbb-easy-join.service` can be copied to `/etc/systemd/system/` and activated by `sudo systemctl daemon-reload`, `sudo systemctl enable bbb-easy-join` and `sudo systemctl start bbb-easy-join`. +You can start the service by running `/usr/bin/node /var/www/bbb-easy-join/app.js` +If you want to keep the service running in background and on startup: +```bash +# copy the systemd service file and register it with systemd +cp bbb-easy-join.service /etc/systemd/system/ +sudo systemctl daemon-reload +# activate it to start on startup +sudo systemctl enable bbb-easy-join +# start it immediately +sudo systemctl start bbb-easy-join +``` ### Note -Your BigBlueButton Instance is then (if not only reachable from inside your network) public, everybody can create and moderate/present in a meeting. Therefore, you may want to think about disabling recordings, the security of accepting presentation files etc. +Your BigBlueButton Instance is then (if not only reachable from inside your network or protected otherwise) public, everybody can create and moderate/present in a meeting. Therefore, you may want to think about disabling recordings, the security of accepting presentation files etc. ### Credits diff --git a/app.js b/app.js index 9d0e93f..0cac5c9 100644 --- a/app.js +++ b/app.js @@ -12,6 +12,8 @@ const host = process.env.HOST || '0.0.0.0' const BBB_API_URL = process.env.BBB_API_URL const BBB_API_SECRET = process.env.BBB_API_SECRET const WELCOME_MESSAGE = process.env.WELCOME_MESSAGE +const NOPW_WELCOME_MESSAGE = process.env.NOPW_WELCOME_MESSAGE +const MOD_WELCOME_MESSAGE = process.env.MOD_WELCOME_MESSAGE // TODO: internationalize more slugify.extend({'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss'}) @@ -56,6 +58,15 @@ const joinMeetingUrl = function (id, name, password) { return buildApiUrl('join', params); } +const generateRandomPassword = function () { + const alpha = 'qwertyuiopasdfghjklzxcvbnm'; + var password = ''; + for (let i=0;i<24;i++) { + password += alpha[Math.floor(Math.random() * alpha.length)] + } + return password; +} + app.use(express.static('public')) app.set('view engine', 'ejs') app.set('trust proxy', 'loopback') @@ -66,39 +77,105 @@ app.get('/', function(req, res) { }) app.get('/b', function (req, res) { - res.render('index') + res.render('index', { + roomName: req.query.roomName, + roomBlocked: req.query.roomBlocked, + roomMissing: req.query.roomMissing }) }) + +// create a room (optionally with password) +// if password is set provide mod and user pw for welcome message app.post('/b', async function (req, res) { var roomName = req.body.room var room = slugify(roomName, { lower: true }) - var options = {} + // if user provides password within room creation, set prefix "user-" otherwise set it to "auto-" + var room_password; + var custom_password = req.body.room_password; + if (custom_password) { + // generate random moderator password to give possibility to provide it in welcome message + var moderator_password = generateRandomPassword(); + room_password = "user-" + custom_password; + // add passwords to options for room creation + var options = { 'attendeePW' : room_password, 'moderatorPW' : moderator_password } + }else { + // create random password with "auto-" prefix. Not entirely necessary, but theoretically random password + // could start with "user-" and might therefore accidentally be interpreted as manual pw + room_password = "auto-" + generateRandomPassword(); + var options = { 'attendeePW' : room_password } + } + + //set welcome message(s) if (typeof WELCOME_MESSAGE === 'string') { - var url = req.protocol + '://' + req.get('host') + '/b/' + room + var url = req.protocol + '://' + req.get('host') + '/b/' + roomName var joinpattern = new RegExp('%%JOINURL%%', 'g') - options.welcome = WELCOME_MESSAGE.replace(joinpattern, url) + //if the room has a manual password set we will provide two welcome msgs and only inform moderators about joining options and passwords + if (room_password.startsWith('user-')) { + var mpasswd = new RegExp('%%MPASSWD%%', 'g') + var upasswd = new RegExp('%%UPASSWD%%', 'g') + var modwelcome = MOD_WELCOME_MESSAGE.replace(joinpattern, url) + modwelcome = modwelcome.replace(mpasswd, moderator_password) + options.moderatorOnlyMessage = modwelcome.replace(upasswd, custom_password) + options.welcome = WELCOME_MESSAGE + } else { + // welcome message with no manual pw gets joinurl + options.welcome = NOPW_WELCOME_MESSAGE.replace(joinpattern, url) + } } var meet = await createMeeting(roomName, room, options) - if (meet.returncode === 'FAILED' && meet.messageKey !== 'idNotUnique') { - res.redirect('/b') - return + //check if roomcreation was not succesful + if (meet.returncode === 'FAILED') { + //if room exists put warning message on page - else just go back + if (meet.messageKey === 'idNotUnique') { + // timeout as a very basic bruteforce prevention to prevent room search + setTimeout(function (){ + res.redirect('/b' + `?roomName=${encodeURIComponent(roomName)}&roomBlocked=true`); + }, 1000); + return + } else { + res.redirect('/b'); + return + } } - res.redirect('/b/' + room) + //slugify roomName because room contains prefix + res.redirect('/b/' + room ) }) app.get('/b/:room', async function (req, res){ - var room = slugify(req.params.room) - + // added slugify lower to prevent error if accidental uppercase or special characters are in adress + var room = slugify(req.params.room, { lower: true }) var info = await getMeetingInfo(room) + + //check if meeting info was not succesfully grabbed and in case redirect with error message (room not created) if (info.returncode === 'FAILED') { - res.redirect('/b') + // timeout as a very basic bruteforce prevention to prevent room search + setTimeout(function (){ + res.redirect('/b' + `?roomName=${encodeURIComponent(req.params.room)}&roomMissing=true`) + }, 1000); return } - res.render('join', { room: room, info: info }) + // This variable decides whether we display a password dialogue. + // Normally, meetings require no user provided password... + var requiresPassword = false; + // ... unless one is set + if (info.attendeePW.startsWith('user-')) { + requiresPassword = true; + } + // ... except when the room is empty, then we'll become the moderator anyway. + if (info.hasUserJoined == false || info.participantCount == 0) { + requiresPassword = false; + } + + res.render('join', { + room: room, + info: info, + requiresPassword: requiresPassword, + username: req.query.username, + wrongPw: req.query.wrongPw }) }) app.post('/b/:room', async function (req, res) { @@ -111,10 +188,26 @@ app.post('/b/:room', async function (req, res) { } var password = info.attendeePW + // check if there is anybody in the meeting - if not give moderator rights if (info.hasUserJoined == false || info.participantCount == 0) { password = info.moderatorPW + // if there is somebody in the meeting get the attendee pw and check if it is manually set (starts wit user-) otherwise just join as attende + } else { + password = info.attendeePW; + if (password.startsWith('user-')) { + // check if provided login password matches required password if not redirect to login page with error + if (password != "user-" + req.body.room_password) { + password = info.moderatorPW; + if (password != req.body.room_password) { + // timeout as a very basic bruteforce prevention - double time for password try (2 seconds) + setTimeout(function (){ + res.redirect('/b/' + info.meetingName + `?username=${encodeURIComponent(name)}&wrongPw=true`); + }, 2000); + return; + } + } + } } - res.redirect(joinMeetingUrl(room, name, password)) }) diff --git a/docs/overview.png b/docs/overview.png index 49faac2..48bebc9 100644 Binary files a/docs/overview.png and b/docs/overview.png differ diff --git a/package-lock.json b/package-lock.json index 2c1e2be..7465022 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", "requires": { "mime-types": "~2.1.24", "negotiator": "0.6.2" @@ -21,7 +21,7 @@ "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", "requires": { "bytes": "3.1.0", "content-type": "~1.0.4", @@ -38,12 +38,12 @@ "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=" }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", "requires": { "safe-buffer": "5.1.2" } @@ -51,12 +51,12 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" }, "cookie": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "integrity": "sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo=" }, "cookie-signature": { "version": "1.0.6", @@ -66,7 +66,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", "requires": { "ms": "2.0.0" } @@ -84,7 +84,7 @@ "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + "integrity": "sha1-l+YZJZradQ7qPk6j4mvO6lQksWo=" }, "ee-first": { "version": "1.1.1", @@ -114,7 +114,7 @@ "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "integrity": "sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=", "requires": { "accepts": "~1.3.7", "array-flatten": "1.1.1", @@ -151,12 +151,12 @@ "fast-xml-parser": { "version": "3.16.0", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.16.0.tgz", - "integrity": "sha512-U+bpScacfgnfNfIKlWHDu4u6rtOaCyxhblOLJ8sZPkhsjgGqdZmVPBhdOyvdMGCDt8CsAv+cssOP3NzQptNt2w==" + "integrity": "sha1-2QXn5rKPxGSMq+vLB0Njhn+1buI=" }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -180,7 +180,7 @@ "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", "requires": { "depd": "~1.1.2", "inherits": "2.0.3", @@ -192,7 +192,7 @@ "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -205,7 +205,7 @@ "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "integrity": "sha1-v/OFQ+64mEglB5/zoqjmy9RngbM=" }, "media-typer": { "version": "0.3.0", @@ -225,7 +225,7 @@ "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=" }, "mime-db": { "version": "1.43.0", @@ -248,12 +248,12 @@ "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=" }, "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "integrity": "sha1-5jNFY4bUqlWGP2dqerDaqP3ssP0=" }, "on-finished": { "version": "2.3.0", @@ -266,7 +266,7 @@ "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=" }, "path-to-regexp": { "version": "0.1.7", @@ -276,7 +276,7 @@ "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "integrity": "sha1-/cIzZQVEfT8vLGOO0nLK9hS7sr8=", "requires": { "forwarded": "~0.1.2", "ipaddr.js": "1.9.1" @@ -285,17 +285,17 @@ "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=" }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=" }, "raw-body": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", "requires": { "bytes": "3.1.0", "http-errors": "1.7.2", @@ -306,17 +306,17 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=", "requires": { "debug": "2.6.9", "depd": "~1.1.2", @@ -336,14 +336,14 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=" } } }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -354,12 +354,12 @@ "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=" }, "slugify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.0.tgz", - "integrity": "sha512-FtLNsMGBSRB/0JOE2A0fxlqjI6fJsgHGS13iTuVT28kViI4JjUiNqp/vyis0ZXYcMnpR3fzGNkv+6vRlI2GwdQ==" + "integrity": "sha1-yVV8ZTxUsMf3qOeG7zQxrdZ20ss=" }, "statuses": { "version": "1.5.0", @@ -369,12 +369,12 @@ "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=" }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.24" diff --git a/views/index.ejs b/views/index.ejs index 3a488da..86e55fa 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -40,20 +40,35 @@ -
-
-
-

Create a Meeting

-

(Think about a short and unique meeting room name)

-
- -
-
- +
+
+
+ <% if (roomMissing) { %> +

Room <%= roomName %> does not exist (yet).

+
+

Do you want to create it?

+

(or just create any room with a unique name)

+ <% } else { %> + <% if (roomBlocked) { %> +

Room <%= roomName %> already exists. +
+ Click here to join <%= roomName %>.

+

+ Or choose another room name: +

+ <% } else { %> +

Enter meeting room name

+

(think of a unique name for your room)

+ <% } %> + <% } %> +
+ value="<%= roomName %>"<% } %>placeholder="Roomname" size="29" class="field input-default" autofocus=""> + +
+
-
diff --git a/views/join.ejs b/views/join.ejs index a05a996..1e2e6a9 100644 --- a/views/join.ejs +++ b/views/join.ejs @@ -34,26 +34,25 @@
- - - - - -
-
-
-

Joining <%= info.meetingName %>

- -
- -
-
- -
-
-
- +
+
+
+

Konferenz <%= info.meetingName %> beitreten

+ <% if (wrongPw) { %> +

Wrong password

+ <% } %> +
+ value="<%= username %>"<% } %> placeholder="Choose a name" size="29" class="field input-default" autofocus> + <% if (requiresPassword) { %> + + <% } %> +
+
+
+
+
+