mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
Moved src to website/src (again)
This commit is contained in:
210
website/src/middleware.js
Normal file
210
website/src/middleware.js
Normal file
@@ -0,0 +1,210 @@
|
||||
var nconf = require('nconf');
|
||||
var _ = require('lodash');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var User = require('./models/user').model
|
||||
var limiter = require('connect-ratelimit');
|
||||
var logging = require('./logging');
|
||||
var domainMiddleware = require('domain-middleware');
|
||||
var cluster = require('cluster');
|
||||
var i18n = require('./i18n.js');
|
||||
var shared = require('habitrpg-shared');
|
||||
var request = require('request');
|
||||
var os = require('os');
|
||||
var moment = require('moment');
|
||||
var utils = require('./utils');
|
||||
|
||||
module.exports.apiThrottle = function(app) {
|
||||
if (nconf.get('NODE_ENV') !== 'production') return;
|
||||
app.use(limiter({
|
||||
end:false,
|
||||
catagories:{
|
||||
normal: {
|
||||
// 2 req/s, but split as minutes
|
||||
totalRequests: 80,
|
||||
every: 60000
|
||||
}
|
||||
}
|
||||
})).use(function(req,res,next){
|
||||
//logging.info(res.ratelimit);
|
||||
if (res.ratelimit.exceeded) return res.json(429,{err:'Rate limit exceeded'});
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.domainMiddleware = function(server,mongoose) {
|
||||
if (nconf.get('NODE_ENV')=='production') {
|
||||
var mins = 3, // how often to run this check
|
||||
useAvg = false, // use average over 3 minutes, or simply the last minute's report
|
||||
url = 'https://api.newrelic.com/v2/applications/'+nconf.get('NEW_RELIC_APPLICATION_ID')+'/metrics/data.json?names[]=Apdex&values[]=score';
|
||||
setInterval(function(){
|
||||
// see https://docs.newrelic.com/docs/apm/apis/api-v2-examples/average-response-time-examples-api-v2, https://rpm.newrelic.com/api/explore/applications/data
|
||||
request({
|
||||
url: useAvg ? url+'&from='+moment().subtract({minutes:mins}).utc().format()+'&to='+moment().utc().format()+'&summarize=true' : url,
|
||||
headers: {'X-Api-Key': nconf.get('NEW_RELIC_API_KEY')}
|
||||
}, function(err, response, body){
|
||||
var ts = JSON.parse(body).metric_data.metrics[0].timeslices,
|
||||
score = ts[ts.length-1].values.score,
|
||||
apdexBad = score < .75 || score == 1,
|
||||
memory = os.freemem() / os.totalmem(),
|
||||
memoryHigh = false; //memory < 0.1;
|
||||
if (apdexBad || memoryHigh) throw "[Memory Leak] Apdex="+score+" Memory="+parseFloat(memory).toFixed(3)+" Time="+moment().format();
|
||||
})
|
||||
}, mins*60*1000);
|
||||
}
|
||||
|
||||
return domainMiddleware({
|
||||
server: {
|
||||
close:function(){
|
||||
server.close();
|
||||
mongoose.connection.close();
|
||||
}
|
||||
},
|
||||
killTimeout: 10000
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.errorHandler = function(err, req, res, next) {
|
||||
//res.locals.domain.emit('error', err);
|
||||
// when we hit an error, send it to admin as an email. If no ADMIN_EMAIL is present, just send it to yourself (SMTP_USER)
|
||||
var stack = (err.stack ? err.stack : err.message ? err.message : err) +
|
||||
"\n ----------------------------\n" +
|
||||
"\n\noriginalUrl: " + req.originalUrl +
|
||||
"\n\nauth: " + req.headers['x-api-user'] + ' | ' + req.headers['x-api-key'] +
|
||||
"\n\nheaders: " + JSON.stringify(req.headers) +
|
||||
"\n\nbody: " + JSON.stringify(req.body) +
|
||||
(res.locals.ops ? "\n\ncompleted ops: " + JSON.stringify(res.locals.ops) : "");
|
||||
logging.error(stack);
|
||||
/*logging.loggly({
|
||||
error: "Uncaught error",
|
||||
stack: (err.stack || err.message || err),
|
||||
body: req.body, headers: req.header,
|
||||
auth: req.headers['x-api-user'],
|
||||
originalUrl: req.originalUrl
|
||||
});*/
|
||||
var message = err.message ? err.message : err;
|
||||
message = (message.length < 200) ? message : message.substring(0,100) + message.substring(message.length-100,message.length);
|
||||
res.json(500,{err:message}); //res.end(err.message);
|
||||
}
|
||||
|
||||
|
||||
module.exports.forceSSL = function(req, res, next){
|
||||
var baseUrl = nconf.get("BASE_URL");
|
||||
// Note x-forwarded-proto is used by Heroku & nginx, you'll have to do something different if you're not using those
|
||||
if (req.headers['x-forwarded-proto'] && req.headers['x-forwarded-proto'] !== 'https'
|
||||
&& nconf.get('NODE_ENV') === 'production'
|
||||
&& baseUrl.indexOf('https') === 0) {
|
||||
return res.redirect(baseUrl + req.url);
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
module.exports.cors = function(req, res, next) {
|
||||
res.header("Access-Control-Allow-Origin", req.headers.origin || "*");
|
||||
res.header("Access-Control-Allow-Methods", "OPTIONS,GET,POST,PUT,HEAD,DELETE");
|
||||
res.header("Access-Control-Allow-Headers", "Content-Type,Accept,Content-Encoding,X-Requested-With,x-api-user,x-api-key");
|
||||
if (req.method === 'OPTIONS') return res.send(200);
|
||||
return next();
|
||||
};
|
||||
|
||||
var siteVersion = 1;
|
||||
|
||||
module.exports.forceRefresh = function(req, res, next){
|
||||
if(req.query.siteVersion && req.query.siteVersion != siteVersion){
|
||||
return res.json(400, {needRefresh: true});
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
var buildFiles = [];
|
||||
|
||||
var walk = function(folder){
|
||||
var res = fs.readdirSync(folder);
|
||||
|
||||
res.forEach(function(fileName){
|
||||
file = folder + '/' + fileName;
|
||||
if(fs.statSync(file).isDirectory()){
|
||||
walk(file);
|
||||
}else{
|
||||
var relFolder = path.relative(path.join(__dirname, "/../build"), folder);
|
||||
var old = fileName.replace(/-.{8}(\.[\d\w]+)$/, '$1');
|
||||
|
||||
if(relFolder){
|
||||
old = relFolder + '/' + old;
|
||||
fileName = relFolder + '/' + fileName;
|
||||
}
|
||||
|
||||
buildFiles[old] = fileName
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
walk(path.join(__dirname, "/../build"));
|
||||
|
||||
var getBuildUrl = function(url){
|
||||
if(buildFiles[url]) return '/' + buildFiles[url];
|
||||
|
||||
return '/' + url;
|
||||
}
|
||||
|
||||
var manifestFiles = require("../public/manifest.json");
|
||||
|
||||
var getManifestFiles = function(page){
|
||||
var files = manifestFiles[page];
|
||||
|
||||
if(!files) throw new Error("Page not found!");
|
||||
|
||||
var code = '';
|
||||
|
||||
if(nconf.get('NODE_ENV') === 'production'){
|
||||
code += '<link rel="stylesheet" type="text/css" href="' + getBuildUrl(page + '.css') + '">';
|
||||
code += '<script type="text/javascript" src="' + getBuildUrl(page + '.js') + '"></script>';
|
||||
}else{
|
||||
_.each(files.css, function(file){
|
||||
code += '<link rel="stylesheet" type="text/css" href="' + getBuildUrl(file) + '">';
|
||||
});
|
||||
_.each(files.js, function(file){
|
||||
code += '<script type="text/javascript" src="' + getBuildUrl(file) + '"></script>';
|
||||
});
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
module.exports.locals = function(req, res, next) {
|
||||
var language = _.find(i18n.avalaibleLanguages, {code: req.language});
|
||||
var isStaticPage = req.url.split('/')[1] === 'static'; // If url contains '/static/'
|
||||
|
||||
// Load moment.js language file only when not on static pages
|
||||
language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined);
|
||||
|
||||
var tavern = require('./models/group').tavern;
|
||||
var envVars = _.pick(nconf.get(), 'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY'.split(' '));
|
||||
res.locals.habitrpg = _.merge(envVars, {
|
||||
IS_MOBILE: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent')),
|
||||
getManifestFiles: getManifestFiles,
|
||||
getBuildUrl: getBuildUrl,
|
||||
avalaibleLanguages: i18n.avalaibleLanguages,
|
||||
language: language,
|
||||
isStaticPage: isStaticPage,
|
||||
translations: i18n.translations[language.code],
|
||||
t: function(){ // stringName and vars are the allowed parameters
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args.push(language.code);
|
||||
return shared.i18n.t.apply(null, args);
|
||||
},
|
||||
siteVersion: siteVersion,
|
||||
Content: shared.content,
|
||||
mods: require('./models/user').mods,
|
||||
tavern: tavern, // for world boss
|
||||
worldDmg: (tavern && tavern.quest && tavern.quest.extra && tavern.quest.extra.worldDmg) || {}
|
||||
});
|
||||
|
||||
// Put query-string party invitations into session to be handled later
|
||||
try{
|
||||
req.session.partyInvite = JSON.parse(utils.decrypt(req.query.partyInvite))
|
||||
} catch(e){}
|
||||
|
||||
next();
|
||||
}
|
||||
Reference in New Issue
Block a user