mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +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_API_KEY":"NEW_RELIC_API_KEY",
|
||||
"GA_ID": "GA_ID",
|
||||
"MIXPANEL_TOKEN": "MIXPANEL_TOKEN",
|
||||
"AMPLITUDE_KEY": "AMPLITUDE_KEY",
|
||||
"AMAZON_PAYMENTS": {
|
||||
"SELLER_ID": "SELLER_ID",
|
||||
|
||||
@@ -148,7 +148,7 @@ module.exports = {
|
||||
|
||||
|
||||
// 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
|
||||
var language = _.find(avalaibleLanguages, {code: 'en'});
|
||||
//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 api = require('./../controllers/user');
|
||||
var auth = require('./../controllers/auth');
|
||||
var middleware = require('../middleware');
|
||||
var logging = require('./../logging');
|
||||
var i18n = require('./../i18n');
|
||||
var forceRefresh = require('../middlewares/forceRefresh').middleware;
|
||||
|
||||
/* ---------- Deprecated API ------------*/
|
||||
|
||||
@@ -161,7 +161,7 @@ router.post('/user/task', auth.auth, i18n.getUserLanguage, cron, api.addTask);
|
||||
// User
|
||||
router.get('/user', auth.auth, i18n.getUserLanguage, cron, api.getUser);
|
||||
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) {
|
||||
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")
|
||||
dataexport = require("../controllers/dataexport")
|
||||
nconf = require("nconf")
|
||||
middleware = require("../middleware")
|
||||
cron = user.cron
|
||||
_ = require('lodash')
|
||||
content = require('../../../common').content
|
||||
i18n = require('../i18n')
|
||||
|
||||
forceRefresh = require('../middlewares/forceRefresh').middleware
|
||||
|
||||
module.exports = (swagger, v2) ->
|
||||
[path,body,query] = [swagger.pathParam, swagger.bodyParam, swagger.queryParam]
|
||||
@@ -326,7 +325,7 @@ module.exports = (swagger, v2) ->
|
||||
parameters:[
|
||||
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
|
||||
|
||||
# Tags
|
||||
|
||||
@@ -4,13 +4,13 @@ var dataexport = require('../controllers/dataexport');
|
||||
var auth = require('../controllers/auth');
|
||||
var nconf = require('nconf');
|
||||
var i18n = require('../i18n');
|
||||
var middleware = require('../middleware.js');
|
||||
var locals = require('../middlewares/locals');
|
||||
|
||||
/* 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('/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('/avatar-:uuid.html', i18n.getUserLanguage, middleware.locals, dataexport.avatarPage);
|
||||
router.get('/avatar-:uuid.png', i18n.getUserLanguage, middleware.locals, dataexport.avatarImage);
|
||||
router.get('/avatar-:uuid.html', i18n.getUserLanguage, locals, dataexport.avatarPage);
|
||||
router.get('/avatar-:uuid.png', i18n.getUserLanguage, locals, dataexport.avatarImage);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -2,13 +2,13 @@ var nconf = require('nconf');
|
||||
var express = require('express');
|
||||
var router = new express.Router();
|
||||
var _ = require('lodash');
|
||||
var middleware = require('../middleware');
|
||||
var locals = require('../middlewares/locals');
|
||||
var user = require('../controllers/user');
|
||||
var auth = require('../controllers/auth');
|
||||
var i18n = require('../i18n');
|
||||
|
||||
// -------- 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))
|
||||
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'];
|
||||
|
||||
_.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});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -30,8 +30,6 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
|
||||
// Setup translations
|
||||
var i18n = require('./i18n');
|
||||
|
||||
var middleware = require('./middleware');
|
||||
|
||||
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
|
||||
var app = express();
|
||||
var server = http.createServer();
|
||||
@@ -89,16 +87,18 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
|
||||
var publicDir = path.join(__dirname, "/../public");
|
||||
|
||||
app.set("port", nconf.get('PORT'));
|
||||
middleware.apiThrottle(app);
|
||||
app.use(middleware.domainMiddleware(server,mongoose));
|
||||
require('./middlewares/apiThrottle')(app);
|
||||
app.use(require('./middlewares/domain')(server,mongoose));
|
||||
if (!isProd) app.use(express.logger("dev"));
|
||||
app.use(express.compress());
|
||||
app.set("views", __dirname + "/../views");
|
||||
app.set("view engine", "jade");
|
||||
app.use(express.favicon(publicDir + '/favicon.ico'));
|
||||
app.use(middleware.cors);
|
||||
app.use(middleware.forceHabitica);
|
||||
app.use(middleware.forceSSL);
|
||||
app.use(require('./middlewares/cors'));
|
||||
|
||||
var redirects = require('./middlewares/redirects');
|
||||
app.use(redirects.forceHabitica);
|
||||
app.use(redirects.forceSSL);
|
||||
app.use(express.urlencoded());
|
||||
app.use(express.json());
|
||||
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('/export', require('./routes/dataexport').middleware);
|
||||
require('./routes/apiv2.coffee')(swagger, v2);
|
||||
app.use(middleware.errorHandler);
|
||||
app.use(require('./middlewares/errorHandler'));
|
||||
|
||||
server.on('request', app);
|
||||
server.listen(app.get("port"), function() {
|
||||
|
||||
Reference in New Issue
Block a user