mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 07:37:25 +01:00
port locals middleware, add some tests and a lot of fixes
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
describe('GET /export/avatar-:memberId.html', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
user = await generateUser();
|
||||
});
|
||||
|
||||
it('validates req.params.memberId', async () => {
|
||||
await expect(user.get(`/export/avatar-:memberId.html`)).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
it('handles non-existing members', async () => {
|
||||
let dummyId = generateUUID();
|
||||
await expect(user.get(`/export/avatar-${dummyId}.html`)).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('userWithIDNotFound', {userId: dummyId}),
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an html page', async () => {
|
||||
let res = await user.get(`/export/avatar-${user._id}.html`);
|
||||
expect(res.substring(0, 100).indexOf('<!DOCTYPE html>')).to.equal(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
// TODO how to test this route since it points to a file on AWS s3?
|
||||
|
||||
describe('GET /export/avatar-:memberId.png', () => {});
|
||||
@@ -0,0 +1,3 @@
|
||||
// TODO how to test this route since it uses session authentication?
|
||||
|
||||
describe('GET /export/history.csv', () => {});
|
||||
@@ -0,0 +1,3 @@
|
||||
// TODO how to test this route since it uses session authentication?
|
||||
|
||||
describe('GET /export/userdata.json', () => {});
|
||||
@@ -0,0 +1,3 @@
|
||||
// TODO how to test this route since it uses session authentication?
|
||||
|
||||
describe('GET /export/userdata.xml', () => {});
|
||||
@@ -7,8 +7,8 @@ function($scope, $rootScope, User, $http, Notification, ApiUrl, Social) {
|
||||
$scope.loadWidgets = Social.loadWidgets;
|
||||
|
||||
if(env.isStaticPage){
|
||||
$scope.languages = env.avalaibleLanguages;
|
||||
$scope.selectedLanguage = _.find(env.avalaibleLanguages, {code: env.language.code});
|
||||
$scope.languages = env.availableLanguages;
|
||||
$scope.selectedLanguage = _.find(env.availableLanguages, {code: env.language.code});
|
||||
|
||||
$rootScope.selectedLanguage = $scope.selectedLanguage;
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ habitrpg.controller('SettingsCtrl',
|
||||
};
|
||||
|
||||
$scope.language = window.env.language;
|
||||
$scope.avalaibleLanguages = window.env.avalaibleLanguages;
|
||||
$scope.availableLanguages = window.env.availableLanguages;
|
||||
|
||||
$scope.changeLanguage = function(){
|
||||
$rootScope.$on('userSynced', function(){
|
||||
|
||||
@@ -14,6 +14,7 @@ import AWS from 'aws-sdk';
|
||||
import nconf from 'nconf';
|
||||
import got from 'got';
|
||||
import Q from 'q';
|
||||
import locals from '../../middlewares/api-v3/locals';
|
||||
|
||||
let S3 = new AWS.S3({
|
||||
accessKeyId: nconf.get('S3:accessKeyId'),
|
||||
@@ -25,6 +26,8 @@ const BASE_URL = nconf.get('BASE_URL');
|
||||
|
||||
let api = {};
|
||||
|
||||
// TODO move these routes out of the /api/v3/export namespace to the top level /export
|
||||
|
||||
/**
|
||||
* @api {get} /export/history.csv Export user tasks history in CSV format. History is only available for habits and dailys so todos and rewards won't be included
|
||||
* @apiVersion 3.0.0
|
||||
@@ -152,6 +155,7 @@ api.exportUserDataXml = {
|
||||
api.exportUserAvatarHtml = {
|
||||
method: 'GET',
|
||||
url: '/export/avatar-:memberId.html',
|
||||
middlewares: [locals],
|
||||
async handler (req, res) {
|
||||
req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID();
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ function _getMembersForItem (type) {
|
||||
*
|
||||
* @apiParam {UUID} groupId The group id
|
||||
* @apiParam {UUID} lastId Query parameter to specify the last member returned in a previous request to this route and get the next batch of results
|
||||
* @apiParam {boolean} includeAllPublicFields Query parameter avalaible only when fetching a party. If === `true` then all public fields for members will be returned (liek when making a request for a single member)
|
||||
* @apiParam {boolean} includeAllPublicFields Query parameter available only when fetching a party. If === `true` then all public fields for members will be returned (liek when making a request for a single member)
|
||||
*
|
||||
* @apiSuccess {array} members An array of members, sorted by _id
|
||||
*/
|
||||
|
||||
@@ -49,7 +49,7 @@ shared.i18n.translations = translations;
|
||||
|
||||
export let langCodes = Object.keys(translations);
|
||||
|
||||
export let avalaibleLanguages = langCodes.map((langCode) => {
|
||||
export let availableLanguages = langCodes.map((langCode) => {
|
||||
return {
|
||||
code: langCode,
|
||||
name: translations[langCode].languageName,
|
||||
@@ -57,7 +57,7 @@ export let avalaibleLanguages = langCodes.map((langCode) => {
|
||||
});
|
||||
|
||||
langCodes.forEach((code) => {
|
||||
let lang = _.find(avalaibleLanguages, {code});
|
||||
let lang = _.find(availableLanguages, {code});
|
||||
|
||||
lang.momentLangCode = momentLangsMapping[code] || code;
|
||||
|
||||
@@ -110,7 +110,7 @@ export let multipleVersionsLanguages = {
|
||||
// TODO review if this can be removed since the old mobile app is no longer active
|
||||
// stringName and vars are the allowed parameters
|
||||
export function enTranslations (...args) {
|
||||
let language = _.find(avalaibleLanguages, {code: 'en'});
|
||||
let language = _.find(availableLanguages, {code: 'en'});
|
||||
|
||||
// language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined);
|
||||
args.push(language.code);
|
||||
|
||||
@@ -27,6 +27,8 @@ export default function attachMiddlewares (app) {
|
||||
app.use(setupBody);
|
||||
app.use(responseHandler);
|
||||
app.use(getUserLanguage);
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', `${__dirname}/../../../views`);
|
||||
|
||||
app.use('/api/v3', routes);
|
||||
app.use(notFoundHandler);
|
||||
|
||||
73
website/src/middlewares/api-v3/locals.js
Normal file
73
website/src/middlewares/api-v3/locals.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import nconf from 'nconf';
|
||||
import _ from 'lodash';
|
||||
import shared from '../../../../common';
|
||||
import * as i18n from '../../libs/api-v3/i18n';
|
||||
import {
|
||||
getBuildUrl,
|
||||
getManifestFiles,
|
||||
} from '../../libs/api-v3/buildManifest';
|
||||
import forceRefresh from './../forceRefresh';
|
||||
import { tavernQuest } from '../../models/group';
|
||||
import { mods } from '../../models/user';
|
||||
import { decrypt } from '../../libs/api-v3/encryption';
|
||||
|
||||
// To avoid stringifying more data then we need,
|
||||
// items from `env` used on the client will have to be specified in this array
|
||||
// TODO where is this used?
|
||||
const CLIENT_VARS = ['language', 'isStaticPage', 'availableLanguages', 'translations',
|
||||
'FACEBOOK_KEY', 'NODE_ENV', 'BASE_URL', 'GA_ID',
|
||||
'AMAZON_PAYMENTS', 'STRIPE_PUB_KEY', 'AMPLITUDE_KEY',
|
||||
'worldDmg', 'mods', 'IS_MOBILE'];
|
||||
|
||||
let env = {
|
||||
getManifestFiles,
|
||||
getBuildUrl,
|
||||
_,
|
||||
clientVars: CLIENT_VARS,
|
||||
mods,
|
||||
Content: shared.content,
|
||||
siteVersion: forceRefresh.siteVersion,
|
||||
availableLanguages: i18n.available,
|
||||
AMAZON_PAYMENTS: {
|
||||
SELLER_ID: nconf.get('AMAZON_PAYMENTS:SELLER_ID'),
|
||||
CLIENT_ID: nconf.get('AMAZON_PAYMENTS:CLIENT_ID'),
|
||||
},
|
||||
};
|
||||
|
||||
'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY AMPLITUDE_KEY'.split(' ').forEach(key => {
|
||||
env[key] = nconf.get(key);
|
||||
});
|
||||
|
||||
export default function locals (req, res, next) {
|
||||
let language = _.find(i18n.availableLanguages, {code: req.language});
|
||||
let 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;
|
||||
|
||||
res.locals.habitrpg = _.assign(env, {
|
||||
IS_MOBILE: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent')),
|
||||
language,
|
||||
isStaticPage,
|
||||
translations: i18n.translations[language.code],
|
||||
t (...args) { // stringName and vars are the allowed parameters
|
||||
args.push(language.code);
|
||||
return shared.i18n.t(...args);
|
||||
},
|
||||
// Defined here and not outside of the middleware because tavernQuest might be an
|
||||
// empty object until the query to fetch it finishes
|
||||
worldDmg: tavernQuest && tavernQuest.extra && tavernQuest.extra.worldDmg || {},
|
||||
});
|
||||
|
||||
// Put query-string party (& guild but use partyInvite for backward compatibility)
|
||||
// invitations into session to be handled later
|
||||
if (req.query.partyInvite) {
|
||||
try {
|
||||
req.session.partyInvite = JSON.parse(decrypt(req.query.partyInvite));
|
||||
} catch (e) {
|
||||
// TODO logs
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
@@ -489,7 +489,7 @@ schema.statics.bossQuest = async function bossQuest (user, progress) {
|
||||
export let tavernQuest = {};
|
||||
let tavernQ = {_id: 'habitrpg', 'quest.key': {$ne: null}};
|
||||
|
||||
// we use process.nextTick because at this point the model is not yet avalaible
|
||||
// we use process.nextTick because at this point the model is not yet available
|
||||
process.nextTick(() => {
|
||||
model // eslint-disable-line no-use-before-define
|
||||
.findOne(tavernQ).exec()
|
||||
|
||||
@@ -6,11 +6,24 @@ var nconf = require('nconf');
|
||||
var i18n = require('../libs/api-v2/i18n');
|
||||
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, locals, dataexport.avatarPage);
|
||||
router.get('/avatar-:uuid.png', i18n.getUserLanguage, locals, dataexport.avatarImage);
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
|
||||
/* Data export deprecated routes */
|
||||
// TODO remove once api v2 is taken down
|
||||
router.get('/history.csv', (req, res) => {
|
||||
res.redirect(`${BASE_URL}/api/v3/export/history.csv`);
|
||||
});
|
||||
router.get('/userdata.xml', (req, res) => {
|
||||
res.redirect(`${BASE_URL}/api/v3/export/userdata.xml`);
|
||||
});
|
||||
router.get('/userdata.json', (req, res) => {
|
||||
res.redirect(`${BASE_URL}/api/v3/export/userdata.json`);
|
||||
});
|
||||
router.get('/avatar-:uuid.html', (req, res) => {
|
||||
res.redirect(`${BASE_URL}/api/v3/export/avatar-${req.params.uuid}.html`);
|
||||
});
|
||||
router.get('/avatar-:uuid.png', (req, res) => {
|
||||
res.redirect(`${BASE_URL}/api/v3/export/avatar-${req.params.uuid}.png`);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -61,7 +61,6 @@ let FacebookStrategy = passportFacebook.Strategy;
|
||||
// have a database of user records, the complete Facebook profile is serialized
|
||||
// and deserialized.
|
||||
passport.serializeUser((user, done) => done(null, user));
|
||||
|
||||
passport.deserializeUser((obj, done) => done(null, obj));
|
||||
|
||||
// FIXME
|
||||
@@ -91,7 +90,9 @@ app.all(/^(?!\/api\/v3).+/i, oldApp);
|
||||
// Matches all requests going to /api/v3
|
||||
app.all('/api/*', newApp);
|
||||
|
||||
// Mount middlewares for the new app
|
||||
// TODO change ^ so that all routes except those marked explictly with api/v2 goes to oldApp
|
||||
|
||||
// Mount middlewares for the new app (api v3)
|
||||
attachMiddlewares(newApp);
|
||||
|
||||
/* OLD APP IS DISABLED UNTIL COMPATIBLE WITH NEW MODELS
|
||||
|
||||
@@ -9,7 +9,7 @@ html(ng-app="habitrpg")
|
||||
meta(name='apple-mobile-web-app-capable', content='yes')
|
||||
|
||||
// .slice(0).push('user') is to clone the array,
|
||||
// to be surethat `user` is never avalaible to other requests' env
|
||||
// to be surethat `user` is never available to other requests' env
|
||||
// TODO does it need only `user` in clientVars, not the others?
|
||||
-
|
||||
clientVars = env.clientVars.slice(0);
|
||||
|
||||
@@ -32,7 +32,7 @@ script(type='text/ng-template', id='partials/options.settings.settings.html')
|
||||
|
||||
.form-horizontal
|
||||
h5=env.t('language')
|
||||
select.form-control(ng-model='language.code', ng-options='lang.code as lang.name for lang in avalaibleLanguages', ng-change='changeLanguage()')
|
||||
select.form-control(ng-model='language.code', ng-options='lang.code as lang.name for lang in availableLanguages', ng-change='changeLanguage()')
|
||||
small
|
||||
!=env.t('americanEnglishGovern')
|
||||
br
|
||||
|
||||
Reference in New Issue
Block a user