mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +01:00
refactor middlewares in their own folder, move buildFiles to libs/buildFiles
This commit is contained in:
@@ -21,7 +21,6 @@
|
|||||||
"NEW_RELIC_APPLICATION_ID":"NEW_RELIC_APPLICATION_ID",
|
"NEW_RELIC_APPLICATION_ID":"NEW_RELIC_APPLICATION_ID",
|
||||||
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
|
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
|
||||||
"GA_ID": "GA_ID",
|
"GA_ID": "GA_ID",
|
||||||
"MIXPANEL_TOKEN": "MIXPANEL_TOKEN",
|
|
||||||
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
||||||
"AMAZON_PAYMENTS": {
|
"AMAZON_PAYMENTS": {
|
||||||
"SELLER_ID": "SELLER_ID",
|
"SELLER_ID": "SELLER_ID",
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ module.exports = {
|
|||||||
|
|
||||||
|
|
||||||
// Export en strings only, temporary solution for mobile
|
// Export en strings only, temporary solution for mobile
|
||||||
// This is copied from middleware.js#module.exports.locals#t()
|
// This is copied from middlewares/locals#t()
|
||||||
module.exports.enTranslations = function(){ // stringName and vars are the allowed parameters
|
module.exports.enTranslations = function(){ // stringName and vars are the allowed parameters
|
||||||
var language = _.find(avalaibleLanguages, {code: 'en'});
|
var language = _.find(avalaibleLanguages, {code: 'en'});
|
||||||
//language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined);
|
//language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined);
|
||||||
|
|||||||
62
website/src/libs/buildManifest.js
Normal file
62
website/src/libs/buildManifest.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var nconf = require('nconf');
|
||||||
|
var _ = require('lodash');
|
||||||
|
var manifestFiles = require("../../public/manifest.json");
|
||||||
|
|
||||||
|
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||||
|
var buildFiles = [];
|
||||||
|
|
||||||
|
var walk = function(folder){
|
||||||
|
var res = fs.readdirSync(folder);
|
||||||
|
var files = [];
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(path.join(__dirname, "/../../build"));
|
||||||
|
|
||||||
|
var getBuildUrl = module.exports.getBuildUrl = function(url){
|
||||||
|
if(buildFiles[url]) return '/' + buildFiles[url];
|
||||||
|
|
||||||
|
return '/' + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getManifestFiles = function(page){
|
||||||
|
var files = manifestFiles[page];
|
||||||
|
|
||||||
|
if(!files) throw new Error("Page not found!");
|
||||||
|
|
||||||
|
var code = '';
|
||||||
|
|
||||||
|
if(IS_PROD){
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
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('../../common');
|
|
||||||
var request = require('request');
|
|
||||||
var os = require('os');
|
|
||||||
var moment = require('moment');
|
|
||||||
var utils = require('./utils');
|
|
||||||
|
|
||||||
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
|
||||||
var BASE_URL = nconf.get("BASE_URL");
|
|
||||||
|
|
||||||
module.exports.apiThrottle = function(app) {
|
|
||||||
if (!IS_PROD) 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 (IS_PROD) {
|
|
||||||
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 = 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHTTP(req) {
|
|
||||||
return (
|
|
||||||
req.headers['x-forwarded-proto'] &&
|
|
||||||
req.headers['x-forwarded-proto'] !== 'https' &&
|
|
||||||
IS_PROD &&
|
|
||||||
BASE_URL.indexOf('https') === 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isProxied(req) {
|
|
||||||
return (
|
|
||||||
req.headers['x-habitica-lb'] &&
|
|
||||||
req.headers['x-habitica-lb'] === 'Yes'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.forceSSL = function(req, res, next){
|
|
||||||
if(isHTTP(req) && !isProxied(req)) {
|
|
||||||
return res.redirect(BASE_URL + req.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect to habitica for non-api urls
|
|
||||||
// NOTE: Currently using a static 'habitica.com' string, rather than BASE_URL,
|
|
||||||
// to make rollback easy. Eventually, BASE_URL should be migrated.
|
|
||||||
|
|
||||||
function nonApiUrl(req) {
|
|
||||||
return req.url.search(/\/api\//) === -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.forceHabitica = function(req, res, next) {
|
|
||||||
var ignoreRedirect = nconf.get('IGNORE_REDIRECT');
|
|
||||||
|
|
||||||
if (IS_PROD && !ignoreRedirect && !isProxied(req) && nonApiUrl(req)) {
|
|
||||||
return res.redirect('https://habitica.com' + 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(IS_PROD){
|
|
||||||
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 AMPLITUDE_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) || {},
|
|
||||||
_: _,
|
|
||||||
MP_ID: nconf.get('MP_ID'),
|
|
||||||
AMAZON_PAYMENTS: {
|
|
||||||
SELLER_ID: nconf.get('AMAZON_PAYMENTS:SELLER_ID'),
|
|
||||||
CLIENT_ID: nconf.get('AMAZON_PAYMENTS:CLIENT_ID')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Put query-string party (& guild but use partyInvite for backward compatibility)
|
|
||||||
// invitations into session to be handled later
|
|
||||||
try{
|
|
||||||
req.session.partyInvite = JSON.parse(utils.decrypt(req.query.partyInvite))
|
|
||||||
} catch(e){}
|
|
||||||
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
21
website/src/middlewares/apiThrottle.js
Normal file
21
website/src/middlewares/apiThrottle.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
var nconf = require('nconf');
|
||||||
|
|
||||||
|
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||||
|
|
||||||
|
module.exports = function(app) {
|
||||||
|
if (!IS_PROD) 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();
|
||||||
|
});
|
||||||
|
}
|
||||||
7
website/src/middlewares/cors.js
Normal file
7
website/src/middlewares/cors.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = 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();
|
||||||
|
};
|
||||||
39
website/src/middlewares/domain.js
Normal file
39
website/src/middlewares/domain.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
var nconf = require('nconf');
|
||||||
|
var moment = require('moment');
|
||||||
|
var domainMiddleware = require('domain-middleware');
|
||||||
|
var os = require('os');
|
||||||
|
var request = require('request');
|
||||||
|
|
||||||
|
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||||
|
|
||||||
|
module.exports = function(server,mongoose) {
|
||||||
|
if (IS_PROD) {
|
||||||
|
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 = 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
|
||||||
|
});
|
||||||
|
}
|
||||||
24
website/src/middlewares/errorHandler.js
Normal file
24
website/src/middlewares/errorHandler.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
var logging = require('../logging');
|
||||||
|
|
||||||
|
module.exports = 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);
|
||||||
|
}
|
||||||
9
website/src/middlewares/forceRefresh.js
Normal file
9
website/src/middlewares/forceRefresh.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module.exports.siteVersion = 1;
|
||||||
|
|
||||||
|
module.exports.middleware = function(req, res, next){
|
||||||
|
if(req.query.siteVersion && req.query.siteVersion != module.exports.siteVersion){
|
||||||
|
return res.json(400, {needRefresh: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
51
website/src/middlewares/locals.js
Normal file
51
website/src/middlewares/locals.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
var nconf = require('nconf');
|
||||||
|
var _ = require('lodash');
|
||||||
|
var utils = require('../utils');
|
||||||
|
var shared = require('../../../common');
|
||||||
|
var i18n = require('../i18n.js');
|
||||||
|
var buildManifest = require('../libs/buildManifest');
|
||||||
|
var shared = require('../../../common');
|
||||||
|
var forceRefresh = require('./forceRefresh');
|
||||||
|
|
||||||
|
module.exports = 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 AMPLITUDE_KEY'.split(' '));
|
||||||
|
res.locals.habitrpg = _.merge(envVars, {
|
||||||
|
IS_MOBILE: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent')),
|
||||||
|
getManifestFiles: buildManifest.getManifestFiles,
|
||||||
|
getBuildUrl: buildManifest.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: forceRefresh.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) || {},
|
||||||
|
_: _,
|
||||||
|
AMAZON_PAYMENTS: {
|
||||||
|
SELLER_ID: nconf.get('AMAZON_PAYMENTS:SELLER_ID'),
|
||||||
|
CLIENT_ID: nconf.get('AMAZON_PAYMENTS:CLIENT_ID')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Put query-string party (& guild but use partyInvite for backward compatibility)
|
||||||
|
// invitations into session to be handled later
|
||||||
|
try{
|
||||||
|
req.session.partyInvite = JSON.parse(utils.decrypt(req.query.partyInvite))
|
||||||
|
} catch(e){}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
43
website/src/middlewares/redirects.js
Normal file
43
website/src/middlewares/redirects.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
var nconf = require('nconf');
|
||||||
|
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||||
|
var ignoreRedirect = nconf.get('IGNORE_REDIRECT');
|
||||||
|
var BASE_URL = nconf.get("BASE_URL");
|
||||||
|
|
||||||
|
function isHTTP(req) {
|
||||||
|
return (
|
||||||
|
req.headers['x-forwarded-proto'] &&
|
||||||
|
req.headers['x-forwarded-proto'] !== 'https' &&
|
||||||
|
IS_PROD &&
|
||||||
|
BASE_URL.indexOf('https') === 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isProxied(req) {
|
||||||
|
return (
|
||||||
|
req.headers['x-habitica-lb'] &&
|
||||||
|
req.headers['x-habitica-lb'] === 'Yes'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.forceSSL = function(req, res, next){
|
||||||
|
if(isHTTP(req) && !isProxied(req)) {
|
||||||
|
return res.redirect(BASE_URL + req.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to habitica for non-api urls
|
||||||
|
// NOTE: Currently using a static 'habitica.com' string, rather than BASE_URL,
|
||||||
|
// to make rollback easy. Eventually, BASE_URL should be migrated.
|
||||||
|
|
||||||
|
function nonApiUrl(req) {
|
||||||
|
return req.url.search(/\/api\//) === -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.forceHabitica = function(req, res, next) {
|
||||||
|
if (IS_PROD && !ignoreRedirect && !isProxied(req) && nonApiUrl(req)) {
|
||||||
|
return res.redirect('https://habitica.com' + req.url);
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
@@ -5,9 +5,9 @@ var async = require('async');
|
|||||||
var icalendar = require('icalendar');
|
var icalendar = require('icalendar');
|
||||||
var api = require('./../controllers/user');
|
var api = require('./../controllers/user');
|
||||||
var auth = require('./../controllers/auth');
|
var auth = require('./../controllers/auth');
|
||||||
var middleware = require('../middleware');
|
|
||||||
var logging = require('./../logging');
|
var logging = require('./../logging');
|
||||||
var i18n = require('./../i18n');
|
var i18n = require('./../i18n');
|
||||||
|
var forceRefresh = require('../middlewares/forceRefresh').middleware;
|
||||||
|
|
||||||
/* ---------- Deprecated API ------------*/
|
/* ---------- Deprecated API ------------*/
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ router.post('/user/task', auth.auth, i18n.getUserLanguage, cron, api.addTask);
|
|||||||
// User
|
// User
|
||||||
router.get('/user', auth.auth, i18n.getUserLanguage, cron, api.getUser);
|
router.get('/user', auth.auth, i18n.getUserLanguage, cron, api.getUser);
|
||||||
router.post('/user/revive', auth.auth, i18n.getUserLanguage, cron, api.revive);
|
router.post('/user/revive', auth.auth, i18n.getUserLanguage, cron, api.revive);
|
||||||
router.post('/user/batch-update', middleware.forceRefresh, auth.auth, i18n.getUserLanguage, cron, batchUpdate);
|
router.post('/user/batch-update', forceRefresh, auth.auth, i18n.getUserLanguage, cron, batchUpdate);
|
||||||
|
|
||||||
function deprecated(req, res) {
|
function deprecated(req, res) {
|
||||||
res.json(404, {err:'API v1 is no longer supported, please use API v2 instead (https://github.com/HabitRPG/habitrpg/blob/develop/API.md)'});
|
res.json(404, {err:'API v1 is no longer supported, please use API v2 instead (https://github.com/HabitRPG/habitrpg/blob/develop/API.md)'});
|
||||||
|
|||||||
@@ -16,12 +16,11 @@ hall = require("../controllers/hall")
|
|||||||
challenges = require("../controllers/challenges")
|
challenges = require("../controllers/challenges")
|
||||||
dataexport = require("../controllers/dataexport")
|
dataexport = require("../controllers/dataexport")
|
||||||
nconf = require("nconf")
|
nconf = require("nconf")
|
||||||
middleware = require("../middleware")
|
|
||||||
cron = user.cron
|
cron = user.cron
|
||||||
_ = require('lodash')
|
_ = require('lodash')
|
||||||
content = require('../../../common').content
|
content = require('../../../common').content
|
||||||
i18n = require('../i18n')
|
i18n = require('../i18n')
|
||||||
|
forceRefresh = require('../middlewares/forceRefresh').middleware
|
||||||
|
|
||||||
module.exports = (swagger, v2) ->
|
module.exports = (swagger, v2) ->
|
||||||
[path,body,query] = [swagger.pathParam, swagger.bodyParam, swagger.queryParam]
|
[path,body,query] = [swagger.pathParam, swagger.bodyParam, swagger.queryParam]
|
||||||
@@ -326,7 +325,7 @@ module.exports = (swagger, v2) ->
|
|||||||
parameters:[
|
parameters:[
|
||||||
body '','The array of batch-operations to perform','object'
|
body '','The array of batch-operations to perform','object'
|
||||||
]
|
]
|
||||||
middleware: [middleware.forceRefresh, auth.auth, i18n.getUserLanguage, cron, user.sessionPartyInvite]
|
middleware: [forceRefresh, auth.auth, i18n.getUserLanguage, cron, user.sessionPartyInvite]
|
||||||
action: user.batchUpdate
|
action: user.batchUpdate
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ var dataexport = require('../controllers/dataexport');
|
|||||||
var auth = require('../controllers/auth');
|
var auth = require('../controllers/auth');
|
||||||
var nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
var i18n = require('../i18n');
|
var i18n = require('../i18n');
|
||||||
var middleware = require('../middleware.js');
|
var locals = require('../middlewares/locals');
|
||||||
|
|
||||||
/* Data export */
|
/* Data export */
|
||||||
router.get('/history.csv',auth.authWithSession,i18n.getUserLanguage,dataexport.history); //[todo] encode data output options in the data controller and use these to build routes
|
router.get('/history.csv',auth.authWithSession,i18n.getUserLanguage,dataexport.history); //[todo] encode data output options in the data controller and use these to build routes
|
||||||
router.get('/userdata.xml',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.xml);
|
router.get('/userdata.xml',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.xml);
|
||||||
router.get('/userdata.json',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.json);
|
router.get('/userdata.json',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.json);
|
||||||
router.get('/avatar-:uuid.html', i18n.getUserLanguage, middleware.locals, dataexport.avatarPage);
|
router.get('/avatar-:uuid.html', i18n.getUserLanguage, locals, dataexport.avatarPage);
|
||||||
router.get('/avatar-:uuid.png', i18n.getUserLanguage, middleware.locals, dataexport.avatarImage);
|
router.get('/avatar-:uuid.png', i18n.getUserLanguage, locals, dataexport.avatarImage);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ var nconf = require('nconf');
|
|||||||
var express = require('express');
|
var express = require('express');
|
||||||
var router = new express.Router();
|
var router = new express.Router();
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var middleware = require('../middleware');
|
var locals = require('../middlewares/locals');
|
||||||
var user = require('../controllers/user');
|
var user = require('../controllers/user');
|
||||||
var auth = require('../controllers/auth');
|
var auth = require('../controllers/auth');
|
||||||
var i18n = require('../i18n');
|
var i18n = require('../i18n');
|
||||||
|
|
||||||
// -------- App --------
|
// -------- App --------
|
||||||
router.get('/', i18n.getUserLanguage, middleware.locals, function(req, res) {
|
router.get('/', i18n.getUserLanguage, locals, function(req, res) {
|
||||||
if (!req.headers['x-api-user'] && !req.headers['x-api-key'] && !(req.session && req.session.userId))
|
if (!req.headers['x-api-user'] && !req.headers['x-api-key'] && !(req.session && req.session.userId))
|
||||||
return res.redirect('/static/front')
|
return res.redirect('/static/front')
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ router.get('/', i18n.getUserLanguage, middleware.locals, function(req, res) {
|
|||||||
var pages = ['front', 'privacy', 'terms', 'api', 'features', 'videos', 'contact', 'plans', 'new-stuff', 'community-guidelines', 'old-news', 'press-kit'];
|
var pages = ['front', 'privacy', 'terms', 'api', 'features', 'videos', 'contact', 'plans', 'new-stuff', 'community-guidelines', 'old-news', 'press-kit'];
|
||||||
|
|
||||||
_.each(pages, function(name){
|
_.each(pages, function(name){
|
||||||
router.get('/static/' + name, i18n.getUserLanguage, middleware.locals, function(req, res) {
|
router.get('/static/' + name, i18n.getUserLanguage, locals, function(req, res) {
|
||||||
res.render('static/' + name, {env: res.locals.habitrpg});
|
res.render('static/' + name, {env: res.locals.habitrpg});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
|
|||||||
// Setup translations
|
// Setup translations
|
||||||
var i18n = require('./i18n');
|
var i18n = require('./i18n');
|
||||||
|
|
||||||
var middleware = require('./middleware');
|
|
||||||
|
|
||||||
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
|
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
|
||||||
var app = express();
|
var app = express();
|
||||||
var server = http.createServer();
|
var server = http.createServer();
|
||||||
@@ -89,16 +87,18 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
|
|||||||
var publicDir = path.join(__dirname, "/../public");
|
var publicDir = path.join(__dirname, "/../public");
|
||||||
|
|
||||||
app.set("port", nconf.get('PORT'));
|
app.set("port", nconf.get('PORT'));
|
||||||
middleware.apiThrottle(app);
|
require('./middlewares/apiThrottle')(app);
|
||||||
app.use(middleware.domainMiddleware(server,mongoose));
|
app.use(require('./middlewares/domain')(server,mongoose));
|
||||||
if (!isProd) app.use(express.logger("dev"));
|
if (!isProd) app.use(express.logger("dev"));
|
||||||
app.use(express.compress());
|
app.use(express.compress());
|
||||||
app.set("views", __dirname + "/../views");
|
app.set("views", __dirname + "/../views");
|
||||||
app.set("view engine", "jade");
|
app.set("view engine", "jade");
|
||||||
app.use(express.favicon(publicDir + '/favicon.ico'));
|
app.use(express.favicon(publicDir + '/favicon.ico'));
|
||||||
app.use(middleware.cors);
|
app.use(require('./middlewares/cors'));
|
||||||
app.use(middleware.forceHabitica);
|
|
||||||
app.use(middleware.forceSSL);
|
var redirects = require('./middlewares/redirects');
|
||||||
|
app.use(redirects.forceHabitica);
|
||||||
|
app.use(redirects.forceSSL);
|
||||||
app.use(express.urlencoded());
|
app.use(express.urlencoded());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(require('method-override')());
|
app.use(require('method-override')());
|
||||||
@@ -134,7 +134,7 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
|
|||||||
app.use('/api/v1', require('./routes/apiv1').middleware);
|
app.use('/api/v1', require('./routes/apiv1').middleware);
|
||||||
app.use('/export', require('./routes/dataexport').middleware);
|
app.use('/export', require('./routes/dataexport').middleware);
|
||||||
require('./routes/apiv2.coffee')(swagger, v2);
|
require('./routes/apiv2.coffee')(swagger, v2);
|
||||||
app.use(middleware.errorHandler);
|
app.use(require('./middlewares/errorHandler'));
|
||||||
|
|
||||||
server.on('request', app);
|
server.on('request', app);
|
||||||
server.listen(app.get("port"), function() {
|
server.listen(app.get("port"), function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user