2016-10-22 00:27:47 +08:00
|
|
|
import { Meteor } from 'meteor/meteor';
|
2019-03-29 23:47:07 +08:00
|
|
|
import { WebAppInternals } from 'meteor/webapp';
|
2017-12-21 00:10:01 +08:00
|
|
|
import Langmap from 'langmap';
|
2018-01-10 20:43:45 +08:00
|
|
|
import fs from 'fs';
|
2019-07-02 03:00:27 +08:00
|
|
|
import Users from '/imports/api/users';
|
2018-09-25 20:37:45 +08:00
|
|
|
import './settings';
|
2019-05-02 08:33:03 +08:00
|
|
|
import { lookup as lookupUserAgent } from 'useragent';
|
2019-09-10 02:45:54 +08:00
|
|
|
import { check } from 'meteor/check';
|
2016-10-18 20:03:51 +08:00
|
|
|
import Logger from './logger';
|
|
|
|
import Redis from './redis';
|
2019-05-02 08:33:03 +08:00
|
|
|
import setMinBrowserVersions from './minBrowserVersion';
|
2019-07-02 03:00:27 +08:00
|
|
|
import userLeaving from '/imports/api/users/server/methods/userLeaving';
|
2016-08-17 23:48:03 +08:00
|
|
|
|
2018-05-30 20:23:05 +08:00
|
|
|
const AVAILABLE_LOCALES = fs.readdirSync('assets/app/locales');
|
|
|
|
|
2016-10-18 20:03:51 +08:00
|
|
|
Meteor.startup(() => {
|
2016-08-17 23:48:03 +08:00
|
|
|
const APP_CONFIG = Meteor.settings.public.app;
|
2019-07-09 03:49:09 +08:00
|
|
|
const INTERVAL_IN_SETTINGS = (Meteor.settings.public.pingPong.clearUsersInSeconds) * 1000;
|
|
|
|
const INTERVAL_TIME = INTERVAL_IN_SETTINGS < 10000 ? 10000 : INTERVAL_IN_SETTINGS;
|
2018-03-22 03:16:07 +08:00
|
|
|
const env = Meteor.isDevelopment ? 'development' : 'production';
|
2019-03-29 23:47:07 +08:00
|
|
|
const CDN_URL = APP_CONFIG.cdn;
|
2019-12-04 04:26:45 +08:00
|
|
|
|
|
|
|
// Commenting out in BBB 2.3 as node12 does not allow for `memwatch`.
|
|
|
|
// We are looking for alternatives
|
|
|
|
|
|
|
|
/* let heapDumpMbThreshold = 100;
|
2019-03-18 16:57:43 +08:00
|
|
|
|
2019-09-10 02:45:54 +08:00
|
|
|
const memoryMonitoringSettings = Meteor.settings.private.memoryMonitoring;
|
|
|
|
if (memoryMonitoringSettings.stat.enabled) {
|
|
|
|
memwatch.on('stats', (stats) => {
|
2019-09-25 05:50:37 +08:00
|
|
|
let heapDumpTriggered = false;
|
|
|
|
|
2019-10-09 01:40:53 +08:00
|
|
|
if (memoryMonitoringSettings.heapdump.enabled) {
|
2019-09-25 05:50:37 +08:00
|
|
|
heapDumpTriggered = (stats.current_base / 1048576) > heapDumpMbThreshold;
|
|
|
|
}
|
|
|
|
Logger.info('memwatch stats', { ...stats, heapDumpEnabled: memoryMonitoringSettings.heapdump.enabled, heapDumpTriggered });
|
|
|
|
|
|
|
|
if (heapDumpTriggered) {
|
|
|
|
heapdump.writeSnapshot(`./heapdump-stats-${Date.now()}.heapsnapshot`);
|
|
|
|
heapDumpMbThreshold += 100;
|
|
|
|
}
|
2019-09-10 02:45:54 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (memoryMonitoringSettings.leak.enabled) {
|
|
|
|
memwatch.on('leak', (info) => {
|
2019-09-10 03:21:27 +08:00
|
|
|
Logger.info('memwatch leak', info);
|
2019-09-10 02:45:54 +08:00
|
|
|
});
|
2019-12-04 04:26:45 +08:00
|
|
|
} */
|
2019-09-10 02:45:54 +08:00
|
|
|
|
2019-03-30 01:33:23 +08:00
|
|
|
if (CDN_URL.trim()) {
|
2019-03-29 23:47:07 +08:00
|
|
|
// Add CDN
|
|
|
|
BrowserPolicy.content.disallowEval();
|
|
|
|
BrowserPolicy.content.allowInlineScripts();
|
|
|
|
BrowserPolicy.content.allowInlineStyles();
|
|
|
|
BrowserPolicy.content.allowImageDataUrl(CDN_URL);
|
|
|
|
BrowserPolicy.content.allowFontDataUrl(CDN_URL);
|
|
|
|
BrowserPolicy.content.allowOriginForAll(CDN_URL);
|
|
|
|
WebAppInternals.setBundledJsCssPrefix(CDN_URL + APP_CONFIG.basename);
|
|
|
|
|
2019-05-02 08:33:03 +08:00
|
|
|
const fontRegExp = /\.(eot|ttf|otf|woff|woff2)$/;
|
2019-03-29 23:47:07 +08:00
|
|
|
|
2019-05-02 08:33:03 +08:00
|
|
|
WebApp.rawConnectHandlers.use('/', (req, res, next) => {
|
2019-03-29 23:47:07 +08:00
|
|
|
if (fontRegExp.test(req._parsedUrl.pathname)) {
|
|
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
|
|
res.setHeader('Vary', 'Origin');
|
|
|
|
res.setHeader('Pragma', 'public');
|
|
|
|
res.setHeader('Cache-Control', '"public"');
|
|
|
|
}
|
|
|
|
return next();
|
|
|
|
});
|
|
|
|
}
|
2019-03-18 16:57:43 +08:00
|
|
|
|
2019-05-02 08:33:03 +08:00
|
|
|
setMinBrowserVersions();
|
|
|
|
|
2019-07-02 03:00:27 +08:00
|
|
|
Meteor.setInterval(() => {
|
|
|
|
const currentTime = Date.now();
|
|
|
|
Logger.info('Checking for inactive users');
|
|
|
|
const users = Users.find({
|
|
|
|
connectionStatus: 'online',
|
2019-07-09 02:51:34 +08:00
|
|
|
clientType: 'HTML5',
|
2019-07-02 03:00:27 +08:00
|
|
|
lastPing: {
|
|
|
|
$lt: (currentTime - INTERVAL_TIME), // get user who has not pinged in the last 10 seconds
|
|
|
|
},
|
2019-07-09 02:51:34 +08:00
|
|
|
loginTime: {
|
|
|
|
$lt: (currentTime - INTERVAL_TIME),
|
|
|
|
},
|
2019-07-02 03:00:27 +08:00
|
|
|
}).fetch();
|
|
|
|
if (!users.length) return Logger.info('No inactive users');
|
|
|
|
Logger.info('Removing inactive users');
|
|
|
|
users.forEach((user) => {
|
2019-07-09 02:51:34 +08:00
|
|
|
Logger.info(`Detected inactive user, userId:${user.userId}, meetingId:${user.meetingId}`);
|
2020-02-14 03:19:29 +08:00
|
|
|
return userLeaving(user.meetingId, user.userId, user.connectionId);
|
2019-07-02 03:00:27 +08:00
|
|
|
});
|
2020-02-14 03:19:29 +08:00
|
|
|
return Logger.info('All inactive users have been removed');
|
2019-07-02 03:00:27 +08:00
|
|
|
}, INTERVAL_TIME);
|
|
|
|
|
2019-03-18 16:57:43 +08:00
|
|
|
Logger.warn(`SERVER STARTED.\nENV=${env},\nnodejs version=${process.version}\nCDN=${CDN_URL}\n`, APP_CONFIG);
|
2016-05-05 04:25:34 +08:00
|
|
|
});
|
2016-05-05 01:00:57 +08:00
|
|
|
|
2017-10-06 20:50:01 +08:00
|
|
|
WebApp.connectHandlers.use('/check', (req, res) => {
|
2017-06-03 03:25:02 +08:00
|
|
|
const payload = { html5clientStatus: 'running' };
|
2016-06-14 01:56:41 +08:00
|
|
|
|
|
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
res.writeHead(200);
|
|
|
|
res.end(JSON.stringify(payload));
|
|
|
|
});
|
|
|
|
|
2016-10-08 00:36:27 +08:00
|
|
|
WebApp.connectHandlers.use('/locale', (req, res) => {
|
2017-03-10 03:50:21 +08:00
|
|
|
const APP_CONFIG = Meteor.settings.public.app;
|
2018-05-23 03:57:43 +08:00
|
|
|
const fallback = APP_CONFIG.defaultSettings.application.fallbackLocale;
|
2019-07-10 21:19:00 +08:00
|
|
|
const override = APP_CONFIG.defaultSettings.application.overrideLocale;
|
2020-03-11 21:55:41 +08:00
|
|
|
const browserLocale = override && req.query.init === 'true'
|
|
|
|
? override.split(/[-_]/g) : req.query.locale.split(/[-_]/g);
|
|
|
|
|
2018-05-28 19:35:24 +08:00
|
|
|
const localeList = [fallback];
|
|
|
|
|
2018-05-30 20:39:02 +08:00
|
|
|
const usableLocales = AVAILABLE_LOCALES
|
2018-05-28 19:35:24 +08:00
|
|
|
.map(file => file.replace('.json', ''))
|
2019-03-09 00:57:40 +08:00
|
|
|
.reduce((locales, locale) => (locale.match(browserLocale[0])
|
|
|
|
? [...locales, locale]
|
|
|
|
: locales), []);
|
2018-05-30 20:39:02 +08:00
|
|
|
|
|
|
|
const regionDefault = usableLocales.find(locale => browserLocale[0] === locale);
|
2018-05-28 19:35:24 +08:00
|
|
|
|
|
|
|
if (regionDefault) localeList.push(regionDefault);
|
2018-05-30 21:09:33 +08:00
|
|
|
if (!regionDefault && usableLocales.length) localeList.push(usableLocales[0]);
|
2018-05-28 19:35:24 +08:00
|
|
|
|
|
|
|
let normalizedLocale;
|
2017-03-10 03:50:21 +08:00
|
|
|
let messages = {};
|
2017-10-27 19:36:27 +08:00
|
|
|
|
2018-05-28 19:35:24 +08:00
|
|
|
if (browserLocale.length > 1) {
|
|
|
|
normalizedLocale = `${browserLocale[0]}_${browserLocale[1].toUpperCase()}`;
|
2017-10-27 19:36:27 +08:00
|
|
|
localeList.push(normalizedLocale);
|
2017-03-10 03:50:21 +08:00
|
|
|
}
|
2018-05-28 19:35:24 +08:00
|
|
|
|
2017-10-27 19:36:27 +08:00
|
|
|
localeList.forEach((locale) => {
|
2017-03-10 03:50:21 +08:00
|
|
|
try {
|
|
|
|
const data = Assets.getText(`locales/${locale}.json`);
|
|
|
|
messages = Object.assign(messages, JSON.parse(data));
|
2017-12-21 00:10:01 +08:00
|
|
|
normalizedLocale = locale;
|
2017-03-10 03:50:21 +08:00
|
|
|
} catch (e) {
|
2019-03-09 00:57:40 +08:00
|
|
|
Logger.warn(`'Could not process locale ${locale}:${e}`);
|
|
|
|
// Getting here means the locale is not available in the current locale files.
|
2017-03-10 03:50:21 +08:00
|
|
|
}
|
|
|
|
});
|
2016-10-08 00:36:27 +08:00
|
|
|
|
|
|
|
res.setHeader('Content-Type', 'application/json');
|
2017-10-27 19:36:27 +08:00
|
|
|
res.end(JSON.stringify({ normalizedLocale, messages }));
|
2016-10-08 00:36:27 +08:00
|
|
|
});
|
|
|
|
|
2017-04-06 20:36:59 +08:00
|
|
|
WebApp.connectHandlers.use('/locales', (req, res) => {
|
2018-05-30 20:23:05 +08:00
|
|
|
let locales = [];
|
2017-12-21 00:10:01 +08:00
|
|
|
try {
|
2018-05-30 20:23:05 +08:00
|
|
|
locales = AVAILABLE_LOCALES
|
2017-12-21 00:10:01 +08:00
|
|
|
.map(file => file.replace('.json', ''))
|
|
|
|
.map(file => file.replace('_', '-'))
|
|
|
|
.map(locale => ({
|
|
|
|
locale,
|
|
|
|
name: Langmap[locale].nativeName,
|
|
|
|
}));
|
|
|
|
} catch (e) {
|
2019-03-09 00:57:40 +08:00
|
|
|
Logger.warn(`'Could not process locales error: ${e}`);
|
2017-04-06 20:36:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
res.writeHead(200);
|
2018-05-30 20:23:05 +08:00
|
|
|
res.end(JSON.stringify(locales));
|
2017-04-06 20:36:59 +08:00
|
|
|
});
|
|
|
|
|
2018-05-10 02:07:40 +08:00
|
|
|
WebApp.connectHandlers.use('/feedback', (req, res) => {
|
|
|
|
req.on('data', Meteor.bindEnvironment((data) => {
|
|
|
|
const body = JSON.parse(data);
|
|
|
|
const {
|
|
|
|
meetingId,
|
|
|
|
userId,
|
2018-05-11 04:23:17 +08:00
|
|
|
authToken,
|
2019-07-12 04:51:09 +08:00
|
|
|
userName: reqUserName,
|
|
|
|
comment,
|
|
|
|
rating,
|
2018-05-10 02:07:40 +08:00
|
|
|
} = body;
|
|
|
|
|
2019-07-12 04:51:09 +08:00
|
|
|
check(meetingId, String);
|
|
|
|
check(userId, String);
|
|
|
|
check(authToken, String);
|
|
|
|
check(reqUserName, String);
|
|
|
|
check(comment, String);
|
|
|
|
check(rating, Number);
|
|
|
|
|
2018-05-11 04:23:17 +08:00
|
|
|
const user = Users.findOne({
|
|
|
|
meetingId,
|
|
|
|
userId,
|
|
|
|
connectionStatus: 'offline',
|
|
|
|
authToken,
|
|
|
|
});
|
2018-05-10 02:07:40 +08:00
|
|
|
|
2019-07-07 06:48:33 +08:00
|
|
|
if (!user) {
|
2019-07-23 03:32:14 +08:00
|
|
|
Logger.warn('Couldn\'t find user for feedback');
|
2019-07-07 06:48:33 +08:00
|
|
|
}
|
|
|
|
|
2019-07-12 04:51:09 +08:00
|
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
res.writeHead(200);
|
|
|
|
res.end(JSON.stringify({ status: 'ok' }));
|
|
|
|
|
|
|
|
body.userName = user ? user.name : `[unconfirmed] ${reqUserName}`;
|
|
|
|
|
2018-05-10 02:07:40 +08:00
|
|
|
const feedback = {
|
|
|
|
...body,
|
|
|
|
};
|
|
|
|
Logger.info('FEEDBACK LOG:', feedback);
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
2019-05-02 08:33:03 +08:00
|
|
|
WebApp.connectHandlers.use('/useragent', (req, res) => {
|
|
|
|
const userAgent = req.headers['user-agent'];
|
|
|
|
let response = 'No user agent found in header';
|
|
|
|
if (userAgent) {
|
2019-05-03 00:47:40 +08:00
|
|
|
response = lookupUserAgent(userAgent).toString();
|
2019-05-02 08:33:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Logger.info(`The requesting user agent is ${response}`);
|
|
|
|
|
|
|
|
// res.setHeader('Content-Type', 'application/json');
|
|
|
|
res.writeHead(200);
|
|
|
|
res.end(response);
|
|
|
|
});
|
2018-05-10 02:07:40 +08:00
|
|
|
|
2016-10-18 20:03:51 +08:00
|
|
|
export const eventEmitter = Redis.emitter;
|
2016-05-05 01:00:57 +08:00
|
|
|
|
2017-06-03 03:25:02 +08:00
|
|
|
export const redisPubSub = Redis;
|