mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-15 05:37:22 +01:00
Automatically Logout Banned Users (#12037)
* wip * logout banned users, fix and refactor language library and middleware * req.locals -> res.locals * fix tests * redirect to login page
This commit is contained in:
111
test/api/unit/libs/language.test.js
Normal file
111
test/api/unit/libs/language.test.js
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import {
|
||||||
|
getLanguageFromBrowser,
|
||||||
|
getLanguageFromUser,
|
||||||
|
} from '../../../../website/server/libs/language';
|
||||||
|
import {
|
||||||
|
generateReq,
|
||||||
|
} from '../../../helpers/api-unit.helper';
|
||||||
|
|
||||||
|
describe('language lib', () => {
|
||||||
|
let req;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
req = generateReq();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getLanguageFromUser', () => {
|
||||||
|
it('uses the user preferred language if avalaible', () => {
|
||||||
|
const user = {
|
||||||
|
preferences: {
|
||||||
|
language: 'it',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getLanguageFromUser(user, req)).to.equal('it');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to english if the user preferred language is not avalaible', () => {
|
||||||
|
const user = {
|
||||||
|
preferences: {
|
||||||
|
language: 'bla',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getLanguageFromUser(user, req)).to.equal('en');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getLanguageFromBrowser', () => {
|
||||||
|
it('uses browser specificed language', () => {
|
||||||
|
req.headers['accept-language'] = 'pt';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('pt');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses first language in series if browser specifies multiple', () => {
|
||||||
|
req.headers['accept-language'] = 'he, pt, it';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('he');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skips invalid lanaguages and uses first language in series if browser specifies multiple', () => {
|
||||||
|
req.headers['accept-language'] = 'blah, he, pt, it';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('he');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses normal version of language if specialized locale is passed in', () => {
|
||||||
|
req.headers['accept-language'] = 'fr-CA';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('fr');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses normal version of language if specialized locale is passed in', () => {
|
||||||
|
req.headers['accept-language'] = 'fr-CA';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('fr');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses es if es is passed in', () => {
|
||||||
|
req.headers['accept-language'] = 'es';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('es');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses es_419 if applicable es-languages are passed in', () => {
|
||||||
|
req.headers['accept-language'] = 'es-mx';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('es_419');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses es_419 if multiple es languages are passed in', () => {
|
||||||
|
req.headers['accept-language'] = 'es-GT, es-MX, es-CR';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('es_419');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('zh', () => {
|
||||||
|
req.headers['accept-language'] = 'zh-TW';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('zh_TW');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses english if browser specified language is not compatible', () => {
|
||||||
|
req.headers['accept-language'] = 'blah';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('en');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses english if browser does not specify', () => {
|
||||||
|
req.headers['accept-language'] = '';
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('en');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses english if browser does not supply an accept-language header', () => {
|
||||||
|
delete req.headers['accept-language'];
|
||||||
|
|
||||||
|
expect(getLanguageFromBrowser(req)).to.equal('en');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -19,7 +19,7 @@ describe('analytics middleware', () => {
|
|||||||
next = generateNext();
|
next = generateNext();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('attaches analytics object res.locals', () => {
|
it('attaches analytics object to res', () => {
|
||||||
const attachAnalytics = requireAgain(pathToAnalyticsMiddleware).default;
|
const attachAnalytics = requireAgain(pathToAnalyticsMiddleware).default;
|
||||||
|
|
||||||
attachAnalytics(req, res, next);
|
attachAnalytics(req, res, next);
|
||||||
|
|||||||
@@ -21,28 +21,11 @@ describe('cron middleware', () => {
|
|||||||
req;
|
req;
|
||||||
let user;
|
let user;
|
||||||
|
|
||||||
beforeEach(done => {
|
beforeEach(async () => {
|
||||||
res = generateRes();
|
res = generateRes();
|
||||||
req = generateReq();
|
req = generateReq();
|
||||||
user = new User({
|
user = await res.locals.user.save();
|
||||||
auth: {
|
res.analytics = analyticsService;
|
||||||
local: {
|
|
||||||
username: 'username',
|
|
||||||
lowerCaseUsername: 'username',
|
|
||||||
email: 'email@email.email',
|
|
||||||
salt: 'salt',
|
|
||||||
hashed_password: 'hashed_password', // eslint-disable-line camelcase
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
user.save()
|
|
||||||
.then(savedUser => {
|
|
||||||
res.locals.user = savedUser;
|
|
||||||
res.analytics = analyticsService;
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import { model as User } from '../../../../website/server/models/user';
|
|||||||
|
|
||||||
const { i18n } = common;
|
const { i18n } = common;
|
||||||
|
|
||||||
|
// TODO some of the checks here can be simplified to simply check
|
||||||
|
// that the right parameters are passed to the functions in libs/language
|
||||||
|
|
||||||
describe('language middleware', () => {
|
describe('language middleware', () => {
|
||||||
describe('res.t', () => {
|
describe('res.t', () => {
|
||||||
let res; let req; let
|
let res; let req; let
|
||||||
@@ -19,6 +22,8 @@ describe('language middleware', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
res = generateRes();
|
res = generateRes();
|
||||||
|
// remove the defaul user
|
||||||
|
res.locals.user = undefined;
|
||||||
req = generateReq();
|
req = generateReq();
|
||||||
next = generateNext();
|
next = generateNext();
|
||||||
|
|
||||||
@@ -57,6 +62,8 @@ describe('language middleware', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
res = generateRes();
|
res = generateRes();
|
||||||
|
// remove the defaul user
|
||||||
|
res.locals.user = undefined;
|
||||||
req = generateReq();
|
req = generateReq();
|
||||||
next = generateNext();
|
next = generateNext();
|
||||||
attachTranslateFunction(req, res, next);
|
attachTranslateFunction(req, res, next);
|
||||||
@@ -88,7 +95,7 @@ describe('language middleware', () => {
|
|||||||
lang: 'es',
|
lang: 'es',
|
||||||
};
|
};
|
||||||
|
|
||||||
req.locals = {
|
res.locals = {
|
||||||
user: {
|
user: {
|
||||||
preferences: {
|
preferences: {
|
||||||
language: 'it',
|
language: 'it',
|
||||||
@@ -108,7 +115,7 @@ describe('language middleware', () => {
|
|||||||
|
|
||||||
context('authorized request', () => {
|
context('authorized request', () => {
|
||||||
it('uses the user preferred language if avalaible', () => {
|
it('uses the user preferred language if avalaible', () => {
|
||||||
req.locals = {
|
res.locals = {
|
||||||
user: {
|
user: {
|
||||||
preferences: {
|
preferences: {
|
||||||
language: 'it',
|
language: 'it',
|
||||||
@@ -122,7 +129,7 @@ describe('language middleware', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to english if the user preferred language is not avalaible', done => {
|
it('falls back to english if the user preferred language is not avalaible', done => {
|
||||||
req.locals = {
|
res.locals = {
|
||||||
user: {
|
user: {
|
||||||
preferences: {
|
preferences: {
|
||||||
language: 'bla',
|
language: 'bla',
|
||||||
@@ -138,7 +145,7 @@ describe('language middleware', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('uses the user preferred language even if a session is included in request', () => {
|
it('uses the user preferred language even if a session is included in request', () => {
|
||||||
req.locals = {
|
res.locals = {
|
||||||
user: {
|
user: {
|
||||||
preferences: {
|
preferences: {
|
||||||
language: 'it',
|
language: 'it',
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
'resting': showRestingBanner
|
'resting': showRestingBanner
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<banned-account-modal />
|
<!-- <banned-account-modal /> -->
|
||||||
<amazon-payments-modal v-if="!isStaticPage" />
|
<amazon-payments-modal v-if="!isStaticPage" />
|
||||||
<payments-success-modal />
|
<payments-success-modal />
|
||||||
<sub-cancel-modal-confirm v-if="isUserLoaded" />
|
<sub-cancel-modal-confirm v-if="isUserLoaded" />
|
||||||
@@ -266,7 +266,6 @@ import {
|
|||||||
} from '@/libs/userlocalManager';
|
} from '@/libs/userlocalManager';
|
||||||
|
|
||||||
import svgClose from '@/assets/svg/close.svg';
|
import svgClose from '@/assets/svg/close.svg';
|
||||||
import bannedAccountModal from '@/components/bannedAccountModal';
|
|
||||||
|
|
||||||
const COMMUNITY_MANAGER_EMAIL = process.env.EMAILS_COMMUNITY_MANAGER_EMAIL; // eslint-disable-line
|
const COMMUNITY_MANAGER_EMAIL = process.env.EMAILS_COMMUNITY_MANAGER_EMAIL; // eslint-disable-line
|
||||||
|
|
||||||
@@ -281,7 +280,6 @@ export default {
|
|||||||
BuyModal,
|
BuyModal,
|
||||||
SelectMembersModal,
|
SelectMembersModal,
|
||||||
amazonPaymentsModal,
|
amazonPaymentsModal,
|
||||||
bannedAccountModal,
|
|
||||||
paymentsSuccessModal,
|
paymentsSuccessModal,
|
||||||
subCancelModalConfirm,
|
subCancelModalConfirm,
|
||||||
subCanceledModal,
|
subCanceledModal,
|
||||||
@@ -385,7 +383,8 @@ export default {
|
|||||||
return response;
|
return response;
|
||||||
}, error => {
|
}, error => {
|
||||||
if (error.response.status >= 400) {
|
if (error.response.status >= 400) {
|
||||||
this.checkForBannedUser(error);
|
const isBanned = this.checkForBannedUser(error);
|
||||||
|
if (isBanned === true) return null; // eslint-disable-line consistent-return
|
||||||
|
|
||||||
// Don't show errors from getting user details. These users have delete their account,
|
// Don't show errors from getting user details. These users have delete their account,
|
||||||
// but their chat message still exists.
|
// but their chat message still exists.
|
||||||
@@ -403,7 +402,8 @@ export default {
|
|||||||
// TODO use a specific error like NotificationNotFound instead of checking for the string
|
// TODO use a specific error like NotificationNotFound instead of checking for the string
|
||||||
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
||||||
if (invalidUserMessage.indexOf(errorMessage) !== -1) {
|
if (invalidUserMessage.indexOf(errorMessage) !== -1) {
|
||||||
this.$store.dispatch('auth:logout');
|
this.$store.dispatch('auth:logout', { redirectToLogin: true });
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Most server errors should return is click to dismiss errors, with some exceptions
|
// Most server errors should return is click to dismiss errors, with some exceptions
|
||||||
@@ -553,7 +553,7 @@ export default {
|
|||||||
|
|
||||||
// Case where user is not logged in
|
// Case where user is not logged in
|
||||||
if (!parseSettings) {
|
if (!parseSettings) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bannedMessage = this.$t('accountSuspended', {
|
const bannedMessage = this.$t('accountSuspended', {
|
||||||
@@ -561,9 +561,10 @@ export default {
|
|||||||
userId: parseSettings.auth.apiId,
|
userId: parseSettings.auth.apiId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (errorMessage !== bannedMessage) return;
|
if (errorMessage !== bannedMessage) return false;
|
||||||
|
|
||||||
this.$root.$emit('bv::show::modal', 'banned-account');
|
this.$store.dispatch('auth:logout', { redirectToLogin: true });
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
initializeModalStack () {
|
initializeModalStack () {
|
||||||
// Manage modals
|
// Manage modals
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ export async function socialAuth (store, params) {
|
|||||||
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
|
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logout () {
|
export function logout (store, options = {}) {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
window.location.href = '/logout-server';
|
const query = options.redirectToLogin === true ? '?redirectToLogin=true' : '';
|
||||||
|
window.location.href = `/logout-server${query}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ api.logout = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
if (req.logout) req.logout(); // passportjs method
|
if (req.logout) req.logout(); // passportjs method
|
||||||
req.session = null;
|
req.session = null;
|
||||||
res.redirect('/');
|
|
||||||
|
const redirectUrl = req.query.redirectToLogin === 'true' ? '/login' : '/';
|
||||||
|
res.redirect(redirectUrl);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,34 +22,10 @@ const momentLangsMapping = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const approvedLanguages = [
|
export const approvedLanguages = [
|
||||||
'bg',
|
'bg', 'cs', 'da', 'de', 'en', 'en_GB', 'en@pirate',
|
||||||
'cs',
|
'es', 'es_419', 'fr', 'he', 'hu', 'id', 'it',
|
||||||
'da',
|
'ja', 'nl', 'pl', 'pt', 'pt_BR', 'ro', 'ru', 'sk',
|
||||||
'de',
|
'sr', 'sv', 'tr', 'uk', 'zh', 'zh_TW',
|
||||||
'en',
|
|
||||||
'en_GB',
|
|
||||||
'en@pirate',
|
|
||||||
'es',
|
|
||||||
'es_419',
|
|
||||||
'fr',
|
|
||||||
'he',
|
|
||||||
'hu',
|
|
||||||
'id',
|
|
||||||
'it',
|
|
||||||
'ja',
|
|
||||||
'nl',
|
|
||||||
'pl',
|
|
||||||
'pt',
|
|
||||||
'pt_BR',
|
|
||||||
'ro',
|
|
||||||
'ru',
|
|
||||||
'sk',
|
|
||||||
'sr',
|
|
||||||
'sv',
|
|
||||||
'tr',
|
|
||||||
'uk',
|
|
||||||
'zh',
|
|
||||||
'zh_TW',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function _loadTranslations (locale) {
|
function _loadTranslations (locale) {
|
||||||
|
|||||||
52
website/server/libs/language.js
Normal file
52
website/server/libs/language.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import accepts from 'accepts';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import {
|
||||||
|
translations,
|
||||||
|
defaultLangCodes,
|
||||||
|
multipleVersionsLanguages,
|
||||||
|
} from './i18n';
|
||||||
|
|
||||||
|
function getUniqueListOfLanguages (languages) {
|
||||||
|
const acceptableLanguages = _(languages).map(lang => lang.slice(0, 2)).uniq().value();
|
||||||
|
|
||||||
|
const uniqueListOfLanguages = _.intersection(acceptableLanguages, defaultLangCodes);
|
||||||
|
|
||||||
|
return uniqueListOfLanguages;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkForApplicableLanguageVariant (originalLanguageOptions) {
|
||||||
|
const languageVariant = _.find(originalLanguageOptions, accepted => {
|
||||||
|
const trimmedAccepted = accepted.slice(0, 2);
|
||||||
|
|
||||||
|
return multipleVersionsLanguages[trimmedAccepted];
|
||||||
|
});
|
||||||
|
|
||||||
|
return languageVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLanguageFromBrowser (req) {
|
||||||
|
const originalLanguageOptions = accepts(req).languages();
|
||||||
|
const uniqueListOfLanguages = getUniqueListOfLanguages(originalLanguageOptions);
|
||||||
|
const baseLanguage = (uniqueListOfLanguages[0] || '').toLowerCase();
|
||||||
|
const languageMapping = multipleVersionsLanguages[baseLanguage];
|
||||||
|
|
||||||
|
if (languageMapping) {
|
||||||
|
let languageVariant = checkForApplicableLanguageVariant(originalLanguageOptions);
|
||||||
|
|
||||||
|
if (languageVariant) {
|
||||||
|
languageVariant = languageVariant.toLowerCase();
|
||||||
|
} else {
|
||||||
|
return 'en';
|
||||||
|
}
|
||||||
|
|
||||||
|
return languageMapping[languageVariant] || baseLanguage;
|
||||||
|
}
|
||||||
|
return baseLanguage || 'en';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLanguageFromUser (user, req) {
|
||||||
|
const preferredLang = user && user.preferences && user.preferences.language;
|
||||||
|
const lang = translations[preferredLang] ? preferredLang : getLanguageFromBrowser(req);
|
||||||
|
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ import {
|
|||||||
model as User,
|
model as User,
|
||||||
} from '../models/user';
|
} from '../models/user';
|
||||||
import gcpStackdriverTracer from '../libs/gcpTraceAgent';
|
import gcpStackdriverTracer from '../libs/gcpTraceAgent';
|
||||||
|
import common from '../../common';
|
||||||
|
import { getLanguageFromUser } from '../libs/language';
|
||||||
|
|
||||||
const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL');
|
const COMMUNITY_MANAGER_EMAIL = nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL');
|
||||||
const USER_FIELDS_ALWAYS_LOADED = ['_id', 'notifications', 'preferences', 'auth', 'flags'];
|
const USER_FIELDS_ALWAYS_LOADED = ['_id', 'notifications', 'preferences', 'auth', 'flags'];
|
||||||
@@ -72,7 +74,17 @@ export function authWithHeaders (options = {}) {
|
|||||||
.exec()
|
.exec()
|
||||||
.then(user => {
|
.then(user => {
|
||||||
if (!user) throw new NotAuthorized(res.t('invalidCredentials'));
|
if (!user) throw new NotAuthorized(res.t('invalidCredentials'));
|
||||||
if (user.auth.blocked) throw new NotAuthorized(res.t('accountSuspended', { communityManagerEmail: COMMUNITY_MANAGER_EMAIL, userId: user._id }));
|
|
||||||
|
if (user.auth.blocked) {
|
||||||
|
// We want the accountSuspended message to be translated but the language
|
||||||
|
// middleware hasn't run yet so we pick it manually
|
||||||
|
const language = getLanguageFromUser(user, req);
|
||||||
|
|
||||||
|
throw new NotAuthorized(common.i18n.t('accountSuspended', {
|
||||||
|
communityManagerEmail: COMMUNITY_MANAGER_EMAIL,
|
||||||
|
userId: user._id,
|
||||||
|
}, language));
|
||||||
|
}
|
||||||
|
|
||||||
res.locals.user = user;
|
res.locals.user = user;
|
||||||
req.session.userId = user._id;
|
req.session.userId = user._id;
|
||||||
|
|||||||
@@ -1,60 +1,15 @@
|
|||||||
import accepts from 'accepts';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import { model as User } from '../models/user';
|
import { model as User } from '../models/user';
|
||||||
import common from '../../common';
|
import common from '../../common';
|
||||||
import {
|
import {
|
||||||
translations,
|
translations,
|
||||||
defaultLangCodes,
|
|
||||||
multipleVersionsLanguages,
|
|
||||||
} from '../libs/i18n';
|
} from '../libs/i18n';
|
||||||
|
import {
|
||||||
|
getLanguageFromUser,
|
||||||
|
getLanguageFromBrowser,
|
||||||
|
} from '../libs/language';
|
||||||
|
|
||||||
const { i18n } = common;
|
const { i18n } = common;
|
||||||
|
|
||||||
function _getUniqueListOfLanguages (languages) {
|
|
||||||
const acceptableLanguages = _(languages).map(lang => lang.slice(0, 2)).uniq().value();
|
|
||||||
|
|
||||||
const uniqueListOfLanguages = _.intersection(acceptableLanguages, defaultLangCodes);
|
|
||||||
|
|
||||||
return uniqueListOfLanguages;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _checkForApplicableLanguageVariant (originalLanguageOptions) {
|
|
||||||
const languageVariant = _.find(originalLanguageOptions, accepted => {
|
|
||||||
const trimmedAccepted = accepted.slice(0, 2);
|
|
||||||
|
|
||||||
return multipleVersionsLanguages[trimmedAccepted];
|
|
||||||
});
|
|
||||||
|
|
||||||
return languageVariant;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getFromBrowser (req) {
|
|
||||||
const originalLanguageOptions = accepts(req).languages();
|
|
||||||
const uniqueListOfLanguages = _getUniqueListOfLanguages(originalLanguageOptions);
|
|
||||||
const baseLanguage = (uniqueListOfLanguages[0] || '').toLowerCase();
|
|
||||||
const languageMapping = multipleVersionsLanguages[baseLanguage];
|
|
||||||
|
|
||||||
if (languageMapping) {
|
|
||||||
let languageVariant = _checkForApplicableLanguageVariant(originalLanguageOptions);
|
|
||||||
|
|
||||||
if (languageVariant) {
|
|
||||||
languageVariant = languageVariant.toLowerCase();
|
|
||||||
} else {
|
|
||||||
return 'en';
|
|
||||||
}
|
|
||||||
|
|
||||||
return languageMapping[languageVariant] || baseLanguage;
|
|
||||||
}
|
|
||||||
return baseLanguage || 'en';
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getFromUser (user, req) {
|
|
||||||
const preferredLang = user && user.preferences && user.preferences.language;
|
|
||||||
const lang = translations[preferredLang] ? preferredLang : _getFromBrowser(req);
|
|
||||||
|
|
||||||
return lang;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function attachTranslateFunction (req, res, next) {
|
export function attachTranslateFunction (req, res, next) {
|
||||||
res.t = function reqTranslation (...args) {
|
res.t = function reqTranslation (...args) {
|
||||||
return i18n.t(...args, req.language);
|
return i18n.t(...args, req.language);
|
||||||
@@ -64,26 +19,33 @@ export function attachTranslateFunction (req, res, next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getUserLanguage (req, res, next) {
|
export function getUserLanguage (req, res, next) {
|
||||||
if (req.query.lang) { // In case the language is specified in the request url, use it
|
// In case the language is specified in the request url, use intersection
|
||||||
|
if (req.query.lang) {
|
||||||
req.language = translations[req.query.lang] ? req.query.lang : 'en';
|
req.language = translations[req.query.lang] ? req.query.lang : 'en';
|
||||||
return next();
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
// If the request is authenticated, use the user's preferred language
|
// If the request is authenticated, use the user's preferred language
|
||||||
} if (req.locals && req.locals.user) {
|
if (res.locals && res.locals.user) {
|
||||||
req.language = _getFromUser(req.locals.user, req);
|
req.language = getLanguageFromUser(res.locals.user, req);
|
||||||
return next();
|
return next();
|
||||||
} if (req.session && req.session.userId) { // Same thing if the user has a valid session
|
}
|
||||||
|
|
||||||
|
// Same thing if the user has a valid session
|
||||||
|
if (req.session && req.session.userId) {
|
||||||
return User.findOne({
|
return User.findOne({
|
||||||
_id: req.session.userId,
|
_id: req.session.userId,
|
||||||
}, 'preferences.language')
|
}, 'preferences.language')
|
||||||
.lean()
|
.lean()
|
||||||
.exec()
|
.exec()
|
||||||
.then(user => {
|
.then(user => {
|
||||||
req.language = _getFromUser(user, req);
|
req.language = getLanguageFromUser(user, req);
|
||||||
return next();
|
return next();
|
||||||
})
|
})
|
||||||
.catch(next);
|
.catch(next);
|
||||||
} // Otherwise get from browser
|
}
|
||||||
req.language = _getFromUser(null, req);
|
|
||||||
|
// Otherwise get from browser
|
||||||
|
req.language = getLanguageFromBrowser(req);
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user