mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-05-21 21:28:52 +00:00
Merge branch 'develop' into release
This commit is contained in:
commit
3ac69d5e75
34 changed files with 618 additions and 250 deletions
69
migrations/archive/2020/20200402_webhooks_add_protocol.js
Normal file
69
migrations/archive/2020/20200402_webhooks_add_protocol.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20200402_webhooks_add_protocol';
|
||||
import { model as User } from '../../../website/server/models/user';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
|
||||
const set = {
|
||||
migration: MIGRATION_NAME,
|
||||
};
|
||||
|
||||
if (user && user.webhooks && user.webhooks.length > 0) {
|
||||
user.webhooks.forEach(webhook => {
|
||||
// Make sure the protocol is set and valid
|
||||
if (webhook.url.startsWith('ftp')) {
|
||||
webhook.url = webhook.url.replace('ftp', 'https');
|
||||
}
|
||||
|
||||
if (!webhook.url.startsWith('http://') && !webhook.url.startsWith('https://')) {
|
||||
// the default in got 9 was https
|
||||
// see https://github.com/sindresorhus/got/commit/92bc8082137d7d085750359bbd76c801e213d7d2#diff-0730bb7c2e8f9ea2438b52e419dd86c9L111
|
||||
webhook.url = `https://${webhook.url}`;
|
||||
}
|
||||
});
|
||||
|
||||
set.webhooks = user.webhooks;
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
webhooks: { $exists: true, $not: { $size: 0 } },
|
||||
};
|
||||
|
||||
const fields = {
|
||||
_id: 1,
|
||||
webhooks: 1,
|
||||
};
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
break;
|
||||
} else {
|
||||
query._id = {
|
||||
$gt: users[users.length - 1]._id,
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
63
migrations/archive/2020/20200402_webhooks_reenable.js
Normal file
63
migrations/archive/2020/20200402_webhooks_reenable.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20200402_webhooks_reenable';
|
||||
import { model as User } from '../../../website/server/models/user';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
|
||||
const set = {
|
||||
migration: MIGRATION_NAME,
|
||||
};
|
||||
|
||||
if (user && user.webhooks && user.webhooks.length > 0) {
|
||||
user.webhooks.forEach(webhook => {
|
||||
// Re-enable webhooks disabled because of too many failures
|
||||
if (webhook.enabled === false && webhook.lastFailureAt === null) {
|
||||
webhook.enabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
set.webhooks = user.webhooks;
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
webhooks: { $exists: true, $not: { $size: 0 } },
|
||||
};
|
||||
|
||||
const fields = {
|
||||
_id: 1,
|
||||
webhooks: 1,
|
||||
};
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
break;
|
||||
} else {
|
||||
query._id = {
|
||||
$gt: users[users.length - 1]._id,
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
209
package-lock.json
generated
209
package-lock.json
generated
|
|
@ -1360,9 +1360,9 @@
|
|||
}
|
||||
},
|
||||
"@sindresorhus/is": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
|
||||
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ=="
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.0.tgz",
|
||||
"integrity": "sha512-lXKXfypKo644k4Da4yXkPCrwcvn6SlUW2X2zFbuflKHNjf0w9htru01bo26uMhleMXsDmnZ12eJLdrAZa9MANg=="
|
||||
},
|
||||
"@sinonjs/commons": {
|
||||
"version": "1.6.0",
|
||||
|
|
@ -1479,11 +1479,22 @@
|
|||
}
|
||||
},
|
||||
"@szmarczak/http-timer": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
|
||||
"integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
|
||||
"integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
|
||||
"requires": {
|
||||
"defer-to-connect": "^1.0.1"
|
||||
"defer-to-connect": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@types/cacheable-request": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
|
||||
"integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
|
||||
"requires": {
|
||||
"@types/http-cache-semantics": "*",
|
||||
"@types/keyv": "*",
|
||||
"@types/node": "*",
|
||||
"@types/responselike": "*"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
|
|
@ -1507,6 +1518,19 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/http-cache-semantics": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
|
||||
"integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A=="
|
||||
},
|
||||
"@types/keyv": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
|
||||
"integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
|
|
@ -1523,6 +1547,14 @@
|
|||
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==",
|
||||
"optional": true
|
||||
},
|
||||
"@types/responselike": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
|
||||
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
|
|
@ -3134,18 +3166,27 @@
|
|||
"unset-value": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"cacheable-lookup": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz",
|
||||
"integrity": "sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==",
|
||||
"requires": {
|
||||
"@types/keyv": "^3.1.1",
|
||||
"keyv": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"cacheable-request": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
|
||||
"integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz",
|
||||
"integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==",
|
||||
"requires": {
|
||||
"clone-response": "^1.0.2",
|
||||
"get-stream": "^5.1.0",
|
||||
"http-cache-semantics": "^4.0.0",
|
||||
"keyv": "^3.0.0",
|
||||
"keyv": "^4.0.0",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"normalize-url": "^4.1.0",
|
||||
"responselike": "^1.0.2"
|
||||
"responselike": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"get-stream": {
|
||||
|
|
@ -3160,6 +3201,14 @@
|
|||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
|
||||
},
|
||||
"responselike": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
|
||||
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
|
||||
"requires": {
|
||||
"lowercase-keys": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -4183,6 +4232,7 @@
|
|||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
|
||||
"integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"mimic-response": "^1.0.0"
|
||||
}
|
||||
|
|
@ -4360,9 +4410,9 @@
|
|||
"integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ="
|
||||
},
|
||||
"defer-to-connect": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz",
|
||||
"integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw=="
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz",
|
||||
"integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg=="
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.1.3",
|
||||
|
|
@ -6754,6 +6804,7 @@
|
|||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
|
||||
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
|
|
@ -7024,21 +7075,82 @@
|
|||
}
|
||||
},
|
||||
"got": {
|
||||
"version": "9.6.0",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
|
||||
"integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
|
||||
"version": "10.7.0",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-10.7.0.tgz",
|
||||
"integrity": "sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==",
|
||||
"requires": {
|
||||
"@sindresorhus/is": "^0.14.0",
|
||||
"@szmarczak/http-timer": "^1.1.2",
|
||||
"cacheable-request": "^6.0.0",
|
||||
"decompress-response": "^3.3.0",
|
||||
"@sindresorhus/is": "^2.0.0",
|
||||
"@szmarczak/http-timer": "^4.0.0",
|
||||
"@types/cacheable-request": "^6.0.1",
|
||||
"cacheable-lookup": "^2.0.0",
|
||||
"cacheable-request": "^7.0.1",
|
||||
"decompress-response": "^5.0.0",
|
||||
"duplexer3": "^0.1.4",
|
||||
"get-stream": "^4.1.0",
|
||||
"lowercase-keys": "^1.0.1",
|
||||
"mimic-response": "^1.0.1",
|
||||
"p-cancelable": "^1.0.0",
|
||||
"to-readable-stream": "^1.0.0",
|
||||
"url-parse-lax": "^3.0.0"
|
||||
"get-stream": "^5.0.0",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"mimic-response": "^2.1.0",
|
||||
"p-cancelable": "^2.0.0",
|
||||
"p-event": "^4.0.0",
|
||||
"responselike": "^2.0.0",
|
||||
"to-readable-stream": "^2.0.0",
|
||||
"type-fest": "^0.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"decompress-response": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz",
|
||||
"integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==",
|
||||
"requires": {
|
||||
"mimic-response": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
|
||||
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lowercase-keys": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
|
||||
},
|
||||
"mimic-response": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
||||
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
|
||||
},
|
||||
"p-event": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-event/-/p-event-4.1.0.tgz",
|
||||
"integrity": "sha512-4vAd06GCsgflX4wHN1JqrMzBh/8QZ4j+rzp0cd2scXRwuBEv+QR3wrVA5aLhWDLw4y2WgDKvzWF3CCLmVM1UgA==",
|
||||
"requires": {
|
||||
"p-timeout": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"p-timeout": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
|
||||
"integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==",
|
||||
"requires": {
|
||||
"p-finally": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"responselike": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
|
||||
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
|
||||
"requires": {
|
||||
"lowercase-keys": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz",
|
||||
"integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
|
|
@ -7575,9 +7687,9 @@
|
|||
}
|
||||
},
|
||||
"http-cache-semantics": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz",
|
||||
"integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew=="
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.7.2",
|
||||
|
|
@ -8681,7 +8793,8 @@
|
|||
"json-buffer": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
|
||||
"integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg="
|
||||
"integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=",
|
||||
"optional": true
|
||||
},
|
||||
"json-content-demux": {
|
||||
"version": "0.1.3",
|
||||
|
|
@ -8825,11 +8938,18 @@
|
|||
}
|
||||
},
|
||||
"keyv": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
|
||||
"integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.0.tgz",
|
||||
"integrity": "sha512-U7ioE8AimvRVLfw4LffyOIRhL2xVgmE8T22L6i0BucSnBUyv4w+I7VN/zVZwRKHOI6ZRUcdMdWHQ8KSUvGpEog==",
|
||||
"requires": {
|
||||
"json-buffer": "3.0.0"
|
||||
"json-buffer": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind-of": {
|
||||
|
|
@ -10707,9 +10827,9 @@
|
|||
}
|
||||
},
|
||||
"p-cancelable": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
|
||||
"integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw=="
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz",
|
||||
"integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg=="
|
||||
},
|
||||
"p-defer": {
|
||||
"version": "1.0.0",
|
||||
|
|
@ -11254,7 +11374,8 @@
|
|||
"prepend-http": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
|
||||
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
|
||||
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
|
||||
"optional": true
|
||||
},
|
||||
"pretty-bytes": {
|
||||
"version": "5.3.0",
|
||||
|
|
@ -11867,6 +11988,7 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
|
||||
"integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"lowercase-keys": "^1.0.0"
|
||||
}
|
||||
|
|
@ -13177,9 +13299,9 @@
|
|||
}
|
||||
},
|
||||
"to-readable-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q=="
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz",
|
||||
"integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w=="
|
||||
},
|
||||
"to-regex": {
|
||||
"version": "3.0.2",
|
||||
|
|
@ -13783,6 +13905,7 @@
|
|||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
|
||||
"integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"prepend-http": "^2.0.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
"express-basic-auth": "^1.1.5",
|
||||
"express-validator": "^5.2.0",
|
||||
"glob": "^7.1.6",
|
||||
"got": "^9.0.0",
|
||||
"got": "^10.7.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-babel": "^8.0.0",
|
||||
"gulp-imagemin": "^6.2.0",
|
||||
|
|
|
|||
|
|
@ -121,8 +121,7 @@ describe('emails', () => {
|
|||
|
||||
sendTxnEmail(mailingInfo, emailType);
|
||||
expect(got.post).to.be.calledWith('undefined/job', sinon.match({
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
data: {
|
||||
emailType: sinon.match.same(emailType),
|
||||
to: sinon.match(value => Array.isArray(value) && value[0].name === mailingInfo.name, 'matches mailing info array'),
|
||||
|
|
@ -154,8 +153,7 @@ describe('emails', () => {
|
|||
|
||||
sendTxnEmail(mailingInfo, emailType);
|
||||
expect(got.post).to.be.calledWith('undefined/job', sinon.match({
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
data: {
|
||||
emailType: sinon.match.same(emailType),
|
||||
to: sinon.match(val => val[0]._id === mailingInfo._id),
|
||||
|
|
@ -177,8 +175,7 @@ describe('emails', () => {
|
|||
|
||||
sendTxnEmail(mailingInfo, emailType, variables);
|
||||
expect(got.post).to.be.calledWith('undefined/job', sinon.match({
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
data: {
|
||||
variables: sinon.match(value => value[0].name === 'BASE_URL', 'matches variables'),
|
||||
personalVariables: sinon.match(value => value[0].rcpt === mailingInfo.email
|
||||
|
|
|
|||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -101,8 +101,7 @@ describe('webhooks', () => {
|
|||
expect(WebhookSender.defaultTransformData).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
json: true,
|
||||
body,
|
||||
json: body,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -122,7 +121,7 @@ describe('webhooks', () => {
|
|||
expect(sendWebhook.attachDefaultData).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
json: true,
|
||||
json: body,
|
||||
});
|
||||
|
||||
expect(body).to.eql({
|
||||
|
|
@ -153,8 +152,7 @@ describe('webhooks', () => {
|
|||
expect(WebhookSender.defaultTransformData).to.not.be.called;
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
foo: 'bar',
|
||||
baz: 'biz',
|
||||
},
|
||||
|
|
@ -271,8 +269,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
body,
|
||||
json: true,
|
||||
json: body,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -292,8 +289,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
body,
|
||||
json: true,
|
||||
json: body,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -316,12 +312,10 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledTwice;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
body,
|
||||
json: true,
|
||||
json: body,
|
||||
});
|
||||
expect(got.post).to.be.calledWithMatch('http://other-url.com', {
|
||||
body,
|
||||
json: true,
|
||||
json: body,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -351,8 +345,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
json: true,
|
||||
body,
|
||||
json: body,
|
||||
});
|
||||
|
||||
await sleep(0.1);
|
||||
|
|
@ -368,8 +361,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://custom-url.com', {
|
||||
json: true,
|
||||
body,
|
||||
json: body,
|
||||
});
|
||||
|
||||
await sleep(0.1);
|
||||
|
|
@ -459,8 +451,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[0].url, {
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
type: 'scored',
|
||||
webhookType: 'taskActivity',
|
||||
user: {
|
||||
|
|
@ -497,8 +488,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch('http://global-activity.com', {
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
type: 'scored',
|
||||
webhookType: 'taskActivity',
|
||||
user: {
|
||||
|
|
@ -551,8 +541,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[0].url, {
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
type,
|
||||
webhookType: 'taskActivity',
|
||||
user: {
|
||||
|
|
@ -592,8 +581,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[0].url, {
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
webhookType: 'taskActivity',
|
||||
user: {
|
||||
_id: user._id,
|
||||
|
|
@ -633,8 +621,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[2].url, {
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
type,
|
||||
webhookType: 'userActivity',
|
||||
user: {
|
||||
|
|
@ -680,8 +667,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[1].url, {
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
type,
|
||||
webhookType: 'questActivity',
|
||||
user: {
|
||||
|
|
@ -727,8 +713,7 @@ describe('webhooks', () => {
|
|||
|
||||
expect(got.post).to.be.calledOnce;
|
||||
expect(got.post).to.be.calledWithMatch(webhooks[webhooks.length - 1].url, {
|
||||
json: true,
|
||||
body: {
|
||||
json: {
|
||||
webhookType: 'groupChatReceived',
|
||||
user: {
|
||||
_id: user._id,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ describe('analytics middleware', () => {
|
|||
next = generateNext();
|
||||
});
|
||||
|
||||
it('attaches analytics object res.locals', () => {
|
||||
it('attaches analytics object to res', () => {
|
||||
const attachAnalytics = requireAgain(pathToAnalyticsMiddleware).default;
|
||||
|
||||
attachAnalytics(req, res, next);
|
||||
|
|
|
|||
|
|
@ -21,28 +21,11 @@ describe('cron middleware', () => {
|
|||
req;
|
||||
let user;
|
||||
|
||||
beforeEach(done => {
|
||||
beforeEach(async () => {
|
||||
res = generateRes();
|
||||
req = generateReq();
|
||||
user = new User({
|
||||
auth: {
|
||||
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);
|
||||
user = await res.locals.user.save();
|
||||
res.analytics = analyticsService;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ import { model as User } from '../../../../website/server/models/user';
|
|||
|
||||
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('res.t', () => {
|
||||
let res; let req; let
|
||||
|
|
@ -19,6 +22,8 @@ describe('language middleware', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
res = generateRes();
|
||||
// remove the defaul user
|
||||
res.locals.user = undefined;
|
||||
req = generateReq();
|
||||
next = generateNext();
|
||||
|
||||
|
|
@ -57,6 +62,8 @@ describe('language middleware', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
res = generateRes();
|
||||
// remove the defaul user
|
||||
res.locals.user = undefined;
|
||||
req = generateReq();
|
||||
next = generateNext();
|
||||
attachTranslateFunction(req, res, next);
|
||||
|
|
@ -88,7 +95,7 @@ describe('language middleware', () => {
|
|||
lang: 'es',
|
||||
};
|
||||
|
||||
req.locals = {
|
||||
res.locals = {
|
||||
user: {
|
||||
preferences: {
|
||||
language: 'it',
|
||||
|
|
@ -108,7 +115,7 @@ describe('language middleware', () => {
|
|||
|
||||
context('authorized request', () => {
|
||||
it('uses the user preferred language if avalaible', () => {
|
||||
req.locals = {
|
||||
res.locals = {
|
||||
user: {
|
||||
preferences: {
|
||||
language: 'it',
|
||||
|
|
@ -122,7 +129,7 @@ describe('language middleware', () => {
|
|||
});
|
||||
|
||||
it('falls back to english if the user preferred language is not avalaible', done => {
|
||||
req.locals = {
|
||||
res.locals = {
|
||||
user: {
|
||||
preferences: {
|
||||
language: 'bla',
|
||||
|
|
@ -138,7 +145,7 @@ describe('language middleware', () => {
|
|||
});
|
||||
|
||||
it('uses the user preferred language even if a session is included in request', () => {
|
||||
req.locals = {
|
||||
res.locals = {
|
||||
user: {
|
||||
preferences: {
|
||||
language: 'it',
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
'resting': showRestingBanner
|
||||
}"
|
||||
>
|
||||
<banned-account-modal />
|
||||
<!-- <banned-account-modal /> -->
|
||||
<amazon-payments-modal v-if="!isStaticPage" />
|
||||
<payments-success-modal />
|
||||
<sub-cancel-modal-confirm v-if="isUserLoaded" />
|
||||
|
|
@ -266,7 +266,6 @@ import {
|
|||
} from '@/libs/userlocalManager';
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -281,7 +280,6 @@ export default {
|
|||
BuyModal,
|
||||
SelectMembersModal,
|
||||
amazonPaymentsModal,
|
||||
bannedAccountModal,
|
||||
paymentsSuccessModal,
|
||||
subCancelModalConfirm,
|
||||
subCanceledModal,
|
||||
|
|
@ -385,7 +383,8 @@ export default {
|
|||
return response;
|
||||
}, error => {
|
||||
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,
|
||||
// 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
|
||||
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
||||
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
|
||||
|
|
@ -553,7 +553,7 @@ export default {
|
|||
|
||||
// Case where user is not logged in
|
||||
if (!parseSettings) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
const bannedMessage = this.$t('accountSuspended', {
|
||||
|
|
@ -561,9 +561,10 @@ export default {
|
|||
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 () {
|
||||
// Manage modals
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ export default {
|
|||
return this.overrideTopPadding;
|
||||
}
|
||||
|
||||
let val = '27px';
|
||||
let val = '24px';
|
||||
|
||||
if (!this.avatarOnly) {
|
||||
if (this.member.items.currentPet) val = '24px';
|
||||
|
|
|
|||
|
|
@ -348,6 +348,7 @@
|
|||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="achievementsCategories[key].number > 5"
|
||||
class="btn btn-flat btn-show-more"
|
||||
|
|
@ -358,7 +359,6 @@
|
|||
$t('showAllAchievements', {category: $t(key+'Achievs')})
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="col-12">
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@ export async function socialAuth (store, params) {
|
|||
localStorage.setItem(LOCALSTORAGE_AUTH_KEY, userLocalData);
|
||||
}
|
||||
|
||||
export function logout () {
|
||||
export function logout (store, options = {}) {
|
||||
localStorage.clear();
|
||||
window.location.href = '/logout-server';
|
||||
const query = options.redirectToLogin === true ? '?redirectToLogin=true' : '';
|
||||
window.location.href = `/logout-server${query}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ context('avatar.vue', () => {
|
|||
};
|
||||
});
|
||||
|
||||
it('defaults to 27px', () => {
|
||||
xit('defaults to 27px', () => {
|
||||
vm.avatarOnly = true;
|
||||
expect(vm.paddingTop).to.equal('27px');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@
|
|||
"achievementTickledPink": "Rosige Bäckchen",
|
||||
"foundNewItems": "Du hast neue Gegenstände gefunden!",
|
||||
"foundNewItemsCTA": "Schau in Dein Inventar und versuche, Dein neues Schlüpfelixier mit einem Ei zu kombinieren!",
|
||||
"foundNewItemsExplanation": "Durch das Abschließen von Aufgaben erhältst Du die Chance Gegenstände, wie etwa Eier, Schlüpfelixiere und Futter, zu finden.",
|
||||
"achievementBugBonanza": "Kostbarer Käfer"
|
||||
"foundNewItemsExplanation": "Durch das Abschließen von Aufgaben erhältst Du die Chance, Gegenstände wie etwa Eier, Schlüpfelixiere und Futter zu finden.",
|
||||
"achievementBugBonanza": "Kostbarer Käfer",
|
||||
"achievementBugBonanzaModalText": "Du hast die Käfer-, Schmetterling-, Schnecken- und Spinnenhaustier-Quests erfüllt!",
|
||||
"achievementBugBonanzaText": "Hat die Käfer-, Schmetterling-, Schnecken- und Spinnenhaustier-Quests erfüllt."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"armoireText": "Verzauberter Schrank",
|
||||
"armoireNotesFull": "Öffne den Schrank, um zufällig spezielle Ausrüstung, Erfahrung oder Futter zu erhalten! Verbleibende Ausrüstungsgegenstände:",
|
||||
"armoireLastItem": "Du hast das letzte Stück seltener Ausrüstung im verzauberten Schrank gefunden.",
|
||||
"armoireNotesEmpty": "Im verzauberten Schrank gibt es jeweils in der ersten Woche eines Monats neue Ausrüstung. Bis dahin, klicke weiter für Erfahrung und Futter!",
|
||||
"armoireNotesEmpty": "Im verzauberten Schrank gibt es jeweils in der ersten Woche des Monats neue Ausrüstung. Bis dahin klicke weiter für Erfahrung und Futter!",
|
||||
"dropEggWolfText": "Wolfsjunges",
|
||||
"dropEggWolfMountText": "Wolfs-Reittier",
|
||||
"dropEggWolfAdjective": "ein treues",
|
||||
|
|
@ -354,5 +354,6 @@
|
|||
"premiumPotionUnlimitedNotes": "Nicht auf Eier von Quest-Haustieren anwendbar.",
|
||||
"hatchingPotionAmber": "Bernstein",
|
||||
"hatchingPotionAurora": "Polarlicht",
|
||||
"hatchingPotionRuby": "Rubinrotes"
|
||||
"hatchingPotionRuby": "Rubinrotes",
|
||||
"hatchingPotionBirchBark": "Birkenborke"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,45 +85,45 @@
|
|||
"scarecrowWarriorSet": "Vogelscheuchenkrieger (Krieger)",
|
||||
"stitchWitchSet": "Stichhexe (Magier)",
|
||||
"potionerSet": "Tränkebrauer (Heiler)",
|
||||
"battleRogueSet": "Kampfschurke (Schurke)",
|
||||
"battleRogueSet": "Kampf-Fleder (Schurke)",
|
||||
"springingBunnySet": "Hüpfendes Häschen (Heiler)",
|
||||
"grandMalkinSet": "Prächtiger Kater (Magier)",
|
||||
"cleverDogSet": "Schlauer Hund (Schurke)",
|
||||
"braveMouseSet": "Mutige Maus (Krieger)",
|
||||
"summer2016SharkWarriorSet": "Haifisch-Krieger (Krieger)",
|
||||
"summer2016DolphinMageSet": "Delfin-Magier (Magier)",
|
||||
"summer2016SeahorseHealerSet": "Seepferdchen-Heiler (Heiler)",
|
||||
"summer2016EelSet": "Zitteraal-Schurke (Schurke)",
|
||||
"summer2016SharkWarriorSet": "Haifisch(Krieger)",
|
||||
"summer2016DolphinMageSet": "Delfin (Magier)",
|
||||
"summer2016SeahorseHealerSet": "Seepferdchen (Heiler)",
|
||||
"summer2016EelSet": "Zitteraal (Schurke)",
|
||||
"fall2016SwampThingSet": "Das Ding aus dem Sumpf (Krieger)",
|
||||
"fall2016WickedSorcererSet": "Boshafter Zauberer (Magier)",
|
||||
"fall2016GorgonHealerSet": "Gorgonen-Heiler (Heiler)",
|
||||
"fall2016BlackWidowSet": "Schurkische Schwarze Witwe (Schurke)",
|
||||
"fall2016GorgonHealerSet": "Gorgone (Heiler)",
|
||||
"fall2016BlackWidowSet": "Schwarze Witwe (Schurke)",
|
||||
"winter2017IceHockeySet": "Eishockey (Krieger)",
|
||||
"winter2017WinterWolfSet": "Winterwolf (Magier)",
|
||||
"winter2017SugarPlumSet": "Zuckerpflaumen-Heiler (Heiler)",
|
||||
"winter2017FrostyRogueSet": "Frostiger Schurke (Schurke)",
|
||||
"spring2017FelineWarriorSet": "Katzenhafter Krieger (Krieger)",
|
||||
"winter2017SugarPlumSet": "Zuckerpflaume (Heiler)",
|
||||
"winter2017FrostyRogueSet": "Frosty (Schurke)",
|
||||
"spring2017FelineWarriorSet": "Katzenhaft (Krieger)",
|
||||
"spring2017CanineConjurorSet": "Bellender Beschwörer (Magier)",
|
||||
"spring2017FloralMouseSet": "Blumenmaus (Heiler)",
|
||||
"spring2017SneakyBunnySet": "Raffiniertes Häschen (Schurke)",
|
||||
"summer2017SandcastleWarriorSet": "Sandburg-Krieger (Krieger)",
|
||||
"summer2017WhirlpoolMageSet": "Whirlpool-Magier (Magier)",
|
||||
"summer2017SandcastleWarriorSet": "Sandburg (Krieger)",
|
||||
"summer2017WhirlpoolMageSet": "Whirlpool (Magier)",
|
||||
"summer2017SeashellSeahealerSet": "Muschel-Meeresheiler (Heiler)",
|
||||
"summer2017SeaDragonSet": "Seedrache (Schurke)",
|
||||
"fall2017HabitoweenSet": "Habitoween-Krieger (Krieger)",
|
||||
"fall2017MasqueradeSet": "Maskerade-Magier (Magier)",
|
||||
"fall2017HauntedHouseSet": "Geisterhaus-Heiler (Heiler)",
|
||||
"fall2017TrickOrTreatSet": "Süßes-oder-Saures-Schurke (Schurke)",
|
||||
"winter2018ConfettiSet": "Konfettimagier (Magier)",
|
||||
"winter2018GiftWrappedSet": "Geschenkpapierverpackter Krieger (Krieger)",
|
||||
"winter2018MistletoeSet": "Mistelzweigheiler (Heiler)",
|
||||
"winter2018ReindeerSet": "Rentier-Schurke (Schurke)",
|
||||
"spring2018SunriseWarriorSet": "Sonnenaufgang-Krieger (Krieger)",
|
||||
"spring2018TulipMageSet": "Tulpenmagier (Magier)",
|
||||
"spring2018GarnetHealerSet": "Granatheiler (Heiler)",
|
||||
"spring2018DucklingRogueSet": "Entchen-Schurke (Schurke)",
|
||||
"summer2018BettaFishWarriorSet": "Kampffisch-Krieger (Krieger)",
|
||||
"summer2018LionfishMageSet": "Feuerfisch-Magier (Magier)",
|
||||
"fall2017HabitoweenSet": "Habitoween (Krieger)",
|
||||
"fall2017MasqueradeSet": "Maskerade (Magier)",
|
||||
"fall2017HauntedHouseSet": "Geisterhaus (Heiler)",
|
||||
"fall2017TrickOrTreatSet": "Süßes oder Saures (Schurke)",
|
||||
"winter2018ConfettiSet": "Konfetti (Magier)",
|
||||
"winter2018GiftWrappedSet": "Geschenkpapierverpackt (Krieger)",
|
||||
"winter2018MistletoeSet": "Mistelzweig(Heiler)",
|
||||
"winter2018ReindeerSet": "Rentier (Schurke)",
|
||||
"spring2018SunriseWarriorSet": "Sonnenaufgang (Krieger)",
|
||||
"spring2018TulipMageSet": "Tulpe (Magier)",
|
||||
"spring2018GarnetHealerSet": "Granat (Heiler)",
|
||||
"spring2018DucklingRogueSet": "Entchen (Schurke)",
|
||||
"summer2018BettaFishWarriorSet": "Kampffisch (Krieger)",
|
||||
"summer2018LionfishMageSet": "Feuerfisch (Magier)",
|
||||
"summer2018MerfolkMonarchSet": "Meervolk-Monarch (Heiler)",
|
||||
"summer2018FisherRogueSet": "Fischdieb (Schurke)",
|
||||
"fall2018MinotaurWarriorSet": "Minotaurus (Krieger)",
|
||||
|
|
@ -173,5 +173,10 @@
|
|||
"winter2020WinterSpiceSet": "Wintergewürz (Heiler)",
|
||||
"winter2020CarolOfTheMageSet": "Weihnachtslied des Magiers (Magier)",
|
||||
"winter2020EvergreenSet": "Immergrün (Krieger)",
|
||||
"decemberYYYY": "Dezember <%= year %>"
|
||||
"decemberYYYY": "Dezember <%= year %>",
|
||||
"spring2020BeetleWarriorSet": "Nashornkäfer (Krieger)",
|
||||
"marchYYYY": "März <%= year %>",
|
||||
"spring2020LapisLazuliRogueSet": "Lapislazuli (Schurke)",
|
||||
"spring2020IrisHealerSet": "Iris (Heiler)",
|
||||
"spring2020PuddleMageSet": "Pfütze (Magier)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,5 +247,6 @@
|
|||
"subMonths": "Monate abonniert",
|
||||
"subscriptionStats": "Abonnenten-Attributwerte",
|
||||
"doubleDropCap": "Verdopple die Beute",
|
||||
"mysterySet202003": "Stachliges Streitgewandset"
|
||||
"mysterySet202003": "Stachliges Streitgewandset",
|
||||
"mysterySet202004": "Mächtiger-Monarch-Set"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
"hatchingPotion": "hatching potion",
|
||||
"noHatchingPotions": "You don't have any hatching potions.",
|
||||
"inventoryText": "Click an egg to see usable potions highlighted in green and then click one of the highlighted potions to hatch your pet. If no potions are highlighted, click that egg again to deselect it, and instead click a potion first to have the usable eggs highlighted. You can also sell unwanted drops to Alexander the Merchant.",
|
||||
"haveHatchablePet": "You have a <%= potion %> hatching potion and <%= egg %> egg to hatch this pet! <b>Click</b> the paw print to hatch.",
|
||||
"haveHatchablePet": "You have a <%= potion %> hatching potion and <%= egg %> egg to hatch this pet! <b>Click</b> to hatch!",
|
||||
"quickInventory": "Quick Inventory",
|
||||
"food": "Pet Food and Saddles",
|
||||
"noFoodAvailable": "You don't have any Pet Food.",
|
||||
|
|
|
|||
|
|
@ -2079,5 +2079,9 @@
|
|||
"weaponSpecialSpring2020WarriorNotes": "Fight or flight, this wing will serve you well! Increases Strength by <%= str %>. Limited Edition 2020 Spring Gear.",
|
||||
"weaponSpecialSpring2020WarriorText": "Sharpened Wing",
|
||||
"weaponSpecialSpring2020RogueNotes": "You'll strike so fast it'll look even MORE blue! Increases Strength by <%= str %>. Limited Edition 2020 Spring Gear.",
|
||||
"weaponSpecialSpring2020RogueText": "Lazurite Blade"
|
||||
"weaponSpecialSpring2020RogueText": "Lazurite Blade",
|
||||
"headAccessoryMystery202004Notes": "They twitch just a bit if the scent of flowers drifts by--use them to find a pretty garden! Confers no benefit. April 2020 Subscriber Item.",
|
||||
"headAccessoryMystery202004Text": "Mighty Monarch Antennae",
|
||||
"backMystery202004Notes": "Make a quick flutter to the nearest flowery meadow or migrate across the continent with these beautiful wings! Confers no benefit. April 2020 Subscriber Item.",
|
||||
"backMystery202004Text": "Mighty Monarch Wings"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,5 +247,6 @@
|
|||
"monthlyMysteryItems": "Monthly Mystery Items",
|
||||
"subscribersReceiveBenefits": "Subscribers receive these useful benefits!",
|
||||
"giftASubscription": "Gift a Subscription",
|
||||
"mysterySet202003": "Barbed Battler Set"
|
||||
"mysterySet202003": "Barbed Battler Set",
|
||||
"mysterySet202004": "Mighty Monarch Set"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2079,5 +2079,9 @@
|
|||
"weaponSpecialSpring2020WarriorNotes": "Lutar ou voar, esta asa irá te atender bem! Aumenta a Força em <%= str %>. Equipamento de edição limitada da primavera de 2020.",
|
||||
"weaponSpecialSpring2020WarriorText": "Asa afiada",
|
||||
"weaponSpecialSpring2020RogueNotes": "Você atacará tão rápido que ficará ainda MAIS AZUL! Aumenta a Força em <%= str %>. Equipamento de edição limitada da primavera de 2020.",
|
||||
"weaponSpecialSpring2020RogueText": "Lâmina de lazurita"
|
||||
"weaponSpecialSpring2020RogueText": "Lâmina de lazurita",
|
||||
"headAccessoryMystery202004Notes": "Elas tremem um pouco se o perfume das flores passa perto -- use-as para encontrar um belo jardim! Não confere benefícios. Item de assinante, Abril de 2020.",
|
||||
"headAccessoryMystery202004Text": "Antenas do(a) Monarca poderoso(a)",
|
||||
"backMystery202004Notes": "Faça um movimento rápido para o prado florido mais próximo ou migre pelo continente com essas lindas asas! Não confere benefícios. Item de assinante, Abril de 2020.",
|
||||
"backMystery202004Text": "Asas do(a) Monarca poderoso(a)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,5 +247,6 @@
|
|||
"monthlyMysteryItems": "Itens misteriosos mensalmente",
|
||||
"subscribersReceiveBenefits": "Assinantes recebem esses benefícios úteis!",
|
||||
"giftASubscription": "Presentar uma Assinatura",
|
||||
"mysterySet202003": "Conjunto do(a) gladiador(a) farpado(a)"
|
||||
"mysterySet202003": "Conjunto do(a) gladiador(a) farpado(a)",
|
||||
"mysterySet202004": "Conjunto do(a) Monarca poderoso(a)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2079,5 +2079,7 @@
|
|||
"armorSpecialSpring2020RogueText": "群青装甲",
|
||||
"armorSpecialSpring2020WarriorText": "外骨骼护甲",
|
||||
"armorSpecialSpring2020MageText": "旋风长袍",
|
||||
"armorSpecialSpring2020HealerText": "防护的花瓣"
|
||||
"armorSpecialSpring2020HealerText": "防护的花瓣",
|
||||
"headAccessoryMystery202004Text": "强大的君主斑蝶触角",
|
||||
"backMystery202004Text": "强大的君主斑蝶翅膀"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,5 +247,6 @@
|
|||
"monthlyMysteryItems": "每月神秘物品",
|
||||
"subscribersReceiveBenefits": "订阅者可以获得这些优越的好处!",
|
||||
"mysterySet202003": "倒刺斗士套装",
|
||||
"giftASubscription": "赠送订阅"
|
||||
"giftASubscription": "赠送订阅",
|
||||
"mysterySet202004": "强大的君主斑蝶套装"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ api.logout = {
|
|||
async handler (req, res) {
|
||||
if (req.logout) req.logout(); // passportjs method
|
||||
req.session = null;
|
||||
res.redirect('/');
|
||||
|
||||
const redirectUrl = req.query.redirectToLogin === 'true' ? '/login' : '/';
|
||||
res.redirect(redirectUrl);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -133,9 +133,9 @@ export async function sendTxn (mailingInfoArray, emailType, variables, personalV
|
|||
return got.post(`${EMAIL_SERVER.url}/job`, {
|
||||
retry: 5, // retry the http request to the email server 5 times
|
||||
timeout: 60000, // wait up to 60s before timing out
|
||||
auth: `${EMAIL_SERVER.auth.user}:${EMAIL_SERVER.auth.password}`,
|
||||
json: true,
|
||||
body: {
|
||||
username: EMAIL_SERVER.auth.user,
|
||||
password: EMAIL_SERVER.auth.password,
|
||||
json: {
|
||||
type: 'email',
|
||||
data: {
|
||||
emailType,
|
||||
|
|
@ -149,7 +149,7 @@ export async function sendTxn (mailingInfoArray, emailType, variables, personalV
|
|||
backoff: { delay: 10 * 60 * 1000, type: 'fixed' },
|
||||
},
|
||||
},
|
||||
}).catch(err => logger.error(err));
|
||||
}).json().catch(err => logger.error(err, 'Error while sending an email.'));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -22,34 +22,10 @@ const momentLangsMapping = {
|
|||
};
|
||||
|
||||
export const approvedLanguages = [
|
||||
'bg',
|
||||
'cs',
|
||||
'da',
|
||||
'de',
|
||||
'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',
|
||||
'bg', 'cs', 'da', 'de', '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) {
|
||||
|
|
|
|||
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;
|
||||
}
|
||||
|
|
@ -13,10 +13,10 @@ function sendWebhook (webhook, body, user) {
|
|||
const { url, lastFailureAt } = webhook;
|
||||
|
||||
got.post(url, {
|
||||
body,
|
||||
json: true,
|
||||
json: body,
|
||||
timeout: 30000, // wait up to 30s before timing out
|
||||
retry: 3, // retry the request up to 3 times
|
||||
// Not calling .json() to parse the response because we simply ignore it
|
||||
}).catch(webhookErr => {
|
||||
// Log the error
|
||||
logger.error(webhookErr, 'Error while sending a webhook request.');
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import {
|
|||
model as User,
|
||||
} from '../models/user';
|
||||
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 USER_FIELDS_ALWAYS_LOADED = ['_id', 'notifications', 'preferences', 'auth', 'flags'];
|
||||
|
|
@ -72,7 +74,17 @@ export function authWithHeaders (options = {}) {
|
|||
.exec()
|
||||
.then(user => {
|
||||
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;
|
||||
req.session.userId = user._id;
|
||||
|
|
|
|||
|
|
@ -1,60 +1,15 @@
|
|||
import accepts from 'accepts';
|
||||
import _ from 'lodash';
|
||||
import { model as User } from '../models/user';
|
||||
import common from '../../common';
|
||||
import {
|
||||
translations,
|
||||
defaultLangCodes,
|
||||
multipleVersionsLanguages,
|
||||
} from '../libs/i18n';
|
||||
import {
|
||||
getLanguageFromUser,
|
||||
getLanguageFromBrowser,
|
||||
} from '../libs/language';
|
||||
|
||||
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) {
|
||||
res.t = function reqTranslation (...args) {
|
||||
return i18n.t(...args, req.language);
|
||||
|
|
@ -64,26 +19,33 @@ export function attachTranslateFunction (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';
|
||||
return next();
|
||||
}
|
||||
|
||||
// If the request is authenticated, use the user's preferred language
|
||||
} if (req.locals && req.locals.user) {
|
||||
req.language = _getFromUser(req.locals.user, req);
|
||||
if (res.locals && res.locals.user) {
|
||||
req.language = getLanguageFromUser(res.locals.user, req);
|
||||
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({
|
||||
_id: req.session.userId,
|
||||
}, 'preferences.language')
|
||||
.lean()
|
||||
.exec()
|
||||
.then(user => {
|
||||
req.language = _getFromUser(user, req);
|
||||
req.language = getLanguageFromUser(user, req);
|
||||
return next();
|
||||
})
|
||||
.catch(next);
|
||||
} // Otherwise get from browser
|
||||
req.language = _getFromUser(null, req);
|
||||
}
|
||||
|
||||
// Otherwise get from browser
|
||||
req.language = getLanguageFromBrowser(req);
|
||||
return next();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ export const schema = new Schema({
|
|||
required: true,
|
||||
validate: [v => validator.isURL(v, {
|
||||
require_tld: !!IS_PRODUCTION, // eslint-disable-line camelcase
|
||||
require_protocol: true, // TODO migrate existing ones
|
||||
protocols: ['http', 'https'],
|
||||
}), shared.i18n.t('invalidUrl')],
|
||||
},
|
||||
enabled: { $type: Boolean, required: true, default: true },
|
||||
|
|
|
|||
Loading…
Reference in a new issue