From f410d5a0b84702d28b8524d43b3440baaa16749a Mon Sep 17 00:00:00 2001 From: Miroslav Pejic Date: Mon, 16 Dec 2024 14:01:57 +0100 Subject: [PATCH] [mirotalk] - add config server side, update dep --- .gitignore | 1 + README.md | 4 ++ app/src/config.template.js | 105 ++++++++++++++++++++++++++++++++++++ app/src/server.js | 36 ++++++++++++- docker-compose.template.yml | 2 + package.json | 4 +- public/js/brand.js | 99 ++++++++++++++++++++++++++-------- public/js/buttons.js | 2 +- public/js/client.js | 30 ++++++++++- public/views/client.html | 2 + 10 files changed, 255 insertions(+), 30 deletions(-) create mode 100644 app/src/config.template.js diff --git a/.gitignore b/.gitignore index 6edaceaa..fbb32a90 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ package-lock.json # personal env .env +config.js docker-push.sh docker-compose.yml output diff --git a/README.md b/README.md index c21f5896..ed14131c 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,8 @@ $ git clone https://github.com/miroslavpejic85/mirotalk.git $ cd mirotalk # copy .env.template to .env (edit it according to your needs) $ cp .env.template .env +# Copy app/src/config.template.js in app/src/config.js (edit it according to your needs) +$ cp app/src/config.template.js app/src/config.js # install dependencies $ npm install # start the server @@ -198,6 +200,8 @@ $ git clone https://github.com/miroslavpejic85/mirotalk.git $ cd mirotalk # copy .env.template to .env (edit it according to your needs) $ cp .env.template .env +# Copy app/src/config.template.js in app/src/config.js (edit it according to your needs) +$ cp app/src/config.template.js app/src/config.js # Copy docker-compose.template.yml in docker-compose.yml (edit it according to your needs) $ cp docker-compose.template.yml docker-compose.yml # Get official image from Docker Hub diff --git a/app/src/config.template.js b/app/src/config.template.js new file mode 100644 index 00000000..58f445b4 --- /dev/null +++ b/app/src/config.template.js @@ -0,0 +1,105 @@ +'use strict'; + +module.exports = { + /** + * Configuration for controlling the visibility of buttons in the MiroTalk P2P client. + * Set properties to true to show the corresponding buttons, or false to hide them. + * captionBtn, showSwapCameraBtn, showScreenShareBtn, showFullScreenBtn, showVideoPipBtn, showDocumentPipBtn -> (auto-detected). + */ + buttons: { + main: { + showShareRoomBtn: true, // For guests + showHideMeBtn: true, + showAudioBtn: true, + showVideoBtn: true, + showScreenBtn: true, // autodetected + showRecordStreamBtn: true, + showChatRoomBtn: true, + showCaptionRoomBtn: true, + showRoomEmojiPickerBtn: true, + showMyHandBtn: true, + showWhiteboardBtn: true, + showSnapshotRoomBtn: true, + showFileShareBtn: true, + showMySettingsBtn: true, + showAboutBtn: true, // Please keep me always true, Thank you! + }, + chat: { + showTogglePinBtn: true, + showMaxBtn: true, + showSaveMessageBtn: true, + showMarkDownBtn: true, + showChatGPTBtn: true, + showFileShareBtn: true, + showShareVideoAudioBtn: true, + showParticipantsBtn: true, + }, + caption: { + showTogglePinBtn: true, + showMaxBtn: true, + }, + settings: { + showMicOptionsBtn: true, + showTabRoomPeerName: true, + showTabRoomParticipants: true, + showTabRoomSecurity: true, + showTabEmailInvitation: true, + showCaptionEveryoneBtn: true, + showMuteEveryoneBtn: true, + showHideEveryoneBtn: true, + showEjectEveryoneBtn: true, + showLockRoomBtn: true, + showUnlockRoomBtn: true, + }, + remote: { + showAudioVolume: true, + audioBtnClickAllowed: true, + videoBtnClickAllowed: true, + showKickOutBtn: true, + showSnapShotBtn: true, + showFileShareBtn: true, + showShareVideoAudioBtn: true, + showPrivateMessageBtn: true, + showZoomInOutBtn: false, + showVideoFocusBtn: true, + }, + local: { + showSnapShotBtn: true, + showVideoCircleBtn: true, + showZoomInOutBtn: false, + }, + whiteboard: { + whiteboardLockBtn: false, + }, + }, + brand: { + app: { + name: 'MiroTalk', + title: 'MiroTalk
Free browser based Real-time video calls.
Simple, Secure, Fast.', + description: + 'Start your next video call with a single click. No download, plug-in, or login is required. Just get straight to talking, messaging, and sharing your screen.', + }, + site: { + landingTitle: 'MiroTalk a Free Secure Video Calls, Chat & Screen Sharing.', + newCallTitle: 'MiroTalk a Free Secure Video Calls, Chat & Screen Sharing.', + loginTitle: 'MiroTalk - Host Protected login required.', + clientTitle: 'MiroTalk WebRTC Video call, Chat Room & Screen Sharing.', + privacyPolicyTitle: 'MiroTalk - privacy and policy.', + stunTurnTitle: 'Test Stun/Turn Servers.', + notFoundTitle: 'MiroTalk - 404 Page not found.', + shortcutIcon: '../images/logo.svg', + appleTouchIcon: '../images/logo.svg', + }, + html: { + features: true, + browsers: true, + teams: true, // please keep me always true ;) + tryEasier: true, + poweredBy: true, + sponsors: true, + advertisers: true, + footer: true, + }, + //... + }, +}; diff --git a/app/src/server.js b/app/src/server.js index 55f846b7..04af45da 100755 --- a/app/src/server.js +++ b/app/src/server.js @@ -39,7 +39,7 @@ dependencies: { * @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.4.00 + * @version 1.4.10 * */ @@ -66,6 +66,9 @@ const Host = require('./host'); const Logs = require('./logs'); const log = new Logs('server'); +// Custom Brand and buttons +const config = safeRequire('./config'); + // Email alerts and notifications const nodemailer = require('./lib/nodemailer'); @@ -397,7 +400,7 @@ app.use((req, res, next) => { method: req.method, path: req.originalUrl, body: req.body, - headers: req.headers, + //headers: req.headers, }); next(); }); @@ -681,6 +684,16 @@ app.post(['/login'], (req, res) => { } }); +// UI buttons configuration +app.get('/buttons', (req, res) => { + res.status(200).json({ message: config && config.buttons ? config.buttons : false }); +}); + +// UI brand configuration +app.get('/brand', (req, res) => { + res.status(200).json({ message: config && config.brand ? config.brand : false }); +}); + /** MiroTalk API v1 For api docs we use: https://swagger.io/ @@ -2003,3 +2016,22 @@ function removeIP(socket) { } } } + +/** + * Load modules if exists + * @param {string} filePath + * @returns + */ +function safeRequire(filePath) { + try { + // Resolve the absolute path of the module + const resolvedPath = require.resolve(filePath); + // Check if the file exists + if (fs.existsSync(resolvedPath)) { + return require(resolvedPath); + } + } catch (error) { + log.error('Module not found', filePath); + } + return null; +} diff --git a/docker-compose.template.yml b/docker-compose.template.yml index bb484e99..f153dece 100644 --- a/docker-compose.template.yml +++ b/docker-compose.template.yml @@ -5,6 +5,8 @@ services: hostname: mirotalk volumes: - .env:/src/.env:ro + # Rebranding: + # - ./app/src/config.js:/src/app/src/config.js:ro # Optional volumes for real-time updates: # - ./app/:/src/app/:ro # - ./public/:/src/public/:ro diff --git a/package.json b/package.json index 1a73ce77..04a3e236 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mirotalk", - "version": "1.4.00", + "version": "1.4.10", "description": "A free WebRTC browser-based video call", "main": "server.js", "scripts": { @@ -42,7 +42,7 @@ "homepage": "https://github.com/miroslavpejic85/mirotalk", "dependencies": { "@mattermost/client": "^10.2.0", - "@sentry/node": "^8.45.0", + "@sentry/node": "^8.45.1", "axios": "^1.7.9", "colors": "^1.4.0", "compression": "^1.7.5", diff --git a/public/js/brand.js b/public/js/brand.js index 7f35fb07..0a59349d 100644 --- a/public/js/brand.js +++ b/public/js/brand.js @@ -1,7 +1,10 @@ 'use strict'; -// Html pages +// Brand +const brandDataKey = 'brandDataP2P'; +const brandData = window.sessionStorage.getItem(brandDataKey); +// Html pages const landingTitle = document.getElementById('landingTitle'); const newCallTitle = document.getElementById('newCallTitle'); const loginTitle = document.getElementById('loginTitle'); @@ -28,7 +31,7 @@ const footer = document.getElementById('footer'); // Brand customizations... -const brand = { +let brand = { app: { name: 'MiroTalk', title: 'MiroTalk
Free browser based Real-time video calls.
Simple, Secure, Fast.', @@ -59,31 +62,79 @@ const brand = { //... }; -// Handle brand... +/** + * Get started + */ +async function initBrand() { + await getBrand(); -if (landingTitle) landingTitle.textContent = brand.site.landingTitle; -if (newCallTitle) newCallTitle.textContent = brand.site.newCallTitle; -if (loginTitle) loginTitle.textContent = brand.site.loginTitle; -if (privacyPolicyTitle) privacyPolicyTitle.textContent = brand.site.privacyPolicyTitle; -if (stunTurnTitle) stunTurnTitle.textContent = brand.site.stunTurnTitle; -if (clientTitle) clientTitle.textContent = brand.site.clientTitle; -if (notFoundTitle) notFoundTitle.textContent = brand.site.notFoundTitle; + handleBrand(); +} -if (shortcutIcon) shortcutIcon.href = brand.site.shortcutIcon; -if (appleTouchIcon) appleTouchIcon.href = brand.site.appleTouchIcon; +/** + * Get brand from server + */ +async function getBrand() { + if (brandData) { + setBrand(JSON.parse(brandData)); + } else { + try { + const response = await fetch('/brand', { timeout: 5000 }); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + const data = await response.json(); + const serverBrand = data.message; + if (serverBrand) { + setBrand(serverBrand); + console.log('FETCH BRAND SETTINGS', { + serverBrand: serverBrand, + clientBrand: brand, + }); + window.sessionStorage.setItem(brandDataKey, JSON.stringify(serverBrand)); + } + } catch (error) { + console.error('FETCH GET BRAND ERROR', error.message); + } + } +} -if (appTitle) appTitle.innerHTML = brand.app.title; -if (appDescription) appDescription.textContent = brand.app.description; +/** + * Set brand + * @param {object} data + */ +function setBrand(data) { + brand = data; + console.log('Set Brand done'); +} -!brand.html.features && elementDisplay(features, false); -!brand.html.browsers && elementDisplay(browsers, false); -!brand.html.teams && elementDisplay(teams, false); -!brand.html.tryEasier && elementDisplay(tryEasier, false); -!brand.html.poweredBy && elementDisplay(poweredBy, false); -!brand.html.sponsors && elementDisplay(sponsors, false); -!brand.html.advertisers && elementDisplay(advertisers, false); -!brand.html.footer && elementDisplay(footer, false); -//... +/** + * Handle Brand + */ +function handleBrand() { + if (landingTitle) landingTitle.textContent = brand.site.landingTitle; + if (newCallTitle) newCallTitle.textContent = brand.site.newCallTitle; + if (loginTitle) loginTitle.textContent = brand.site.loginTitle; + if (privacyPolicyTitle) privacyPolicyTitle.textContent = brand.site.privacyPolicyTitle; + if (stunTurnTitle) stunTurnTitle.textContent = brand.site.stunTurnTitle; + if (clientTitle) clientTitle.textContent = brand.site.clientTitle; + if (notFoundTitle) notFoundTitle.textContent = brand.site.notFoundTitle; + + if (shortcutIcon) shortcutIcon.href = brand.site.shortcutIcon; + if (appleTouchIcon) appleTouchIcon.href = brand.site.appleTouchIcon; + + if (appTitle) appTitle.innerHTML = brand.app.title; + if (appDescription) appDescription.textContent = brand.app.description; + + !brand.html.features && elementDisplay(features, false); + !brand.html.browsers && elementDisplay(browsers, false); + !brand.html.teams && elementDisplay(teams, false); + !brand.html.tryEasier && elementDisplay(tryEasier, false); + !brand.html.poweredBy && elementDisplay(poweredBy, false); + !brand.html.sponsors && elementDisplay(sponsors, false); + !brand.html.advertisers && elementDisplay(advertisers, false); + !brand.html.footer && elementDisplay(footer, false); +} /** * Handle Element display @@ -95,3 +146,5 @@ function elementDisplay(element, display, mode = 'block') { if (!element) return; element.style.display = display ? mode : 'none'; } + +initBrand(); diff --git a/public/js/buttons.js b/public/js/buttons.js index 8abeb1f2..49182dd9 100644 --- a/public/js/buttons.js +++ b/public/js/buttons.js @@ -5,7 +5,7 @@ * Set properties to true to show the corresponding buttons, or false to hide them. * captionBtn, showSwapCameraBtn, showScreenShareBtn, showFullScreenBtn, showVideoPipBtn, showDocumentPipBtn -> (auto-detected). */ -const buttons = { +let buttons = { main: { showShareRoomBtn: true, showHideMeBtn: true, diff --git a/public/js/client.js b/public/js/client.js index a1814a19..31c738b6 100644 --- a/public/js/client.js +++ b/public/js/client.js @@ -15,7 +15,7 @@ * @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.4.00 + * @version 1.4.10 * */ @@ -1198,6 +1198,7 @@ async function handleConnect() { getHtmlElementsById(); setButtonsToolTip(); handleUsernameEmojiPicker(); + await getButtons(); manageButtons(); handleButtonsRule(); setupMySettings(); @@ -1382,6 +1383,31 @@ function handleButtonsRule() { : elemDisplay(whiteboardLockBtn, false); } +/** + * Get Buttons config from server side and apply to current client + */ +async function getButtons() { + try { + const response = await axios.get('/buttons', { + timeout: 5000, + }); + const serverButtons = response.data.message; + if (serverButtons) { + // Merge serverButtons into BUTTONS, keeping the existing keys in BUTTONS if they are not present in serverButtons + buttons = { + ...buttons, // Spread current BUTTONS first to keep existing keys + ...serverButtons, // Overwrite or add new keys from serverButtons + }; + console.log('AXIOS ROOM BUTTONS SETTINGS', { + serverButtons: serverButtons, + clientButtons: buttons, + }); + } + } catch (error) { + console.error('AXIOS GET CONFIG ERROR', error.message); + } +} + /** * set your name for the conference */ @@ -10664,7 +10690,7 @@ function showAbout() { Swal.fire({ background: swBg, position: 'center', - title: 'WebRTC P2P v1.4.00', + title: 'WebRTC P2P v1.4.10', imageAlt: 'mirotalk-about', imageUrl: images.about, customClass: { image: 'img-about' }, diff --git a/public/views/client.html b/public/views/client.html index 4489974d..b95e80e4 100755 --- a/public/views/client.html +++ b/public/views/client.html @@ -1020,6 +1020,7 @@ access to use this app. - https://flatpickr.js.org/ (https://github.com/flatpickr/flatpickr) - https://simonwep.github.io/pickr/ (https://github.com/simonwep/pickr) - https://uaparser.dev/ (https://github.com/faisalman/ua-parser-js) + - https://www.npmjs.com/package/axios (https://github.com/axios/axios) --> @@ -1037,6 +1038,7 @@ access to use this app. +