[mirotalk] - #290 refactor: centralize environment config in config.template.js
This commit is contained in:
@@ -173,7 +173,9 @@ $ 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)
|
||||
# Setup configuration: copy the template and edit config.js to match your environment.
|
||||
# config.js is the central configuration file — it reads environment variables
|
||||
# and also contains branding, button visibility, and webhook settings.
|
||||
$ cp app/src/config.template.js app/src/config.js
|
||||
# install dependencies
|
||||
$ npm install
|
||||
@@ -202,7 +204,7 @@ $ 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)
|
||||
# Copy app/src/config.template.js in app/src/config.js (central configuration file)
|
||||
$ 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
|
||||
|
||||
+3
-2
@@ -5,8 +5,9 @@ const CryptoJS = require('crypto-js');
|
||||
|
||||
const { v4: uuidV4 } = require('uuid');
|
||||
|
||||
const JWT_KEY = process.env.JWT_KEY || 'mirotalk_jwt_secret';
|
||||
const JWT_EXP = process.env.JWT_EXP || '1h';
|
||||
const config = require('./config');
|
||||
const JWT_KEY = config.jwt.key;
|
||||
const JWT_EXP = config.jwt.exp;
|
||||
module.exports = class ServerApi {
|
||||
constructor(host = null, authorization = null, api_key_secret = null) {
|
||||
this._host = host;
|
||||
|
||||
@@ -5,13 +5,258 @@
|
||||
* MiroTalk P2P v.1.7.42 - Configuration File
|
||||
* ==============================================
|
||||
*
|
||||
* This file is the central configuration source.
|
||||
* All environment variables are read here so the
|
||||
* rest of the codebase imports config values
|
||||
* instead of reading process.env directly.
|
||||
*
|
||||
* Setup:
|
||||
* cp app/src/config.template.js app/src/config.js
|
||||
* Then edit config.js to match your environment.
|
||||
*
|
||||
* Docker/container environments inject values via
|
||||
* environment variables which are read at startup.
|
||||
*
|
||||
* Branding and customizations require a license:
|
||||
* https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const packageJson = require('../../package.json');
|
||||
|
||||
// Helper: parse env string to boolean
|
||||
function getEnvBoolean(key, force_true_if_undefined = false) {
|
||||
if (key == undefined && force_true_if_undefined) return true;
|
||||
return key == 'true' ? true : false;
|
||||
}
|
||||
|
||||
// Helper: safely parse JSON env vars with a fallback
|
||||
function parseJsonEnv(envValue, fallback) {
|
||||
if (!envValue) return fallback;
|
||||
try {
|
||||
return JSON.parse(envValue);
|
||||
} catch (e) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
|
||||
module.exports = {
|
||||
// ==========================================
|
||||
// Server
|
||||
// ==========================================
|
||||
server: {
|
||||
port: port,
|
||||
host: process.env.HOST || `http://localhost:${port}`,
|
||||
environment: process.env.NODE_ENV || 'development',
|
||||
trustProxy: !!getEnvBoolean(process.env.TRUST_PROXY),
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// CORS
|
||||
// ==========================================
|
||||
cors: {
|
||||
origin: parseJsonEnv(process.env.CORS_ORIGIN, '*'),
|
||||
methods: parseJsonEnv(process.env.CORS_METHODS, ['GET', 'POST']),
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Host Protection
|
||||
// ==========================================
|
||||
host: {
|
||||
protected: getEnvBoolean(process.env.HOST_PROTECTED),
|
||||
userAuth: getEnvBoolean(process.env.HOST_USER_AUTH),
|
||||
users: parseJsonEnv(process.env.HOST_USERS, [{ username: 'MiroTalk', password: 'P2P' }]),
|
||||
maxLoginAttempts: process.env.HOST_MAX_LOGIN_ATTEMPTS || 5,
|
||||
minLoginBlockTime: process.env.HOST_MIN_LOGIN_BLOCK_TIME || 15, // in minutes
|
||||
maxRoomParticipants: parseInt(process.env.ROOM_MAX_PARTICIPANTS) || 1000,
|
||||
showActiveRooms: getEnvBoolean(process.env.SHOW_ACTIVE_ROOMS) || false,
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// JWT
|
||||
// ==========================================
|
||||
jwt: {
|
||||
key: process.env.JWT_KEY || 'mirotalk_jwt_secret',
|
||||
exp: process.env.JWT_EXP || '1h',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Presenters
|
||||
// ==========================================
|
||||
presenters: parseJsonEnv(process.env.PRESENTERS, ['MiroTalk P2P']),
|
||||
|
||||
// ==========================================
|
||||
// API
|
||||
// ==========================================
|
||||
api: {
|
||||
keySecret: process.env.API_KEY_SECRET || 'mirotalkp2p_default_secret',
|
||||
disabled: parseJsonEnv(process.env.API_DISABLED, ['token', 'meetings']),
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Ngrok
|
||||
// ==========================================
|
||||
ngrok: {
|
||||
enabled: getEnvBoolean(process.env.NGROK_ENABLED),
|
||||
authToken: process.env.NGROK_AUTH_TOKEN,
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// WebRTC ICE Servers
|
||||
// ==========================================
|
||||
webrtc: {
|
||||
stun: {
|
||||
enabled: getEnvBoolean(process.env.STUN_SERVER_ENABLED),
|
||||
url: process.env.STUN_SERVER_URL,
|
||||
},
|
||||
turn: {
|
||||
enabled: getEnvBoolean(process.env.TURN_SERVER_ENABLED),
|
||||
url: process.env.TURN_SERVER_URL,
|
||||
username: process.env.TURN_SERVER_USERNAME,
|
||||
credential: process.env.TURN_SERVER_CREDENTIAL,
|
||||
},
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// IP Lookup
|
||||
// ==========================================
|
||||
ipLookup: {
|
||||
enabled: getEnvBoolean(process.env.IP_LOOKUP_ENABLED),
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Survey
|
||||
// ==========================================
|
||||
survey: {
|
||||
enabled: getEnvBoolean(process.env.SURVEY_ENABLED),
|
||||
url: process.env.SURVEY_URL || 'https://www.questionpro.com/t/AUs7VZq00L',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Redirect
|
||||
// ==========================================
|
||||
redirect: {
|
||||
enabled: getEnvBoolean(process.env.REDIRECT_ENABLED),
|
||||
url: process.env.REDIRECT_URL || '/newcall',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Sentry
|
||||
// ==========================================
|
||||
sentry: {
|
||||
enabled: getEnvBoolean(process.env.SENTRY_ENABLED),
|
||||
dsn: process.env.SENTRY_DSN,
|
||||
tracesSampleRate: parseFloat(process.env.SENTRY_TRACES_SAMPLE_RATE || '0.0'),
|
||||
logLevels: process.env.SENTRY_LOG_LEVELS
|
||||
? process.env.SENTRY_LOG_LEVELS.split(',').map((level) => level.trim())
|
||||
: ['error'],
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Slack
|
||||
// ==========================================
|
||||
slack: {
|
||||
enabled: getEnvBoolean(process.env.SLACK_ENABLED),
|
||||
signingSecret: process.env.SLACK_SIGNING_SECRET,
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// ChatGPT / OpenAI
|
||||
// ==========================================
|
||||
chatGPT: {
|
||||
enabled: getEnvBoolean(process.env.CHATGPT_ENABLED),
|
||||
basePath: process.env.CHATGPT_BASE_PATH,
|
||||
apiKey: process.env.CHATGPT_APIKEY,
|
||||
model: process.env.CHATGPT_MODEL,
|
||||
max_tokens: parseInt(process.env.CHATGPT_MAX_TOKENS),
|
||||
temperature: parseInt(process.env.CHATGPT_TEMPERATURE),
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// IP Whitelist
|
||||
// ==========================================
|
||||
ipWhitelist: {
|
||||
enabled: getEnvBoolean(process.env.IP_WHITELIST_ENABLED),
|
||||
allowed: parseJsonEnv(process.env.IP_WHITELIST_ALLOWED, []),
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// OIDC - OpenID Connect
|
||||
// ==========================================
|
||||
oidc: {
|
||||
enabled: process.env.OIDC_ENABLED ? getEnvBoolean(process.env.OIDC_ENABLED) : false,
|
||||
allowRoomCreationForAuthUsers: process.env.OIDC_ALLOW_ROOMS_CREATION_FOR_AUTH_USERS
|
||||
? getEnvBoolean(process.env.OIDC_ALLOW_ROOMS_CREATION_FOR_AUTH_USERS)
|
||||
: false,
|
||||
baseUrlDynamic: process.env.OIDC_BASE_URL_DYNAMIC
|
||||
? getEnvBoolean(process.env.OIDC_BASE_URL_DYNAMIC)
|
||||
: false,
|
||||
config: {
|
||||
issuerBaseURL: process.env.OIDC_ISSUER_BASE_URL,
|
||||
clientID: process.env.OIDC_CLIENT_ID,
|
||||
clientSecret: process.env.OIDC_CLIENT_SECRET,
|
||||
baseURL: process.env.OIDC_BASE_URL,
|
||||
secret: process.env.SESSION_SECRET,
|
||||
authorizationParams: {
|
||||
response_type: 'code',
|
||||
scope: 'openid profile email',
|
||||
},
|
||||
authRequired: process.env.OIDC_AUTH_REQUIRED
|
||||
? getEnvBoolean(process.env.OIDC_AUTH_REQUIRED)
|
||||
: false,
|
||||
auth0Logout: process.env.OIDC_AUTH_LOGOUT
|
||||
? getEnvBoolean(process.env.OIDC_AUTH_LOGOUT)
|
||||
: true,
|
||||
routes: {
|
||||
callback: '/auth/callback',
|
||||
login: false,
|
||||
logout: '/logout',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Mattermost
|
||||
// ==========================================
|
||||
mattermost: {
|
||||
enabled: getEnvBoolean(process.env.MATTERMOST_ENABLED),
|
||||
serverUrl: process.env.MATTERMOST_SERVER_URL,
|
||||
username: process.env.MATTERMOST_USERNAME,
|
||||
password: process.env.MATTERMOST_PASSWORD,
|
||||
token: process.env.MATTERMOST_TOKEN,
|
||||
roomTokenExpire: process.env.MATTERMOST_ROOM_TOKEN_EXPIRE,
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Stats / Analytics
|
||||
// ==========================================
|
||||
stats: {
|
||||
enabled: process.env.STATS_ENABLED ? getEnvBoolean(process.env.STATS_ENABLED) : true,
|
||||
src: process.env.STATS_SCR || 'https://stats.mirotalk.com/script.js',
|
||||
id: process.env.STATS_ID || 'c7615aa7-ceec-464a-baba-54cb605d7261',
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Email
|
||||
// ==========================================
|
||||
email: {
|
||||
alert: process.env.EMAIL_ALERT === 'true' || false,
|
||||
host: process.env.EMAIL_HOST,
|
||||
port: Number(process.env.EMAIL_PORT),
|
||||
username: process.env.EMAIL_USERNAME,
|
||||
password: process.env.EMAIL_PASSWORD,
|
||||
from: process.env.EMAIL_FROM || process.env.EMAIL_USERNAME,
|
||||
sendTo: process.env.EMAIL_SEND_TO,
|
||||
https: process.env.HTTPS === 'true' || false,
|
||||
serverPort: process.env.PORT || 3000,
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// Branding (UI customizations)
|
||||
// ==========================================
|
||||
brand: {
|
||||
htmlInjection: true,
|
||||
app: {
|
||||
@@ -206,6 +451,9 @@ module.exports = {
|
||||
whiteboardLockBtn: false,
|
||||
},
|
||||
},
|
||||
// ==========================================
|
||||
// Webhook
|
||||
// ==========================================
|
||||
webhook: {
|
||||
enabled: false, // Enable webhook functionality
|
||||
url: 'http://localhost:8888/webhook-endpoint', // Webhook server URL
|
||||
|
||||
@@ -4,18 +4,20 @@ const nodemailer = require('nodemailer');
|
||||
const Logs = require('../logs');
|
||||
const log = new Logs('NodeMailer');
|
||||
|
||||
const config = require('../config');
|
||||
|
||||
// Email config
|
||||
const emailCfg = {
|
||||
alert: process.env.EMAIL_ALERT === 'true' || false,
|
||||
host: process.env.EMAIL_HOST,
|
||||
port: Number(process.env.EMAIL_PORT),
|
||||
username: process.env.EMAIL_USERNAME,
|
||||
password: process.env.EMAIL_PASSWORD,
|
||||
from: process.env.EMAIL_FROM || process.env.EMAIL_USERNAME,
|
||||
send_to: process.env.EMAIL_SEND_TO,
|
||||
alert: config.email.alert,
|
||||
host: config.email.host,
|
||||
port: config.email.port,
|
||||
username: config.email.username,
|
||||
password: config.email.password,
|
||||
from: config.email.from,
|
||||
send_to: config.email.sendTo,
|
||||
// Room join params
|
||||
https: process.env.HTTPS === 'true' || false,
|
||||
server_port: process.env.PORT || 3000,
|
||||
https: config.email.https,
|
||||
server_port: config.email.serverPort,
|
||||
};
|
||||
|
||||
const isTLSPort = emailCfg.port === 465; // 465 is the default TLS/SSL port
|
||||
|
||||
+59
-155
@@ -51,8 +51,6 @@ dependencies: {
|
||||
|
||||
'use strict'; // https://www.w3schools.com/js/js_strict.asp
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const { auth, requiresAuth } = require('express-openid-connect');
|
||||
const { Server } = require('socket.io');
|
||||
const httpolyglot = require('httpolyglot');
|
||||
@@ -74,8 +72,8 @@ const Host = require('./host');
|
||||
const Logs = require('./logs');
|
||||
const log = new Logs('server');
|
||||
|
||||
// Custom Brand and buttons
|
||||
const config = safeRequire('./config');
|
||||
// Central configuration (reads .env via dotenv internally)
|
||||
const config = require('./config');
|
||||
|
||||
// Email alerts and notifications
|
||||
const nodemailer = require('./lib/nodemailer');
|
||||
@@ -84,8 +82,8 @@ const packageJson = require('../../package.json');
|
||||
|
||||
// Login attempts limit
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const maxAttempts = process.env.HOST_MAX_LOGIN_ATTEMPTS || 5;
|
||||
const minBlockTime = process.env.HOST_MIN_LOGIN_BLOCK_TIME || 15; // in minutes
|
||||
const maxAttempts = config.host.maxLoginAttempts;
|
||||
const minBlockTime = config.host.minLoginBlockTime; // in minutes
|
||||
const loginLimiter = rateLimit({
|
||||
windowMs: minBlockTime * 60 * 1000, // 15 minutes default
|
||||
max: maxAttempts,
|
||||
@@ -93,8 +91,8 @@ const loginLimiter = rateLimit({
|
||||
keyGenerator: (req) => req.body?.username || getIP(req),
|
||||
});
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
const host = process.env.HOST || `http://localhost:${port}`;
|
||||
const port = config.server.port;
|
||||
const host = config.server.host;
|
||||
|
||||
const authHost = new Host(); // Authenticated IP by Login
|
||||
|
||||
@@ -122,34 +120,12 @@ server.on('clientError', (err, socket) => {
|
||||
});
|
||||
|
||||
// Trust Proxy
|
||||
const trustProxy = !!getEnvBoolean(process.env.TRUST_PROXY);
|
||||
const trustProxy = config.server.trustProxy;
|
||||
|
||||
// Cors
|
||||
const cors_origin = process.env.CORS_ORIGIN;
|
||||
const cors_methods = process.env.CORS_METHODS;
|
||||
|
||||
let corsOrigin = '*';
|
||||
let corsMethods = ['GET', 'POST'];
|
||||
|
||||
if (cors_origin && cors_origin !== '*') {
|
||||
try {
|
||||
corsOrigin = JSON.parse(cors_origin);
|
||||
} catch (error) {
|
||||
log.error('Error parsing CORS_ORIGIN', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (cors_methods && cors_methods !== '') {
|
||||
try {
|
||||
corsMethods = JSON.parse(cors_methods);
|
||||
} catch (error) {
|
||||
log.error('Error parsing CORS_METHODS', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
const corsOptions = {
|
||||
origin: corsOrigin,
|
||||
methods: corsMethods,
|
||||
origin: config.cors.origin,
|
||||
methods: config.cors.methods,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -164,28 +140,23 @@ const io = new Server({
|
||||
// console.log(io);
|
||||
|
||||
// Host protection (disabled by default)
|
||||
const hostProtected = getEnvBoolean(process.env.HOST_PROTECTED);
|
||||
const userAuth = getEnvBoolean(process.env.HOST_USER_AUTH);
|
||||
const hostUsersString = process.env.HOST_USERS || '[{"username": "MiroTalk", "password": "P2P"}]';
|
||||
const hostUsers = JSON.parse(hostUsersString);
|
||||
const hostCfg = {
|
||||
protected: hostProtected,
|
||||
user_auth: userAuth,
|
||||
users: hostUsers,
|
||||
authenticated: !hostProtected,
|
||||
maxRoomParticipants: parseInt(process.env.ROOM_MAX_PARTICIPANTS) || 1000,
|
||||
showActiveRooms: getEnvBoolean(process.env.SHOW_ACTIVE_ROOMS) || false,
|
||||
protected: config.host.protected,
|
||||
user_auth: config.host.userAuth,
|
||||
users: config.host.users,
|
||||
authenticated: !config.host.protected,
|
||||
maxRoomParticipants: config.host.maxRoomParticipants,
|
||||
showActiveRooms: config.host.showActiveRooms,
|
||||
};
|
||||
|
||||
// JWT config
|
||||
const jwtCfg = {
|
||||
JWT_KEY: process.env.JWT_KEY || 'mirotalk_jwt_secret',
|
||||
JWT_EXP: process.env.JWT_EXP || '1h',
|
||||
JWT_KEY: config.jwt.key,
|
||||
JWT_EXP: config.jwt.exp,
|
||||
};
|
||||
|
||||
// Room presenters
|
||||
const roomPresentersString = process.env.PRESENTERS || '["MiroTalk P2P"]';
|
||||
const roomPresenters = JSON.parse(roomPresentersString);
|
||||
const roomPresenters = config.presenters;
|
||||
|
||||
// Swagger config
|
||||
const yaml = require('js-yaml');
|
||||
@@ -196,30 +167,29 @@ const swaggerDocument = yaml.load(fs.readFileSync(path.join(__dirname, '/../api/
|
||||
const { v4: uuidV4 } = require('uuid');
|
||||
const apiBasePath = '/api/v1'; // api endpoint path
|
||||
const api_docs = host + apiBasePath + '/docs'; // api docs
|
||||
const api_key_secret = process.env.API_KEY_SECRET || 'mirotalkp2p_default_secret';
|
||||
const apiDisabledString = process.env.API_DISABLED || '["token", "meetings"]';
|
||||
const api_disabled = JSON.parse(apiDisabledString);
|
||||
const api_key_secret = config.api.keySecret;
|
||||
const api_disabled = config.api.disabled;
|
||||
|
||||
// Ngrok config
|
||||
const ngrok = require('@ngrok/ngrok');
|
||||
const ngrokEnabled = getEnvBoolean(process.env.NGROK_ENABLED);
|
||||
const ngrokAuthToken = process.env.NGROK_AUTH_TOKEN;
|
||||
const ngrokEnabled = config.ngrok.enabled;
|
||||
const ngrokAuthToken = config.ngrok.authToken;
|
||||
|
||||
// Handle WebHook
|
||||
const webhook = {
|
||||
enabled: config?.webhook?.enabled || false,
|
||||
url: config?.webhook?.url || 'http://localhost:8888/webhook-endpoint',
|
||||
enabled: config.webhook?.enabled || false,
|
||||
url: config.webhook?.url || 'http://localhost:8888/webhook-endpoint',
|
||||
};
|
||||
|
||||
// Stun (https://bloggeek.me/webrtcglossary/stun/)
|
||||
// Turn (https://bloggeek.me/webrtcglossary/turn/)
|
||||
const iceServers = [];
|
||||
const stunServerUrl = process.env.STUN_SERVER_URL;
|
||||
const turnServerUrl = process.env.TURN_SERVER_URL;
|
||||
const turnServerUsername = process.env.TURN_SERVER_USERNAME;
|
||||
const turnServerCredential = process.env.TURN_SERVER_CREDENTIAL;
|
||||
const stunServerEnabled = getEnvBoolean(process.env.STUN_SERVER_ENABLED);
|
||||
const turnServerEnabled = getEnvBoolean(process.env.TURN_SERVER_ENABLED);
|
||||
const stunServerUrl = config.webrtc.stun.url;
|
||||
const turnServerUrl = config.webrtc.turn.url;
|
||||
const turnServerUsername = config.webrtc.turn.username;
|
||||
const turnServerCredential = config.webrtc.turn.credential;
|
||||
const stunServerEnabled = config.webrtc.stun.enabled;
|
||||
const turnServerEnabled = config.webrtc.turn.enabled;
|
||||
// Stun is mandatory for not internal network
|
||||
if (stunServerEnabled && stunServerUrl) iceServers.push({ urls: stunServerUrl });
|
||||
// Turn is recommended if direct peer to peer connection is not possible
|
||||
@@ -232,27 +202,27 @@ if (turnServerEnabled && turnServerUrl && turnServerUsername && turnServerCreden
|
||||
const testStunTurn = host + '/icetest';
|
||||
|
||||
// IP Lookup
|
||||
const IPLookupEnabled = getEnvBoolean(process.env.IP_LOOKUP_ENABLED);
|
||||
const IPLookupEnabled = config.ipLookup.enabled;
|
||||
|
||||
// Survey URL
|
||||
const surveyEnabled = getEnvBoolean(process.env.SURVEY_ENABLED);
|
||||
const surveyURL = process.env.SURVEY_URL || 'https://www.questionpro.com/t/AUs7VZq00L';
|
||||
const surveyEnabled = config.survey.enabled;
|
||||
const surveyURL = config.survey.url;
|
||||
|
||||
// Redirect URL
|
||||
const redirectEnabled = getEnvBoolean(process.env.REDIRECT_ENABLED);
|
||||
const redirectURL = process.env.REDIRECT_URL || '/newcall';
|
||||
const redirectEnabled = config.redirect.enabled;
|
||||
const redirectURL = config.redirect.url;
|
||||
|
||||
// Sentry config
|
||||
const Sentry = require('@sentry/node');
|
||||
const sentryEnabled = getEnvBoolean(process.env.SENTRY_ENABLED);
|
||||
const sentryDSN = process.env.SENTRY_DSN;
|
||||
const sentryTracesSampleRate = parseFloat(process.env.SENTRY_TRACES_SAMPLE_RATE || '0.0');
|
||||
const sentryEnabled = config.sentry.enabled;
|
||||
const sentryDSN = config.sentry.dsn;
|
||||
const sentryTracesSampleRate = config.sentry.tracesSampleRate;
|
||||
|
||||
// Slack API
|
||||
const CryptoJS = require('crypto-js');
|
||||
const qS = require('qs');
|
||||
const slackEnabled = getEnvBoolean(process.env.SLACK_ENABLED);
|
||||
const slackSigningSecret = process.env.SLACK_SIGNING_SECRET;
|
||||
const slackEnabled = config.slack.enabled;
|
||||
const slackSigningSecret = config.slack.signingSecret;
|
||||
|
||||
// Setup sentry client
|
||||
if (sentryEnabled && typeof sentryDSN === 'string' && sentryDSN.trim()) {
|
||||
@@ -263,9 +233,7 @@ if (sentryEnabled && typeof sentryDSN === 'string' && sentryDSN.trim()) {
|
||||
tracesSampleRate: sentryTracesSampleRate,
|
||||
});
|
||||
|
||||
const logLevels = process.env.SENTRY_LOG_LEVELS
|
||||
? process.env.SENTRY_LOG_LEVELS.split(',').map((level) => level.trim())
|
||||
: ['error'];
|
||||
const logLevels = config.sentry.logLevels;
|
||||
|
||||
const stripAnsi = (str) => (typeof str === 'string' ? str.replace(/\u001b\[[0-9;]*m/g, '') : str);
|
||||
|
||||
@@ -294,14 +262,7 @@ if (sentryEnabled && typeof sentryDSN === 'string' && sentryDSN.trim()) {
|
||||
|
||||
// OpenAI/ChatGPT
|
||||
let chatGPT;
|
||||
const configChatGPT = {
|
||||
enabled: getEnvBoolean(process.env.CHATGPT_ENABLED),
|
||||
basePath: process.env.CHATGPT_BASE_PATH,
|
||||
apiKey: process.env.CHATGPT_APIKEY,
|
||||
model: process.env.CHATGPT_MODEL,
|
||||
max_tokens: parseInt(process.env.CHATGPT_MAX_TOKENS),
|
||||
temperature: parseInt(process.env.CHATGPT_TEMPERATURE),
|
||||
};
|
||||
const configChatGPT = config.chatGPT;
|
||||
if (configChatGPT.enabled) {
|
||||
if (configChatGPT.apiKey) {
|
||||
const { OpenAI } = require('openai');
|
||||
@@ -316,37 +277,10 @@ if (configChatGPT.enabled) {
|
||||
}
|
||||
|
||||
// IP Whitelist
|
||||
const ipWhitelist = {
|
||||
enabled: getEnvBoolean(process.env.IP_WHITELIST_ENABLED),
|
||||
allowed: process.env.IP_WHITELIST_ALLOWED ? JSON.parse(process.env.IP_WHITELIST_ALLOWED) : [],
|
||||
};
|
||||
const ipWhitelist = config.ipWhitelist;
|
||||
|
||||
// OIDC - Open ID Connect
|
||||
const OIDC = {
|
||||
enabled: process.env.OIDC_ENABLED ? getEnvBoolean(process.env.OIDC_ENABLED) : false,
|
||||
allowRoomCreationForAuthUsers: process.env.OIDC_ALLOW_ROOMS_CREATION_FOR_AUTH_USERS
|
||||
? getEnvBoolean(process.env.OIDC_ALLOW_ROOMS_CREATION_FOR_AUTH_USERS)
|
||||
: false,
|
||||
baseUrlDynamic: process.env.OIDC_BASE_URL_DYNAMIC ? getEnvBoolean(process.env.OIDC_BASE_URL_DYNAMIC) : false,
|
||||
config: {
|
||||
issuerBaseURL: process.env.OIDC_ISSUER_BASE_URL,
|
||||
clientID: process.env.OIDC_CLIENT_ID,
|
||||
clientSecret: process.env.OIDC_CLIENT_SECRET,
|
||||
baseURL: process.env.OIDC_BASE_URL,
|
||||
secret: process.env.SESSION_SECRET,
|
||||
authorizationParams: {
|
||||
response_type: 'code',
|
||||
scope: 'openid profile email',
|
||||
},
|
||||
authRequired: process.env.OIDC_AUTH_REQUIRED ? getEnvBoolean(process.env.OIDC_AUTH_REQUIRED) : false, // Set to true if authentication is required for all routes
|
||||
auth0Logout: process.env.OIDC_AUTH_LOGOUT ? getEnvBoolean(process.env.OIDC_AUTH_LOGOUT) : true, // Set to true to enable logout with Auth0
|
||||
routes: {
|
||||
callback: '/auth/callback', // Indicating the endpoint where your application will handle the callback from the authentication provider after a user has been authenticated.
|
||||
login: false, // Dedicated route in your application for user login.
|
||||
logout: '/logout', // Indicating the endpoint where your application will handle user logout requests.
|
||||
},
|
||||
},
|
||||
};
|
||||
const OIDC = config.oidc;
|
||||
|
||||
// Custom middleware function for OIDC authentication
|
||||
function OIDCAuth(req, res, next) {
|
||||
@@ -390,23 +324,19 @@ function OIDCAuth(req, res, next) {
|
||||
|
||||
// Mattermost config
|
||||
const mattermostCfg = {
|
||||
enabled: getEnvBoolean(process.env.MATTERMOST_ENABLED),
|
||||
server_url: process.env.MATTERMOST_SERVER_URL,
|
||||
username: process.env.MATTERMOST_USERNAME,
|
||||
password: process.env.MATTERMOST_PASSWORD,
|
||||
token: process.env.MATTERMOST_TOKEN,
|
||||
roomTokenExpire: process.env.MATTERMOST_ROOM_TOKEN_EXPIRE,
|
||||
encryptionKey: process.env.JWT_KEY,
|
||||
enabled: config.mattermost.enabled,
|
||||
server_url: config.mattermost.serverUrl,
|
||||
username: config.mattermost.username,
|
||||
password: config.mattermost.password,
|
||||
token: config.mattermost.token,
|
||||
roomTokenExpire: config.mattermost.roomTokenExpire,
|
||||
encryptionKey: config.jwt.key,
|
||||
security: hostCfg.protected || OIDC.enabled,
|
||||
api_disabled: api_disabled,
|
||||
};
|
||||
|
||||
// stats configuration
|
||||
const statsData = {
|
||||
enabled: process.env.STATS_ENABLED ? getEnvBoolean(process.env.STATS_ENABLED) : true,
|
||||
src: process.env.STATS_SCR || 'https://stats.mirotalk.com/script.js',
|
||||
id: process.env.STATS_ID || 'c7615aa7-ceec-464a-baba-54cb605d7261',
|
||||
};
|
||||
const statsData = config.stats;
|
||||
|
||||
// directory
|
||||
const dir = {
|
||||
@@ -427,11 +357,11 @@ const views = {
|
||||
};
|
||||
|
||||
// Branding configuration
|
||||
const brandHtmlInjection = config?.brand?.htmlInjection ?? true;
|
||||
const brandHtmlInjection = config.brand?.htmlInjection ?? true;
|
||||
|
||||
// File to cache and inject custom HTML data like OG tags and any other elements.
|
||||
const filesPath = [views.landing, views.newCall, views.client, views.login, views.activeRooms, views.customizeRoom];
|
||||
const htmlInjector = new HtmlInjector(filesPath, config?.brand || null);
|
||||
const htmlInjector = new HtmlInjector(filesPath, config.brand || null);
|
||||
|
||||
const channels = {}; // collect channels
|
||||
const sockets = {}; // collect sockets
|
||||
@@ -644,7 +574,7 @@ app.post('/isRoomActive', (req, res) => {
|
||||
app.post('/isWidgetRoomActive', (req, res) => {
|
||||
const { roomId } = checkXSS(req.body);
|
||||
const roomWidgetActive =
|
||||
roomId && roomId === config?.brand?.widget?.roomId && Object.prototype.hasOwnProperty.call(peers, roomId);
|
||||
roomId && roomId === config.brand?.widget?.roomId && Object.prototype.hasOwnProperty.call(peers, roomId);
|
||||
log.debug('isWidgetRoomActive', { roomId, roomWidgetActive });
|
||||
res.status(200).json({ message: roomWidgetActive });
|
||||
});
|
||||
@@ -829,12 +759,12 @@ app.post('/login', loginLimiter, (req, res) => {
|
||||
|
||||
// UI buttons configuration
|
||||
app.get('/buttons', (req, res) => {
|
||||
res.status(200).json({ message: config && config.buttons ? config.buttons : false });
|
||||
res.status(200).json({ message: config.buttons ? config.buttons : false });
|
||||
});
|
||||
|
||||
// UI brand configuration
|
||||
app.get('/brand', (req, res) => {
|
||||
res.status(200).json({ message: config && config.brand && brandHtmlInjection ? config.brand : false });
|
||||
res.status(200).json({ message: config.brand && brandHtmlInjection ? config.brand : false });
|
||||
});
|
||||
|
||||
// Join roomId redirect to /join?room=roomId
|
||||
@@ -1154,10 +1084,10 @@ function getServerConfig(tunnel = false) {
|
||||
redirect: redirectEnabled ? redirectURL : false,
|
||||
|
||||
// Widget Configuration
|
||||
widget: config?.brand?.widget?.enabled ? config.brand.widget : false,
|
||||
widget: config.brand?.widget?.enabled ? config.brand.widget : false,
|
||||
|
||||
// Versions and environment information
|
||||
environment: process.env.NODE_ENV || 'development',
|
||||
environment: config.server.environment,
|
||||
app_version: packageJson.version,
|
||||
node_version: process.versions.node,
|
||||
};
|
||||
@@ -2133,17 +2063,6 @@ io.sockets.on('connect', async (socket) => {
|
||||
}
|
||||
}); // end [sockets.on-connect]
|
||||
|
||||
/**
|
||||
* Get Env as boolean
|
||||
* @param {string} key
|
||||
* @param {boolean} force_true_if_undefined
|
||||
* @returns boolean
|
||||
*/
|
||||
function getEnvBoolean(key, force_true_if_undefined = false) {
|
||||
if (key == undefined && force_true_if_undefined) return true;
|
||||
return key == 'true' ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if valid filename
|
||||
* @param {string} fileName
|
||||
@@ -2427,21 +2346,6 @@ function removeIP(socket) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load modules if exists
|
||||
* @param {string} filePath
|
||||
* @returns
|
||||
*/
|
||||
function safeRequire(filePath) {
|
||||
let data = null;
|
||||
try {
|
||||
data = require(filePath);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup HTML injector when the application is shutting down
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"description": "A free WebRTC browser-based video call",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"prestart": "test -f app/src/config.js || cp app/src/config.template.js app/src/config.js",
|
||||
"start": "node app/src/server.js",
|
||||
"start-dev": "nodemon app/src/server.js",
|
||||
"test": "mocha tests/*.js",
|
||||
|
||||
+3
-2
@@ -9,6 +9,7 @@ const proxyquire = require('proxyquire');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const CryptoJS = require('crypto-js');
|
||||
const ServerApi = require('../app/src/api');
|
||||
const config = require('../app/src/config');
|
||||
|
||||
describe('test-api', () => {
|
||||
let serverApi;
|
||||
@@ -184,12 +185,12 @@ describe('test-api', () => {
|
||||
result.should.equal('jwtToken');
|
||||
|
||||
signStub
|
||||
.calledWith({ data: 'encryptedPayload' }, 'mirotalk_jwt_secret', { expiresIn: '1h' })
|
||||
.calledWith({ data: 'encryptedPayload' }, config.jwt.key, { expiresIn: '1h' })
|
||||
.should.be.true();
|
||||
encryptStub
|
||||
.calledWith(
|
||||
JSON.stringify({ username: 'user', password: 'pass', presenter: 'true' }),
|
||||
'mirotalk_jwt_secret'
|
||||
config.jwt.key
|
||||
)
|
||||
.should.be.true();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user