From 298837abceff320b0072285a240a007ae0fc0d53 Mon Sep 17 00:00:00 2001 From: Miroslav Pejic Date: Tue, 16 Apr 2024 14:33:17 +0200 Subject: [PATCH] [mirotalksfu] - welcome cloudron.io, add email notify/alerts, update dep --- README.md | 1 + app/src/Server.js | 10 ++- app/src/config.template.js | 13 ++++ app/src/lib/nodemailer.js | 134 +++++++++++++++++++++++++++++++++++ package.json | 7 +- public/js/Room.js | 2 +- public/js/RoomClient.js | 2 +- public/sponsors/Cloudron.png | Bin 0 -> 5493 bytes public/views/landing.html | 10 +++ 9 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 app/src/lib/nodemailer.js create mode 100644 public/sponsors/Cloudron.png diff --git a/README.md b/README.md index 9da5efab..8d349211 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,7 @@ Do you find MiroTalk SFU indispensable for your needs? Join us in supporting thi | [![BroadcastX](public/sponsors/BroadcastX.png)](https://broadcastx.de/) | [![Hetzner](public/sponsors/HetznerLogo.png)](https://hetzner.cloud/?ref=XdRifCzCK3bn) | | [![LuvLounge](public/sponsors/LuvLounge.png)](https://luvlounge.ca) | [![QuestionPro](public/sponsors/QuestionPro.png)](https://www.questionpro.com) | | [![BrowserStack](public/sponsors/BrowserStack.png)](https://www.browserstack.com) | [![CrystalSound](public/sponsors/CrystalSound.png)](https://crystalsound.ai) | +| [![Cloudron](public/sponsors/Cloudron.png)](https://cloudron.io) | | diff --git a/app/src/Server.js b/app/src/Server.js index 6b7c9a7c..bcea73d9 100644 --- a/app/src/Server.js +++ b/app/src/Server.js @@ -41,7 +41,7 @@ dependencies: { * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.4.18 + * @version 1.4.19 * */ @@ -73,6 +73,9 @@ const { CaptureConsole } = require('@sentry/integrations'); const restrictAccessByIP = require('./middleware/IpWhitelist.js'); const packageJson = require('../../package.json'); +// Email alerts and notifications +const nodemailer = require('./lib/nodemailer'); + // Slack API const CryptoJS = require('crypto-js'); const qS = require('qs'); @@ -1015,6 +1018,11 @@ function startServer() { return cb('isLobby'); } + // SCENARIO: Notify when the first user join room and is awaiting assistance... + if (room.getPeersCount() === 1) { + nodemailer.sendEmailAlert('join', { peer_name: peer_name, room_id: room.id }); // config.email.alert: true + } + cb(room.toJson()); }); diff --git a/app/src/config.template.js b/app/src/config.template.js index a4231c09..5b2c094e 100644 --- a/app/src/config.template.js +++ b/app/src/config.template.js @@ -107,6 +107,19 @@ module.exports = { ], join_first: true, // Set to true for traditional behavior, false to prioritize presenters }, + email: { + /* + Configure email settings for notifications or alerts + Refer to the documentation for Gmail configuration: https://support.google.com/mail/answer/185833?hl=en + */ + alert: false, + host: 'smtp.gmail.com', + port: 587, + username: 'your_username', + password: 'your_password', + sendTo: 'sfu.mirotalk@gmail.com', + domain: 'sfu.mirotalk.com', + }, ui: { /* Customize your MiroTalk instance diff --git a/app/src/lib/nodemailer.js b/app/src/lib/nodemailer.js new file mode 100644 index 00000000..83d9c44e --- /dev/null +++ b/app/src/lib/nodemailer.js @@ -0,0 +1,134 @@ +'use-strict'; + +const nodemailer = require('nodemailer'); +const config = require('../config'); +const Logger = require('../Logger'); +const log = new Logger('NodeMailer'); + +// #################################################### +// EMAIL CONFIG +// #################################################### + +const EMAIL_HOST = config.email ? config.email.host : false; +const EMAIL_PORT = config.email ? config.email.port : false; +const EMAIL_USERNAME = config.email ? config.email.username : false; +const EMAIL_PASSWORD = config.email ? config.email.password : false; +const EMAIL_SEND_TO = config.email ? config.email.sendTo : false; +const EMAIL_ALERT = config.email ? config.email.alert : false; +const DOMAIN = config.email ? config.email.domain : 'sfu.mirotlalk.com'; + +const ROOM_URL = `https://${DOMAIN}/join/`; + +log.info('Email', { + alert: EMAIL_ALERT, + host: EMAIL_HOST, + port: EMAIL_PORT, + username: EMAIL_USERNAME, + password: EMAIL_PASSWORD, +}); + +const transport = nodemailer.createTransport({ + host: EMAIL_HOST, + port: EMAIL_PORT, + auth: { + user: EMAIL_USERNAME, + pass: EMAIL_PASSWORD, + }, +}); + +// #################################################### +// EMAIL SEND ALERTS AND NOTIFICATIONS +// #################################################### + +function sendEmailAlert(event, data) { + if (!EMAIL_ALERT || !EMAIL_HOST || !EMAIL_PORT || !EMAIL_USERNAME || !EMAIL_PASSWORD || !EMAIL_SEND_TO) return; + + log.info('sendEMailAlert', { + event: event, + data: data, + }); + + let subject = false; + let body = false; + + switch (event) { + case 'join': + const { peer_name, room_id } = data; + subject = getJoinRoomSubject(room_id); + body = getJoinRoomBody(peer_name, room_id); + break; + // ... + default: + break; + } + + if (subject && body) sendEmail(subject, body); +} + +function sendEmail(subject, body) { + transport + .sendMail({ + from: EMAIL_USERNAME, + to: EMAIL_SEND_TO, + subject: subject, + html: body, + }) + .catch((err) => log.error(err)); +} + +// #################################################### +// EMAIL TEMPLATES +// #################################################### + +function getJoinRoomSubject(room_id) { + return `MiroTalk SFU - New user Join to Room ${room_id}`; +} +function getJoinRoomBody(peer_name, room_id) { + const currentDataTime = getCurrentDataTime(); + return ` +

New user join

+ + + + + + + + + + + + + + +
User${peer_name}
Room${ROOM_URL}${room_id}
Time${currentDataTime}
+ `; +} + +// #################################################### +// UTILITY +// #################################################### + +function getCurrentDataTime() { + const currentTime = new Date().toLocaleString('en-US', log.tzOptions); + const milliseconds = String(new Date().getMilliseconds()).padStart(3, '0'); + return `${currentTime}:${milliseconds}`; +} + +module.exports = { + sendEmailAlert, +}; diff --git a/package.json b/package.json index 75ca0f46..27ff4a9c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mirotalksfu", - "version": "1.4.18", + "version": "1.4.19", "description": "WebRTC SFU browser-based video calls", "main": "Server.js", "scripts": { @@ -41,8 +41,8 @@ "node": ">=18" }, "dependencies": { - "@sentry/integrations": "7.110.0", - "@sentry/node": "7.110.0", + "@sentry/integrations": "7.110.1", + "@sentry/node": "7.110.1", "axios": "^1.6.8", "body-parser": "1.20.2", "colors": "1.4.0", @@ -55,6 +55,7 @@ "mediasoup": "3.14.1", "mediasoup-client": "3.7.6", "ngrok": "^5.0.0-beta.2", + "nodemailer": "^6.9.13", "openai": "^4.33.1", "qs": "6.12.1", "socket.io": "4.7.5", diff --git a/public/js/Room.js b/public/js/Room.js index 624a0444..6c31938a 100644 --- a/public/js/Room.js +++ b/public/js/Room.js @@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.4.18 + * @version 1.4.19 * */ diff --git a/public/js/RoomClient.js b/public/js/RoomClient.js index 5cbe9a44..d47c4b00 100644 --- a/public/js/RoomClient.js +++ b/public/js/RoomClient.js @@ -9,7 +9,7 @@ * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.4.18 + * @version 1.4.19 * */ diff --git a/public/sponsors/Cloudron.png b/public/sponsors/Cloudron.png new file mode 100644 index 0000000000000000000000000000000000000000..b94a10d6d1bce0dc91987023ea52a3e37546de74 GIT binary patch literal 5493 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 MHGS1e6`Sz?1IxRCR{#J2 literal 0 HcmV?d00001 diff --git a/public/views/landing.html b/public/views/landing.html index cc2d308c..7f2bdfb5 100644 --- a/public/views/landing.html +++ b/public/views/landing.html @@ -490,6 +490,16 @@ /> +
  • + +